Vendor import of lldb trunk r256945:
https://llvm.org/svn/llvm-project/lldb/trunk@256945
This commit is contained in:
parent
3bd2e91fae
commit
9e6d35490a
4
.arcconfig
Normal file
4
.arcconfig
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"project_id" : "lldb",
|
||||
"conduit_uri" : "http://reviews.llvm.org/"
|
||||
}
|
9
.clang-format
Normal file
9
.clang-format
Normal file
@ -0,0 +1,9 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
BreakBeforeBraces: Allman
|
||||
AlwaysBreakAfterReturnType: All
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
IndentCaseLabels: true
|
||||
AccessModifierOffset: -4
|
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@ -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
|
62
CMakeLists.txt
Normal file
62
CMakeLists.txt
Normal file
@ -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 ()
|
59
CODE_OWNERS.txt
Normal file
59
CODE_OWNERS.txt
Normal file
@ -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
|
||||
|
13
INSTALL.txt
Normal file
13
INSTALL.txt
Normal file
@ -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
|
120
Makefile
Normal file
120
Makefile
Normal file
@ -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
|
207
cmake/LLDBDependencies.cmake
Normal file
207
cmake/LLDBDependencies.cmake
Normal file
@ -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()
|
114
cmake/modules/AddLLDB.cmake
Normal file
114
cmake/modules/AddLLDB.cmake
Normal file
@ -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()
|
412
cmake/modules/LLDBConfig.cmake
Normal file
412
cmake/modules/LLDBConfig.cmake
Normal file
@ -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 $<CONFIG:Debug>: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. $<CONFIG:Debug> evaluates to 1 when the Debug configuration is being generated,
|
||||
# or 0 in all other cases.
|
||||
# 2. $<$<CONFIG:Debug>:${PYTHON_DEBUG_LIB}> expands to ${PYTHON_DEBUG_LIB} when the Debug
|
||||
# configuration is being generated, or nothing (literally) in all other cases.
|
||||
# 3. $<$<NOT:$<CONFIG:Debug>>:${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 $<$<CONFIG:Debug>:${PYTHON_DEBUG_EXE}>$<$<NOT:$<CONFIG:Debug>>:${PYTHON_RELEASE_EXE}>)
|
||||
set (PYTHON_LIBRARY $<$<CONFIG:Debug>:${PYTHON_DEBUG_LIB}>$<$<NOT:$<CONFIG:Debug>>:${PYTHON_RELEASE_LIB}>)
|
||||
set (PYTHON_DLL $<$<CONFIG:Debug>:${PYTHON_DEBUG_DLL}>$<$<NOT:$<CONFIG:Debug>>:${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 <sys/uio.h>
|
||||
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 <sys/syscall.h>
|
||||
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 ()
|
99
cmake/modules/LLDBStandalone.cmake
Normal file
99
cmake/modules/LLDBStandalone.cmake
Normal file
@ -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()
|
188
cmake/platforms/Android.cmake
Normal file
188
cmake/platforms/Android.cmake
Normal file
@ -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=<toolchain_dir> \
|
||||
# -DANDROID_ABI=<target_abi> \
|
||||
# -DCMAKE_CXX_COMPILER_VERSION=<gcc_version> \
|
||||
# -DLLVM_TARGET_ARCH=<llvm_target_arch> \
|
||||
# -DLLVM_TARGETS_TO_BUILD=<llvm_targets_to_build> \
|
||||
# -DLLVM_TABLEGEN=<path_to_llvm-tblgen> \
|
||||
# -DCLANG_TABLEGEN=<path_to_clang-tblgen>
|
||||
#
|
||||
# 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 <dlfcn.h>
|
||||
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 ##################
|
41
docs/CMakeLists.txt
Normal file
41
docs/CMakeLists.txt
Normal file
@ -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)
|
50
docs/building-with-debug-llvm.txt
Normal file
50
docs/building-with-debug-llvm.txt
Normal file
@ -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.
|
61
docs/code-signing.txt
Normal file
61
docs/code-signing.txt
Normal file
@ -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.
|
1631
docs/doxygen.cfg.in
Normal file
1631
docs/doxygen.cfg.in
Normal file
File diff suppressed because it is too large
Load Diff
13
docs/doxygen.footer
Normal file
13
docs/doxygen.footer
Normal file
@ -0,0 +1,13 @@
|
||||
<hr>
|
||||
<p class="footer">
|
||||
Generated on $datetime for <a href="http://lldb.llvm.org/">$projectname</a> by
|
||||
<a href="http://www.doxygen.org"><img src="doxygen.png" alt="Doxygen"
|
||||
align="middle" border="0"/>$doxygenversion</a><br>
|
||||
Copyright © 2003-2013 University of Illinois at Urbana-Champaign.
|
||||
All Rights Reserved.</p>
|
||||
|
||||
<hr>
|
||||
<!--#include virtual="/attrib.incl" -->
|
||||
|
||||
</body>
|
||||
</html>
|
9
docs/doxygen.header
Normal file
9
docs/doxygen.header
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/>
|
||||
<meta name="keywords" content="LLDB,C++,doxygen,API,documentation"/>
|
||||
<meta name="description" content="C++ source code API documentation for LLDB."/>
|
||||
<title>LLVM: $title</title>
|
||||
<link href="doxygen.css" rel="stylesheet" type="text/css"/>
|
||||
</head><body>
|
||||
<p class="title">LLDB API Documentation</p>
|
19
docs/doxygen.intro
Normal file
19
docs/doxygen.intro
Normal file
@ -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
|
488
docs/lldb-for-gdb-users.txt
Normal file
488
docs/lldb-for-gdb-users.txt
Normal file
@ -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
|
||||
|
||||
<noun> <verb> [-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 <TAB>.
|
||||
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
|
||||
<SKTGraphicView: 0x100208b40>
|
||||
|
||||
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.
|
1675
docs/lldb-gdb-remote.txt
Normal file
1675
docs/lldb-gdb-remote.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
||||
|
309
docs/testsuite/a-detailed-walkthrough.txt
Normal file
309
docs/testsuite/a-detailed-walkthrough.txt
Normal file
@ -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.
|
93
docs/testsuite/best-practices.txt
Normal file
93
docs/testsuite/best-practices.txt
Normal file
@ -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.
|
5
examples/customization/bin-utils/.lldbinit
Normal file
5
examples/customization/bin-utils/.lldbinit
Normal file
@ -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
|
36
examples/customization/bin-utils/README
Normal file
36
examples/customization/bin-utils/README
Normal file
@ -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)
|
122
examples/customization/bin-utils/binutils.py
Normal file
122
examples/customization/bin-utils/binutils.py
Normal file
@ -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)
|
||||
|
40
examples/customization/import-python/README
Normal file
40
examples/customization/import-python/README
Normal file
@ -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
|
31
examples/customization/import-python/importcmd.py
Normal file
31
examples/customization/import-python/importcmd.py
Normal file
@ -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
|
7
examples/customization/pwd-cd-and-system/.lldbinit
Normal file
7
examples/customization/pwd-cd-and-system/.lldbinit
Normal file
@ -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
|
41
examples/customization/pwd-cd-and-system/README
Normal file
41
examples/customization/pwd-cd-and-system/README
Normal file
@ -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)
|
49
examples/customization/pwd-cd-and-system/utils.py
Normal file
49
examples/customization/pwd-cd-and-system/utils.py
Normal file
@ -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
|
1244
examples/darwin/heap_find/heap.py
Normal file
1244
examples/darwin/heap_find/heap.py
Normal file
File diff suppressed because it is too large
Load Diff
33
examples/darwin/heap_find/heap/Makefile
Normal file
33
examples/darwin/heap_find/heap/Makefile
Normal file
@ -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)"
|
||||
|
||||
|
||||
|
1071
examples/darwin/heap_find/heap/heap_find.cpp
Normal file
1071
examples/darwin/heap_find/heap/heap_find.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
examples/functions/Makefile
Normal file
18
examples/functions/Makefile
Normal file
@ -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
|
364
examples/functions/main.cpp
Normal file
364
examples/functions/main.cpp
Normal file
@ -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 <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <LLDB/LLDB.h>
|
||||
#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 <string>
|
||||
|
||||
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=<ARCH>] [--platform=<PLATFORM>] [--verbose] [--help] [--canonical] --] <PATH> [<PATH>....]\n"
|
||||
"\n"
|
||||
"DESCRIPTION\n"
|
||||
" Loads the executable pointed to by <PATH> 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<num_locations; ++bp_loc_idx)
|
||||
{
|
||||
SBBreakpointLocation bp_loc = bp.GetLocationAtIndex(bp_loc_idx);
|
||||
SBSymbolContext sc (bp_loc.GetAddress().GetSymbolContext(eSymbolContextEverything));
|
||||
if (sc.IsValid())
|
||||
{
|
||||
if (sc.GetBlock().GetContainingInlinedBlock().IsValid())
|
||||
{
|
||||
// Skip inlined functions
|
||||
continue;
|
||||
}
|
||||
SBFunction function (sc.GetFunction());
|
||||
if (function.IsValid())
|
||||
{
|
||||
addr_t lo_pc = function.GetStartAddress().GetFileAddress();
|
||||
if (lo_pc == LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
// Skip functions that don't have concrete instances in the binary
|
||||
continue;
|
||||
}
|
||||
addr_t hi_pc = function.GetEndAddress().GetFileAddress();
|
||||
const char *func_demangled_name = function.GetName();
|
||||
const char *func_mangled_name = function.GetMangledName();
|
||||
|
||||
bool dump = true;
|
||||
const bool is_objc_method = ((func_demangled_name[0] == '-') || (func_demangled_name[0] == '+')) && (func_demangled_name[1] == '[');
|
||||
if (external_only)
|
||||
{
|
||||
// Dump all objective C methods, or external symbols
|
||||
dump = is_objc_method;
|
||||
if (!dump)
|
||||
dump = sc.GetSymbol().IsExternal();
|
||||
}
|
||||
|
||||
if (dump)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
printf ("\n name: %s\n", func_demangled_name);
|
||||
if (func_mangled_name)
|
||||
printf ("mangled: %s\n", func_mangled_name);
|
||||
printf (" range: [0x%16.16llx - 0x%16.16llx)\n type: ", lo_pc, hi_pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("[0x%16.16llx - 0x%16.16llx) ", lo_pc, hi_pc);
|
||||
}
|
||||
SBType function_type = function.GetType();
|
||||
SBType return_type = function_type.GetFunctionReturnType();
|
||||
|
||||
if (canonical)
|
||||
return_type = return_type.GetCanonicalType();
|
||||
|
||||
if (func_mangled_name &&
|
||||
func_mangled_name[0] == '_' &&
|
||||
func_mangled_name[1] == 'Z')
|
||||
{
|
||||
printf ("%s %s\n", return_type.GetName(), func_demangled_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
SBTypeList function_args = function_type.GetFunctionArgumentTypes();
|
||||
const size_t num_function_args = function_args.GetSize();
|
||||
|
||||
if (is_objc_method)
|
||||
{
|
||||
const char *class_name_start = func_demangled_name + 2;
|
||||
|
||||
if (num_function_args == 0)
|
||||
{
|
||||
printf("%c(%s)[%s\n", func_demangled_name[0], return_type.GetName(), class_name_start);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *class_name_end = strchr(class_name_start,' ');
|
||||
const int class_name_len = class_name_end - class_name_start;
|
||||
printf ("%c(%s)[%*.*s", func_demangled_name[0], return_type.GetName(), class_name_len, class_name_len, class_name_start);
|
||||
|
||||
const char *selector_pos = class_name_end + 1;
|
||||
for (uint32_t function_arg_idx = 0; function_arg_idx < num_function_args; ++function_arg_idx)
|
||||
{
|
||||
const char *selector_end = strchr(selector_pos, ':') + 1;
|
||||
const int selector_len = selector_end - selector_pos;
|
||||
SBType function_arg_type = function_args.GetTypeAtIndex(function_arg_idx);
|
||||
|
||||
if (canonical)
|
||||
function_arg_type = function_arg_type.GetCanonicalType();
|
||||
|
||||
printf (" %*.*s", selector_len, selector_len, selector_pos);
|
||||
if (function_arg_type.IsValid())
|
||||
{
|
||||
printf ("(%s)", function_arg_type.GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("(?)");
|
||||
}
|
||||
selector_pos = selector_end;
|
||||
}
|
||||
printf ("]\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("%s ", return_type.GetName());
|
||||
if (strchr (func_demangled_name, '('))
|
||||
printf ("(*)(");
|
||||
else
|
||||
printf ("%s(", func_demangled_name);
|
||||
|
||||
for (uint32_t function_arg_idx = 0; function_arg_idx < num_function_args; ++function_arg_idx)
|
||||
{
|
||||
SBType function_arg_type = function_args.GetTypeAtIndex(function_arg_idx);
|
||||
|
||||
if (canonical)
|
||||
function_arg_type = function_arg_type.GetCanonicalType();
|
||||
|
||||
if (function_arg_type.IsValid())
|
||||
{
|
||||
printf ("%s%s", function_arg_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;
|
||||
}
|
||||
|
1152
examples/interposing/darwin/fd_interposing/FDInterposing.cpp
Normal file
1152
examples/interposing/darwin/fd_interposing/FDInterposing.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
examples/interposing/darwin/fd_interposing/Makefile
Normal file
7
examples/interposing/darwin/fd_interposing/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
LEVEL = ../../../../test/make
|
||||
|
||||
DYLIB_NAME := FDInterposing
|
||||
DYLIB_ONLY := YES
|
||||
DYLIB_CXX_SOURCES := FDInterposing.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
17
examples/lookup/Makefile
Normal file
17
examples/lookup/Makefile
Normal file
@ -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
|
235
examples/lookup/main.cpp
Normal file
235
examples/lookup/main.cpp
Normal file
@ -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 <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <LLDB/LLDB.h>
|
||||
#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 <string>
|
||||
|
||||
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=<ARCH>] [--platform=<PLATFORM>] [--verbose] [--help] --] <PATH> <ADDRESS> [<ADDRESS>....]\n"
|
||||
"\n"
|
||||
"DESCRIPTION\n"
|
||||
" Loads the executable pointed to by <PATH> and looks up and <ADDRESS>\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;
|
||||
}
|
||||
|
56
examples/plugins/commands/fooplugin.cpp
Normal file
56
examples/plugins/commands/fooplugin.cpp
Normal file
@ -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 <LLDB/SBCommandInterpreter.h>
|
||||
#include <LLDB/SBCommandReturnObject.h>
|
||||
#include <LLDB/SBDebugger.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
76
examples/python/cmdtemplate.py
Normal file
76
examples/python/cmdtemplate.py
Normal file
@ -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.<thing>.whatever with <thing>.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.'
|
829
examples/python/crashlog.py
Executable file
829
examples/python/crashlog.py
Executable file
@ -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> [PATH ...]"
|
||||
description='''Dump information about one or more images in all crash logs. The <PATH> 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] <output-path>"
|
||||
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> [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'
|
||||
|
115
examples/python/delta.py
Executable file
115
examples/python/delta.py
Executable file
@ -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] [<LOGFILEPATH>]"
|
||||
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 <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'
|
171
examples/python/diagnose_nsstring.py
Normal file
171
examples/python/diagnose_nsstring.py
Normal file
@ -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 <expr returning 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
|
270
examples/python/diagnose_unwind.py
Normal file
270
examples/python/diagnose_unwind.py
Normal file
@ -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.'
|
61
examples/python/dict_utils.py
Executable file
61
examples/python/dict_utils.py
Executable file
@ -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__()
|
168
examples/python/disasm-stress-test.py
Executable file
168
examples/python/disasm-stress-test.py
Executable file
@ -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()
|
119
examples/python/disasm.py
Executable file
119
examples/python/disasm.py
Executable file
@ -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()
|
221
examples/python/file_extract.py
Executable file
221
examples/python/file_extract.py
Executable file
@ -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
|
24
examples/python/gdb_disassemble.py
Executable file
24
examples/python/gdb_disassemble.py
Executable file
@ -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'
|
1362
examples/python/gdbremote.py
Executable file
1362
examples/python/gdbremote.py
Executable file
File diff suppressed because it is too large
Load Diff
72
examples/python/globals.py
Executable file
72
examples/python/globals.py
Executable file
@ -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 <path> [<path> ...])
|
||||
#
|
||||
# For the shells sh, bash:
|
||||
# PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ./globals.py <path> [<path> ...]
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
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> [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:])
|
||||
|
173
examples/python/jump.py
Normal file
173
examples/python/jump.py
Normal file
@ -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 <linenum>"
|
||||
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 +<count>"
|
||||
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 -<count>"
|
||||
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 <filename>:<linenum>"
|
||||
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-expression>"
|
||||
address = long(mo.group(1), base=0)
|
||||
breakpoint = target.BreakpointCreateByAddress(address)
|
||||
|
||||
if (not matched):
|
||||
#print "Trying <function-name>"
|
||||
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 <linespec> [<location-id>]
|
||||
|
||||
Command Options Usage:
|
||||
jump <linenum>
|
||||
jump +<count>
|
||||
jump -<count>
|
||||
jump <filename>:<linenum>
|
||||
jump <function-name>
|
||||
jump *<address-expression>
|
||||
|
||||
<location-id> 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 + " <location-id>' 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 <ENTER>" for detailed help.'
|
59
examples/python/lldb_module_utils.py
Normal file
59
examples/python/lldb_module_utils.py
Normal file
@ -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'
|
544
examples/python/lldbtk.py
Normal file
544
examples/python/lldbtk.py
Normal file
@ -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('<<TreeviewOpen>>', 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()
|
||||
|
1687
examples/python/mach_o.py
Executable file
1687
examples/python/mach_o.py
Executable file
File diff suppressed because it is too large
Load Diff
181
examples/python/memory.py
Executable file
181
examples/python/memory.py
Executable file
@ -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'
|
104
examples/python/operating_system.py
Normal file
104
examples/python/operating_system.py
Normal file
@ -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
|
||||
|
335
examples/python/performance.py
Executable file
335
examples/python/performance.py
Executable file
@ -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)
|
278
examples/python/process_events.py
Executable file
278
examples/python/process_events.py
Executable file
@ -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:])
|
328
examples/python/pytracer.py
Normal file
328
examples/python/pytracer.py
Normal file
@ -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() == "<module>":
|
||||
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() == "<module>":
|
||||
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() == "<module>":
|
||||
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() == "<module>":
|
||||
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()
|
255
examples/python/sbvalue.py
Executable file
255
examples/python/sbvalue.py
Executable file
@ -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()
|
||||
|
186
examples/python/scripted_step.py
Normal file
186
examples/python/scripted_step.py
Normal file
@ -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
|
||||
|
28
examples/python/sources.py
Normal file
28
examples/python/sources.py
Normal file
@ -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.'
|
59
examples/python/stacks.py
Executable file
59
examples/python/stacks.py
Executable file
@ -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> [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."
|
640
examples/python/symbolication.py
Executable file
640
examples/python/symbolication.py
Executable file
@ -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<name>[^=]+)=(?P<range>.*)')
|
||||
addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$')
|
||||
range_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[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 '<name>=<base> __TEXT=0x123000 Section from base address only'
|
||||
print '<name>=<base>-<end> __TEXT=0x123000-0x124000 Section from base address and end address'
|
||||
print '<name>=<base>+<size> __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 "<invalid>"
|
||||
|
||||
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] <addr1> [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 <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>')
|
||||
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:])
|
265
examples/python/types.py
Executable file
265
examples/python/types.py
Executable file
@ -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<PADDING>' % (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<PADDING>' % (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'
|
353
examples/python/x86_64_linux_target_definition.py
Normal file
353
examples/python/x86_64_linux_target_definition.py
Normal file
@ -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()
|
352
examples/python/x86_64_qemu_target_definition.py
Normal file
352
examples/python/x86_64_qemu_target_definition.py
Normal file
@ -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()
|
357
examples/python/x86_64_target_definition.py
Normal file
357
examples/python/x86_64_target_definition.py
Normal file
@ -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()
|
200
examples/scripting/dictionary.c
Normal file
200
examples/scripting/dictionary.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
118
examples/scripting/tree_utils.py
Executable file
118
examples/scripting/tree_utils.py
Executable file
@ -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))
|
||||
|
||||
|
204
examples/summaries/cocoa/CFArray.py
Normal file
204
examples/summaries/cocoa/CFArray.py
Normal file
@ -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 "<variable is not NSArray>"
|
||||
|
||||
# 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 = '<variable is not NSArray>'
|
||||
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")
|
146
examples/summaries/cocoa/CFBag.py
Normal file
146
examples/summaries/cocoa/CFBag.py
Normal file
@ -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 "<variable is not CFBag>"
|
||||
|
||||
|
||||
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 = '<variable is not CFBag>'
|
||||
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")
|
142
examples/summaries/cocoa/CFBinaryHeap.py
Normal file
142
examples/summaries/cocoa/CFBinaryHeap.py
Normal file
@ -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 '<variable is not CFBinaryHeap>'
|
||||
|
||||
|
||||
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 = '<variable is not CFBinaryHeap>'
|
||||
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")
|
175
examples/summaries/cocoa/CFBitVector.py
Normal file
175
examples/summaries/cocoa/CFBitVector.py
Normal file
@ -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 '<unable to summarize this CFBitVector>'
|
||||
|
||||
|
||||
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 = '<variable is not CFBitVector>'
|
||||
return summary
|
||||
return 'Summary Unavailable'
|
||||
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand("type summary add -F CFBitVector.CFBitVector_SummaryProvider CFBitVectorRef CFMutableBitVectorRef")
|
234
examples/summaries/cocoa/CFDictionary.py
Normal file
234
examples/summaries/cocoa/CFDictionary.py
Normal file
@ -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 '<variable is not NSDictionary>'
|
||||
|
||||
|
||||
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 '<variable is not NSDictionary>'
|
||||
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 = '<variable is not CFDictionary>'
|
||||
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")
|
325
examples/summaries/cocoa/CFString.py
Normal file
325
examples/summaries/cocoa/CFString.py
Normal file
@ -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 = '<variable is not NSString>'
|
||||
return '@'+summary
|
||||
return ''
|
||||
|
||||
def CFAttributedString_SummaryProvider (valobj,dict):
|
||||
logger = lldb.formatters.Logger.Logger()
|
||||
offset = valobj.GetTarget().GetProcess().GetAddressByteSize()
|
||||
pointee = valobj.GetValueAsUnsigned(0)
|
||||
summary = '<variable is not NSAttributedString>'
|
||||
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 = '<variable is not NSAttributedString>'
|
||||
if summary == None:
|
||||
summary = '<variable is not NSAttributedString>'
|
||||
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();
|
21
examples/summaries/cocoa/Class.py
Normal file
21
examples/summaries/cocoa/Class.py
Normal file
@ -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 '<error: unknown Class>'
|
||||
class_data = runtime.read_class_data()
|
||||
if class_data == None or not class_data.is_valid():
|
||||
return '<error: unknown Class>'
|
||||
return class_data.class_name()
|
||||
|
122
examples/summaries/cocoa/Logger.py
Normal file
122
examples/summaries/cocoa/Logger.py
Normal file
@ -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()
|
||||
|
127
examples/summaries/cocoa/NSBundle.py
Normal file
127
examples/summaries/cocoa/NSBundle.py
Normal file
@ -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 '<variable is not NSBundle>'
|
||||
|
||||
|
||||
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 = '<variable is not NSBundle>'
|
||||
return summary
|
||||
return 'Summary Unavailable'
|
||||
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand("type summary add -F NSBundle.NSBundle_SummaryProvider NSBundle")
|
163
examples/summaries/cocoa/NSData.py
Normal file
163
examples/summaries/cocoa/NSData.py
Normal file
@ -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 '<variable is not NSData>'
|
||||
|
||||
|
||||
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 = '<variable is not NSData>'
|
||||
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 = '<variable is not CFData>'
|
||||
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")
|
269
examples/summaries/cocoa/NSDate.py
Normal file
269
examples/summaries/cocoa/NSDate.py
Normal file
@ -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 '<variable is not NSDate>'
|
||||
|
||||
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 = '<variable is not NSDate>'
|
||||
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 = '<variable is not NSTimeZone>'
|
||||
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")
|
||||
|
114
examples/summaries/cocoa/NSException.py
Normal file
114
examples/summaries/cocoa/NSException.py
Normal file
@ -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 '<variable is not NSException>'
|
||||
|
||||
|
||||
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 = '<variable is not NSException>'
|
||||
return str(summary)
|
||||
return 'Summary Unavailable'
|
||||
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand("type summary add -F NSException.NSException_SummaryProvider NSException")
|
150
examples/summaries/cocoa/NSIndexSet.py
Normal file
150
examples/summaries/cocoa/NSIndexSet.py
Normal file
@ -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 '<variable is not NSIndexSet>'
|
||||
|
||||
|
||||
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 = '<variable is not NSIndexSet>'
|
||||
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")
|
123
examples/summaries/cocoa/NSMachPort.py
Normal file
123
examples/summaries/cocoa/NSMachPort.py
Normal file
@ -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 '<variable is not NSMachPort>'
|
||||
|
||||
|
||||
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 = '<variable is not NSMachPort>'
|
||||
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")
|
110
examples/summaries/cocoa/NSNotification.py
Normal file
110
examples/summaries/cocoa/NSNotification.py
Normal file
@ -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 '<variable is not NSNotification>'
|
||||
|
||||
|
||||
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 = '<variable is not NSNotification>'
|
||||
return str(summary)
|
||||
return 'Summary Unavailable'
|
||||
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand("type summary add -F NSNotification.NSNotification_SummaryProvider NSNotification")
|
235
examples/summaries/cocoa/NSNumber.py
Normal file
235
examples/summaries/cocoa/NSNumber.py
Normal file
@ -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 '<variable is not NSNumber>'
|
||||
|
||||
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 = '<variable is not NSNumber>'
|
||||
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")
|
||||
|
263
examples/summaries/cocoa/NSSet.py
Normal file
263
examples/summaries/cocoa/NSSet.py
Normal file
@ -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 '<variable is not NSSet>'
|
||||
|
||||
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 = '<variable is not NSSet>'
|
||||
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 = '<variable is not CFSet>'
|
||||
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")
|
137
examples/summaries/cocoa/NSURL.py
Normal file
137
examples/summaries/cocoa/NSURL.py
Normal file
@ -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 '<variable is not NSURL>'
|
||||
|
||||
|
||||
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 = '<variable is not NSURL>'
|
||||
return summary
|
||||
return 'Summary Unavailable'
|
||||
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand("type summary add -F NSURL.NSURL_SummaryProvider NSURL CFURLRef")
|
14
examples/summaries/cocoa/Selector.py
Normal file
14
examples/summaries/cocoa/Selector.py
Normal file
@ -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()
|
38
examples/summaries/cocoa/attrib_fromdict.py
Normal file
38
examples/summaries/cocoa/attrib_fromdict.py
Normal file
@ -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)
|
35
examples/summaries/cocoa/cache.py
Normal file
35
examples/summaries/cocoa/cache.py
Normal file
@ -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
|
||||
|
94
examples/summaries/cocoa/metrics.py
Normal file
94
examples/summaries/cocoa/metrics.py
Normal file
@ -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
|
781
examples/summaries/cocoa/objc_runtime.py
Normal file
781
examples/summaries/cocoa/objc_runtime.py
Normal file
@ -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 '@"<nil>"'
|
||||
else:
|
||||
return '<invalid pointer>'
|
||||
|
||||
class InvalidISA_Description(SpecialSituation_Description):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def message(self):
|
||||
return '<not an Objective-C object>'
|
||||
|
||||
class ThisIsZombie_Description(SpecialSituation_Description):
|
||||
def message(self):
|
||||
return '<freed object>'
|
5
examples/summaries/essentials
Normal file
5
examples/summaries/essentials
Normal file
@ -0,0 +1,5 @@
|
||||
type summary add -s "${var._M_dataplus._M_p}" std::string std::basic_string<char> "std::basic_string<char,std::char_traits<char>,std::allocator<char> >"
|
||||
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<
|
28
examples/summaries/lldb
Normal file
28
examples/summaries/lldb
Normal file
@ -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<lldb::StateType>" -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
|
16
examples/summaries/objc.py
Normal file
16
examples/summaries/objc.py
Normal file
@ -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 "<invalid>"
|
||||
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)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user