Index: vendor/lldb/dist/cmake/modules/LLDBConfig.cmake =================================================================== --- vendor/lldb/dist/cmake/modules/LLDBConfig.cmake (revision 317454) +++ vendor/lldb/dist/cmake/modules/LLDBConfig.cmake (revision 317455) @@ -1,451 +1,445 @@ include(CheckCXXSymbolExists) 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) elseif (CMAKE_SYSTEM_NAME MATCHES "Android" ) 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() 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.") set(LLDB_USE_SYSTEM_SIX 0 CACHE BOOL "Use six.py shipped with system and do not install a copy of it") if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") set(LLDB_EXPORT_ALL_SYMBOLS 0 CACHE BOOL "Causes lldb to export all symbols when building liblldb.") else() # Windows doesn't support toggling this, so don't bother making it a # cache variable. set(LLDB_EXPORT_ALL_SYMBOLS 0) endif() if ((NOT MSVC) OR MSVC12) add_definitions( -DHAVE_ROUND ) endif() if (LLDB_DISABLE_CURSES) add_definitions( -DLLDB_DISABLE_CURSES ) endif() # On Windows, we can't use the normal FindPythonLibs module that comes with CMake, # for a number of reasons. # 1) Prior to MSVC 2015, it is only possible to embed Python if python itself was # compiled with an identical version (and build configuration) of MSVC as LLDB. # The standard algorithm does not take into account the differences between # a binary release distribution of python and a custom built distribution. # 2) From MSVC 2015 and onwards, it is only possible to use Python 3.5 or later. # 3) FindPythonLibs queries the registry to locate Python, and when looking for a # 64-bit version of Python, since cmake.exe is a 32-bit executable, it will see # a 32-bit view of the registry. As such, it is impossible for FindPythonLibs to # locate 64-bit Python libraries. # This function is designed to address those limitations. Currently it only partially # addresses them, but it can be improved and extended on an as-needed basis. function(find_python_libs_windows) if ("${PYTHON_HOME}" STREQUAL "") message("LLDB embedded Python on Windows requires specifying a value for PYTHON_HOME. Python support disabled.") set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) return() endif() file(TO_CMAKE_PATH "${PYTHON_HOME}/Include" PYTHON_INCLUDE_DIRS) if(EXISTS "${PYTHON_INCLUDE_DIRS}/patchlevel.h") file(STRINGS "${PYTHON_INCLUDE_DIRS}/patchlevel.h" python_version_str REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") string(REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"+]+)[+]?\".*" "\\1" PYTHONLIBS_VERSION_STRING "${python_version_str}") message("-- Found Python version ${PYTHONLIBS_VERSION_STRING}") string(REGEX REPLACE "([0-9]+)[.]([0-9]+)[.][0-9]+" "python\\1\\2" PYTHONLIBS_BASE_NAME "${PYTHONLIBS_VERSION_STRING}") unset(python_version_str) else() message("Unable to find ${PYTHON_INCLUDE_DIRS}/patchlevel.h, Python installation is corrupt.") message("Python support will be disabled for this build.") set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) return() endif() file(TO_CMAKE_PATH "${PYTHON_HOME}" PYTHON_HOME) file(TO_CMAKE_PATH "${PYTHON_HOME}/python_d.exe" PYTHON_DEBUG_EXE) file(TO_CMAKE_PATH "${PYTHON_HOME}/libs/${PYTHONLIBS_BASE_NAME}_d.lib" PYTHON_DEBUG_LIB) file(TO_CMAKE_PATH "${PYTHON_HOME}/${PYTHONLIBS_BASE_NAME}_d.dll" PYTHON_DEBUG_DLL) file(TO_CMAKE_PATH "${PYTHON_HOME}/python.exe" PYTHON_RELEASE_EXE) file(TO_CMAKE_PATH "${PYTHON_HOME}/libs/${PYTHONLIBS_BASE_NAME}.lib" PYTHON_RELEASE_LIB) file(TO_CMAKE_PATH "${PYTHON_HOME}/${PYTHONLIBS_BASE_NAME}.dll" PYTHON_RELEASE_DLL) if (NOT EXISTS ${PYTHON_DEBUG_EXE}) message("Unable to find ${PYTHON_DEBUG_EXE}") unset(PYTHON_DEBUG_EXE) endif() if (NOT EXISTS ${PYTHON_RELEASE_EXE}) message("Unable to find ${PYTHON_RELEASE_EXE}") unset(PYTHON_RELEASE_EXE) endif() if (NOT EXISTS ${PYTHON_DEBUG_LIB}) message("Unable to find ${PYTHON_DEBUG_LIB}") unset(PYTHON_DEBUG_LIB) endif() if (NOT EXISTS ${PYTHON_RELEASE_LIB}) message("Unable to find ${PYTHON_RELEASE_LIB}") unset(PYTHON_RELEASE_LIB) endif() if (NOT EXISTS ${PYTHON_DEBUG_DLL}) message("Unable to find ${PYTHON_DEBUG_DLL}") unset(PYTHON_DEBUG_DLL) endif() if (NOT EXISTS ${PYTHON_RELEASE_DLL}) message("Unable to find ${PYTHON_RELEASE_DLL}") unset(PYTHON_RELEASE_DLL) endif() if (NOT (PYTHON_DEBUG_EXE AND PYTHON_RELEASE_EXE AND PYTHON_DEBUG_LIB AND PYTHON_RELEASE_LIB AND PYTHON_DEBUG_DLL AND PYTHON_RELEASE_DLL)) message("Python installation is corrupt. Python support will be disabled for this build.") set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) return() endif() # Generator expressions are evaluated in the context of each build configuration generated # by CMake. Here we use the $:VALUE logical generator expression to ensure # that the debug Python library, DLL, and executable are used in the Debug build configuration. # # Generator expressions can be difficult to grok at first so here's a breakdown of the one # used for PYTHON_LIBRARY: # # 1. $ evaluates to 1 when the Debug configuration is being generated, # or 0 in all other cases. # 2. $<$:${PYTHON_DEBUG_LIB}> expands to ${PYTHON_DEBUG_LIB} when the Debug # configuration is being generated, or nothing (literally) in all other cases. # 3. $<$>:${PYTHON_RELEASE_LIB}> expands to ${PYTHON_RELEASE_LIB} when # any configuration other than Debug is being generated, or nothing in all other cases. # 4. The conditionals in 2 & 3 are mutually exclusive. # 5. A logical expression with a conditional that evaluates to 0 yields no value at all. # # Due to 4 & 5 it's possible to concatenate 2 & 3 to obtain a single value specific to each # build configuration. In this example the value will be ${PYTHON_DEBUG_LIB} when generating the # Debug configuration, or ${PYTHON_RELEASE_LIB} when generating any other configuration. # Note that it's imperative that there is no whitespace between the two expressions, otherwise # CMake will insert a semicolon between the two. set (PYTHON_EXECUTABLE $<$:${PYTHON_DEBUG_EXE}>$<$>:${PYTHON_RELEASE_EXE}>) set (PYTHON_LIBRARY $<$:${PYTHON_DEBUG_LIB}>$<$>:${PYTHON_RELEASE_LIB}>) set (PYTHON_DLL $<$:${PYTHON_DEBUG_DLL}>$<$>:${PYTHON_RELEASE_DLL}>) set (PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} PARENT_SCOPE) set (PYTHON_LIBRARY ${PYTHON_LIBRARY} PARENT_SCOPE) set (PYTHON_DLL ${PYTHON_DLL} PARENT_SCOPE) set (PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE) message("-- LLDB Found PythonExecutable: ${PYTHON_RELEASE_EXE} and ${PYTHON_DEBUG_EXE}") message("-- LLDB Found PythonLibs: ${PYTHON_RELEASE_LIB} and ${PYTHON_DEBUG_LIB}") message("-- LLDB Found PythonDLL: ${PYTHON_RELEASE_DLL} and ${PYTHON_DEBUG_DLL}") message("-- LLDB Found PythonIncludeDirs: ${PYTHON_INCLUDE_DIRS}") endfunction(find_python_libs_windows) if (NOT LLDB_DISABLE_PYTHON) if ("${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() # Use the Unicode (UTF-16) APIs by default on Win32 if (CMAKE_SYSTEM_NAME MATCHES "Windows") add_definitions( -D_UNICODE -DUNICODE ) 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}") include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include ) if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) install(DIRECTORY include/ COMPONENT lldb_headers DESTINATION include FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN ".cmake" EXCLUDE PATTERN "Config.h" EXCLUDE ) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/ COMPONENT lldb_headers DESTINATION include FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN ".cmake" 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") set(LLDB_FRAMEWORK_INSTALL_DIR Library/Frameworks CACHE STRING "Output directory for LLDB.framework") set(LLDB_FRAMEWORK_VERSION A CACHE STRING "LLDB.framework version (default is A)") set(LLDB_FRAMEWORK_RESOURCE_DIR LLDB.framework/Versions/${LLDB_FRAMEWORK_VERSION}/Resources) 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 (CMAKE_SYSTEM_NAME MATCHES "Linux") # Check for syscall used by lldb-server on linux. # If these are not found, it will fall back to ptrace (slow) for memory reads. check_cxx_source_compiles(" #include int main() { process_vm_readv(0, nullptr, 0, nullptr, 0, 0); return 0; }" HAVE_PROCESS_VM_READV) if (HAVE_PROCESS_VM_READV) add_definitions(-DHAVE_PROCESS_VM_READV) else() # If we don't have the syscall wrapper function, but we know the syscall number, we can # still issue the syscall manually check_cxx_source_compiles(" #include int main() { return __NR_process_vm_readv; }" HAVE_NR_PROCESS_VM_READV) if (HAVE_NR_PROCESS_VM_READV) add_definitions(-DHAVE_NR_PROCESS_VM_READV) endif() endif() endif() # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|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 () check_cxx_symbol_exists("__GLIBCXX__" "string" LLDB_USING_LIBSTDCXX) if(LLDB_USING_LIBSTDCXX) # There doesn't seem to be an easy way to check the library version. Instead, we rely on the # fact that std::set did not have the allocator constructor available until version 4.9 check_cxx_source_compiles(" #include std::set s = std::set(std::allocator()); int main() { return 0; }" LLDB_USING_LIBSTDCXX_4_9) if (NOT LLDB_USING_LIBSTDCXX_4_9 AND NOT LLVM_ENABLE_EH) message(WARNING "You appear to be linking to libstdc++ version lesser than 4.9 without exceptions " "enabled. These versions of the library have an issue, which causes occasional " "lldb crashes. See for " "details. Possible courses of action are:\n" "- use libstdc++ version 4.9 or newer\n" "- use libc++ (via LLVM_ENABLE_LIBCXX)\n" "- enable exceptions (via LLVM_ENABLE_EH)\n" "- ignore this warning and accept occasional instability") endif() endif() if(MSVC) set(LLDB_USE_BUILTIN_DEMANGLER ON) else() option(LLDB_USE_BUILTIN_DEMANGLER "Use lldb's builtin demangler instead of the system one" ON) endif() if(LLDB_USE_BUILTIN_DEMANGLER) add_definitions(-DLLDB_USE_BUILTIN_DEMANGLER) endif() if ((CMAKE_SYSTEM_NAME MATCHES "Android") AND LLVM_BUILD_STATIC AND ((ANDROID_ABI MATCHES "armeabi") OR (ANDROID_ABI MATCHES "mips"))) add_definitions(-DANDROID_USE_ACCEPT_WORKAROUND) endif() find_package(Backtrace) include(CheckIncludeFile) check_include_file(termios.h HAVE_TERMIOS_H) check_include_file(sys/event.h HAVE_SYS_EVENT_H) # These checks exist in LLVM's configuration, so I want to match the LLVM names # so that the check isn't duplicated, but we translate them into the LLDB names # so that I don't have to change all the uses at the moment. set(LLDB_CONFIG_TERMIOS_SUPPORTED ${HAVE_TERMIOS_H}) if(NOT UNIX) set(LLDB_DISABLE_POSIX 1) endif() # This should be done at the end configure_file( ${LLDB_INCLUDE_ROOT}/lldb/Host/Config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/lldb/Host/Config.h) Index: vendor/lldb/dist/cmake/platforms/Android.cmake =================================================================== --- vendor/lldb/dist/cmake/platforms/Android.cmake (revision 317454) +++ vendor/lldb/dist/cmake/platforms/Android.cmake (nonexistent) @@ -1,155 +0,0 @@ -# Toolchain config for Android standalone NDK. -# -# Usage: -# build host llvm and clang first -# cmake -DCMAKE_TOOLCHAIN_FILE=../lldb/cmake/platforms/Android.cmake \ -# -DANDROID_TOOLCHAIN_DIR= \ -# -DANDROID_ABI= \ -# -DCMAKE_CXX_COMPILER_VERSION= \ -# -DLLVM_TARGET_ARCH= \ -# -DLLVM_TARGETS_TO_BUILD= \ -# -DLLVM_TABLEGEN= \ -# -DCLANG_TABLEGEN= -# -# Current Support: -# ANDROID_ABI = x86, x86_64 -# CMAKE_CXX_COMPILER_VERSION = 4.9 -# LLVM_TARGET_ARCH = X86 -# LLVM_TARGETS_TO_BUILD = X86 -# LLVM_TABLEGEN = path to host llvm-tblgen -# CLANG_TABLEGEN = path to host clang-tblgen - -if( DEFINED CMAKE_CROSSCOMPILING ) - return() -endif() - -get_property( IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) -if( IS_IN_TRY_COMPILE ) - # this seems necessary and works fine but I'm unsure if it breaks anything - return() -endif() - -set( CMAKE_SYSTEM_NAME Android ) -include( CMakeForceCompiler ) - -# flags and definitions -add_definitions( -DANDROID -DLLDB_DISABLE_LIBEDIT ) -set( ANDROID 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" ) -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() -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 ) Index: vendor/lldb/dist/examples/python/disassembly_mode.py =================================================================== --- vendor/lldb/dist/examples/python/disassembly_mode.py (nonexistent) +++ vendor/lldb/dist/examples/python/disassembly_mode.py (revision 317455) @@ -0,0 +1,48 @@ +""" Adds the 'toggle-disassembly' command to switch you into a disassembly only mode """ +import lldb + +class DisassemblyMode: + def __init__(self, debugger, unused): + self.dbg = debugger + self.interp = debugger.GetCommandInterpreter() + self.store_state() + self.mode_off = True + + def store_state(self): + self.dis_count = self.get_string_value("stop-disassembly-count") + self.dis_display = self.get_string_value("stop-disassembly-display") + self.before_count = self.get_string_value("stop-line-count-before") + self.after_count = self.get_string_value("stop-line-count-after") + + def get_string_value(self, setting): + result = lldb.SBCommandReturnObject() + self.interp.HandleCommand("settings show " + setting, result) + value = result.GetOutput().split(" = ")[1].rstrip("\n") + return value + + def set_value(self, setting, value): + result = lldb.SBCommandReturnObject() + self.interp.HandleCommand("settings set " + setting + " " + value, result) + + def __call__(self, debugger, command, exe_ctx, result): + if self.mode_off: + self.mode_off = False + self.store_state() + self.set_value("stop-disassembly-display","always") + self.set_value("stop-disassembly-count", "8") + self.set_value("stop-line-count-before", "0") + self.set_value("stop-line-count-after", "0") + result.AppendMessage("Disassembly mode on.") + else: + self.mode_off = True + self.set_value("stop-disassembly-display",self.dis_display) + self.set_value("stop-disassembly-count", self.dis_count) + self.set_value("stop-line-count-before", self.before_count) + self.set_value("stop-line-count-after", self.after_count) + result.AppendMessage("Disassembly mode off.") + + def get_short_help(self): + return "Toggles between a disassembly only mode and normal source mode\n" + +def __lldb_init_module(debugger, unused): + debugger.HandleCommand("command script add -c disassembly_mode.DisassemblyMode toggle-disassembly") Property changes on: vendor/lldb/dist/examples/python/disassembly_mode.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/include/lldb/API/LLDB.h =================================================================== --- vendor/lldb/dist/include/lldb/API/LLDB.h (revision 317454) +++ vendor/lldb/dist/include/lldb/API/LLDB.h (revision 317455) @@ -1,80 +1,82 @@ //===-- LLDB.h --------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_LLDB_h_ #define LLDB_LLDB_h_ // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/API/SBAddress.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBlock.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBCompileUnit.h" #include "lldb/API/SBData.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFileSpecList.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBFunction.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBPlatform.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbol.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/API/SBSymbolContextList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBType.h" #include "lldb/API/SBTypeCategory.h" #include "lldb/API/SBTypeEnumMember.h" #include "lldb/API/SBTypeFilter.h" #include "lldb/API/SBTypeFormat.h" #include "lldb/API/SBTypeNameSpecifier.h" #include "lldb/API/SBTypeSummary.h" #include "lldb/API/SBTypeSynthetic.h" #include "lldb/API/SBUnixSignals.h" #include "lldb/API/SBValue.h" #include "lldb/API/SBValueList.h" #include "lldb/API/SBVariablesOptions.h" #include "lldb/API/SBWatchpoint.h" #endif // LLDB_LLDB_h_ Index: vendor/lldb/dist/include/lldb/API/SBDefines.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBDefines.h (revision 317454) +++ vendor/lldb/dist/include/lldb/API/SBDefines.h (revision 317455) @@ -1,101 +1,103 @@ //===-- SBDefines.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBDefines_h_ #define LLDB_SBDefines_h_ // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" #include "lldb/lldb-versioning.h" #ifdef SWIG #define LLDB_API #endif // Forward Declarations namespace lldb { class LLDB_API SBAddress; class LLDB_API SBBlock; class LLDB_API SBBreakpoint; class LLDB_API SBBreakpointLocation; class LLDB_API SBBroadcaster; class LLDB_API SBCommand; class LLDB_API SBCommandInterpreter; class LLDB_API SBCommandInterpreterRunOptions; class LLDB_API SBCommandPluginInterface; class LLDB_API SBCommandReturnObject; class LLDB_API SBCommunication; class LLDB_API SBCompileUnit; class LLDB_API SBData; class LLDB_API SBDebugger; class LLDB_API SBDeclaration; class LLDB_API SBError; class LLDB_API SBEvent; class LLDB_API SBEventList; class LLDB_API SBExecutionContext; class LLDB_API SBExpressionOptions; class LLDB_API SBFileSpec; class LLDB_API SBFileSpecList; class LLDB_API SBFrame; class LLDB_API SBFunction; class LLDB_API SBHostOS; class LLDB_API SBInstruction; class LLDB_API SBInstructionList; class LLDB_API SBLanguageRuntime; class LLDB_API SBLaunchInfo; class LLDB_API SBLineEntry; class LLDB_API SBListener; class LLDB_API SBMemoryRegionInfo; class LLDB_API SBMemoryRegionInfoList; class LLDB_API SBModule; class LLDB_API SBModuleSpec; class LLDB_API SBModuleSpecList; class LLDB_API SBProcess; class LLDB_API SBQueue; class LLDB_API SBQueueItem; class LLDB_API SBSection; class LLDB_API SBSourceManager; class LLDB_API SBStream; class LLDB_API SBStringList; class LLDB_API SBStructuredData; class LLDB_API SBSymbol; class LLDB_API SBSymbolContext; class LLDB_API SBSymbolContextList; class LLDB_API SBTarget; class LLDB_API SBThread; class LLDB_API SBThreadCollection; class LLDB_API SBThreadPlan; +class LLDB_API SBTrace; +class LLDB_API SBTraceOptions; class LLDB_API SBType; class LLDB_API SBTypeCategory; class LLDB_API SBTypeEnumMember; class LLDB_API SBTypeEnumMemberList; class LLDB_API SBTypeFilter; class LLDB_API SBTypeFormat; class LLDB_API SBTypeMemberFunction; class LLDB_API SBTypeNameSpecifier; class LLDB_API SBTypeSummary; class LLDB_API SBTypeSummaryOptions; class LLDB_API SBTypeSynthetic; class LLDB_API SBTypeList; class LLDB_API SBValue; class LLDB_API SBValueList; class LLDB_API SBVariablesOptions; class LLDB_API SBWatchpoint; class LLDB_API SBUnixSignals; } #endif // LLDB_SBDefines_h_ Index: vendor/lldb/dist/include/lldb/API/SBError.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBError.h (revision 317454) +++ vendor/lldb/dist/include/lldb/API/SBError.h (revision 317455) @@ -1,88 +1,89 @@ //===-- SBError.h -----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBError_h_ #define LLDB_SBError_h_ #include "lldb/API/SBDefines.h" namespace lldb { class LLDB_API SBError { public: SBError(); SBError(const lldb::SBError &rhs); ~SBError(); const SBError &operator=(const lldb::SBError &rhs); const char *GetCString() const; void Clear(); bool Fail() const; bool Success() const; uint32_t GetError() const; lldb::ErrorType GetType() const; void SetError(uint32_t err, lldb::ErrorType type); void SetErrorToErrno(); void SetErrorToGenericError(); void SetErrorString(const char *err_str); int SetErrorStringWithFormat(const char *format, ...) __attribute__((format(printf, 2, 3))); bool IsValid() const; bool GetDescription(lldb::SBStream &description); protected: friend class SBCommandReturnObject; friend class SBData; friend class SBDebugger; friend class SBCommunication; friend class SBHostOS; friend class SBPlatform; friend class SBProcess; friend class SBStructuredData; friend class SBThread; + friend class SBTrace; friend class SBTarget; friend class SBValue; friend class SBWatchpoint; friend class SBBreakpoint; friend class SBBreakpointLocation; lldb_private::Error *get(); lldb_private::Error *operator->(); const lldb_private::Error &operator*() const; lldb_private::Error &ref(); void SetError(const lldb_private::Error &lldb_error); private: std::unique_ptr m_opaque_ap; void CreateIfNeeded(); }; } // namespace lldb #endif // LLDB_SBError_h_ Index: vendor/lldb/dist/include/lldb/API/SBProcess.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBProcess.h (revision 317454) +++ vendor/lldb/dist/include/lldb/API/SBProcess.h (revision 317455) @@ -1,379 +1,406 @@ //===-- SBProcess.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBProcess_h_ #define LLDB_SBProcess_h_ #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBTarget.h" #include namespace lldb { class SBEvent; class LLDB_API SBProcess { public: //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ FLAGS_ANONYMOUS_ENUM(){eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5)}; SBProcess(); SBProcess(const lldb::SBProcess &rhs); const lldb::SBProcess &operator=(const lldb::SBProcess &rhs); SBProcess(const lldb::ProcessSP &process_sp); ~SBProcess(); static const char *GetBroadcasterClassName(); const char *GetPluginName(); // DEPRECATED: use GetPluginName() const char *GetShortPluginName(); void Clear(); bool IsValid() const; lldb::SBTarget GetTarget() const; lldb::ByteOrder GetByteOrder() const; size_t PutSTDIN(const char *src, size_t src_len); size_t GetSTDOUT(char *dst, size_t dst_len) const; size_t GetSTDERR(char *dst, size_t dst_len) const; size_t GetAsyncProfileData(char *dst, size_t dst_len) const; void ReportEventState(const lldb::SBEvent &event, FILE *out) const; void AppendEventStateReport(const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); //------------------------------------------------------------------ /// Remote connection related functions. These will fail if the /// process is not in eStateConnected. They are intended for use /// when connecting to an externally managed debugserver instance. //------------------------------------------------------------------ bool RemoteAttachToProcessWithID(lldb::pid_t pid, lldb::SBError &error); bool RemoteLaunch(char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError &error); //------------------------------------------------------------------ // Thread related functions //------------------------------------------------------------------ uint32_t GetNumThreads(); lldb::SBThread GetThreadAtIndex(size_t index); lldb::SBThread GetThreadByID(lldb::tid_t sb_thread_id); lldb::SBThread GetThreadByIndexID(uint32_t index_id); lldb::SBThread GetSelectedThread() const; //------------------------------------------------------------------ // Function for lazily creating a thread using the current OS // plug-in. This function will be removed in the future when there // are APIs to create SBThread objects through the interface and add // them to the process through the SBProcess API. //------------------------------------------------------------------ lldb::SBThread CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context); bool SetSelectedThread(const lldb::SBThread &thread); bool SetSelectedThreadByID(lldb::tid_t tid); bool SetSelectedThreadByIndexID(uint32_t index_id); //------------------------------------------------------------------ // Queue related functions //------------------------------------------------------------------ uint32_t GetNumQueues(); lldb::SBQueue GetQueueAtIndex(size_t index); //------------------------------------------------------------------ // Stepping related functions //------------------------------------------------------------------ lldb::StateType GetState(); int GetExitStatus(); const char *GetExitDescription(); //------------------------------------------------------------------ /// Gets the process ID /// /// Returns the process identifier for the process as it is known /// on the system on which the process is running. For unix systems /// this is typically the same as if you called "getpid()" in the /// process. /// /// @return /// Returns LLDB_INVALID_PROCESS_ID if this object does not /// contain a valid process object, or if the process has not /// been launched. Returns a valid process ID if the process is /// valid. //------------------------------------------------------------------ lldb::pid_t GetProcessID(); //------------------------------------------------------------------ /// Gets the unique ID associated with this process object /// /// Unique IDs start at 1 and increment up with each new process /// instance. Since starting a process on a system might always /// create a process with the same process ID, there needs to be a /// way to tell two process instances apart. /// /// @return /// Returns a non-zero integer ID if this object contains a /// valid process object, zero if this object does not contain /// a valid process object. //------------------------------------------------------------------ uint32_t GetUniqueID(); uint32_t GetAddressByteSize() const; lldb::SBError Destroy(); lldb::SBError Continue(); lldb::SBError Stop(); lldb::SBError Kill(); lldb::SBError Detach(); lldb::SBError Detach(bool keep_stopped); lldb::SBError Signal(int signal); lldb::SBUnixSignals GetUnixSignals(); void SendAsyncInterrupt(); uint32_t GetStopID(bool include_expression_stops = false); //------------------------------------------------------------------ /// Gets the stop event corresponding to stop ID. // /// Note that it wasn't fully implemented and tracks only the stop /// event for the last natural stop ID. /// /// @param [in] stop_id /// The ID of the stop event to return. /// /// @return /// The stop event corresponding to stop ID. //------------------------------------------------------------------ lldb::SBEvent GetStopEventForStopID(uint32_t stop_id); size_t ReadMemory(addr_t addr, void *buf, size_t size, lldb::SBError &error); size_t WriteMemory(addr_t addr, const void *buf, size_t size, lldb::SBError &error); size_t ReadCStringFromMemory(addr_t addr, void *buf, size_t size, lldb::SBError &error); uint64_t ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size, lldb::SBError &error); lldb::addr_t ReadPointerFromMemory(addr_t addr, lldb::SBError &error); // Events static lldb::StateType GetStateFromEvent(const lldb::SBEvent &event); static bool GetRestartedFromEvent(const lldb::SBEvent &event); static size_t GetNumRestartedReasonsFromEvent(const lldb::SBEvent &event); static const char * GetRestartedReasonAtIndexFromEvent(const lldb::SBEvent &event, size_t idx); static lldb::SBProcess GetProcessFromEvent(const lldb::SBEvent &event); static bool GetInterruptedFromEvent(const lldb::SBEvent &event); static lldb::SBStructuredData GetStructuredDataFromEvent(const lldb::SBEvent &event); static bool EventIsProcessEvent(const lldb::SBEvent &event); static bool EventIsStructuredDataEvent(const lldb::SBEvent &event); lldb::SBBroadcaster GetBroadcaster() const; static const char *GetBroadcasterClass(); bool GetDescription(lldb::SBStream &description); + //------------------------------------------------------------------ + /// Start Tracing with the given SBTraceOptions. + /// + /// @param[in] options + /// Class containing trace options like trace buffer size, meta + /// data buffer size, TraceType and any custom parameters + /// {formatted as a JSON Dictionary}. In case of errors in + /// formatting, an error would be reported. + /// It must be noted that tracing options such as buffer sizes + /// or other custom parameters passed maybe invalid for some + /// trace technologies. In such cases the trace implementations + /// could choose to either throw an error or could round off to + /// the nearest valid options to start tracing if the passed + /// value is not supported. To obtain the actual used trace + /// options please use the GetTraceConfig API. For the custom + /// parameters, only the parameters recognized by the target + /// would be used and others would be ignored. + /// + /// @param[out] error + /// An error explaining what went wrong. + /// + /// @return + /// A SBTrace instance, which should be used + /// to get the trace data or other trace related operations. + //------------------------------------------------------------------ + lldb::SBTrace StartTrace(SBTraceOptions &options, lldb::SBError &error); + uint32_t GetNumSupportedHardwareWatchpoints(lldb::SBError &error) const; //------------------------------------------------------------------ /// Load a shared library into this process. /// /// @param[in] remote_image_spec /// The path for the shared library on the target what you want /// to load. /// /// @param[out] error /// An error object that gets filled in with any errors that /// might occur when trying to load the shared library. /// /// @return /// A token that represents the shared library that can be /// later used to unload the shared library. A value of /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared /// library can't be opened. //------------------------------------------------------------------ uint32_t LoadImage(lldb::SBFileSpec &remote_image_spec, lldb::SBError &error); //------------------------------------------------------------------ /// Load a shared library into this process. /// /// @param[in] local_image_spec /// The file spec that points to the shared library that you /// want to load if the library is located on the host. The /// library will be copied over to the location specified by /// remote_image_spec or into the current working directory with /// the same filename if the remote_image_spec isn't specified. /// /// @param[in] remote_image_spec /// If local_image_spec is specified then the location where the /// library should be copied over from the host. If /// local_image_spec isn't specified, then the path for the /// shared library on the target what you want to load. /// /// @param[out] error /// An error object that gets filled in with any errors that /// might occur when trying to load the shared library. /// /// @return /// A token that represents the shared library that can be /// later used to unload the shared library. A value of /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared /// library can't be opened. //------------------------------------------------------------------ uint32_t LoadImage(const lldb::SBFileSpec &local_image_spec, const lldb::SBFileSpec &remote_image_spec, lldb::SBError &error); lldb::SBError UnloadImage(uint32_t image_token); lldb::SBError SendEventData(const char *data); //------------------------------------------------------------------ /// Return the number of different thread-origin extended backtraces /// this process can support. /// /// When the process is stopped and you have an SBThread, lldb may be /// able to show a backtrace of when that thread was originally created, /// or the work item was enqueued to it (in the case of a libdispatch /// queue). /// /// @return /// The number of thread-origin extended backtrace types that may be /// available. //------------------------------------------------------------------ uint32_t GetNumExtendedBacktraceTypes(); //------------------------------------------------------------------ /// Return the name of one of the thread-origin extended backtrace /// methods. /// /// @param [in] idx /// The index of the name to return. They will be returned in /// the order that the user will most likely want to see them. /// e.g. if the type at index 0 is not available for a thread, /// see if the type at index 1 provides an extended backtrace. /// /// @return /// The name at that index. //------------------------------------------------------------------ const char *GetExtendedBacktraceTypeAtIndex(uint32_t idx); lldb::SBThreadCollection GetHistoryThreads(addr_t addr); bool IsInstrumentationRuntimePresent(InstrumentationRuntimeType type); // Save the state of the process in a core file (or mini dump on Windows). lldb::SBError SaveCore(const char *file_name); //------------------------------------------------------------------ /// Query the address load_addr and store the details of the memory /// region that contains it in the supplied SBMemoryRegionInfo object. /// To iterate over all memory regions use GetMemoryRegionList. /// /// @param[in] load_addr /// The address to be queried. /// /// @param[out] region_info /// A reference to an SBMemoryRegionInfo object that will contain /// the details of the memory region containing load_addr. /// /// @return /// An error object describes any errors that occurred while /// querying load_addr. //------------------------------------------------------------------ lldb::SBError GetMemoryRegionInfo(lldb::addr_t load_addr, lldb::SBMemoryRegionInfo ®ion_info); //------------------------------------------------------------------ /// Return the list of memory regions within the process. /// /// @return /// A list of all witin the process memory regions. //------------------------------------------------------------------ lldb::SBMemoryRegionInfoList GetMemoryRegions(); protected: friend class SBAddress; friend class SBBreakpoint; friend class SBBreakpointLocation; friend class SBCommandInterpreter; friend class SBDebugger; friend class SBExecutionContext; friend class SBFunction; friend class SBModule; friend class SBTarget; friend class SBThread; friend class SBValue; friend class lldb_private::QueueImpl; lldb::ProcessSP GetSP() const; void SetSP(const lldb::ProcessSP &process_sp); lldb::ProcessWP m_opaque_wp; }; } // namespace lldb #endif // LLDB_SBProcess_h_ Index: vendor/lldb/dist/include/lldb/API/SBStructuredData.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBStructuredData.h (revision 317454) +++ vendor/lldb/dist/include/lldb/API/SBStructuredData.h (revision 317455) @@ -1,45 +1,47 @@ //===-- SBStructuredData.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SBStructuredData_h #define SBStructuredData_h #include "lldb/API/SBDefines.h" #include "lldb/API/SBModule.h" -class StructuredDataImpl; - namespace lldb { class SBStructuredData { public: SBStructuredData(); SBStructuredData(const lldb::SBStructuredData &rhs); SBStructuredData(const lldb::EventSP &event_sp); ~SBStructuredData(); lldb::SBStructuredData &operator=(const lldb::SBStructuredData &rhs); bool IsValid() const; + lldb::SBError SetFromJSON(lldb::SBStream &stream); + void Clear(); lldb::SBError GetAsJSON(lldb::SBStream &stream) const; lldb::SBError GetDescription(lldb::SBStream &stream) const; -private: - std::unique_ptr m_impl_up; +protected: + friend class SBTraceOptions; + + StructuredDataImplUP m_impl_up; }; } #endif /* SBStructuredData_h */ Index: vendor/lldb/dist/include/lldb/API/SBTrace.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBTrace.h (nonexistent) +++ vendor/lldb/dist/include/lldb/API/SBTrace.h (revision 317455) @@ -0,0 +1,122 @@ +//===-- SBTrace.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTrace_h_ +#define LLDB_SBTrace_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" + +class TraceImpl; + +namespace lldb { + +class LLDB_API SBTrace { +public: + SBTrace(); + //------------------------------------------------------------------ + /// Obtain the trace data as raw bytes. + /// + /// @param[out] error + /// An error explaining what went wrong. + /// + /// @param[in] buf + /// Buffer to write the trace data to. + /// + /// @param[in] size + /// The size of the buffer used to read the data. This is + /// also the size of the data intended to read. It is also + /// possible to partially read the trace data for some trace + /// technologies by specifying a smaller buffer. + /// + /// @param[in] offset + /// The start offset to begin reading the trace data. + /// + /// @param[in] thread_id + /// Tracing could be started for the complete process or a + /// single thread, in the first case the uid obtained would + /// map to all the threads existing within the process and the + /// ones spawning later. The thread_id parameter can be used in + /// such a scenario to select the trace data for a specific + /// thread. + /// + /// @return + /// The size of the trace data effectively read by the API call. + //------------------------------------------------------------------ + size_t GetTraceData(SBError &error, void *buf, size_t size, size_t offset = 0, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); + + //------------------------------------------------------------------ + /// Obtain any meta data as raw bytes for the tracing instance. + /// The input parameter definition is similar to the previous + /// function. + //------------------------------------------------------------------ + size_t GetMetaData(SBError &error, void *buf, size_t size, size_t offset = 0, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); + + //------------------------------------------------------------------ + /// Stop the tracing instance. Stopping the trace will also + /// lead to deletion of any gathered trace data. + /// + /// @param[out] error + /// An error explaining what went wrong. + /// + /// @param[in] thread_id + /// The user id could map to a tracing instance for a thread + /// or could also map to a group of threads being traced with + /// the same trace options. A thread_id is normally optional + /// except in the case of tracing a complete process and tracing + /// needs to switched off on a particular thread. + /// A situation could occur where initially a thread (lets say + /// thread A) is being individually traced with a particular uid + /// and then tracing is started on the complete process, in this + /// case thread A will continue without any change. All newly + /// spawned threads would be traced with the uid of the process. + /// Now if the StopTrace API is called for the whole process, + /// thread A will not be stopped and must be stopped separately. + //------------------------------------------------------------------ + void StopTrace(SBError &error, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); + + //------------------------------------------------------------------ + /// Get the trace configuration being used for the trace instance. + /// The threadid in the SBTraceOptions needs to be set when the + /// configuration used by a specific thread is being requested. + /// + /// @param[out] options + /// The trace options actually used by the trace instance + /// would be filled by the API. + /// + /// @param[out] error + /// An error explaining what went wrong. + //------------------------------------------------------------------ + void GetTraceConfig(SBTraceOptions &options, SBError &error); + + lldb::user_id_t GetTraceUID(); + + bool IsValid(); + +protected: + typedef std::shared_ptr TraceImplSP; + + friend class SBProcess; + + void SetTraceUID(lldb::user_id_t uid); + + TraceImplSP m_trace_impl_sp; + + lldb::ProcessSP GetSP() const; + + void SetSP(const ProcessSP &process_sp); + + lldb::ProcessWP m_opaque_wp; +}; +} // namespace lldb + +#endif // LLDB_SBTrace_h_ Property changes on: vendor/lldb/dist/include/lldb/API/SBTrace.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/include/lldb/API/SBTraceOptions.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBTraceOptions.h (nonexistent) +++ vendor/lldb/dist/include/lldb/API/SBTraceOptions.h (revision 317455) @@ -0,0 +1,58 @@ +//===-- SBTraceOptions ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SBTRACEOPTIONS_H_ +#define SBTRACEOPTIONS_H_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class LLDB_API SBTraceOptions { +public: + SBTraceOptions(); + + lldb::TraceType getType() const; + + uint64_t getTraceBufferSize() const; + + /// The trace parameters consist of any custom parameters + /// apart from the generic parameters such as + /// TraceType, trace_buffer_size and meta_data_buffer_size. + /// The returned parameters would be formatted as a JSON Dictionary. + lldb::SBStructuredData getTraceParams(lldb::SBError &error); + + uint64_t getMetaDataBufferSize() const; + + /// SBStructuredData is meant to hold any custom parameters + /// apart from meta buffer size and trace size. They should + /// be formatted as a JSON Dictionary. + void setTraceParams(lldb::SBStructuredData ¶ms); + + void setType(lldb::TraceType type); + + void setTraceBufferSize(uint64_t size); + + void setMetaDataBufferSize(uint64_t size); + + void setThreadID(lldb::tid_t thread_id); + + lldb::tid_t getThreadID(); + + bool IsValid(); + +protected: + friend class SBProcess; + friend class SBTrace; + + lldb::TraceOptionsSP m_traceoptions_sp; +}; +} + +#endif /* SBTRACEOPTIONS_H_ */ Property changes on: vendor/lldb/dist/include/lldb/API/SBTraceOptions.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h (nonexistent) +++ vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h (revision 317455) @@ -0,0 +1,95 @@ +//===-- StructuredDataImpl.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StructuredDataImpl_h_ +#define liblldb_StructuredDataImpl_h_ + +#include "lldb/Core/Event.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Utility/Error.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Target/StructuredDataPlugin.h" +#include "lldb/lldb-forward.h" + +#pragma mark-- +#pragma mark StructuredDataImpl + +namespace lldb_private { + +class StructuredDataImpl { +public: + StructuredDataImpl() : m_plugin_wp(), m_data_sp() {} + + StructuredDataImpl(const StructuredDataImpl &rhs) = default; + + StructuredDataImpl(const lldb::EventSP &event_sp) + : m_plugin_wp( + EventDataStructuredData::GetPluginFromEvent(event_sp.get())), + m_data_sp(EventDataStructuredData::GetObjectFromEvent(event_sp.get())) { + } + + ~StructuredDataImpl() = default; + + StructuredDataImpl &operator=(const StructuredDataImpl &rhs) = default; + + bool IsValid() const { return m_data_sp.get() != nullptr; } + + void Clear() { + m_plugin_wp.reset(); + m_data_sp.reset(); + } + + Error GetAsJSON(Stream &stream) const { + Error error; + + if (!m_data_sp) { + error.SetErrorString("No structured data."); + return error; + } + + m_data_sp->Dump(stream); + return error; + } + + Error GetDescription(Stream &stream) const { + Error error; + + if (!m_data_sp) { + error.SetErrorString("Cannot pretty print structured data: " + "no data to print."); + return error; + } + + // Grab the plugin. + auto plugin_sp = lldb::StructuredDataPluginSP(m_plugin_wp); + if (!plugin_sp) { + error.SetErrorString("Cannot pretty print structured data: " + "plugin doesn't exist."); + return error; + } + + // Get the data's description. + return plugin_sp->GetDescription(m_data_sp, stream); + } + + StructuredData::ObjectSP GetObjectSP() { + return m_data_sp; + } + + void SetObjectSP(const StructuredData::ObjectSP &obj) { + m_data_sp = obj; + } + +private: + + lldb::StructuredDataPluginWP m_plugin_wp; + StructuredData::ObjectSP m_data_sp; +}; +} +#endif Property changes on: vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/include/lldb/Core/TraceOptions.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/TraceOptions.h (nonexistent) +++ vendor/lldb/dist/include/lldb/Core/TraceOptions.h (revision 317455) @@ -0,0 +1,62 @@ +//===-- TraceOptions.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TraceOptions_h_ +#define liblldb_TraceOptions_h_ + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/StructuredData.h" + +namespace lldb_private { +class TraceOptions { +public: + TraceOptions() + : m_trace_params(new StructuredData::Dictionary()) {} + + const StructuredData::DictionarySP &getTraceParams() const { + return m_trace_params; + } + + lldb::TraceType getType() const { return m_type; } + + uint64_t getTraceBufferSize() const { return m_trace_buffer_size; } + + uint64_t getMetaDataBufferSize() const { return m_meta_data_buffer_size; } + + void setTraceParams(const StructuredData::DictionarySP &dict_obj) { + m_trace_params = dict_obj; + } + + void setType(lldb::TraceType type) { m_type = type; } + + void setTraceBufferSize(uint64_t size) { m_trace_buffer_size = size; } + + void setMetaDataBufferSize(uint64_t size) { m_meta_data_buffer_size = size; } + + void setThreadID(lldb::tid_t thread_id) { m_thread_id = thread_id; } + + lldb::tid_t getThreadID() { return m_thread_id; } + +private: + lldb::TraceType m_type; + uint64_t m_trace_buffer_size; + uint64_t m_meta_data_buffer_size; + lldb::tid_t m_thread_id; + + /// m_trace_params is meant to hold any custom parameters + /// apart from meta buffer size and trace size. + /// The interpretation of such parameters is left to + /// the lldb-server. + StructuredData::DictionarySP m_trace_params; +}; +} + +#endif // liblldb_TraceOptions_h_ \ No newline at end of file Property changes on: vendor/lldb/dist/include/lldb/Core/TraceOptions.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/include/lldb/Core/ValueObject.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/ValueObject.h (revision 317454) +++ vendor/lldb/dist/include/lldb/Core/ValueObject.h (revision 317455) @@ -1,1102 +1,1086 @@ //===-- ValueObject.h -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ValueObject_h_ #define liblldb_ValueObject_h_ #include "lldb/Core/Value.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" // for DumpValueObj... #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Type.h" // for TypeImpl #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/SharedCluster.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-defines.h" // for LLDB_INVALID... #include "lldb/lldb-enumerations.h" // for DynamicValue... #include "lldb/lldb-forward.h" // for ValueObjectSP #include "lldb/lldb-private-enumerations.h" // for AddressType #include "lldb/lldb-types.h" // for addr_t, offs... +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" // for StringRef #include #include #include #include // for recursive_mutex #include // for string #include // for pair -#include #include // for size_t #include // for uint32_t namespace lldb_private { class Declaration; } namespace lldb_private { class EvaluateExpressionOptions; } namespace lldb_private { class ExecutionContextScope; } namespace lldb_private { class Log; } namespace lldb_private { class Scalar; } namespace lldb_private { class Stream; } namespace lldb_private { class SymbolContextScope; } namespace lldb_private { class TypeFormatImpl; } namespace lldb_private { class TypeSummaryImpl; } namespace lldb_private { class TypeSummaryOptions; } namespace lldb_private { /// ValueObject: /// /// This abstract class provides an interface to a particular value, be it a /// register, a local or global variable, /// that is evaluated in some particular scope. The ValueObject also has the /// capability of being the "child" of /// some other variable object, and in turn of having children. /// If a ValueObject is a root variable object - having no parent - then it must /// be constructed with respect to some /// particular ExecutionContextScope. If it is a child, it inherits the /// ExecutionContextScope from its parent. /// The ValueObject will update itself if necessary before fetching its value, /// summary, object description, etc. /// But it will always update itself in the ExecutionContextScope with which it /// was originally created. /// A brief note on life cycle management for ValueObjects. This is a little /// tricky because a ValueObject can contain /// various other ValueObjects - the Dynamic Value, its children, the /// dereference value, etc. Any one of these can be /// handed out as a shared pointer, but for that contained value object to be /// valid, the root object and potentially other /// of the value objects need to stay around. /// We solve this problem by handing out shared pointers to the Value Object and /// any of its dependents using a shared /// ClusterManager. This treats each shared pointer handed out for the entire /// cluster as a reference to the whole /// cluster. The whole cluster will stay around until the last reference is /// released. /// /// The ValueObject mostly handle this automatically, if a value object is made /// with a Parent ValueObject, then it adds /// itself to the ClusterManager of the parent. /// It does mean that external to the ValueObjects we should only ever make /// available ValueObjectSP's, never ValueObjects /// or pointers to them. So all the "Root level" ValueObject derived /// constructors should be private, and /// should implement a Create function that new's up object and returns a Shared /// Pointer that it gets from the GetSP() method. /// /// However, if you are making an derived ValueObject that will be contained in /// a parent value object, you should just /// hold onto a pointer to it internally, and by virtue of passing the parent /// ValueObject into its constructor, it will /// be added to the ClusterManager for the parent. Then if you ever hand out a /// Shared Pointer to the contained ValueObject, /// just do so by calling GetSP() on the contained object. class ValueObject : public UserID { public: enum GetExpressionPathFormat { eGetExpressionPathFormatDereferencePointers = 1, eGetExpressionPathFormatHonorPointers }; enum ValueObjectRepresentationStyle { eValueObjectRepresentationStyleValue = 1, eValueObjectRepresentationStyleSummary, eValueObjectRepresentationStyleLanguageSpecific, eValueObjectRepresentationStyleLocation, eValueObjectRepresentationStyleChildrenCount, eValueObjectRepresentationStyleType, eValueObjectRepresentationStyleName, eValueObjectRepresentationStyleExpressionPath }; enum ExpressionPathScanEndReason { eExpressionPathScanEndReasonEndOfString = 1, // out of data to parse eExpressionPathScanEndReasonNoSuchChild, // child element not found eExpressionPathScanEndReasonNoSuchSyntheticChild, // (synthetic) child // element not found eExpressionPathScanEndReasonEmptyRangeNotAllowed, // [] only allowed for // arrays eExpressionPathScanEndReasonDotInsteadOfArrow, // . used when -> should be // used eExpressionPathScanEndReasonArrowInsteadOfDot, // -> used when . should be // used eExpressionPathScanEndReasonFragileIVarNotAllowed, // ObjC ivar expansion // not allowed eExpressionPathScanEndReasonRangeOperatorNotAllowed, // [] not allowed by // options eExpressionPathScanEndReasonRangeOperatorInvalid, // [] not valid on objects // other than scalars, // pointers or arrays eExpressionPathScanEndReasonArrayRangeOperatorMet, // [] is good for arrays, // but I cannot parse it eExpressionPathScanEndReasonBitfieldRangeOperatorMet, // [] is good for // bitfields, but I // cannot parse after // it eExpressionPathScanEndReasonUnexpectedSymbol, // something is malformed in // the expression eExpressionPathScanEndReasonTakingAddressFailed, // impossible to apply & // operator eExpressionPathScanEndReasonDereferencingFailed, // impossible to apply * // operator eExpressionPathScanEndReasonRangeOperatorExpanded, // [] was expanded into a // VOList eExpressionPathScanEndReasonSyntheticValueMissing, // getting the synthetic // children failed eExpressionPathScanEndReasonUnknown = 0xFFFF }; enum ExpressionPathEndResultType { eExpressionPathEndResultTypePlain = 1, // anything but... eExpressionPathEndResultTypeBitfield, // a bitfield eExpressionPathEndResultTypeBoundedRange, // a range [low-high] eExpressionPathEndResultTypeUnboundedRange, // a range [] eExpressionPathEndResultTypeValueObjectList, // several items in a VOList eExpressionPathEndResultTypeInvalid = 0xFFFF }; enum ExpressionPathAftermath { eExpressionPathAftermathNothing = 1, // just return it eExpressionPathAftermathDereference, // dereference the target eExpressionPathAftermathTakeAddress // take target's address }; enum ClearUserVisibleDataItems { eClearUserVisibleDataItemsNothing = 1u << 0, eClearUserVisibleDataItemsValue = 1u << 1, eClearUserVisibleDataItemsSummary = 1u << 2, eClearUserVisibleDataItemsLocation = 1u << 3, eClearUserVisibleDataItemsDescription = 1u << 4, eClearUserVisibleDataItemsSyntheticChildren = 1u << 5, eClearUserVisibleDataItemsValidator = 1u << 6, eClearUserVisibleDataItemsAllStrings = eClearUserVisibleDataItemsValue | eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsLocation | eClearUserVisibleDataItemsDescription, eClearUserVisibleDataItemsAll = 0xFFFF }; struct GetValueForExpressionPathOptions { enum class SyntheticChildrenTraversal { None, ToSynthetic, FromSynthetic, Both }; bool m_check_dot_vs_arrow_syntax; bool m_no_fragile_ivar; bool m_allow_bitfields_syntax; SyntheticChildrenTraversal m_synthetic_children_traversal; GetValueForExpressionPathOptions( bool dot = false, bool no_ivar = false, bool bitfield = true, SyntheticChildrenTraversal synth_traverse = SyntheticChildrenTraversal::ToSynthetic) : m_check_dot_vs_arrow_syntax(dot), m_no_fragile_ivar(no_ivar), m_allow_bitfields_syntax(bitfield), m_synthetic_children_traversal(synth_traverse) {} GetValueForExpressionPathOptions &DoCheckDotVsArrowSyntax() { m_check_dot_vs_arrow_syntax = true; return *this; } GetValueForExpressionPathOptions &DontCheckDotVsArrowSyntax() { m_check_dot_vs_arrow_syntax = false; return *this; } GetValueForExpressionPathOptions &DoAllowFragileIVar() { m_no_fragile_ivar = false; return *this; } GetValueForExpressionPathOptions &DontAllowFragileIVar() { m_no_fragile_ivar = true; return *this; } GetValueForExpressionPathOptions &DoAllowBitfieldSyntax() { m_allow_bitfields_syntax = true; return *this; } GetValueForExpressionPathOptions &DontAllowBitfieldSyntax() { m_allow_bitfields_syntax = false; return *this; } GetValueForExpressionPathOptions & SetSyntheticChildrenTraversal(SyntheticChildrenTraversal traverse) { m_synthetic_children_traversal = traverse; return *this; } static const GetValueForExpressionPathOptions DefaultOptions() { static GetValueForExpressionPathOptions g_default_options; return g_default_options; } }; class EvaluationPoint { public: EvaluationPoint(); EvaluationPoint(ExecutionContextScope *exe_scope, bool use_selected = false); EvaluationPoint(const EvaluationPoint &rhs); ~EvaluationPoint(); const ExecutionContextRef &GetExecutionContextRef() const { return m_exe_ctx_ref; } // Set the EvaluationPoint to the values in exe_scope, // Return true if the Evaluation Point changed. // Since the ExecutionContextScope is always going to be valid currently, // the Updated Context will also always be valid. // bool // SetContext (ExecutionContextScope *exe_scope); void SetIsConstant() { SetUpdated(); m_mod_id.SetInvalid(); } bool IsConstant() const { return !m_mod_id.IsValid(); } ProcessModID GetModID() const { return m_mod_id; } void SetUpdateID(ProcessModID new_id) { m_mod_id = new_id; } void SetNeedsUpdate() { m_needs_update = true; } void SetUpdated(); bool NeedsUpdating(bool accept_invalid_exe_ctx) { SyncWithProcessState(accept_invalid_exe_ctx); return m_needs_update; } bool IsValid() { const bool accept_invalid_exe_ctx = false; if (!m_mod_id.IsValid()) return false; else if (SyncWithProcessState(accept_invalid_exe_ctx)) { if (!m_mod_id.IsValid()) return false; } return true; } void SetInvalid() { // Use the stop id to mark us as invalid, leave the thread id and the // stack id around for logging and // history purposes. m_mod_id.SetInvalid(); // Can't update an invalid state. m_needs_update = false; } private: bool SyncWithProcessState(bool accept_invalid_exe_ctx); ProcessModID m_mod_id; // This is the stop id when this ValueObject was last // evaluated. ExecutionContextRef m_exe_ctx_ref; bool m_needs_update; }; virtual ~ValueObject(); const EvaluationPoint &GetUpdatePoint() const { return m_update_point; } EvaluationPoint &GetUpdatePoint() { return m_update_point; } const ExecutionContextRef &GetExecutionContextRef() const { return m_update_point.GetExecutionContextRef(); } lldb::TargetSP GetTargetSP() const { return m_update_point.GetExecutionContextRef().GetTargetSP(); } lldb::ProcessSP GetProcessSP() const { return m_update_point.GetExecutionContextRef().GetProcessSP(); } lldb::ThreadSP GetThreadSP() const { return m_update_point.GetExecutionContextRef().GetThreadSP(); } lldb::StackFrameSP GetFrameSP() const { return m_update_point.GetExecutionContextRef().GetFrameSP(); } void SetNeedsUpdate(); CompilerType GetCompilerType(); // this vends a TypeImpl that is useful at the SB API layer virtual TypeImpl GetTypeImpl(); virtual bool CanProvideValue(); //------------------------------------------------------------------ // Subclasses must implement the functions below. //------------------------------------------------------------------ virtual uint64_t GetByteSize() = 0; virtual lldb::ValueType GetValueType() const = 0; //------------------------------------------------------------------ // Subclasses can implement the functions below. //------------------------------------------------------------------ virtual ConstString GetTypeName(); virtual ConstString GetDisplayTypeName(); virtual ConstString GetQualifiedTypeName(); virtual lldb::LanguageType GetObjectRuntimeLanguage(); virtual uint32_t GetTypeInfo(CompilerType *pointee_or_element_compiler_type = nullptr); virtual bool IsPointerType(); virtual bool IsArrayType(); virtual bool IsScalarType(); virtual bool IsPointerOrReferenceType(); virtual bool IsPossibleDynamicType(); bool IsNilReference(); bool IsUninitializedReference(); virtual bool IsBaseClass() { return false; } bool IsBaseClass(uint32_t &depth); virtual bool IsDereferenceOfParent() { return false; } bool IsIntegerType(bool &is_signed); virtual bool GetBaseClassPath(Stream &s); virtual void GetExpressionPath( Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eGetExpressionPathFormatDereferencePointers); lldb::ValueObjectSP GetValueForExpressionPath( llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop = nullptr, ExpressionPathEndResultType *final_value_type = nullptr, const GetValueForExpressionPathOptions &options = GetValueForExpressionPathOptions::DefaultOptions(), ExpressionPathAftermath *final_task_on_target = nullptr); virtual bool IsInScope() { return true; } virtual lldb::offset_t GetByteOffset() { return 0; } virtual uint32_t GetBitfieldBitSize() { return 0; } virtual uint32_t GetBitfieldBitOffset() { return 0; } bool IsBitfield() { return (GetBitfieldBitSize() != 0) || (GetBitfieldBitOffset() != 0); } virtual bool IsArrayItemForPointer() { return m_is_array_item_for_pointer; } virtual const char *GetValueAsCString(); virtual bool GetValueAsCString(const lldb_private::TypeFormatImpl &format, std::string &destination); bool GetValueAsCString(lldb::Format format, std::string &destination); virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success = nullptr); virtual int64_t GetValueAsSigned(int64_t fail_value, bool *success = nullptr); virtual bool SetValueFromCString(const char *value_str, Error &error); // Return the module associated with this value object in case the // value is from an executable file and might have its data in // sections of the file. This can be used for variables. virtual lldb::ModuleSP GetModule(); ValueObject *GetRoot(); // Given a ValueObject, loop over itself and its parent, and its parent's // parent, .. // until either the given callback returns false, or you end up at a null // pointer ValueObject *FollowParentChain(std::function); virtual bool GetDeclaration(Declaration &decl); //------------------------------------------------------------------ // The functions below should NOT be modified by subclasses //------------------------------------------------------------------ const Error &GetError(); const ConstString &GetName() const; virtual lldb::ValueObjectSP GetChildAtIndex(size_t idx, bool can_create); // this will always create the children if necessary - lldb::ValueObjectSP - GetChildAtIndexPath(const std::initializer_list &idxs, - size_t *index_of_error = nullptr); - - lldb::ValueObjectSP GetChildAtIndexPath(const std::vector &idxs, + lldb::ValueObjectSP GetChildAtIndexPath(llvm::ArrayRef idxs, size_t *index_of_error = nullptr); - lldb::ValueObjectSP GetChildAtIndexPath( - const std::initializer_list> &idxs, - size_t *index_of_error = nullptr); - lldb::ValueObjectSP - GetChildAtIndexPath(const std::vector> &idxs, + GetChildAtIndexPath(llvm::ArrayRef> idxs, size_t *index_of_error = nullptr); // this will always create the children if necessary - lldb::ValueObjectSP - GetChildAtNamePath(const std::initializer_list &names, - ConstString *name_of_error = nullptr); - - lldb::ValueObjectSP GetChildAtNamePath(const std::vector &names, + lldb::ValueObjectSP GetChildAtNamePath(llvm::ArrayRef names, ConstString *name_of_error = nullptr); - lldb::ValueObjectSP GetChildAtNamePath( - const std::initializer_list> &names, - ConstString *name_of_error = nullptr); - lldb::ValueObjectSP - GetChildAtNamePath(const std::vector> &names, + GetChildAtNamePath(llvm::ArrayRef> names, ConstString *name_of_error = nullptr); virtual lldb::ValueObjectSP GetChildMemberWithName(const ConstString &name, bool can_create); virtual size_t GetIndexOfChildWithName(const ConstString &name); size_t GetNumChildren(uint32_t max = UINT32_MAX); const Value &GetValue() const; Value &GetValue(); virtual bool ResolveValue(Scalar &scalar); // return 'false' whenever you set the error, otherwise // callers may assume true means everything is OK - this will // break breakpoint conditions among potentially a few others virtual bool IsLogicalTrue(Error &error); virtual const char *GetLocationAsCString(); const char * GetSummaryAsCString(lldb::LanguageType lang = lldb::eLanguageTypeUnknown); bool GetSummaryAsCString(TypeSummaryImpl *summary_ptr, std::string &destination, lldb::LanguageType lang = lldb::eLanguageTypeUnknown); bool GetSummaryAsCString(std::string &destination, const TypeSummaryOptions &options); bool GetSummaryAsCString(TypeSummaryImpl *summary_ptr, std::string &destination, const TypeSummaryOptions &options); std::pair GetValidationStatus(); const char *GetObjectDescription(); bool HasSpecialPrintableRepresentation( ValueObjectRepresentationStyle val_obj_display, lldb::Format custom_format); enum class PrintableRepresentationSpecialCases : bool { eDisable = false, eAllow = true }; bool DumpPrintableRepresentation(Stream &s, ValueObjectRepresentationStyle val_obj_display = eValueObjectRepresentationStyleSummary, lldb::Format custom_format = lldb::eFormatInvalid, PrintableRepresentationSpecialCases special = PrintableRepresentationSpecialCases::eAllow, bool do_dump_error = true); bool GetValueIsValid() const; // If you call this on a newly created ValueObject, it will always return // false. bool GetValueDidChange(); bool UpdateValueIfNeeded(bool update_format = true); bool UpdateFormatsIfNeeded(); lldb::ValueObjectSP GetSP() { return m_manager->GetSharedPointer(this); } // Change the name of the current ValueObject. Should *not* be used from a // synthetic child provider as it would change the name of the non synthetic // child as well. void SetName(const ConstString &name); virtual lldb::addr_t GetAddressOf(bool scalar_is_load_address = true, AddressType *address_type = nullptr); lldb::addr_t GetPointerValue(AddressType *address_type = nullptr); lldb::ValueObjectSP GetSyntheticChild(const ConstString &key) const; lldb::ValueObjectSP GetSyntheticArrayMember(size_t index, bool can_create); lldb::ValueObjectSP GetSyntheticBitFieldChild(uint32_t from, uint32_t to, bool can_create); lldb::ValueObjectSP GetSyntheticExpressionPathChild(const char *expression, bool can_create); virtual lldb::ValueObjectSP GetSyntheticChildAtOffset(uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str = ConstString()); virtual lldb::ValueObjectSP GetSyntheticBase(uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str = ConstString()); virtual lldb::ValueObjectSP GetDynamicValue(lldb::DynamicValueType valueType); lldb::DynamicValueType GetDynamicValueType(); virtual lldb::ValueObjectSP GetStaticValue(); virtual lldb::ValueObjectSP GetNonSyntheticValue(); lldb::ValueObjectSP GetSyntheticValue(bool use_synthetic = true); virtual bool HasSyntheticValue(); virtual bool IsSynthetic() { return false; } lldb::ValueObjectSP GetQualifiedRepresentationIfAvailable(lldb::DynamicValueType dynValue, bool synthValue); virtual lldb::ValueObjectSP CreateConstantValue(const ConstString &name); virtual lldb::ValueObjectSP Dereference(Error &error); // Creates a copy of the ValueObject with a new name and setting the current // ValueObject as its parent. It should be used when we want to change the // name of a ValueObject without modifying the actual ValueObject itself // (e.g. sythetic child provider). virtual lldb::ValueObjectSP Clone(const ConstString &new_name); virtual lldb::ValueObjectSP AddressOf(Error &error); virtual lldb::addr_t GetLiveAddress() { return LLDB_INVALID_ADDRESS; } virtual void SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, AddressType address_type = eAddressTypeLoad) {} // Find the address of the C++ vtable pointer virtual lldb::addr_t GetCPPVTableAddress(AddressType &address_type); virtual lldb::ValueObjectSP Cast(const CompilerType &compiler_type); virtual lldb::ValueObjectSP CastPointerType(const char *name, CompilerType &ast_type); virtual lldb::ValueObjectSP CastPointerType(const char *name, lldb::TypeSP &type_sp); // The backing bits of this value object were updated, clear any // descriptive string, so we know we have to refetch them virtual void ValueUpdated() { ClearUserVisibleData(eClearUserVisibleDataItemsValue | eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsDescription); } virtual bool IsDynamic() { return false; } virtual bool DoesProvideSyntheticValue() { return false; } virtual bool IsSyntheticChildrenGenerated(); virtual void SetSyntheticChildrenGenerated(bool b); virtual SymbolContextScope *GetSymbolContextScope(); void Dump(Stream &s); void Dump(Stream &s, const DumpValueObjectOptions &options); static lldb::ValueObjectSP CreateValueObjectFromExpression(llvm::StringRef name, llvm::StringRef expression, const ExecutionContext &exe_ctx); static lldb::ValueObjectSP CreateValueObjectFromExpression(llvm::StringRef name, llvm::StringRef expression, const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options); static lldb::ValueObjectSP CreateValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type); static lldb::ValueObjectSP CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type); void LogValueObject(Log *log); void LogValueObject(Log *log, const DumpValueObjectOptions &options); lldb::ValueObjectSP Persist(); // returns true if this is a char* or a char[] // if it is a char* and check_pointer is true, // it also checks that the pointer is valid bool IsCStringContainer(bool check_pointer = false); std::pair ReadPointedString(lldb::DataBufferSP &buffer_sp, Error &error, uint32_t max_length = 0, bool honor_array = true, lldb::Format item_format = lldb::eFormatCharArray); virtual size_t GetPointeeData(DataExtractor &data, uint32_t item_idx = 0, uint32_t item_count = 1); virtual uint64_t GetData(DataExtractor &data, Error &error); virtual bool SetData(DataExtractor &data, Error &error); virtual bool GetIsConstant() const { return m_update_point.IsConstant(); } bool NeedsUpdating() { const bool accept_invalid_exe_ctx = (CanUpdateWithInvalidExecutionContext() == eLazyBoolYes); return m_update_point.NeedsUpdating(accept_invalid_exe_ctx); } void SetIsConstant() { m_update_point.SetIsConstant(); } lldb::Format GetFormat() const; virtual void SetFormat(lldb::Format format) { if (format != m_format) ClearUserVisibleData(eClearUserVisibleDataItemsValue); m_format = format; } virtual lldb::LanguageType GetPreferredDisplayLanguage(); void SetPreferredDisplayLanguage(lldb::LanguageType); lldb::TypeSummaryImplSP GetSummaryFormat() { UpdateFormatsIfNeeded(); return m_type_summary_sp; } void SetSummaryFormat(lldb::TypeSummaryImplSP format) { m_type_summary_sp = format; ClearUserVisibleData(eClearUserVisibleDataItemsSummary); } lldb::TypeValidatorImplSP GetValidator() { UpdateFormatsIfNeeded(); return m_type_validator_sp; } void SetValidator(lldb::TypeValidatorImplSP format) { m_type_validator_sp = format; ClearUserVisibleData(eClearUserVisibleDataItemsValidator); } void SetValueFormat(lldb::TypeFormatImplSP format) { m_type_format_sp = format; ClearUserVisibleData(eClearUserVisibleDataItemsValue); } lldb::TypeFormatImplSP GetValueFormat() { UpdateFormatsIfNeeded(); return m_type_format_sp; } void SetSyntheticChildren(const lldb::SyntheticChildrenSP &synth_sp) { if (synth_sp.get() == m_synthetic_children_sp.get()) return; ClearUserVisibleData(eClearUserVisibleDataItemsSyntheticChildren); m_synthetic_children_sp = synth_sp; } lldb::SyntheticChildrenSP GetSyntheticChildren() { UpdateFormatsIfNeeded(); return m_synthetic_children_sp; } // Use GetParent for display purposes, but if you want to tell the parent to // update itself // then use m_parent. The ValueObjectDynamicValue's parent is not the correct // parent for // displaying, they are really siblings, so for display it needs to route // through to its grandparent. virtual ValueObject *GetParent() { return m_parent; } virtual const ValueObject *GetParent() const { return m_parent; } ValueObject *GetNonBaseClassParent(); void SetAddressTypeOfChildren(AddressType at) { m_address_type_of_ptr_or_ref_children = at; } AddressType GetAddressTypeOfChildren(); void SetHasCompleteType() { m_did_calculate_complete_objc_class_type = true; } //------------------------------------------------------------------ /// Find out if a ValueObject might have children. /// /// This call is much more efficient than CalculateNumChildren() as /// it doesn't need to complete the underlying type. This is designed /// to be used in a UI environment in order to detect if the /// disclosure triangle should be displayed or not. /// /// This function returns true for class, union, structure, /// pointers, references, arrays and more. Again, it does so without /// doing any expensive type completion. /// /// @return /// Returns \b true if the ValueObject might have children, or \b /// false otherwise. //------------------------------------------------------------------ virtual bool MightHaveChildren(); virtual lldb::VariableSP GetVariable() { return nullptr; } virtual bool IsRuntimeSupportValue(); virtual uint64_t GetLanguageFlags(); virtual void SetLanguageFlags(uint64_t flags); protected: typedef ClusterManager ValueObjectManager; class ChildrenManager { public: ChildrenManager() : m_mutex(), m_children(), m_children_count(0) {} bool HasChildAtIndex(size_t idx) { std::lock_guard guard(m_mutex); return (m_children.find(idx) != m_children.end()); } ValueObject *GetChildAtIndex(size_t idx) { std::lock_guard guard(m_mutex); const auto iter = m_children.find(idx); return ((iter == m_children.end()) ? nullptr : iter->second); } void SetChildAtIndex(size_t idx, ValueObject *valobj) { // we do not need to be mutex-protected to make a pair ChildrenPair pair(idx, valobj); std::lock_guard guard(m_mutex); m_children.insert(pair); } void SetChildrenCount(size_t count) { Clear(count); } size_t GetChildrenCount() { return m_children_count; } void Clear(size_t new_count = 0) { std::lock_guard guard(m_mutex); m_children_count = new_count; m_children.clear(); } private: typedef std::map ChildrenMap; typedef ChildrenMap::iterator ChildrenIterator; typedef ChildrenMap::value_type ChildrenPair; std::recursive_mutex m_mutex; ChildrenMap m_children; size_t m_children_count; }; //------------------------------------------------------------------ // Classes that inherit from ValueObject can see and modify these //------------------------------------------------------------------ ValueObject *m_parent; // The parent value object, or nullptr if this has no parent ValueObject *m_root; // The root of the hierarchy for this ValueObject (or // nullptr if never calculated) EvaluationPoint m_update_point; // Stores both the stop id and the full // context at which this value was last // updated. When we are asked to update the value object, we check whether // the context & stop id are the same before updating. ConstString m_name; // The name of this object DataExtractor m_data; // A data extractor that can be used to extract the value. Value m_value; Error m_error; // An error object that can describe any errors that occur when // updating values. std::string m_value_str; // Cached value string that will get cleared if/when // the value is updated. std::string m_old_value_str; // Cached old value string from the last time the // value was gotten std::string m_location_str; // Cached location string that will get cleared // if/when the value is updated. std::string m_summary_str; // Cached summary string that will get cleared // if/when the value is updated. std::string m_object_desc_str; // Cached result of the "object printer". This // differs from the summary // in that the summary is consed up by us, the object_desc_string is builtin. llvm::Optional> m_validation_result; CompilerType m_override_type; // If the type of the value object should be // overridden, the type to impose. ValueObjectManager *m_manager; // This object is managed by the root object // (any ValueObject that gets created // without a parent.) The manager gets passed through all the generations of // dependent objects, and will keep the whole cluster of objects alive as long // as a shared pointer to any of them has been handed out. Shared pointers to // value objects must always be made with the GetSP method. ChildrenManager m_children; std::map m_synthetic_children; ValueObject *m_dynamic_value; ValueObject *m_synthetic_value; ValueObject *m_deref_valobj; lldb::ValueObjectSP m_addr_of_valobj_sp; // We have to hold onto a shared // pointer to this one because it is // created // as an independent ValueObjectConstResult, which isn't managed by us. lldb::Format m_format; lldb::Format m_last_format; uint32_t m_last_format_mgr_revision; lldb::TypeSummaryImplSP m_type_summary_sp; lldb::TypeFormatImplSP m_type_format_sp; lldb::SyntheticChildrenSP m_synthetic_children_sp; lldb::TypeValidatorImplSP m_type_validator_sp; ProcessModID m_user_id_of_forced_summary; AddressType m_address_type_of_ptr_or_ref_children; llvm::SmallVector m_value_checksum; lldb::LanguageType m_preferred_display_language; uint64_t m_language_flags; bool m_value_is_valid : 1, m_value_did_change : 1, m_children_count_valid : 1, m_old_value_valid : 1, m_is_deref_of_parent : 1, m_is_array_item_for_pointer : 1, m_is_bitfield_for_scalar : 1, m_is_child_at_offset : 1, m_is_getting_summary : 1, m_did_calculate_complete_objc_class_type : 1, m_is_synthetic_children_generated : 1; friend class ValueObjectChild; friend class ClangExpressionDeclMap; // For GetValue friend class ExpressionVariable; // For SetName friend class Target; // For SetName friend class ValueObjectConstResultImpl; friend class ValueObjectSynthetic; // For ClearUserVisibleData //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ // Use the no-argument constructor to make a constant variable object (with no // ExecutionContextScope.) ValueObject(); // Use this constructor to create a "root variable object". The ValueObject // will be locked to this context // through-out its lifespan. ValueObject(ExecutionContextScope *exe_scope, AddressType child_ptr_or_ref_addr_type = eAddressTypeLoad); // Use this constructor to create a ValueObject owned by another ValueObject. // It will inherit the ExecutionContext // of its parent. ValueObject(ValueObject &parent); ValueObjectManager *GetManager() { return m_manager; } virtual bool UpdateValue() = 0; virtual LazyBool CanUpdateWithInvalidExecutionContext() { return eLazyBoolCalculate; } virtual void CalculateDynamicValue(lldb::DynamicValueType use_dynamic); virtual lldb::DynamicValueType GetDynamicValueTypeImpl() { return lldb::eNoDynamicValues; } virtual bool HasDynamicValueTypeInfo() { return false; } virtual void CalculateSyntheticValue(bool use_synthetic = true); // Should only be called by ValueObject::GetChildAtIndex() // Returns a ValueObject managed by this ValueObject's manager. virtual ValueObject *CreateChildAtIndex(size_t idx, bool synthetic_array_member, int32_t synthetic_index); // Should only be called by ValueObject::GetNumChildren() virtual size_t CalculateNumChildren(uint32_t max = UINT32_MAX) = 0; void SetNumChildren(size_t num_children); void SetValueDidChange(bool value_changed); void SetValueIsValid(bool valid); void ClearUserVisibleData( uint32_t items = ValueObject::eClearUserVisibleDataItemsAllStrings); void AddSyntheticChild(const ConstString &key, ValueObject *valobj); DataExtractor &GetDataExtractor(); void ClearDynamicTypeInformation(); //------------------------------------------------------------------ // Subclasses must implement the functions below. //------------------------------------------------------------------ virtual CompilerType GetCompilerTypeImpl() = 0; const char *GetLocationAsCStringImpl(const Value &value, const DataExtractor &data); bool IsChecksumEmpty(); void SetPreferredDisplayLanguageIfNeeded(lldb::LanguageType); private: virtual CompilerType MaybeCalculateCompleteType(); lldb::ValueObjectSP GetValueForExpressionPath_Impl( llvm::StringRef expression_cstr, ExpressionPathScanEndReason *reason_to_stop, ExpressionPathEndResultType *final_value_type, const GetValueForExpressionPathOptions &options, ExpressionPathAftermath *final_task_on_target); DISALLOW_COPY_AND_ASSIGN(ValueObject); }; //------------------------------------------------------------------------------ // A value object manager class that is seeded with the static variable value // and it vends the user facing value object. If the type is dynamic it can // vend the dynamic type. If this user type also has a synthetic type associated // with it, it will vend the synthetic type. The class watches the process' stop // ID and will update the user type when needed. //------------------------------------------------------------------------------ class ValueObjectManager { // The root value object is the static typed variable object. lldb::ValueObjectSP m_root_valobj_sp; // The user value object is the value object the user wants to see. lldb::ValueObjectSP m_user_valobj_sp; lldb::DynamicValueType m_use_dynamic; uint32_t m_stop_id; // The stop ID that m_user_valobj_sp is valid for. bool m_use_synthetic; public: ValueObjectManager() {} ValueObjectManager(lldb::ValueObjectSP in_valobj_sp, lldb::DynamicValueType use_dynamic, bool use_synthetic); bool IsValid() const; lldb::ValueObjectSP GetRootSP() const { return m_root_valobj_sp; } // Gets the correct value object from the root object for a given process // stop ID. If dynamic values are enabled, or if synthetic children are // enabled, the value object that the user wants to see might change while // debugging. lldb::ValueObjectSP GetSP(); void SetUseDynamic(lldb::DynamicValueType use_dynamic); void SetUseSynthetic(bool use_synthetic); lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; } bool GetUseSynthetic() const { return m_use_synthetic; } lldb::TargetSP GetTargetSP() const; lldb::ProcessSP GetProcessSP() const; lldb::ThreadSP GetThreadSP() const; lldb::StackFrameSP GetFrameSP() const; }; } // namespace lldb_private #endif // liblldb_ValueObject_h_ Index: vendor/lldb/dist/include/lldb/Host/SocketAddress.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/SocketAddress.h (revision 317454) +++ vendor/lldb/dist/include/lldb/Host/SocketAddress.h (revision 317455) @@ -1,230 +1,231 @@ //===-- SocketAddress.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_SocketAddress_h_ #define liblldb_SocketAddress_h_ // C Includes #include #ifdef _WIN32 #include "lldb/Host/windows/windows.h" #include #include typedef ADDRESS_FAMILY sa_family_t; #else #include #include #include #endif #if defined(__FreeBSD__) #include #endif // C++ Includes // Other libraries and framework includes // Project includes #include #include namespace lldb_private { class SocketAddress { public: //---------------------------------------------------------------------------- // Static method to get all address information for a host and/or service //---------------------------------------------------------------------------- - static std::vector GetAddressInfo(const char *hostname, - const char *servname); + static std::vector + GetAddressInfo(const char *hostname, const char *servname, int ai_family, + int ai_socktype, int ai_protocol, int ai_flags = 0); //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ SocketAddress(); SocketAddress(const struct addrinfo *addr_info); SocketAddress(const struct sockaddr &s); SocketAddress(const struct sockaddr_in &s); SocketAddress(const struct sockaddr_in6 &s); SocketAddress(const struct sockaddr_storage &s); SocketAddress(const SocketAddress &rhs); ~SocketAddress(); //------------------------------------------------------------------ // Operators //------------------------------------------------------------------ const SocketAddress &operator=(const SocketAddress &rhs); const SocketAddress &operator=(const struct addrinfo *addr_info); const SocketAddress &operator=(const struct sockaddr &s); const SocketAddress &operator=(const struct sockaddr_in &s); const SocketAddress &operator=(const struct sockaddr_in6 &s); const SocketAddress &operator=(const struct sockaddr_storage &s); bool operator==(const SocketAddress &rhs) const; bool operator!=(const SocketAddress &rhs) const; //------------------------------------------------------------------ // Clear the contents of this socket address //------------------------------------------------------------------ void Clear(); //------------------------------------------------------------------ // Get the length for the current socket address family //------------------------------------------------------------------ socklen_t GetLength() const; //------------------------------------------------------------------ // Get the max length for the largest socket address supported. //------------------------------------------------------------------ static socklen_t GetMaxLength(); //------------------------------------------------------------------ // Get the socket address family //------------------------------------------------------------------ sa_family_t GetFamily() const; //------------------------------------------------------------------ // Set the socket address family //------------------------------------------------------------------ void SetFamily(sa_family_t family); //------------------------------------------------------------------ // Get the address //------------------------------------------------------------------ std::string GetIPAddress() const; //------------------------------------------------------------------ // Get the port if the socket address for the family has a port //------------------------------------------------------------------ uint16_t GetPort() const; //------------------------------------------------------------------ // Set the port if the socket address for the family has a port. // The family must be set correctly prior to calling this function. //------------------------------------------------------------------ bool SetPort(uint16_t port); //------------------------------------------------------------------ // Set the socket address according to the first match from a call // to getaddrinfo() (or equivalent functions for systems that don't // have getaddrinfo(). If "addr_info_ptr" is not NULL, it will get // filled in with the match that was used to populate this socket // address. //------------------------------------------------------------------ bool getaddrinfo(const char *host, // Hostname ("foo.bar.com" or "foo" or IP // address string ("123.234.12.1" or // "2001:0db8:85a3:0000:0000:8a2e:0370:7334") const char *service, // Protocol name ("tcp", "http", etc) or a // raw port number string ("81") int ai_family = PF_UNSPEC, int ai_socktype = 0, int ai_protocol = 0, int ai_flags = 0); //------------------------------------------------------------------ // Quick way to set the SocketAddress to localhost given the family. // Returns true if successful, false if "family" doesn't support // localhost or if "family" is not supported by this class. //------------------------------------------------------------------ bool SetToLocalhost(sa_family_t family, uint16_t port); bool SetToAnyAddress(sa_family_t family, uint16_t port); //------------------------------------------------------------------ // Returns true if there is a valid socket address in this object. //------------------------------------------------------------------ bool IsValid() const; //------------------------------------------------------------------ // Returns true if the socket is INADDR_ANY //------------------------------------------------------------------ bool IsAnyAddr() const; //------------------------------------------------------------------ // Direct access to all of the sockaddr structures //------------------------------------------------------------------ struct sockaddr &sockaddr() { return m_socket_addr.sa; } const struct sockaddr &sockaddr() const { return m_socket_addr.sa; } struct sockaddr_in &sockaddr_in() { return m_socket_addr.sa_ipv4; } const struct sockaddr_in &sockaddr_in() const { return m_socket_addr.sa_ipv4; } struct sockaddr_in6 &sockaddr_in6() { return m_socket_addr.sa_ipv6; } const struct sockaddr_in6 &sockaddr_in6() const { return m_socket_addr.sa_ipv6; } struct sockaddr_storage &sockaddr_storage() { return m_socket_addr.sa_storage; } const struct sockaddr_storage &sockaddr_storage() const { return m_socket_addr.sa_storage; } //------------------------------------------------------------------ // Conversion operators to allow getting the contents of this class // as a pointer to the appropriate structure. This allows an instance // of this class to be used in calls that take one of the sockaddr // structure variants without having to manually use the correct // accessor function. //------------------------------------------------------------------ operator struct sockaddr *() { return &m_socket_addr.sa; } operator const struct sockaddr *() const { return &m_socket_addr.sa; } operator struct sockaddr_in *() { return &m_socket_addr.sa_ipv4; } operator const struct sockaddr_in *() const { return &m_socket_addr.sa_ipv4; } operator struct sockaddr_in6 *() { return &m_socket_addr.sa_ipv6; } operator const struct sockaddr_in6 *() const { return &m_socket_addr.sa_ipv6; } operator const struct sockaddr_storage *() const { return &m_socket_addr.sa_storage; } operator struct sockaddr_storage *() { return &m_socket_addr.sa_storage; } protected: typedef union sockaddr_tag { struct sockaddr sa; struct sockaddr_in sa_ipv4; struct sockaddr_in6 sa_ipv6; struct sockaddr_storage sa_storage; } sockaddr_t; //------------------------------------------------------------------ // Classes that inherit from SocketAddress can see and modify these //------------------------------------------------------------------ sockaddr_t m_socket_addr; }; } // namespace lldb_private #endif // liblldb_SocketAddress_h_ Index: vendor/lldb/dist/include/lldb/Target/Process.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/Process.h (revision 317454) +++ vendor/lldb/dist/include/lldb/Target/Process.h (revision 317455) @@ -1,3181 +1,3255 @@ //===-- Process.h -----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Process_h_ #define liblldb_Process_h_ #include "lldb/Host/Config.h" // C Includes #include // C++ Includes #include #include #include #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointSiteList.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Event.h" #include "lldb/Core/Listener.h" #include "lldb/Core/LoadedModuleInfoList.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Core/TraceOptions.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/ProcessRunLock.h" #include "lldb/Interpreter/Options.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Memory.h" #include "lldb/Target/ProcessInfo.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/ThreadList.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/NameMatches.h" #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" namespace lldb_private { template struct Range; //---------------------------------------------------------------------- // ProcessProperties //---------------------------------------------------------------------- class ProcessProperties : public Properties { public: // Pass nullptr for "process" if the ProcessProperties are to be the global // copy ProcessProperties(lldb_private::Process *process); ~ProcessProperties() override; bool GetDisableMemoryCache() const; uint64_t GetMemoryCacheLineSize() const; Args GetExtraStartupCommands() const; void SetExtraStartupCommands(const Args &args); FileSpec GetPythonOSPluginPath() const; void SetPythonOSPluginPath(const FileSpec &file); bool GetIgnoreBreakpointsInExpressions() const; void SetIgnoreBreakpointsInExpressions(bool ignore); bool GetUnwindOnErrorInExpressions() const; void SetUnwindOnErrorInExpressions(bool ignore); bool GetStopOnSharedLibraryEvents() const; void SetStopOnSharedLibraryEvents(bool stop); bool GetDetachKeepsStopped() const; void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; protected: static void OptionValueChangedCallback(void *baton, OptionValue *option_value); Process *m_process; // Can be nullptr for global ProcessProperties }; typedef std::shared_ptr ProcessPropertiesSP; //---------------------------------------------------------------------- // ProcessInstanceInfo // // Describes an existing process and any discoverable information that // pertains to that process. //---------------------------------------------------------------------- class ProcessInstanceInfo : public ProcessInfo { public: ProcessInstanceInfo() : ProcessInfo(), m_euid(UINT32_MAX), m_egid(UINT32_MAX), m_parent_pid(LLDB_INVALID_PROCESS_ID) {} ProcessInstanceInfo(const char *name, const ArchSpec &arch, lldb::pid_t pid) : ProcessInfo(name, arch, pid), m_euid(UINT32_MAX), m_egid(UINT32_MAX), m_parent_pid(LLDB_INVALID_PROCESS_ID) {} void Clear() { ProcessInfo::Clear(); m_euid = UINT32_MAX; m_egid = UINT32_MAX; m_parent_pid = LLDB_INVALID_PROCESS_ID; } uint32_t GetEffectiveUserID() const { return m_euid; } uint32_t GetEffectiveGroupID() const { return m_egid; } bool EffectiveUserIDIsValid() const { return m_euid != UINT32_MAX; } bool EffectiveGroupIDIsValid() const { return m_egid != UINT32_MAX; } void SetEffectiveUserID(uint32_t uid) { m_euid = uid; } void SetEffectiveGroupID(uint32_t gid) { m_egid = gid; } lldb::pid_t GetParentProcessID() const { return m_parent_pid; } void SetParentProcessID(lldb::pid_t pid) { m_parent_pid = pid; } bool ParentProcessIDIsValid() const { return m_parent_pid != LLDB_INVALID_PROCESS_ID; } void Dump(Stream &s, Platform *platform) const; static void DumpTableHeader(Stream &s, Platform *platform, bool show_args, bool verbose); void DumpAsTableRow(Stream &s, Platform *platform, bool show_args, bool verbose) const; protected: uint32_t m_euid; uint32_t m_egid; lldb::pid_t m_parent_pid; }; //---------------------------------------------------------------------- // ProcessAttachInfo // // Describes any information that is required to attach to a process. //---------------------------------------------------------------------- class ProcessAttachInfo : public ProcessInstanceInfo { public: ProcessAttachInfo() : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) {} ProcessAttachInfo(const ProcessLaunchInfo &launch_info) : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) { ProcessInfo::operator=(launch_info); SetProcessPluginName(launch_info.GetProcessPluginName()); SetResumeCount(launch_info.GetResumeCount()); SetListener(launch_info.GetListener()); SetHijackListener(launch_info.GetHijackListener()); m_detach_on_error = launch_info.GetDetachOnError(); } bool GetWaitForLaunch() const { return m_wait_for_launch; } void SetWaitForLaunch(bool b) { m_wait_for_launch = b; } bool GetAsync() const { return m_async; } void SetAsync(bool b) { m_async = b; } bool GetIgnoreExisting() const { return m_ignore_existing; } void SetIgnoreExisting(bool b) { m_ignore_existing = b; } bool GetContinueOnceAttached() const { return m_continue_once_attached; } void SetContinueOnceAttached(bool b) { m_continue_once_attached = b; } uint32_t GetResumeCount() const { return m_resume_count; } void SetResumeCount(uint32_t c) { m_resume_count = c; } const char *GetProcessPluginName() const { return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str()); } void SetProcessPluginName(llvm::StringRef plugin) { m_plugin_name = plugin; } void Clear() { ProcessInstanceInfo::Clear(); m_plugin_name.clear(); m_resume_count = 0; m_wait_for_launch = false; m_ignore_existing = true; m_continue_once_attached = false; } bool ProcessInfoSpecified() const { if (GetExecutableFile()) return true; if (GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID) return true; return false; } lldb::ListenerSP GetHijackListener() const { return m_hijack_listener_sp; } void SetHijackListener(const lldb::ListenerSP &listener_sp) { m_hijack_listener_sp = listener_sp; } bool GetDetachOnError() const { return m_detach_on_error; } void SetDetachOnError(bool enable) { m_detach_on_error = enable; } // Get and set the actual listener that will be used for the process events lldb::ListenerSP GetListener() const { return m_listener_sp; } void SetListener(const lldb::ListenerSP &listener_sp) { m_listener_sp = listener_sp; } lldb::ListenerSP GetListenerForProcess(Debugger &debugger); protected: lldb::ListenerSP m_listener_sp; lldb::ListenerSP m_hijack_listener_sp; std::string m_plugin_name; uint32_t m_resume_count; // How many times do we resume after launching bool m_wait_for_launch; bool m_ignore_existing; bool m_continue_once_attached; // Supports the use-case scenario of // immediately continuing the process once // attached. bool m_detach_on_error; // If we are debugging remotely, instruct the stub to // detach rather than killing the target on error. bool m_async; // Use an async attach where we start the attach and return // immediately (used by GUI programs with --waitfor so they can // call SBProcess::Stop() to cancel attach) }; class ProcessLaunchCommandOptions : public Options { public: ProcessLaunchCommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting () OptionParsingStarting(nullptr); } ~ProcessLaunchCommandOptions() override = default; Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override; void OptionParsingStarting(ExecutionContext *execution_context) override { launch_info.Clear(); disable_aslr = eLazyBoolCalculate; } llvm::ArrayRef GetDefinitions() override; // Instance variables to hold the values for command options. ProcessLaunchInfo launch_info; lldb_private::LazyBool disable_aslr; }; //---------------------------------------------------------------------- // ProcessInstanceInfoMatch // // A class to help matching one ProcessInstanceInfo to another. //---------------------------------------------------------------------- class ProcessInstanceInfoMatch { public: ProcessInstanceInfoMatch() : m_match_info(), m_name_match_type(NameMatch::Ignore), m_match_all_users(false) {} ProcessInstanceInfoMatch(const char *process_name, NameMatch process_name_match_type) : m_match_info(), m_name_match_type(process_name_match_type), m_match_all_users(false) { m_match_info.GetExecutableFile().SetFile(process_name, false); } ProcessInstanceInfo &GetProcessInfo() { return m_match_info; } const ProcessInstanceInfo &GetProcessInfo() const { return m_match_info; } bool GetMatchAllUsers() const { return m_match_all_users; } void SetMatchAllUsers(bool b) { m_match_all_users = b; } NameMatch GetNameMatchType() const { return m_name_match_type; } void SetNameMatchType(NameMatch name_match_type) { m_name_match_type = name_match_type; } bool NameMatches(const char *process_name) const; bool Matches(const ProcessInstanceInfo &proc_info) const; bool MatchAllProcesses() const; void Clear(); protected: ProcessInstanceInfo m_match_info; NameMatch m_name_match_type; bool m_match_all_users; }; class ProcessInstanceInfoList { public: ProcessInstanceInfoList() = default; void Clear() { m_infos.clear(); } size_t GetSize() { return m_infos.size(); } void Append(const ProcessInstanceInfo &info) { m_infos.push_back(info); } const char *GetProcessNameAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetName() : nullptr); } size_t GetProcessNameLengthAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetNameLength() : 0); } lldb::pid_t GetProcessIDAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetProcessID() : 0); } bool GetInfoAtIndex(size_t idx, ProcessInstanceInfo &info) { if (idx < m_infos.size()) { info = m_infos[idx]; return true; } return false; } // You must ensure "idx" is valid before calling this function const ProcessInstanceInfo &GetProcessInfoAtIndex(size_t idx) const { assert(idx < m_infos.size()); return m_infos[idx]; } protected: typedef std::vector collection; collection m_infos; }; // This class tracks the Modification state of the process. Things that can // currently modify // the program are running the program (which will up the StopID) and writing // memory (which // will up the MemoryID.) // FIXME: Should we also include modification of register states? class ProcessModID { friend bool operator==(const ProcessModID &lhs, const ProcessModID &rhs); public: ProcessModID() : m_stop_id(0), m_last_natural_stop_id(0), m_resume_id(0), m_memory_id(0), m_last_user_expression_resume(0), m_running_user_expression(false) {} ProcessModID(const ProcessModID &rhs) : m_stop_id(rhs.m_stop_id), m_memory_id(rhs.m_memory_id) {} const ProcessModID &operator=(const ProcessModID &rhs) { if (this != &rhs) { m_stop_id = rhs.m_stop_id; m_memory_id = rhs.m_memory_id; } return *this; } ~ProcessModID() = default; void BumpStopID() { m_stop_id++; if (!IsLastResumeForUserExpression()) m_last_natural_stop_id++; } void BumpMemoryID() { m_memory_id++; } void BumpResumeID() { m_resume_id++; if (m_running_user_expression > 0) m_last_user_expression_resume = m_resume_id; } uint32_t GetStopID() const { return m_stop_id; } uint32_t GetLastNaturalStopID() const { return m_last_natural_stop_id; } uint32_t GetMemoryID() const { return m_memory_id; } uint32_t GetResumeID() const { return m_resume_id; } uint32_t GetLastUserExpressionResumeID() const { return m_last_user_expression_resume; } bool MemoryIDEqual(const ProcessModID &compare) const { return m_memory_id == compare.m_memory_id; } bool StopIDEqual(const ProcessModID &compare) const { return m_stop_id == compare.m_stop_id; } void SetInvalid() { m_stop_id = UINT32_MAX; } bool IsValid() const { return m_stop_id != UINT32_MAX; } bool IsLastResumeForUserExpression() const { // If we haven't yet resumed the target, then it can't be for a user // expression... if (m_resume_id == 0) return false; return m_resume_id == m_last_user_expression_resume; } void SetRunningUserExpression(bool on) { if (on) m_running_user_expression++; else m_running_user_expression--; } void SetStopEventForLastNaturalStopID(lldb::EventSP event_sp) { m_last_natural_stop_event = event_sp; } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { if (stop_id == m_last_natural_stop_id) return m_last_natural_stop_event; return lldb::EventSP(); } private: uint32_t m_stop_id; uint32_t m_last_natural_stop_id; uint32_t m_resume_id; uint32_t m_memory_id; uint32_t m_last_user_expression_resume; uint32_t m_running_user_expression; lldb::EventSP m_last_natural_stop_event; }; inline bool operator==(const ProcessModID &lhs, const ProcessModID &rhs) { if (lhs.StopIDEqual(rhs) && lhs.MemoryIDEqual(rhs)) return true; else return false; } inline bool operator!=(const ProcessModID &lhs, const ProcessModID &rhs) { return (!lhs.StopIDEqual(rhs) || !lhs.MemoryIDEqual(rhs)); } //---------------------------------------------------------------------- /// @class Process Process.h "lldb/Target/Process.h" /// @brief A plug-in interface definition class for debugging a process. //---------------------------------------------------------------------- class Process : public std::enable_shared_from_this, public ProcessProperties, public UserID, public Broadcaster, public ExecutionContextScope, public PluginInterface { friend class FunctionCaller; // For WaitForStateChangeEventsPrivate friend class Debugger; // For PopProcessIOHandler and ProcessIOHandlerIsActive friend class DynamicLoader; // For LoadOperatingSystemPlugin friend class ProcessEventData; friend class StopInfo; friend class Target; friend class ThreadList; public: //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ enum { eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5), }; enum { eBroadcastInternalStateControlStop = (1 << 0), eBroadcastInternalStateControlPause = (1 << 1), eBroadcastInternalStateControlResume = (1 << 2) }; //------------------------------------------------------------------ /// Process warning types. //------------------------------------------------------------------ enum Warnings { eWarningsOptimization = 1 }; typedef Range LoadRange; // We use a read/write lock to allow on or more clients to // access the process state while the process is stopped (reader). // We lock the write lock to control access to the process // while it is running (readers, or clients that want the process // stopped can block waiting for the process to stop, or just // try to lock it to see if they can immediately access the stopped // process. If the try read lock fails, then the process is running. typedef ProcessRunLock::ProcessRunLocker StopLocker; // These two functions fill out the Broadcaster interface: static ConstString &GetStaticBroadcasterClass(); ConstString &GetBroadcasterClass() const override { return GetStaticBroadcasterClass(); } //------------------------------------------------------------------ /// A notification structure that can be used by clients to listen /// for changes in a process's lifetime. /// /// @see RegisterNotificationCallbacks (const Notifications&) /// @see UnregisterNotificationCallbacks (const Notifications&) //------------------------------------------------------------------ #ifndef SWIG typedef struct { void *baton; void (*initialize)(void *baton, Process *process); void (*process_state_changed)(void *baton, Process *process, lldb::StateType state); } Notifications; class ProcessEventData : public EventData { friend class Process; public: ProcessEventData(); ProcessEventData(const lldb::ProcessSP &process, lldb::StateType state); ~ProcessEventData() override; static const ConstString &GetFlavorString(); const ConstString &GetFlavor() const override; lldb::ProcessSP GetProcessSP() const { return m_process_wp.lock(); } lldb::StateType GetState() const { return m_state; } bool GetRestarted() const { return m_restarted; } size_t GetNumRestartedReasons() { return m_restarted_reasons.size(); } const char *GetRestartedReasonAtIndex(size_t idx) { return ((idx < m_restarted_reasons.size()) ? m_restarted_reasons[idx].c_str() : nullptr); } bool GetInterrupted() const { return m_interrupted; } void Dump(Stream *s) const override; void DoOnRemoval(Event *event_ptr) override; static const Process::ProcessEventData * GetEventDataFromEvent(const Event *event_ptr); static lldb::ProcessSP GetProcessFromEvent(const Event *event_ptr); static lldb::StateType GetStateFromEvent(const Event *event_ptr); static bool GetRestartedFromEvent(const Event *event_ptr); static size_t GetNumRestartedReasons(const Event *event_ptr); static const char *GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx); static void AddRestartedReason(Event *event_ptr, const char *reason); static void SetRestartedInEvent(Event *event_ptr, bool new_value); static bool GetInterruptedFromEvent(const Event *event_ptr); static void SetInterruptedInEvent(Event *event_ptr, bool new_value); static bool SetUpdateStateOnRemoval(Event *event_ptr); private: void SetUpdateStateOnRemoval() { m_update_state++; } void SetRestarted(bool new_value) { m_restarted = new_value; } void SetInterrupted(bool new_value) { m_interrupted = new_value; } void AddRestartedReason(const char *reason) { m_restarted_reasons.push_back(reason); } lldb::ProcessWP m_process_wp; lldb::StateType m_state; std::vector m_restarted_reasons; bool m_restarted; // For "eStateStopped" events, this is true if the target // was automatically restarted. int m_update_state; bool m_interrupted; DISALLOW_COPY_AND_ASSIGN(ProcessEventData); }; #endif // SWIG //------------------------------------------------------------------ /// Construct with a shared pointer to a target, and the Process listener. /// Uses the Host UnixSignalsSP by default. //------------------------------------------------------------------ Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); //------------------------------------------------------------------ /// Construct with a shared pointer to a target, the Process listener, /// and the appropriate UnixSignalsSP for the process. //------------------------------------------------------------------ Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const lldb::UnixSignalsSP &unix_signals_sp); //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ ~Process() override; static void SettingsInitialize(); static void SettingsTerminate(); static const ProcessPropertiesSP &GetGlobalProperties(); //------------------------------------------------------------------ /// Find a Process plug-in that can debug \a module using the /// currently selected architecture. /// /// Scans all loaded plug-in interfaces that implement versions of /// the Process plug-in interface and returns the first instance /// that can debug the file. /// /// @param[in] module_sp /// The module shared pointer that this process will debug. /// /// @param[in] plugin_name /// If nullptr, select the best plug-in for the binary. If non-nullptr /// then look for a plugin whose PluginInfo's name matches /// this string. /// /// @see Process::CanDebug () //------------------------------------------------------------------ static lldb::ProcessSP FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); //------------------------------------------------------------------ /// Static function that can be used with the \b host function /// Host::StartMonitoringChildProcess (). /// /// This function can be used by lldb_private::Process subclasses /// when they want to watch for a local process and have its exit /// status automatically set when the host child process exits. /// Subclasses should call Host::StartMonitoringChildProcess () /// with: /// callback = Process::SetHostProcessExitStatus /// pid = Process::GetID() /// monitor_signals = false //------------------------------------------------------------------ static bool SetProcessExitStatus(lldb::pid_t pid, // The process ID we want to monitor bool exited, int signo, // Zero for no signal int status); // Exit value of process if signal is zero lldb::ByteOrder GetByteOrder() const; uint32_t GetAddressByteSize() const; uint32_t GetUniqueID() const { return m_process_unique_id; } //------------------------------------------------------------------ /// Check if a plug-in instance can debug the file in \a module. /// /// Each plug-in is given a chance to say whether it can debug /// the file in \a module. If the Process plug-in instance can /// debug a file on the current system, it should return \b true. /// /// @return /// Returns \b true if this Process plug-in instance can /// debug the executable, \b false otherwise. //------------------------------------------------------------------ virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) = 0; //------------------------------------------------------------------ /// This object is about to be destroyed, do any necessary cleanup. /// /// Subclasses that override this method should always call this /// superclass method. //------------------------------------------------------------------ virtual void Finalize(); //------------------------------------------------------------------ /// Return whether this object is valid (i.e. has not been finalized.) /// /// @return /// Returns \b true if this Process has not been finalized /// and \b false otherwise. //------------------------------------------------------------------ bool IsValid() const { return !m_finalize_called; } //------------------------------------------------------------------ /// Return a multi-word command object that can be used to expose /// plug-in specific commands. /// /// This object will be used to resolve plug-in commands and can be /// triggered by a call to: /// /// (lldb) process commmand /// /// @return /// A CommandObject which can be one of the concrete subclasses /// of CommandObject like CommandObjectRaw, CommandObjectParsed, /// or CommandObjectMultiword. //------------------------------------------------------------------ virtual CommandObject *GetPluginCommandObject() { return nullptr; } //------------------------------------------------------------------ /// Launch a new process. /// /// Launch a new process by spawning a new process using the /// target object's executable module's file as the file to launch. /// /// This function is not meant to be overridden by Process /// subclasses. It will first call Process::WillLaunch (Module *) /// and if that returns \b true, Process::DoLaunch (Module*, /// char const *[],char const *[],const char *,const char *, /// const char *) will be called to actually do the launching. If /// DoLaunch returns \b true, then Process::DidLaunch() will be /// called. /// /// @param[in] launch_info /// Details regarding the environment, STDIN/STDOUT/STDERR /// redirection, working path, etc. related to the requested launch. /// /// @return /// An error object. Call GetID() to get the process ID if /// the error object is success. //------------------------------------------------------------------ virtual Error Launch(ProcessLaunchInfo &launch_info); virtual Error LoadCore(); virtual Error DoLoadCore() { Error error; error.SetErrorStringWithFormat( "error: %s does not support loading core files.", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Get the dynamic loader plug-in for this process. /// /// The default action is to let the DynamicLoader plug-ins check /// the main executable and the DynamicLoader will select itself /// automatically. Subclasses can override this if inspecting the /// executable is not desired, or if Process subclasses can only /// use a specific DynamicLoader plug-in. //------------------------------------------------------------------ virtual DynamicLoader *GetDynamicLoader(); //------------------------------------------------------------------ // Returns AUXV structure found in many ELF-based environments. // // The default action is to return an empty data buffer. // // @return // A data buffer containing the contents of the AUXV data. //------------------------------------------------------------------ virtual const lldb::DataBufferSP GetAuxvData(); //------------------------------------------------------------------ /// Sometimes processes know how to retrieve and load shared libraries. /// This is normally done by DynamicLoader plug-ins, but sometimes the /// connection to the process allows retrieving this information. The /// dynamic loader plug-ins can use this function if they can't /// determine the current shared library load state. /// /// @return /// The number of shared libraries that were loaded //------------------------------------------------------------------ virtual size_t LoadModules() { return 0; } virtual size_t LoadModules(LoadedModuleInfoList &) { return 0; } protected: virtual JITLoaderList &GetJITLoaders(); public: //------------------------------------------------------------------ /// Get the system runtime plug-in for this process. /// /// @return /// Returns a pointer to the SystemRuntime plugin for this Process /// if one is available. Else returns nullptr. //------------------------------------------------------------------ virtual SystemRuntime *GetSystemRuntime(); //------------------------------------------------------------------ /// Attach to an existing process using the process attach info. /// /// This function is not meant to be overridden by Process /// subclasses. It will first call WillAttach (lldb::pid_t) /// or WillAttach (const char *), and if that returns \b /// true, DoAttach (lldb::pid_t) or DoAttach (const char *) will /// be called to actually do the attach. If DoAttach returns \b /// true, then Process::DidAttach() will be called. /// /// @param[in] pid /// The process ID that we should attempt to attach to. /// /// @return /// Returns \a pid if attaching was successful, or /// LLDB_INVALID_PROCESS_ID if attaching fails. //------------------------------------------------------------------ virtual Error Attach(ProcessAttachInfo &attach_info); //------------------------------------------------------------------ /// Attach to a remote system via a URL /// /// @param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// @param[in] remote_url /// The URL format that we are connecting to. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error ConnectRemote(Stream *strm, llvm::StringRef remote_url); bool GetShouldDetach() const { return m_should_detach; } void SetShouldDetach(bool b) { m_should_detach = b; } //------------------------------------------------------------------ /// Get the image information address for the current process. /// /// Some runtimes have system functions that can help dynamic /// loaders locate the dynamic loader information needed to observe /// shared libraries being loaded or unloaded. This function is /// in the Process interface (as opposed to the DynamicLoader /// interface) to ensure that remote debugging can take advantage of /// this functionality. /// /// @return /// The address of the dynamic loader information, or /// LLDB_INVALID_ADDRESS if this is not supported by this /// interface. //------------------------------------------------------------------ virtual lldb::addr_t GetImageInfoAddress(); //------------------------------------------------------------------ /// Called when the process is about to broadcast a public stop. /// /// There are public and private stops. Private stops are when the /// process is doing things like stepping and the client doesn't /// need to know about starts and stop that implement a thread plan. /// Single stepping over a source line in code might end up being /// implemented by one or more process starts and stops. Public stops /// are when clients will be notified that the process is stopped. /// These events typically trigger UI updates (thread stack frames to /// be displayed, variables to be displayed, and more). This function /// can be overriden and allows process subclasses to do something /// before the eBroadcastBitStateChanged event is sent to public /// clients. //------------------------------------------------------------------ virtual void WillPublicStop() {} //------------------------------------------------------------------ /// Register for process and thread notifications. /// /// Clients can register notification callbacks by filling out a /// Process::Notifications structure and calling this function. /// /// @param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// @see Process::Notifications //------------------------------------------------------------------ #ifndef SWIG void RegisterNotificationCallbacks(const Process::Notifications &callbacks); #endif //------------------------------------------------------------------ /// Unregister for process and thread notifications. /// /// Clients can unregister notification callbacks by passing a copy of /// the original baton and callbacks in \a callbacks. /// /// @param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// @return /// Returns \b true if the notification callbacks were /// successfully removed from the process, \b false otherwise. /// /// @see Process::Notifications //------------------------------------------------------------------ #ifndef SWIG bool UnregisterNotificationCallbacks(const Process::Notifications &callbacks); #endif //================================================================== // Built in Process Control functions //================================================================== //------------------------------------------------------------------ /// Resumes all of a process's threads as configured using the /// Thread run control functions. /// /// Threads for a process should be updated with one of the run /// control actions (resume, step, or suspend) that they should take /// when the process is resumed. If no run control action is given /// to a thread it will be resumed by default. /// /// This function is not meant to be overridden by Process /// subclasses. This function will take care of disabling any /// breakpoints that threads may be stopped at, single stepping, and /// re-enabling breakpoints, and enabling the basic flow control /// that the plug-in instances need not worry about. /// /// N.B. This function also sets the Write side of the Run Lock, /// which is unset when the corresponding stop event is pulled off /// the Public Event Queue. If you need to resume the process without /// setting the Run Lock, use PrivateResume (though you should only do /// that from inside the Process class. /// /// @return /// Returns an error object. /// /// @see Thread:Resume() /// @see Thread:Step() /// @see Thread:Suspend() //------------------------------------------------------------------ Error Resume(); Error ResumeSynchronous(Stream *stream); //------------------------------------------------------------------ /// Halts a running process. /// /// This function is not meant to be overridden by Process /// subclasses. /// If the process is successfully halted, a eStateStopped /// process event with GetInterrupted will be broadcast. If false, we will /// halt the process with no events generated by the halt. /// /// @param[in] clear_thread_plans /// If true, when the process stops, clear all thread plans. /// /// @param[in] use_run_lock /// Whether to release the run lock after the stop. /// /// @return /// Returns an error object. If the error is empty, the process is /// halted. /// otherwise the halt has failed. //------------------------------------------------------------------ Error Halt(bool clear_thread_plans = false, bool use_run_lock = true); //------------------------------------------------------------------ /// Detaches from a running or stopped process. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @param[in] keep_stopped /// If true, don't resume the process on detach. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Error Detach(bool keep_stopped); //------------------------------------------------------------------ /// Kills the process and shuts down all threads that were spawned /// to track and monitor the process. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @param[in] force_kill /// Whether lldb should force a kill (instead of a detach) from /// the inferior process. Normally if lldb launched a binary and /// Destory is called, lldb kills it. If lldb attached to a /// running process and Destory is called, lldb detaches. If /// this behavior needs to be over-ridden, this is the bool that /// can be used. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Error Destroy(bool force_kill); //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Error Signal(int signal); void SetUnixSignals(lldb::UnixSignalsSP &&signals_sp); const lldb::UnixSignalsSP &GetUnixSignals(); //================================================================== // Plug-in Process Control Overrides //================================================================== //------------------------------------------------------------------ /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillAttachToProcessWithID(lldb::pid_t pid) { return Error(); } //------------------------------------------------------------------ /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return Error(); } //------------------------------------------------------------------ /// Attach to a remote system via a URL /// /// @param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// @param[in] remote_url /// The URL format that we are connecting to. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { Error error; error.SetErrorString("remote connections are not supported"); return error; } //------------------------------------------------------------------ /// Attach to an existing process using a process ID. /// /// @param[in] pid /// The process ID that we should attempt to attach to. /// /// @param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// @return /// Returns a successful Error attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. /// hanming : need flag //------------------------------------------------------------------ virtual Error DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) { Error error; error.SetErrorStringWithFormat( "error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Attach to an existing process using a partial process name. /// /// @param[in] process_name /// The name of the process to attach to. /// /// @param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// @return /// Returns a successful Error attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. //------------------------------------------------------------------ virtual Error DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) { Error error; error.SetErrorString("attach by name is not supported"); return error; } //------------------------------------------------------------------ /// Called after attaching a process. /// /// @param[in] process_arch /// If you can figure out the process architecture after attach, fill it /// in here. /// /// Allow Process plug-ins to execute some code after attaching to /// a process. //------------------------------------------------------------------ virtual void DidAttach(ArchSpec &process_arch) { process_arch.Clear(); } //------------------------------------------------------------------ /// Called after a process re-execs itself. /// /// Allow Process plug-ins to execute some code after a process has /// exec'ed itself. Subclasses typically should override DoDidExec() /// as the lldb_private::Process class needs to remove its dynamic /// loader, runtime, ABI and other plug-ins, as well as unload all /// shared libraries. //------------------------------------------------------------------ virtual void DidExec(); //------------------------------------------------------------------ /// Subclasses of Process should implement this function if they /// need to do anything after a process exec's itself. //------------------------------------------------------------------ virtual void DoDidExec() {} //------------------------------------------------------------------ /// Called before launching to a process. /// /// Allow Process plug-ins to execute some code before launching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillLaunch(Module *module) { return Error(); } //------------------------------------------------------------------ /// Launch a new process. /// /// Launch a new process by spawning a new process using /// \a exe_module's file as the file to launch. Launch details are /// provided in \a launch_info. /// /// @param[in] exe_module /// The module from which to extract the file specification and /// launch. /// /// @param[in] launch_info /// Details (e.g. arguments, stdio redirection, etc.) for the /// requested launch. /// /// @return /// An Error instance indicating success or failure of the /// operation. //------------------------------------------------------------------ virtual Error DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Error error; error.SetErrorStringWithFormat( "error: %s does not support launching processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after launching a process. /// /// Allow Process plug-ins to execute some code after launching /// a process. //------------------------------------------------------------------ virtual void DidLaunch() {} //------------------------------------------------------------------ /// Called before resuming to a process. /// /// Allow Process plug-ins to execute some code before resuming a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillResume() { return Error(); } //------------------------------------------------------------------ /// Resumes all of a process's threads as configured using the /// Thread run control functions. /// /// Threads for a process should be updated with one of the run /// control actions (resume, step, or suspend) that they should take /// when the process is resumed. If no run control action is given /// to a thread it will be resumed by default. /// /// @return /// Returns \b true if the process successfully resumes using /// the thread run control actions, \b false otherwise. /// /// @see Thread:Resume() /// @see Thread:Step() /// @see Thread:Suspend() //------------------------------------------------------------------ virtual Error DoResume() { Error error; error.SetErrorStringWithFormat( "error: %s does not support resuming processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after resuming a process. /// /// Allow Process plug-ins to execute some code after resuming /// a process. //------------------------------------------------------------------ virtual void DidResume() {} //------------------------------------------------------------------ /// Called before halting to a process. /// /// Allow Process plug-ins to execute some code before halting a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillHalt() { return Error(); } //------------------------------------------------------------------ /// Halts a running process. /// /// DoHalt must produce one and only one stop StateChanged event if it /// actually /// stops the process. If the stop happens through some natural event (for /// instance a SIGSTOP), then forwarding that event will do. Otherwise, you /// must /// generate the event manually. This function is called from the context of /// the /// private state thread. /// /// @param[out] caused_stop /// If true, then this Halt caused the stop, otherwise, the /// process was already stopped. /// /// @return /// Returns \b true if the process successfully halts, \b false /// otherwise. //------------------------------------------------------------------ virtual Error DoHalt(bool &caused_stop) { Error error; error.SetErrorStringWithFormat( "error: %s does not support halting processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after halting a process. /// /// Allow Process plug-ins to execute some code after halting /// a process. //------------------------------------------------------------------ virtual void DidHalt() {} //------------------------------------------------------------------ /// Called before detaching from a process. /// /// Allow Process plug-ins to execute some code before detaching /// from a process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error WillDetach() { return Error(); } //------------------------------------------------------------------ /// Detaches from a running or stopped process. /// /// @return /// Returns \b true if the process successfully detaches, \b /// false otherwise. //------------------------------------------------------------------ virtual Error DoDetach(bool keep_stopped) { Error error; error.SetErrorStringWithFormat( "error: %s does not support detaching from processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after detaching from a process. /// /// Allow Process plug-ins to execute some code after detaching /// from a process. //------------------------------------------------------------------ virtual void DidDetach() {} virtual bool DetachRequiresHalt() { return false; } //------------------------------------------------------------------ /// Called before sending a signal to a process. /// /// Allow Process plug-ins to execute some code before sending a /// signal to a process. /// /// @return /// Returns no error if it is safe to proceed with a call to /// Process::DoSignal(int), otherwise an error describing what /// prevents the signal from being sent. //------------------------------------------------------------------ virtual Error WillSignal() { return Error(); } //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error DoSignal(int signal) { Error error; error.SetErrorStringWithFormat( "error: %s does not support sending signals to processes", GetPluginName().GetCString()); return error; } virtual Error WillDestroy() { return Error(); } virtual Error DoDestroy() = 0; virtual void DidDestroy() {} virtual bool DestroyRequiresHalt() { return true; } //------------------------------------------------------------------ /// Called after sending a signal to a process. /// /// Allow Process plug-ins to execute some code after sending a /// signal to a process. //------------------------------------------------------------------ virtual void DidSignal() {} //------------------------------------------------------------------ /// Currently called as part of ShouldStop. /// FIXME: Should really happen when the target stops before the /// event is taken from the queue... /// /// This callback is called as the event /// is about to be queued up to allow Process plug-ins to execute /// some code prior to clients being notified that a process was /// stopped. Common operations include updating the thread list, /// invalidating any thread state (registers, stack, etc) prior to /// letting the notification go out. /// //------------------------------------------------------------------ virtual void RefreshStateAfterStop() = 0; //------------------------------------------------------------------ /// Sometimes the connection to a process can detect the host OS /// version that the process is running on. The current platform /// should be checked first in case the platform is connected, but /// clients can fall back onto this function if the platform fails /// to identify the host OS version. The platform should be checked /// first in case you are running a simulator platform that might /// itself be running natively, but have different heuristics for /// figuring out which OS is is emulating. /// /// @param[out] major /// The major OS version, or UINT32_MAX if it can't be determined /// /// @param[out] minor /// The minor OS version, or UINT32_MAX if it can't be determined /// /// @param[out] update /// The update OS version, or UINT32_MAX if it can't be determined /// /// @return /// Returns \b true if the host OS version info was filled in /// and \b false otherwise. //------------------------------------------------------------------ virtual bool GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { major = UINT32_MAX; minor = UINT32_MAX; update = UINT32_MAX; return false; } //------------------------------------------------------------------ /// Get the target object pointer for this module. /// /// @return /// A Target object pointer to the target that owns this /// module. //------------------------------------------------------------------ Target &GetTarget() { return *m_target_sp.lock(); } //------------------------------------------------------------------ /// Get the const target object pointer for this module. /// /// @return /// A const Target object pointer to the target that owns this /// module. //------------------------------------------------------------------ const Target &GetTarget() const { return *m_target_sp.lock(); } //------------------------------------------------------------------ /// Flush all data in the process. /// /// Flush the memory caches, all threads, and any other cached data /// in the process. /// /// This function can be called after a world changing event like /// adding a new symbol file, or after the process makes a large /// context switch (from boot ROM to booted into an OS). //------------------------------------------------------------------ void Flush(); //------------------------------------------------------------------ /// Get accessor for the current process state. /// /// @return /// The current state of the process. /// /// @see lldb::StateType //------------------------------------------------------------------ lldb::StateType GetState(); lldb::ExpressionResults RunThreadPlan(ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, const EvaluateExpressionOptions &options, DiagnosticManager &diagnostic_manager); static const char *ExecutionResultAsCString(lldb::ExpressionResults result); void GetStatus(Stream &ostrm); size_t GetThreadStatus(Stream &ostrm, bool only_threads_with_stop_reason, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format); void SendAsyncInterrupt(); //------------------------------------------------------------------ // Notify this process class that modules got loaded. // // If subclasses override this method, they must call this version // before doing anything in the subclass version of the function. //------------------------------------------------------------------ virtual void ModulesDidLoad(ModuleList &module_list); //------------------------------------------------------------------ /// Retrieve the list of shared libraries that are loaded for this process /// This method is used on pre-macOS 10.12, pre-iOS 10, pre-tvOS 10, /// pre-watchOS 3 systems. The following two methods are for newer versions /// of those OSes. /// /// For certain platforms, the time it takes for the DynamicLoader plugin to /// read all of the shared libraries out of memory over a slow communication /// channel may be too long. In that instance, the gdb-remote stub may be /// able to retrieve the necessary information about the solibs out of memory /// and return a concise summary sufficient for the DynamicLoader plugin. /// /// @param [in] image_list_address /// The address where the table of shared libraries is stored in memory, /// if that is appropriate for this platform. Else this may be /// passed as LLDB_INVALID_ADDRESS. /// /// @param [in] image_count /// The number of shared libraries that are present in this process, if /// that is appropriate for this platofrm Else this may be passed as /// LLDB_INVALID_ADDRESS. /// /// @return /// A StructureDataSP object which, if non-empty, will contain the /// information the DynamicLoader needs to get the initial scan of /// solibs resolved. //------------------------------------------------------------------ virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, lldb::addr_t image_count) { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // the full list of loaded shared libraries without needing any input. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // information about binaries given their load addresses. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { return StructuredData::ObjectSP(); } //------------------------------------------------------------------ // Get information about the library shared cache, if that exists // // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // information about the library shared cache (a set of standard libraries // that are // loaded at the same location for all processes on a system) in use. //------------------------------------------------------------------ virtual lldb_private::StructuredData::ObjectSP GetSharedCacheInfo() { return StructuredData::ObjectSP(); } //------------------------------------------------------------------ /// Print a user-visible warning about a module being built with optimization /// /// Prints a async warning message to the user one time per Module /// where a function is found that was compiled with optimization, per /// Process. /// /// @param [in] sc /// A SymbolContext with eSymbolContextFunction and eSymbolContextModule /// pre-computed. //------------------------------------------------------------------ void PrintWarningOptimization(const SymbolContext &sc); virtual bool GetProcessInfo(ProcessInstanceInfo &info); public: //------------------------------------------------------------------ /// Get the exit status for a process. /// /// @return /// The process's return code, or -1 if the current process /// state is not eStateExited. //------------------------------------------------------------------ int GetExitStatus(); //------------------------------------------------------------------ /// Get a textual description of what the process exited. /// /// @return /// The textual description of why the process exited, or nullptr /// if there is no description available. //------------------------------------------------------------------ const char *GetExitDescription(); virtual void DidExit() {} //------------------------------------------------------------------ /// Get the Modification ID of the process. /// /// @return /// The modification ID of the process. //------------------------------------------------------------------ ProcessModID GetModID() const { return m_mod_id; } const ProcessModID &GetModIDRef() const { return m_mod_id; } uint32_t GetStopID() const { return m_mod_id.GetStopID(); } uint32_t GetResumeID() const { return m_mod_id.GetResumeID(); } uint32_t GetLastUserExpressionResumeID() const { return m_mod_id.GetLastUserExpressionResumeID(); } uint32_t GetLastNaturalStopID() const { return m_mod_id.GetLastNaturalStopID(); } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { return m_mod_id.GetStopEventForStopID(stop_id); } //------------------------------------------------------------------ /// Set accessor for the process exit status (return code). /// /// Sometimes a child exits and the exit can be detected by global /// functions (signal handler for SIGCHLD for example). This /// accessor allows the exit status to be set from an external /// source. /// /// Setting this will cause a eStateExited event to be posted to /// the process event queue. /// /// @param[in] exit_status /// The value for the process's return code. /// /// @see lldb::StateType //------------------------------------------------------------------ virtual bool SetExitStatus(int exit_status, const char *cstr); //------------------------------------------------------------------ /// Check if a process is still alive. /// /// @return /// Returns \b true if the process is still valid, \b false /// otherwise. //------------------------------------------------------------------ virtual bool IsAlive(); //------------------------------------------------------------------ /// Before lldb detaches from a process, it warns the user that they are about /// to lose their debug session. /// In some cases, this warning doesn't need to be emitted -- for instance, /// with core file debugging where /// the user can reconstruct the "state" by simply re-running the debugger on /// the core file. /// /// @return // true if the user should be warned about detaching from this process. //------------------------------------------------------------------ virtual bool WarnBeforeDetach() const { return true; } //------------------------------------------------------------------ /// Actually do the reading of memory from a process. /// /// Subclasses must override this function and can return fewer /// bytes than requested when memory requests are too large. This /// class will break up the memory requests and keep advancing the /// arguments along as needed. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// @param[in] size /// The number of bytes to read. /// /// @param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// @return /// The number of bytes that were actually read into \a buf. //------------------------------------------------------------------ virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Error &error) = 0; //------------------------------------------------------------------ /// Read of memory from a process. /// /// This function will read memory from the current process's /// address space and remove any traps that may have been inserted /// into the memory. /// /// This function is not meant to be overridden by Process /// subclasses, the subclasses should implement /// Process::DoReadMemory (lldb::addr_t, size_t, void *). /// /// @param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// @param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// @param[in] size /// The number of bytes to read. /// /// @return /// The number of bytes that were actually read into \a buf. If /// the returned number is greater than zero, yet less than \a /// size, then this function will get called again with \a /// vm_addr, \a buf, and \a size updated appropriately. Zero is /// returned to indicate an error. //------------------------------------------------------------------ virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Error &error); //------------------------------------------------------------------ /// Read a NULL terminated string from memory /// /// This function will read a cache page at a time until a NULL /// string terminator is found. It will stop reading if an aligned /// sequence of NULL termination \a type_width bytes is not found /// before reading \a cstr_max_len bytes. The results are always /// guaranteed to be NULL terminated, and that no more than /// (max_bytes - type_width) bytes will be read. /// /// @param[in] vm_addr /// The virtual load address to start the memory read. /// /// @param[in] str /// A character buffer containing at least max_bytes. /// /// @param[in] max_bytes /// The maximum number of bytes to read. /// /// @param[in] error /// The error status of the read operation. /// /// @param[in] type_width /// The size of the null terminator (1 to 4 bytes per /// character). Defaults to 1. /// /// @return /// The error status or the number of bytes prior to the null terminator. //------------------------------------------------------------------ size_t ReadStringFromMemory(lldb::addr_t vm_addr, char *str, size_t max_bytes, Error &error, size_t type_width = 1); //------------------------------------------------------------------ /// Read a NULL terminated C string from memory /// /// This function will read a cache page at a time until the NULL /// C string terminator is found. It will stop reading if the NULL /// termination byte isn't found before reading \a cstr_max_len /// bytes, and the results are always guaranteed to be NULL /// terminated (at most cstr_max_len - 1 bytes will be read). //------------------------------------------------------------------ size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Error &error); size_t ReadCStringFromMemory(lldb::addr_t vm_addr, std::string &out_str, Error &error); size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf, size_t size, Error &error); //------------------------------------------------------------------ /// Reads an unsigned integer of the specified byte size from /// process memory. /// /// @param[in] load_addr /// A load address of the integer to read. /// /// @param[in] byte_size /// The size in byte of the integer to read. /// /// @param[in] fail_value /// The value to return if we fail to read an integer. /// /// @param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// @return /// The unsigned integer that was read from the process memory /// space. If the integer was smaller than a uint64_t, any /// unused upper bytes will be zero filled. If the process /// byte order differs from the host byte order, the integer /// value will be appropriately byte swapped into host byte /// order. //------------------------------------------------------------------ uint64_t ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Error &error); int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Error &error); lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Error &error); bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Error &error); //------------------------------------------------------------------ /// Actually do the writing of memory to a process. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// @param[in] size /// The number of bytes to write. /// /// @param[out] error /// An error value in case the memory write fails. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ virtual size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) { error.SetErrorStringWithFormat( "error: %s does not support writing to processes", GetPluginName().GetCString()); return 0; } //------------------------------------------------------------------ /// Write all or part of a scalar value to memory. /// /// The value contained in \a scalar will be swapped to match the /// byte order of the process that is being debugged. If \a size is /// less than the size of scalar, the least significant \a size bytes /// from scalar will be written. If \a size is larger than the byte /// size of scalar, then the extra space will be padded with zeros /// and the scalar value will be placed in the least significant /// bytes in memory. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] scalar /// The scalar to write to the debugged process. /// /// @param[in] size /// This value can be smaller or larger than the scalar value /// itself. If \a size is smaller than the size of \a scalar, /// the least significant bytes in \a scalar will be used. If /// \a size is larger than the byte size of \a scalar, then /// the extra space will be padded with zeros. If \a size is /// set to UINT32_MAX, then the size of \a scalar will be used. /// /// @param[out] error /// An error value in case the memory write fails. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ size_t WriteScalarToMemory(lldb::addr_t vm_addr, const Scalar &scalar, size_t size, Error &error); size_t ReadScalarIntegerFromMemory(lldb::addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Error &error); //------------------------------------------------------------------ /// Write memory to a process. /// /// This function will write memory to the current process's /// address space and maintain any traps that might be present due /// to software breakpoints. /// /// This function is not meant to be overridden by Process /// subclasses, the subclasses should implement /// Process::DoWriteMemory (lldb::addr_t, size_t, void *). /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// @param[in] size /// The number of bytes to write. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ // TODO: change this to take an ArrayRef size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error); //------------------------------------------------------------------ /// Actually allocate memory in the process. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ virtual lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, Error &error) { error.SetErrorStringWithFormat( "error: %s does not support allocating in the debug process", GetPluginName().GetCString()); return LLDB_INVALID_ADDRESS; } //------------------------------------------------------------------ /// The public interface to allocating memory in the process. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// @param[in,out] error /// An error object to fill in if things go wrong. /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ lldb::addr_t AllocateMemory(size_t size, uint32_t permissions, Error &error); //------------------------------------------------------------------ /// The public interface to allocating memory in the process, this also /// clears the allocated memory. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// @param[in/out] error /// An error object to fill in if things go wrong. /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ lldb::addr_t CallocateMemory(size_t size, uint32_t permissions, Error &error); //------------------------------------------------------------------ /// Resolve dynamically loaded indirect functions. /// /// @param[in] address /// The load address of the indirect function to resolve. /// /// @param[out] error /// An error value in case the resolve fails. /// /// @return /// The address of the resolved function. /// LLDB_INVALID_ADDRESS if the resolution failed. //------------------------------------------------------------------ virtual lldb::addr_t ResolveIndirectFunction(const Address *address, Error &error); //------------------------------------------------------------------ /// Locate the memory region that contains load_addr. /// /// If load_addr is within the address space the process has mapped /// range_info will be filled in with the start and end of that range /// as well as the permissions for that range and range_info.GetMapped /// will return true. /// /// If load_addr is outside any mapped region then range_info will /// have its start address set to load_addr and the end of the /// range will indicate the start of the next mapped range or be /// set to LLDB_INVALID_ADDRESS if there are no valid mapped ranges /// between load_addr and the end of the process address space. /// /// GetMemoryRegionInfo will only return an error if it is /// unimplemented for the current process. /// /// @param[in] load_addr /// The load address to query the range_info for. /// /// @param[out] range_info /// An range_info value containing the details of the range. /// /// @return /// An error value. //------------------------------------------------------------------ virtual Error GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { Error error; error.SetErrorString("Process::GetMemoryRegionInfo() not supported"); return error; } //------------------------------------------------------------------ /// Obtain all the mapped memory regions within this process. /// /// @param[out] region_list /// A vector to contain MemoryRegionInfo objects for all mapped /// ranges. /// /// @return /// An error value. //------------------------------------------------------------------ virtual Error GetMemoryRegions(std::vector ®ion_list); virtual Error GetWatchpointSupportInfo(uint32_t &num) { Error error; num = 0; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } virtual Error GetWatchpointSupportInfo(uint32_t &num, bool &after) { Error error; num = 0; after = true; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, lldb::addr_t header_addr, size_t size_to_read = 512); //------------------------------------------------------------------ /// Attempt to get the attributes for a region of memory in the process. /// /// It may be possible for the remote debug server to inspect attributes /// for a region of memory in the process, such as whether there is a /// valid page of memory at a given address or whether that page is /// readable/writable/executable by the process. /// /// @param[in] load_addr /// The address of interest in the process. /// /// @param[out] permissions /// If this call returns successfully, this bitmask will have /// its Permissions bits set to indicate whether the region is /// readable/writable/executable. If this call fails, the /// bitmask values are undefined. /// /// @return /// Returns true if it was able to determine the attributes of the /// memory region. False if not. //------------------------------------------------------------------ virtual bool GetLoadAddressPermissions(lldb::addr_t load_addr, uint32_t &permissions); //------------------------------------------------------------------ /// Determines whether executing JIT-compiled code in this process /// is possible. /// /// @return /// True if execution of JIT code is possible; false otherwise. //------------------------------------------------------------------ bool CanJIT(); //------------------------------------------------------------------ /// Sets whether executing JIT-compiled code in this process /// is possible. /// /// @param[in] can_jit /// True if execution of JIT code is possible; false otherwise. //------------------------------------------------------------------ void SetCanJIT(bool can_jit); //------------------------------------------------------------------ /// Determines whether executing function calls using the interpreter /// is possible for this process. /// /// @return /// True if possible; false otherwise. //------------------------------------------------------------------ bool CanInterpretFunctionCalls() { return m_can_interpret_function_calls; } //------------------------------------------------------------------ /// Sets whether executing function calls using the interpreter /// is possible for this process. /// /// @param[in] can_interpret_function_calls /// True if possible; false otherwise. //------------------------------------------------------------------ void SetCanInterpretFunctionCalls(bool can_interpret_function_calls) { m_can_interpret_function_calls = can_interpret_function_calls; } //------------------------------------------------------------------ /// Sets whether executing code in this process is possible. /// This could be either through JIT or interpreting. /// /// @param[in] can_run_code /// True if execution of code is possible; false otherwise. //------------------------------------------------------------------ void SetCanRunCode(bool can_run_code); //------------------------------------------------------------------ /// Actually deallocate memory in the process. /// /// This function will deallocate memory in the process's address /// space that was allocated with AllocateMemory. /// /// @param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// @return /// \btrue if the memory was deallocated, \bfalse otherwise. //------------------------------------------------------------------ virtual Error DoDeallocateMemory(lldb::addr_t ptr) { Error error; error.SetErrorStringWithFormat( "error: %s does not support deallocating in the debug process", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// The public interface to deallocating memory in the process. /// /// This function will deallocate memory in the process's address /// space that was allocated with AllocateMemory. /// /// @param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// @return /// \btrue if the memory was deallocated, \bfalse otherwise. //------------------------------------------------------------------ Error DeallocateMemory(lldb::addr_t ptr); //------------------------------------------------------------------ /// Get any available STDOUT. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDOUT. /// /// Note that the implementation will probably need to start a read /// thread in the background to make sure that the pipe is drained /// and the STDOUT buffered appropriately, to prevent the process /// from deadlocking trying to write to a full buffer. /// /// Events will be queued indicating that there is STDOUT available /// that can be retrieved using this function. /// /// @param[out] buf /// A buffer that will receive any STDOUT bytes that are /// currently available. /// /// @param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDOUT data. //------------------------------------------------------------------ virtual size_t GetSTDOUT(char *buf, size_t buf_size, Error &error); //------------------------------------------------------------------ /// Get any available STDERR. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDERR. /// /// Note that the implementation will probably need to start a read /// thread in the background to make sure that the pipe is drained /// and the STDERR buffered appropriately, to prevent the process /// from deadlocking trying to write to a full buffer. /// /// Events will be queued indicating that there is STDERR available /// that can be retrieved using this function. /// /// @param[in] buf /// A buffer that will receive any STDERR bytes that are /// currently available. /// /// @param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDERR data. //------------------------------------------------------------------ virtual size_t GetSTDERR(char *buf, size_t buf_size, Error &error); //------------------------------------------------------------------ /// Puts data into this process's STDIN. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDIN. /// /// @param[in] buf /// A buffer that contains the data to write to the process's STDIN. /// /// @param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// less than \a buf_size, another call to this function should /// be made to write the rest of the data. //------------------------------------------------------------------ virtual size_t PutSTDIN(const char *buf, size_t buf_size, Error &error) { error.SetErrorString("stdin unsupported"); return 0; } //------------------------------------------------------------------ /// Get any available profile data. /// /// @param[out] buf /// A buffer that will receive any profile data bytes that are /// currently available. /// /// @param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more profile data. //------------------------------------------------------------------ virtual size_t GetAsyncProfileData(char *buf, size_t buf_size, Error &error); //---------------------------------------------------------------------- // Process Breakpoints //---------------------------------------------------------------------- size_t GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site); virtual Error EnableBreakpointSite(BreakpointSite *bp_site) { Error error; error.SetErrorStringWithFormat( "error: %s does not support enabling breakpoints", GetPluginName().GetCString()); return error; } virtual Error DisableBreakpointSite(BreakpointSite *bp_site) { Error error; error.SetErrorStringWithFormat( "error: %s does not support disabling breakpoints", GetPluginName().GetCString()); return error; } // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of // read existing opcode, write breakpoint opcode, verify breakpoint opcode // doesn't work for a specific process plug-in. virtual Error EnableSoftwareBreakpoint(BreakpointSite *bp_site); // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of // restoring original opcode in memory and verifying the restored opcode // doesn't work for a specific process plug-in. virtual Error DisableSoftwareBreakpoint(BreakpointSite *bp_site); BreakpointSiteList &GetBreakpointSiteList(); const BreakpointSiteList &GetBreakpointSiteList() const; void DisableAllBreakpointSites(); Error ClearBreakpointSiteByID(lldb::user_id_t break_id); lldb::break_id_t CreateBreakpointSite(const lldb::BreakpointLocationSP &owner, bool use_hardware); Error DisableBreakpointSiteByID(lldb::user_id_t break_id); Error EnableBreakpointSiteByID(lldb::user_id_t break_id); // BreakpointLocations use RemoveOwnerFromBreakpointSite to remove // themselves from the owner's list of this breakpoint sites. void RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, lldb::BreakpointSiteSP &bp_site_sp); //---------------------------------------------------------------------- // Process Watchpoints (optional) //---------------------------------------------------------------------- virtual Error EnableWatchpoint(Watchpoint *wp, bool notify = true); virtual Error DisableWatchpoint(Watchpoint *wp, bool notify = true); //------------------------------------------------------------------ // Thread Queries //------------------------------------------------------------------ virtual bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; void UpdateThreadListIfNeeded(); ThreadList &GetThreadList() { return m_thread_list; } // When ExtendedBacktraces are requested, the HistoryThreads that are // created need an owner -- they're saved here in the Process. The // threads in this list are not iterated over - driver programs need to // request the extended backtrace calls starting from a root concrete // thread one by one. ThreadList &GetExtendedThreadList() { return m_extended_thread_list; } ThreadList::ThreadIterable Threads() { return m_thread_list.Threads(); } uint32_t GetNextThreadIndexID(uint64_t thread_id); lldb::ThreadSP CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context); // Returns true if an index id has been assigned to a thread. bool HasAssignedIndexIDToThread(uint64_t sb_thread_id); // Given a thread_id, it will assign a more reasonable index id for display to // the user. // If the thread_id has previously been assigned, the same index id will be // used. uint32_t AssignIndexIDToThread(uint64_t thread_id); //------------------------------------------------------------------ // Queue Queries //------------------------------------------------------------------ void UpdateQueueListIfNeeded(); QueueList &GetQueueList() { UpdateQueueListIfNeeded(); return m_queue_list; } QueueList::QueueIterable Queues() { UpdateQueueListIfNeeded(); return m_queue_list.Queues(); } //------------------------------------------------------------------ // Event Handling //------------------------------------------------------------------ lldb::StateType GetNextEvent(lldb::EventSP &event_sp); // Returns the process state when it is stopped. If specified, event_sp_ptr // is set to the event which triggered the stop. If wait_always = false, // and the process is already stopped, this function returns immediately. // If the process is hijacked and use_run_lock is true (the default), then // this // function releases the run lock after the stop. Setting use_run_lock to // false // will avoid this behavior. lldb::StateType WaitForProcessToStop(const Timeout &timeout, lldb::EventSP *event_sp_ptr = nullptr, bool wait_always = true, lldb::ListenerSP hijack_listener = lldb::ListenerSP(), Stream *stream = nullptr, bool use_run_lock = true); uint32_t GetIOHandlerID() const { return m_iohandler_sync.GetValue(); } //-------------------------------------------------------------------------------------- /// Waits for the process state to be running within a given msec timeout. /// /// The main purpose of this is to implement an interlock waiting for /// HandlePrivateEvent /// to push an IOHandler. /// /// @param[in] timeout_msec /// The maximum time length to wait for the process to transition to the /// eStateRunning state, specified in milliseconds. //-------------------------------------------------------------------------------------- void SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec); lldb::StateType GetStateChangedEvents( lldb::EventSP &event_sp, const Timeout &timeout, lldb::ListenerSP hijack_listener); // Pass an empty ListenerSP to use builtin listener //-------------------------------------------------------------------------------------- /// Centralize the code that handles and prints descriptions for process state /// changes. /// /// @param[in] event_sp /// The process state changed event /// /// @param[in] stream /// The output stream to get the state change description /// /// @param[in,out] pop_process_io_handler /// If this value comes in set to \b true, then pop the Process IOHandler /// if needed. /// Else this variable will be set to \b true or \b false to indicate if /// the process /// needs to have its process IOHandler popped. /// /// @return /// \b true if the event describes a process state changed event, \b false /// otherwise. //-------------------------------------------------------------------------------------- static bool HandleProcessStateChangedEvent(const lldb::EventSP &event_sp, Stream *stream, bool &pop_process_io_handler); Event *PeekAtStateChangedEvents(); class ProcessEventHijacker { public: ProcessEventHijacker(Process &process, lldb::ListenerSP listener_sp) : m_process(process) { m_process.HijackProcessEvents(listener_sp); } ~ProcessEventHijacker() { m_process.RestoreProcessEvents(); } private: Process &m_process; }; friend class ProcessEventHijacker; friend class ProcessProperties; //------------------------------------------------------------------ /// If you need to ensure that you and only you will hear about some public /// event, then make a new listener, set to listen to process events, and /// then call this with that listener. Then you will have to wait on that /// listener explicitly for events (rather than using the GetNextEvent & /// WaitFor* /// calls above. Be sure to call RestoreProcessEvents when you are done. /// /// @param[in] listener /// This is the new listener to whom all process events will be delivered. /// /// @return /// Returns \b true if the new listener could be installed, /// \b false otherwise. //------------------------------------------------------------------ bool HijackProcessEvents(lldb::ListenerSP listener_sp); //------------------------------------------------------------------ /// Restores the process event broadcasting to its normal state. /// //------------------------------------------------------------------ void RestoreProcessEvents(); const lldb::ABISP &GetABI(); OperatingSystem *GetOperatingSystem() { return m_os_ap.get(); } ArchSpec::StopInfoOverrideCallbackType GetStopInfoOverrideCallback() const { return m_stop_info_override_callback; } virtual LanguageRuntime *GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null = true); virtual CPPLanguageRuntime *GetCPPLanguageRuntime(bool retry_if_null = true); virtual ObjCLanguageRuntime * GetObjCLanguageRuntime(bool retry_if_null = true); bool IsPossibleDynamicValue(ValueObject &in_value); bool IsRunning() const; DynamicCheckerFunctions *GetDynamicCheckers() { return m_dynamic_checkers_ap.get(); } void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers); //------------------------------------------------------------------ /// Call this to set the lldb in the mode where it breaks on new thread /// creations, and then auto-restarts. This is useful when you are trying /// to run only one thread, but either that thread or the kernel is creating /// new threads in the process. If you stop when the thread is created, you /// can immediately suspend it, and keep executing only the one thread you /// intend. /// /// @return /// Returns \b true if we were able to start up the notification /// \b false otherwise. //------------------------------------------------------------------ virtual bool StartNoticingNewThreads() { return true; } //------------------------------------------------------------------ /// Call this to turn off the stop & notice new threads mode. /// /// @return /// Returns \b true if we were able to start up the notification /// \b false otherwise. //------------------------------------------------------------------ virtual bool StopNoticingNewThreads() { return true; } void SetRunningUserExpression(bool on); //------------------------------------------------------------------ // lldb::ExecutionContextScope pure virtual functions //------------------------------------------------------------------ lldb::TargetSP CalculateTarget() override; lldb::ProcessSP CalculateProcess() override { return shared_from_this(); } lldb::ThreadSP CalculateThread() override { return lldb::ThreadSP(); } lldb::StackFrameSP CalculateStackFrame() override { return lldb::StackFrameSP(); } void CalculateExecutionContext(ExecutionContext &exe_ctx) override; void SetSTDIOFileDescriptor(int file_descriptor); //------------------------------------------------------------------ // Add a permanent region of memory that should never be read or // written to. This can be used to ensure that memory reads or writes // to certain areas of memory never end up being sent to the // DoReadMemory or DoWriteMemory functions which can improve // performance. //------------------------------------------------------------------ void AddInvalidMemoryRegion(const LoadRange ®ion); //------------------------------------------------------------------ // Remove a permanent region of memory that should never be read or // written to that was previously added with AddInvalidMemoryRegion. //------------------------------------------------------------------ bool RemoveInvalidMemoryRange(const LoadRange ®ion); //------------------------------------------------------------------ // If the setup code of a thread plan needs to do work that might involve // calling a function in the target, it should not do that work directly // in one of the thread plan functions (DidPush/WillResume) because // such work needs to be handled carefully. Instead, put that work in // a PreResumeAction callback, and register it with the process. It will // get done before the actual "DoResume" gets called. //------------------------------------------------------------------ typedef bool(PreResumeActionCallback)(void *); void AddPreResumeAction(PreResumeActionCallback callback, void *baton); bool RunPreResumeActions(); void ClearPreResumeActions(); void ClearPreResumeAction(PreResumeActionCallback callback, void *baton); ProcessRunLock &GetRunLock(); virtual Error SendEventData(const char *data) { Error return_error("Sending an event is not supported for this process."); return return_error; } lldb::ThreadCollectionSP GetHistoryThreads(lldb::addr_t addr); lldb::InstrumentationRuntimeSP GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type); //------------------------------------------------------------------ /// Try to fetch the module specification for a module with the /// given file name and architecture. Process sub-classes have to /// override this method if they support platforms where the /// Platform object can't get the module spec for all module. /// /// @param[in] module_file_spec /// The file name of the module to get specification for. /// /// @param[in] arch /// The architecture of the module to get specification for. /// /// @param[out] module_spec /// The fetched module specification if the return value is /// \b true, unchanged otherwise. /// /// @return /// Returns \b true if the module spec fetched successfully, /// \b false otherwise. //------------------------------------------------------------------ virtual bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec); virtual void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, const llvm::Triple &triple) {} //------------------------------------------------------------------ /// Try to find the load address of a file. /// The load address is defined as the address of the first memory /// region what contains data mapped from the specified file. /// /// @param[in] file /// The name of the file whose load address we are looking for /// /// @param[out] is_loaded /// \b True if the file is loaded into the memory and false /// otherwise. /// /// @param[out] load_addr /// The load address of the file if it is loaded into the /// processes address space, LLDB_INVALID_ADDRESS otherwise. //------------------------------------------------------------------ virtual Error GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { return Error("Not supported"); } size_t AddImageToken(lldb::addr_t image_ptr); lldb::addr_t GetImagePtrFromToken(size_t token) const; void ResetImageToken(size_t token); //------------------------------------------------------------------ /// Find the next branch instruction to set a breakpoint on /// /// When instruction stepping through a source line, instead of /// stepping through each instruction, we can put a breakpoint on /// the next branch instruction (within the range of instructions /// we are stepping through) and continue the process to there, /// yielding significant performance benefits over instruction /// stepping. /// /// @param[in] default_stop_addr /// The address of the instruction where lldb would put a /// breakpoint normally. /// /// @param[in] range_bounds /// The range which the breakpoint must be contained within. /// Typically a source line. /// /// @return /// The address of the next branch instruction, or the end of /// the range provided in range_bounds. If there are any /// problems with the disassembly or getting the instructions, /// the original default_stop_addr will be returned. //------------------------------------------------------------------ Address AdvanceAddressToNextBranchInstruction(Address default_stop_addr, AddressRange range_bounds); //------------------------------------------------------------------ /// Configure asynchronous structured data feature. /// /// Each Process type that supports using an asynchronous StructuredData /// feature should implement this to enable/disable/configure the feature. /// The default implementation here will always return an error indiciating /// the feature is unsupported. /// /// StructuredDataPlugin implementations will call this to configure /// a feature that has been reported as being supported. /// /// @param[in] type_name /// The StructuredData type name as previously discovered by /// the Process-derived instance. /// /// @param[in] config /// Configuration data for the feature being enabled. This config /// data, which may be null, will be passed along to the feature /// to process. The feature will dictate whether this is a dictionary, /// an array or some other object. If the feature needs to be /// set up properly before it can be enabled, then the config should /// also take an enable/disable flag. /// /// @return /// Returns the result of attempting to configure the feature. //------------------------------------------------------------------ virtual Error ConfigureStructuredData(const ConstString &type_name, const StructuredData::ObjectSP &config_sp); //------------------------------------------------------------------ /// Broadcasts the given structured data object from the given /// plugin. /// /// StructuredDataPlugin instances can use this to optionally /// broadcast any of their data if they want to make it available /// for clients. The data will come in on the structured data /// event bit (eBroadcastBitStructuredData). /// /// @param[in] object_sp /// The structured data object to broadcast. /// /// @param[in] plugin_sp /// The plugin that will be reported in the event's plugin /// parameter. //------------------------------------------------------------------ void BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, const lldb::StructuredDataPluginSP &plugin_sp); //------------------------------------------------------------------ /// Returns the StructuredDataPlugin associated with a given type /// name, if there is one. /// /// There will only be a plugin for a given StructuredDataType if the /// debugged process monitor claims that the feature is supported. /// This is one way to tell whether a feature is available. /// /// @return /// The plugin if one is available for the specified feature; /// otherwise, returns an empty shared pointer. //------------------------------------------------------------------ lldb::StructuredDataPluginSP GetStructuredDataPlugin(const ConstString &type_name) const; + + //------------------------------------------------------------------ + /// Starts tracing with the configuration provided in options. To + /// enable tracing on the complete process the thread_id in the + /// options should be set to LLDB_INVALID_THREAD_ID. The API returns + /// a user_id which is needed by other API's that manipulate the + /// trace instance. + /// The handling of erroneous or unsupported configuration is left + /// to the trace technology implementations in the server, as they + /// could be returned as an error, or rounded to a valid + /// configuration to start tracing. In the later case the + /// GetTraceConfig should supply the actual used trace + /// configuration. + //------------------------------------------------------------------ + virtual lldb::user_id_t StartTrace(lldb::TraceOptionsSP &options, + Error &error) { + error.SetErrorString("Not implemented"); + return LLDB_INVALID_UID; + } + + //------------------------------------------------------------------ + /// Stops the tracing instance leading to deletion of the trace + /// data. The tracing instance is identified by the user_id which + /// is obtained when tracing was started from the StartTrace. + /// In case tracing of the complete process needs to be stopped + /// the thread_id should be set to LLDB_INVALID_THREAD_ID. + /// In the other case that tracing on an individual thread needs + /// to be stopped a thread_id can be supplied. + //------------------------------------------------------------------ + virtual void StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error) { + error.SetErrorString("Not implemented"); + } + + //------------------------------------------------------------------ + /// Provides the trace data as raw bytes. A buffer needs to be + /// supplied to copy the trace data. The exact behavior of this API + /// may vary across trace technology, as some may support partial + /// reading of the trace data from a specified offset while some + /// may not. The thread_id should be used to select a particular + /// thread for trace extraction. + //------------------------------------------------------------------ + virtual size_t GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error, void *buf, size_t size, + size_t offset = 0) { + error.SetErrorString("Not implemented"); + return 0; + } + + //------------------------------------------------------------------ + /// Similar API as above except for obtaining meta data + //------------------------------------------------------------------ + virtual size_t GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error, void *buf, size_t size, + size_t offset = 0) { + error.SetErrorString("Not implemented"); + return 0; + } + + //------------------------------------------------------------------ + /// API to obtain the trace configuration used by a trace instance. + /// Configurations that may be specific to some trace technology + /// should be stored in the custom parameters. The options are + /// transported to the server, which shall interpret accordingly. + /// The thread_id can be specified in the options to obtain the + /// configuration used by a specific thread. The thread_id specified + /// should also match the uid otherwise an error will be returned. + //------------------------------------------------------------------ + virtual void GetTraceConfig(lldb::user_id_t uid, Error &error, + lldb::TraceOptionsSP &options) { + error.SetErrorString("Not implemented"); + return; + } protected: void SetState(lldb::EventSP &event_sp); lldb::StateType GetPrivateState(); //------------------------------------------------------------------ /// The "private" side of resuming a process. This doesn't alter the /// state of m_run_lock, but just causes the process to resume. /// /// @return /// An Error object describing the success or failure of the resume. //------------------------------------------------------------------ Error PrivateResume(); //------------------------------------------------------------------ // Called internally //------------------------------------------------------------------ void CompleteAttach(); //------------------------------------------------------------------ /// Print a user-visible warning one time per Process /// /// A facility for printing a warning to the user once per repeat_key. /// /// warning_type is from the Process::Warnings enums. /// repeat_key is a pointer value that will be used to ensure that the /// warning message is not printed multiple times. For instance, with a /// warning about a function being optimized, you can pass the CompileUnit /// pointer to have the warning issued for only the first function in a /// CU, or the Function pointer to have it issued once for every function, /// or a Module pointer to have it issued once per Module. /// /// Classes outside Process should call a specific PrintWarning method /// so that the warning strings are all centralized in Process, instead of /// calling PrintWarning() directly. /// /// @param [in] warning_type /// One of the types defined in Process::Warnings. /// /// @param [in] repeat_key /// A pointer value used to ensure that the warning is only printed once. /// May be nullptr, indicating that the warning is printed unconditionally /// every time. /// /// @param [in] fmt /// printf style format string //------------------------------------------------------------------ void PrintWarning(uint64_t warning_type, const void *repeat_key, const char *fmt, ...) __attribute__((format(printf, 4, 5))); //------------------------------------------------------------------ // NextEventAction provides a way to register an action on the next // event that is delivered to this process. There is currently only // one next event action allowed in the process at one time. If a // new "NextEventAction" is added while one is already present, the // old action will be discarded (with HandleBeingUnshipped called // after it is discarded.) // // If you want to resume the process as a result of a resume action, // call RequestResume, don't call Resume directly. //------------------------------------------------------------------ class NextEventAction { public: typedef enum EventActionResult { eEventActionSuccess, eEventActionRetry, eEventActionExit } EventActionResult; NextEventAction(Process *process) : m_process(process) {} virtual ~NextEventAction() = default; virtual EventActionResult PerformAction(lldb::EventSP &event_sp) = 0; virtual void HandleBeingUnshipped() {} virtual EventActionResult HandleBeingInterrupted() = 0; virtual const char *GetExitString() = 0; void RequestResume() { m_process->m_resume_requested = true; } protected: Process *m_process; }; void SetNextEventAction(Process::NextEventAction *next_event_action) { if (m_next_event_action_ap.get()) m_next_event_action_ap->HandleBeingUnshipped(); m_next_event_action_ap.reset(next_event_action); } // This is the completer for Attaching: class AttachCompletionHandler : public NextEventAction { public: AttachCompletionHandler(Process *process, uint32_t exec_count); ~AttachCompletionHandler() override = default; EventActionResult PerformAction(lldb::EventSP &event_sp) override; EventActionResult HandleBeingInterrupted() override; const char *GetExitString() override; private: uint32_t m_exec_count; std::string m_exit_string; }; bool PrivateStateThreadIsValid() const { lldb::StateType state = m_private_state.GetValue(); return state != lldb::eStateInvalid && state != lldb::eStateDetached && state != lldb::eStateExited && m_private_state_thread.IsJoinable(); } void ForceNextEventDelivery() { m_force_next_event_delivery = true; } //------------------------------------------------------------------ /// Loads any plugins associated with asynchronous structured data /// and maps the relevant supported type name to the plugin. /// /// Processes can receive asynchronous structured data from the /// process monitor. This method will load and map any structured /// data plugins that support the given set of supported type names. /// Later, if any of these features are enabled, the process monitor /// is free to generate asynchronous structured data. The data must /// come in as a single \b StructuredData::Dictionary. That dictionary /// must have a string field named 'type', with a value that equals /// the relevant type name string (one of the values in /// \b supported_type_names). /// /// @param[in] supported_type_names /// An array of zero or more type names. Each must be unique. /// For each entry in the list, a StructuredDataPlugin will be /// searched for that supports the structured data type name. //------------------------------------------------------------------ void MapSupportedStructuredDataPlugins( const StructuredData::Array &supported_type_names); //------------------------------------------------------------------ /// Route the incoming structured data dictionary to the right plugin. /// /// The incoming structured data must be a dictionary, and it must /// have a key named 'type' that stores a string value. The string /// value must be the name of the structured data feature that /// knows how to handle it. /// /// @param[in] object_sp /// When non-null and pointing to a dictionary, the 'type' /// key's string value is used to look up the plugin that /// was registered for that structured data type. It then /// calls the following method on the StructuredDataPlugin /// instance: /// /// virtual void /// HandleArrivalOfStructuredData(Process &process, /// const ConstString &type_name, /// const StructuredData::ObjectSP /// &object_sp) /// /// @return /// True if the structured data was routed to a plugin; otherwise, /// false. //------------------------------------------------------------------ bool RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp); //------------------------------------------------------------------ // Type definitions //------------------------------------------------------------------ typedef std::map LanguageRuntimeCollection; typedef std::unordered_set WarningsPointerSet; typedef std::map WarningsCollection; struct PreResumeCallbackAndBaton { bool (*callback)(void *); void *baton; PreResumeCallbackAndBaton(PreResumeActionCallback in_callback, void *in_baton) : callback(in_callback), baton(in_baton) {} bool operator== (const PreResumeCallbackAndBaton &rhs) { return callback == rhs.callback && baton == rhs.baton; } }; using StructuredDataPluginMap = std::map; //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ std::weak_ptr m_target_sp; ///< The target that owns this process. ThreadSafeValue m_public_state; ThreadSafeValue m_private_state; // The actual state of our process Broadcaster m_private_state_broadcaster; // This broadcaster feeds state // changed events into the private // state thread's listener. Broadcaster m_private_state_control_broadcaster; // This is the control // broadcaster, used to // pause, resume & stop the // private state thread. lldb::ListenerSP m_private_state_listener_sp; // This is the listener for the // private state thread. HostThread m_private_state_thread; ///< Thread ID for the thread that watches ///internal state events ProcessModID m_mod_id; ///< Tracks the state of the process over stops and ///other alterations. uint32_t m_process_unique_id; ///< Each lldb_private::Process class that is ///created gets a unique integer ID that ///increments with each new instance uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index ///that won't get re-used. std::map m_thread_id_to_index_id_map; int m_exit_status; ///< The exit status of the process, or -1 if not set. std::string m_exit_string; ///< A textual description of why a process exited. std::mutex m_exit_status_mutex; ///< Mutex so m_exit_status m_exit_string can ///be safely accessed from multiple threads std::recursive_mutex m_thread_mutex; ThreadList m_thread_list_real; ///< The threads for this process as are known ///to the protocol we are debugging with ThreadList m_thread_list; ///< The threads for this process as the user will ///see them. This is usually the same as ///< m_thread_list_real, but might be different if there is an OS plug-in ///creating memory threads ThreadList m_extended_thread_list; ///< Owner for extended threads that may be ///generated, cleared on natural stops uint32_t m_extended_thread_stop_id; ///< The natural stop id when ///extended_thread_list was last updated QueueList m_queue_list; ///< The list of libdispatch queues at a given stop point uint32_t m_queue_list_stop_id; ///< The natural stop id when queue list was ///last fetched std::vector m_notifications; ///< The list of notifications ///that this process can deliver. std::vector m_image_tokens; lldb::ListenerSP m_listener_sp; ///< Shared pointer to the listener used for ///public events. Can not be empty. BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint ///locations we intend to insert in ///the target. lldb::DynamicLoaderUP m_dyld_ap; lldb::JITLoaderListUP m_jit_loaders_ap; lldb::DynamicCheckerFunctionsUP m_dynamic_checkers_ap; ///< The functions used ///by the expression ///parser to validate ///data that ///expressions use. lldb::OperatingSystemUP m_os_ap; lldb::SystemRuntimeUP m_system_runtime_ap; lldb::UnixSignalsSP m_unix_signals_sp; /// This is the current signal set for this process. lldb::ABISP m_abi_sp; lldb::IOHandlerSP m_process_input_reader; Communication m_stdio_communication; std::recursive_mutex m_stdio_communication_mutex; bool m_stdin_forward; /// Remember if stdin must be forwarded to remote debug /// server std::string m_stdout_data; std::string m_stderr_data; std::recursive_mutex m_profile_data_comm_mutex; std::vector m_profile_data; Predicate m_iohandler_sync; MemoryCache m_memory_cache; AllocatedMemoryCache m_allocated_memory_cache; bool m_should_detach; /// Should we detach if the process object goes away /// with an explicit call to Kill or Detach? LanguageRuntimeCollection m_language_runtimes; InstrumentationRuntimeCollection m_instrumentation_runtimes; std::unique_ptr m_next_event_action_ap; std::vector m_pre_resume_actions; ProcessRunLock m_public_run_lock; ProcessRunLock m_private_run_lock; ArchSpec::StopInfoOverrideCallbackType m_stop_info_override_callback; bool m_currently_handling_do_on_removals; bool m_resume_requested; // If m_currently_handling_event or // m_currently_handling_do_on_removals are true, // Resume will only request a resume, using this flag // to check. bool m_finalizing; // This is set at the beginning of Process::Finalize() to // stop functions from looking up or creating things during // a finalize call bool m_finalize_called; // This is set at the end of Process::Finalize() bool m_clear_thread_plans_on_stop; bool m_force_next_event_delivery; lldb::StateType m_last_broadcast_state; /// This helps with the Public event /// coalescing in /// ShouldBroadcastEvent. std::map m_resolved_indirect_addresses; bool m_destroy_in_process; bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, // don't support the ability to modify // the stack. WarningsCollection m_warnings_issued; // A set of object pointers which have // already had warnings printed std::mutex m_run_thread_plan_lock; StructuredDataPluginMap m_structured_data_plugin_map; enum { eCanJITDontKnow = 0, eCanJITYes, eCanJITNo } m_can_jit; size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size, uint8_t *buf) const; void SynchronouslyNotifyStateChanged(lldb::StateType state); void SetPublicState(lldb::StateType new_state, bool restarted); void SetPrivateState(lldb::StateType state); bool StartPrivateStateThread(bool is_secondary_thread = false); void StopPrivateStateThread(); void PausePrivateStateThread(); void ResumePrivateStateThread(); private: struct PrivateStateThreadArgs { PrivateStateThreadArgs(Process *p, bool s) : process(p), is_secondary_thread(s){}; Process *process; bool is_secondary_thread; }; // arg is a pointer to a new'ed PrivateStateThreadArgs structure. // PrivateStateThread will free it for you. static lldb::thread_result_t PrivateStateThread(void *arg); // The starts up the private state thread that will watch for events from the // debugee. // Pass true for is_secondary_thread in the case where you have to temporarily // spin up a // secondary state thread to handle events from a hand-called function on the // primary // private state thread. lldb::thread_result_t RunPrivateStateThread(bool is_secondary_thread); protected: void HandlePrivateEvent(lldb::EventSP &event_sp); Error HaltPrivate(); lldb::StateType WaitForProcessStopPrivate(lldb::EventSP &event_sp, const Timeout &timeout); // This waits for both the state change broadcaster, and the control // broadcaster. // If control_only, it only waits for the control broadcaster. bool GetEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout, bool control_only); lldb::StateType GetStateChangedEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout); size_t WriteMemoryPrivate(lldb::addr_t addr, const void *buf, size_t size, Error &error); void AppendSTDOUT(const char *s, size_t len); void AppendSTDERR(const char *s, size_t len); void BroadcastAsyncProfileData(const std::string &one_profile_data); static void STDIOReadThreadBytesReceived(void *baton, const void *src, size_t src_len); bool PushProcessIOHandler(); bool PopProcessIOHandler(); bool ProcessIOHandlerIsActive(); bool ProcessIOHandlerExists() const { return static_cast(m_process_input_reader); } Error StopForDestroyOrDetach(lldb::EventSP &exit_event_sp); virtual Error UpdateAutomaticSignalFiltering(); bool StateChangedIsExternallyHijacked(); void LoadOperatingSystemPlugin(bool flush); private: //------------------------------------------------------------------ /// This is the part of the event handling that for a process event. /// It decides what to do with the event and returns true if the /// event needs to be propagated to the user, and false otherwise. /// If the event is not propagated, this call will most likely set /// the target to executing again. /// There is only one place where this call should be called, /// HandlePrivateEvent. /// Don't call it from anywhere else... /// /// @param[in] event_ptr /// This is the event we are handling. /// /// @return /// Returns \b true if the event should be reported to the /// user, \b false otherwise. //------------------------------------------------------------------ bool ShouldBroadcastEvent(Event *event_ptr); void ControlPrivateStateThread(uint32_t signal); DISALLOW_COPY_AND_ASSIGN(Process); }; } // namespace lldb_private #endif // liblldb_Process_h_ Index: vendor/lldb/dist/include/lldb/lldb-enumerations.h =================================================================== --- vendor/lldb/dist/include/lldb/lldb-enumerations.h (revision 317454) +++ vendor/lldb/dist/include/lldb/lldb-enumerations.h (revision 317455) @@ -1,1073 +1,1080 @@ //===-- lldb-enumerations.h -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_lldb_enumerations_h_ #define LLDB_lldb_enumerations_h_ #ifndef SWIG // With MSVC, the default type of an enum is always signed, even if one of the // enumerator values is too large to fit into a signed integer but would // otherwise fit into an unsigned integer. As a result of this, all of LLDB's // flag-style enumerations that specify something like eValueFoo = 1u << 31 // result in negative values. This usually just results in a benign warning, // but in a few places we actually do comparisons on the enum values, which // would cause a real bug. Furthermore, there's no way to silence only this // warning, as it's part of -Wmicrosoft which also catches a whole slew of // other useful issues. // // To make matters worse, early versions of SWIG don't recognize the syntax // of specifying the underlying type of an enum (and Python doesn't care anyway) // so we need a way to specify the underlying type when the enum is being used // from C++ code, but just use a regular enum when swig is pre-processing. #define FLAGS_ENUM(Name) enum Name : unsigned #define FLAGS_ANONYMOUS_ENUM() enum : unsigned #else #define FLAGS_ENUM(Name) enum Name #define FLAGS_ANONYMOUS_ENUM() enum #endif namespace lldb { //---------------------------------------------------------------------- // Process and Thread States //---------------------------------------------------------------------- enum StateType { eStateInvalid = 0, eStateUnloaded, ///< Process is object is valid, but not currently loaded eStateConnected, ///< Process is connected to remote debug services, but not ///launched or attached to anything yet eStateAttaching, ///< Process is currently trying to attach eStateLaunching, ///< Process is in the process of launching eStateStopped, ///< Process or thread is stopped and can be examined. eStateRunning, ///< Process or thread is running and can't be examined. eStateStepping, ///< Process or thread is in the process of stepping and can ///not be examined. eStateCrashed, ///< Process or thread has crashed and can be examined. eStateDetached, ///< Process has been detached and can't be examined. eStateExited, ///< Process has exited and can't be examined. eStateSuspended ///< Process or thread is in a suspended state as far ///< as the debugger is concerned while other processes ///< or threads get the chance to run. }; //---------------------------------------------------------------------- // Launch Flags //---------------------------------------------------------------------- FLAGS_ENUM(LaunchFlags){ eLaunchFlagNone = 0u, eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling ///process into a new process eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to ///allow the process to be debugged eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point ///instead of auto-continuing when ///launching or attaching at entry point eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host eLaunchFlagLaunchInShell = (1u << 6), ///< Launch the process inside a shell to get shell expansion eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7), ///< Launch the process in a separate process group eLaunchFlagDontSetExitStatus = (1u << 8), ///< If you are going to hand the ///process off (e.g. to ///debugserver) ///< set this flag so lldb & the handee don't race to set its exit status. eLaunchFlagDetachOnError = (1u << 9), ///< If set, then the client stub ///should detach rather than killing ///the debugee ///< if it loses connection with lldb. eLaunchFlagShellExpandArguments = (1u << 10), ///< Perform shell-style argument expansion eLaunchFlagCloseTTYOnExit = (1u << 11), ///< Close the open TTY on exit }; //---------------------------------------------------------------------- // Thread Run Modes //---------------------------------------------------------------------- enum RunMode { eOnlyThisThread, eAllThreads, eOnlyDuringStepping }; //---------------------------------------------------------------------- // Byte ordering definitions //---------------------------------------------------------------------- enum ByteOrder { eByteOrderInvalid = 0, eByteOrderBig = 1, eByteOrderPDP = 2, eByteOrderLittle = 4 }; //---------------------------------------------------------------------- // Register encoding definitions //---------------------------------------------------------------------- enum Encoding { eEncodingInvalid = 0, eEncodingUint, // unsigned integer eEncodingSint, // signed integer eEncodingIEEE754, // float eEncodingVector // vector registers }; //---------------------------------------------------------------------- // Display format definitions //---------------------------------------------------------------------- enum Format { eFormatDefault = 0, eFormatInvalid = 0, eFormatBoolean, eFormatBinary, eFormatBytes, eFormatBytesWithASCII, eFormatChar, eFormatCharPrintable, // Only printable characters, space if not printable eFormatComplex, // Floating point complex type eFormatComplexFloat = eFormatComplex, eFormatCString, // NULL terminated C strings eFormatDecimal, eFormatEnum, eFormatHex, eFormatHexUppercase, eFormatFloat, eFormatOctal, eFormatOSType, // OS character codes encoded into an integer 'PICT' 'text' // etc... eFormatUnicode16, eFormatUnicode32, eFormatUnsigned, eFormatPointer, eFormatVectorOfChar, eFormatVectorOfSInt8, eFormatVectorOfUInt8, eFormatVectorOfSInt16, eFormatVectorOfUInt16, eFormatVectorOfSInt32, eFormatVectorOfUInt32, eFormatVectorOfSInt64, eFormatVectorOfUInt64, eFormatVectorOfFloat16, eFormatVectorOfFloat32, eFormatVectorOfFloat64, eFormatVectorOfUInt128, eFormatComplexInteger, // Integer complex type eFormatCharArray, // Print characters with no single quotes, used for // character arrays that can contain non printable // characters eFormatAddressInfo, // Describe what an address points to (func + offset with // file/line, symbol + offset, data, etc) eFormatHexFloat, // ISO C99 hex float string eFormatInstruction, // Disassemble an opcode eFormatVoid, // Do not print this kNumFormats }; //---------------------------------------------------------------------- // Description levels for "void GetDescription(Stream *, DescriptionLevel)" // calls //---------------------------------------------------------------------- enum DescriptionLevel { eDescriptionLevelBrief = 0, eDescriptionLevelFull, eDescriptionLevelVerbose, eDescriptionLevelInitial, kNumDescriptionLevels }; //---------------------------------------------------------------------- // Script interpreter types //---------------------------------------------------------------------- enum ScriptLanguage { eScriptLanguageNone, eScriptLanguagePython, eScriptLanguageDefault = eScriptLanguagePython, eScriptLanguageUnknown }; //---------------------------------------------------------------------- // Register numbering types // See RegisterContext::ConvertRegisterKindToRegisterNumber to convert // any of these to the lldb internal register numbering scheme // (eRegisterKindLLDB). //---------------------------------------------------------------------- enum RegisterKind { eRegisterKindEHFrame = 0, // the register numbers seen in eh_frame eRegisterKindDWARF, // the register numbers seen DWARF eRegisterKindGeneric, // insn ptr reg, stack ptr reg, etc not specific to any // particular target eRegisterKindProcessPlugin, // num used by the process plugin - e.g. by the // remote gdb-protocol stub program eRegisterKindLLDB, // lldb's internal register numbers kNumRegisterKinds }; //---------------------------------------------------------------------- // Thread stop reasons //---------------------------------------------------------------------- enum StopReason { eStopReasonInvalid = 0, eStopReasonNone, eStopReasonTrace, eStopReasonBreakpoint, eStopReasonWatchpoint, eStopReasonSignal, eStopReasonException, eStopReasonExec, // Program was re-exec'ed eStopReasonPlanComplete, eStopReasonThreadExiting, eStopReasonInstrumentation }; //---------------------------------------------------------------------- // Command Return Status Types //---------------------------------------------------------------------- enum ReturnStatus { eReturnStatusInvalid, eReturnStatusSuccessFinishNoResult, eReturnStatusSuccessFinishResult, eReturnStatusSuccessContinuingNoResult, eReturnStatusSuccessContinuingResult, eReturnStatusStarted, eReturnStatusFailed, eReturnStatusQuit }; //---------------------------------------------------------------------- // The results of expression evaluation: //---------------------------------------------------------------------- enum ExpressionResults { eExpressionCompleted = 0, eExpressionSetupError, eExpressionParseError, eExpressionDiscarded, eExpressionInterrupted, eExpressionHitBreakpoint, eExpressionTimedOut, eExpressionResultUnavailable, eExpressionStoppedForDebug }; //---------------------------------------------------------------------- // Connection Status Types //---------------------------------------------------------------------- enum ConnectionStatus { eConnectionStatusSuccess, // Success eConnectionStatusEndOfFile, // End-of-file encountered eConnectionStatusError, // Check GetError() for details eConnectionStatusTimedOut, // Request timed out eConnectionStatusNoConnection, // No connection eConnectionStatusLostConnection, // Lost connection while connected to a valid // connection eConnectionStatusInterrupted // Interrupted read }; enum ErrorType { eErrorTypeInvalid, eErrorTypeGeneric, ///< Generic errors that can be any value. eErrorTypeMachKernel, ///< Mach kernel error codes. eErrorTypePOSIX, ///< POSIX error codes. eErrorTypeExpression, ///< These are from the ExpressionResults enum. eErrorTypeWin32 ///< Standard Win32 error codes. }; enum ValueType { eValueTypeInvalid = 0, eValueTypeVariableGlobal = 1, // globals variable eValueTypeVariableStatic = 2, // static variable eValueTypeVariableArgument = 3, // function argument variables eValueTypeVariableLocal = 4, // function local variables eValueTypeRegister = 5, // stack frame register value eValueTypeRegisterSet = 6, // A collection of stack frame register values eValueTypeConstResult = 7, // constant result variables eValueTypeVariableThreadLocal = 8 // thread local storage variable }; //---------------------------------------------------------------------- // Token size/granularities for Input Readers //---------------------------------------------------------------------- enum InputReaderGranularity { eInputReaderGranularityInvalid = 0, eInputReaderGranularityByte, eInputReaderGranularityWord, eInputReaderGranularityLine, eInputReaderGranularityAll }; //------------------------------------------------------------------ /// These mask bits allow a common interface for queries that can /// limit the amount of information that gets parsed to only the /// information that is requested. These bits also can indicate what /// actually did get resolved during query function calls. /// /// Each definition corresponds to a one of the member variables /// in this class, and requests that that item be resolved, or /// indicates that the member did get resolved. //------------------------------------------------------------------ FLAGS_ENUM(SymbolContextItem){ eSymbolContextTarget = (1u << 0), ///< Set when \a target is requested from ///a query, or was located in query ///results eSymbolContextModule = (1u << 1), ///< Set when \a module is requested from ///a query, or was located in query ///results eSymbolContextCompUnit = (1u << 2), ///< Set when \a comp_unit is requested ///from a query, or was located in query ///results eSymbolContextFunction = (1u << 3), ///< Set when \a function is requested ///from a query, or was located in query ///results eSymbolContextBlock = (1u << 4), ///< Set when the deepest \a block is ///requested from a query, or was located ///in query results eSymbolContextLineEntry = (1u << 5), ///< Set when \a line_entry is ///requested from a query, or was ///located in query results eSymbolContextSymbol = (1u << 6), ///< Set when \a symbol is requested from ///a query, or was located in query ///results eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1u), ///< Indicates to try and lookup everything ///up during a routine symbol context ///query. eSymbolContextVariable = (1u << 7) ///< Set when \a global or static ///variable is requested from a query, or ///was located in query results. ///< eSymbolContextVariable is potentially expensive to lookup so it isn't ///included in ///< eSymbolContextEverything which stops it from being used during frame PC ///lookups and ///< many other potential address to symbol context lookups. }; FLAGS_ENUM(Permissions){ePermissionsWritable = (1u << 0), ePermissionsReadable = (1u << 1), ePermissionsExecutable = (1u << 2)}; enum InputReaderAction { eInputReaderActivate, // reader is newly pushed onto the reader stack eInputReaderAsynchronousOutputWritten, // an async output event occurred; the // reader may want to do something eInputReaderReactivate, // reader is on top of the stack again after another // reader was popped off eInputReaderDeactivate, // another reader was pushed on the stack eInputReaderGotToken, // reader got one of its tokens (granularity) eInputReaderInterrupt, // reader received an interrupt signal (probably from a // control-c) eInputReaderEndOfFile, // reader received an EOF char (probably from a // control-d) eInputReaderDone // reader was just popped off the stack and is done }; FLAGS_ENUM(BreakpointEventType){ eBreakpointEventTypeInvalidType = (1u << 0), eBreakpointEventTypeAdded = (1u << 1), eBreakpointEventTypeRemoved = (1u << 2), eBreakpointEventTypeLocationsAdded = (1u << 3), // Locations added doesn't // get sent when the // breakpoint is created eBreakpointEventTypeLocationsRemoved = (1u << 4), eBreakpointEventTypeLocationsResolved = (1u << 5), eBreakpointEventTypeEnabled = (1u << 6), eBreakpointEventTypeDisabled = (1u << 7), eBreakpointEventTypeCommandChanged = (1u << 8), eBreakpointEventTypeConditionChanged = (1u << 9), eBreakpointEventTypeIgnoreChanged = (1u << 10), eBreakpointEventTypeThreadChanged = (1u << 11)}; FLAGS_ENUM(WatchpointEventType){ eWatchpointEventTypeInvalidType = (1u << 0), eWatchpointEventTypeAdded = (1u << 1), eWatchpointEventTypeRemoved = (1u << 2), eWatchpointEventTypeEnabled = (1u << 6), eWatchpointEventTypeDisabled = (1u << 7), eWatchpointEventTypeCommandChanged = (1u << 8), eWatchpointEventTypeConditionChanged = (1u << 9), eWatchpointEventTypeIgnoreChanged = (1u << 10), eWatchpointEventTypeThreadChanged = (1u << 11), eWatchpointEventTypeTypeChanged = (1u << 12)}; //---------------------------------------------------------------------- /// Programming language type. /// /// These enumerations use the same language enumerations as the DWARF /// specification for ease of use and consistency. /// The enum -> string code is in Language.cpp, don't change this /// table without updating that code as well. //---------------------------------------------------------------------- enum LanguageType { eLanguageTypeUnknown = 0x0000, ///< Unknown or invalid language value. eLanguageTypeC89 = 0x0001, ///< ISO C:1989. eLanguageTypeC = 0x0002, ///< Non-standardized C, such as K&R. eLanguageTypeAda83 = 0x0003, ///< ISO Ada:1983. eLanguageTypeC_plus_plus = 0x0004, ///< ISO C++:1998. eLanguageTypeCobol74 = 0x0005, ///< ISO Cobol:1974. eLanguageTypeCobol85 = 0x0006, ///< ISO Cobol:1985. eLanguageTypeFortran77 = 0x0007, ///< ISO Fortran 77. eLanguageTypeFortran90 = 0x0008, ///< ISO Fortran 90. eLanguageTypePascal83 = 0x0009, ///< ISO Pascal:1983. eLanguageTypeModula2 = 0x000a, ///< ISO Modula-2:1996. eLanguageTypeJava = 0x000b, ///< Java. eLanguageTypeC99 = 0x000c, ///< ISO C:1999. eLanguageTypeAda95 = 0x000d, ///< ISO Ada:1995. eLanguageTypeFortran95 = 0x000e, ///< ISO Fortran 95. eLanguageTypePLI = 0x000f, ///< ANSI PL/I:1976. eLanguageTypeObjC = 0x0010, ///< Objective-C. eLanguageTypeObjC_plus_plus = 0x0011, ///< Objective-C++. eLanguageTypeUPC = 0x0012, ///< Unified Parallel C. eLanguageTypeD = 0x0013, ///< D. eLanguageTypePython = 0x0014, ///< Python. // NOTE: The below are DWARF5 constants, subject to change upon // completion of the DWARF5 specification eLanguageTypeOpenCL = 0x0015, ///< OpenCL. eLanguageTypeGo = 0x0016, ///< Go. eLanguageTypeModula3 = 0x0017, ///< Modula 3. eLanguageTypeHaskell = 0x0018, ///< Haskell. eLanguageTypeC_plus_plus_03 = 0x0019, ///< ISO C++:2003. eLanguageTypeC_plus_plus_11 = 0x001a, ///< ISO C++:2011. eLanguageTypeOCaml = 0x001b, ///< OCaml. eLanguageTypeRust = 0x001c, ///< Rust. eLanguageTypeC11 = 0x001d, ///< ISO C:2011. eLanguageTypeSwift = 0x001e, ///< Swift. eLanguageTypeJulia = 0x001f, ///< Julia. eLanguageTypeDylan = 0x0020, ///< Dylan. eLanguageTypeC_plus_plus_14 = 0x0021, ///< ISO C++:2014. eLanguageTypeFortran03 = 0x0022, ///< ISO Fortran 2003. eLanguageTypeFortran08 = 0x0023, ///< ISO Fortran 2008. // Vendor Extensions // Note: Language::GetNameForLanguageType // assumes these can be used as indexes into array language_names, and // Language::SetLanguageFromCString and Language::AsCString // assume these can be used as indexes into array g_languages. eLanguageTypeMipsAssembler = 0x0024, ///< Mips_Assembler. eLanguageTypeExtRenderScript = 0x0025, ///< RenderScript. eNumLanguageTypes }; enum InstrumentationRuntimeType { eInstrumentationRuntimeTypeAddressSanitizer = 0x0000, eInstrumentationRuntimeTypeThreadSanitizer = 0x0001, eNumInstrumentationRuntimeTypes }; enum DynamicValueType { eNoDynamicValues = 0, eDynamicCanRunTarget = 1, eDynamicDontRunTarget = 2 }; enum StopShowColumn { eStopShowColumnAnsiOrCaret = 0, eStopShowColumnAnsi = 1, eStopShowColumnCaret = 2, eStopShowColumnNone = 3 }; enum AccessType { eAccessNone, eAccessPublic, eAccessPrivate, eAccessProtected, eAccessPackage }; enum CommandArgumentType { eArgTypeAddress = 0, eArgTypeAddressOrExpression, eArgTypeAliasName, eArgTypeAliasOptions, eArgTypeArchitecture, eArgTypeBoolean, eArgTypeBreakpointID, eArgTypeBreakpointIDRange, eArgTypeBreakpointName, eArgTypeByteSize, eArgTypeClassName, eArgTypeCommandName, eArgTypeCount, eArgTypeDescriptionVerbosity, eArgTypeDirectoryName, eArgTypeDisassemblyFlavor, eArgTypeEndAddress, eArgTypeExpression, eArgTypeExpressionPath, eArgTypeExprFormat, eArgTypeFilename, eArgTypeFormat, eArgTypeFrameIndex, eArgTypeFullName, eArgTypeFunctionName, eArgTypeFunctionOrSymbol, eArgTypeGDBFormat, eArgTypeHelpText, eArgTypeIndex, eArgTypeLanguage, eArgTypeLineNum, eArgTypeLogCategory, eArgTypeLogChannel, eArgTypeMethod, eArgTypeName, eArgTypeNewPathPrefix, eArgTypeNumLines, eArgTypeNumberPerLine, eArgTypeOffset, eArgTypeOldPathPrefix, eArgTypeOneLiner, eArgTypePath, eArgTypePermissionsNumber, eArgTypePermissionsString, eArgTypePid, eArgTypePlugin, eArgTypeProcessName, eArgTypePythonClass, eArgTypePythonFunction, eArgTypePythonScript, eArgTypeQueueName, eArgTypeRegisterName, eArgTypeRegularExpression, eArgTypeRunArgs, eArgTypeRunMode, eArgTypeScriptedCommandSynchronicity, eArgTypeScriptLang, eArgTypeSearchWord, eArgTypeSelector, eArgTypeSettingIndex, eArgTypeSettingKey, eArgTypeSettingPrefix, eArgTypeSettingVariableName, eArgTypeShlibName, eArgTypeSourceFile, eArgTypeSortOrder, eArgTypeStartAddress, eArgTypeSummaryString, eArgTypeSymbol, eArgTypeThreadID, eArgTypeThreadIndex, eArgTypeThreadName, eArgTypeTypeName, eArgTypeUnsignedInteger, eArgTypeUnixSignal, eArgTypeVarName, eArgTypeValue, eArgTypeWidth, eArgTypeNone, eArgTypePlatform, eArgTypeWatchpointID, eArgTypeWatchpointIDRange, eArgTypeWatchType, eArgRawInput, eArgTypeLastArg // Always keep this entry as the last entry in this // enumeration!! }; //---------------------------------------------------------------------- // Symbol types //---------------------------------------------------------------------- enum SymbolType { eSymbolTypeAny = 0, eSymbolTypeInvalid = 0, eSymbolTypeAbsolute, eSymbolTypeCode, eSymbolTypeResolver, eSymbolTypeData, eSymbolTypeTrampoline, eSymbolTypeRuntime, eSymbolTypeException, eSymbolTypeSourceFile, eSymbolTypeHeaderFile, eSymbolTypeObjectFile, eSymbolTypeCommonBlock, eSymbolTypeBlock, eSymbolTypeLocal, eSymbolTypeParam, eSymbolTypeVariable, eSymbolTypeVariableType, eSymbolTypeLineEntry, eSymbolTypeLineHeader, eSymbolTypeScopeBegin, eSymbolTypeScopeEnd, eSymbolTypeAdditional, // When symbols take more than one entry, the extra // entries get this type eSymbolTypeCompiler, eSymbolTypeInstrumentation, eSymbolTypeUndefined, eSymbolTypeObjCClass, eSymbolTypeObjCMetaClass, eSymbolTypeObjCIVar, eSymbolTypeReExported }; enum SectionType { eSectionTypeInvalid, eSectionTypeCode, eSectionTypeContainer, // The section contains child sections eSectionTypeData, eSectionTypeDataCString, // Inlined C string data eSectionTypeDataCStringPointers, // Pointers to C string data eSectionTypeDataSymbolAddress, // Address of a symbol in the symbol table eSectionTypeData4, eSectionTypeData8, eSectionTypeData16, eSectionTypeDataPointers, eSectionTypeDebug, eSectionTypeZeroFill, eSectionTypeDataObjCMessageRefs, // Pointer to function pointer + selector eSectionTypeDataObjCCFStrings, // Objective C const CFString/NSString objects eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr, eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo, eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugMacro, eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges, eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets, eSectionTypeDWARFAppleNames, eSectionTypeDWARFAppleTypes, eSectionTypeDWARFAppleNamespaces, eSectionTypeDWARFAppleObjC, eSectionTypeELFSymbolTable, // Elf SHT_SYMTAB section eSectionTypeELFDynamicSymbols, // Elf SHT_DYNSYM section eSectionTypeELFRelocationEntries, // Elf SHT_REL or SHT_REL section eSectionTypeELFDynamicLinkInfo, // Elf SHT_DYNAMIC section eSectionTypeEHFrame, eSectionTypeARMexidx, eSectionTypeARMextab, eSectionTypeCompactUnwind, // compact unwind section in Mach-O, // __TEXT,__unwind_info eSectionTypeGoSymtab, eSectionTypeAbsoluteAddress, // Dummy section for symbols with absolute // address eSectionTypeOther }; FLAGS_ENUM(EmulateInstructionOptions){ eEmulateInstructionOptionNone = (0u), eEmulateInstructionOptionAutoAdvancePC = (1u << 0), eEmulateInstructionOptionIgnoreConditions = (1u << 1)}; FLAGS_ENUM(FunctionNameType){ eFunctionNameTypeNone = 0u, eFunctionNameTypeAuto = (1u << 1), // Automatically figure out which FunctionNameType // bits to set based on the function name. eFunctionNameTypeFull = (1u << 2), // The function name. // For C this is the same as just the name of the function // For C++ this is the mangled or demangled version of the mangled name. // For ObjC this is the full function signature with the + or // - and the square brackets and the class and selector eFunctionNameTypeBase = (1u << 3), // The function name only, no namespaces // or arguments and no class // methods or selectors will be searched. eFunctionNameTypeMethod = (1u << 4), // Find function by method name (C++) // with no namespace or arguments eFunctionNameTypeSelector = (1u << 5), // Find function by selector name (ObjC) names eFunctionNameTypeAny = eFunctionNameTypeAuto // DEPRECATED: use eFunctionNameTypeAuto }; //---------------------------------------------------------------------- // Basic types enumeration for the public API SBType::GetBasicType() //---------------------------------------------------------------------- enum BasicType { eBasicTypeInvalid = 0, eBasicTypeVoid = 1, eBasicTypeChar, eBasicTypeSignedChar, eBasicTypeUnsignedChar, eBasicTypeWChar, eBasicTypeSignedWChar, eBasicTypeUnsignedWChar, eBasicTypeChar16, eBasicTypeChar32, eBasicTypeShort, eBasicTypeUnsignedShort, eBasicTypeInt, eBasicTypeUnsignedInt, eBasicTypeLong, eBasicTypeUnsignedLong, eBasicTypeLongLong, eBasicTypeUnsignedLongLong, eBasicTypeInt128, eBasicTypeUnsignedInt128, eBasicTypeBool, eBasicTypeHalf, eBasicTypeFloat, eBasicTypeDouble, eBasicTypeLongDouble, eBasicTypeFloatComplex, eBasicTypeDoubleComplex, eBasicTypeLongDoubleComplex, eBasicTypeObjCID, eBasicTypeObjCClass, eBasicTypeObjCSel, eBasicTypeNullPtr, eBasicTypeOther }; +enum TraceType { + eTraceTypeNone = 0, + + // Hardware Trace generated by the processor. + eTraceTypeProcessorTrace +}; + FLAGS_ENUM(TypeClass){ eTypeClassInvalid = (0u), eTypeClassArray = (1u << 0), eTypeClassBlockPointer = (1u << 1), eTypeClassBuiltin = (1u << 2), eTypeClassClass = (1u << 3), eTypeClassComplexFloat = (1u << 4), eTypeClassComplexInteger = (1u << 5), eTypeClassEnumeration = (1u << 6), eTypeClassFunction = (1u << 7), eTypeClassMemberPointer = (1u << 8), eTypeClassObjCObject = (1u << 9), eTypeClassObjCInterface = (1u << 10), eTypeClassObjCObjectPointer = (1u << 11), eTypeClassPointer = (1u << 12), eTypeClassReference = (1u << 13), eTypeClassStruct = (1u << 14), eTypeClassTypedef = (1u << 15), eTypeClassUnion = (1u << 16), eTypeClassVector = (1u << 17), // Define the last type class as the MSBit of a 32 bit value eTypeClassOther = (1u << 31), // Define a mask that can be used for any type when finding types eTypeClassAny = (0xffffffffu)}; enum TemplateArgumentKind { eTemplateArgumentKindNull = 0, eTemplateArgumentKindType, eTemplateArgumentKindDeclaration, eTemplateArgumentKindIntegral, eTemplateArgumentKindTemplate, eTemplateArgumentKindTemplateExpansion, eTemplateArgumentKindExpression, eTemplateArgumentKindPack }; //---------------------------------------------------------------------- // Options that can be set for a formatter to alter its behavior // Not all of these are applicable to all formatter types //---------------------------------------------------------------------- FLAGS_ENUM(TypeOptions){eTypeOptionNone = (0u), eTypeOptionCascade = (1u << 0), eTypeOptionSkipPointers = (1u << 1), eTypeOptionSkipReferences = (1u << 2), eTypeOptionHideChildren = (1u << 3), eTypeOptionHideValue = (1u << 4), eTypeOptionShowOneLiner = (1u << 5), eTypeOptionHideNames = (1u << 6), eTypeOptionNonCacheable = (1u << 7), eTypeOptionHideEmptyAggregates = (1u << 8)}; //---------------------------------------------------------------------- // This is the return value for frame comparisons. If you are comparing frame A // to frame B // the following cases arise: // 1) When frame A pushes frame B (or a frame that ends up pushing B) A is Older // than B. // 2) When frame A pushed frame B (or if frame A is on the stack but B is not) A // is Younger than B // 3) When frame A and frame B have the same StackID, they are Equal. // 4) When frame A and frame B have the same immediate parent frame, but are not // equal, the comparison yields // SameParent. // 5) If the two frames are on different threads or processes the comparison is // Invalid // 6) If for some reason we can't figure out what went on, we return Unknown. //---------------------------------------------------------------------- enum FrameComparison { eFrameCompareInvalid, eFrameCompareUnknown, eFrameCompareEqual, eFrameCompareSameParent, eFrameCompareYounger, eFrameCompareOlder }; //---------------------------------------------------------------------- // Address Class // // A way of classifying an address used for disassembling and setting // breakpoints. Many object files can track exactly what parts of their // object files are code, data and other information. This is of course // above and beyond just looking at the section types. For example, code // might contain PC relative data and the object file might be able to // tell us that an address in code is data. //---------------------------------------------------------------------- enum AddressClass { eAddressClassInvalid, eAddressClassUnknown, eAddressClassCode, eAddressClassCodeAlternateISA, eAddressClassData, eAddressClassDebug, eAddressClassRuntime }; //---------------------------------------------------------------------- // File Permissions // // Designed to mimic the unix file permission bits so they can be // used with functions that set 'mode_t' to certain values for // permissions. //---------------------------------------------------------------------- FLAGS_ENUM(FilePermissions){ eFilePermissionsUserRead = (1u << 8), eFilePermissionsUserWrite = (1u << 7), eFilePermissionsUserExecute = (1u << 6), eFilePermissionsGroupRead = (1u << 5), eFilePermissionsGroupWrite = (1u << 4), eFilePermissionsGroupExecute = (1u << 3), eFilePermissionsWorldRead = (1u << 2), eFilePermissionsWorldWrite = (1u << 1), eFilePermissionsWorldExecute = (1u << 0), eFilePermissionsUserRW = (eFilePermissionsUserRead | eFilePermissionsUserWrite | 0), eFileFilePermissionsUserRX = (eFilePermissionsUserRead | 0 | eFilePermissionsUserExecute), eFilePermissionsUserRWX = (eFilePermissionsUserRead | eFilePermissionsUserWrite | eFilePermissionsUserExecute), eFilePermissionsGroupRW = (eFilePermissionsGroupRead | eFilePermissionsGroupWrite | 0), eFilePermissionsGroupRX = (eFilePermissionsGroupRead | 0 | eFilePermissionsGroupExecute), eFilePermissionsGroupRWX = (eFilePermissionsGroupRead | eFilePermissionsGroupWrite | eFilePermissionsGroupExecute), eFilePermissionsWorldRW = (eFilePermissionsWorldRead | eFilePermissionsWorldWrite | 0), eFilePermissionsWorldRX = (eFilePermissionsWorldRead | 0 | eFilePermissionsWorldExecute), eFilePermissionsWorldRWX = (eFilePermissionsWorldRead | eFilePermissionsWorldWrite | eFilePermissionsWorldExecute), eFilePermissionsEveryoneR = (eFilePermissionsUserRead | eFilePermissionsGroupRead | eFilePermissionsWorldRead), eFilePermissionsEveryoneW = (eFilePermissionsUserWrite | eFilePermissionsGroupWrite | eFilePermissionsWorldWrite), eFilePermissionsEveryoneX = (eFilePermissionsUserExecute | eFilePermissionsGroupExecute | eFilePermissionsWorldExecute), eFilePermissionsEveryoneRW = (eFilePermissionsEveryoneR | eFilePermissionsEveryoneW | 0), eFilePermissionsEveryoneRX = (eFilePermissionsEveryoneR | 0 | eFilePermissionsEveryoneX), eFilePermissionsEveryoneRWX = (eFilePermissionsEveryoneR | eFilePermissionsEveryoneW | eFilePermissionsEveryoneX), eFilePermissionsFileDefault = eFilePermissionsUserRW, eFilePermissionsDirectoryDefault = eFilePermissionsUserRWX, }; //---------------------------------------------------------------------- // Queue work item types // // The different types of work that can be enqueued on a libdispatch // aka Grand Central Dispatch (GCD) queue. //---------------------------------------------------------------------- enum QueueItemKind { eQueueItemKindUnknown = 0, eQueueItemKindFunction, eQueueItemKindBlock }; //---------------------------------------------------------------------- // Queue type // libdispatch aka Grand Central Dispatch (GCD) queues can be either serial // (executing on one thread) or concurrent (executing on multiple threads). //---------------------------------------------------------------------- enum QueueKind { eQueueKindUnknown = 0, eQueueKindSerial, eQueueKindConcurrent }; //---------------------------------------------------------------------- // Expression Evaluation Stages // These are the cancellable stages of expression evaluation, passed to the // expression evaluation callback, so that you can interrupt expression // evaluation at the various points in its lifecycle. //---------------------------------------------------------------------- enum ExpressionEvaluationPhase { eExpressionEvaluationParse = 0, eExpressionEvaluationIRGen, eExpressionEvaluationExecution, eExpressionEvaluationComplete }; //---------------------------------------------------------------------- // Watchpoint Kind // Indicates what types of events cause the watchpoint to fire. // Used by Native*Protocol-related classes. //---------------------------------------------------------------------- FLAGS_ENUM(WatchpointKind){eWatchpointKindRead = (1u << 0), eWatchpointKindWrite = (1u << 1)}; enum GdbSignal { eGdbSignalBadAccess = 0x91, eGdbSignalBadInstruction = 0x92, eGdbSignalArithmetic = 0x93, eGdbSignalEmulation = 0x94, eGdbSignalSoftware = 0x95, eGdbSignalBreakpoint = 0x96 }; //---------------------------------------------------------------------- // Used with SBHost::GetPath (lldb::PathType) to find files that are // related to LLDB on the current host machine. Most files are relative // to LLDB or are in known locations. //---------------------------------------------------------------------- enum PathType { ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB // mach-o file in LLDB.framework (MacOSX) exists ePathTypeSupportExecutableDir, // Find LLDB support executable directory // (debugserver, etc) ePathTypeHeaderDir, // Find LLDB header file directory ePathTypePythonDir, // Find Python modules (PYTHONPATH) directory ePathTypeLLDBSystemPlugins, // System plug-ins directory ePathTypeLLDBUserPlugins, // User plug-ins directory ePathTypeLLDBTempSystemDir, // The LLDB temp directory for this system that // will be cleaned up on exit ePathTypeGlobalLLDBTempSystemDir, // The LLDB temp directory for this system, // NOT cleaned up on a process exit. ePathTypeClangDir // Find path to Clang builtin headers }; //---------------------------------------------------------------------- // Kind of member function // Used by the type system //---------------------------------------------------------------------- enum MemberFunctionKind { eMemberFunctionKindUnknown = 0, // Not sure what the type of this is eMemberFunctionKindConstructor, // A function used to create instances eMemberFunctionKindDestructor, // A function used to tear down existing // instances eMemberFunctionKindInstanceMethod, // A function that applies to a specific // instance eMemberFunctionKindStaticMethod // A function that applies to a type rather // than any instance }; //---------------------------------------------------------------------- // String matching algorithm used by SBTarget //---------------------------------------------------------------------- enum MatchType { eMatchTypeNormal, eMatchTypeRegex, eMatchTypeStartsWith }; //---------------------------------------------------------------------- // Bitmask that describes details about a type //---------------------------------------------------------------------- FLAGS_ENUM(TypeFlags){ eTypeHasChildren = (1u << 0), eTypeHasValue = (1u << 1), eTypeIsArray = (1u << 2), eTypeIsBlock = (1u << 3), eTypeIsBuiltIn = (1u << 4), eTypeIsClass = (1u << 5), eTypeIsCPlusPlus = (1u << 6), eTypeIsEnumeration = (1u << 7), eTypeIsFuncPrototype = (1u << 8), eTypeIsMember = (1u << 9), eTypeIsObjC = (1u << 10), eTypeIsPointer = (1u << 11), eTypeIsReference = (1u << 12), eTypeIsStructUnion = (1u << 13), eTypeIsTemplate = (1u << 14), eTypeIsTypedef = (1u << 15), eTypeIsVector = (1u << 16), eTypeIsScalar = (1u << 17), eTypeIsInteger = (1u << 18), eTypeIsFloat = (1u << 19), eTypeIsComplex = (1u << 20), eTypeIsSigned = (1u << 21), eTypeInstanceIsPointer = (1u << 22)}; FLAGS_ENUM(CommandFlags){ //---------------------------------------------------------------------- // eCommandRequiresTarget // // Ensures a valid target is contained in m_exe_ctx prior to executing // the command. If a target doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidTargetDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidTargetDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresTarget = (1u << 0), //---------------------------------------------------------------------- // eCommandRequiresProcess // // Ensures a valid process is contained in m_exe_ctx prior to executing // the command. If a process doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidProcessDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidProcessDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresProcess = (1u << 1), //---------------------------------------------------------------------- // eCommandRequiresThread // // Ensures a valid thread is contained in m_exe_ctx prior to executing // the command. If a thread doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidThreadDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidThreadDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresThread = (1u << 2), //---------------------------------------------------------------------- // eCommandRequiresFrame // // Ensures a valid frame is contained in m_exe_ctx prior to executing // the command. If a frame doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidFrameDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidFrameDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresFrame = (1u << 3), //---------------------------------------------------------------------- // eCommandRequiresRegContext // // Ensures a valid register context (from the selected frame if there // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) // is available from m_exe_ctx prior to executing the command. If a // target doesn't exist or is invalid, the command will fail and // CommandObject::GetInvalidRegContextDescription() will be returned as // the error. CommandObject subclasses can override the virtual function // for GetInvalidRegContextDescription() to provide custom strings when // needed. //---------------------------------------------------------------------- eCommandRequiresRegContext = (1u << 4), //---------------------------------------------------------------------- // eCommandTryTargetAPILock // // Attempts to acquire the target lock if a target is selected in the // command interpreter. If the command object fails to acquire the API // lock, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandTryTargetAPILock = (1u << 5), //---------------------------------------------------------------------- // eCommandProcessMustBeLaunched // // Verifies that there is a launched process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandProcessMustBeLaunched = (1u << 6), //---------------------------------------------------------------------- // eCommandProcessMustBePaused // // Verifies that there is a paused process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandProcessMustBePaused = (1u << 7)}; //---------------------------------------------------------------------- // Whether a summary should cap how much data it returns to users or not //---------------------------------------------------------------------- enum TypeSummaryCapping { eTypeSummaryCapped = true, eTypeSummaryUncapped = false }; } // namespace lldb #endif // LLDB_lldb_enumerations_h_ Index: vendor/lldb/dist/include/lldb/lldb-forward.h =================================================================== --- vendor/lldb/dist/include/lldb/lldb-forward.h (revision 317454) +++ vendor/lldb/dist/include/lldb/lldb-forward.h (revision 317455) @@ -1,489 +1,493 @@ //===-- lldb-forward.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_lldb_forward_h_ #define LLDB_lldb_forward_h_ #if defined(__cplusplus) #include "lldb/Utility/SharingPtr.h" //---------------------------------------------------------------------- // lldb forward declarations //---------------------------------------------------------------------- namespace lldb_private { class ABI; class Address; class AddressImpl; class AddressRange; class AddressResolver; class ArchSpec; class ArmUnwindInfo; class Args; class ASTResultSynthesizer; class ASTStructExtractor; class Baton; class Block; class Breakpoint; class BreakpointID; class BreakpointIDList; class BreakpointList; class BreakpointLocation; class BreakpointLocationCollection; class BreakpointLocationList; class BreakpointOptions; class BreakpointResolver; class BreakpointSite; class BreakpointSiteList; class BroadcastEventSpec; class Broadcaster; class BroadcasterManager; class CPPLanguageRuntime; class ClangASTContext; class ClangASTImporter; class ClangASTMetadata; class ClangASTSource; class ClangExpressionDeclMap; class ClangExpressionParser; class ClangExpressionVariable; class ClangExpressionVariables; class ClangModulesDeclVendor; class ClangPersistentVariables; class CommandInterpreter; class CommandInterpreterRunOptions; class CommandObject; class CommandObjectMultiword; class CommandReturnObject; class Communication; class CompactUnwindInfo; struct CompilerContext; class CompilerDecl; class CompilerDeclContext; class CompilerType; class CompileUnit; class Condition; class Connection; class ConnectionFileDescriptor; class ConstString; class CXXSyntheticChildren; class DWARFCallFrameInfo; class DWARFExpression; class DataBuffer; class DataEncoder; class DataExtractor; class Debugger; class Declaration; class DiagnosticManager; class Disassembler; class DumpValueObjectOptions; class DynamicCheckerFunctions; class DynamicLoader; class Editline; class EmulateInstruction; class Error; class EvaluateExpressionOptions; class Event; class EventData; class EventDataStructuredData; class ExecutionContext; class ExecutionContextRef; class ExecutionContextRefLocker; class ExecutionContextScope; class Expression; class ExpressionVariable; class ExpressionVariableList; class ExpressionTypeSystemHelper; class File; class FileSpec; class FileSpecList; class Flags; class GoASTContext; class TypeCategoryImpl; class FormatManager; class FormattersMatchCandidate; class FuncUnwinders; class Function; class FunctionInfo; class InlineFunctionInfo; class Instruction; class InstructionList; class InstrumentationRuntime; class IOHandler; class IOObject; class IRExecutionUnit; class JITLoader; class JITLoaderList; class Language; class LanguageCategory; class LanguageRuntime; class MemoryRegionInfo; class LineTable; class Listener; class Log; class Mangled; class Materializer; class MemoryHistory; class Module; class ModuleList; class ModuleSpec; class ModuleSpecList; class Mutex; struct NameSearchContext; class ObjCLanguageRuntime; class ObjCRuntimeSyntheticProvider; class ObjectContainer; class OptionGroup; class OptionGroupOptions; class OptionGroupPlatform; class ObjectFile; class ObjectFileJITDelegate; class OperatingSystem; class Options; class OptionValue; class OptionValueArch; class OptionValueArgs; class OptionValueArray; class OptionValueBoolean; class OptionValueChar; class OptionValueDictionary; class OptionValueEnumeration; class OptionValueFileSpec; class OptionValueFileSpecList; class OptionValueFormat; class OptionValueLanguage; class OptionValueFormatEntity; class OptionValuePathMappings; class OptionValueProperties; class OptionValueRegex; class OptionValueSInt64; class OptionValueString; class OptionValueUInt64; class OptionValueUUID; class NamedOption; class PathMappingList; class FunctionCaller; class PersistentExpressionState; class Platform; class Process; class ProcessAttachInfo; class ProcessModID; class ProcessInfo; class ProcessInstanceInfo; class ProcessInstanceInfoList; class ProcessInstanceInfoMatch; class ProcessLaunchInfo; class Property; struct PropertyDefinition; class RegisterCheckpoint; class RegisterContext; class RegisterLocation; class RegisterLocationList; class RegisterValue; class RegularExpression; class REPL; class Scalar; class ScriptInterpreter; class ScriptInterpreterLocker; struct ScriptSummaryFormat; class SearchFilter; class Section; class SectionImpl; class SectionList; class SectionLoadHistory; class SectionLoadList; class Settings; class SourceManager; class SourceManagerImpl; class StackFrame; class StackFrameImpl; class StackFrameList; class StackID; class StopInfo; class Stoppoint; class StoppointCallbackContext; class StoppointLocation; class Stream; template class StreamBuffer; class StreamFile; class StreamString; class StringList; struct StringSummaryFormat; +class StructuredDataImpl; class StructuredDataPlugin; class SystemRuntime; class TypeSummaryImpl; class TypeSummaryOptions; class Symbol; class SymbolContext; class SymbolContextList; class SymbolContextScope; class SymbolContextSpecifier; class SymbolFile; class SymbolFileType; class SymbolVendor; class Symtab; class SyntheticChildren; class SyntheticChildrenFrontEnd; class TypeFilterImpl; class TypeSystem; #ifndef LLDB_DISABLE_PYTHON class ScriptedSyntheticChildren; #endif class Queue; class QueueItem; class QueueImpl; class Target; class TargetList; class TargetProperties; class Thread; class ThreadCollection; class ThreadList; class ThreadPlan; class ThreadPlanBase; class ThreadPlanRunToAddress; class ThreadPlanStepInstruction; class ThreadPlanStepOut; class ThreadPlanStepOverBreakpoint; class ThreadPlanStepRange; class ThreadPlanStepThrough; class ThreadPlanTracer; class ThreadSpec; +class TraceOptions; class Type; class TypeAndOrName; class TypeCategoryMap; class TypeImpl; class TypeList; class TypeMap; class TypeListImpl; class TypeMemberImpl; class TypeMemberFunctionImpl; class TypeEnumMemberImpl; class TypeEnumMemberListImpl; class TypeFormatImpl; class TypeNameSpecifierImpl; class TypePair; class TypeValidatorImpl; class UUID; class UnixSignals; class Unwind; class UnwindAssembly; class UnwindPlan; class UnwindTable; class UserExpression; class UtilityFunction; class VMRange; class Value; class ValueList; class ValueObject; class ValueObjectChild; class ValueObjectConstResult; class ValueObjectConstResultChild; class ValueObjectConstResultImpl; class ValueObjectList; class ValueObjectPrinter; class Variable; class VariableList; class Watchpoint; class WatchpointList; class WatchpointOptions; struct LineEntry; } // namespace lldb_private //---------------------------------------------------------------------- // lldb forward declarations //---------------------------------------------------------------------- namespace lldb { typedef std::shared_ptr ABISP; typedef std::shared_ptr BatonSP; typedef std::shared_ptr BlockSP; typedef std::shared_ptr BreakpointSP; typedef std::weak_ptr BreakpointWP; typedef std::shared_ptr BreakpointSiteSP; typedef std::weak_ptr BreakpointSiteWP; typedef std::shared_ptr BreakpointLocationSP; typedef std::weak_ptr BreakpointLocationWP; typedef std::shared_ptr BreakpointResolverSP; typedef std::shared_ptr BroadcasterSP; typedef std::shared_ptr BroadcasterManagerSP; typedef std::weak_ptr BroadcasterManagerWP; typedef std::unique_ptr ClangASTContextUP; typedef std::shared_ptr ClangASTImporterSP; typedef std::unique_ptr ClangModulesDeclVendorUP; typedef std::unique_ptr ClangPersistentVariablesUP; typedef std::shared_ptr UserExpressionSP; typedef std::shared_ptr CommandObjectSP; typedef std::shared_ptr CommunicationSP; typedef std::shared_ptr ConnectionSP; typedef std::shared_ptr CompUnitSP; typedef std::shared_ptr DataBufferSP; typedef std::shared_ptr DataExtractorSP; typedef std::shared_ptr DebuggerSP; typedef std::weak_ptr DebuggerWP; typedef std::shared_ptr DisassemblerSP; typedef std::unique_ptr DynamicCheckerFunctionsUP; typedef std::shared_ptr DynamicLoaderSP; typedef std::unique_ptr DynamicLoaderUP; typedef std::shared_ptr EventSP; typedef std::shared_ptr EventDataSP; typedef std::shared_ptr EventDataStructuredDataSP; typedef std::shared_ptr ExecutionContextRefSP; typedef std::shared_ptr ExpressionVariableSP; typedef std::shared_ptr FileSP; typedef std::shared_ptr FunctionSP; typedef std::shared_ptr FunctionCallerSP; typedef std::shared_ptr FuncUnwindersSP; typedef std::unique_ptr GoASTContextUP; typedef std::shared_ptr InlineFunctionInfoSP; typedef std::shared_ptr InstructionSP; typedef std::shared_ptr InstrumentationRuntimeSP; typedef std::shared_ptr IOHandlerSP; typedef std::shared_ptr IOObjectSP; typedef std::shared_ptr IRExecutionUnitSP; typedef std::shared_ptr JITLoaderSP; typedef std::unique_ptr JITLoaderListUP; typedef std::shared_ptr LanguageRuntimeSP; typedef std::shared_ptr SystemRuntimeSP; typedef std::unique_ptr SystemRuntimeUP; typedef std::shared_ptr LineTableSP; typedef std::shared_ptr ListenerSP; typedef std::weak_ptr ListenerWP; typedef std::shared_ptr MemoryHistorySP; typedef std::shared_ptr MemoryRegionInfoSP; typedef std::unique_ptr MemoryRegionInfoUP; typedef std::shared_ptr ModuleSP; typedef std::weak_ptr ModuleWP; typedef std::shared_ptr ObjectFileSP; typedef std::weak_ptr ObjectFileWP; typedef std::shared_ptr ObjectFileJITDelegateSP; typedef std::weak_ptr ObjectFileJITDelegateWP; typedef std::unique_ptr OperatingSystemUP; typedef std::shared_ptr OptionValueSP; typedef std::weak_ptr OptionValueWP; typedef std::shared_ptr OptionValueArchSP; typedef std::shared_ptr OptionValueArgsSP; typedef std::shared_ptr OptionValueArraySP; typedef std::shared_ptr OptionValueBooleanSP; typedef std::shared_ptr OptionValueDictionarySP; typedef std::shared_ptr OptionValueFileSpecSP; typedef std::shared_ptr OptionValueFileSpecListSP; typedef std::shared_ptr OptionValueFormatSP; typedef std::shared_ptr OptionValuePathMappingsSP; typedef std::shared_ptr OptionValuePropertiesSP; typedef std::shared_ptr OptionValueRegexSP; typedef std::shared_ptr OptionValueSInt64SP; typedef std::shared_ptr OptionValueStringSP; typedef std::shared_ptr OptionValueUInt64SP; typedef std::shared_ptr OptionValueUUIDSP; typedef std::shared_ptr PlatformSP; typedef std::shared_ptr ProcessSP; typedef std::shared_ptr ProcessAttachInfoSP; typedef std::shared_ptr ProcessLaunchInfoSP; typedef std::weak_ptr ProcessWP; typedef std::shared_ptr PropertySP; typedef std::shared_ptr RegisterCheckpointSP; typedef std::shared_ptr RegisterContextSP; typedef std::shared_ptr RegularExpressionSP; typedef std::shared_ptr QueueSP; typedef std::weak_ptr QueueWP; typedef std::shared_ptr QueueItemSP; typedef std::shared_ptr REPLSP; typedef std::shared_ptr ScriptSummaryFormatSP; typedef std::shared_ptr ScriptInterpreterSP; typedef std::unique_ptr ScriptInterpreterUP; typedef std::shared_ptr SectionSP; typedef std::unique_ptr SectionListUP; typedef std::weak_ptr SectionWP; typedef std::shared_ptr SectionLoadListSP; typedef std::shared_ptr SearchFilterSP; typedef std::shared_ptr SettingsSP; typedef std::unique_ptr SourceManagerUP; typedef std::shared_ptr StackFrameSP; typedef std::unique_ptr StackFrameUP; typedef std::weak_ptr StackFrameWP; typedef std::shared_ptr StackFrameListSP; typedef std::shared_ptr StopInfoSP; typedef std::shared_ptr StoppointLocationSP; typedef std::shared_ptr StreamSP; typedef std::weak_ptr StreamWP; typedef std::shared_ptr StreamFileSP; typedef std::shared_ptr StringTypeSummaryImplSP; +typedef std::unique_ptr StructuredDataImplUP; typedef std::shared_ptr StructuredDataPluginSP; typedef std::weak_ptr StructuredDataPluginWP; typedef std::shared_ptr SymbolFileSP; typedef std::shared_ptr SymbolFileTypeSP; typedef std::weak_ptr SymbolFileTypeWP; typedef std::shared_ptr SymbolContextSpecifierSP; typedef std::unique_ptr SymbolVendorUP; typedef std::shared_ptr SyntheticChildrenSP; typedef std::shared_ptr SyntheticChildrenFrontEndSP; typedef std::shared_ptr TargetSP; typedef std::weak_ptr TargetWP; typedef std::shared_ptr TargetPropertiesSP; typedef std::shared_ptr ThreadSP; typedef std::weak_ptr ThreadWP; typedef std::shared_ptr ThreadCollectionSP; typedef std::shared_ptr ThreadPlanSP; typedef std::shared_ptr ThreadPlanTracerSP; +typedef std::shared_ptr TraceOptionsSP; typedef std::shared_ptr TypeSP; typedef std::weak_ptr TypeWP; typedef std::shared_ptr TypeCategoryImplSP; typedef std::shared_ptr TypeImplSP; typedef std::shared_ptr TypeMemberFunctionImplSP; typedef std::shared_ptr TypeEnumMemberImplSP; typedef std::shared_ptr TypeFilterImplSP; typedef std::shared_ptr TypeSystemSP; typedef std::shared_ptr TypeFormatImplSP; typedef std::shared_ptr TypeNameSpecifierImplSP; typedef std::shared_ptr TypeSummaryImplSP; typedef std::shared_ptr TypeSummaryOptionsSP; typedef std::shared_ptr TypeValidatorImplSP; #ifndef LLDB_DISABLE_PYTHON typedef std::shared_ptr ScriptedSyntheticChildrenSP; #endif typedef std::shared_ptr UnixSignalsSP; typedef std::weak_ptr UnixSignalsWP; typedef std::shared_ptr UnwindAssemblySP; typedef std::shared_ptr UnwindPlanSP; typedef std::shared_ptr UtilityFunctionSP; typedef lldb_private::SharingPtr ValueObjectSP; typedef std::shared_ptr ValueSP; typedef std::shared_ptr ValueListSP; typedef std::shared_ptr VariableSP; typedef std::shared_ptr VariableListSP; typedef std::shared_ptr ValueObjectListSP; typedef std::shared_ptr WatchpointSP; } // namespace lldb #endif // #if defined(__cplusplus) #endif // LLDB_lldb_forward_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 317454) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 317455) @@ -1,85 +1,85 @@ """ Test that we can backtrace correctly with 'noreturn' functions on the stack """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class NoreturnUnwind(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIfWindows # clang-cl does not support gcc style attributes. - @expectedFailureAndroid(bugnumber="llvm.org/pr31192", archs=["x86_64"]) + @expectedFailureAndroid(bugnumber="llvm.org/pr31192") @expectedFailureAll(bugnumber="llvm.org/pr31192", oslist=['linux'], compiler="gcc", archs=['arm']) def test(self): """Test that we can backtrace correctly with 'noreturn' functions on the stack""" self.build() self.setTearDownCleanup() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) process = target.LaunchSimple( None, None, self.get_process_working_directory()) if not process: self.fail("SBTarget.Launch() failed") if process.GetState() != lldb.eStateStopped: self.fail("Process should be in the 'stopped' state, " "instead the actual state is: '%s'" % lldbutil.state_type_to_str(process.GetState())) thread = process.GetThreadAtIndex(0) abort_frame_number = 0 for f in thread.frames: # Some C libraries mangle the abort symbol into __GI_abort. if f.GetFunctionName() in ["abort", "__GI_abort"]: break abort_frame_number = abort_frame_number + 1 if self.TraceOn(): print("Backtrace once we're stopped:") for f in thread.frames: print(" %d %s" % (f.GetFrameID(), f.GetFunctionName())) # I'm going to assume that abort() ends up calling/invoking another # function before halting the process. In which case if abort_frame_number # equals 0, we didn't find abort() in the backtrace. if abort_frame_number == len(thread.frames): self.fail("Unable to find abort() in backtrace.") func_c_frame_number = abort_frame_number + 1 if thread.GetFrameAtIndex( func_c_frame_number).GetFunctionName() != "func_c": self.fail("Did not find func_c() above abort().") # This depends on whether we see the func_b inlined function in the backtrace # or not. I'm not interested in testing that aspect of the backtrace here # right now. if thread.GetFrameAtIndex( func_c_frame_number + 1).GetFunctionName() == "func_b": func_a_frame_number = func_c_frame_number + 2 else: func_a_frame_number = func_c_frame_number + 1 if thread.GetFrameAtIndex( func_a_frame_number).GetFunctionName() != "func_a": self.fail("Did not find func_a() above func_c().") main_frame_number = func_a_frame_number + 1 if thread.GetFrameAtIndex( main_frame_number).GetFunctionName() != "main": self.fail("Did not find main() above func_a().") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py (revision 317454) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py (revision 317455) @@ -1,125 +1,125 @@ """Test stepping over watchpoints.""" from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestStepOverWatchpoint(TestBase): mydir = TestBase.compute_mydir(__file__) def getCategories(self): return ['basic_process'] # Watchpoints not supported @expectedFailureAndroid(archs=['arm', 'aarch64']) @expectedFailureAll( oslist=["linux"], archs=[ 'aarch64', 'arm'], bugnumber="llvm.org/pr26031") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows") # Read-write watchpoints not supported on SystemZ @expectedFailureAll(archs=['s390x']) def test(self): """Test stepping over watchpoints.""" self.build() exe = os.path.join(os.getcwd(), 'a.out') target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) lldbutil.run_break_set_by_symbol(self, 'main') process = target.LaunchSimple(None, None, self.get_process_working_directory()) self.assertTrue(process.IsValid(), PROCESS_IS_VALID) self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) self.assertTrue(thread.IsValid(), "Failed to get thread.") frame = thread.GetFrameAtIndex(0) self.assertTrue(frame.IsValid(), "Failed to get frame.") read_value = frame.FindValue('g_watch_me_read', lldb.eValueTypeVariableGlobal) self.assertTrue(read_value.IsValid(), "Failed to find read value.") error = lldb.SBError() # resolve_location=True, read=True, write=False read_watchpoint = read_value.Watch(True, True, False, error) self.assertTrue(error.Success(), "Error while setting watchpoint: %s" % error.GetCString()) self.assertTrue(read_watchpoint, "Failed to set read watchpoint.") thread.StepOver() self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint, STOPPED_DUE_TO_WATCHPOINT) self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 1') process.Continue() self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) self.assertTrue(thread.GetStopDescription(20) == 'step over') self.step_inst_for_watchpoint(1) write_value = frame.FindValue('g_watch_me_write', lldb.eValueTypeVariableGlobal) self.assertTrue(write_value, "Failed to find write value.") # Most of the MIPS boards provide only one H/W watchpoints, and S/W # watchpoints are not supported yet arch = self.getArchitecture() if re.match("^mips", arch): self.runCmd("watchpoint delete 1") # resolve_location=True, read=False, write=True write_watchpoint = write_value.Watch(True, False, True, error) - self.assertTrue(read_watchpoint, "Failed to set write watchpoint.") + self.assertTrue(write_watchpoint, "Failed to set write watchpoint.") self.assertTrue(error.Success(), "Error while setting watchpoint: %s" % error.GetCString()) thread.StepOver() self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint, STOPPED_DUE_TO_WATCHPOINT) self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 2') process.Continue() self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) self.assertTrue(thread.GetStopDescription(20) == 'step over') self.step_inst_for_watchpoint(2) def step_inst_for_watchpoint(self, wp_id): watchpoint_hit = False current_line = self.frame().GetLineEntry().GetLine() while self.frame().GetLineEntry().GetLine() == current_line: self.thread().StepInstruction(False) # step_over=False stop_reason = self.thread().GetStopReason() if stop_reason == lldb.eStopReasonWatchpoint: self.assertFalse(watchpoint_hit, "Watchpoint already hit.") expected_stop_desc = "watchpoint %d" % wp_id actual_stop_desc = self.thread().GetStopDescription(20) self.assertTrue(actual_stop_desc == expected_stop_desc, "Watchpoint ID didn't match.") watchpoint_hit = True else: self.assertTrue(stop_reason == lldb.eStopReasonPlanComplete, STOPPED_DUE_TO_STEP_IN) self.assertTrue(watchpoint_hit, "Watchpoint never hit.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/inlines/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/inlines/main.c (revision 317454) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/inlines/main.c (revision 317455) @@ -1,19 +1,24 @@ #include inline void test1(int) __attribute__ ((always_inline)); inline void test2(int) __attribute__ ((always_inline)); void test2(int b) { printf("test2(%d)\n", b); //% self.expect("expression b", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["42"]) + { + int c = b * 2; + printf("c=%d\n", c); //% self.expect("expression b", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["42"]) + //% self.expect("expression c", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["84"]) + } } void test1(int a) { printf("test1(%d)\n", a); test2(a+1);//% self.dbg.HandleCommand("step") //% self.expect("expression b", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["24"]) } int main() { test2(42); test1(23); } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py (revision 317454) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py (revision 317455) @@ -1,132 +1,132 @@ """ Test display and Python APIs on file and class static variables. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class StaticVariableTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break at. self.line = line_number('main.cpp', '// Set break point at this line.') @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24764") def test_with_run_command(self): """Test that file and class static variables display correctly.""" self.build() self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # global variables are no longer displayed with the "frame variable" # command. self.expect( 'target variable A::g_points', VARIABLES_DISPLAYED_CORRECTLY, patterns=['\(PointType \[[1-9]*\]\) A::g_points = {']) self.expect('target variable g_points', VARIABLES_DISPLAYED_CORRECTLY, substrs=['(PointType [2]) g_points']) # On Mac OS X, gcc 4.2 emits the wrong debug info for A::g_points. # A::g_points is an array of two elements. if self.platformIsDarwin() or self.getPlatform() == "linux": self.expect( "target variable A::g_points[1].x", VARIABLES_DISPLAYED_CORRECTLY, startstr="(int) A::g_points[1].x = 11") @expectedFailureAll( compiler=["gcc"], bugnumber="Compiler emits incomplete debug info") @expectedFailureAll( compiler=["clang"], - compiler_version=["<", "3.8"], + compiler_version=["<", "3.9"], bugnumber='llvm.org/pr20550') @add_test_categories(['pyapi']) def test_with_python_api(self): """Test Python APIs on file and class static variables.""" self.build() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) self.assertTrue(breakpoint, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. process = target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) # The stop reason of the thread should be breakpoint. thread = lldbutil.get_stopped_thread( process, lldb.eStopReasonBreakpoint) self.assertIsNotNone(thread) # Get the SBValue of 'A::g_points' and 'g_points'. frame = thread.GetFrameAtIndex(0) # arguments => False # locals => False # statics => True # in_scope_only => False valList = frame.GetVariables(False, False, True, False) for val in valList: self.DebugSBValue(val) name = val.GetName() self.assertTrue(name in ['g_points', 'A::g_points']) if name == 'g_points': self.assertTrue( val.GetValueType() == lldb.eValueTypeVariableStatic) self.assertEqual(val.GetNumChildren(), 2) elif name == 'A::g_points': self.assertTrue( val.GetValueType() == lldb.eValueTypeVariableGlobal) self.assertEqual(val.GetNumChildren(), 2) child1 = val.GetChildAtIndex(1) self.DebugSBValue(child1) child1_x = child1.GetChildAtIndex(0) self.DebugSBValue(child1_x) self.assertTrue(child1_x.GetTypeName() == 'int' and child1_x.GetValue() == '11') # SBFrame.FindValue() should also work. val = frame.FindValue("A::g_points", lldb.eValueTypeVariableGlobal) self.DebugSBValue(val) self.assertTrue(val.GetName() == 'A::g_points') # Also exercise the "parameter" and "local" scopes while we are at it. val = frame.FindValue("argc", lldb.eValueTypeVariableArgument) self.DebugSBValue(val) self.assertTrue(val.GetName() == 'argc') val = frame.FindValue("argv", lldb.eValueTypeVariableArgument) self.DebugSBValue(val) self.assertTrue(val.GetName() == 'argv') val = frame.FindValue("hello_world", lldb.eValueTypeVariableLocal) self.DebugSBValue(val) self.assertTrue(val.GetName() == 'hello_world') Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/Makefile (revision 317455) @@ -0,0 +1,3 @@ +LEVEL = ../../../make +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules \ No newline at end of file Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py (revision 317455) @@ -0,0 +1,7 @@ +from lldbsuite.test import lldbinline +from lldbsuite.test import decorators + +lldbinline.MakeInlineTest( + __file__, globals(), [ + decorators.expectedFailureAll( + oslist=["windows"], bugnumber="llvm.org/pr24764")]) \ No newline at end of file Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/main.cpp (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/main.cpp (revision 317455) @@ -0,0 +1,36 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LIDENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace n { + struct D { + int i; + static int anInt() { return 2; } + int dump() { return i; } + }; + + class C { + public: + int foo(D *D); + }; +} + +using namespace n; + +int C::foo(D* D) { + return D->dump(); //% self.expect("expression -- D->dump()", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["int", "2"]) + //% self.expect("expression -- D::anInt()", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["int", "2"]) + +} + +int main (int argc, char const *argv[]) +{ + D myD { D::anInt() }; + C().foo(&myD); + return 0; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/main.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py (revision 317454) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py (revision 317455) @@ -1,139 +1,139 @@ """ Tests imported namespaces in C++. """ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestCppNsImport(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=['freebsd'], bugnumber="llvm.org/pr25925") def test_with_run_command(self): """Tests imported namespaces in C++.""" self.build() # Get main source file src_file = "main.cpp" src_file_spec = lldb.SBFileSpec(src_file) self.assertTrue(src_file_spec.IsValid(), "Main source file") # Get the path of the executable cwd = os.getcwd() exe_file = "a.out" exe_path = os.path.join(cwd, exe_file) # Load the executable target = self.dbg.CreateTarget(exe_path) self.assertTrue(target.IsValid(), VALID_TARGET) # Break on main function break_0 = target.BreakpointCreateBySourceRegex( "// break 0", src_file_spec) self.assertTrue( break_0.IsValid() and break_0.GetNumLocations() >= 1, VALID_BREAKPOINT) break_1 = target.BreakpointCreateBySourceRegex( "// break 1", src_file_spec) self.assertTrue( break_1.IsValid() and break_1.GetNumLocations() >= 1, VALID_BREAKPOINT) # Launch the process args = None env = None process = target.LaunchSimple( args, env, self.get_process_working_directory()) self.assertTrue(process.IsValid(), PROCESS_IS_VALID) # Get the thread of the process self.assertTrue( process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) thread = lldbutil.get_stopped_thread( process, lldb.eStopReasonBreakpoint) # Get current fream of the thread at the breakpoint frame = thread.GetSelectedFrame() # Test imported namespaces test_result = frame.EvaluateExpression("n") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 1, "n = 1") test_result = frame.EvaluateExpression("N::n") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 1, "N::n = 1") test_result = frame.EvaluateExpression("nested") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 3, "nested = 3") test_result = frame.EvaluateExpression("anon") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 2, "anon = 2") test_result = frame.EvaluateExpression("global") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 4, "global = 4") test_result = frame.EvaluateExpression("fun_var") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 9, "fun_var = 9") test_result = frame.EvaluateExpression("Fun::fun_var") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 0, "Fun::fun_var = 0") test_result = frame.EvaluateExpression("not_imported") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 35, "not_imported = 35") # Currently there is no way to distinguish between "::imported" and "imported" in ClangExpressionDeclMap so this fails #test_result = frame.EvaluateExpression("::imported") #self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 89, "::imported = 89") test_result = frame.EvaluateExpression("Imported::imported") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 99, "Imported::imported = 99") test_result = frame.EvaluateExpression("imported") self.assertTrue( - test_result.IsValid() and test_result.GetError().Fail(), - "imported is ambiguous") + test_result.IsValid() and test_result.GetValueAsSigned() == 99, + "imported = 99") test_result = frame.EvaluateExpression("single") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 3, "single = 3") # Continue to second breakpoint process.Continue() # Get the thread of the process self.assertTrue( process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) thread = lldbutil.get_stopped_thread( process, lldb.eStopReasonBreakpoint) # Get current fream of the thread at the breakpoint frame = thread.GetSelectedFrame() # Test function inside namespace test_result = frame.EvaluateExpression("fun_var") self.assertTrue( test_result.IsValid() and test_result.GetValueAsSigned() == 5, "fun_var = 5") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/Makefile (revision 317455) @@ -0,0 +1,3 @@ +LEVEL = ../../../make +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/TestSymbols.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/TestSymbols.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/TestSymbols.py (revision 317455) @@ -0,0 +1,7 @@ +from lldbsuite.test import lldbinline +from lldbsuite.test import decorators + +lldbinline.MakeInlineTest( + __file__, globals(), [ + decorators.expectedFailureAll( + oslist=["windows"], bugnumber="llvm.org/pr24764")]) Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/TestSymbols.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/main.cpp (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/main.cpp (revision 317455) @@ -0,0 +1,40 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LIDENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +void *D = 0; + +class D { + static int i; +}; + +int D::i = 3; + +namespace errno { + int j = 4; +}; + +int twice(int n) +{ + return n * 2; //% self.expect("expression -- D::i", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["int", "3"]) + //% self.expect("expression -- D", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["void"]) + //% self.expect("expression -- errno::j", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["int", "4"]) +} + +const char getAChar() +{ + const char D[] = "Hello world"; + return D[0]; //% self.expect("expression -- D::i", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["int", "3"]) + //% self.expect("expression -- D", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["char", "Hello"]) +} + +int main (int argc, char const *argv[]) +{ + int six = twice(3); + return 0; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/symbols/main.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/scripts/interface/SBProcess.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBProcess.i (revision 317454) +++ vendor/lldb/dist/scripts/interface/SBProcess.i (revision 317455) @@ -1,515 +1,518 @@ //===-- SWIG Interface for SBProcess ----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// namespace lldb { %feature("docstring", "Represents the process associated with the target program. SBProcess supports thread iteration. For example (from test/lldbutil.py), # ================================================== # Utility functions related to Threads and Processes # ================================================== def get_stopped_threads(process, reason): '''Returns the thread(s) with the specified stop reason in a list. The list can be empty if no such thread exists. ''' threads = [] for t in process: if t.GetStopReason() == reason: threads.append(t) return threads ... " ) SBProcess; class SBProcess { public: //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ enum { eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5) }; SBProcess (); SBProcess (const lldb::SBProcess& rhs); ~SBProcess(); static const char * GetBroadcasterClassName (); const char * GetPluginName (); const char * GetShortPluginName (); void Clear (); bool IsValid() const; lldb::SBTarget GetTarget() const; lldb::ByteOrder GetByteOrder() const; %feature("autodoc", " Writes data into the current process's stdin. API client specifies a Python string as the only argument. ") PutSTDIN; size_t PutSTDIN (const char *src, size_t src_len); %feature("autodoc", " Reads data from the current process's stdout stream. API client specifies the size of the buffer to read data into. It returns the byte buffer in a Python string. ") GetSTDOUT; size_t GetSTDOUT (char *dst, size_t dst_len) const; %feature("autodoc", " Reads data from the current process's stderr stream. API client specifies the size of the buffer to read data into. It returns the byte buffer in a Python string. ") GetSTDERR; size_t GetSTDERR (char *dst, size_t dst_len) const; size_t GetAsyncProfileData(char *dst, size_t dst_len) const; void ReportEventState (const lldb::SBEvent &event, FILE *out) const; void AppendEventStateReport (const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); %feature("docstring", " //------------------------------------------------------------------ /// Remote connection related functions. These will fail if the /// process is not in eStateConnected. They are intended for use /// when connecting to an externally managed debugserver instance. //------------------------------------------------------------------ ") RemoteAttachToProcessWithID; bool RemoteAttachToProcessWithID (lldb::pid_t pid, lldb::SBError& error); %feature("docstring", "See SBTarget.Launch for argument description and usage." ) RemoteLaunch; bool RemoteLaunch (char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError& error); //------------------------------------------------------------------ // Thread related functions //------------------------------------------------------------------ uint32_t GetNumThreads (); %feature("autodoc", " Returns the INDEX'th thread from the list of current threads. The index of a thread is only valid for the current stop. For a persistent thread identifier use either the thread ID or the IndexID. See help on SBThread for more details. ") GetThreadAtIndex; lldb::SBThread GetThreadAtIndex (size_t index); %feature("autodoc", " Returns the thread with the given thread ID. ") GetThreadByID; lldb::SBThread GetThreadByID (lldb::tid_t sb_thread_id); %feature("autodoc", " Returns the thread with the given thread IndexID. ") GetThreadByIndexID; lldb::SBThread GetThreadByIndexID (uint32_t index_id); %feature("autodoc", " Returns the currently selected thread. ") GetSelectedThread; lldb::SBThread GetSelectedThread () const; %feature("autodoc", " Lazily create a thread on demand through the current OperatingSystem plug-in, if the current OperatingSystem plug-in supports it. ") CreateOSPluginThread; lldb::SBThread CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); bool SetSelectedThread (const lldb::SBThread &thread); bool SetSelectedThreadByID (lldb::tid_t tid); bool SetSelectedThreadByIndexID (uint32_t index_id); //------------------------------------------------------------------ // Queue related functions //------------------------------------------------------------------ uint32_t GetNumQueues (); lldb::SBQueue GetQueueAtIndex (uint32_t index); //------------------------------------------------------------------ // Stepping related functions //------------------------------------------------------------------ lldb::StateType GetState (); int GetExitStatus (); const char * GetExitDescription (); %feature("autodoc", " Returns the process ID of the process. ") GetProcessID; lldb::pid_t GetProcessID (); %feature("autodoc", " Returns an integer ID that is guaranteed to be unique across all process instances. This is not the process ID, just a unique integer for comparison and caching purposes. ") GetUniqueID; uint32_t GetUniqueID(); uint32_t GetAddressByteSize() const; %feature("docstring", " Kills the process and shuts down all threads that were spawned to track and monitor process. ") Destroy; lldb::SBError Destroy (); lldb::SBError Continue (); lldb::SBError Stop (); %feature("docstring", "Same as Destroy(self).") Destroy; lldb::SBError Kill (); lldb::SBError Detach (); %feature("docstring", "Sends the process a unix signal.") Signal; lldb::SBError Signal (int signal); lldb::SBUnixSignals GetUnixSignals(); %feature("docstring", " Returns a stop id that will increase every time the process executes. If include_expression_stops is true, then stops caused by expression evaluation will cause the returned value to increase, otherwise the counter returned will only increase when execution is continued explicitly by the user. Note, the value will always increase, but may increase by more than one per stop. ") GetStopID; uint32_t GetStopID(bool include_expression_stops = false); void SendAsyncInterrupt(); %feature("autodoc", " Reads memory from the current process's address space and removes any traps that may have been inserted into the memory. It returns the byte buffer in a Python string. Example: # Read 4 bytes from address 'addr' and assume error.Success() is True. content = process.ReadMemory(addr, 4, error) new_bytes = bytearray(content) ") ReadMemory; size_t ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); %feature("autodoc", " Writes memory to the current process's address space and maintains any traps that might be present due to software breakpoints. Example: # Create a Python string from the byte array. new_value = str(bytes) result = process.WriteMemory(addr, new_value, error) if not error.Success() or result != len(bytes): print('SBProcess.WriteMemory() failed!') ") WriteMemory; size_t WriteMemory (addr_t addr, const void *buf, size_t size, lldb::SBError &error); %feature("autodoc", " Reads a NULL terminated C string from the current process's address space. It returns a python string of the exact length, or truncates the string if the maximum character limit is reached. Example: # Read a C string of at most 256 bytes from address '0x1000' error = lldb.SBError() cstring = process.ReadCStringFromMemory(0x1000, 256, error) if error.Success(): print('cstring: ', cstring) else print('error: ', error) ") ReadCStringFromMemory; size_t ReadCStringFromMemory (addr_t addr, void *char_buf, size_t size, lldb::SBError &error); %feature("autodoc", " Reads an unsigned integer from memory given a byte size and an address. Returns the unsigned integer that was read. Example: # Read a 4 byte unsigned integer from address 0x1000 error = lldb.SBError() uint = ReadUnsignedFromMemory(0x1000, 4, error) if error.Success(): print('integer: %u' % uint) else print('error: ', error) ") ReadUnsignedFromMemory; uint64_t ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &error); %feature("autodoc", " Reads a pointer from memory from an address and returns the value. Example: # Read a pointer from address 0x1000 error = lldb.SBError() ptr = ReadPointerFromMemory(0x1000, error) if error.Success(): print('pointer: 0x%x' % ptr) else print('error: ', error) ") ReadPointerFromMemory; lldb::addr_t ReadPointerFromMemory (addr_t addr, lldb::SBError &error); // Events static lldb::StateType GetStateFromEvent (const lldb::SBEvent &event); static bool GetRestartedFromEvent (const lldb::SBEvent &event); static size_t GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event); static const char * GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx); static lldb::SBProcess GetProcessFromEvent (const lldb::SBEvent &event); static bool GetInterruptedFromEvent (const lldb::SBEvent &event); static lldb::SBStructuredData GetStructuredDataFromEvent (const lldb::SBEvent &event); static bool EventIsProcessEvent (const lldb::SBEvent &event); static bool EventIsStructuredDataEvent (const lldb::SBEvent &event); lldb::SBBroadcaster GetBroadcaster () const; bool GetDescription (lldb::SBStream &description); uint32_t GetNumSupportedHardwareWatchpoints (lldb::SBError &error) const; uint32_t LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); lldb::SBError UnloadImage (uint32_t image_token); lldb::SBError SendEventData (const char *event_data); %feature("autodoc", " Return the number of different thread-origin extended backtraces this process can support as a uint32_t. When the process is stopped and you have an SBThread, lldb may be able to show a backtrace of when that thread was originally created, or the work item was enqueued to it (in the case of a libdispatch queue). ") GetNumExtendedBacktraceTypes; uint32_t GetNumExtendedBacktraceTypes (); %feature("autodoc", " Takes an index argument, returns the name of one of the thread-origin extended backtrace methods as a str. ") GetExtendedBacktraceTypeAtIndex; const char * GetExtendedBacktraceTypeAtIndex (uint32_t idx); lldb::SBThreadCollection GetHistoryThreads (addr_t addr); bool IsInstrumentationRuntimePresent(lldb::InstrumentationRuntimeType type); lldb::SBError SaveCore(const char *file_name); + lldb::SBTrace + StartTrace(SBTraceOptions &options, lldb::SBError &error); + lldb::SBError GetMemoryRegionInfo(lldb::addr_t load_addr, lldb::SBMemoryRegionInfo ®ion_info); lldb::SBMemoryRegionInfoList GetMemoryRegions(); %pythoncode %{ def __get_is_alive__(self): '''Returns "True" if the process is currently alive, "False" otherwise''' s = self.GetState() if (s == eStateAttaching or s == eStateLaunching or s == eStateStopped or s == eStateRunning or s == eStateStepping or s == eStateCrashed or s == eStateSuspended): return True return False def __get_is_running__(self): '''Returns "True" if the process is currently running, "False" otherwise''' state = self.GetState() if state == eStateRunning or state == eStateStepping: return True return False def __get_is_stopped__(self): '''Returns "True" if the process is currently stopped, "False" otherwise''' state = self.GetState() if state == eStateStopped or state == eStateCrashed or state == eStateSuspended: return True return False class threads_access(object): '''A helper object that will lazily hand out thread for a process when supplied an index.''' def __init__(self, sbprocess): self.sbprocess = sbprocess def __len__(self): if self.sbprocess: return int(self.sbprocess.GetNumThreads()) return 0 def __getitem__(self, key): if type(key) is int and key < len(self): return self.sbprocess.GetThreadAtIndex(key) return None def get_threads_access_object(self): '''An accessor function that returns a modules_access() object which allows lazy thread access from a lldb.SBProcess object.''' return self.threads_access (self) def get_process_thread_list(self): '''An accessor function that returns a list() that contains all threads in a lldb.SBProcess object.''' threads = [] accessor = self.get_threads_access_object() for idx in range(len(accessor)): threads.append(accessor[idx]) return threads __swig_getmethods__["threads"] = get_process_thread_list if _newclass: threads = property(get_process_thread_list, None, doc='''A read only property that returns a list() of lldb.SBThread objects for this process.''') __swig_getmethods__["thread"] = get_threads_access_object if _newclass: thread = property(get_threads_access_object, None, doc='''A read only property that returns an object that can access threads by thread index (thread = lldb.process.thread[12]).''') __swig_getmethods__["is_alive"] = __get_is_alive__ if _newclass: is_alive = property(__get_is_alive__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently alive.''') __swig_getmethods__["is_running"] = __get_is_running__ if _newclass: is_running = property(__get_is_running__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently running.''') __swig_getmethods__["is_stopped"] = __get_is_stopped__ if _newclass: is_stopped = property(__get_is_stopped__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently stopped.''') __swig_getmethods__["id"] = GetProcessID if _newclass: id = property(GetProcessID, None, doc='''A read only property that returns the process ID as an integer.''') __swig_getmethods__["target"] = GetTarget if _newclass: target = property(GetTarget, None, doc='''A read only property that an lldb object that represents the target (lldb.SBTarget) that owns this process.''') __swig_getmethods__["num_threads"] = GetNumThreads if _newclass: num_threads = property(GetNumThreads, None, doc='''A read only property that returns the number of threads in this process as an integer.''') __swig_getmethods__["selected_thread"] = GetSelectedThread __swig_setmethods__["selected_thread"] = SetSelectedThread if _newclass: selected_thread = property(GetSelectedThread, SetSelectedThread, doc='''A read/write property that gets/sets the currently selected thread in this process. The getter returns a lldb.SBThread object and the setter takes an lldb.SBThread object.''') __swig_getmethods__["state"] = GetState if _newclass: state = property(GetState, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eState") that represents the current state of this process (running, stopped, exited, etc.).''') __swig_getmethods__["exit_state"] = GetExitStatus if _newclass: exit_state = property(GetExitStatus, None, doc='''A read only property that returns an exit status as an integer of this process when the process state is lldb.eStateExited.''') __swig_getmethods__["exit_description"] = GetExitDescription if _newclass: exit_description = property(GetExitDescription, None, doc='''A read only property that returns an exit description as a string of this process when the process state is lldb.eStateExited.''') __swig_getmethods__["broadcaster"] = GetBroadcaster if _newclass: broadcaster = property(GetBroadcaster, None, doc='''A read only property that an lldb object that represents the broadcaster (lldb.SBBroadcaster) for this process.''') %} }; } // namespace lldb Index: vendor/lldb/dist/scripts/interface/SBStructuredData.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBStructuredData.i (revision 317454) +++ vendor/lldb/dist/scripts/interface/SBStructuredData.i (revision 317455) @@ -1,42 +1,45 @@ //===-- SWIG Interface for SBStructuredData ---------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// namespace lldb { %feature("docstring", "A class representing a StructuredData event. This class wraps the event type generated by StructuredData features." ) SBStructuredData; class SBStructuredData { public: SBStructuredData(); SBStructuredData(const lldb::SBStructuredData &rhs); SBStructuredData(const lldb::EventSP &event_sp); ~SBStructuredData(); bool IsValid() const; void Clear(); lldb::SBError GetAsJSON(lldb::SBStream &stream) const; lldb::SBError GetDescription(lldb::SBStream &stream) const; + + lldb::SBError + SetFromJSON(lldb::SBStream &stream); }; } Index: vendor/lldb/dist/scripts/interface/SBTrace.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBTrace.i (nonexistent) +++ vendor/lldb/dist/scripts/interface/SBTrace.i (revision 317455) @@ -0,0 +1,34 @@ +//===-- SWIG Interface for SBTrace.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class LLDB_API SBTrace { +public: + SBTrace(); + size_t GetTraceData(SBError &error, void *buf, + size_t size, size_t offset, + lldb::tid_t thread_id); + + size_t GetMetaData(SBError &error, void *buf, + size_t size, size_t offset, + lldb::tid_t thread_id); + + void StopTrace(SBError &error, + lldb::tid_t thread_id); + + void GetTraceConfig(SBTraceOptions &options, + SBError &error); + + lldb::user_id_t GetTraceUID(); + + bool IsValid(); + +}; +} // namespace lldb \ No newline at end of file Index: vendor/lldb/dist/scripts/interface/SBTraceOptions.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBTraceOptions.i (nonexistent) +++ vendor/lldb/dist/scripts/interface/SBTraceOptions.i (revision 317455) @@ -0,0 +1,38 @@ +//===-- SWIG Interface for SBTraceOptions -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class LLDB_API SBTraceOptions { +public: + SBTraceOptions(); + + lldb::TraceType getType() const; + + uint64_t getTraceBufferSize() const; + + lldb::SBStructuredData getTraceParams(lldb::SBError &error); + + uint64_t getMetaDataBufferSize() const; + + void setTraceParams(lldb::SBStructuredData ¶ms); + + void setType(lldb::TraceType type); + + void setTraceBufferSize(uint64_t size); + + void setMetaDataBufferSize(uint64_t size); + + void setThreadID(lldb::tid_t thread_id); + + lldb::tid_t getThreadID(); + + bool IsValid(); +}; +} Index: vendor/lldb/dist/scripts/lldb.swig =================================================================== --- vendor/lldb/dist/scripts/lldb.swig (revision 317454) +++ vendor/lldb/dist/scripts/lldb.swig (revision 317455) @@ -1,205 +1,209 @@ /* lldb.swig This is the input file for SWIG, to create the appropriate C++ wrappers and functions for various scripting languages, to enable them to call the liblldb Script Bridge functions. */ /* Define our module docstring. */ %define DOCSTRING "The lldb module contains the public APIs for Python binding. Some of the important classes are described here: o SBTarget: Represents the target program running under the debugger. o SBProcess: Represents the process associated with the target program. o SBThread: Represents a thread of execution. SBProcess contains SBThread(s). o SBFrame: Represents one of the stack frames associated with a thread. SBThread contains SBFrame(s). o SBSymbolContext: A container that stores various debugger related info. o SBValue: Represents the value of a variable, a register, or an expression. o SBModule: Represents an executable image and its associated object and symbol files. SBTarget contains SBModule(s). o SBBreakpoint: Represents a logical breakpoint and its associated settings. SBTarget contains SBBreakpoint(s). o SBSymbol: Represents the symbol possibly associated with a stack frame. o SBCompileUnit: Represents a compilation unit, or compiled source file. o SBFunction: Represents a generic function, which can be inlined or not. o SBBlock: Represents a lexical block. SBFunction contains SBBlock(s). o SBLineEntry: Specifies an association with a contiguous range of instructions and a source file location. SBCompileUnit contains SBLineEntry(s)." %enddef // The name of the module to be created. %module(docstring=DOCSTRING) lldb // Parameter types will be used in the autodoc string. %feature("autodoc", "1"); %pythoncode%{ import uuid import re import os import six %} %include "./Python/python-typemaps.swig" /* C++ headers to be included. */ %{ #include #include %} /* The liblldb header files to be included. */ %{ #include "lldb/lldb-public.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBlock.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBCompileUnit.h" #include "lldb/API/SBData.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFileSpecList.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBFunction.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBPlatform.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbol.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/API/SBSymbolContextList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBType.h" #include "lldb/API/SBTypeCategory.h" #include "lldb/API/SBTypeEnumMember.h" #include "lldb/API/SBTypeFilter.h" #include "lldb/API/SBTypeFormat.h" #include "lldb/API/SBTypeNameSpecifier.h" #include "lldb/API/SBTypeSummary.h" #include "lldb/API/SBTypeSynthetic.h" #include "lldb/API/SBValue.h" #include "lldb/API/SBValueList.h" #include "lldb/API/SBVariablesOptions.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBUnixSignals.h" #include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "../scripts/Python/python-swigsafecast.swig" %} /* Various liblldb typedefs that SWIG needs to know about. */ #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ /* The ISO C99 standard specifies that in C++ implementations limit macros such as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ #define __STDC_LIMIT_MACROS %include "stdint.i" %include "lldb/lldb-defines.h" %include "lldb/lldb-enumerations.h" %include "lldb/lldb-forward.h" %include "lldb/lldb-types.h" /* Forward declaration of SB classes. */ %include "lldb/API/SBDefines.h" /* Python interface files with docstrings. */ %include "./interface/SBAddress.i" %include "./interface/SBAttachInfo.i" %include "./interface/SBBlock.i" %include "./interface/SBBreakpoint.i" %include "./interface/SBBreakpointLocation.i" %include "./interface/SBBroadcaster.i" %include "./interface/SBCommandInterpreter.i" %include "./interface/SBCommandReturnObject.i" %include "./interface/SBCommunication.i" %include "./interface/SBCompileUnit.i" %include "./interface/SBData.i" %include "./interface/SBDebugger.i" %include "./interface/SBDeclaration.i" %include "./interface/SBError.i" %include "./interface/SBEvent.i" %include "./interface/SBExecutionContext.i" %include "./interface/SBExpressionOptions.i" %include "./interface/SBFileSpec.i" %include "./interface/SBFileSpecList.i" %include "./interface/SBFrame.i" %include "./interface/SBFunction.i" %include "./interface/SBHostOS.i" %include "./interface/SBInstruction.i" %include "./interface/SBInstructionList.i" %include "./interface/SBLanguageRuntime.i" %include "./interface/SBLaunchInfo.i" %include "./interface/SBLineEntry.i" %include "./interface/SBListener.i" %include "./interface/SBMemoryRegionInfo.i" %include "./interface/SBMemoryRegionInfoList.i" %include "./interface/SBModule.i" %include "./interface/SBModuleSpec.i" %include "./interface/SBPlatform.i" %include "./interface/SBProcess.i" %include "./interface/SBQueue.i" %include "./interface/SBQueueItem.i" %include "./interface/SBSection.i" %include "./interface/SBSourceManager.i" %include "./interface/SBStream.i" %include "./interface/SBStringList.i" %include "./interface/SBStructuredData.i" %include "./interface/SBSymbol.i" %include "./interface/SBSymbolContext.i" %include "./interface/SBSymbolContextList.i" %include "./interface/SBTarget.i" %include "./interface/SBThread.i" %include "./interface/SBThreadCollection.i" %include "./interface/SBThreadPlan.i" +%include "./interface/SBTrace.i" +%include "./interface/SBTraceOptions.i" %include "./interface/SBType.i" %include "./interface/SBTypeCategory.i" %include "./interface/SBTypeEnumMember.i" %include "./interface/SBTypeFilter.i" %include "./interface/SBTypeFormat.i" %include "./interface/SBTypeNameSpecifier.i" %include "./interface/SBTypeSummary.i" %include "./interface/SBTypeSynthetic.i" %include "./interface/SBValue.i" %include "./interface/SBValueList.i" %include "./interface/SBVariablesOptions.i" %include "./interface/SBWatchpoint.i" %include "./interface/SBUnixSignals.i" %include "./Python/python-extensions.swig" %include "./Python/python-wrapper.swig" Index: vendor/lldb/dist/source/API/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/API/CMakeLists.txt (revision 317454) +++ vendor/lldb/dist/source/API/CMakeLists.txt (revision 317455) @@ -1,172 +1,174 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) add_definitions( -DEXPORT_LIBLLDB ) endif() # Include this so that add_lldb_library() has the list of dependencies # for liblldb to link against include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) option(LLDB_BUILD_FRAMEWORK "Build the Darwin LLDB.framework" Off) if(LLDB_BUILD_FRAMEWORK AND CMAKE_VERSION VERSION_LESS 3.7) message(FATAL_ERROR "LLDB_BUILD_FRAMEWORK is not supported on CMake < 3.7") endif() if (LLDB_BUILD_FRAMEWORK AND NOT APPLE) message(FATAL_ERROR "LLDB.framework cannot be generated unless targeting Apple platforms.") endif() get_property(LLDB_ALL_PLUGINS GLOBAL PROPERTY LLDB_PLUGINS) add_lldb_library(liblldb SHARED SBAddress.cpp SBAttachInfo.cpp SBBlock.cpp SBBreakpoint.cpp SBBreakpointLocation.cpp SBBroadcaster.cpp SBCommandInterpreter.cpp SBCommandReturnObject.cpp SBCommunication.cpp SBCompileUnit.cpp SBData.cpp SBDebugger.cpp SBDeclaration.cpp SBError.cpp SBEvent.cpp SBExecutionContext.cpp SBExpressionOptions.cpp SBFileSpec.cpp SBFileSpecList.cpp SBFrame.cpp SBFunction.cpp SBHostOS.cpp SBInstruction.cpp SBInstructionList.cpp SBLanguageRuntime.cpp SBLaunchInfo.cpp SBLineEntry.cpp SBListener.cpp SBMemoryRegionInfo.cpp SBMemoryRegionInfoList.cpp SBModule.cpp SBModuleSpec.cpp SBPlatform.cpp SBProcess.cpp SBQueue.cpp SBQueueItem.cpp SBSection.cpp SBSourceManager.cpp SBStream.cpp SBStringList.cpp SBStructuredData.cpp SBSymbol.cpp SBSymbolContext.cpp SBSymbolContextList.cpp SBTarget.cpp SBThread.cpp SBThreadCollection.cpp SBThreadPlan.cpp + SBTrace.cpp + SBTraceOptions.cpp SBType.cpp SBTypeCategory.cpp SBTypeEnumMember.cpp SBTypeFilter.cpp SBTypeFormat.cpp SBTypeNameSpecifier.cpp SBTypeSummary.cpp SBTypeSynthetic.cpp SBValue.cpp SBValueList.cpp SBVariablesOptions.cpp SBWatchpoint.cpp SBUnixSignals.cpp SystemInitializerFull.cpp ${LLDB_WRAP_PYTHON} LINK_LIBS lldbBase lldbBreakpoint lldbCore lldbDataFormatters lldbExpression lldbHost lldbInitialization lldbInterpreter lldbSymbol lldbTarget lldbUtility ${LLDB_ALL_PLUGINS} LINK_COMPONENTS Support ) if (LLVM_ENABLE_WERROR) if (MSVC) set_property(SOURCE ${LLDB_WRAP_PYTHON} APPEND_STRING PROPERTY COMPILE_FLAGS " /W0") else() set_property(SOURCE ${LLDB_WRAP_PYTHON} APPEND_STRING PROPERTY COMPILE_FLAGS " -w") endif() endif() # This should not be part of LLDBDependencies.cmake, because we don't # want every single library taking a dependency on the script interpreters. target_link_libraries(liblldb PRIVATE lldbPluginScriptInterpreterNone lldbPluginScriptInterpreterPython ) set_target_properties(liblldb PROPERTIES VERSION ${LLDB_VERSION} ) if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") if (NOT LLDB_EXPORT_ALL_SYMBOLS) # If we're not exporting all symbols, we'll want to explicitly set # the exported symbols here. This prevents 'log enable --stack ...' # from working on some systems but limits the liblldb size. MESSAGE("-- Symbols (liblldb): exporting all symbols from the lldb namespace") add_llvm_symbol_exports(liblldb ${CMAKE_CURRENT_SOURCE_DIR}/liblldb.exports) else() # Don't use an explicit export. Instead, tell the linker to # export all symbols. MESSAGE("-- Symbols (liblldb): exporting all symbols from the lldb and lldb_private namespaces") add_llvm_symbol_exports(liblldb ${CMAKE_CURRENT_SOURCE_DIR}/liblldb-private.exports) endif() endif() if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) # Only MSVC has the ABI compatibility problem and avoids using FindPythonLibs, # so only it needs to explicitly link against ${PYTHON_LIBRARY} if (MSVC AND NOT LLDB_DISABLE_PYTHON) target_link_libraries(liblldb PRIVATE ${PYTHON_LIBRARY}) endif() else() set_target_properties(liblldb PROPERTIES OUTPUT_NAME lldb ) endif() if (LLDB_WRAP_PYTHON) add_dependencies(liblldb swig_wrapper) endif() target_link_libraries(liblldb PRIVATE ${LLDB_SYSTEM_LIBS}) if(LLDB_BUILD_FRAMEWORK) file(GLOB public_headers ${LLDB_SOURCE_DIR}/include/lldb/API/*.h) set_target_properties(liblldb PROPERTIES OUTPUT_NAME LLDB FRAMEWORK On FRAMEWORK_VERSION ${LLDB_FRAMEWORK_VERSION} LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LLDB_FRAMEWORK_INSTALL_DIR} PUBLIC_HEADER "${public_headers}") add_custom_command(TARGET liblldb POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $/Versions/${LLDB_FRAMEWORK_VERSION} COMMAND ${CMAKE_COMMAND} -E copy_directory ${LLDB_SOURCE_DIR}/include/lldb/API $/Headers COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers ${CMAKE_BINARY_DIR}/${LLDB_FRAMEWORK_INSTALL_DIR}/LLDB.framework/Headers COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/clang/${LLDB_VERSION} $/Resources/Clang ) endif() Index: vendor/lldb/dist/source/API/SBProcess.cpp =================================================================== --- vendor/lldb/dist/source/API/SBProcess.cpp (revision 317454) +++ vendor/lldb/dist/source/API/SBProcess.cpp (revision 317455) @@ -1,1338 +1,1360 @@ //===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBProcess.h" // C Includes #include #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" // Project includes #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBUnixSignals.h" using namespace lldb; using namespace lldb_private; SBProcess::SBProcess() : m_opaque_wp() {} //---------------------------------------------------------------------- // SBProcess constructor //---------------------------------------------------------------------- SBProcess::SBProcess(const SBProcess &rhs) : m_opaque_wp(rhs.m_opaque_wp) {} SBProcess::SBProcess(const lldb::ProcessSP &process_sp) : m_opaque_wp(process_sp) {} const SBProcess &SBProcess::operator=(const SBProcess &rhs) { if (this != &rhs) m_opaque_wp = rhs.m_opaque_wp; return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SBProcess::~SBProcess() {} const char *SBProcess::GetBroadcasterClassName() { return Process::GetStaticBroadcasterClass().AsCString(); } const char *SBProcess::GetPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } const char *SBProcess::GetShortPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } lldb::ProcessSP SBProcess::GetSP() const { return m_opaque_wp.lock(); } void SBProcess::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; } void SBProcess::Clear() { m_opaque_wp.reset(); } bool SBProcess::IsValid() const { ProcessSP process_sp(m_opaque_wp.lock()); return ((bool)process_sp && process_sp->IsValid()); } bool SBProcess::RemoteLaunch(char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, " "stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, " "stop_at_entry=%i, &error (%p))...", static_cast(m_opaque_wp.lock().get()), static_cast(argv), static_cast(envp), stdin_path ? stdin_path : "NULL", stdout_path ? stdout_path : "NULL", stderr_path ? stderr_path : "NULL", working_directory ? working_directory : "NULL", launch_flags, stop_at_entry, static_cast(error.get())); ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { if (stop_at_entry) launch_flags |= eLaunchFlagStopAtEntry; ProcessLaunchInfo launch_info( FileSpec{stdin_path, false}, FileSpec{stdout_path, false}, FileSpec{stderr_path, false}, FileSpec{working_directory, false}, launch_flags); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); if (argv) launch_info.GetArguments().AppendArguments(argv); if (envp) launch_info.GetEnvironmentEntries().SetArguments(envp); error.SetError(process_sp->Launch(launch_info)); } else { error.SetErrorString("must be in eStateConnected to call RemoteLaunch"); } } else { error.SetErrorString("unable to attach pid"); } if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", static_cast(process_sp.get()), static_cast(error.get()), sstr.GetData()); } return error.Success(); } bool SBProcess::RemoteAttachToProcessWithID(lldb::pid_t pid, lldb::SBError &error) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { ProcessAttachInfo attach_info; attach_info.SetProcessID(pid); error.SetError(process_sp->Attach(attach_info)); } else { error.SetErrorString( "must be in eStateConnected to call RemoteAttachToProcessWithID"); } } else { error.SetErrorString("unable to attach pid"); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", static_cast(process_sp.get()), pid, static_cast(error.get()), sstr.GetData()); } return error.Success(); } uint32_t SBProcess::GetNumThreads() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_threads = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_threads = process_sp->GetThreadList().GetSize(can_update); } if (log) log->Printf("SBProcess(%p)::GetNumThreads () => %d", static_cast(process_sp.get()), num_threads); return num_threads; } SBThread SBProcess::GetSelectedThread() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetSelectedThread(); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", static_cast(process_sp.get()), static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->CreateOSPluginThread(tid, context); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", static_cast(process_sp.get()), tid, context, static_cast(thread_sp.get())); return sb_thread; } SBTarget SBProcess::GetTarget() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBTarget sb_target; TargetSP target_sp; ProcessSP process_sp(GetSP()); if (process_sp) { target_sp = process_sp->GetTarget().shared_from_this(); sb_target.SetSP(target_sp); } if (log) log->Printf("SBProcess(%p)::GetTarget () => SBTarget(%p)", static_cast(process_sp.get()), static_cast(target_sp.get())); return sb_target; } size_t SBProcess::PutSTDIN(const char *src, size_t src_len) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; ret_val = process_sp->PutSTDIN(src, src_len, error); } if (log) log->Printf("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), src, static_cast(src_len), static_cast(ret_val)); return ret_val; } size_t SBProcess::GetSTDOUT(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDOUT(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetSTDERR(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetSTDERR(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Error error; bytes_read = process_sp->GetAsyncProfileData(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetAsyncProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; +} + +lldb::SBTrace SBProcess::StartTrace(SBTraceOptions &options, + lldb::SBError &error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + ProcessSP process_sp(GetSP()); + error.Clear(); + SBTrace trace_instance; + trace_instance.SetSP(process_sp); + lldb::user_id_t uid = LLDB_INVALID_UID; + + if (!process_sp) { + error.SetErrorString("invalid process"); + } else { + + uid = process_sp->StartTrace(options.m_traceoptions_sp, error.ref()); + trace_instance.SetTraceUID(uid); + LLDB_LOG(log, "SBProcess::returned uid - %" PRIx64, uid); + } + return trace_instance; } void SBProcess::ReportEventState(const SBEvent &event, FILE *out) const { if (out == NULL) return; ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; int message_len = ::snprintf( message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); if (message_len > 0) ::fwrite(message, 1, message_len, out); } } void SBProcess::AppendEventStateReport(const SBEvent &event, SBCommandReturnObject &result) { ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; ::snprintf(message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); result.AppendMessage(message); } } bool SBProcess::SetSelectedThread(const SBThread &thread) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); return process_sp->GetThreadList().SetSelectedThreadByID( thread.GetThreadID()); } return false; } bool SBProcess::SetSelectedThreadByID(lldb::tid_t tid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByID(tid); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", static_cast(process_sp.get()), tid, (ret_val ? "true" : "false")); return ret_val; } bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", static_cast(process_sp.get()), index_id, (ret_val ? "true" : "false")); return ret_val; } SBThread SBProcess::GetThreadAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(thread_sp.get())); return sb_thread; } uint32_t SBProcess::GetNumQueues() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_queues = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_queues = process_sp->GetQueueList().GetSize(); } } if (log) log->Printf("SBProcess(%p)::GetNumQueues () => %d", static_cast(process_sp.get()), num_queues); return num_queues; } SBQueue SBProcess::GetQueueAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBQueue sb_queue; QueueSP queue_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); sb_queue.SetQueue(queue_sp); } } if (log) log->Printf("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(queue_sp.get())); return sb_queue; } uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (include_expression_stops) return process_sp->GetStopID(); else return process_sp->GetLastNaturalStopID(); } return 0; } SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBEvent sb_event; EventSP event_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); event_sp = process_sp->GetStopEventForStopID(stop_id); sb_event.reset(event_sp); } if (log) log->Printf("SBProcess(%p)::GetStopEventForStopID (stop_id=%" PRIu32 ") => SBEvent(%p)", static_cast(process_sp.get()), stop_id, static_cast(event_sp.get())); return sb_event; } StateType SBProcess::GetState() { StateType ret_val = eStateInvalid; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetState(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetState () => %s", static_cast(process_sp.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } int SBProcess::GetExitStatus() { int exit_status = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_status = process_sp->GetExitStatus(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", static_cast(process_sp.get()), exit_status, exit_status); return exit_status; } const char *SBProcess::GetExitDescription() { const char *exit_desc = NULL; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_desc = process_sp->GetExitDescription(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitDescription () => %s", static_cast(process_sp.get()), exit_desc); return exit_desc; } lldb::pid_t SBProcess::GetProcessID() { lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetProcessID () => %" PRIu64, static_cast(process_sp.get()), ret_val); return ret_val; } uint32_t SBProcess::GetUniqueID() { uint32_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetUniqueID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetUniqueID () => %" PRIu32, static_cast(process_sp.get()), ret_val); return ret_val; } ByteOrder SBProcess::GetByteOrder() const { ByteOrder byteOrder = eByteOrderInvalid; ProcessSP process_sp(GetSP()); if (process_sp) byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetByteOrder () => %d", static_cast(process_sp.get()), byteOrder); return byteOrder; } uint32_t SBProcess::GetAddressByteSize() const { uint32_t size = 0; ProcessSP process_sp(GetSP()); if (process_sp) size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetAddressByteSize () => %d", static_cast(process_sp.get()), size); return size; } SBError SBProcess::Continue() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBError sb_error; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::Continue ()...", static_cast(process_sp.get())); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetTarget().GetDebugger().GetAsyncExecution()) sb_error.ref() = process_sp->Resume(); else sb_error.ref() = process_sp->ResumeSynchronous(NULL); } else sb_error.SetErrorString("SBProcess is invalid"); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Continue () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Destroy() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(false)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Destroy () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Stop() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Halt()); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Stop () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Kill() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(true)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Kill () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Detach() { // FIXME: This should come from a process default. bool keep_stopped = false; return Detach(keep_stopped); } SBError SBProcess::Detach(bool keep_stopped) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Detach(keep_stopped)); } else sb_error.SetErrorString("SBProcess is invalid"); return sb_error; } SBError SBProcess::Signal(int signo) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Signal(signo)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", static_cast(process_sp.get()), signo, static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBUnixSignals SBProcess::GetUnixSignals() { if (auto process_sp = GetSP()) return SBUnixSignals{process_sp}; return {}; } void SBProcess::SendAsyncInterrupt() { ProcessSP process_sp(GetSP()); if (process_sp) { process_sp->SendAsyncInterrupt(); } } SBThread SBProcess::GetThreadByID(tid_t tid) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByID(tid, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", static_cast(process_sp.get()), tid, static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::GetThreadByIndexID(uint32_t index_id) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByIndexID(index_id, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", static_cast(process_sp.get()), index_id, static_cast(thread_sp.get())); return sb_thread; } StateType SBProcess::GetStateFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); StateType ret_val = Process::ProcessEventData::GetStateFromEvent(event.get()); if (log) log->Printf("SBProcess::GetStateFromEvent (event.sp=%p) => %s", static_cast(event.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } bool SBProcess::GetRestartedFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = Process::ProcessEventData::GetRestartedFromEvent(event.get()); if (log) log->Printf("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, static_cast(event.get()), ret_val); return ret_val; } size_t SBProcess::GetNumRestartedReasonsFromEvent(const lldb::SBEvent &event) { return Process::ProcessEventData::GetNumRestartedReasons(event.get()); } const char * SBProcess::GetRestartedReasonAtIndexFromEvent(const lldb::SBEvent &event, size_t idx) { return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); } SBProcess SBProcess::GetProcessFromEvent(const SBEvent &event) { ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event.get()); if (!process_sp) { // StructuredData events also know the process they come from. // Try that. process_sp = EventDataStructuredData::GetProcessFromEvent(event.get()); } return SBProcess(process_sp); } bool SBProcess::GetInterruptedFromEvent(const SBEvent &event) { return Process::ProcessEventData::GetInterruptedFromEvent(event.get()); } lldb::SBStructuredData SBProcess::GetStructuredDataFromEvent(const lldb::SBEvent &event) { return SBStructuredData(event.GetSP()); } bool SBProcess::EventIsProcessEvent(const SBEvent &event) { return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) && !EventIsStructuredDataEvent(event); } bool SBProcess::EventIsStructuredDataEvent(const lldb::SBEvent &event) { EventSP event_sp = event.GetSP(); EventData *event_data = event_sp ? event_sp->GetData() : nullptr; return event_data && (event_data->GetFlavor() == EventDataStructuredData::GetFlavorString()); } SBBroadcaster SBProcess::GetBroadcaster() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); SBBroadcaster broadcaster(process_sp.get(), false); if (log) log->Printf("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", static_cast(process_sp.get()), static_cast(broadcaster.get())); return broadcaster; } const char *SBProcess::GetBroadcasterClass() { return Process::GetStaticBroadcasterClass().AsCString(); } size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len, SBError &sb_error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadMemory(addr, dst, dst_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::ReadMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_read)); } return bytes_read; } size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size, lldb::SBError &sb_error) { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadCStringFromMemory(addr, (char *)buf, size, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadCStringFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return bytes_read; } uint64_t SBProcess::ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size, lldb::SBError &sb_error) { uint64_t value = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); value = process_sp->ReadUnsignedIntegerFromMemory(addr, byte_size, 0, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadUnsignedFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return value; } lldb::addr_t SBProcess::ReadPointerFromMemory(addr_t addr, lldb::SBError &sb_error) { lldb::addr_t ptr = LLDB_INVALID_ADDRESS; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ptr = process_sp->ReadPointerFromMemory(addr, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadPointerFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return ptr; } size_t SBProcess::WriteMemory(addr_t addr, const void *src, size_t src_len, SBError &sb_error) { size_t bytes_written = 0; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_written = process_sp->WriteMemory(addr, src, src_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::WriteMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_written)); } return bytes_written; } bool SBProcess::GetDescription(SBStream &description) { Stream &strm = description.ref(); ProcessSP process_sp(GetSP()); if (process_sp) { char path[PATH_MAX]; GetTarget().GetExecutable().GetPath(path, sizeof(path)); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); const char *exe_name = NULL; if (exe_module) exe_name = exe_module->GetFileSpec().GetFilename().AsCString(); strm.Printf("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s", process_sp->GetID(), lldb_private::StateAsCString(GetState()), GetNumThreads(), exe_name ? ", executable = " : "", exe_name ? exe_name : ""); } else strm.PutCString("No value"); return true; } uint32_t SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->GetWatchpointSupportInfo(num)); if (log) log->Printf("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", static_cast(process_sp.get()), num); } else { sb_error.SetErrorString("SBProcess is invalid"); } return num; } uint32_t SBProcess::LoadImage(lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { return LoadImage(SBFileSpec(), sb_remote_image_spec, sb_error); } uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec, const lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec, *sb_remote_image_spec, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::LoadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } return LLDB_INVALID_IMAGE_TOKEN; } lldb::SBError SBProcess::UnloadImage(uint32_t image_token) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); sb_error.SetError( platform_sp->UnloadImage(process_sp.get(), image_token)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::UnloadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } lldb::SBError SBProcess::SendEventData(const char *event_data) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->SendEventData(event_data)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::SendEventData() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } uint32_t SBProcess::GetNumExtendedBacktraceTypes() { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); return runtime->GetExtendedBacktraceTypes().size(); } return 0; } const char *SBProcess::GetExtendedBacktraceTypeAtIndex(uint32_t idx) { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); const std::vector &names = runtime->GetExtendedBacktraceTypes(); if (idx < names.size()) { return names[idx].AsCString(); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExtendedBacktraceTypeAtIndex() => " "error: requested extended backtrace name out of bounds", static_cast(process_sp.get())); } } return NULL; } SBThreadCollection SBProcess::GetHistoryThreads(addr_t addr) { ProcessSP process_sp(GetSP()); SBThreadCollection threads; if (process_sp) { threads = SBThreadCollection(process_sp->GetHistoryThreads(addr)); } return threads; } bool SBProcess::IsInstrumentationRuntimePresent( InstrumentationRuntimeType type) { ProcessSP process_sp(GetSP()); if (!process_sp) return false; InstrumentationRuntimeSP runtime_sp = process_sp->GetInstrumentationRuntime(type); if (!runtime_sp.get()) return false; return runtime_sp->IsActive(); } lldb::SBError SBProcess::SaveCore(const char *file_name) { lldb::SBError error; ProcessSP process_sp(GetSP()); if (!process_sp) { error.SetErrorString("SBProcess is invalid"); return error; } std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() != eStateStopped) { error.SetErrorString("the process is not stopped"); return error; } FileSpec core_file(file_name, false); error.ref() = PluginManager::SaveCore(process_sp, core_file); return error; } lldb::SBError SBProcess::GetMemoryRegionInfo(lldb::addr_t load_addr, SBMemoryRegionInfo &sb_region_info) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); MemoryRegionInfoSP region_info_sp = std::make_shared(); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.ref() = process_sp->GetMemoryRegionInfo(load_addr, *region_info_sp); if (sb_error.Success()) { sb_region_info.ref() = *region_info_sp; } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_error; } lldb::SBMemoryRegionInfoList SBProcess::GetMemoryRegions() { lldb::SBError sb_error; lldb::SBMemoryRegionInfoList sb_region_list; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); std::vector region_list; sb_error.ref() = process_sp->GetMemoryRegions(region_list); if (sb_error.Success()) { std::vector::iterator end = region_list.end(); for (std::vector::iterator it = region_list.begin(); it != end; it++) { SBMemoryRegionInfo sb_region_info(it->get()); sb_region_list.Append(sb_region_info); } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_region_list; } Index: vendor/lldb/dist/source/API/SBStructuredData.cpp =================================================================== --- vendor/lldb/dist/source/API/SBStructuredData.cpp (revision 317454) +++ vendor/lldb/dist/source/API/SBStructuredData.cpp (revision 317455) @@ -1,118 +1,69 @@ //===-- SBStructuredData.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Event.h" #include "lldb/Core/StructuredData.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; #pragma mark-- -#pragma mark StructuredDataImpl - -class StructuredDataImpl { -public: - StructuredDataImpl() : m_plugin_wp(), m_data_sp() {} - - StructuredDataImpl(const StructuredDataImpl &rhs) = default; - - StructuredDataImpl(const EventSP &event_sp) - : m_plugin_wp( - EventDataStructuredData::GetPluginFromEvent(event_sp.get())), - m_data_sp(EventDataStructuredData::GetObjectFromEvent(event_sp.get())) { - } - - ~StructuredDataImpl() = default; - - StructuredDataImpl &operator=(const StructuredDataImpl &rhs) = default; - - bool IsValid() const { return m_data_sp.get() != nullptr; } - - void Clear() { - m_plugin_wp.reset(); - m_data_sp.reset(); - } - - SBError GetAsJSON(lldb_private::Stream &stream) const { - SBError sb_error; - - if (!m_data_sp) { - sb_error.SetErrorString("No structured data."); - return sb_error; - } - - m_data_sp->Dump(stream); - return sb_error; - } - - Error GetDescription(lldb_private::Stream &stream) const { - Error error; - - if (!m_data_sp) { - error.SetErrorString("Cannot pretty print structured data: " - "no data to print."); - return error; - } - - // Grab the plugin. - auto plugin_sp = StructuredDataPluginSP(m_plugin_wp); - if (!plugin_sp) { - error.SetErrorString("Cannot pretty print structured data: " - "plugin doesn't exist."); - return error; - } - - // Get the data's description. - return plugin_sp->GetDescription(m_data_sp, stream); - } - -private: - StructuredDataPluginWP m_plugin_wp; - StructuredData::ObjectSP m_data_sp; -}; - -#pragma mark-- #pragma mark SBStructuredData SBStructuredData::SBStructuredData() : m_impl_up(new StructuredDataImpl()) {} SBStructuredData::SBStructuredData(const lldb::SBStructuredData &rhs) : m_impl_up(new StructuredDataImpl(*rhs.m_impl_up.get())) {} SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp) : m_impl_up(new StructuredDataImpl(event_sp)) {} SBStructuredData::~SBStructuredData() {} SBStructuredData &SBStructuredData:: operator=(const lldb::SBStructuredData &rhs) { *m_impl_up = *rhs.m_impl_up; return *this; } +lldb::SBError SBStructuredData::SetFromJSON(lldb::SBStream &stream) { + lldb::SBError error; + std::string json_str(stream.GetData()); + + StructuredData::ObjectSP json_obj = StructuredData::ParseJSON(json_str); + m_impl_up->SetObjectSP(json_obj); + + if (!json_obj || json_obj->GetType() != StructuredData::Type::eTypeDictionary) + error.SetErrorString("Invalid Syntax"); + return error; +} + bool SBStructuredData::IsValid() const { return m_impl_up->IsValid(); } void SBStructuredData::Clear() { m_impl_up->Clear(); } SBError SBStructuredData::GetAsJSON(lldb::SBStream &stream) const { - return m_impl_up->GetAsJSON(stream.ref()); + SBError error; + error.SetError(m_impl_up->GetAsJSON(stream.ref())); + return error; } lldb::SBError SBStructuredData::GetDescription(lldb::SBStream &stream) const { Error error = m_impl_up->GetDescription(stream.ref()); SBError sb_error; sb_error.SetError(error); return sb_error; } Index: vendor/lldb/dist/source/API/SBTrace.cpp =================================================================== --- vendor/lldb/dist/source/API/SBTrace.cpp (nonexistent) +++ vendor/lldb/dist/source/API/SBTrace.cpp (revision 317455) @@ -0,0 +1,109 @@ +//===-- SBTrace.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Log.h" +#include "lldb/Target/Process.h" + +#include "lldb/API/SBTrace.h" +#include "lldb/API/SBTraceOptions.h" + +using namespace lldb; +using namespace lldb_private; + +class TraceImpl { +public: + lldb::user_id_t uid; +}; + +lldb::ProcessSP SBTrace::GetSP() const { return m_opaque_wp.lock(); } + +size_t SBTrace::GetTraceData(SBError &error, void *buf, size_t size, + size_t offset, lldb::tid_t thread_id) { + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + error.Clear(); + + if (!process_sp) { + error.SetErrorString("invalid process"); + } else { + bytes_read = process_sp->GetData(GetTraceUID(), thread_id, error.ref(), buf, + size, offset); + LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + } + return bytes_read; +} + +size_t SBTrace::GetMetaData(SBError &error, void *buf, size_t size, + size_t offset, lldb::tid_t thread_id) { + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + error.Clear(); + + if (!process_sp) { + error.SetErrorString("invalid process"); + } else { + + bytes_read = process_sp->GetMetaData(GetTraceUID(), thread_id, error.ref(), + buf, size, offset); + LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + } + return bytes_read; +} + +void SBTrace::StopTrace(SBError &error, lldb::tid_t thread_id) { + ProcessSP process_sp(GetSP()); + error.Clear(); + + if (!process_sp) { + error.SetErrorString("invalid process"); + return; + } + process_sp->StopTrace(GetTraceUID(), thread_id, error.ref()); +} + +void SBTrace::GetTraceConfig(SBTraceOptions &options, SBError &error) { + ProcessSP process_sp(GetSP()); + error.Clear(); + + if (!process_sp) { + error.SetErrorString("invalid process"); + } else { + process_sp->GetTraceConfig(GetTraceUID(), error.ref(), + options.m_traceoptions_sp); + } +} + +lldb::user_id_t SBTrace::GetTraceUID() { + if (m_trace_impl_sp) + return m_trace_impl_sp->uid; + return LLDB_INVALID_UID; +} + +void SBTrace::SetTraceUID(lldb::user_id_t uid) { + if (m_trace_impl_sp) + m_trace_impl_sp->uid = uid; +} + +SBTrace::SBTrace() { + m_trace_impl_sp.reset(new TraceImpl); + if (m_trace_impl_sp) + m_trace_impl_sp->uid = LLDB_INVALID_UID; +} + +void SBTrace::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; } + +bool SBTrace::IsValid() { + if (!m_trace_impl_sp) + return false; + if (!GetSP()) + return false; + return true; +} Property changes on: vendor/lldb/dist/source/API/SBTrace.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/source/API/SBTraceOptions.cpp =================================================================== --- vendor/lldb/dist/source/API/SBTraceOptions.cpp (nonexistent) +++ vendor/lldb/dist/source/API/SBTraceOptions.cpp (revision 317455) @@ -0,0 +1,94 @@ +//===-- SBTraceOptions.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBTraceOptions.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/Utility/Log.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Core/TraceOptions.h" + +using namespace lldb; +using namespace lldb_private; + +SBTraceOptions::SBTraceOptions() { + m_traceoptions_sp.reset(new TraceOptions()); +} + +lldb::TraceType SBTraceOptions::getType() const { + if (m_traceoptions_sp) + return m_traceoptions_sp->getType(); + return lldb::TraceType::eTraceTypeNone; +} + +uint64_t SBTraceOptions::getTraceBufferSize() const { + if (m_traceoptions_sp) + return m_traceoptions_sp->getTraceBufferSize(); + return 0; +} + +lldb::SBStructuredData SBTraceOptions::getTraceParams(lldb::SBError &error) { + error.Clear(); + const lldb_private::StructuredData::DictionarySP dict_obj = + m_traceoptions_sp->getTraceParams(); + lldb::SBStructuredData structData; + if (dict_obj && structData.m_impl_up) + structData.m_impl_up->SetObjectSP(dict_obj->shared_from_this()); + else + error.SetErrorString("Empty trace params"); + return structData; +} + +uint64_t SBTraceOptions::getMetaDataBufferSize() const { + if (m_traceoptions_sp) + return m_traceoptions_sp->getTraceBufferSize(); + return 0; +} + +void SBTraceOptions::setTraceParams(lldb::SBStructuredData ¶ms) { + if (m_traceoptions_sp && params.m_impl_up) { + StructuredData::ObjectSP obj_sp = params.m_impl_up->GetObjectSP(); + if (obj_sp && obj_sp->GetAsDictionary() != nullptr) + m_traceoptions_sp->setTraceParams( + std::static_pointer_cast(obj_sp)); + } + return; +} + +void SBTraceOptions::setType(lldb::TraceType type) { + if (m_traceoptions_sp) + m_traceoptions_sp->setType(type); +} + +void SBTraceOptions::setTraceBufferSize(uint64_t size) { + if (m_traceoptions_sp) + m_traceoptions_sp->setTraceBufferSize(size); +} + +void SBTraceOptions::setMetaDataBufferSize(uint64_t size) { + if (m_traceoptions_sp) + m_traceoptions_sp->setMetaDataBufferSize(size); +} + +bool SBTraceOptions::IsValid() { + if (m_traceoptions_sp) + return true; + return false; +} + +void SBTraceOptions::setThreadID(lldb::tid_t thread_id) { + if (m_traceoptions_sp) + m_traceoptions_sp->setThreadID(thread_id); +} + +lldb::tid_t SBTraceOptions::getThreadID() { + if (m_traceoptions_sp) + return m_traceoptions_sp->getThreadID(); + return LLDB_INVALID_THREAD_ID; +} Property changes on: vendor/lldb/dist/source/API/SBTraceOptions.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/source/Core/ValueObject.cpp =================================================================== --- vendor/lldb/dist/source/Core/ValueObject.cpp (revision 317454) +++ vendor/lldb/dist/source/Core/ValueObject.cpp (revision 317455) @@ -1,3473 +1,3447 @@ //===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/ValueObject.h" #include "lldb/Core/Address.h" // for Address #include "lldb/Core/Module.h" #include "lldb/Core/Scalar.h" // for Scalar #include "lldb/Core/ValueObjectCast.h" #include "lldb/Core/ValueObjectChild.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectDynamicValue.h" #include "lldb/Core/ValueObjectMemory.h" #include "lldb/Core/ValueObjectSyntheticFilter.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormatManager.h" // for FormatManager #include "lldb/DataFormatters/StringPrinter.h" #include "lldb/DataFormatters/TypeFormat.h" // for TypeFormatImpl_F... #include "lldb/DataFormatters/TypeSummary.h" // for TypeSummaryOptions #include "lldb/DataFormatters/TypeValidator.h" // for TypeValidatorImp... #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Expression/ExpressionVariable.h" // for ExpressionVariable #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Declaration.h" // for Declaration #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Symbol/Type.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Language.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" // for StackFrame #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadList.h" // for ThreadList #include "lldb/Utility/DataBuffer.h" // for DataBuffer #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Flags.h" // for Flags #include "lldb/Utility/Log.h" #include "lldb/Utility/Logging.h" // for GetLogIfAllCateg... #include "lldb/Utility/SharingPtr.h" // for SharingPtr #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StreamString.h" #include "lldb/lldb-private-types.h" // for RegisterInfo #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include // for min #include // for uint32_t, uint64_t #include // for size_t, NULL #include // for shared_ptr, oper... #include // for tie, tuple #include // for assert #include // for PRIu64, PRIx64 #include // for snprintf #include // for memcpy, memcmp namespace lldb_private { class ExecutionContextScope; } namespace lldb_private { class SymbolContextScope; } using namespace lldb; using namespace lldb_private; using namespace lldb_utility; static user_id_t g_value_obj_uid = 0; //---------------------------------------------------------------------- // ValueObject constructor //---------------------------------------------------------------------- ValueObject::ValueObject(ValueObject &parent) : UserID(++g_value_obj_uid), // Unique identifier for every value object m_parent(&parent), m_root(NULL), m_update_point(parent.GetUpdatePoint()), m_name(), m_data(), m_value(), m_error(), m_value_str(), m_old_value_str(), m_location_str(), m_summary_str(), m_object_desc_str(), m_validation_result(), m_manager(parent.GetManager()), m_children(), m_synthetic_children(), m_dynamic_value(NULL), m_synthetic_value(NULL), m_deref_valobj(NULL), m_format(eFormatDefault), m_last_format(eFormatDefault), m_last_format_mgr_revision(0), m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(), m_type_validator_sp(), m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), m_value_checksum(), m_preferred_display_language(lldb::eLanguageTypeUnknown), m_language_flags(0), m_value_is_valid(false), m_value_did_change(false), m_children_count_valid(false), m_old_value_valid(false), m_is_deref_of_parent(false), m_is_array_item_for_pointer(false), m_is_bitfield_for_scalar(false), m_is_child_at_offset(false), m_is_getting_summary(false), m_did_calculate_complete_objc_class_type(false), m_is_synthetic_children_generated( parent.m_is_synthetic_children_generated) { m_manager->ManageObject(this); } //---------------------------------------------------------------------- // ValueObject constructor //---------------------------------------------------------------------- ValueObject::ValueObject(ExecutionContextScope *exe_scope, AddressType child_ptr_or_ref_addr_type) : UserID(++g_value_obj_uid), // Unique identifier for every value object m_parent(NULL), m_root(NULL), m_update_point(exe_scope), m_name(), m_data(), m_value(), m_error(), m_value_str(), m_old_value_str(), m_location_str(), m_summary_str(), m_object_desc_str(), m_validation_result(), m_manager(), m_children(), m_synthetic_children(), m_dynamic_value(NULL), m_synthetic_value(NULL), m_deref_valobj(NULL), m_format(eFormatDefault), m_last_format(eFormatDefault), m_last_format_mgr_revision(0), m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(), m_type_validator_sp(), m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), m_value_checksum(), m_preferred_display_language(lldb::eLanguageTypeUnknown), m_language_flags(0), m_value_is_valid(false), m_value_did_change(false), m_children_count_valid(false), m_old_value_valid(false), m_is_deref_of_parent(false), m_is_array_item_for_pointer(false), m_is_bitfield_for_scalar(false), m_is_child_at_offset(false), m_is_getting_summary(false), m_did_calculate_complete_objc_class_type(false), m_is_synthetic_children_generated(false) { m_manager = new ValueObjectManager(); m_manager->ManageObject(this); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ValueObject::~ValueObject() {} bool ValueObject::UpdateValueIfNeeded(bool update_format) { bool did_change_formats = false; if (update_format) did_change_formats = UpdateFormatsIfNeeded(); // If this is a constant value, then our success is predicated on whether // we have an error or not if (GetIsConstant()) { // if you are constant, things might still have changed behind your back // (e.g. you are a frozen object and things have changed deeper than you // cared to freeze-dry yourself) // in this case, your value has not changed, but "computed" entries might // have, so you might now have // a different summary, or a different object description. clear these so we // will recompute them if (update_format && !did_change_formats) ClearUserVisibleData(eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsDescription); return m_error.Success(); } bool first_update = IsChecksumEmpty(); if (NeedsUpdating()) { m_update_point.SetUpdated(); // Save the old value using swap to avoid a string copy which // also will clear our m_value_str if (m_value_str.empty()) { m_old_value_valid = false; } else { m_old_value_valid = true; m_old_value_str.swap(m_value_str); ClearUserVisibleData(eClearUserVisibleDataItemsValue); } ClearUserVisibleData(); if (IsInScope()) { const bool value_was_valid = GetValueIsValid(); SetValueDidChange(false); m_error.Clear(); // Call the pure virtual function to update the value bool need_compare_checksums = false; llvm::SmallVector old_checksum; if (!first_update && CanProvideValue()) { need_compare_checksums = true; old_checksum.resize(m_value_checksum.size()); std::copy(m_value_checksum.begin(), m_value_checksum.end(), old_checksum.begin()); } bool success = UpdateValue(); SetValueIsValid(success); if (success) { const uint64_t max_checksum_size = 128; m_data.Checksum(m_value_checksum, max_checksum_size); } else { need_compare_checksums = false; m_value_checksum.clear(); } assert(!need_compare_checksums || (!old_checksum.empty() && !m_value_checksum.empty())); if (first_update) SetValueDidChange(false); else if (!m_value_did_change && success == false) { // The value wasn't gotten successfully, so we mark this // as changed if the value used to be valid and now isn't SetValueDidChange(value_was_valid); } else if (need_compare_checksums) { SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0], m_value_checksum.size())); } } else { m_error.SetErrorString("out of scope"); } } return m_error.Success(); } bool ValueObject::UpdateFormatsIfNeeded() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); if (log) log->Printf("[%s %p] checking for FormatManager revisions. ValueObject " "rev: %d - Global rev: %d", GetName().GetCString(), static_cast(this), m_last_format_mgr_revision, DataVisualization::GetCurrentRevision()); bool any_change = false; if ((m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) { m_last_format_mgr_revision = DataVisualization::GetCurrentRevision(); any_change = true; SetValueFormat(DataVisualization::GetFormat(*this, eNoDynamicValues)); SetSummaryFormat( DataVisualization::GetSummaryFormat(*this, GetDynamicValueType())); #ifndef LLDB_DISABLE_PYTHON SetSyntheticChildren( DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType())); #endif SetValidator(DataVisualization::GetValidator(*this, GetDynamicValueType())); } return any_change; } void ValueObject::SetNeedsUpdate() { m_update_point.SetNeedsUpdate(); // We have to clear the value string here so ConstResult children will notice // if their values are // changed by hand (i.e. with SetValueAsCString). ClearUserVisibleData(eClearUserVisibleDataItemsValue); } void ValueObject::ClearDynamicTypeInformation() { m_children_count_valid = false; m_did_calculate_complete_objc_class_type = false; m_last_format_mgr_revision = 0; m_override_type = CompilerType(); SetValueFormat(lldb::TypeFormatImplSP()); SetSummaryFormat(lldb::TypeSummaryImplSP()); SetSyntheticChildren(lldb::SyntheticChildrenSP()); } CompilerType ValueObject::MaybeCalculateCompleteType() { CompilerType compiler_type(GetCompilerTypeImpl()); if (m_did_calculate_complete_objc_class_type) { if (m_override_type.IsValid()) return m_override_type; else return compiler_type; } CompilerType class_type; bool is_pointer_type = false; if (ClangASTContext::IsObjCObjectPointerType(compiler_type, &class_type)) { is_pointer_type = true; } else if (ClangASTContext::IsObjCObjectOrInterfaceType(compiler_type)) { class_type = compiler_type; } else { return compiler_type; } m_did_calculate_complete_objc_class_type = true; if (class_type) { ConstString class_name(class_type.GetConstTypeName()); if (class_name) { ProcessSP process_sp( GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); if (process_sp) { ObjCLanguageRuntime *objc_language_runtime( process_sp->GetObjCLanguageRuntime()); if (objc_language_runtime) { TypeSP complete_objc_class_type_sp = objc_language_runtime->LookupInCompleteClassCache(class_name); if (complete_objc_class_type_sp) { CompilerType complete_class( complete_objc_class_type_sp->GetFullCompilerType()); if (complete_class.GetCompleteType()) { if (is_pointer_type) { m_override_type = complete_class.GetPointerType(); } else { m_override_type = complete_class; } if (m_override_type.IsValid()) return m_override_type; } } } } } } return compiler_type; } CompilerType ValueObject::GetCompilerType() { return MaybeCalculateCompleteType(); } TypeImpl ValueObject::GetTypeImpl() { return TypeImpl(GetCompilerType()); } DataExtractor &ValueObject::GetDataExtractor() { UpdateValueIfNeeded(false); return m_data; } const Error &ValueObject::GetError() { UpdateValueIfNeeded(false); return m_error; } const ConstString &ValueObject::GetName() const { return m_name; } const char *ValueObject::GetLocationAsCString() { return GetLocationAsCStringImpl(m_value, m_data); } const char *ValueObject::GetLocationAsCStringImpl(const Value &value, const DataExtractor &data) { if (UpdateValueIfNeeded(false)) { if (m_location_str.empty()) { StreamString sstr; Value::ValueType value_type = value.GetValueType(); switch (value_type) { case Value::eValueTypeScalar: case Value::eValueTypeVector: if (value.GetContextType() == Value::eContextTypeRegisterInfo) { RegisterInfo *reg_info = value.GetRegisterInfo(); if (reg_info) { if (reg_info->name) m_location_str = reg_info->name; else if (reg_info->alt_name) m_location_str = reg_info->alt_name; if (m_location_str.empty()) m_location_str = (reg_info->encoding == lldb::eEncodingVector) ? "vector" : "scalar"; } } if (m_location_str.empty()) m_location_str = (value_type == Value::eValueTypeVector) ? "vector" : "scalar"; break; case Value::eValueTypeLoadAddress: case Value::eValueTypeFileAddress: case Value::eValueTypeHostAddress: { uint32_t addr_nibble_size = data.GetAddressByteSize() * 2; sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size, value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS)); m_location_str = sstr.GetString(); } break; } } } return m_location_str.c_str(); } Value &ValueObject::GetValue() { return m_value; } const Value &ValueObject::GetValue() const { return m_value; } bool ValueObject::ResolveValue(Scalar &scalar) { if (UpdateValueIfNeeded( false)) // make sure that you are up to date before returning anything { ExecutionContext exe_ctx(GetExecutionContextRef()); Value tmp_value(m_value); scalar = tmp_value.ResolveValue(&exe_ctx); if (scalar.IsValid()) { const uint32_t bitfield_bit_size = GetBitfieldBitSize(); if (bitfield_bit_size) return scalar.ExtractBitfield(bitfield_bit_size, GetBitfieldBitOffset()); return true; } } return false; } bool ValueObject::IsLogicalTrue(Error &error) { if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { LazyBool is_logical_true = language->IsLogicalTrue(*this, error); switch (is_logical_true) { case eLazyBoolYes: case eLazyBoolNo: return (is_logical_true == true); case eLazyBoolCalculate: break; } } Scalar scalar_value; if (!ResolveValue(scalar_value)) { error.SetErrorString("failed to get a scalar result"); return false; } bool ret; if (scalar_value.ULongLong(1) == 0) ret = false; else ret = true; error.Clear(); return ret; } bool ValueObject::GetValueIsValid() const { return m_value_is_valid; } void ValueObject::SetValueIsValid(bool b) { m_value_is_valid = b; } bool ValueObject::GetValueDidChange() { return m_value_did_change; } void ValueObject::SetValueDidChange(bool value_changed) { m_value_did_change = value_changed; } ValueObjectSP ValueObject::GetChildAtIndex(size_t idx, bool can_create) { ValueObjectSP child_sp; // We may need to update our value if we are dynamic if (IsPossibleDynamicType()) UpdateValueIfNeeded(false); if (idx < GetNumChildren()) { // Check if we have already made the child value object? if (can_create && !m_children.HasChildAtIndex(idx)) { // No we haven't created the child at this index, so lets have our // subclass do it and cache the result for quick future access. m_children.SetChildAtIndex(idx, CreateChildAtIndex(idx, false, 0)); } ValueObject *child = m_children.GetChildAtIndex(idx); if (child != NULL) return child->GetSP(); } return child_sp; } -ValueObjectSP -ValueObject::GetChildAtIndexPath(const std::initializer_list &idxs, - size_t *index_of_error) { - return GetChildAtIndexPath(std::vector(idxs), index_of_error); -} - -ValueObjectSP ValueObject::GetChildAtIndexPath( - const std::initializer_list> &idxs, - size_t *index_of_error) { - return GetChildAtIndexPath(std::vector>(idxs), - index_of_error); -} - lldb::ValueObjectSP -ValueObject::GetChildAtIndexPath(const std::vector &idxs, +ValueObject::GetChildAtIndexPath(llvm::ArrayRef idxs, size_t *index_of_error) { if (idxs.size() == 0) return GetSP(); ValueObjectSP root(GetSP()); for (size_t idx : idxs) { root = root->GetChildAtIndex(idx, true); if (!root) { if (index_of_error) *index_of_error = idx; return root; } } return root; } lldb::ValueObjectSP ValueObject::GetChildAtIndexPath( - const std::vector> &idxs, size_t *index_of_error) { + llvm::ArrayRef> idxs, size_t *index_of_error) { if (idxs.size() == 0) return GetSP(); ValueObjectSP root(GetSP()); for (std::pair idx : idxs) { root = root->GetChildAtIndex(idx.first, idx.second); if (!root) { if (index_of_error) *index_of_error = idx.first; return root; } } return root; } lldb::ValueObjectSP -ValueObject::GetChildAtNamePath(const std::initializer_list &names, +ValueObject::GetChildAtNamePath(llvm::ArrayRef names, ConstString *name_of_error) { - return GetChildAtNamePath(std::vector(names), name_of_error); -} - -lldb::ValueObjectSP ValueObject::GetChildAtNamePath( - const std::initializer_list> &names, - ConstString *name_of_error) { - return GetChildAtNamePath(std::vector>(names), - name_of_error); -} - -lldb::ValueObjectSP -ValueObject::GetChildAtNamePath(const std::vector &names, - ConstString *name_of_error) { if (names.size() == 0) return GetSP(); ValueObjectSP root(GetSP()); for (ConstString name : names) { root = root->GetChildMemberWithName(name, true); if (!root) { if (name_of_error) *name_of_error = name; return root; } } return root; } lldb::ValueObjectSP ValueObject::GetChildAtNamePath( - const std::vector> &names, + llvm::ArrayRef> names, ConstString *name_of_error) { if (names.size() == 0) return GetSP(); ValueObjectSP root(GetSP()); for (std::pair name : names) { root = root->GetChildMemberWithName(name.first, name.second); if (!root) { if (name_of_error) *name_of_error = name.first; return root; } } return root; } size_t ValueObject::GetIndexOfChildWithName(const ConstString &name) { bool omit_empty_base_classes = true; return GetCompilerType().GetIndexOfChildWithName(name.GetCString(), omit_empty_base_classes); } ValueObjectSP ValueObject::GetChildMemberWithName(const ConstString &name, bool can_create) { // when getting a child by name, it could be buried inside some base // classes (which really aren't part of the expression path), so we // need a vector of indexes that can get us down to the correct child ValueObjectSP child_sp; // We may need to update our value if we are dynamic if (IsPossibleDynamicType()) UpdateValueIfNeeded(false); std::vector child_indexes; bool omit_empty_base_classes = true; const size_t num_child_indexes = GetCompilerType().GetIndexOfChildMemberWithName( name.GetCString(), omit_empty_base_classes, child_indexes); if (num_child_indexes > 0) { std::vector::const_iterator pos = child_indexes.begin(); std::vector::const_iterator end = child_indexes.end(); child_sp = GetChildAtIndex(*pos, can_create); for (++pos; pos != end; ++pos) { if (child_sp) { ValueObjectSP new_child_sp(child_sp->GetChildAtIndex(*pos, can_create)); child_sp = new_child_sp; } else { child_sp.reset(); } } } return child_sp; } size_t ValueObject::GetNumChildren(uint32_t max) { UpdateValueIfNeeded(); if (max < UINT32_MAX) { if (m_children_count_valid) { size_t children_count = m_children.GetChildrenCount(); return children_count <= max ? children_count : max; } else return CalculateNumChildren(max); } if (!m_children_count_valid) { SetNumChildren(CalculateNumChildren()); } return m_children.GetChildrenCount(); } bool ValueObject::MightHaveChildren() { bool has_children = false; const uint32_t type_info = GetTypeInfo(); if (type_info) { if (type_info & (eTypeHasChildren | eTypeIsPointer | eTypeIsReference)) has_children = true; } else { has_children = GetNumChildren() > 0; } return has_children; } // Should only be called by ValueObject::GetNumChildren() void ValueObject::SetNumChildren(size_t num_children) { m_children_count_valid = true; m_children.SetChildrenCount(num_children); } void ValueObject::SetName(const ConstString &name) { m_name = name; } ValueObject *ValueObject::CreateChildAtIndex(size_t idx, bool synthetic_array_member, int32_t synthetic_index) { ValueObject *valobj = NULL; bool omit_empty_base_classes = true; bool ignore_array_bounds = synthetic_array_member; std::string child_name_str; uint32_t child_byte_size = 0; int32_t child_byte_offset = 0; uint32_t child_bitfield_bit_size = 0; uint32_t child_bitfield_bit_offset = 0; bool child_is_base_class = false; bool child_is_deref_of_parent = false; uint64_t language_flags = 0; const bool transparent_pointers = synthetic_array_member == false; CompilerType child_compiler_type; ExecutionContext exe_ctx(GetExecutionContextRef()); child_compiler_type = GetCompilerType().GetChildCompilerTypeAtIndex( &exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, this, language_flags); if (child_compiler_type) { if (synthetic_index) child_byte_offset += child_byte_size * synthetic_index; ConstString child_name; if (!child_name_str.empty()) child_name.SetCString(child_name_str.c_str()); valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, language_flags); // if (valobj) // valobj->SetAddressTypeOfChildren(eAddressTypeInvalid); } return valobj; } bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr, std::string &destination, lldb::LanguageType lang) { return GetSummaryAsCString(summary_ptr, destination, TypeSummaryOptions().SetLanguage(lang)); } bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr, std::string &destination, const TypeSummaryOptions &options) { destination.clear(); // ideally we would like to bail out if passing NULL, but if we do so // we end up not providing the summary for function pointers anymore if (/*summary_ptr == NULL ||*/ m_is_getting_summary) return false; m_is_getting_summary = true; TypeSummaryOptions actual_options(options); if (actual_options.GetLanguage() == lldb::eLanguageTypeUnknown) actual_options.SetLanguage(GetPreferredDisplayLanguage()); // this is a hot path in code and we prefer to avoid setting this string all // too often also clearing out other // information that we might care to see in a crash log. might be useful in // very specific situations though. /*Host::SetCrashDescriptionWithFormat("Trying to fetch a summary for %s %s. Summary provider's description is %s", GetTypeName().GetCString(), GetName().GetCString(), summary_ptr->GetDescription().c_str());*/ if (UpdateValueIfNeeded(false) && summary_ptr) { if (HasSyntheticValue()) m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on // the synthetic children being // up-to-date (e.g. ${svar%#}) summary_ptr->FormatObject(this, destination, actual_options); } m_is_getting_summary = false; return !destination.empty(); } const char *ValueObject::GetSummaryAsCString(lldb::LanguageType lang) { if (UpdateValueIfNeeded(true) && m_summary_str.empty()) { TypeSummaryOptions summary_options; summary_options.SetLanguage(lang); GetSummaryAsCString(GetSummaryFormat().get(), m_summary_str, summary_options); } if (m_summary_str.empty()) return NULL; return m_summary_str.c_str(); } bool ValueObject::GetSummaryAsCString(std::string &destination, const TypeSummaryOptions &options) { return GetSummaryAsCString(GetSummaryFormat().get(), destination, options); } bool ValueObject::IsCStringContainer(bool check_pointer) { CompilerType pointee_or_element_compiler_type; const Flags type_flags(GetTypeInfo(&pointee_or_element_compiler_type)); bool is_char_arr_ptr(type_flags.AnySet(eTypeIsArray | eTypeIsPointer) && pointee_or_element_compiler_type.IsCharType()); if (!is_char_arr_ptr) return false; if (!check_pointer) return true; if (type_flags.Test(eTypeIsArray)) return true; addr_t cstr_address = LLDB_INVALID_ADDRESS; AddressType cstr_address_type = eAddressTypeInvalid; cstr_address = GetAddressOf(true, &cstr_address_type); return (cstr_address != LLDB_INVALID_ADDRESS); } size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, uint32_t item_count) { CompilerType pointee_or_element_compiler_type; const uint32_t type_info = GetTypeInfo(&pointee_or_element_compiler_type); const bool is_pointer_type = type_info & eTypeIsPointer; const bool is_array_type = type_info & eTypeIsArray; if (!(is_pointer_type || is_array_type)) return 0; if (item_count == 0) return 0; ExecutionContext exe_ctx(GetExecutionContextRef()); const uint64_t item_type_size = pointee_or_element_compiler_type.GetByteSize( exe_ctx.GetBestExecutionContextScope()); const uint64_t bytes = item_count * item_type_size; const uint64_t offset = item_idx * item_type_size; if (item_idx == 0 && item_count == 1) // simply a deref { if (is_pointer_type) { Error error; ValueObjectSP pointee_sp = Dereference(error); if (error.Fail() || pointee_sp.get() == NULL) return 0; return pointee_sp->GetData(data, error); } else { ValueObjectSP child_sp = GetChildAtIndex(0, true); if (child_sp.get() == NULL) return 0; Error error; return child_sp->GetData(data, error); } return true; } else /* (items > 1) */ { Error error; lldb_private::DataBufferHeap *heap_buf_ptr = NULL; lldb::DataBufferSP data_sp(heap_buf_ptr = new lldb_private::DataBufferHeap()); AddressType addr_type; lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type) : GetAddressOf(true, &addr_type); switch (addr_type) { case eAddressTypeFile: { ModuleSP module_sp(GetModule()); if (module_sp) { addr = addr + offset; Address so_addr; module_sp->ResolveFileAddress(addr, so_addr); ExecutionContext exe_ctx(GetExecutionContextRef()); Target *target = exe_ctx.GetTargetPtr(); if (target) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = target->ReadMemory( so_addr, false, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success()) { data.SetData(data_sp); return bytes_read; } } } } break; case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; } } } break; case eAddressTypeHost: { const uint64_t max_bytes = GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope()); if (max_bytes > offset) { size_t bytes_read = std::min(max_bytes - offset, bytes); addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); if (addr == 0 || addr == LLDB_INVALID_ADDRESS) break; heap_buf_ptr->CopyData((uint8_t *)(addr + offset), bytes_read); data.SetData(data_sp); return bytes_read; } } break; case eAddressTypeInvalid: break; } } return 0; } uint64_t ValueObject::GetData(DataExtractor &data, Error &error) { UpdateValueIfNeeded(false); ExecutionContext exe_ctx(GetExecutionContextRef()); error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); if (error.Fail()) { if (m_data.GetByteSize()) { data = m_data; error.Clear(); return data.GetByteSize(); } else { return 0; } } data.SetAddressByteSize(m_data.GetAddressByteSize()); data.SetByteOrder(m_data.GetByteOrder()); return data.GetByteSize(); } bool ValueObject::SetData(DataExtractor &data, Error &error) { error.Clear(); // Make sure our value is up to date first so that our location and location // type is valid. if (!UpdateValueIfNeeded(false)) { error.SetErrorString("unable to read value"); return false; } uint64_t count = 0; const Encoding encoding = GetCompilerType().GetEncoding(count); const size_t byte_size = GetByteSize(); Value::ValueType value_type = m_value.GetValueType(); switch (value_type) { case Value::eValueTypeScalar: { Error set_error = m_value.GetScalar().SetValueFromData(data, encoding, byte_size); if (!set_error.Success()) { error.SetErrorStringWithFormat("unable to set scalar value: %s", set_error.AsCString()); return false; } } break; case Value::eValueTypeLoadAddress: { // If it is a load address, then the scalar value is the storage location // of the data, and we have to shove this value down to that load location. ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); size_t bytes_written = process->WriteMemory( target_addr, data.GetDataStart(), byte_size, error); if (!error.Success()) return false; if (bytes_written != byte_size) { error.SetErrorString("unable to write value to memory"); return false; } } } break; case Value::eValueTypeHostAddress: { // If it is a host address, then we stuff the scalar as a DataBuffer into // the Value's data. DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0)); m_data.SetData(buffer_sp, 0); data.CopyByteOrderedData(0, byte_size, const_cast(m_data.GetDataStart()), byte_size, m_data.GetByteOrder()); m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); } break; case Value::eValueTypeFileAddress: case Value::eValueTypeVector: break; } // If we have reached this point, then we have successfully changed the value. SetNeedsUpdate(); return true; } static bool CopyStringDataToBufferSP(const StreamString &source, lldb::DataBufferSP &destination) { destination.reset(new DataBufferHeap(source.GetSize() + 1, 0)); memcpy(destination->GetBytes(), source.GetString().data(), source.GetSize()); return true; } std::pair ValueObject::ReadPointedString(lldb::DataBufferSP &buffer_sp, Error &error, uint32_t max_length, bool honor_array, Format item_format) { bool was_capped = false; StreamString s; ExecutionContext exe_ctx(GetExecutionContextRef()); Target *target = exe_ctx.GetTargetPtr(); if (!target) { s << ""; error.SetErrorString("no target to read from"); CopyStringDataToBufferSP(s, buffer_sp); return {0, was_capped}; } if (max_length == 0) max_length = target->GetMaximumSizeOfStringSummary(); size_t bytes_read = 0; size_t total_bytes_read = 0; CompilerType compiler_type = GetCompilerType(); CompilerType elem_or_pointee_compiler_type; const Flags type_flags(GetTypeInfo(&elem_or_pointee_compiler_type)); if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer) && elem_or_pointee_compiler_type.IsCharType()) { addr_t cstr_address = LLDB_INVALID_ADDRESS; AddressType cstr_address_type = eAddressTypeInvalid; size_t cstr_len = 0; bool capped_data = false; const bool is_array = type_flags.Test(eTypeIsArray); if (is_array) { // We have an array uint64_t array_size = 0; if (compiler_type.IsArrayType(NULL, &array_size, NULL)) { cstr_len = array_size; if (cstr_len > max_length) { capped_data = true; cstr_len = max_length; } } cstr_address = GetAddressOf(true, &cstr_address_type); } else { // We have a pointer cstr_address = GetPointerValue(&cstr_address_type); } if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) { if (cstr_address_type == eAddressTypeHost && is_array) { const char *cstr = GetDataExtractor().PeekCStr(0); if (cstr == nullptr) { s << ""; error.SetErrorString("invalid address"); CopyStringDataToBufferSP(s, buffer_sp); return {0, was_capped}; } buffer_sp.reset(new DataBufferHeap(cstr_len, 0)); memcpy(buffer_sp->GetBytes(), cstr, cstr_len); return {cstr_len, was_capped}; } else { s << ""; error.SetErrorString("invalid address"); CopyStringDataToBufferSP(s, buffer_sp); return {0, was_capped}; } } Address cstr_so_addr(cstr_address); DataExtractor data; if (cstr_len > 0 && honor_array) { // I am using GetPointeeData() here to abstract the fact that some // ValueObjects are actually frozen pointers in the host // but the pointed-to data lives in the debuggee, and GetPointeeData() // automatically takes care of this GetPointeeData(data, 0, cstr_len); if ((bytes_read = data.GetByteSize()) > 0) { total_bytes_read = bytes_read; for (size_t offset = 0; offset < bytes_read; offset++) s.Printf("%c", *data.PeekData(offset, 1)); if (capped_data) was_capped = true; } } else { cstr_len = max_length; const size_t k_max_buf_size = 64; size_t offset = 0; int cstr_len_displayed = -1; bool capped_cstr = false; // I am using GetPointeeData() here to abstract the fact that some // ValueObjects are actually frozen pointers in the host // but the pointed-to data lives in the debuggee, and GetPointeeData() // automatically takes care of this while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) { total_bytes_read += bytes_read; const char *cstr = data.PeekCStr(0); size_t len = strnlen(cstr, k_max_buf_size); if (cstr_len_displayed < 0) cstr_len_displayed = len; if (len == 0) break; cstr_len_displayed += len; if (len > bytes_read) len = bytes_read; if (len > cstr_len) len = cstr_len; for (size_t offset = 0; offset < bytes_read; offset++) s.Printf("%c", *data.PeekData(offset, 1)); if (len < k_max_buf_size) break; if (len >= cstr_len) { capped_cstr = true; break; } cstr_len -= len; offset += len; } if (cstr_len_displayed >= 0) { if (capped_cstr) was_capped = true; } } } else { error.SetErrorString("not a string object"); s << ""; } CopyStringDataToBufferSP(s, buffer_sp); return {total_bytes_read, was_capped}; } std::pair ValueObject::GetValidationStatus() { if (!UpdateValueIfNeeded(true)) return {TypeValidatorResult::Success, ""}; // not the validator's job to discuss update problems if (m_validation_result.hasValue()) return m_validation_result.getValue(); if (!m_type_validator_sp) return {TypeValidatorResult::Success, ""}; // no validator no failure auto outcome = m_type_validator_sp->FormatObject(this); return (m_validation_result = {outcome.m_result, outcome.m_message}) .getValue(); } const char *ValueObject::GetObjectDescription() { if (!UpdateValueIfNeeded(true)) return NULL; if (!m_object_desc_str.empty()) return m_object_desc_str.c_str(); ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process == NULL) return NULL; StreamString s; LanguageType language = GetObjectRuntimeLanguage(); LanguageRuntime *runtime = process->GetLanguageRuntime(language); if (runtime == NULL) { // Aw, hell, if the things a pointer, or even just an integer, let's try // ObjC anyway... CompilerType compiler_type = GetCompilerType(); if (compiler_type) { bool is_signed; if (compiler_type.IsIntegerType(is_signed) || compiler_type.IsPointerType()) { runtime = process->GetLanguageRuntime(eLanguageTypeObjC); } } } if (runtime && runtime->GetObjectDescription(s, *this)) { m_object_desc_str.append(s.GetString()); } if (m_object_desc_str.empty()) return NULL; else return m_object_desc_str.c_str(); } bool ValueObject::GetValueAsCString(const lldb_private::TypeFormatImpl &format, std::string &destination) { if (UpdateValueIfNeeded(false)) return format.FormatObject(this, destination); else return false; } bool ValueObject::GetValueAsCString(lldb::Format format, std::string &destination) { return GetValueAsCString(TypeFormatImpl_Format(format), destination); } const char *ValueObject::GetValueAsCString() { if (UpdateValueIfNeeded(true)) { lldb::TypeFormatImplSP format_sp; lldb::Format my_format = GetFormat(); if (my_format == lldb::eFormatDefault) { if (m_type_format_sp) format_sp = m_type_format_sp; else { if (m_is_bitfield_for_scalar) my_format = eFormatUnsigned; else { if (m_value.GetContextType() == Value::eContextTypeRegisterInfo) { const RegisterInfo *reg_info = m_value.GetRegisterInfo(); if (reg_info) my_format = reg_info->format; } else { my_format = GetValue().GetCompilerType().GetFormat(); } } } } if (my_format != m_last_format || m_value_str.empty()) { m_last_format = my_format; if (!format_sp) format_sp.reset(new TypeFormatImpl_Format(my_format)); if (GetValueAsCString(*format_sp.get(), m_value_str)) { if (!m_value_did_change && m_old_value_valid) { // The value was gotten successfully, so we consider the // value as changed if the value string differs SetValueDidChange(m_old_value_str != m_value_str); } } } } if (m_value_str.empty()) return NULL; return m_value_str.c_str(); } // if > 8bytes, 0 is returned. this method should mostly be used // to read address values out of pointers uint64_t ValueObject::GetValueAsUnsigned(uint64_t fail_value, bool *success) { // If our byte size is zero this is an aggregate type that has children if (CanProvideValue()) { Scalar scalar; if (ResolveValue(scalar)) { if (success) *success = true; return scalar.ULongLong(fail_value); } // fallthrough, otherwise... } if (success) *success = false; return fail_value; } int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) { // If our byte size is zero this is an aggregate type that has children if (CanProvideValue()) { Scalar scalar; if (ResolveValue(scalar)) { if (success) *success = true; return scalar.SLongLong(fail_value); } // fallthrough, otherwise... } if (success) *success = false; return fail_value; } // if any more "special cases" are added to // ValueObject::DumpPrintableRepresentation() please keep // this call up to date by returning true for your new special cases. We will // eventually move // to checking this call result before trying to display special cases bool ValueObject::HasSpecialPrintableRepresentation( ValueObjectRepresentationStyle val_obj_display, Format custom_format) { Flags flags(GetTypeInfo()); if (flags.AnySet(eTypeIsArray | eTypeIsPointer) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) { if (IsCStringContainer(true) && (custom_format == eFormatCString || custom_format == eFormatCharArray || custom_format == eFormatChar || custom_format == eFormatVectorOfChar)) return true; if (flags.Test(eTypeIsArray)) { if ((custom_format == eFormatBytes) || (custom_format == eFormatBytesWithASCII)) return true; if ((custom_format == eFormatVectorOfChar) || (custom_format == eFormatVectorOfFloat32) || (custom_format == eFormatVectorOfFloat64) || (custom_format == eFormatVectorOfSInt16) || (custom_format == eFormatVectorOfSInt32) || (custom_format == eFormatVectorOfSInt64) || (custom_format == eFormatVectorOfSInt8) || (custom_format == eFormatVectorOfUInt128) || (custom_format == eFormatVectorOfUInt16) || (custom_format == eFormatVectorOfUInt32) || (custom_format == eFormatVectorOfUInt64) || (custom_format == eFormatVectorOfUInt8)) return true; } } return false; } bool ValueObject::DumpPrintableRepresentation( Stream &s, ValueObjectRepresentationStyle val_obj_display, Format custom_format, PrintableRepresentationSpecialCases special, bool do_dump_error) { Flags flags(GetTypeInfo()); bool allow_special = (special == ValueObject::PrintableRepresentationSpecialCases::eAllow); const bool only_special = false; if (allow_special) { if (flags.AnySet(eTypeIsArray | eTypeIsPointer) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) { // when being asked to get a printable display an array or pointer type // directly, // try to "do the right thing" if (IsCStringContainer(true) && (custom_format == eFormatCString || custom_format == eFormatCharArray || custom_format == eFormatChar || custom_format == eFormatVectorOfChar)) // print char[] & char* directly { Error error; lldb::DataBufferSP buffer_sp; std::pair read_string = ReadPointedString( buffer_sp, error, 0, (custom_format == eFormatVectorOfChar) || (custom_format == eFormatCharArray)); lldb_private::formatters::StringPrinter:: ReadBufferAndDumpToStreamOptions options(*this); options.SetData(DataExtractor( buffer_sp, lldb::eByteOrderInvalid, 8)); // none of this matters for a string - pass some defaults options.SetStream(&s); options.SetPrefixToken(0); options.SetQuote('"'); options.SetSourceSize(buffer_sp->GetByteSize()); options.SetIsTruncated(read_string.second); formatters::StringPrinter::ReadBufferAndDumpToStream< lldb_private::formatters::StringPrinter::StringElementType::ASCII>( options); return !error.Fail(); } if (custom_format == eFormatEnum) return false; // this only works for arrays, because I have no way to know when // the pointed memory ends, and no special \0 end of data marker if (flags.Test(eTypeIsArray)) { if ((custom_format == eFormatBytes) || (custom_format == eFormatBytesWithASCII)) { const size_t count = GetNumChildren(); s << '['; for (size_t low = 0; low < count; low++) { if (low) s << ','; ValueObjectSP child = GetChildAtIndex(low, true); if (!child.get()) { s << ""; continue; } child->DumpPrintableRepresentation( s, ValueObject::eValueObjectRepresentationStyleValue, custom_format); } s << ']'; return true; } if ((custom_format == eFormatVectorOfChar) || (custom_format == eFormatVectorOfFloat32) || (custom_format == eFormatVectorOfFloat64) || (custom_format == eFormatVectorOfSInt16) || (custom_format == eFormatVectorOfSInt32) || (custom_format == eFormatVectorOfSInt64) || (custom_format == eFormatVectorOfSInt8) || (custom_format == eFormatVectorOfUInt128) || (custom_format == eFormatVectorOfUInt16) || (custom_format == eFormatVectorOfUInt32) || (custom_format == eFormatVectorOfUInt64) || (custom_format == eFormatVectorOfUInt8)) // arrays of bytes, bytes // with ASCII or any vector // format should be printed // directly { const size_t count = GetNumChildren(); Format format = FormatManager::GetSingleItemFormat(custom_format); s << '['; for (size_t low = 0; low < count; low++) { if (low) s << ','; ValueObjectSP child = GetChildAtIndex(low, true); if (!child.get()) { s << ""; continue; } child->DumpPrintableRepresentation( s, ValueObject::eValueObjectRepresentationStyleValue, format); } s << ']'; return true; } } if ((custom_format == eFormatBoolean) || (custom_format == eFormatBinary) || (custom_format == eFormatChar) || (custom_format == eFormatCharPrintable) || (custom_format == eFormatComplexFloat) || (custom_format == eFormatDecimal) || (custom_format == eFormatHex) || (custom_format == eFormatHexUppercase) || (custom_format == eFormatFloat) || (custom_format == eFormatOctal) || (custom_format == eFormatOSType) || (custom_format == eFormatUnicode16) || (custom_format == eFormatUnicode32) || (custom_format == eFormatUnsigned) || (custom_format == eFormatPointer) || (custom_format == eFormatComplexInteger) || (custom_format == eFormatComplex) || (custom_format == eFormatDefault)) // use the [] operator return false; } } if (only_special) return false; bool var_success = false; { llvm::StringRef str; // this is a local stream that we are using to ensure that the data pointed // to by cstr survives long enough for us to copy it to its destination - it // is necessary to have this temporary storage area for cases where our // desired output is not backed by some other longer-term storage StreamString strm; if (custom_format != eFormatInvalid) SetFormat(custom_format); switch (val_obj_display) { case eValueObjectRepresentationStyleValue: str = GetValueAsCString(); break; case eValueObjectRepresentationStyleSummary: str = GetSummaryAsCString(); break; case eValueObjectRepresentationStyleLanguageSpecific: str = GetObjectDescription(); break; case eValueObjectRepresentationStyleLocation: str = GetLocationAsCString(); break; case eValueObjectRepresentationStyleChildrenCount: strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildren()); str = strm.GetString(); break; case eValueObjectRepresentationStyleType: str = GetTypeName().GetStringRef(); break; case eValueObjectRepresentationStyleName: str = GetName().GetStringRef(); break; case eValueObjectRepresentationStyleExpressionPath: GetExpressionPath(strm, false); str = strm.GetString(); break; } if (str.empty()) { if (val_obj_display == eValueObjectRepresentationStyleValue) str = GetSummaryAsCString(); else if (val_obj_display == eValueObjectRepresentationStyleSummary) { if (!CanProvideValue()) { strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); str = strm.GetString(); } else str = GetValueAsCString(); } } if (!str.empty()) s << str; else { if (m_error.Fail()) { if (do_dump_error) s.Printf("<%s>", m_error.AsCString()); else return false; } else if (val_obj_display == eValueObjectRepresentationStyleSummary) s.PutCString(""); else if (val_obj_display == eValueObjectRepresentationStyleValue) s.PutCString(""); else if (val_obj_display == eValueObjectRepresentationStyleLanguageSpecific) s.PutCString(""); // edit this if we // have other runtimes // that support a // description else s.PutCString(""); } // we should only return false here if we could not do *anything* // even if we have an error message as output, that's a success // from our callers' perspective, so return true var_success = true; if (custom_format != eFormatInvalid) SetFormat(eFormatDefault); } return var_success; } addr_t ValueObject::GetAddressOf(bool scalar_is_load_address, AddressType *address_type) { // Can't take address of a bitfield if (IsBitfield()) return LLDB_INVALID_ADDRESS; if (!UpdateValueIfNeeded(false)) return LLDB_INVALID_ADDRESS; switch (m_value.GetValueType()) { case Value::eValueTypeScalar: case Value::eValueTypeVector: if (scalar_is_load_address) { if (address_type) *address_type = eAddressTypeLoad; return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); } break; case Value::eValueTypeLoadAddress: case Value::eValueTypeFileAddress: { if (address_type) *address_type = m_value.GetValueAddressType(); return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); } break; case Value::eValueTypeHostAddress: { if (address_type) *address_type = m_value.GetValueAddressType(); return LLDB_INVALID_ADDRESS; } break; } if (address_type) *address_type = eAddressTypeInvalid; return LLDB_INVALID_ADDRESS; } addr_t ValueObject::GetPointerValue(AddressType *address_type) { addr_t address = LLDB_INVALID_ADDRESS; if (address_type) *address_type = eAddressTypeInvalid; if (!UpdateValueIfNeeded(false)) return address; switch (m_value.GetValueType()) { case Value::eValueTypeScalar: case Value::eValueTypeVector: address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); break; case Value::eValueTypeHostAddress: case Value::eValueTypeLoadAddress: case Value::eValueTypeFileAddress: { lldb::offset_t data_offset = 0; address = m_data.GetPointer(&data_offset); } break; } if (address_type) *address_type = GetAddressTypeOfChildren(); return address; } bool ValueObject::SetValueFromCString(const char *value_str, Error &error) { error.Clear(); // Make sure our value is up to date first so that our location and location // type is valid. if (!UpdateValueIfNeeded(false)) { error.SetErrorString("unable to read value"); return false; } uint64_t count = 0; const Encoding encoding = GetCompilerType().GetEncoding(count); const size_t byte_size = GetByteSize(); Value::ValueType value_type = m_value.GetValueType(); if (value_type == Value::eValueTypeScalar) { // If the value is already a scalar, then let the scalar change itself: m_value.GetScalar().SetValueFromCString(value_str, encoding, byte_size); } else if (byte_size <= 16) { // If the value fits in a scalar, then make a new scalar and again let the // scalar code do the conversion, then figure out where to put the new // value. Scalar new_scalar; error = new_scalar.SetValueFromCString(value_str, encoding, byte_size); if (error.Success()) { switch (value_type) { case Value::eValueTypeLoadAddress: { // If it is a load address, then the scalar value is the storage // location // of the data, and we have to shove this value down to that load // location. ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); size_t bytes_written = process->WriteScalarToMemory( target_addr, new_scalar, byte_size, error); if (!error.Success()) return false; if (bytes_written != byte_size) { error.SetErrorString("unable to write value to memory"); return false; } } } break; case Value::eValueTypeHostAddress: { // If it is a host address, then we stuff the scalar as a DataBuffer // into the Value's data. DataExtractor new_data; new_data.SetByteOrder(m_data.GetByteOrder()); DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0)); m_data.SetData(buffer_sp, 0); bool success = new_scalar.GetData(new_data); if (success) { new_data.CopyByteOrderedData( 0, byte_size, const_cast(m_data.GetDataStart()), byte_size, m_data.GetByteOrder()); } m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); } break; case Value::eValueTypeFileAddress: case Value::eValueTypeScalar: case Value::eValueTypeVector: break; } } else { return false; } } else { // We don't support setting things bigger than a scalar at present. error.SetErrorString("unable to write aggregate data type"); return false; } // If we have reached this point, then we have successfully changed the value. SetNeedsUpdate(); return true; } bool ValueObject::GetDeclaration(Declaration &decl) { decl.Clear(); return false; } ConstString ValueObject::GetTypeName() { return GetCompilerType().GetConstTypeName(); } ConstString ValueObject::GetDisplayTypeName() { return GetTypeName(); } ConstString ValueObject::GetQualifiedTypeName() { return GetCompilerType().GetConstQualifiedTypeName(); } LanguageType ValueObject::GetObjectRuntimeLanguage() { return GetCompilerType().GetMinimumLanguage(); } void ValueObject::AddSyntheticChild(const ConstString &key, ValueObject *valobj) { m_synthetic_children[key] = valobj; } ValueObjectSP ValueObject::GetSyntheticChild(const ConstString &key) const { ValueObjectSP synthetic_child_sp; std::map::const_iterator pos = m_synthetic_children.find(key); if (pos != m_synthetic_children.end()) synthetic_child_sp = pos->second->GetSP(); return synthetic_child_sp; } uint32_t ValueObject::GetTypeInfo(CompilerType *pointee_or_element_compiler_type) { return GetCompilerType().GetTypeInfo(pointee_or_element_compiler_type); } bool ValueObject::IsPointerType() { return GetCompilerType().IsPointerType(); } bool ValueObject::IsArrayType() { return GetCompilerType().IsArrayType(NULL, NULL, NULL); } bool ValueObject::IsScalarType() { return GetCompilerType().IsScalarType(); } bool ValueObject::IsIntegerType(bool &is_signed) { return GetCompilerType().IsIntegerType(is_signed); } bool ValueObject::IsPointerOrReferenceType() { return GetCompilerType().IsPointerOrReferenceType(); } bool ValueObject::IsPossibleDynamicType() { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) return process->IsPossibleDynamicValue(*this); else return GetCompilerType().IsPossibleDynamicType(NULL, true, true); } bool ValueObject::IsRuntimeSupportValue() { Process *process(GetProcessSP().get()); if (process) { LanguageRuntime *runtime = process->GetLanguageRuntime(GetObjectRuntimeLanguage()); if (!runtime) runtime = process->GetObjCLanguageRuntime(); if (runtime) return runtime->IsRuntimeSupportValue(*this); } return false; } bool ValueObject::IsNilReference() { if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { return language->IsNilReference(*this); } return false; } bool ValueObject::IsUninitializedReference() { if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { return language->IsUninitializedReference(*this); } return false; } // This allows you to create an array member using and index // that doesn't not fall in the normal bounds of the array. // Many times structure can be defined as: // struct Collection // { // uint32_t item_count; // Item item_array[0]; // }; // The size of the "item_array" is 1, but many times in practice // there are more items in "item_array". ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index, bool can_create) { ValueObjectSP synthetic_child_sp; if (IsPointerType() || IsArrayType()) { char index_str[64]; snprintf(index_str, sizeof(index_str), "[%" PRIu64 "]", (uint64_t)index); ConstString index_const_str(index_str); // Check if we have already created a synthetic array member in this // valid object. If we have we will re-use it. synthetic_child_sp = GetSyntheticChild(index_const_str); if (!synthetic_child_sp) { ValueObject *synthetic_child; // We haven't made a synthetic array member for INDEX yet, so // lets make one and cache it for any future reference. synthetic_child = CreateChildAtIndex(0, true, index); // Cache the value if we got one back... if (synthetic_child) { AddSyntheticChild(index_const_str, synthetic_child); synthetic_child_sp = synthetic_child->GetSP(); synthetic_child_sp->SetName(ConstString(index_str)); synthetic_child_sp->m_is_array_item_for_pointer = true; } } } return synthetic_child_sp; } ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to, bool can_create) { ValueObjectSP synthetic_child_sp; if (IsScalarType()) { char index_str[64]; snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to); ConstString index_const_str(index_str); // Check if we have already created a synthetic array member in this // valid object. If we have we will re-use it. synthetic_child_sp = GetSyntheticChild(index_const_str); if (!synthetic_child_sp) { uint32_t bit_field_size = to - from + 1; uint32_t bit_field_offset = from; if (GetDataExtractor().GetByteOrder() == eByteOrderBig) bit_field_offset = GetByteSize() * 8 - bit_field_size - bit_field_offset; // We haven't made a synthetic array member for INDEX yet, so // lets make one and cache it for any future reference. ValueObjectChild *synthetic_child = new ValueObjectChild( *this, GetCompilerType(), index_const_str, GetByteSize(), 0, bit_field_size, bit_field_offset, false, false, eAddressTypeInvalid, 0); // Cache the value if we got one back... if (synthetic_child) { AddSyntheticChild(index_const_str, synthetic_child); synthetic_child_sp = synthetic_child->GetSP(); synthetic_child_sp->SetName(ConstString(index_str)); synthetic_child_sp->m_is_bitfield_for_scalar = true; } } } return synthetic_child_sp; } ValueObjectSP ValueObject::GetSyntheticChildAtOffset( uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str) { ValueObjectSP synthetic_child_sp; if (name_const_str.IsEmpty()) { char name_str[64]; snprintf(name_str, sizeof(name_str), "@%i", offset); name_const_str.SetCString(name_str); } // Check if we have already created a synthetic array member in this // valid object. If we have we will re-use it. synthetic_child_sp = GetSyntheticChild(name_const_str); if (synthetic_child_sp.get()) return synthetic_child_sp; if (!can_create) return ValueObjectSP(); ExecutionContext exe_ctx(GetExecutionContextRef()); ValueObjectChild *synthetic_child = new ValueObjectChild( *this, type, name_const_str, type.GetByteSize(exe_ctx.GetBestExecutionContextScope()), offset, 0, 0, false, false, eAddressTypeInvalid, 0); if (synthetic_child) { AddSyntheticChild(name_const_str, synthetic_child); synthetic_child_sp = synthetic_child->GetSP(); synthetic_child_sp->SetName(name_const_str); synthetic_child_sp->m_is_child_at_offset = true; } return synthetic_child_sp; } ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str) { ValueObjectSP synthetic_child_sp; if (name_const_str.IsEmpty()) { char name_str[128]; snprintf(name_str, sizeof(name_str), "base%s@%i", type.GetTypeName().AsCString(""), offset); name_const_str.SetCString(name_str); } // Check if we have already created a synthetic array member in this // valid object. If we have we will re-use it. synthetic_child_sp = GetSyntheticChild(name_const_str); if (synthetic_child_sp.get()) return synthetic_child_sp; if (!can_create) return ValueObjectSP(); const bool is_base_class = true; ExecutionContext exe_ctx(GetExecutionContextRef()); ValueObjectChild *synthetic_child = new ValueObjectChild( *this, type, name_const_str, type.GetByteSize(exe_ctx.GetBestExecutionContextScope()), offset, 0, 0, is_base_class, false, eAddressTypeInvalid, 0); if (synthetic_child) { AddSyntheticChild(name_const_str, synthetic_child); synthetic_child_sp = synthetic_child->GetSP(); synthetic_child_sp->SetName(name_const_str); } return synthetic_child_sp; } // your expression path needs to have a leading . or -> // (unless it somehow "looks like" an array, in which case it has // a leading [ symbol). while the [ is meaningful and should be shown // to the user, . and -> are just parser design, but by no means // added information for the user.. strip them off static const char *SkipLeadingExpressionPathSeparators(const char *expression) { if (!expression || !expression[0]) return expression; if (expression[0] == '.') return expression + 1; if (expression[0] == '-' && expression[1] == '>') return expression + 2; return expression; } ValueObjectSP ValueObject::GetSyntheticExpressionPathChild(const char *expression, bool can_create) { ValueObjectSP synthetic_child_sp; ConstString name_const_string(expression); // Check if we have already created a synthetic array member in this // valid object. If we have we will re-use it. synthetic_child_sp = GetSyntheticChild(name_const_string); if (!synthetic_child_sp) { // We haven't made a synthetic array member for expression yet, so // lets make one and cache it for any future reference. synthetic_child_sp = GetValueForExpressionPath( expression, NULL, NULL, GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal( GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: None)); // Cache the value if we got one back... if (synthetic_child_sp.get()) { // FIXME: this causes a "real" child to end up with its name changed to // the contents of expression AddSyntheticChild(name_const_string, synthetic_child_sp.get()); synthetic_child_sp->SetName( ConstString(SkipLeadingExpressionPathSeparators(expression))); } } return synthetic_child_sp; } void ValueObject::CalculateSyntheticValue(bool use_synthetic) { if (use_synthetic == false) return; TargetSP target_sp(GetTargetSP()); if (target_sp && target_sp->GetEnableSyntheticValue() == false) { m_synthetic_value = NULL; return; } lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); if (!UpdateFormatsIfNeeded() && m_synthetic_value) return; if (m_synthetic_children_sp.get() == NULL) return; if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value) return; m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp); } void ValueObject::CalculateDynamicValue(DynamicValueType use_dynamic) { if (use_dynamic == eNoDynamicValues) return; if (!m_dynamic_value && !IsDynamic()) { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsPossibleDynamicValue(*this)) { ClearDynamicTypeInformation(); m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic); } } } ValueObjectSP ValueObject::GetDynamicValue(DynamicValueType use_dynamic) { if (use_dynamic == eNoDynamicValues) return ValueObjectSP(); if (!IsDynamic() && m_dynamic_value == NULL) { CalculateDynamicValue(use_dynamic); } if (m_dynamic_value) return m_dynamic_value->GetSP(); else return ValueObjectSP(); } ValueObjectSP ValueObject::GetStaticValue() { return GetSP(); } lldb::ValueObjectSP ValueObject::GetNonSyntheticValue() { return GetSP(); } ValueObjectSP ValueObject::GetSyntheticValue(bool use_synthetic) { if (use_synthetic == false) return ValueObjectSP(); CalculateSyntheticValue(use_synthetic); if (m_synthetic_value) return m_synthetic_value->GetSP(); else return ValueObjectSP(); } bool ValueObject::HasSyntheticValue() { UpdateFormatsIfNeeded(); if (m_synthetic_children_sp.get() == NULL) return false; CalculateSyntheticValue(true); if (m_synthetic_value) return true; else return false; } bool ValueObject::GetBaseClassPath(Stream &s) { if (IsBaseClass()) { bool parent_had_base_class = GetParent() && GetParent()->GetBaseClassPath(s); CompilerType compiler_type = GetCompilerType(); std::string cxx_class_name; bool this_had_base_class = ClangASTContext::GetCXXClassName(compiler_type, cxx_class_name); if (this_had_base_class) { if (parent_had_base_class) s.PutCString("::"); s.PutCString(cxx_class_name); } return parent_had_base_class || this_had_base_class; } return false; } ValueObject *ValueObject::GetNonBaseClassParent() { if (GetParent()) { if (GetParent()->IsBaseClass()) return GetParent()->GetNonBaseClassParent(); else return GetParent(); } return NULL; } bool ValueObject::IsBaseClass(uint32_t &depth) { if (!IsBaseClass()) { depth = 0; return false; } if (GetParent()) { GetParent()->IsBaseClass(depth); depth = depth + 1; return true; } // TODO: a base of no parent? weird.. depth = 1; return true; } void ValueObject::GetExpressionPath(Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) { // synthetic children do not actually "exist" as part of the hierarchy, and // sometimes they are consed up in ways // that don't make sense from an underlying language/API standpoint. So, use a // special code path here to return // something that can hopefully be used in expression if (m_is_synthetic_children_generated) { UpdateValueIfNeeded(); if (m_value.GetValueType() == Value::eValueTypeLoadAddress) { if (IsPointerOrReferenceType()) { s.Printf("((%s)0x%" PRIx64 ")", GetTypeName().AsCString("void"), GetValueAsUnsigned(0)); return; } else { uint64_t load_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); if (load_addr != LLDB_INVALID_ADDRESS) { s.Printf("(*( (%s *)0x%" PRIx64 "))", GetTypeName().AsCString("void"), load_addr); return; } } } if (CanProvideValue()) { s.Printf("((%s)%s)", GetTypeName().AsCString("void"), GetValueAsCString()); return; } return; } const bool is_deref_of_parent = IsDereferenceOfParent(); if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) { // this is the original format of GetExpressionPath() producing code like // *(a_ptr).memberName, which is entirely // fine, until you put this into // StackFrame::GetValueForVariableExpressionPath() which prefers to see // a_ptr->memberName. // the eHonorPointers mode is meant to produce strings in this latter format s.PutCString("*("); } ValueObject *parent = GetParent(); if (parent) parent->GetExpressionPath(s, qualify_cxx_base_classes, epformat); // if we are a deref_of_parent just because we are synthetic array // members made up to allow ptr[%d] syntax to work in variable // printing, then add our name ([%d]) to the expression path if (m_is_array_item_for_pointer && epformat == eGetExpressionPathFormatHonorPointers) s.PutCString(m_name.AsCString()); if (!IsBaseClass()) { if (!is_deref_of_parent) { ValueObject *non_base_class_parent = GetNonBaseClassParent(); if (non_base_class_parent && !non_base_class_parent->GetName().IsEmpty()) { CompilerType non_base_class_parent_compiler_type = non_base_class_parent->GetCompilerType(); if (non_base_class_parent_compiler_type) { if (parent && parent->IsDereferenceOfParent() && epformat == eGetExpressionPathFormatHonorPointers) { s.PutCString("->"); } else { const uint32_t non_base_class_parent_type_info = non_base_class_parent_compiler_type.GetTypeInfo(); if (non_base_class_parent_type_info & eTypeIsPointer) { s.PutCString("->"); } else if ((non_base_class_parent_type_info & eTypeHasChildren) && !(non_base_class_parent_type_info & eTypeIsArray)) { s.PutChar('.'); } } } } const char *name = GetName().GetCString(); if (name) { if (qualify_cxx_base_classes) { if (GetBaseClassPath(s)) s.PutCString("::"); } s.PutCString(name); } } } if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) { s.PutChar(')'); } } ValueObjectSP ValueObject::GetValueForExpressionPath( llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop, ExpressionPathEndResultType *final_value_type, const GetValueForExpressionPathOptions &options, ExpressionPathAftermath *final_task_on_target) { ExpressionPathScanEndReason dummy_reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnknown; ExpressionPathEndResultType dummy_final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; ValueObjectSP ret_val = GetValueForExpressionPath_Impl( expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, final_value_type ? final_value_type : &dummy_final_value_type, options, final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) return ret_val; if (ret_val.get() && ((final_value_type ? *final_value_type : dummy_final_value_type) == eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress // of plain objects { if ((final_task_on_target ? *final_task_on_target : dummy_final_task_on_target) == ValueObject::eExpressionPathAftermathDereference) { Error error; ValueObjectSP final_value = ret_val->Dereference(error); if (error.Fail() || !final_value.get()) { if (reason_to_stop) *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; if (final_value_type) *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } else { if (final_task_on_target) *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; return final_value; } } if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) { Error error; ValueObjectSP final_value = ret_val->AddressOf(error); if (error.Fail() || !final_value.get()) { if (reason_to_stop) *reason_to_stop = ValueObject::eExpressionPathScanEndReasonTakingAddressFailed; if (final_value_type) *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } else { if (final_task_on_target) *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; return final_value; } } } return ret_val; // final_task_on_target will still have its original value, so // you know I did not do it } ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop, ExpressionPathEndResultType *final_result, const GetValueForExpressionPathOptions &options, ExpressionPathAftermath *what_next) { ValueObjectSP root = GetSP(); if (!root) return nullptr; llvm::StringRef remainder = expression; while (true) { llvm::StringRef temp_expression = remainder; CompilerType root_compiler_type = root->GetCompilerType(); CompilerType pointee_compiler_type; Flags pointee_compiler_type_info; Flags root_compiler_type_info( root_compiler_type.GetTypeInfo(&pointee_compiler_type)); if (pointee_compiler_type) pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo()); if (temp_expression.empty()) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; return root; } switch (temp_expression.front()) { case '-': { temp_expression = temp_expression.drop_front(); if (options.m_check_dot_vs_arrow_syntax && root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to // use -> on a // non-pointer and I // must catch the error { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to // extract an ObjC IVar // when this is forbidden root_compiler_type_info.Test(eTypeIsPointer) && options.m_no_fragile_ivar) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } if (!temp_expression.startswith(">")) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } } LLVM_FALLTHROUGH; case '.': // or fallthrough from -> { if (options.m_check_dot_vs_arrow_syntax && temp_expression.front() == '.' && root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to // use . on a pointer // and I must catch the // error { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } temp_expression = temp_expression.drop_front(); // skip . or > size_t next_sep_pos = temp_expression.find_first_of("-.[", 1); ConstString child_name; if (next_sep_pos == llvm::StringRef::npos) // if no other separator just // expand this last layer { child_name.SetString(temp_expression); ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); if (child_valobj_sp.get()) // we know we are done, so just return { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; *final_result = ValueObject::eExpressionPathEndResultTypePlain; return child_valobj_sp; } else { switch (options.m_synthetic_children_traversal) { case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: None: break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: FromSynthetic: if (root->IsSynthetic()) { child_valobj_sp = root->GetNonSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: ToSynthetic: if (!root->IsSynthetic()) { child_valobj_sp = root->GetSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: Both: if (root->IsSynthetic()) { child_valobj_sp = root->GetNonSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } else { child_valobj_sp = root->GetSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; } } // if we are here and options.m_no_synthetic_children is true, // child_valobj_sp is going to be a NULL SP, // so we hit the "else" branch, and return an error if (child_valobj_sp.get()) // if it worked, just return { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; *final_result = ValueObject::eExpressionPathEndResultTypePlain; return child_valobj_sp; } else { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } } else // other layers do expand { llvm::StringRef next_separator = temp_expression.substr(next_sep_pos); child_name.SetString(temp_expression.slice(0, next_sep_pos)); ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); if (child_valobj_sp.get()) // store the new root and move on { root = child_valobj_sp; remainder = next_separator; *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } else { switch (options.m_synthetic_children_traversal) { case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: None: break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: FromSynthetic: if (root->IsSynthetic()) { child_valobj_sp = root->GetNonSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: ToSynthetic: if (!root->IsSynthetic()) { child_valobj_sp = root->GetSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: Both: if (root->IsSynthetic()) { child_valobj_sp = root->GetNonSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } else { child_valobj_sp = root->GetSyntheticValue(); if (child_valobj_sp.get()) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } break; } } // if we are here and options.m_no_synthetic_children is true, // child_valobj_sp is going to be a NULL SP, // so we hit the "else" branch, and return an error if (child_valobj_sp.get()) // if it worked, move on { root = child_valobj_sp; remainder = next_separator; *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } else { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } } break; } case '[': { if (!root_compiler_type_info.Test(eTypeIsArray) && !root_compiler_type_info.Test(eTypeIsPointer) && !root_compiler_type_info.Test( eTypeIsVector)) // if this is not a T[] nor a T* { if (!root_compiler_type_info.Test( eTypeIsScalar)) // if this is not even a scalar... { if (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: None) // ...only chance left is synthetic { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } } else if (!options.m_allow_bitfields_syntax) // if this is a scalar, // check that we can // expand bitfields { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } } if (temp_expression[1] == ']') // if this is an unbounded range it only works for arrays { if (!root_compiler_type_info.Test(eTypeIsArray)) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else // even if something follows, we cannot expand unbounded ranges, // just let the caller do it { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; return root; } } size_t close_bracket_position = temp_expression.find(']', 1); if (close_bracket_position == llvm::StringRef::npos) // if there is no ], this is a syntax error { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } llvm::StringRef bracket_expr = temp_expression.slice(1, close_bracket_position); // If this was an empty expression it would have been caught by the if // above. assert(!bracket_expr.empty()); if (!bracket_expr.contains('-')) { // if no separator, this is of the form [N]. Note that this cannot // be an unbounded range of the form [], because that case was handled // above with an unconditional return. unsigned long index = 0; if (bracket_expr.getAsInteger(0, index)) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } // from here on we do have a valid index if (root_compiler_type_info.Test(eTypeIsArray)) { ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); if (!child_valobj_sp) child_valobj_sp = root->GetSyntheticArrayMember(index, true); if (!child_valobj_sp) if (root->HasSyntheticValue() && root->GetSyntheticValue()->GetNumChildren() > index) child_valobj_sp = root->GetSyntheticValue()->GetChildAtIndex(index, true); if (child_valobj_sp) { root = child_valobj_sp; remainder = temp_expression.substr(close_bracket_position + 1); // skip ] *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } else { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } } else if (root_compiler_type_info.Test(eTypeIsPointer)) { if (*what_next == ValueObject:: eExpressionPathAftermathDereference && // if this is a // ptr-to-scalar, I // am accessing it // by index and I // would have // deref'ed anyway, // then do it now // and use this as // a bitfield pointee_compiler_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); if (error.Fail() || !root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else { *what_next = eExpressionPathAftermathNothing; continue; } } else { if (root->GetCompilerType().GetMinimumLanguage() == eLanguageTypeObjC && pointee_compiler_type_info.AllClear(eTypeIsPointer) && root->HasSyntheticValue() && (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::ToSynthetic || options.m_synthetic_children_traversal == GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::Both)) { root = root->GetSyntheticValue()->GetChildAtIndex(index, true); } else root = root->GetSyntheticArrayMember(index, true); if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else { remainder = temp_expression.substr(close_bracket_position + 1); // skip ] *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } } } else if (root_compiler_type_info.Test(eTypeIsScalar)) { root = root->GetSyntheticBitFieldChild(index, index, true); if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else // we do not know how to expand members of bitfields, so we // just return and let the caller do any further processing { *reason_to_stop = ValueObject:: eExpressionPathScanEndReasonBitfieldRangeOperatorMet; *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; return root; } } else if (root_compiler_type_info.Test(eTypeIsVector)) { root = root->GetChildAtIndex(index, true); if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } else { remainder = temp_expression.substr(close_bracket_position + 1); // skip ] *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } } else if (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::ToSynthetic || options.m_synthetic_children_traversal == GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::Both) { if (root->HasSyntheticValue()) root = root->GetSyntheticValue(); else if (!root->IsSynthetic()) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } // if we are here, then root itself is a synthetic VO.. should be good // to go if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } root = root->GetChildAtIndex(index, true); if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else { remainder = temp_expression.substr(close_bracket_position + 1); // skip ] *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } } else { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } } else { // we have a low and a high index llvm::StringRef sleft, sright; unsigned long low_index, high_index; std::tie(sleft, sright) = bracket_expr.split('-'); if (sleft.getAsInteger(0, low_index) || sright.getAsInteger(0, high_index)) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } if (low_index > high_index) // swap indices if required std::swap(low_index, high_index); if (root_compiler_type_info.Test( eTypeIsScalar)) // expansion only works for scalars { root = root->GetSyntheticBitFieldChild(low_index, high_index, true); if (!root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else { *reason_to_stop = ValueObject:: eExpressionPathScanEndReasonBitfieldRangeOperatorMet; *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; return root; } } else if (root_compiler_type_info.Test( eTypeIsPointer) && // if this is a ptr-to-scalar, I am // accessing it by index and I would // have deref'ed anyway, then do it // now and use this as a bitfield *what_next == ValueObject::eExpressionPathAftermathDereference && pointee_compiler_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); if (error.Fail() || !root) { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } else { *what_next = ValueObject::eExpressionPathAftermathNothing; continue; } } else { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; return root; } } break; } default: // some non-separator is in the way { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return nullptr; } } } } void ValueObject::LogValueObject(Log *log) { if (log) return LogValueObject(log, DumpValueObjectOptions(*this)); } void ValueObject::LogValueObject(Log *log, const DumpValueObjectOptions &options) { if (log) { StreamString s; Dump(s, options); if (s.GetSize()) log->PutCString(s.GetData()); } } void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); } void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) { ValueObjectPrinter printer(this, &s, options); printer.PrintValueObject(); } ValueObjectSP ValueObject::CreateConstantValue(const ConstString &name) { ValueObjectSP valobj_sp; if (UpdateValueIfNeeded(false) && m_error.Success()) { ExecutionContext exe_ctx(GetExecutionContextRef()); DataExtractor data; data.SetByteOrder(m_data.GetByteOrder()); data.SetAddressByteSize(m_data.GetAddressByteSize()); if (IsBitfield()) { Value v(Scalar(GetValueAsUnsigned(UINT64_MAX))); m_error = v.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); } else m_error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); valobj_sp = ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), GetCompilerType(), name, data, GetAddressOf()); } if (!valobj_sp) { ExecutionContext exe_ctx(GetExecutionContextRef()); valobj_sp = ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), m_error); } return valobj_sp; } ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable( lldb::DynamicValueType dynValue, bool synthValue) { ValueObjectSP result_sp(GetSP()); switch (dynValue) { case lldb::eDynamicCanRunTarget: case lldb::eDynamicDontRunTarget: { if (!result_sp->IsDynamic()) { if (result_sp->GetDynamicValue(dynValue)) result_sp = result_sp->GetDynamicValue(dynValue); } } break; case lldb::eNoDynamicValues: { if (result_sp->IsDynamic()) { if (result_sp->GetStaticValue()) result_sp = result_sp->GetStaticValue(); } } break; } if (synthValue) { if (!result_sp->IsSynthetic()) { if (result_sp->GetSyntheticValue()) result_sp = result_sp->GetSyntheticValue(); } } else { if (result_sp->IsSynthetic()) { if (result_sp->GetNonSyntheticValue()) result_sp = result_sp->GetNonSyntheticValue(); } } return result_sp; } lldb::addr_t ValueObject::GetCPPVTableAddress(AddressType &address_type) { CompilerType pointee_type; CompilerType this_type(GetCompilerType()); uint32_t type_info = this_type.GetTypeInfo(&pointee_type); if (type_info) { bool ptr_or_ref = false; if (type_info & (eTypeIsPointer | eTypeIsReference)) { ptr_or_ref = true; type_info = pointee_type.GetTypeInfo(); } const uint32_t cpp_class = eTypeIsClass | eTypeIsCPlusPlus; if ((type_info & cpp_class) == cpp_class) { if (ptr_or_ref) { address_type = GetAddressTypeOfChildren(); return GetValueAsUnsigned(LLDB_INVALID_ADDRESS); } else return GetAddressOf(false, &address_type); } } address_type = eAddressTypeInvalid; return LLDB_INVALID_ADDRESS; } ValueObjectSP ValueObject::Dereference(Error &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); if (is_pointer_or_reference_type) { bool omit_empty_base_classes = true; bool ignore_array_bounds = false; std::string child_name_str; uint32_t child_byte_size = 0; int32_t child_byte_offset = 0; uint32_t child_bitfield_bit_size = 0; uint32_t child_bitfield_bit_offset = 0; bool child_is_base_class = false; bool child_is_deref_of_parent = false; const bool transparent_pointers = false; CompilerType compiler_type = GetCompilerType(); CompilerType child_compiler_type; uint64_t language_flags; ExecutionContext exe_ctx(GetExecutionContextRef()); child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex( &exe_ctx, 0, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, this, language_flags); if (child_compiler_type && child_byte_size) { ConstString child_name; if (!child_name_str.empty()) child_name.SetCString(child_name_str.c_str()); m_deref_valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, language_flags); } } else if (HasSyntheticValue()) { m_deref_valobj = GetSyntheticValue() ->GetChildMemberWithName(ConstString("$$dereference$$"), true) .get(); } if (m_deref_valobj) { error.Clear(); return m_deref_valobj->GetSP(); } else { StreamString strm; GetExpressionPath(strm, true); if (is_pointer_or_reference_type) error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString(""), strm.GetData()); else error.SetErrorStringWithFormat("not a pointer or reference type: (%s) %s", GetTypeName().AsCString(""), strm.GetData()); return ValueObjectSP(); } } ValueObjectSP ValueObject::AddressOf(Error &error) { if (m_addr_of_valobj_sp) return m_addr_of_valobj_sp; AddressType address_type = eAddressTypeInvalid; const bool scalar_is_load_address = false; addr_t addr = GetAddressOf(scalar_is_load_address, &address_type); error.Clear(); if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) { switch (address_type) { case eAddressTypeInvalid: { StreamString expr_path_strm; GetExpressionPath(expr_path_strm, true); error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetData()); } break; case eAddressTypeFile: case eAddressTypeLoad: { CompilerType compiler_type = GetCompilerType(); if (compiler_type) { std::string name(1, '&'); name.append(m_name.AsCString("")); ExecutionContext exe_ctx(GetExecutionContextRef()); m_addr_of_valobj_sp = ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), compiler_type.GetPointerType(), ConstString(name.c_str()), addr, eAddressTypeInvalid, m_data.GetAddressByteSize()); } } break; default: break; } } else { StreamString expr_path_strm; GetExpressionPath(expr_path_strm, true); error.SetErrorStringWithFormat("'%s' doesn't have a valid address", expr_path_strm.GetData()); } return m_addr_of_valobj_sp; } ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) { return ValueObjectCast::Create(*this, GetName(), compiler_type); } lldb::ValueObjectSP ValueObject::Clone(const ConstString &new_name) { return ValueObjectCast::Create(*this, new_name, GetCompilerType()); } ValueObjectSP ValueObject::CastPointerType(const char *name, CompilerType &compiler_type) { ValueObjectSP valobj_sp; AddressType address_type; addr_t ptr_value = GetPointerValue(&address_type); if (ptr_value != LLDB_INVALID_ADDRESS) { Address ptr_addr(ptr_value); ExecutionContext exe_ctx(GetExecutionContextRef()); valobj_sp = ValueObjectMemory::Create( exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, compiler_type); } return valobj_sp; } ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) { ValueObjectSP valobj_sp; AddressType address_type; addr_t ptr_value = GetPointerValue(&address_type); if (ptr_value != LLDB_INVALID_ADDRESS) { Address ptr_addr(ptr_value); ExecutionContext exe_ctx(GetExecutionContextRef()); valobj_sp = ValueObjectMemory::Create( exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, type_sp); } return valobj_sp; } ValueObject::EvaluationPoint::EvaluationPoint() : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) {} ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope, bool use_selected) : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) { ExecutionContext exe_ctx(exe_scope); TargetSP target_sp(exe_ctx.GetTargetSP()); if (target_sp) { m_exe_ctx_ref.SetTargetSP(target_sp); ProcessSP process_sp(exe_ctx.GetProcessSP()); if (!process_sp) process_sp = target_sp->GetProcessSP(); if (process_sp) { m_mod_id = process_sp->GetModID(); m_exe_ctx_ref.SetProcessSP(process_sp); ThreadSP thread_sp(exe_ctx.GetThreadSP()); if (!thread_sp) { if (use_selected) thread_sp = process_sp->GetThreadList().GetSelectedThread(); } if (thread_sp) { m_exe_ctx_ref.SetThreadSP(thread_sp); StackFrameSP frame_sp(exe_ctx.GetFrameSP()); if (!frame_sp) { if (use_selected) frame_sp = thread_sp->GetSelectedFrame(); } if (frame_sp) m_exe_ctx_ref.SetFrameSP(frame_sp); } } } } ValueObject::EvaluationPoint::EvaluationPoint( const ValueObject::EvaluationPoint &rhs) : m_mod_id(), m_exe_ctx_ref(rhs.m_exe_ctx_ref), m_needs_update(true) {} ValueObject::EvaluationPoint::~EvaluationPoint() {} // This function checks the EvaluationPoint against the current process state. // If the current // state matches the evaluation point, or the evaluation point is already // invalid, then we return // false, meaning "no change". If the current state is different, we update our // state, and return // true meaning "yes, change". If we did see a change, we also set // m_needs_update to true, so // future calls to NeedsUpdate will return true. // exe_scope will be set to the current execution context scope. bool ValueObject::EvaluationPoint::SyncWithProcessState( bool accept_invalid_exe_ctx) { // Start with the target, if it is NULL, then we're obviously not going to get // any further: const bool thread_and_frame_only_if_stopped = true; ExecutionContext exe_ctx( m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped)); if (exe_ctx.GetTargetPtr() == NULL) return false; // If we don't have a process nothing can change. Process *process = exe_ctx.GetProcessPtr(); if (process == NULL) return false; // If our stop id is the current stop ID, nothing has changed: ProcessModID current_mod_id = process->GetModID(); // If the current stop id is 0, either we haven't run yet, or the process // state has been cleared. // In either case, we aren't going to be able to sync with the process state. if (current_mod_id.GetStopID() == 0) return false; bool changed = false; const bool was_valid = m_mod_id.IsValid(); if (was_valid) { if (m_mod_id == current_mod_id) { // Everything is already up to date in this object, no need to // update the execution context scope. changed = false; } else { m_mod_id = current_mod_id; m_needs_update = true; changed = true; } } // Now re-look up the thread and frame in case the underlying objects have // gone away & been recreated. // That way we'll be sure to return a valid exe_scope. // If we used to have a thread or a frame but can't find it anymore, then mark // ourselves as invalid. if (!accept_invalid_exe_ctx) { if (m_exe_ctx_ref.HasThreadRef()) { ThreadSP thread_sp(m_exe_ctx_ref.GetThreadSP()); if (thread_sp) { if (m_exe_ctx_ref.HasFrameRef()) { StackFrameSP frame_sp(m_exe_ctx_ref.GetFrameSP()); if (!frame_sp) { // We used to have a frame, but now it is gone SetInvalid(); changed = was_valid; } } } else { // We used to have a thread, but now it is gone SetInvalid(); changed = was_valid; } } } return changed; } void ValueObject::EvaluationPoint::SetUpdated() { ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); if (process_sp) m_mod_id = process_sp->GetModID(); m_needs_update = false; } void ValueObject::ClearUserVisibleData(uint32_t clear_mask) { if ((clear_mask & eClearUserVisibleDataItemsValue) == eClearUserVisibleDataItemsValue) m_value_str.clear(); if ((clear_mask & eClearUserVisibleDataItemsLocation) == eClearUserVisibleDataItemsLocation) m_location_str.clear(); if ((clear_mask & eClearUserVisibleDataItemsSummary) == eClearUserVisibleDataItemsSummary) m_summary_str.clear(); if ((clear_mask & eClearUserVisibleDataItemsDescription) == eClearUserVisibleDataItemsDescription) m_object_desc_str.clear(); if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) == eClearUserVisibleDataItemsSyntheticChildren) { if (m_synthetic_value) m_synthetic_value = NULL; } if ((clear_mask & eClearUserVisibleDataItemsValidator) == eClearUserVisibleDataItemsValidator) m_validation_result.reset(); } SymbolContextScope *ValueObject::GetSymbolContextScope() { if (m_parent) { if (!m_parent->IsPointerOrReferenceType()) return m_parent->GetSymbolContextScope(); } return NULL; } lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression(llvm::StringRef name, llvm::StringRef expression, const ExecutionContext &exe_ctx) { return CreateValueObjectFromExpression(name, expression, exe_ctx, EvaluateExpressionOptions()); } lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression( llvm::StringRef name, llvm::StringRef expression, const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options) { lldb::ValueObjectSP retval_sp; lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); if (!target_sp) return retval_sp; if (expression.empty()) return retval_sp; target_sp->EvaluateExpression(expression, exe_ctx.GetFrameSP().get(), retval_sp, options); if (retval_sp && !name.empty()) retval_sp->SetName(ConstString(name)); return retval_sp; } lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type) { if (type) { CompilerType pointer_type(type.GetPointerType()); if (pointer_type) { lldb::DataBufferSP buffer( new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t))); lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), pointer_type, ConstString(name), buffer, exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize())); if (ptr_result_valobj_sp) { ptr_result_valobj_sp->GetValue().SetValueType( Value::eValueTypeLoadAddress); Error err; ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); if (ptr_result_valobj_sp && !name.empty()) ptr_result_valobj_sp->SetName(ConstString(name)); } return ptr_result_valobj_sp; } } return lldb::ValueObjectSP(); } lldb::ValueObjectSP ValueObject::CreateValueObjectFromData( llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type) { lldb::ValueObjectSP new_value_sp; new_value_sp = ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), type, ConstString(name), data, LLDB_INVALID_ADDRESS); new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); if (new_value_sp && !name.empty()) new_value_sp->SetName(ConstString(name)); return new_value_sp; } ModuleSP ValueObject::GetModule() { ValueObject *root(GetRoot()); if (root != this) return root->GetModule(); return lldb::ModuleSP(); } ValueObject *ValueObject::GetRoot() { if (m_root) return m_root; return (m_root = FollowParentChain([](ValueObject *vo) -> bool { return (vo->m_parent != nullptr); })); } ValueObject * ValueObject::FollowParentChain(std::function f) { ValueObject *vo = this; while (vo) { if (f(vo) == false) break; vo = vo->m_parent; } return vo; } AddressType ValueObject::GetAddressTypeOfChildren() { if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) { ValueObject *root(GetRoot()); if (root != this) return root->GetAddressTypeOfChildren(); } return m_address_type_of_ptr_or_ref_children; } lldb::DynamicValueType ValueObject::GetDynamicValueType() { ValueObject *with_dv_info = this; while (with_dv_info) { if (with_dv_info->HasDynamicValueTypeInfo()) return with_dv_info->GetDynamicValueTypeImpl(); with_dv_info = with_dv_info->m_parent; } return lldb::eNoDynamicValues; } lldb::Format ValueObject::GetFormat() const { const ValueObject *with_fmt_info = this; while (with_fmt_info) { if (with_fmt_info->m_format != lldb::eFormatDefault) return with_fmt_info->m_format; with_fmt_info = with_fmt_info->m_parent; } return m_format; } lldb::LanguageType ValueObject::GetPreferredDisplayLanguage() { lldb::LanguageType type = m_preferred_display_language; if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { if (GetRoot()) { if (GetRoot() == this) { if (StackFrameSP frame_sp = GetFrameSP()) { const SymbolContext &sc( frame_sp->GetSymbolContext(eSymbolContextCompUnit)); if (CompileUnit *cu = sc.comp_unit) type = cu->GetLanguage(); } } else { type = GetRoot()->GetPreferredDisplayLanguage(); } } } return (m_preferred_display_language = type); // only compute it once } void ValueObject::SetPreferredDisplayLanguage(lldb::LanguageType lt) { m_preferred_display_language = lt; } void ValueObject::SetPreferredDisplayLanguageIfNeeded(lldb::LanguageType lt) { if (m_preferred_display_language == lldb::eLanguageTypeUnknown) SetPreferredDisplayLanguage(lt); } bool ValueObject::CanProvideValue() { // we need to support invalid types as providers of values because some // bare-board // debugging scenarios have no notion of types, but still manage to have raw // numeric // values for things like registers. sigh. const CompilerType &type(GetCompilerType()); return (false == type.IsValid()) || (0 != (type.GetTypeInfo() & eTypeHasValue)); } bool ValueObject::IsChecksumEmpty() { return m_value_checksum.empty(); } ValueObjectSP ValueObject::Persist() { if (!UpdateValueIfNeeded()) return nullptr; TargetSP target_sp(GetTargetSP()); if (!target_sp) return nullptr; PersistentExpressionState *persistent_state = target_sp->GetPersistentExpressionStateForLanguage( GetPreferredDisplayLanguage()); if (!persistent_state) return nullptr; ConstString name(persistent_state->GetNextPersistentVariableName()); ValueObjectSP const_result_sp = ValueObjectConstResult::Create(target_sp.get(), GetValue(), name); ExpressionVariableSP clang_var_sp = persistent_state->CreatePersistentVariable(const_result_sp); clang_var_sp->m_live_sp = clang_var_sp->m_frozen_sp; clang_var_sp->m_flags |= ExpressionVariable::EVIsProgramReference; return clang_var_sp->GetValueObject(); } bool ValueObject::IsSyntheticChildrenGenerated() { return m_is_synthetic_children_generated; } void ValueObject::SetSyntheticChildrenGenerated(bool b) { m_is_synthetic_children_generated = b; } uint64_t ValueObject::GetLanguageFlags() { return m_language_flags; } void ValueObject::SetLanguageFlags(uint64_t flags) { m_language_flags = flags; } ValueObjectManager::ValueObjectManager(lldb::ValueObjectSP in_valobj_sp, lldb::DynamicValueType use_dynamic, bool use_synthetic) : m_root_valobj_sp(), m_user_valobj_sp(), m_use_dynamic(use_dynamic), m_stop_id(UINT32_MAX), m_use_synthetic(use_synthetic) { if (!in_valobj_sp) return; // If the user passes in a value object that is dynamic or synthetic, then // water it down to the static type. m_root_valobj_sp = in_valobj_sp->GetQualifiedRepresentationIfAvailable(lldb::eNoDynamicValues, false); } bool ValueObjectManager::IsValid() const { if (!m_root_valobj_sp) return false; lldb::TargetSP target_sp = GetTargetSP(); if (target_sp) return target_sp->IsValid(); return false; } lldb::ValueObjectSP ValueObjectManager::GetSP() { lldb::ProcessSP process_sp = GetProcessSP(); if (!process_sp) return lldb::ValueObjectSP(); const uint32_t current_stop_id = process_sp->GetLastNaturalStopID(); if (current_stop_id == m_stop_id) return m_user_valobj_sp; m_stop_id = current_stop_id; if (!m_root_valobj_sp) { m_user_valobj_sp.reset(); return m_root_valobj_sp; } m_user_valobj_sp = m_root_valobj_sp; if (m_use_dynamic != lldb::eNoDynamicValues) { lldb::ValueObjectSP dynamic_sp = m_user_valobj_sp->GetDynamicValue(m_use_dynamic); if (dynamic_sp) m_user_valobj_sp = dynamic_sp; } if (m_use_synthetic) { lldb::ValueObjectSP synthetic_sp = m_user_valobj_sp->GetSyntheticValue(m_use_synthetic); if (synthetic_sp) m_user_valobj_sp = synthetic_sp; } return m_user_valobj_sp; } void ValueObjectManager::SetUseDynamic(lldb::DynamicValueType use_dynamic) { if (use_dynamic != m_use_dynamic) { m_use_dynamic = use_dynamic; m_user_valobj_sp.reset(); m_stop_id = UINT32_MAX; } } void ValueObjectManager::SetUseSynthetic(bool use_synthetic) { if (m_use_synthetic != use_synthetic) { m_use_synthetic = use_synthetic; m_user_valobj_sp.reset(); m_stop_id = UINT32_MAX; } } lldb::TargetSP ValueObjectManager::GetTargetSP() const { if (!m_root_valobj_sp) return m_root_valobj_sp->GetTargetSP(); return lldb::TargetSP(); } lldb::ProcessSP ValueObjectManager::GetProcessSP() const { if (m_root_valobj_sp) return m_root_valobj_sp->GetProcessSP(); return lldb::ProcessSP(); } lldb::ThreadSP ValueObjectManager::GetThreadSP() const { if (m_root_valobj_sp) return m_root_valobj_sp->GetThreadSP(); return lldb::ThreadSP(); } lldb::StackFrameSP ValueObjectManager::GetFrameSP() const { if (m_root_valobj_sp) return m_root_valobj_sp->GetFrameSP(); return lldb::StackFrameSP(); } Index: vendor/lldb/dist/source/Host/common/SocketAddress.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/SocketAddress.cpp (revision 317454) +++ vendor/lldb/dist/source/Host/common/SocketAddress.cpp (revision 317455) @@ -1,337 +1,331 @@ //===-- SocketAddress.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(_MSC_VER) #define _WINSOCK_DEPRECATED_NO_WARNINGS #endif #include "lldb/Host/SocketAddress.h" #include #include // C Includes #if !defined(_WIN32) #include #endif #include #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Host/PosixApi.h" // WindowsXP needs an inet_ntop implementation #ifdef _WIN32 #ifndef INET6_ADDRSTRLEN // might not be defined in older Windows SDKs #define INET6_ADDRSTRLEN 46 #endif // TODO: implement shortened form "::" for runs of zeros const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { if (size == 0) { return nullptr; } switch (af) { case AF_INET: { { const char *formatted = inet_ntoa(*static_cast(src)); if (formatted && strlen(formatted) < static_cast(size)) { return ::strcpy(dst, formatted); } } return nullptr; case AF_INET6: { char tmp[INET6_ADDRSTRLEN] = {0}; const uint16_t *src16 = static_cast(src); int full_size = ::snprintf( tmp, sizeof(tmp), "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(src16[0]), ntohs(src16[1]), ntohs(src16[2]), ntohs(src16[3]), ntohs(src16[4]), ntohs(src16[5]), ntohs(src16[6]), ntohs(src16[7])); if (full_size < static_cast(size)) { return ::strcpy(dst, tmp); } return nullptr; } } } return nullptr; } #endif using namespace lldb_private; //---------------------------------------------------------------------- // SocketAddress constructor //---------------------------------------------------------------------- SocketAddress::SocketAddress() { Clear(); } SocketAddress::SocketAddress(const struct sockaddr &s) { m_socket_addr.sa = s; } SocketAddress::SocketAddress(const struct sockaddr_in &s) { m_socket_addr.sa_ipv4 = s; } SocketAddress::SocketAddress(const struct sockaddr_in6 &s) { m_socket_addr.sa_ipv6 = s; } SocketAddress::SocketAddress(const struct sockaddr_storage &s) { m_socket_addr.sa_storage = s; } SocketAddress::SocketAddress(const struct addrinfo *addr_info) { *this = addr_info; } //---------------------------------------------------------------------- // SocketAddress copy constructor //---------------------------------------------------------------------- SocketAddress::SocketAddress(const SocketAddress &rhs) : m_socket_addr(rhs.m_socket_addr) {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SocketAddress::~SocketAddress() {} void SocketAddress::Clear() { memset(&m_socket_addr, 0, sizeof(m_socket_addr)); } bool SocketAddress::IsValid() const { return GetLength() != 0; } static socklen_t GetFamilyLength(sa_family_t family) { switch (family) { case AF_INET: return sizeof(struct sockaddr_in); case AF_INET6: return sizeof(struct sockaddr_in6); } assert(0 && "Unsupported address family"); return 0; } socklen_t SocketAddress::GetLength() const { #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) return m_socket_addr.sa.sa_len; #else return GetFamilyLength(GetFamily()); #endif } socklen_t SocketAddress::GetMaxLength() { return sizeof(sockaddr_t); } sa_family_t SocketAddress::GetFamily() const { return m_socket_addr.sa.sa_family; } void SocketAddress::SetFamily(sa_family_t family) { m_socket_addr.sa.sa_family = family; #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) m_socket_addr.sa.sa_len = GetFamilyLength(family); #endif } std::string SocketAddress::GetIPAddress() const { char str[INET6_ADDRSTRLEN] = {0}; switch (GetFamily()) { case AF_INET: if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv4.sin_addr, str, sizeof(str))) return str; break; case AF_INET6: if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv6.sin6_addr, str, sizeof(str))) return str; break; } return ""; } uint16_t SocketAddress::GetPort() const { switch (GetFamily()) { case AF_INET: return ntohs(m_socket_addr.sa_ipv4.sin_port); case AF_INET6: return ntohs(m_socket_addr.sa_ipv6.sin6_port); } return 0; } bool SocketAddress::SetPort(uint16_t port) { switch (GetFamily()) { case AF_INET: m_socket_addr.sa_ipv4.sin_port = htons(port); return true; case AF_INET6: m_socket_addr.sa_ipv6.sin6_port = htons(port); return true; } return false; } //---------------------------------------------------------------------- // SocketAddress assignment operator //---------------------------------------------------------------------- const SocketAddress &SocketAddress::operator=(const SocketAddress &rhs) { if (this != &rhs) m_socket_addr = rhs.m_socket_addr; return *this; } const SocketAddress &SocketAddress:: operator=(const struct addrinfo *addr_info) { Clear(); if (addr_info && addr_info->ai_addr && addr_info->ai_addrlen > 0 && addr_info->ai_addrlen <= sizeof m_socket_addr) { ::memcpy(&m_socket_addr, addr_info->ai_addr, addr_info->ai_addrlen); } return *this; } const SocketAddress &SocketAddress::operator=(const struct sockaddr &s) { m_socket_addr.sa = s; return *this; } const SocketAddress &SocketAddress::operator=(const struct sockaddr_in &s) { m_socket_addr.sa_ipv4 = s; return *this; } const SocketAddress &SocketAddress::operator=(const struct sockaddr_in6 &s) { m_socket_addr.sa_ipv6 = s; return *this; } const SocketAddress &SocketAddress:: operator=(const struct sockaddr_storage &s) { m_socket_addr.sa_storage = s; return *this; } bool SocketAddress::getaddrinfo(const char *host, const char *service, int ai_family, int ai_socktype, int ai_protocol, int ai_flags) { Clear(); + auto addresses = GetAddressInfo(host, service, ai_family, ai_socktype, ai_protocol, ai_flags); + if (!addresses.empty()) + *this = addresses[0]; + return IsValid(); +} + +std::vector +SocketAddress::GetAddressInfo(const char *hostname, const char *servname, + int ai_family, int ai_socktype, int ai_protocol, + int ai_flags) { + std::vector addr_list; + struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = ai_family; hints.ai_socktype = ai_socktype; hints.ai_protocol = ai_protocol; hints.ai_flags = ai_flags; - bool result = false; struct addrinfo *service_info_list = NULL; - int err = ::getaddrinfo(host, service, &hints, &service_info_list); - if (err == 0 && service_info_list) { - *this = service_info_list; - result = IsValid(); - } - - if (service_info_list) - ::freeaddrinfo(service_info_list); - - return result; -} - -std::vector SocketAddress::GetAddressInfo(const char *hostname, - const char *servname) { - std::vector addr_list; - - struct addrinfo *service_info_list = NULL; - int err = ::getaddrinfo(hostname, servname, NULL, &service_info_list); + int err = ::getaddrinfo(hostname, servname, &hints, &service_info_list); if (err == 0 && service_info_list) { for (struct addrinfo *service_ptr = service_info_list; service_ptr != NULL; service_ptr = service_ptr->ai_next) { addr_list.emplace_back(SocketAddress(service_ptr)); } } if (service_info_list) ::freeaddrinfo(service_info_list); return addr_list; } bool SocketAddress::SetToLocalhost(sa_family_t family, uint16_t port) { switch (family) { case AF_INET: SetFamily(AF_INET); if (SetPort(port)) { m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); return true; } break; case AF_INET6: SetFamily(AF_INET6); if (SetPort(port)) { m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback; return true; } break; } Clear(); return false; } bool SocketAddress::SetToAnyAddress(sa_family_t family, uint16_t port) { switch (family) { case AF_INET: SetFamily(AF_INET); if (SetPort(port)) { m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_ANY); return true; } break; case AF_INET6: SetFamily(AF_INET6); if (SetPort(port)) { m_socket_addr.sa_ipv6.sin6_addr = in6addr_any; return true; } break; } Clear(); return false; } bool SocketAddress::IsAnyAddr() const { return (GetFamily() == AF_INET) ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_ANY) : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_any, 16); } bool SocketAddress::operator==(const SocketAddress &rhs) const { if (GetFamily() != rhs.GetFamily()) return false; if (GetLength() != rhs.GetLength()) return false; switch (GetFamily()) { case AF_INET: return m_socket_addr.sa_ipv4.sin_addr.s_addr == rhs.m_socket_addr.sa_ipv4.sin_addr.s_addr; case AF_INET6: return 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &rhs.m_socket_addr.sa_ipv6.sin6_addr, 16); } return false; } bool SocketAddress::operator!=(const SocketAddress &rhs) const { return !(*this == rhs); } Index: vendor/lldb/dist/source/Host/freebsd/Host.cpp =================================================================== --- vendor/lldb/dist/source/Host/freebsd/Host.cpp (revision 317454) +++ vendor/lldb/dist/source/Host/freebsd/Host.cpp (revision 317455) @@ -1,245 +1,253 @@ //===-- source/Host/freebsd/Host.cpp ------------------------------*- C++ //-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #include #include #include #include #include #include #include #include #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/NameMatches.h" #include "llvm/Support/Host.h" extern "C" { extern char **environ; } using namespace lldb; using namespace lldb_private; static bool GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr, ProcessInstanceInfo &process_info) { - if (process_info.ProcessIDIsValid()) { - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, - (int)process_info.GetProcessID()}; + if (!process_info.ProcessIDIsValid()) + return false; - char arg_data[8192]; - size_t arg_data_size = sizeof(arg_data); - if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) == 0) { - DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(), - sizeof(void *)); - lldb::offset_t offset = 0; - const char *cstr; + int pid = process_info.GetProcessID(); - cstr = data.GetCStr(&offset); - if (cstr) { - process_info.GetExecutableFile().SetFile(cstr, false); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid}; - if (!(match_info_ptr == NULL || - NameMatches( - process_info.GetExecutableFile().GetFilename().GetCString(), - match_info_ptr->GetNameMatchType(), - match_info_ptr->GetProcessInfo().GetName()))) - return false; + char arg_data[8192]; + size_t arg_data_size = sizeof(arg_data); + if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0) + return false; - Args &proc_args = process_info.GetArguments(); - while (1) { - const uint8_t *p = data.PeekData(offset, 1); - while ((p != NULL) && (*p == '\0') && offset < arg_data_size) { - ++offset; - p = data.PeekData(offset, 1); - } - if (p == NULL || offset >= arg_data_size) - return true; + DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(), + sizeof(void *)); + lldb::offset_t offset = 0; + const char *cstr; - cstr = data.GetCStr(&offset); - if (cstr) - proc_args.AppendArgument(llvm::StringRef(cstr)); - else - return true; - } - } + cstr = data.GetCStr(&offset); + if (!cstr) + return false; + + process_info.GetExecutableFile().SetFile(cstr, false); + + if (!(match_info_ptr == NULL || + NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName()))) + return false; + + Args &proc_args = process_info.GetArguments(); + while (1) { + const uint8_t *p = data.PeekData(offset, 1); + while ((p != NULL) && (*p == '\0') && offset < arg_data_size) { + ++offset; + p = data.PeekData(offset, 1); } + if (p == NULL || offset >= arg_data_size) + break; + + cstr = data.GetCStr(&offset); + if (!cstr) + break; + + proc_args.AppendArgument(llvm::StringRef(cstr)); } - return false; + + return true; } static bool GetFreeBSDProcessCPUType(ProcessInstanceInfo &process_info) { if (process_info.ProcessIDIsValid()) { process_info.GetArchitecture() = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); return true; } process_info.GetArchitecture().Clear(); return false; } static bool GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) { struct kinfo_proc proc_kinfo; size_t proc_kinfo_size; + const int pid = process_info.GetProcessID(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - if (process_info.ProcessIDIsValid()) { - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, - (int)process_info.GetProcessID()}; - proc_kinfo_size = sizeof(struct kinfo_proc); + if (!process_info.ProcessIDIsValid()) + goto error; - if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) { - if (proc_kinfo_size > 0) { - process_info.SetParentProcessID(proc_kinfo.ki_ppid); - process_info.SetUserID(proc_kinfo.ki_ruid); - process_info.SetGroupID(proc_kinfo.ki_rgid); - process_info.SetEffectiveUserID(proc_kinfo.ki_uid); - if (proc_kinfo.ki_ngroups > 0) - process_info.SetEffectiveGroupID(proc_kinfo.ki_groups[0]); - else - process_info.SetEffectiveGroupID(UINT32_MAX); - return true; - } - } - } + proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) != 0) + goto error; + + if (proc_kinfo_size == 0) + goto error; + + process_info.SetParentProcessID(proc_kinfo.ki_ppid); + process_info.SetUserID(proc_kinfo.ki_ruid); + process_info.SetGroupID(proc_kinfo.ki_rgid); + process_info.SetEffectiveUserID(proc_kinfo.ki_uid); + if (proc_kinfo.ki_ngroups > 0) + process_info.SetEffectiveGroupID(proc_kinfo.ki_groups[0]); + else + process_info.SetEffectiveGroupID(UINT32_MAX); + return true; + +error: process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID); process_info.SetUserID(UINT32_MAX); process_info.SetGroupID(UINT32_MAX); process_info.SetEffectiveUserID(UINT32_MAX); process_info.SetEffectiveGroupID(UINT32_MAX); return false; } uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { + const ::pid_t our_pid = ::getpid(); + const ::uid_t our_uid = ::getuid(); std::vector kinfos; + // Special case, if lldb is being run as root we can attach to anything. + bool all_users = match_info.GetMatchAllUsers() || (our_uid == 0); int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; size_t pid_data_size = 0; if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0) return 0; // Add a few extra in case a few more show up const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; kinfos.resize(estimated_pid_count); pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0) return 0; const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); - bool all_users = match_info.GetMatchAllUsers(); - const ::pid_t our_pid = getpid(); - const uid_t our_uid = getuid(); for (size_t i = 0; i < actual_pid_count; i++) { const struct kinfo_proc &kinfo = kinfos[i]; - const bool kinfo_user_matches = (all_users || (kinfo.ki_ruid == our_uid) || - // Special case, if lldb is being run as - // root we can attach to anything. - (our_uid == 0)); - if (kinfo_user_matches == false || // Make sure the user is acceptable - kinfo.ki_pid == our_pid || // Skip this process - kinfo.ki_pid == 0 || // Skip kernel (kernel pid is zero) - kinfo.ki_stat == SZOMB || // Zombies are bad, they like brains... - kinfo.ki_flag & P_TRACED || // Being debugged? - kinfo.ki_flag & P_WEXIT) // Working on exiting + /* Make sure the user is acceptable */ + if (!all_users && kinfo.ki_ruid != our_uid) continue; + if (kinfo.ki_pid == our_pid || // Skip this process + kinfo.ki_pid == 0 || // Skip kernel (kernel pid is 0) + kinfo.ki_stat == SZOMB || // Zombies are bad + kinfo.ki_flag & P_TRACED || // Being debugged? + kinfo.ki_flag & P_WEXIT) // Working on exiting + continue; + // Every thread is a process in FreeBSD, but all the threads of a single - // process - // have the same pid. Do not store the process info in the result list if a - // process - // with given identifier is already registered there. + // process have the same pid. Do not store the process info in the + // result list if a process with given identifier is already registered + // there. bool already_registered = false; for (uint32_t pi = 0; !already_registered && (const int)kinfo.ki_numthreads > 1 && pi < (const uint32_t)process_infos.GetSize(); pi++) already_registered = (process_infos.GetProcessIDAtIndex(pi) == (uint32_t)kinfo.ki_pid); if (already_registered) continue; ProcessInstanceInfo process_info; process_info.SetProcessID(kinfo.ki_pid); process_info.SetParentProcessID(kinfo.ki_ppid); process_info.SetUserID(kinfo.ki_ruid); process_info.SetGroupID(kinfo.ki_rgid); process_info.SetEffectiveUserID(kinfo.ki_svuid); process_info.SetEffectiveGroupID(kinfo.ki_svgid); // Make sure our info matches before we go fetch the name and cpu type if (match_info.Matches(process_info) && GetFreeBSDProcessArgs(&match_info, process_info)) { GetFreeBSDProcessCPUType(process_info); if (match_info.Matches(process_info)) process_infos.Append(process_info); } } return process_infos.GetSize(); } bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { process_info.SetProcessID(pid); if (GetFreeBSDProcessArgs(NULL, process_info)) { // should use libprocstat instead of going right into sysctl? GetFreeBSDProcessCPUType(process_info); GetFreeBSDProcessUserAndGroup(process_info); return true; } process_info.Clear(); return false; } size_t Host::GetEnvironment(StringList &env) { char **host_env = environ; char *env_entry; size_t i; for (i = 0; (env_entry = host_env[i]) != NULL; ++i) env.AppendString(env_entry); return i; } Error Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { return Error("unimplemented"); } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp (revision 317454) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp (revision 317455) @@ -1,2220 +1,2213 @@ //===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ClangExpressionDeclMap.h" #include "ASTDumper.h" #include "ClangASTSource.h" #include "ClangModulesDeclVendor.h" #include "ClangPersistentVariables.h" #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Expression/Materializer.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/Log.h" #include "lldb/lldb-private.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclarationName.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" using namespace lldb; using namespace lldb_private; using namespace clang; namespace { const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars"; } // anonymous namespace ClangExpressionDeclMap::ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, ExecutionContext &exe_ctx) : ClangASTSource(exe_ctx.GetTargetSP()), m_found_entities(), m_struct_members(), m_keep_result_in_memory(keep_result_in_memory), m_result_delegate(result_delegate), m_parser_vars(), m_struct_vars() { EnableStructVars(); } ClangExpressionDeclMap::~ClangExpressionDeclMap() { // Note: The model is now that the parser's AST context and all associated // data does not vanish until the expression has been executed. This means // that valuable lookup data (like namespaces) doesn't vanish, but DidParse(); DisableStructVars(); } bool ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx, Materializer *materializer) { ClangASTMetrics::ClearLocalCounters(); EnableParserVars(); m_parser_vars->m_exe_ctx = exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (exe_ctx.GetFramePtr()) m_parser_vars->m_sym_ctx = exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything); else if (exe_ctx.GetThreadPtr() && exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)) m_parser_vars->m_sym_ctx = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext( lldb::eSymbolContextEverything); else if (exe_ctx.GetProcessPtr()) { m_parser_vars->m_sym_ctx.Clear(true); m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); } else if (target) { m_parser_vars->m_sym_ctx.Clear(true); m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); } if (target) { m_parser_vars->m_persistent_vars = llvm::cast( target->GetPersistentExpressionStateForLanguage(eLanguageTypeC)); if (!target->GetScratchClangASTContext()) return false; } m_parser_vars->m_target_info = GetTargetInfo(); m_parser_vars->m_materializer = materializer; return true; } void ClangExpressionDeclMap::InstallCodeGenerator( clang::ASTConsumer *code_gen) { assert(m_parser_vars); m_parser_vars->m_code_gen = code_gen; } void ClangExpressionDeclMap::DidParse() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) ClangASTMetrics::DumpCounters(log); if (m_parser_vars.get()) { for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); entity_index < num_entities; ++entity_index) { ExpressionVariableSP var_sp( m_found_entities.GetVariableAtIndex(entity_index)); if (var_sp) llvm::cast(var_sp.get()) ->DisableParserVars(GetParserID()); } for (size_t pvar_index = 0, num_pvars = m_parser_vars->m_persistent_vars->GetSize(); pvar_index < num_pvars; ++pvar_index) { ExpressionVariableSP pvar_sp( m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); if (ClangExpressionVariable *clang_var = llvm::dyn_cast(pvar_sp.get())) clang_var->DisableParserVars(GetParserID()); } DisableParserVars(); } } // Interface for IRForTarget ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() { assert(m_parser_vars.get()); TargetInfo ret; ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Process *process = exe_ctx.GetProcessPtr(); if (process) { ret.byte_order = process->GetByteOrder(); ret.address_byte_size = process->GetAddressByteSize(); } else { Target *target = exe_ctx.GetTargetPtr(); if (target) { ret.byte_order = target->GetArchitecture().GetByteOrder(); ret.address_byte_size = target->GetArchitecture().GetAddressByteSize(); } } return ret; } bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl, const ConstString &name, TypeFromParser parser_type, bool is_result, bool is_lvalue) { assert(m_parser_vars.get()); ClangASTContext *ast = llvm::dyn_cast_or_null(parser_type.GetTypeSystem()); if (ast == nullptr) return false; if (m_parser_vars->m_materializer && is_result) { Error err; ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (target == nullptr) return false; ClangASTContext *context(target->GetScratchClangASTContext()); TypeFromUser user_type(m_ast_importer_sp->DeportType( context->getASTContext(), ast->getASTContext(), parser_type.GetOpaqueQualType()), context); uint32_t offset = m_parser_vars->m_materializer->AddResultVariable( user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err); ClangExpressionVariable *var = new ClangExpressionVariable( exe_ctx.GetBestExecutionContextScope(), name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size); m_found_entities.AddNewlyConstructedVariable(var); var->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = var->GetParserVars(GetParserID()); parser_vars->m_named_decl = decl; parser_vars->m_parser_type = parser_type; var->EnableJITVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = var->GetJITVars(GetParserID()); jit_vars->m_offset = offset; return true; } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; Target *target = exe_ctx.GetTargetPtr(); if (target == NULL) return false; ClangASTContext *context(target->GetScratchClangASTContext()); TypeFromUser user_type(m_ast_importer_sp->DeportType( context->getASTContext(), ast->getASTContext(), parser_type.GetOpaqueQualType()), context); if (!user_type.GetOpaqueQualType()) { if (log) log->Printf("Persistent variable's type wasn't copied successfully"); return false; } if (!m_parser_vars->m_target_info.IsValid()) return false; ClangExpressionVariable *var = llvm::cast( m_parser_vars->m_persistent_vars ->CreatePersistentVariable( exe_ctx.GetBestExecutionContextScope(), name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size) .get()); if (!var) return false; var->m_frozen_sp->SetHasCompleteType(); if (is_result) var->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; else var->m_flags |= ClangExpressionVariable::EVKeepInTarget; // explicitly-declared // persistent variables should // persist if (is_lvalue) { var->m_flags |= ClangExpressionVariable::EVIsProgramReference; } else { var->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; var->m_flags |= ClangExpressionVariable::EVNeedsAllocation; } if (m_keep_result_in_memory) { var->m_flags |= ClangExpressionVariable::EVKeepInTarget; } if (log) log->Printf("Created persistent variable with flags 0x%hx", var->m_flags); var->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = var->GetParserVars(GetParserID()); parser_vars->m_named_decl = decl; parser_vars->m_parser_type = parser_type; return true; } bool ClangExpressionDeclMap::AddValueToStruct(const NamedDecl *decl, const ConstString &name, llvm::Value *value, size_t size, lldb::offset_t alignment) { assert(m_struct_vars.get()); assert(m_parser_vars.get()); bool is_persistent_variable = false; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); m_struct_vars->m_struct_laid_out = false; if (ClangExpressionVariable::FindVariableInList(m_struct_members, decl, GetParserID())) return true; ClangExpressionVariable *var(ClangExpressionVariable::FindVariableInList( m_found_entities, decl, GetParserID())); if (!var) { var = ClangExpressionVariable::FindVariableInList( *m_parser_vars->m_persistent_vars, decl, GetParserID()); is_persistent_variable = true; } if (!var) return false; if (log) log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure", static_cast(decl), name.GetCString(), var->GetName().GetCString()); // We know entity->m_parser_vars is valid because we used a parser variable // to find it ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(var)->GetParserVars(GetParserID()); parser_vars->m_llvm_value = value; if (ClangExpressionVariable::JITVars *jit_vars = llvm::cast(var)->GetJITVars(GetParserID())) { // We already laid this out; do not touch if (log) log->Printf("Already placed at 0x%llx", (unsigned long long)jit_vars->m_offset); } llvm::cast(var)->EnableJITVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = llvm::cast(var)->GetJITVars(GetParserID()); jit_vars->m_alignment = alignment; jit_vars->m_size = size; m_struct_members.AddVariable(var->shared_from_this()); if (m_parser_vars->m_materializer) { uint32_t offset = 0; Error err; if (is_persistent_variable) { ExpressionVariableSP var_sp(var->shared_from_this()); offset = m_parser_vars->m_materializer->AddPersistentVariable( var_sp, nullptr, err); } else { if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym) offset = m_parser_vars->m_materializer->AddSymbol(*sym, err); else if (const RegisterInfo *reg_info = var->GetRegisterInfo()) offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err); else if (parser_vars->m_lldb_var) offset = m_parser_vars->m_materializer->AddVariable( parser_vars->m_lldb_var, err); } if (!err.Success()) return false; if (log) log->Printf("Placed at 0x%llx", (unsigned long long)offset); jit_vars->m_offset = offset; // TODO DoStructLayout() should not change this. } return true; } bool ClangExpressionDeclMap::DoStructLayout() { assert(m_struct_vars.get()); if (m_struct_vars->m_struct_laid_out) return true; if (!m_parser_vars->m_materializer) return false; m_struct_vars->m_struct_alignment = m_parser_vars->m_materializer->GetStructAlignment(); m_struct_vars->m_struct_size = m_parser_vars->m_materializer->GetStructByteSize(); m_struct_vars->m_struct_laid_out = true; return true; } bool ClangExpressionDeclMap::GetStructInfo(uint32_t &num_elements, size_t &size, lldb::offset_t &alignment) { assert(m_struct_vars.get()); if (!m_struct_vars->m_struct_laid_out) return false; num_elements = m_struct_members.GetSize(); size = m_struct_vars->m_struct_size; alignment = m_struct_vars->m_struct_alignment; return true; } bool ClangExpressionDeclMap::GetStructElement(const NamedDecl *&decl, llvm::Value *&value, lldb::offset_t &offset, ConstString &name, uint32_t index) { assert(m_struct_vars.get()); if (!m_struct_vars->m_struct_laid_out) return false; if (index >= m_struct_members.GetSize()) return false; ExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); if (!member_sp) return false; ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(member_sp.get()) ->GetParserVars(GetParserID()); ClangExpressionVariable::JITVars *jit_vars = llvm::cast(member_sp.get()) ->GetJITVars(GetParserID()); if (!parser_vars || !jit_vars || !member_sp->GetValueObject()) return false; decl = parser_vars->m_named_decl; value = parser_vars->m_llvm_value; offset = jit_vars->m_offset; name = member_sp->GetName(); return true; } bool ClangExpressionDeclMap::GetFunctionInfo(const NamedDecl *decl, uint64_t &ptr) { ClangExpressionVariable *entity(ClangExpressionVariable::FindVariableInList( m_found_entities, decl, GetParserID())); if (!entity) return false; // We know m_parser_vars is valid since we searched for the variable by // its NamedDecl ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); return true; } addr_t ClangExpressionDeclMap::GetSymbolAddress(Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type, lldb_private::Module *module) { SymbolContextList sc_list; if (module) module->FindSymbolsWithNameAndType(name, symbol_type, sc_list); else target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list); const uint32_t num_matches = sc_list.GetSize(); addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; for (uint32_t i = 0; i < num_matches && (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS); i++) { SymbolContext sym_ctx; sc_list.GetContextAtIndex(i, sym_ctx); const Address sym_address = sym_ctx.symbol->GetAddress(); if (!sym_address.IsValid()) continue; switch (sym_ctx.symbol->GetType()) { case eSymbolTypeCode: case eSymbolTypeTrampoline: symbol_load_addr = sym_address.GetCallableLoadAddress(&target); break; case eSymbolTypeResolver: symbol_load_addr = sym_address.GetCallableLoadAddress(&target, true); break; case eSymbolTypeReExported: { ConstString reexport_name = sym_ctx.symbol->GetReExportedSymbolName(); if (reexport_name) { ModuleSP reexport_module_sp; ModuleSpec reexport_module_spec; reexport_module_spec.GetPlatformFileSpec() = sym_ctx.symbol->GetReExportedSymbolSharedLibrary(); if (reexport_module_spec.GetPlatformFileSpec()) { reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); if (!reexport_module_sp) { reexport_module_spec.GetPlatformFileSpec().GetDirectory().Clear(); reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); } } symbol_load_addr = GetSymbolAddress( target, process, sym_ctx.symbol->GetReExportedSymbolName(), symbol_type, reexport_module_sp.get()); } } break; case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeVariable: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeInvalid: case eSymbolTypeAbsolute: case eSymbolTypeException: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeUndefined: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: symbol_load_addr = sym_address.GetLoadAddress(&target); break; } } if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) { ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime(); if (runtime) { symbol_load_addr = runtime->LookupRuntimeSymbol(name); } } return symbol_load_addr; } addr_t ClangExpressionDeclMap::GetSymbolAddress(const ConstString &name, lldb::SymbolType symbol_type) { assert(m_parser_vars.get()); if (!m_parser_vars->m_exe_ctx.GetTargetPtr()) return false; return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), m_parser_vars->m_exe_ctx.GetProcessPtr(), name, symbol_type); } const Symbol *ClangExpressionDeclMap::FindGlobalDataSymbol( Target &target, const ConstString &name, lldb_private::Module *module) { SymbolContextList sc_list; if (module) module->FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); else target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); const uint32_t matches = sc_list.GetSize(); for (uint32_t i = 0; i < matches; ++i) { SymbolContext sym_ctx; sc_list.GetContextAtIndex(i, sym_ctx); if (sym_ctx.symbol) { const Symbol *symbol = sym_ctx.symbol; const Address sym_address = symbol->GetAddress(); if (sym_address.IsValid()) { switch (symbol->GetType()) { case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeAbsolute: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: if (symbol->GetDemangledNameIsSynthesized()) { // If the demangled name was synthesized, then don't use it // for expressions. Only let the symbol match if the mangled // named matches for these symbols. if (symbol->GetMangled().GetMangledName() != name) break; } return symbol; case eSymbolTypeReExported: { ConstString reexport_name = symbol->GetReExportedSymbolName(); if (reexport_name) { ModuleSP reexport_module_sp; ModuleSpec reexport_module_spec; reexport_module_spec.GetPlatformFileSpec() = symbol->GetReExportedSymbolSharedLibrary(); if (reexport_module_spec.GetPlatformFileSpec()) { reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); if (!reexport_module_sp) { reexport_module_spec.GetPlatformFileSpec() .GetDirectory() .Clear(); reexport_module_sp = target.GetImages().FindFirstModule(reexport_module_spec); } } // Don't allow us to try and resolve a re-exported symbol if it is // the same // as the current symbol if (name == symbol->GetReExportedSymbolName() && module == reexport_module_sp.get()) return NULL; return FindGlobalDataSymbol(target, symbol->GetReExportedSymbolName(), reexport_module_sp.get()); } } break; case eSymbolTypeCode: // We already lookup functions elsewhere case eSymbolTypeVariable: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeTrampoline: case eSymbolTypeInvalid: case eSymbolTypeException: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeUndefined: case eSymbolTypeResolver: break; } } } } return NULL; } lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable( Target &target, ModuleSP &module, const ConstString &name, CompilerDeclContext *namespace_decl, TypeFromUser *type) { VariableList vars; if (module && namespace_decl) module->FindGlobalVariables(name, namespace_decl, true, -1, vars); else target.GetImages().FindGlobalVariables(name, true, -1, vars); if (vars.GetSize()) { if (type) { for (size_t i = 0; i < vars.GetSize(); ++i) { VariableSP var_sp = vars.GetVariableAtIndex(i); if (ClangASTContext::AreTypesSame( *type, var_sp->GetType()->GetFullCompilerType())) return var_sp; } } else { return vars.GetVariableAtIndex(0); } } return VariableSP(); } ClangASTContext *ClangExpressionDeclMap::GetClangASTContext() { StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); if (frame == nullptr) return nullptr; SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); if (sym_ctx.block == nullptr) return nullptr; CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); if (!frame_decl_context) return nullptr; return llvm::dyn_cast_or_null( frame_decl_context.GetTypeSystem()); } // Interface for ClangASTSource void ClangExpressionDeclMap::FindExternalVisibleDecls( NameSearchContext &context) { assert(m_ast_context); ClangASTMetrics::RegisterVisibleQuery(); const ConstString name(context.m_decl_name.getAsString().c_str()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (GetImportInProgress()) { if (log && log->GetVerbose()) log->Printf("Ignoring a query during an import"); return; } static unsigned int invocation_id = 0; unsigned int current_id = invocation_id++; if (log) { if (!context.m_decl_context) log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in a NULL DeclContext", current_id, name.GetCString()); else if (const NamedDecl *context_named_decl = dyn_cast(context.m_decl_context)) log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in '%s'", current_id, name.GetCString(), context_named_decl->getNameAsString().c_str()); else log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for " "'%s' in a '%s'", current_id, name.GetCString(), context.m_decl_context->getDeclKindName()); } if (const NamespaceDecl *namespace_context = dyn_cast(context.m_decl_context)) { if (namespace_context->getName().str() == std::string(g_lldb_local_vars_namespace_cstr)) { CompilerDeclContext compiler_decl_ctx( GetClangASTContext(), const_cast(static_cast( context.m_decl_context))); FindExternalVisibleDecls(context, lldb::ModuleSP(), compiler_decl_ctx, current_id); return; } ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer_sp->GetNamespaceMap(namespace_context); if (log && log->GetVerbose()) log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", current_id, static_cast(namespace_map.get()), (int)namespace_map->size()); if (!namespace_map) return; for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); i != e; ++i) { if (log) log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s", current_id, i->second.GetName().AsCString(), i->first->GetFileSpec().GetFilename().GetCString()); FindExternalVisibleDecls(context, i->first, i->second, current_id); } } else if (isa(context.m_decl_context)) { CompilerDeclContext namespace_decl; if (log) log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl, current_id); } - - if (!context.m_found.variable && !context.m_found.local_vars_nsp) - ClangASTSource::FindExternalVisibleDecls(context); + + ClangASTSource::FindExternalVisibleDecls(context); } void ClangExpressionDeclMap::FindExternalVisibleDecls( NameSearchContext &context, lldb::ModuleSP module_sp, CompilerDeclContext &namespace_decl, unsigned int current_id) { assert(m_ast_context); std::function MaybeRegisterFunctionBody = [this](clang::FunctionDecl *copied_function_decl) { if (copied_function_decl->getBody() && m_parser_vars->m_code_gen) { DeclGroupRef decl_group_ref(copied_function_decl); m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref); } }; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); SymbolContextList sc_list; const ConstString name(context.m_decl_name.getAsString().c_str()); const char *name_unique_cstr = name.GetCString(); if (name_unique_cstr == NULL) return; static ConstString id_name("id"); static ConstString Class_name("Class"); if (name == id_name || name == Class_name) return; // Only look for functions by name out in our symbols if the function // doesn't start with our phony prefix of '$' Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); SymbolContext sym_ctx; if (frame != nullptr) sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); // Try the persistent decls, which take precedence over all else. if (!namespace_decl) { do { if (!target) break; ClangASTContext *scratch_clang_ast_context = target->GetScratchClangASTContext(); if (!scratch_clang_ast_context) break; ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); if (!scratch_ast_context) break; NamedDecl *persistent_decl = m_parser_vars->m_persistent_vars->GetPersistentDecl(name); if (!persistent_decl) break; Decl *parser_persistent_decl = m_ast_importer_sp->CopyDecl( m_ast_context, scratch_ast_context, persistent_decl); if (!parser_persistent_decl) break; NamedDecl *parser_named_decl = dyn_cast(parser_persistent_decl); if (!parser_named_decl) break; if (clang::FunctionDecl *parser_function_decl = llvm::dyn_cast(parser_named_decl)) { MaybeRegisterFunctionBody(parser_function_decl); } if (log) log->Printf(" CEDM::FEVD[%u] Found persistent decl %s", current_id, name.GetCString()); context.AddNamedDecl(parser_named_decl); } while (0); } if (name_unique_cstr[0] == '$' && !namespace_decl) { static ConstString g_lldb_class_name("$__lldb_class"); if (name == g_lldb_class_name) { // Clang is looking for the type of "this" if (frame == NULL) return; // Find the block that defines the function represented by "sym_ctx" Block *function_block = sym_ctx.GetFunctionBlock(); if (!function_block) return; CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); if (!function_decl_ctx) return; clang::CXXMethodDecl *method_decl = ClangASTContext::DeclContextGetAsCXXMethodDecl(function_decl_ctx); if (method_decl) { clang::CXXRecordDecl *class_decl = method_decl->getParent(); QualType class_qual_type(class_decl->getTypeForDecl(), 0); TypeFromUser class_user_type( class_qual_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&class_decl->getASTContext())); if (log) { ASTDumper ast_dumper(class_qual_type); log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); } AddThisType(context, class_user_type, current_id); if (method_decl->isInstance()) { // self is a pointer to the object QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); TypeFromUser self_user_type( class_pointer_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } } else { // This branch will get hit if we are executing code in the context of a // function that // claims to have an object pointer (through DW_AT_object_pointer?) but // is not formally a // method of the class. In that case, just look up the "this" variable // in the current // scope and use its type. // FIXME: This code is formally correct, but clang doesn't currently // emit DW_AT_object_pointer // for C++ so it hasn't actually been tested. VariableList *vars = frame->GetVariableList(false); lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); if (this_var && this_var->IsInScope(frame) && this_var->LocationIsValidForFrame(frame)) { Type *this_type = this_var->GetType(); if (!this_type) return; TypeFromUser pointee_type = this_type->GetForwardCompilerType().GetPointeeType(); if (pointee_type.IsValid()) { if (log) { ASTDumper ast_dumper(pointee_type); log->Printf(" FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); } AddThisType(context, pointee_type, current_id); TypeFromUser this_user_type(this_type->GetFullCompilerType()); m_struct_vars->m_object_pointer_type = this_user_type; return; } } } return; } static ConstString g_lldb_objc_class_name("$__lldb_objc_class"); if (name == g_lldb_objc_class_name) { // Clang is looking for the type of "*self" if (!frame) return; SymbolContext sym_ctx = frame->GetSymbolContext( lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); // Find the block that defines the function represented by "sym_ctx" Block *function_block = sym_ctx.GetFunctionBlock(); if (!function_block) return; CompilerDeclContext function_decl_ctx = function_block->GetDeclContext(); if (!function_decl_ctx) return; clang::ObjCMethodDecl *method_decl = ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx); if (method_decl) { ObjCInterfaceDecl *self_interface = method_decl->getClassInterface(); if (!self_interface) return; const clang::Type *interface_type = self_interface->getTypeForDecl(); if (!interface_type) return; // This is unlikely, but we have seen crashes where this // occurred TypeFromUser class_user_type( QualType(interface_type, 0).getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); if (log) { ASTDumper ast_dumper(interface_type); log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); } AddOneType(context, class_user_type, current_id); if (method_decl->isInstanceMethod()) { // self is a pointer to the object QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType( QualType(interface_type, 0)); TypeFromUser self_user_type( class_pointer_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } else { // self is a Class pointer QualType class_type = method_decl->getASTContext().getObjCClassType(); TypeFromUser self_user_type( class_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&method_decl->getASTContext())); m_struct_vars->m_object_pointer_type = self_user_type; } return; } else { // This branch will get hit if we are executing code in the context of a // function that // claims to have an object pointer (through DW_AT_object_pointer?) but // is not formally a // method of the class. In that case, just look up the "self" variable // in the current // scope and use its type. VariableList *vars = frame->GetVariableList(false); lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); if (self_var && self_var->IsInScope(frame) && self_var->LocationIsValidForFrame(frame)) { Type *self_type = self_var->GetType(); if (!self_type) return; CompilerType self_clang_type = self_type->GetFullCompilerType(); if (ClangASTContext::IsObjCClassType(self_clang_type)) { return; } else if (ClangASTContext::IsObjCObjectPointerType( self_clang_type)) { self_clang_type = self_clang_type.GetPointeeType(); if (!self_clang_type) return; if (log) { ASTDumper ast_dumper(self_type->GetFullCompilerType()); log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); } TypeFromUser class_user_type(self_clang_type); AddOneType(context, class_user_type, current_id); TypeFromUser self_user_type(self_type->GetFullCompilerType()); m_struct_vars->m_object_pointer_type = self_user_type; return; } } } return; } if (name == ConstString(g_lldb_local_vars_namespace_cstr)) { CompilerDeclContext frame_decl_context = sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext() : CompilerDeclContext(); if (frame_decl_context) { ClangASTContext *ast = llvm::dyn_cast_or_null( frame_decl_context.GetTypeSystem()); if (ast) { clang::NamespaceDecl *namespace_decl = ClangASTContext::GetUniqueNamespaceDeclaration( m_ast_context, name_unique_cstr, nullptr); if (namespace_decl) { context.AddNamedDecl(namespace_decl); clang::DeclContext *clang_decl_ctx = clang::Decl::castToDeclContext(namespace_decl); clang_decl_ctx->setHasExternalVisibleStorage(true); context.m_found.local_vars_nsp = true; } } } return; } // any other $__lldb names should be weeded out now if (!::strncmp(name_unique_cstr, "$__lldb", sizeof("$__lldb") - 1)) return; ExpressionVariableSP pvar_sp( m_parser_vars->m_persistent_vars->GetVariable(name)); if (pvar_sp) { AddOneVariable(context, pvar_sp, current_id); return; } const char *reg_name(&name.GetCString()[1]); if (m_parser_vars->m_exe_ctx.GetRegisterContext()) { const RegisterInfo *reg_info( m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName( reg_name)); if (reg_info) { if (log) log->Printf(" CEDM::FEVD[%u] Found register %s", current_id, reg_info->name); AddOneRegister(context, reg_info, current_id); } } } else { ValueObjectSP valobj; VariableSP var; bool local_var_lookup = !namespace_decl || (namespace_decl.GetName() == ConstString(g_lldb_local_vars_namespace_cstr)); if (frame && local_var_lookup) { CompilerDeclContext compiler_decl_context = sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext() : CompilerDeclContext(); if (compiler_decl_context) { // Make sure that the variables are parsed so that we have the // declarations. VariableListSP vars = frame->GetInScopeVariableList(true); for (size_t i = 0; i < vars->GetSize(); i++) vars->GetVariableAtIndex(i)->GetDecl(); // Search for declarations matching the name. Do not include imported // decls // in the search if we are looking for decls in the artificial namespace // $__lldb_local_vars. std::vector found_decls = compiler_decl_context.FindDeclByName(name, namespace_decl.IsValid()); bool variable_found = false; for (CompilerDecl decl : found_decls) { for (size_t vi = 0, ve = vars->GetSize(); vi != ve; ++vi) { VariableSP candidate_var = vars->GetVariableAtIndex(vi); if (candidate_var->GetDecl() == decl) { var = candidate_var; break; } } - if (var) { + if (var && !variable_found) { variable_found = true; valobj = ValueObjectVariable::Create(frame, var); AddOneVariable(context, var, valobj, current_id); context.m_found.variable = true; } } if (variable_found) return; } } if (target) { var = FindGlobalVariable(*target, module_sp, name, &namespace_decl, NULL); if (var) { valobj = ValueObjectVariable::Create(target, var); AddOneVariable(context, var, valobj, current_id); context.m_found.variable = true; return; } } std::vector decls_from_modules; if (target) { if (ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor()) { decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules); } } - if (!context.m_found.variable) { - const bool include_inlines = false; - const bool append = false; + const bool include_inlines = false; + const bool append = false; - if (namespace_decl && module_sp) { - const bool include_symbols = false; + if (namespace_decl && module_sp) { + const bool include_symbols = false; - module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase, - include_symbols, include_inlines, append, - sc_list); - } else if (target && !namespace_decl) { - const bool include_symbols = true; + module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase, + include_symbols, include_inlines, append, + sc_list); + } else if (target && !namespace_decl) { + const bool include_symbols = true; - // TODO Fix FindFunctions so that it doesn't return - // instance methods for eFunctionNameTypeBase. + // TODO Fix FindFunctions so that it doesn't return + // instance methods for eFunctionNameTypeBase. - target->GetImages().FindFunctions(name, eFunctionNameTypeFull, - include_symbols, include_inlines, - append, sc_list); - } + target->GetImages().FindFunctions(name, eFunctionNameTypeFull, + include_symbols, include_inlines, + append, sc_list); + } - // If we found more than one function, see if we can use the - // frame's decl context to remove functions that are shadowed - // by other functions which match in type but are nearer in scope. - // - // AddOneFunction will not add a function whose type has already been - // added, so if there's another function in the list with a matching - // type, check to see if their decl context is a parent of the current - // frame's or was imported via a and using statement, and pick the - // best match according to lookup rules. - if (sc_list.GetSize() > 1) { - // Collect some info about our frame's context. - StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); - SymbolContext frame_sym_ctx; - if (frame != nullptr) - frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | - lldb::eSymbolContextBlock); - CompilerDeclContext frame_decl_context = - frame_sym_ctx.block != nullptr - ? frame_sym_ctx.block->GetDeclContext() - : CompilerDeclContext(); + // If we found more than one function, see if we can use the + // frame's decl context to remove functions that are shadowed + // by other functions which match in type but are nearer in scope. + // + // AddOneFunction will not add a function whose type has already been + // added, so if there's another function in the list with a matching + // type, check to see if their decl context is a parent of the current + // frame's or was imported via a and using statement, and pick the + // best match according to lookup rules. + if (sc_list.GetSize() > 1) { + // Collect some info about our frame's context. + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + SymbolContext frame_sym_ctx; + if (frame != nullptr) + frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + CompilerDeclContext frame_decl_context = + frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext() + : CompilerDeclContext(); - // We can't do this without a compiler decl context for our frame. - if (frame_decl_context) { - clang::DeclContext *frame_decl_ctx = - (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext(); - ClangASTContext *ast = llvm::dyn_cast_or_null( - frame_decl_context.GetTypeSystem()); + // We can't do this without a compiler decl context for our frame. + if (frame_decl_context) { + clang::DeclContext *frame_decl_ctx = + (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext(); + ClangASTContext *ast = llvm::dyn_cast_or_null( + frame_decl_context.GetTypeSystem()); - // Structure to hold the info needed when comparing function - // declarations. - struct FuncDeclInfo { - ConstString m_name; - CompilerType m_copied_type; - uint32_t m_decl_lvl; - SymbolContext m_sym_ctx; - }; + // Structure to hold the info needed when comparing function + // declarations. + struct FuncDeclInfo { + ConstString m_name; + CompilerType m_copied_type; + uint32_t m_decl_lvl; + SymbolContext m_sym_ctx; + }; - // First, symplify things by looping through the symbol contexts - // to remove unwanted functions and separate out the functions we - // want to compare and prune into a separate list. - // Cache the info needed about the function declarations in a - // vector for efficiency. - SymbolContextList sc_sym_list; - uint32_t num_indices = sc_list.GetSize(); - std::vector fdi_cache; - fdi_cache.reserve(num_indices); - for (uint32_t index = 0; index < num_indices; ++index) { - FuncDeclInfo fdi; - SymbolContext sym_ctx; - sc_list.GetContextAtIndex(index, sym_ctx); + // First, symplify things by looping through the symbol contexts + // to remove unwanted functions and separate out the functions we + // want to compare and prune into a separate list. + // Cache the info needed about the function declarations in a + // vector for efficiency. + SymbolContextList sc_sym_list; + uint32_t num_indices = sc_list.GetSize(); + std::vector fdi_cache; + fdi_cache.reserve(num_indices); + for (uint32_t index = 0; index < num_indices; ++index) { + FuncDeclInfo fdi; + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); - // We don't know enough about symbols to compare them, - // but we should keep them in the list. - Function *function = sym_ctx.function; - if (!function) { - sc_sym_list.Append(sym_ctx); - continue; - } - // Filter out functions without declaration contexts, as well as - // class/instance methods, since they'll be skipped in the - // code that follows anyway. - CompilerDeclContext func_decl_context = function->GetDeclContext(); - if (!func_decl_context || - func_decl_context.IsClassMethod(nullptr, nullptr, nullptr)) - continue; - // We can only prune functions for which we can copy the type. - CompilerType func_clang_type = - function->GetType()->GetFullCompilerType(); - CompilerType copied_func_type = GuardedCopyType(func_clang_type); - if (!copied_func_type) { - sc_sym_list.Append(sym_ctx); - continue; - } + // We don't know enough about symbols to compare them, + // but we should keep them in the list. + Function *function = sym_ctx.function; + if (!function) { + sc_sym_list.Append(sym_ctx); + continue; + } + // Filter out functions without declaration contexts, as well as + // class/instance methods, since they'll be skipped in the + // code that follows anyway. + CompilerDeclContext func_decl_context = function->GetDeclContext(); + if (!func_decl_context || + func_decl_context.IsClassMethod(nullptr, nullptr, nullptr)) + continue; + // We can only prune functions for which we can copy the type. + CompilerType func_clang_type = + function->GetType()->GetFullCompilerType(); + CompilerType copied_func_type = GuardedCopyType(func_clang_type); + if (!copied_func_type) { + sc_sym_list.Append(sym_ctx); + continue; + } - fdi.m_sym_ctx = sym_ctx; - fdi.m_name = function->GetName(); - fdi.m_copied_type = copied_func_type; - fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL; - if (fdi.m_copied_type && func_decl_context) { - // Call CountDeclLevels to get the number of parent scopes we - // have to look through before we find the function declaration. - // When comparing functions of the same type, the one with a - // lower count will be closer to us in the lookup scope and - // shadows the other. - clang::DeclContext *func_decl_ctx = - (clang::DeclContext *) - func_decl_context.GetOpaqueDeclContext(); - fdi.m_decl_lvl = - ast->CountDeclLevels(frame_decl_ctx, func_decl_ctx, - &fdi.m_name, &fdi.m_copied_type); - } - fdi_cache.emplace_back(fdi); + fdi.m_sym_ctx = sym_ctx; + fdi.m_name = function->GetName(); + fdi.m_copied_type = copied_func_type; + fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL; + if (fdi.m_copied_type && func_decl_context) { + // Call CountDeclLevels to get the number of parent scopes we + // have to look through before we find the function declaration. + // When comparing functions of the same type, the one with a + // lower count will be closer to us in the lookup scope and + // shadows the other. + clang::DeclContext *func_decl_ctx = + (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext(); + fdi.m_decl_lvl = ast->CountDeclLevels( + frame_decl_ctx, func_decl_ctx, &fdi.m_name, &fdi.m_copied_type); } + fdi_cache.emplace_back(fdi); + } - // Loop through the functions in our cache looking for matching types, - // then compare their scope levels to see which is closer. - std::multimap matches; - for (const FuncDeclInfo &fdi : fdi_cache) { - const CompilerType t = fdi.m_copied_type; - auto q = matches.find(t); - if (q != matches.end()) { - if (q->second->m_decl_lvl > fdi.m_decl_lvl) - // This function is closer; remove the old set. - matches.erase(t); - else if (q->second->m_decl_lvl < fdi.m_decl_lvl) - // The functions in our set are closer - skip this one. - continue; - } - matches.insert(std::make_pair(t, &fdi)); + // Loop through the functions in our cache looking for matching types, + // then compare their scope levels to see which is closer. + std::multimap matches; + for (const FuncDeclInfo &fdi : fdi_cache) { + const CompilerType t = fdi.m_copied_type; + auto q = matches.find(t); + if (q != matches.end()) { + if (q->second->m_decl_lvl > fdi.m_decl_lvl) + // This function is closer; remove the old set. + matches.erase(t); + else if (q->second->m_decl_lvl < fdi.m_decl_lvl) + // The functions in our set are closer - skip this one. + continue; } + matches.insert(std::make_pair(t, &fdi)); + } - // Loop through our matches and add their symbol contexts to our list. - SymbolContextList sc_func_list; - for (const auto &q : matches) - sc_func_list.Append(q.second->m_sym_ctx); + // Loop through our matches and add their symbol contexts to our list. + SymbolContextList sc_func_list; + for (const auto &q : matches) + sc_func_list.Append(q.second->m_sym_ctx); - // Rejoin the lists with the functions in front. - sc_list = sc_func_list; - sc_list.Append(sc_sym_list); - } + // Rejoin the lists with the functions in front. + sc_list = sc_func_list; + sc_list.Append(sc_sym_list); } + } - if (sc_list.GetSize()) { - Symbol *extern_symbol = NULL; - Symbol *non_extern_symbol = NULL; + if (sc_list.GetSize()) { + Symbol *extern_symbol = NULL; + Symbol *non_extern_symbol = NULL; - for (uint32_t index = 0, num_indices = sc_list.GetSize(); - index < num_indices; ++index) { - SymbolContext sym_ctx; - sc_list.GetContextAtIndex(index, sym_ctx); + for (uint32_t index = 0, num_indices = sc_list.GetSize(); + index < num_indices; ++index) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); - if (sym_ctx.function) { - CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); + if (sym_ctx.function) { + CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); - if (!decl_ctx) - continue; + if (!decl_ctx) + continue; - // Filter out class/instance methods. - if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) - continue; + // Filter out class/instance methods. + if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) + continue; - AddOneFunction(context, sym_ctx.function, NULL, current_id); - context.m_found.function_with_type_info = true; - context.m_found.function = true; - } else if (sym_ctx.symbol) { - if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) { - sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target); - if (sym_ctx.symbol == NULL) - continue; - } - - if (sym_ctx.symbol->IsExternal()) - extern_symbol = sym_ctx.symbol; - else - non_extern_symbol = sym_ctx.symbol; + AddOneFunction(context, sym_ctx.function, NULL, current_id); + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } else if (sym_ctx.symbol) { + if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) { + sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target); + if (sym_ctx.symbol == NULL) + continue; } + + if (sym_ctx.symbol->IsExternal()) + extern_symbol = sym_ctx.symbol; + else + non_extern_symbol = sym_ctx.symbol; } + } - if (!context.m_found.function_with_type_info) { - for (clang::NamedDecl *decl : decls_from_modules) { - if (llvm::isa(decl)) { - clang::NamedDecl *copied_decl = - llvm::cast_or_null(m_ast_importer_sp->CopyDecl( - m_ast_context, &decl->getASTContext(), decl)); - if (copied_decl) { - context.AddNamedDecl(copied_decl); - context.m_found.function_with_type_info = true; - } + if (!context.m_found.function_with_type_info) { + for (clang::NamedDecl *decl : decls_from_modules) { + if (llvm::isa(decl)) { + clang::NamedDecl *copied_decl = + llvm::cast_or_null(m_ast_importer_sp->CopyDecl( + m_ast_context, &decl->getASTContext(), decl)); + if (copied_decl) { + context.AddNamedDecl(copied_decl); + context.m_found.function_with_type_info = true; } } } + } - if (!context.m_found.function_with_type_info) { - if (extern_symbol) { - AddOneFunction(context, NULL, extern_symbol, current_id); - context.m_found.function = true; - } else if (non_extern_symbol) { - AddOneFunction(context, NULL, non_extern_symbol, current_id); - context.m_found.function = true; - } + if (!context.m_found.function_with_type_info) { + if (extern_symbol) { + AddOneFunction(context, NULL, extern_symbol, current_id); + context.m_found.function = true; + } else if (non_extern_symbol) { + AddOneFunction(context, NULL, non_extern_symbol, current_id); + context.m_found.function = true; } } + } - if (!context.m_found.function_with_type_info) { - // Try the modules next. + if (!context.m_found.function_with_type_info) { + // Try the modules next. - do { - if (ClangModulesDeclVendor *modules_decl_vendor = - m_target->GetClangModulesDeclVendor()) { - bool append = false; - uint32_t max_matches = 1; - std::vector decls; + do { + if (ClangModulesDeclVendor *modules_decl_vendor = + m_target->GetClangModulesDeclVendor()) { + bool append = false; + uint32_t max_matches = 1; + std::vector decls; - if (!modules_decl_vendor->FindDecls(name, append, max_matches, - decls)) - break; + if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls)) + break; - clang::NamedDecl *const decl_from_modules = decls[0]; + clang::NamedDecl *const decl_from_modules = decls[0]; - if (llvm::isa(decl_from_modules)) { - if (log) { - log->Printf(" CAS::FEVD[%u] Matching function found for " - "\"%s\" in the modules", - current_id, name.GetCString()); - } + if (llvm::isa(decl_from_modules)) { + if (log) { + log->Printf(" CAS::FEVD[%u] Matching function found for " + "\"%s\" in the modules", + current_id, name.GetCString()); + } - clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( - m_ast_context, &decl_from_modules->getASTContext(), - decl_from_modules); - clang::FunctionDecl *copied_function_decl = - copied_decl ? dyn_cast(copied_decl) - : nullptr; + clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( + m_ast_context, &decl_from_modules->getASTContext(), + decl_from_modules); + clang::FunctionDecl *copied_function_decl = + copied_decl ? dyn_cast(copied_decl) + : nullptr; - if (!copied_function_decl) { - if (log) - log->Printf(" CAS::FEVD[%u] - Couldn't export a function " - "declaration from the modules", - current_id); + if (!copied_function_decl) { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a function " + "declaration from the modules", + current_id); - break; - } + break; + } - MaybeRegisterFunctionBody(copied_function_decl); + MaybeRegisterFunctionBody(copied_function_decl); - context.AddNamedDecl(copied_function_decl); + context.AddNamedDecl(copied_function_decl); - context.m_found.function_with_type_info = true; - context.m_found.function = true; - } else if (llvm::isa(decl_from_modules)) { - if (log) { - log->Printf(" CAS::FEVD[%u] Matching variable found for " - "\"%s\" in the modules", - current_id, name.GetCString()); - } + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } else if (llvm::isa(decl_from_modules)) { + if (log) { + log->Printf(" CAS::FEVD[%u] Matching variable found for " + "\"%s\" in the modules", + current_id, name.GetCString()); + } - clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( - m_ast_context, &decl_from_modules->getASTContext(), - decl_from_modules); - clang::VarDecl *copied_var_decl = - copied_decl ? dyn_cast_or_null(copied_decl) - : nullptr; + clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl( + m_ast_context, &decl_from_modules->getASTContext(), + decl_from_modules); + clang::VarDecl *copied_var_decl = + copied_decl ? dyn_cast_or_null(copied_decl) + : nullptr; - if (!copied_var_decl) { - if (log) - log->Printf(" CAS::FEVD[%u] - Couldn't export a variable " - "declaration from the modules", - current_id); + if (!copied_var_decl) { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a variable " + "declaration from the modules", + current_id); - break; - } + break; + } - context.AddNamedDecl(copied_var_decl); + context.AddNamedDecl(copied_var_decl); - context.m_found.variable = true; - } + context.m_found.variable = true; } - } while (0); - } + } + } while (0); + } - if (target && !context.m_found.variable && !namespace_decl) { - // We couldn't find a non-symbol variable for this. Now we'll hunt for - // a generic - // data symbol, and -- if it is found -- treat it as a variable. + if (target && !context.m_found.variable && !namespace_decl) { + // We couldn't find a non-symbol variable for this. Now we'll hunt for + // a generic + // data symbol, and -- if it is found -- treat it as a variable. - const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); + const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); - if (data_symbol) { - std::string warning("got name from symbols: "); - warning.append(name.AsCString()); - const unsigned diag_id = - m_ast_context->getDiagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Level::Warning, "%0"); - m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str(); - AddOneGenericVariable(context, *data_symbol, current_id); - context.m_found.variable = true; - } + if (data_symbol) { + std::string warning("got name from symbols: "); + warning.append(name.AsCString()); + const unsigned diag_id = + m_ast_context->getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Level::Warning, "%0"); + m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str(); + AddOneGenericVariable(context, *data_symbol, current_id); + context.m_found.variable = true; } } } } // static opaque_compiler_type_t // MaybePromoteToBlockPointerType //( // ASTContext *ast_context, // opaque_compiler_type_t candidate_type //) //{ // if (!candidate_type) // return candidate_type; // // QualType candidate_qual_type = QualType::getFromOpaquePtr(candidate_type); // // const PointerType *candidate_pointer_type = // dyn_cast(candidate_qual_type); // // if (!candidate_pointer_type) // return candidate_type; // // QualType pointee_qual_type = candidate_pointer_type->getPointeeType(); // // const RecordType *pointee_record_type = // dyn_cast(pointee_qual_type); // // if (!pointee_record_type) // return candidate_type; // // RecordDecl *pointee_record_decl = pointee_record_type->getDecl(); // // if (!pointee_record_decl->isRecord()) // return candidate_type; // // if // (!pointee_record_decl->getName().startswith(llvm::StringRef("__block_literal_"))) // return candidate_type; // // QualType generic_function_type = // ast_context->getFunctionNoProtoType(ast_context->UnknownAnyTy); // QualType block_pointer_type = // ast_context->getBlockPointerType(generic_function_type); // // return block_pointer_type.getAsOpaquePtr(); //} bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var, lldb_private::Value &var_location, TypeFromUser *user_type, TypeFromParser *parser_type) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Type *var_type = var->GetType(); if (!var_type) { if (log) log->PutCString("Skipped a definition because it has no type"); return false; } CompilerType var_clang_type = var_type->GetFullCompilerType(); if (!var_clang_type) { if (log) log->PutCString("Skipped a definition because it has no Clang type"); return false; } ClangASTContext *clang_ast = llvm::dyn_cast_or_null( var_type->GetForwardCompilerType().GetTypeSystem()); if (!clang_ast) { if (log) log->PutCString("Skipped a definition because it has no Clang AST"); return false; } ASTContext *ast = clang_ast->getASTContext(); if (!ast) { if (log) log->PutCString( "There is no AST context for the current execution context"); return false; } // var_clang_type = MaybePromoteToBlockPointerType (ast, var_clang_type); DWARFExpression &var_location_expr = var->LocationExpression(); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); Error err; if (var->GetLocationIsConstantValueData()) { DataExtractor const_value_extractor; if (var_location_expr.GetExpressionData(const_value_extractor)) { var_location = Value(const_value_extractor.GetDataStart(), const_value_extractor.GetByteSize()); var_location.SetValueType(Value::eValueTypeHostAddress); } else { if (log) log->Printf("Error evaluating constant variable: %s", err.AsCString()); return false; } } CompilerType type_to_use = GuardedCopyType(var_clang_type); if (!type_to_use) { if (log) log->Printf( "Couldn't copy a variable's type into the parser's AST context"); return false; } if (parser_type) *parser_type = TypeFromParser(type_to_use); if (var_location.GetContextType() == Value::eContextTypeInvalid) var_location.SetCompilerType(type_to_use); if (var_location.GetValueType() == Value::eValueTypeFileAddress) { SymbolContext var_sc; var->CalculateSymbolContext(&var_sc); if (!var_sc.module_sp) return false; Address so_addr(var_location.GetScalar().ULongLong(), var_sc.module_sp->GetSectionList()); lldb::addr_t load_addr = so_addr.GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { var_location.GetScalar() = load_addr; var_location.SetValueType(Value::eValueTypeLoadAddress); } } if (user_type) *user_type = TypeFromUser(var_clang_type); return true; } void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, VariableSP var, ValueObjectSP valobj, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); TypeFromUser ut; TypeFromParser pt; Value var_location; if (!GetVariableValue(var, var_location, &ut, &pt)) return; clang::QualType parser_opaque_type = QualType::getFromOpaquePtr(pt.GetOpaqueQualType()); if (parser_opaque_type.isNull()) return; if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) { if (const TagType *tag_type = dyn_cast(parser_type)) CompleteType(tag_type->getDecl()); if (const ObjCObjectPointerType *objc_object_ptr_type = dyn_cast(parser_type)) CompleteType(objc_object_ptr_type->getInterfaceDecl()); } bool is_reference = pt.IsReferenceType(); NamedDecl *var_decl = NULL; if (is_reference) var_decl = context.AddVarDecl(pt); else var_decl = context.AddVarDecl(pt.GetLValueReferenceType()); std::string decl_name(context.m_decl_name.getAsString()); ConstString entity_name(decl_name.c_str()); ClangExpressionVariable *entity(new ClangExpressionVariable(valobj)); m_found_entities.AddNewlyConstructedVariable(entity); assert(entity); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); parser_vars->m_parser_type = pt; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value = var_location; parser_vars->m_lldb_var = var; if (is_reference) entity->m_flags |= ClangExpressionVariable::EVTypeIsReference; if (log) { ASTDumper orig_dumper(ut.GetOpaqueQualType()); ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)", current_id, decl_name.c_str(), ast_dumper.GetCString(), orig_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, ExpressionVariableSP &pvar_sp, unsigned int current_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); TypeFromUser user_type( llvm::cast(pvar_sp.get())->GetTypeFromUser()); TypeFromParser parser_type(GuardedCopyType(user_type)); if (!parser_type.GetOpaqueQualType()) { if (log) log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s", current_id, pvar_sp->GetName().GetCString()); return; } NamedDecl *var_decl = context.AddVarDecl(parser_type.GetLValueReferenceType()); llvm::cast(pvar_sp.get()) ->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(pvar_sp.get()) ->GetParserVars(GetParserID()); parser_vars->m_parser_type = parser_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value.Clear(); if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id, pvar_sp->GetName().GetCString(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); if (target == NULL) return; ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); TypeFromUser user_type( ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid) .GetPointerType() .GetLValueReferenceType()); TypeFromParser parser_type( ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid) .GetPointerType() .GetLValueReferenceType()); NamedDecl *var_decl = context.AddVarDecl(parser_type); std::string decl_name(context.m_decl_name.getAsString()); ConstString entity_name(decl_name.c_str()); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), entity_name, user_type, m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); const Address symbol_address = symbol.GetAddress(); lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target); // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, // user_type.GetOpaqueQualType()); parser_vars->m_lldb_value.SetCompilerType(user_type); parser_vars->m_lldb_value.GetScalar() = symbol_load_addr; parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); parser_vars->m_parser_type = parser_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_sym = &symbol; if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id, decl_name.c_str(), ast_dumper.GetCString()); } } bool ClangExpressionDeclMap::ResolveUnknownTypes() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); ClangASTContext *scratch_ast_context = target->GetScratchClangASTContext(); for (size_t index = 0, num_entities = m_found_entities.GetSize(); index < num_entities; ++index) { ExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index); ClangExpressionVariable::ParserVars *parser_vars = llvm::cast(entity.get()) ->GetParserVars(GetParserID()); if (entity->m_flags & ClangExpressionVariable::EVUnknownType) { const NamedDecl *named_decl = parser_vars->m_named_decl; const VarDecl *var_decl = dyn_cast(named_decl); if (!var_decl) { if (log) log->Printf("Entity of unknown type does not have a VarDecl"); return false; } if (log) { ASTDumper ast_dumper(const_cast(var_decl)); log->Printf("Variable of unknown type now has Decl %s", ast_dumper.GetCString()); } QualType var_type = var_decl->getType(); TypeFromParser parser_type( var_type.getAsOpaquePtr(), ClangASTContext::GetASTContext(&var_decl->getASTContext())); lldb::opaque_compiler_type_t copied_type = m_ast_importer_sp->CopyType( scratch_ast_context->getASTContext(), &var_decl->getASTContext(), var_type.getAsOpaquePtr()); if (!copied_type) { if (log) log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't " "import the type for a variable"); return (bool)lldb::ExpressionVariableSP(); } TypeFromUser user_type(copied_type, scratch_ast_context); // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, // user_type.GetOpaqueQualType()); parser_vars->m_lldb_value.SetCompilerType(user_type); parser_vars->m_parser_type = parser_type; entity->SetCompilerType(user_type); entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType); } } return true; } void ClangExpressionDeclMap::AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info, unsigned int current_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); CompilerType clang_type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( m_ast_context, reg_info->encoding, reg_info->byte_size * 8); if (!clang_type) { if (log) log->Printf(" Tried to add a type for %s, but couldn't get one", context.m_decl_name.getAsString().c_str()); return; } TypeFromParser parser_clang_type(clang_type); NamedDecl *var_decl = context.AddVarDecl(parser_clang_type); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); std::string decl_name(context.m_decl_name.getAsString()); entity->SetName(ConstString(decl_name.c_str())); entity->SetRegisterInfo(reg_info); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); parser_vars->m_parser_type = parser_clang_type; parser_vars->m_named_decl = var_decl; parser_vars->m_llvm_value = NULL; parser_vars->m_lldb_value.Clear(); entity->m_flags |= ClangExpressionVariable::EVBareRegister; if (log) { ASTDumper ast_dumper(var_decl); log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id, context.m_decl_name.getAsString().c_str(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context, Function *function, Symbol *symbol, unsigned int current_id) { assert(m_parser_vars.get()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); NamedDecl *function_decl = NULL; Address fun_address; CompilerType function_clang_type; bool is_indirect_function = false; if (function) { Type *function_type = function->GetType(); const auto lang = function->GetCompileUnit()->GetLanguage(); const auto name = function->GetMangled().GetMangledName().AsCString(); const bool extern_c = (Language::LanguageIsC(lang) && !CPlusPlusLanguage::IsCPPMangledName(name)) || (Language::LanguageIsObjC(lang) && !Language::LanguageIsCPlusPlus(lang)); if (!extern_c) { TypeSystem *type_system = function->GetDeclContext().GetTypeSystem(); if (ClangASTContext *src_ast = llvm::dyn_cast(type_system)) { clang::DeclContext *src_decl_context = (clang::DeclContext *)function->GetDeclContext() .GetOpaqueDeclContext(); clang::FunctionDecl *src_function_decl = llvm::dyn_cast_or_null(src_decl_context); if (src_function_decl) { if (clang::FunctionDecl *copied_function_decl = llvm::dyn_cast_or_null( m_ast_importer_sp->CopyDecl(m_ast_context, src_ast->getASTContext(), src_function_decl))) { if (log) { ASTDumper ast_dumper((clang::Decl *)copied_function_decl); StreamString ss; function->DumpSymbolContext(&ss); log->Printf(" CEDM::FEVD[%u] Imported decl for function %s " "(description %s), returned %s", current_id, copied_function_decl->getNameAsString().c_str(), ss.GetData(), ast_dumper.GetCString()); } context.AddNamedDecl(copied_function_decl); return; } else { if (log) { log->Printf(" Failed to import the function decl for '%s'", src_function_decl->getName().str().c_str()); } } } } } if (!function_type) { if (log) log->PutCString(" Skipped a function because it has no type"); return; } function_clang_type = function_type->GetFullCompilerType(); if (!function_clang_type) { if (log) log->PutCString(" Skipped a function because it has no Clang type"); return; } fun_address = function->GetAddressRange().GetBaseAddress(); CompilerType copied_function_type = GuardedCopyType(function_clang_type); if (copied_function_type) { function_decl = context.AddFunDecl(copied_function_type, extern_c); if (!function_decl) { if (log) { log->Printf( " Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}", function_type->GetName().GetCString(), function_type->GetID()); } return; } } else { // We failed to copy the type we found if (log) { log->Printf(" Failed to import the function type '%s' {0x%8.8" PRIx64 "} into the expression parser AST contenxt", function_type->GetName().GetCString(), function_type->GetID()); } return; } } else if (symbol) { fun_address = symbol->GetAddress(); function_decl = context.AddGenericFunDecl(); is_indirect_function = symbol->IsIndirect(); } else { if (log) log->PutCString(" AddOneFunction called with no function and no symbol"); return; } Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); lldb::addr_t load_addr = fun_address.GetCallableLoadAddress(target, is_indirect_function); ClangExpressionVariable *entity(new ClangExpressionVariable( m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), m_parser_vars->m_target_info.byte_order, m_parser_vars->m_target_info.address_byte_size)); m_found_entities.AddNewlyConstructedVariable(entity); std::string decl_name(context.m_decl_name.getAsString()); entity->SetName(ConstString(decl_name.c_str())); entity->SetCompilerType(function_clang_type); entity->EnableParserVars(GetParserID()); ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); if (load_addr != LLDB_INVALID_ADDRESS) { parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); parser_vars->m_lldb_value.GetScalar() = load_addr; } else { // We have to try finding a file address. lldb::addr_t file_addr = fun_address.GetFileAddress(); parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress); parser_vars->m_lldb_value.GetScalar() = file_addr; } parser_vars->m_named_decl = function_decl; parser_vars->m_llvm_value = NULL; if (log) { ASTDumper ast_dumper(function_decl); StreamString ss; fun_address.Dump(&ss, m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); log->Printf( " CEDM::FEVD[%u] Found %s function %s (description %s), returned %s", current_id, (function ? "specific" : "generic"), decl_name.c_str(), ss.GetData(), ast_dumper.GetCString()); } } void ClangExpressionDeclMap::AddThisType(NameSearchContext &context, TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (!copied_clang_type) { if (log) log->Printf( "ClangExpressionDeclMap::AddThisType - Couldn't import the type"); return; } if (copied_clang_type.IsAggregateType() && copied_clang_type.GetCompleteType()) { CompilerType void_clang_type = ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid); CompilerType void_ptr_clang_type = void_clang_type.GetPointerType(); CompilerType method_type = ClangASTContext::CreateFunctionType( m_ast_context, void_clang_type, &void_ptr_clang_type, 1, false, 0); const bool is_virtual = false; const bool is_static = false; const bool is_inline = false; const bool is_explicit = false; const bool is_attr_used = true; const bool is_artificial = false; CXXMethodDecl *method_decl = ClangASTContext::GetASTContext(m_ast_context) ->AddMethodToCXXRecordType( copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); if (log) { ASTDumper method_ast_dumper((clang::Decl *)method_decl); ASTDumper type_ast_dumper(copied_clang_type); log->Printf(" CEDM::AddThisType Added function $__lldb_expr " "(description %s) for this type %s", method_ast_dumper.GetCString(), type_ast_dumper.GetCString()); } } if (!copied_clang_type.IsValid()) return; TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo( QualType::getFromOpaquePtr(copied_clang_type.GetOpaqueQualType())); if (!type_source_info) return; // Construct a typedef type because if "*this" is a templated type we can't // just return ClassTemplateSpecializationDecls in response to name queries. // Using a typedef makes this much more robust. TypedefDecl *typedef_decl = TypedefDecl::Create( *m_ast_context, m_ast_context->getTranslationUnitDecl(), SourceLocation(), SourceLocation(), context.m_decl_name.getAsIdentifierInfo(), type_source_info); if (!typedef_decl) return; context.AddNamedDecl(typedef_decl); return; } void ClangExpressionDeclMap::AddOneType(NameSearchContext &context, TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); if (!copied_clang_type) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) log->Printf( "ClangExpressionDeclMap::AddOneType - Couldn't import the type"); return; } context.AddTypeDecl(copied_clang_type); } Index: vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp (revision 317454) +++ vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp (revision 317455) @@ -1,1071 +1,1071 @@ //===-- ThreadSanitizerRuntime.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ThreadSanitizerRuntime.h" #include "Plugins/Process/Utility/HistoryThread.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/InstrumentationRuntimeStopInfo.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; lldb::InstrumentationRuntimeSP ThreadSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) { return InstrumentationRuntimeSP(new ThreadSanitizerRuntime(process_sp)); } void ThreadSanitizerRuntime::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.", CreateInstance, GetTypeStatic); } void ThreadSanitizerRuntime::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ThreadSanitizerRuntime::GetPluginNameStatic() { return ConstString("ThreadSanitizer"); } lldb::InstrumentationRuntimeType ThreadSanitizerRuntime::GetTypeStatic() { return eInstrumentationRuntimeTypeThreadSanitizer; } ThreadSanitizerRuntime::~ThreadSanitizerRuntime() { Deactivate(); } static constexpr std::chrono::seconds g_retrieve_data_function_timeout(2); const char *thread_sanitizer_retrieve_report_data_prefix = R"( extern "C" { void *__tsan_get_current_report(); int __tsan_get_report_data(void *report, const char **description, int *count, int *stack_count, int *mop_count, int *loc_count, int *mutex_count, int *thread_count, int *unique_tid_count, void **sleep_trace, unsigned long trace_size); int __tsan_get_report_stack(void *report, unsigned long idx, void **trace, unsigned long trace_size); int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, int *size, int *write, int *atomic, void **trace, unsigned long trace_size); int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, void **addr, unsigned long *start, unsigned long *size, int *tid, int *fd, int *suppressable, void **trace, unsigned long trace_size); int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr, int *destroyed, void **trace, unsigned long trace_size); int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id, int *running, const char **name, int *parent_tid, void **trace, unsigned long trace_size); int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid); // TODO: dlsym won't work on Windows. void *dlsym(void* handle, const char* symbol); int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type); } const int REPORT_TRACE_SIZE = 128; const int REPORT_ARRAY_SIZE = 4; struct data { void *report; const char *description; int report_count; void *sleep_trace[REPORT_TRACE_SIZE]; int stack_count; struct { int idx; void *trace[REPORT_TRACE_SIZE]; } stacks[REPORT_ARRAY_SIZE]; int mop_count; struct { int idx; int tid; int size; int write; int atomic; void *addr; void *trace[REPORT_TRACE_SIZE]; } mops[REPORT_ARRAY_SIZE]; int loc_count; struct { int idx; const char *type; void *addr; unsigned long start; unsigned long size; int tid; int fd; int suppressable; void *trace[REPORT_TRACE_SIZE]; const char *object_type; } locs[REPORT_ARRAY_SIZE]; int mutex_count; struct { int idx; unsigned long mutex_id; void *addr; int destroyed; void *trace[REPORT_TRACE_SIZE]; } mutexes[REPORT_ARRAY_SIZE]; int thread_count; struct { int idx; int tid; unsigned long os_id; int running; const char *name; int parent_tid; void *trace[REPORT_TRACE_SIZE]; } threads[REPORT_ARRAY_SIZE]; int unique_tid_count; struct { int idx; int tid; } unique_tids[REPORT_ARRAY_SIZE]; }; )"; const char *thread_sanitizer_retrieve_report_data_command = R"( data t = {0}; ptr__tsan_get_report_loc_object_type = (typeof(ptr__tsan_get_report_loc_object_type))(void *)dlsym((void*)-2 /*RTLD_DEFAULT*/, "__tsan_get_report_loc_object_type"); t.report = __tsan_get_current_report(); __tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE); if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.stack_count; i++) { t.stacks[i].idx = i; __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE); } if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.mop_count; i++) { t.mops[i].idx = i; __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE); } if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.loc_count; i++) { t.locs[i].idx = i; __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE); if (ptr__tsan_get_report_loc_object_type) ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type); } if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.mutex_count; i++) { t.mutexes[i].idx = i; __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE); } if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.thread_count; i++) { t.threads[i].idx = i; __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE); } if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.unique_tid_count; i++) { t.unique_tids[i].idx = i; __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid); } t; )"; static StructuredData::Array * CreateStackTrace(ValueObjectSP o, const std::string &trace_item_name = ".trace") { StructuredData::Array *trace = new StructuredData::Array(); ValueObjectSP trace_value_object = o->GetValueForExpressionPath(trace_item_name.c_str()); size_t count = trace_value_object->GetNumChildren(); for (size_t j = 0; j < count; j++) { addr_t trace_addr = trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0); if (trace_addr == 0) break; trace->AddItem( StructuredData::ObjectSP(new StructuredData::Integer(trace_addr))); } return trace; } static StructuredData::Array *ConvertToStructuredArray( ValueObjectSP return_value_sp, const std::string &items_name, const std::string &count_name, std::function const &callback) { StructuredData::Array *array = new StructuredData::Array(); unsigned int count = return_value_sp->GetValueForExpressionPath(count_name.c_str()) ->GetValueAsUnsigned(0); ValueObjectSP objects = return_value_sp->GetValueForExpressionPath(items_name.c_str()); for (unsigned int i = 0; i < count; i++) { ValueObjectSP o = objects->GetChildAtIndex(i, true); StructuredData::Dictionary *dict = new StructuredData::Dictionary(); callback(o, dict); array->AddItem(StructuredData::ObjectSP(dict)); } return array; } static std::string RetrieveString(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path) { addr_t ptr = return_value_sp->GetValueForExpressionPath(expression_path.c_str()) ->GetValueAsUnsigned(0); std::string str; Error error; process_sp->ReadCStringFromMemory(ptr, str, error); return str; } static void GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data, std::map &thread_id_map) { ConvertToStructuredArray( data, ".threads", ".thread_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { uint64_t thread_id = o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0); uint64_t thread_os_id = o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0); user_id_t lldb_user_id = 0; bool can_update = true; ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID( thread_os_id, can_update); if (lldb_thread) { lldb_user_id = lldb_thread->GetIndexID(); } else { // This isn't a live thread anymore. Ask process to assign a new // Index ID (or return an old one if we've already seen this // thread_os_id). // It will also make sure that no new threads are assigned this Index // ID. lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id); } thread_id_map[thread_id] = lldb_user_id; }); } static user_id_t Renumber(uint64_t id, std::map &thread_id_map) { auto IT = thread_id_map.find(id); if (IT == thread_id_map.end()) return 0; return IT->second; } StructuredData::ObjectSP ThreadSanitizerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) { ProcessSP process_sp = GetProcessSP(); if (!process_sp) return StructuredData::ObjectSP(); ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); if (!frame_sp) return StructuredData::ObjectSP(); EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetTryAllThreads(true); options.SetStopOthers(true); options.SetIgnoreBreakpoints(true); options.SetTimeout(g_retrieve_data_function_timeout); options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix); options.SetAutoApplyFixIts(false); options.SetLanguage(eLanguageTypeObjC_plus_plus); ValueObjectSP main_value; ExecutionContext exe_ctx; Error eval_error; frame_sp->CalculateExecutionContext(exe_ctx); ExpressionResults result = UserExpression::Evaluate( exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "", main_value, eval_error); if (result != eExpressionCompleted) { process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf( "Warning: Cannot evaluate ThreadSanitizer expression:\n%s\n", eval_error.AsCString()); return StructuredData::ObjectSP(); } std::map thread_id_map; GetRenumberedThreadIds(process_sp, main_value, thread_id_map); StructuredData::Dictionary *dict = new StructuredData::Dictionary(); dict->AddStringItem("instrumentation_class", "ThreadSanitizer"); dict->AddStringItem("issue_type", RetrieveString(main_value, process_sp, ".description")); dict->AddIntegerItem("report_count", main_value->GetValueForExpressionPath(".report_count") ->GetValueAsUnsigned(0)); dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace( main_value, ".sleep_trace"))); StructuredData::Array *stacks = ConvertToStructuredArray( main_value, ".stacks", ".stack_count", [thread_sp](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); // "stacks" happen on the current thread dict->AddIntegerItem("thread_id", thread_sp->GetIndexID()); }); dict->AddItem("stacks", StructuredData::ObjectSP(stacks)); StructuredData::Array *mops = ConvertToStructuredArray( main_value, ".mops", ".mop_count", [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "size", o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); dict->AddBooleanItem( "is_write", o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0)); dict->AddBooleanItem( "is_atomic", o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("mops", StructuredData::ObjectSP(mops)); StructuredData::Array *locs = ConvertToStructuredArray( main_value, ".locs", ".loc_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddStringItem("type", RetrieveString(o, process_sp, ".type")); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "start", o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "size", o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "file_descriptor", o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0)); dict->AddIntegerItem("suppressable", o->GetValueForExpressionPath(".suppressable") ->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); dict->AddStringItem("object_type", RetrieveString(o, process_sp, ".object_type")); }); dict->AddItem("locs", StructuredData::ObjectSP(locs)); StructuredData::Array *mutexes = ConvertToStructuredArray( main_value, ".mutexes", ".mutex_count", [](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "mutex_id", o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "destroyed", o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("mutexes", StructuredData::ObjectSP(mutexes)); StructuredData::Array *threads = ConvertToStructuredArray( main_value, ".threads", ".thread_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "thread_os_id", o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "running", o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0)); dict->AddStringItem("name", RetrieveString(o, process_sp, ".name")); dict->AddIntegerItem( "parent_thread_id", Renumber(o->GetValueForExpressionPath(".parent_tid") ->GetValueAsUnsigned(0), thread_id_map)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("threads", StructuredData::ObjectSP(threads)); StructuredData::Array *unique_tids = ConvertToStructuredArray( main_value, ".unique_tids", ".unique_tid_count", [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "tid", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); }); dict->AddItem("unique_tids", StructuredData::ObjectSP(unique_tids)); return StructuredData::ObjectSP(dict); } std::string ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) { std::string description = report->GetAsDictionary() ->GetValueForKey("issue_type") ->GetAsString() ->GetValue(); if (description == "data-race") { return "Data race"; } else if (description == "data-race-vptr") { return "Data race on C++ virtual pointer"; } else if (description == "heap-use-after-free") { return "Use of deallocated memory"; } else if (description == "heap-use-after-free-vptr") { return "Use of deallocated C++ virtual pointer"; } else if (description == "thread-leak") { return "Thread leak"; } else if (description == "locked-mutex-destroy") { return "Destruction of a locked mutex"; } else if (description == "mutex-double-lock") { return "Double lock of a mutex"; } else if (description == "mutex-invalid-access") { return "Use of an uninitialized or destroyed mutex"; } else if (description == "mutex-bad-unlock") { return "Unlock of an unlocked mutex (or by a wrong thread)"; } else if (description == "mutex-bad-read-lock") { return "Read lock of a write locked mutex"; } else if (description == "mutex-bad-read-unlock") { return "Read unlock of a write locked mutex"; } else if (description == "signal-unsafe-call") { return "Signal-unsafe call inside a signal handler"; } else if (description == "errno-in-signal-handler") { return "Overwrite of errno in a signal handler"; } else if (description == "lock-order-inversion") { return "Lock order inversion (potential deadlock)"; } else if (description == "external-race") { return "Race on a library object"; } else if (description == "swift-access-race") { return "Swift access race"; } // for unknown report codes just show the code return description; } static std::string Sprintf(const char *format, ...) { StreamString s; va_list args; va_start(args, format); s.PrintfVarArg(format, args); va_end(args); return s.GetString(); } static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) { lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) return ""; lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); if (!symbol) return ""; std::string sym_name = symbol->GetName().GetCString(); return sym_name; } static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr, Declaration &decl) { lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) return; lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); if (!symbol) return; ConstString sym_name = symbol->GetMangled().GetName( lldb::eLanguageTypeUnknown, Mangled::ePreferMangled); ModuleSP module = symbol->CalculateSymbolContextModule(); if (!module) return; VariableList var_list; module->FindGlobalVariables(sym_name, nullptr, true, 1U, var_list); if (var_list.GetSize() < 1) return; VariableSP var = var_list.GetVariableAtIndex(0); decl = var->GetDeclaration(); } addr_t ThreadSanitizerRuntime::GetFirstNonInternalFramePc( StructuredData::ObjectSP trace, bool skip_one_frame) { ProcessSP process_sp = GetProcessSP(); ModuleSP runtime_module_sp = GetRuntimeModuleSP(); StructuredData::Array *trace_array = trace->GetAsArray(); - for (int i = 0; i < trace_array->GetSize(); i++) { + for (size_t i = 0; i < trace_array->GetSize(); i++) { if (skip_one_frame && i == 0) continue; addr_t addr; if (!trace_array->GetItemAtIndexAsInteger(i, addr)) continue; lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( addr, so_addr)) continue; if (so_addr.GetModule() == runtime_module_sp) continue; return addr; } return 0; } std::string ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) { ProcessSP process_sp = GetProcessSP(); std::string summary = report->GetAsDictionary() ->GetValueForKey("description") ->GetAsString() ->GetValue(); bool skip_one_frame = report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() == "external-race"; addr_t pc = 0; if (report->GetAsDictionary() ->GetValueForKey("mops") ->GetAsArray() ->GetSize() > 0) pc = GetFirstNonInternalFramePc(report->GetAsDictionary() ->GetValueForKey("mops") ->GetAsArray() ->GetItemAtIndex(0) ->GetAsDictionary() ->GetValueForKey("trace"), skip_one_frame); if (report->GetAsDictionary() ->GetValueForKey("stacks") ->GetAsArray() ->GetSize() > 0) pc = GetFirstNonInternalFramePc(report->GetAsDictionary() ->GetValueForKey("stacks") ->GetAsArray() ->GetItemAtIndex(0) ->GetAsDictionary() ->GetValueForKey("trace"), skip_one_frame); if (pc != 0) { summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); } if (report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetSize() > 0) { StructuredData::ObjectSP loc = report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetItemAtIndex(0); std::string object_type = loc->GetAsDictionary() ->GetValueForKey("object_type") ->GetAsString() ->GetValue(); if (!object_type.empty()) { summary = "Race on " + object_type + " object"; } addr_t addr = loc->GetAsDictionary() ->GetValueForKey("address") ->GetAsInteger() ->GetValue(); if (addr == 0) addr = loc->GetAsDictionary() ->GetValueForKey("start") ->GetAsInteger() ->GetValue(); if (addr != 0) { std::string global_name = GetSymbolNameFromAddress(process_sp, addr); if (!global_name.empty()) { summary = summary + " at " + global_name; } else { summary = summary + " at " + Sprintf("0x%llx", addr); } } else { int fd = loc->GetAsDictionary() ->GetValueForKey("file_descriptor") ->GetAsInteger() ->GetValue(); if (fd != 0) { summary = summary + " on file descriptor " + Sprintf("%d", fd); } } } return summary; } addr_t ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) { addr_t result = (addr_t)-1; report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( [&result](StructuredData::Object *o) -> bool { addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); if (addr < result) result = addr; return true; }); return (result == (addr_t)-1) ? 0 : result; } std::string ThreadSanitizerRuntime::GetLocationDescription( StructuredData::ObjectSP report, addr_t &global_addr, std::string &global_name, std::string &filename, uint32_t &line) { std::string result = ""; ProcessSP process_sp = GetProcessSP(); if (report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetSize() > 0) { StructuredData::ObjectSP loc = report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetItemAtIndex(0); std::string type = loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); if (type == "global") { global_addr = loc->GetAsDictionary() ->GetValueForKey("address") ->GetAsInteger() ->GetValue(); global_name = GetSymbolNameFromAddress(process_sp, global_addr); if (!global_name.empty()) { result = Sprintf("'%s' is a global variable (0x%llx)", global_name.c_str(), global_addr); } else { result = Sprintf("0x%llx is a global variable", global_addr); } Declaration decl; GetSymbolDeclarationFromAddress(process_sp, global_addr, decl); if (decl.GetFile()) { filename = decl.GetFile().GetPath(); line = decl.GetLine(); } } else if (type == "heap") { addr_t addr = loc->GetAsDictionary() ->GetValueForKey("start") ->GetAsInteger() ->GetValue(); long size = loc->GetAsDictionary() ->GetValueForKey("size") ->GetAsInteger() ->GetValue(); std::string object_type = loc->GetAsDictionary() ->GetValueForKey("object_type") ->GetAsString() ->GetValue(); if (!object_type.empty()) { result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size, object_type.c_str(), addr); } else { result = Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); } } else if (type == "stack") { int tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is stack of thread %d", tid); } else if (type == "tls") { int tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is TLS of thread %d", tid); } else if (type == "fd") { int fd = loc->GetAsDictionary() ->GetValueForKey("file_descriptor") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is file descriptor %d", fd); } } return result; } bool ThreadSanitizerRuntime::NotifyBreakpointHit( void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) { assert(baton && "null baton"); if (!baton) return false; ThreadSanitizerRuntime *const instance = static_cast(baton); StructuredData::ObjectSP report = instance->RetrieveReportData(context->exe_ctx_ref); std::string stop_reason_description; if (report) { std::string issue_description = instance->FormatDescription(report); report->GetAsDictionary()->AddStringItem("description", issue_description); stop_reason_description = issue_description + " detected"; report->GetAsDictionary()->AddStringItem("stop_description", stop_reason_description); std::string summary = instance->GenerateSummary(report); report->GetAsDictionary()->AddStringItem("summary", summary); addr_t main_address = instance->GetMainRacyAddress(report); report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); addr_t global_addr = 0; std::string global_name = ""; std::string location_filename = ""; uint32_t location_line = 0; std::string location_description = instance->GetLocationDescription( report, global_addr, global_name, location_filename, location_line); report->GetAsDictionary()->AddStringItem("location_description", location_description); if (global_addr != 0) { report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); } if (!global_name.empty()) { report->GetAsDictionary()->AddStringItem("global_name", global_name); } if (location_filename != "") { report->GetAsDictionary()->AddStringItem("location_filename", location_filename); report->GetAsDictionary()->AddIntegerItem("location_line", location_line); } bool all_addresses_are_same = true; report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( [&all_addresses_are_same, main_address](StructuredData::Object *o) -> bool { addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); if (main_address != addr) all_addresses_are_same = false; return true; }); report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", all_addresses_are_same); } ProcessSP process_sp = instance->GetProcessSP(); // Make sure this is the right process if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); if (thread_sp) thread_sp->SetStopInfo( InstrumentationRuntimeStopInfo:: CreateStopReasonWithInstrumentationData( *thread_sp, stop_reason_description, report)); StreamFileSP stream_sp( process_sp->GetTarget().GetDebugger().GetOutputFile()); if (stream_sp) { stream_sp->Printf("ThreadSanitizer report breakpoint hit. Use 'thread " "info -s' to get extended information about the " "report.\n"); } return true; // Return true to stop the target } else return false; // Let target run } const RegularExpression &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() { static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); return regex; } bool ThreadSanitizerRuntime::CheckIfRuntimeIsValid( const lldb::ModuleSP module_sp) { static ConstString g_tsan_get_current_report("__tsan_get_current_report"); const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( g_tsan_get_current_report, lldb::eSymbolTypeAny); return symbol != nullptr; } void ThreadSanitizerRuntime::Activate() { if (IsActive()) return; ProcessSP process_sp = GetProcessSP(); if (!process_sp) return; ConstString symbol_name("__tsan_on_report"); const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( symbol_name, eSymbolTypeCode); if (symbol == NULL) return; if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) return; Target &target = process_sp->GetTarget(); addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); if (symbol_address == LLDB_INVALID_ADDRESS) return; bool internal = true; bool hardware = false; Breakpoint *breakpoint = process_sp->GetTarget() .CreateBreakpoint(symbol_address, internal, hardware) .get(); breakpoint->SetCallback(ThreadSanitizerRuntime::NotifyBreakpointHit, this, true); breakpoint->SetBreakpointKind("thread-sanitizer-report"); SetBreakpointID(breakpoint->GetID()); StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile()); if (stream_sp) { stream_sp->Printf("ThreadSanitizer debugger support is active.\n"); } SetActive(true); } void ThreadSanitizerRuntime::Deactivate() { if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) { ProcessSP process_sp = GetProcessSP(); if (process_sp) { process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); SetBreakpointID(LLDB_INVALID_BREAK_ID); } } SetActive(false); } static std::string GenerateThreadName(const std::string &path, StructuredData::Object *o, StructuredData::ObjectSP main_info) { std::string result = "additional information"; if (path == "mops") { int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue(); int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); bool is_write = o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); bool is_atomic = o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); std::string addr_string = Sprintf(" at 0x%llx", addr); if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same") ->GetBooleanValue()) { addr_string = ""; } if (main_info->GetObjectForDotSeparatedPath("issue_type") ->GetStringValue() == "external-race") { result = Sprintf("%s access by thread %d", is_write ? "mutating" : "read-only", thread_id); } else if (main_info->GetObjectForDotSeparatedPath("issue_type") ->GetStringValue() == "swift-access-race") { result = Sprintf("modifying access by thread %d", thread_id); } else { result = Sprintf("%s%s of size %d%s by thread %d", is_atomic ? "atomic " : "", is_write ? "write" : "read", size, addr_string.c_str(), thread_id); } } if (path == "threads") { int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); result = Sprintf("Thread %d created", thread_id); } if (path == "locs") { std::string type = o->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); int fd = o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue(); if (type == "heap") { result = Sprintf("Heap block allocated by thread %d", thread_id); } else if (type == "fd") { result = Sprintf("File descriptor %d created by thread %t", fd, thread_id); } } if (path == "mutexes") { int mutex_id = o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue(); result = Sprintf("Mutex M%d created", mutex_id); } if (path == "stacks") { int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); result = Sprintf("Thread %d", thread_id); } result[0] = toupper(result[0]); return result; } static void AddThreadsForPath(const std::string &path, ThreadCollectionSP threads, ProcessSP process_sp, StructuredData::ObjectSP info) { info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach( [process_sp, threads, path, info](StructuredData::Object *o) -> bool { std::vector pcs; o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach( [&pcs](StructuredData::Object *pc) -> bool { pcs.push_back(pc->GetAsInteger()->GetValue()); return true; }); if (pcs.size() == 0) return true; StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_os_id"); tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; uint32_t stop_id = 0; bool stop_id_is_valid = false; HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); ThreadSP new_thread_sp(history_thread); new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); // Save this in the Process' ExtendedThreadList so a strong pointer // retains the object process_sp->GetExtendedThreadList().AddThread(new_thread_sp); threads->AddThread(new_thread_sp); return true; }); } lldb::ThreadCollectionSP ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP info) { ThreadCollectionSP threads; threads.reset(new ThreadCollection()); if (info->GetObjectForDotSeparatedPath("instrumentation_class") ->GetStringValue() != "ThreadSanitizer") return threads; ProcessSP process_sp = GetProcessSP(); AddThreadsForPath("stacks", threads, process_sp, info); AddThreadsForPath("mops", threads, process_sp, info); AddThreadsForPath("locs", threads, process_sp, info); AddThreadsForPath("mutexes", threads, process_sp, info); AddThreadsForPath("threads", threads, process_sp, info); return threads; } Index: vendor/lldb/dist/source/Plugins/Language/CPlusPlus/LibCxx.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Language/CPlusPlus/LibCxx.cpp (revision 317454) +++ vendor/lldb/dist/source/Plugins/Language/CPlusPlus/LibCxx.cpp (revision 317455) @@ -1,587 +1,588 @@ //===-- LibCxx.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "LibCxx.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/StringPrinter.h" #include "lldb/DataFormatters/TypeSummary.h" #include "lldb/DataFormatters/VectorIterator.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ProcessStructReader.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Error.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); if (!valobj_sp) return false; ValueObjectSP ptr_sp( valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)); ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath( {ConstString("__cntrl_"), ConstString("__shared_owners_")})); ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath( {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")})); if (!ptr_sp) return false; if (ptr_sp->GetValueAsUnsigned(0) == 0) { stream.Printf("nullptr"); return true; } else { bool print_pointee = false; Error error; ValueObjectSP pointee_sp = ptr_sp->Dereference(error); if (pointee_sp && error.Success()) { if (pointee_sp->DumpPrintableRepresentation( stream, ValueObject::eValueObjectRepresentationStyleSummary, lldb::eFormatInvalid, ValueObject::PrintableRepresentationSpecialCases::eDisable, false)) print_pointee = true; } if (!print_pointee) stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); } if (count_sp) stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0)); if (weakcount_sp) stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0)); return true; } /* (lldb) fr var ibeg --raw --ptr-depth 1 (std::__1::__map_iterator, std::__1::allocator > >, std::__1::__tree_node, std::__1::allocator > >, void *> *, long> >) ibeg = { __i_ = { __ptr_ = 0x0000000100103870 { std::__1::__tree_node_base = { std::__1::__tree_end_node *> = { __left_ = 0x0000000000000000 } __right_ = 0x0000000000000000 __parent_ = 0x00000001001038b0 __is_black_ = true } __value_ = { first = 0 second = { std::string } */ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() { if (valobj_sp) Update(); } bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { m_pair_sp.reset(); m_pair_ptr = nullptr; ValueObjectSP valobj_sp = m_backend.GetSP(); if (!valobj_sp) return false; TargetSP target_sp(valobj_sp->GetTargetSP()); if (!target_sp) return false; if (!valobj_sp) return false; static ConstString g___i_("__i_"); // this must be a ValueObject* because it is a child of the ValueObject we are // producing children for // it if were a ValueObjectSP, we would end up with a loop (iterator -> // synthetic -> child -> parent == iterator) // and that would in turn leak memory by never allowing the ValueObjects to // die and free their memory m_pair_ptr = valobj_sp ->GetValueForExpressionPath( ".__i_.__ptr_->__value_", nullptr, nullptr, ValueObject::GetValueForExpressionPathOptions() .DontCheckDotVsArrowSyntax() .SetSyntheticChildrenTraversal( ValueObject::GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::None), nullptr) .get(); if (!m_pair_ptr) { m_pair_ptr = valobj_sp ->GetValueForExpressionPath( ".__i_.__ptr_", nullptr, nullptr, ValueObject::GetValueForExpressionPathOptions() .DontCheckDotVsArrowSyntax() .SetSyntheticChildrenTraversal( ValueObject::GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::None), nullptr) .get(); if (m_pair_ptr) { auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true)); lldb::TemplateArgumentKind kind; if (!__i_) { m_pair_ptr = nullptr; return false; } CompilerType pair_type(__i_->GetCompilerType().GetTemplateArgument(0, kind)); std::string name; uint64_t bit_offset_ptr; uint32_t bitfield_bit_size_ptr; bool is_bitfield_ptr; pair_type = pair_type.GetFieldAtIndex(0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); if (!pair_type) { m_pair_ptr = nullptr; return false; } auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS)); m_pair_ptr = nullptr; if (addr && addr!=LLDB_INVALID_ADDRESS) { ClangASTContext *ast_ctx = llvm::dyn_cast_or_null(pair_type.GetTypeSystem()); if (!ast_ctx) return false; CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(ConstString(), { {"ptr0",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, {"ptr1",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, {"ptr2",ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, {"cw",ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, {"payload",pair_type} }); DataBufferSP buffer_sp(new DataBufferHeap(tree_node_type.GetByteSize(nullptr),0)); ProcessSP process_sp(target_sp->GetProcessSP()); Error error; process_sp->ReadMemory(addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), error); if (error.Fail()) return false; DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()); auto pair_sp = CreateValueObjectFromData("pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type); if (pair_sp) m_pair_sp = pair_sp->GetChildAtIndex(4,true); } } } return false; } size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: CalculateNumChildren() { return 2; } lldb::ValueObjectSP lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( size_t idx) { if (m_pair_ptr) return m_pair_ptr->GetChildAtIndex(idx, true); if (m_pair_sp) return m_pair_sp->GetChildAtIndex(idx, true); return lldb::ValueObjectSP(); } bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: MightHaveChildren() { return true; } size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: GetIndexOfChildWithName(const ConstString &name) { if (name == ConstString("first")) return 0; if (name == ConstString("second")) return 1; return UINT32_MAX; } lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: ~LibCxxMapIteratorSyntheticFrontEnd() { // this will be deleted when its parent dies (since it's a child object) // delete m_pair_ptr; } SyntheticChildrenFrontEnd * lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) : nullptr); } /* (lldb) fr var ibeg --raw --ptr-depth 1 -T (std::__1::__wrap_iter) ibeg = { (std::__1::__wrap_iter::iterator_type) __i = 0x00000001001037a0 { (int) *__i = 1 } } */ SyntheticChildrenFrontEnd * lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { static ConstString g_item_name; if (!g_item_name) g_item_name.SetCString("__i"); return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name) : nullptr); } lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(), m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) { if (valobj_sp) Update(); } size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: CalculateNumChildren() { return (m_cntrl ? 1 : 0); } lldb::ValueObjectSP lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( size_t idx) { if (!m_cntrl) return lldb::ValueObjectSP(); ValueObjectSP valobj_sp = m_backend.GetSP(); if (!valobj_sp) return lldb::ValueObjectSP(); if (idx == 0) return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true); if (idx > 2) return lldb::ValueObjectSP(); if (idx == 1) { if (!m_count_sp) { ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName( ConstString("__shared_owners_"), true)); if (!shared_owners_sp) return lldb::ValueObjectSP(); uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); DataExtractor data(&count, 8, m_byte_order, m_ptr_size); m_count_sp = CreateValueObjectFromData( "count", data, valobj_sp->GetExecutionContextRef(), shared_owners_sp->GetCompilerType()); } return m_count_sp; } else /* if (idx == 2) */ { if (!m_weak_count_sp) { ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName( ConstString("__shared_weak_owners_"), true)); if (!shared_weak_owners_sp) return lldb::ValueObjectSP(); uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); DataExtractor data(&count, 8, m_byte_order, m_ptr_size); m_weak_count_sp = CreateValueObjectFromData( "count", data, valobj_sp->GetExecutionContextRef(), shared_weak_owners_sp->GetCompilerType()); } return m_weak_count_sp; } } bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { m_count_sp.reset(); m_weak_count_sp.reset(); m_cntrl = nullptr; ValueObjectSP valobj_sp = m_backend.GetSP(); if (!valobj_sp) return false; TargetSP target_sp(valobj_sp->GetTargetSP()); if (!target_sp) return false; m_byte_order = target_sp->GetArchitecture().GetByteOrder(); m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); lldb::ValueObjectSP cntrl_sp( valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true)); m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular // dependency return false; } bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: MightHaveChildren() { return true; } size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: GetIndexOfChildWithName(const ConstString &name) { if (name == ConstString("__ptr_")) return 0; if (name == ConstString("count")) return 1; if (name == ConstString("weak_count")) return 2; return UINT32_MAX; } lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: ~LibcxxSharedPtrSyntheticFrontEnd() = default; SyntheticChildrenFrontEnd * lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) : nullptr); } bool lldb_private::formatters::LibcxxContainerSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { if (valobj.IsPointerType()) { uint64_t value = valobj.GetValueAsUnsigned(0); if (!value) return false; stream.Printf("0x%016" PRIx64 " ", value); } return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, nullptr, nullptr, &valobj, false, false); } // the field layout in a libc++ string (cap, side, data or data, size, cap) enum LibcxxStringLayoutMode { eLibcxxStringLayoutModeCSD = 0, eLibcxxStringLayoutModeDSC = 1, eLibcxxStringLayoutModeInvalid = 0xffff }; // this function abstracts away the layout and mode details of a libc++ string // and returns the address of the data and the size ready for callers to consume static bool ExtractLibcxxStringInfo(ValueObject &valobj, ValueObjectSP &location_sp, uint64_t &size) { ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0})); if (!D) return false; - ValueObjectSP layout_decider(D->GetChildAtIndexPath({0, 0})); + ValueObjectSP layout_decider( + D->GetChildAtIndexPath(llvm::ArrayRef({0, 0}))); // this child should exist if (!layout_decider) return false; ConstString g_data_name("__data_"); ConstString g_size_name("__size_"); bool short_mode = false; // this means the string is in short-mode and the // data is stored inline LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) ? eLibcxxStringLayoutModeDSC : eLibcxxStringLayoutModeCSD; uint64_t size_mode_value = 0; if (layout == eLibcxxStringLayoutModeDSC) { ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0})); if (!size_mode) return false; if (size_mode->GetName() != g_size_name) { // we are hitting the padding structure, move along size_mode = D->GetChildAtIndexPath({1, 1, 1}); if (!size_mode) return false; } size_mode_value = (size_mode->GetValueAsUnsigned(0)); short_mode = ((size_mode_value & 0x80) == 0); } else { ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0})); if (!size_mode) return false; size_mode_value = (size_mode->GetValueAsUnsigned(0)); short_mode = ((size_mode_value & 1) == 0); } if (short_mode) { ValueObjectSP s(D->GetChildAtIndex(1, true)); if (!s) return false; location_sp = s->GetChildAtIndex( (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); size = (layout == eLibcxxStringLayoutModeDSC) ? size_mode_value : ((size_mode_value >> 1) % 256); return (location_sp.get() != nullptr); } else { ValueObjectSP l(D->GetChildAtIndex(0, true)); if (!l) return false; // we can use the layout_decider object as the data pointer location_sp = (layout == eLibcxxStringLayoutModeDSC) ? layout_decider : l->GetChildAtIndex(2, true); ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); if (!size_vo || !location_sp) return false; size = size_vo->GetValueAsUnsigned(0); return true; } } bool lldb_private::formatters::LibcxxWStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &summary_options) { uint64_t size = 0; ValueObjectSP location_sp; if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) return false; if (size == 0) { stream.Printf("L\"\""); return true; } if (!location_sp) return false; DataExtractor extractor; StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); if (size > max_size) { size = max_size; options.SetIsTruncated(true); } } location_sp->GetPointeeData(extractor, 0, size); // std::wstring::size() is measured in 'characters', not bytes auto wchar_t_size = valobj.GetTargetSP() ->GetScratchClangASTContext() ->GetBasicType(lldb::eBasicTypeWChar) .GetByteSize(nullptr); options.SetData(extractor); options.SetStream(&stream); options.SetPrefixToken("L"); options.SetQuote('"'); options.SetSourceSize(size); options.SetBinaryZeroIsTerminator(false); switch (wchar_t_size) { case 1: StringPrinter::ReadBufferAndDumpToStream< lldb_private::formatters::StringPrinter::StringElementType::UTF8>( options); break; case 2: lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream< lldb_private::formatters::StringPrinter::StringElementType::UTF16>( options); break; case 4: lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream< lldb_private::formatters::StringPrinter::StringElementType::UTF32>( options); break; default: stream.Printf("size for wchar_t is not valid"); return true; } return true; } bool lldb_private::formatters::LibcxxStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &summary_options) { uint64_t size = 0; ValueObjectSP location_sp; if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) return false; if (size == 0) { stream.Printf("\"\""); return true; } if (!location_sp) return false; StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); DataExtractor extractor; if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); if (size > max_size) { size = max_size; options.SetIsTruncated(true); } } location_sp->GetPointeeData(extractor, 0, size); options.SetData(extractor); options.SetStream(&stream); options.SetPrefixToken(nullptr); options.SetQuote('"'); options.SetSourceSize(size); options.SetBinaryZeroIsTerminator(false); StringPrinter::ReadBufferAndDumpToStream< StringPrinter::StringElementType::ASCII>(options); return true; } class LibcxxFunctionFrontEnd : public SyntheticValueProviderFrontEnd { public: LibcxxFunctionFrontEnd(ValueObject &backend) : SyntheticValueProviderFrontEnd(backend) {} lldb::ValueObjectSP GetSyntheticValue() override { static ConstString g___f_("__f_"); return m_backend.GetChildMemberWithName(g___f_, true); } }; SyntheticChildrenFrontEnd * lldb_private::formatters::LibcxxFunctionFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { if (valobj_sp) return new LibcxxFunctionFrontEnd(*valobj_sp); return nullptr; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (revision 317454) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (revision 317455) @@ -1,4088 +1,4151 @@ //===-- DWARFASTParserClang.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "DWARFASTParserClang.h" #include "DWARFCompileUnit.h" #include "DWARFDIE.h" #include "DWARFDIECollection.h" #include "DWARFDebugInfo.h" #include "DWARFDeclContext.h" #include "DWARFDefines.h" #include "SymbolFileDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "UniqueDWARFASTType.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/Args.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Target/Language.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include #include //#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN #ifdef ENABLE_DEBUG_PRINTF #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif using namespace lldb; using namespace lldb_private; DWARFASTParserClang::DWARFASTParserClang(ClangASTContext &ast) : m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {} DWARFASTParserClang::~DWARFASTParserClang() {} static AccessType DW_ACCESS_to_AccessType(uint32_t dwarf_accessibility) { switch (dwarf_accessibility) { case DW_ACCESS_public: return eAccessPublic; case DW_ACCESS_private: return eAccessPrivate; case DW_ACCESS_protected: return eAccessProtected; default: break; } return eAccessNone; } static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) { switch (decl_kind) { case clang::Decl::CXXRecord: case clang::Decl::ClassTemplateSpecialization: return true; default: break; } return false; } struct BitfieldInfo { uint64_t bit_size; uint64_t bit_offset; BitfieldInfo() : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {} void Clear() { bit_size = LLDB_INVALID_ADDRESS; bit_offset = LLDB_INVALID_ADDRESS; } bool IsValid() const { return (bit_size != LLDB_INVALID_ADDRESS) && (bit_offset != LLDB_INVALID_ADDRESS); } bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const { if (IsValid()) { // This bitfield info is valid, so any subsequent bitfields // must not overlap and must be at a higher bit offset than // any previous bitfield + size. return (bit_size + bit_offset) <= next_bit_offset; } else { // If the this BitfieldInfo is not valid, then any offset isOK return true; } } }; ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() { if (!m_clang_ast_importer_ap) { m_clang_ast_importer_ap.reset(new ClangASTImporter); } return *m_clang_ast_importer_ap; } TypeSP DWARFASTParserClang::ParseTypeFromDWO(const DWARFDIE &die, Log *log) { ModuleSP dwo_module_sp = die.GetContainingDWOModule(); if (dwo_module_sp) { // This type comes from an external DWO module std::vector dwo_context; die.GetDWOContext(dwo_context); TypeMap dwo_types; if (dwo_module_sp->GetSymbolVendor()->FindTypes(dwo_context, true, dwo_types)) { const size_t num_dwo_types = dwo_types.GetSize(); if (num_dwo_types == 1) { // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die TypeSP dwo_type_sp = dwo_types.GetTypeAtIndex(0); if (dwo_type_sp) { lldb_private::CompilerType dwo_type = dwo_type_sp->GetForwardCompilerType(); lldb_private::CompilerType type = GetClangASTImporter().CopyType(m_ast, dwo_type); // printf ("copied_qual_type: ast = %p, clang_type = %p, name = // '%s'\n", m_ast, copied_qual_type.getAsOpaquePtr(), // external_type->GetName().GetCString()); if (type) { SymbolFileDWARF *dwarf = die.GetDWARF(); TypeSP type_sp(new Type(die.GetID(), dwarf, dwo_type_sp->GetName(), dwo_type_sp->GetByteSize(), NULL, LLDB_INVALID_UID, Type::eEncodingInvalid, &dwo_type_sp->GetDeclaration(), type, Type::eResolveStateForward)); dwarf->GetTypeList()->Insert(type_sp); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::TagDecl *tag_decl = ClangASTContext::GetAsTagDecl(type); if (tag_decl) LinkDeclContextToDIE(tag_decl, die); else { clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); } return type_sp; } } } } } return TypeSP(); } TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, const DWARFDIE &die, Log *log, bool *type_is_new_ptr) { TypeSP type_sp; if (type_is_new_ptr) *type_is_new_ptr = false; AccessType accessibility = eAccessNone; if (die) { SymbolFileDWARF *dwarf = die.GetDWARF(); if (log) { DWARFDIE context_die; clang::DeclContext *context = GetClangDeclContextContainingDIE(die, &context_die); dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die " "0x%8.8x)) %s name = '%s')", die.GetOffset(), static_cast(context), context_die.GetOffset(), die.GetTagAsCString(), die.GetName()); } // // Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); // if (log && dwarf_cu) // { // StreamString s; // die->DumpLocation (this, dwarf_cu, s); // dwarf->GetObjectFile()->GetModule()->LogMessage (log, // "SymbolFileDwarf::%s %s", __FUNCTION__, s.GetData()); // // } Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); TypeList *type_list = dwarf->GetTypeList(); if (type_ptr == NULL) { if (type_is_new_ptr) *type_is_new_ptr = true; const dw_tag_t tag = die.Tag(); bool is_forward_declaration = false; DWARFAttributes attributes; const char *type_name_cstr = NULL; ConstString type_name_const_str; Type::ResolveState resolve_state = Type::eResolveStateUnresolved; uint64_t byte_size = 0; Declaration decl; Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; CompilerType clang_type; DWARFFormValue form_value; dw_attr_t attr; switch (tag) { case DW_TAG_typedef: case DW_TAG_base_type: case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: case DW_TAG_const_type: case DW_TAG_restrict_type: case DW_TAG_volatile_type: case DW_TAG_unspecified_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; const size_t num_attributes = die.GetAttributes(attributes); uint32_t encoding = 0; DWARFFormValue encoding_uid; if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); // Work around a bug in llvm-gcc where they give a name to a // reference type which doesn't // include the "&"... if (tag == DW_TAG_reference_type) { if (strchr(type_name_cstr, '&') == NULL) type_name_cstr = NULL; } if (type_name_cstr) type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_encoding: encoding = form_value.Unsigned(); break; case DW_AT_type: encoding_uid = form_value; break; default: case DW_AT_sibling: break; } } } } if (tag == DW_TAG_typedef && encoding_uid.IsValid()) { // Try to parse a typedef from the DWO file first as modules // can contain typedef'ed structures that have no names like: // // typedef struct { int a; } Foo; // // In this case we will have a structure with no name and a // typedef named "Foo" that points to this unnamed structure. // The name in the typedef is the only identifier for the struct, // so always try to get typedefs from DWO files if possible. // // The type_sp returned will be empty if the typedef doesn't exist // in a DWO file, so it is cheap to call this function just to check. // // If we don't do this we end up creating a TypeSP that says this // is a typedef to type 0x123 (the DW_AT_type value would be 0x123 // in the DW_TAG_typedef), and this is the unnamed structure type. // We will have a hard time tracking down an unnammed structure // type in the module DWO file, so we make sure we don't get into // this situation by always resolving typedefs from the DWO file. const DWARFDIE encoding_die = dwarf->GetDIE(DIERef(encoding_uid)); // First make sure that the die that this is typedef'ed to _is_ // just a declaration (DW_AT_declaration == 1), not a full definition // since template types can't be represented in modules since only // concrete instances of templates are ever emitted and modules // won't contain those if (encoding_die && encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr, encoding_uid.Reference()); switch (tag) { default: break; case DW_TAG_unspecified_type: if (strcmp(type_name_cstr, "nullptr_t") == 0 || strcmp(type_name_cstr, "decltype(nullptr)") == 0) { resolve_state = Type::eResolveStateFull; clang_type = m_ast.GetBasicType(eBasicTypeNullPtr); break; } // Fall through to base type below in case we can handle the type // there... LLVM_FALLTHROUGH; case DW_TAG_base_type: resolve_state = Type::eResolveStateFull; clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( type_name_cstr, encoding, byte_size * 8); break; case DW_TAG_pointer_type: encoding_data_type = Type::eEncodingIsPointerUID; break; case DW_TAG_reference_type: encoding_data_type = Type::eEncodingIsLValueReferenceUID; break; case DW_TAG_rvalue_reference_type: encoding_data_type = Type::eEncodingIsRValueReferenceUID; break; case DW_TAG_typedef: encoding_data_type = Type::eEncodingIsTypedefUID; break; case DW_TAG_const_type: encoding_data_type = Type::eEncodingIsConstUID; break; case DW_TAG_restrict_type: encoding_data_type = Type::eEncodingIsRestrictUID; break; case DW_TAG_volatile_type: encoding_data_type = Type::eEncodingIsVolatileUID; break; } if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID || encoding_data_type == Type::eEncodingIsTypedefUID) && sc.comp_unit != NULL) { if (tag == DW_TAG_pointer_type) { DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type); if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) { // Blocks have a __FuncPtr inside them which is a pointer to a // function of the proper type. for (DWARFDIE child_die = target_die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""), "__FuncPtr")) { DWARFDIE function_pointer_type = child_die.GetReferencedDIE(DW_AT_type); if (function_pointer_type) { DWARFDIE function_type = function_pointer_type.GetReferencedDIE(DW_AT_type); bool function_type_is_new_pointer; TypeSP lldb_function_type_sp = ParseTypeFromDWARF( sc, function_type, log, &function_type_is_new_pointer); if (lldb_function_type_sp) { clang_type = m_ast.CreateBlockPointerType( lldb_function_type_sp->GetForwardCompilerType()); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } break; } } } } bool translation_unit_is_objc = (sc.comp_unit->GetLanguage() == eLanguageTypeObjC || sc.comp_unit->GetLanguage() == eLanguageTypeObjC_plus_plus); if (translation_unit_is_objc) { if (type_name_cstr != NULL) { static ConstString g_objc_type_name_id("id"); static ConstString g_objc_type_name_Class("Class"); static ConstString g_objc_type_name_selector("SEL"); if (type_name_const_str == g_objc_type_name_id) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'id' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCID); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } else if (type_name_const_str == g_objc_type_name_Class) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'Class' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCClass); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } else if (type_name_const_str == g_objc_type_name_selector) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'selector' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCSel); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } else if (encoding_data_type == Type::eEncodingIsPointerUID && encoding_uid.IsValid()) { // Clang sometimes erroneously emits id as objc_object*. In that // case we fix up the type to "id". const DWARFDIE encoding_die = dwarf->GetDIE(DIERef(encoding_uid)); if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) { if (const char *struct_name = encoding_die.GetName()) { if (!strcmp(struct_name, "objc_object")) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s " "'%s' is 'objc_object*', which we overrode to " "'id'.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCID); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } } } } } type_sp.reset( new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, DIERef(encoding_uid).GetUID(dwarf), encoding_data_type, &decl, clang_type, resolve_state)); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); // Type* encoding_type = // GetUniquedTypeForDIEOffset(encoding_uid, type_sp, // NULL, 0, 0, false); // if (encoding_type != NULL) // { // if (encoding_type != DIE_IS_BEING_PARSED) // type_sp->SetEncodingType(encoding_type); // else // m_indirect_fixups.push_back(type_sp.get()); // } } break; case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; bool byte_size_valid = false; LanguageType class_language = eLanguageTypeUnknown; bool is_complete_objc_class = false; // bool struct_is_class = false; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: if (die.GetCU()->DW_AT_decl_file_attributes_are_invalid()) { // llvm-gcc outputs invalid DW_AT_decl_file attributes that // always // point to the compile unit file, so we clear this invalid // value // so that we can still unique types efficiently. decl.SetFile(FileSpec("", false)); } else decl.SetFile( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); byte_size_valid = true; break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: is_forward_declaration = form_value.Boolean(); break; case DW_AT_APPLE_runtime_class: class_language = (LanguageType)form_value.Signed(); break; case DW_AT_APPLE_objc_complete_type: is_complete_objc_class = form_value.Signed(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_description: case DW_AT_start_scope: case DW_AT_visibility: default: case DW_AT_sibling: break; } } } } // UniqueDWARFASTType is large, so don't create a local variables on the // stack, put it on the heap. This function is often called recursively // and clang isn't good and sharing the stack space for variables in // different blocks. std::unique_ptr unique_ast_entry_ap( new UniqueDWARFASTType()); ConstString unique_typename(type_name_const_str); Declaration unique_decl(decl); if (type_name_const_str) { LanguageType die_language = die.GetLanguage(); if (Language::LanguageIsCPlusPlus(die_language)) { // For C++, we rely solely upon the one definition rule that says // only // one thing can exist at a given decl context. We ignore the file // and // line that things are declared on. std::string qualified_name; if (die.GetQualifiedName(qualified_name)) unique_typename = ConstString(qualified_name); unique_decl.Clear(); } if (dwarf->GetUniqueDWARFASTTypeMap().Find( unique_typename, die, unique_decl, byte_size_valid ? byte_size : -1, *unique_ast_entry_ap)) { type_sp = unique_ast_entry_ap->m_type_sp; if (type_sp) { dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); int tag_decl_kind = -1; AccessType default_accessibility = eAccessNone; if (tag == DW_TAG_structure_type) { tag_decl_kind = clang::TTK_Struct; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_union_type) { tag_decl_kind = clang::TTK_Union; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_class_type) { tag_decl_kind = clang::TTK_Class; default_accessibility = eAccessPrivate; } if (byte_size_valid && byte_size == 0 && type_name_cstr && die.HasChildren() == false && sc.comp_unit->GetLanguage() == eLanguageTypeObjC) { // Work around an issue with clang at the moment where // forward declarations for objective C classes are emitted // as: // DW_TAG_structure_type [2] // DW_AT_name( "ForwardObjcClass" ) // DW_AT_byte_size( 0x00 ) // DW_AT_decl_file( "..." ) // DW_AT_decl_line( 1 ) // // Note that there is no DW_AT_declaration and there are // no children, and the byte size is zero. is_forward_declaration = true; } if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) { if (!is_complete_objc_class && die.Supports_DW_AT_APPLE_objc_complete_type()) { // We have a valid eSymbolTypeObjCClass class symbol whose // name matches the current objective C class that we // are trying to find and this DIE isn't the complete // definition (we checked is_complete_objc_class above and // know it is false), so the real definition is in here somewhere type_sp = dwarf->FindCompleteObjCDefinitionTypeForDIE( die, type_name_const_str, true); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE( die, type_name_const_str, true); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an " "incomplete objc type, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } } } if (is_forward_declaration) { // We have a forward declaration to a type and we need // to try and find a full declaration. We look in the // current type index just in case we have a forward // declaration followed by an actual declarations in the // DWARF. If this fails, we need to look elsewhere... if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, trying to find complete type", static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr); } // See if the type comes from a DWO module and if so, track down that // type. type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; DWARFDeclContext die_decl_ctx; die.GetDWARFDeclContext(die_decl_ctx); // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die, // type_name_const_str); type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( die_decl_ctx); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE( dwarf->DebugInfo()->GetDIE(DIERef(type_sp->GetID(), dwarf))); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); return type_sp; } } assert(tag_decl_kind != -1); bool clang_type_was_created = false; clang_type.SetCompilerType( &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); if (!clang_type) { clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); if (accessibility == eAccessNone && decl_ctx) { // Check the decl context that contains this class/struct/union. // If it is a class we must give it an accessibility. const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind(); if (DeclKindIsCXXClass(containing_decl_kind)) accessibility = default_accessibility; } ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die)); if (type_name_cstr && strchr(type_name_cstr, '<')) { ClangASTContext::TemplateParameterInfos template_param_infos; if (ParseTemplateParameterInfos(die, template_param_infos)) { clang::ClassTemplateDecl *class_template_decl = m_ast.ParseClassTemplateDecl(decl_ctx, accessibility, type_name_cstr, tag_decl_kind, template_param_infos); clang::ClassTemplateSpecializationDecl *class_specialization_decl = m_ast.CreateClassTemplateSpecializationDecl( decl_ctx, class_template_decl, tag_decl_kind, template_param_infos); clang_type = m_ast.CreateClassTemplateSpecializationType( class_specialization_decl); clang_type_was_created = true; m_ast.SetMetadata(class_template_decl, metadata); m_ast.SetMetadata(class_specialization_decl, metadata); } } if (!clang_type_was_created) { clang_type_was_created = true; clang_type = m_ast.CreateRecordType(decl_ctx, accessibility, type_name_cstr, tag_decl_kind, class_language, &metadata); } } // Store a forward declaration to this class type in case any // parameters in any class methods need it for the clang // types for function prototypes. LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateForward)); type_sp->SetIsCompleteObjCClass(is_complete_objc_class); // Add our type to the unique type map so we don't // end up creating many copies of the same type over // and over in the ASTContext for our module unique_ast_entry_ap->m_type_sp = type_sp; unique_ast_entry_ap->m_die = die; unique_ast_entry_ap->m_declaration = unique_decl; unique_ast_entry_ap->m_byte_size = byte_size; dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename, *unique_ast_entry_ap); if (is_forward_declaration && die.HasChildren()) { // Check to see if the DIE actually has a definition, some version of // GCC will // emit DIEs with DW_AT_declaration set to true, but yet still have // subprogram, // members, or inheritance, so we can't trust it DWARFDIE child_die = die.GetFirstChild(); while (child_die) { switch (child_die.Tag()) { case DW_TAG_inheritance: case DW_TAG_subprogram: case DW_TAG_member: case DW_TAG_APPLE_property: case DW_TAG_class_type: case DW_TAG_structure_type: case DW_TAG_enumeration_type: case DW_TAG_typedef: case DW_TAG_union_type: child_die.Clear(); is_forward_declaration = false; break; default: child_die = child_die.GetSibling(); break; } } } if (!is_forward_declaration) { // Always start the definition for a class type so that // if the class has child classes or types that require // the class to be created for use as their decl contexts // the class will be ready to accept these child definitions. if (die.HasChildren() == false) { // No children for this struct/union/class, lets finish it if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", die.GetOffset(), type_name_cstr); } if (tag == DW_TAG_structure_type) // this only applies in C { clang::RecordDecl *record_decl = ClangASTContext::GetAsRecordDecl(clang_type); if (record_decl) { GetClangASTImporter().InsertRecordDecl( record_decl, ClangASTImporter::LayoutInfo()); } } } else if (clang_type_was_created) { // Start the definition if the class is not objective C since // the underlying decls respond to isCompleteDefinition(). Objective // C decls don't respond to isCompleteDefinition() so we can't // start the declaration definition right away. For C++ // class/union/structs // we want to start the definition in case the class is needed as // the // declaration context for a contained class or type without the // need // to complete that type.. if (class_language != eLanguageTypeObjC && class_language != eLanguageTypeObjC_plus_plus) ClangASTContext::StartTagDeclarationDefinition(clang_type); // Leave this as a forward declaration until we need // to know the details of the type. lldb_private::Type // will automatically call the SymbolFile virtual function // "SymbolFileDWARF::CompleteType(Type *)" // When the definition needs to be defined. assert(!dwarf->GetForwardDeclClangTypeToDie().count( ClangUtil::RemoveFastQualifiers(clang_type) .GetOpaqueQualType()) && "Type already in the forward declaration map!"); // Can't assume m_ast.GetSymbolFile() is actually a SymbolFileDWARF, // it can be a // SymbolFileDWARFDebugMap for Apple binaries. dwarf->GetForwardDeclDieToClangType()[die.GetDIE()] = clang_type.GetOpaqueQualType(); dwarf->GetForwardDeclClangTypeToDie() [ClangUtil::RemoveFastQualifiers(clang_type) .GetOpaqueQualType()] = die.GetDIERef(); m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } } break; case DW_TAG_enumeration_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue encoding_form; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: is_forward_declaration = form_value.Boolean(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_bit_stride: case DW_AT_byte_stride: case DW_AT_data_location: case DW_AT_description: case DW_AT_start_scope: case DW_AT_visibility: case DW_AT_specification: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } if (is_forward_declaration) { type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; DWARFDeclContext die_decl_ctx; die.GetDWARFDeclContext(die_decl_ctx); type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( die_decl_ctx); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(dwarf->DebugInfo()->GetDIE( DIERef(type_sp->GetID(), dwarf))); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); return type_sp; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); CompilerType enumerator_clang_type; clang_type.SetCompilerType( &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); if (!clang_type) { if (encoding_form.IsValid()) { Type *enumerator_type = dwarf->ResolveTypeUID(DIERef(encoding_form)); if (enumerator_type) enumerator_clang_type = enumerator_type->GetFullCompilerType(); } if (!enumerator_clang_type) { if (byte_size > 0) { enumerator_clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( NULL, DW_ATE_signed, byte_size * 8); } else { enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt); } } clang_type = m_ast.CreateEnumerationType( type_name_cstr, GetClangDeclContextContainingDIE(die, nullptr), decl, enumerator_clang_type); } else { enumerator_clang_type = m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType()); } LinkDeclContextToDIE( ClangASTContext::GetDeclContextForType(clang_type), die); type_sp.reset(new Type( die.GetID(), dwarf, type_name_const_str, byte_size, NULL, DIERef(encoding_form).GetUID(dwarf), Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateForward)); if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { if (die.HasChildren()) { SymbolContext cu_sc(die.GetLLDBCompileUnit()); bool is_signed = false; enumerator_clang_type.IsIntegerType(is_signed); ParseChildEnumerators(cu_sc, clang_type, is_signed, type_sp->GetByteSize(), die); } ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", die.GetOffset(), type_name_cstr); } } } break; case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: case DW_TAG_subroutine_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue type_die_form; bool is_variadic = false; bool is_inline = false; bool is_static = false; bool is_virtual = false; bool is_explicit = false; bool is_artificial = false; bool has_template_params = false; DWARFFormValue specification_die_form; DWARFFormValue abstract_origin_die_form; dw_offset_t object_pointer_die_offset = DW_INVALID_OFFSET; unsigned type_quals = 0; clang::StorageClass storage = clang::SC_None; //, Extern, Static, PrivateExtern const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: break; // mangled = // form_value.AsCString(&dwarf->get_debug_str_data()); // break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; case DW_AT_inline: is_inline = form_value.Boolean(); break; case DW_AT_virtuality: is_virtual = form_value.Boolean(); break; case DW_AT_explicit: is_explicit = form_value.Boolean(); break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_external: if (form_value.Unsigned()) { if (storage == clang::SC_None) storage = clang::SC_Extern; else storage = clang::SC_PrivateExtern; } break; case DW_AT_specification: specification_die_form = form_value; break; case DW_AT_abstract_origin: abstract_origin_die_form = form_value; break; case DW_AT_object_pointer: object_pointer_die_offset = form_value.Reference(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_address_class: case DW_AT_calling_convention: case DW_AT_data_location: case DW_AT_elemental: case DW_AT_entry_pc: case DW_AT_frame_base: case DW_AT_high_pc: case DW_AT_low_pc: case DW_AT_prototyped: case DW_AT_pure: case DW_AT_ranges: case DW_AT_recursive: case DW_AT_return_addr: case DW_AT_segment: case DW_AT_start_scope: case DW_AT_static_link: case DW_AT_trampoline: case DW_AT_visibility: case DW_AT_vtable_elem_location: case DW_AT_description: case DW_AT_sibling: break; } } } } std::string object_pointer_name; if (object_pointer_die_offset != DW_INVALID_OFFSET) { DWARFDIE object_pointer_die = die.GetDIE(object_pointer_die_offset); if (object_pointer_die) { const char *object_pointer_name_cstr = object_pointer_die.GetName(); if (object_pointer_name_cstr) object_pointer_name = object_pointer_name_cstr; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); CompilerType return_clang_type; Type *func_type = NULL; if (type_die_form.IsValid()) func_type = dwarf->ResolveTypeUID(DIERef(type_die_form)); if (func_type) return_clang_type = func_type->GetForwardCompilerType(); else return_clang_type = m_ast.GetBasicType(eBasicTypeVoid); std::vector function_param_types; std::vector function_param_decls; // Parse the function children for the parameters DWARFDIE decl_ctx_die; clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, &decl_ctx_die); const clang::Decl::Kind containing_decl_kind = containing_decl_ctx->getDeclKind(); bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind); // Start off static. This will be set to false in // ParseChildParameters(...) // if we find a "this" parameters as the first parameter if (is_cxx_method) { is_static = true; } if (die.HasChildren()) { bool skip_artificial = true; ParseChildParameters(sc, containing_decl_ctx, die, skip_artificial, is_static, is_variadic, has_template_params, function_param_types, function_param_decls, type_quals); } bool ignore_containing_context = false; // Check for templatized class member functions. If we had any // DW_TAG_template_type_parameter // or DW_TAG_template_value_parameter the DW_TAG_subprogram DIE, then we // can't let this become // a method in a class. Why? Because templatized functions are only // emitted if one of the // templatized methods is used in the current compile unit and we will // end up with classes // that may or may not include these member functions and this means one // class won't match another // class definition and it affects our ability to use a class in the // clang expression parser. So // for the greater good, we currently must not allow any template member // functions in a class definition. if (is_cxx_method && has_template_params) { ignore_containing_context = true; is_cxx_method = false; } // clang_type will get the function prototype clang type after this call clang_type = m_ast.CreateFunctionType( return_clang_type, function_param_types.data(), function_param_types.size(), is_variadic, type_quals); if (type_name_cstr) { bool type_handled = false; if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) { ObjCLanguage::MethodName objc_method(type_name_cstr, true); if (objc_method.IsValid(true)) { CompilerType class_opaque_type; ConstString class_name(objc_method.GetClassName()); if (class_name) { TypeSP complete_objc_class_type_sp( dwarf->FindCompleteObjCDefinitionTypeForDIE( DWARFDIE(), class_name, false)); if (complete_objc_class_type_sp) { CompilerType type_clang_forward_type = complete_objc_class_type_sp->GetForwardCompilerType(); if (ClangASTContext::IsObjCObjectOrInterfaceType( type_clang_forward_type)) class_opaque_type = type_clang_forward_type; } } if (class_opaque_type) { // If accessibility isn't set to anything valid, assume public // for // now... if (accessibility == eAccessNone) accessibility = eAccessPublic; clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( class_opaque_type, type_name_cstr, clang_type, accessibility, is_artificial, is_variadic); type_handled = objc_method_decl != NULL; if (type_handled) { LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext(objc_method_decl), die); m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), " "please file a bug and attach the file at the start of " "this error message", die.GetOffset(), tag, DW_TAG_value_to_name(tag)); } } } else if (is_cxx_method) { // Look at the parent of this DIE and see if is is // a class or struct and see if this is actually a // C++ method Type *class_type = dwarf->ResolveType(decl_ctx_die); if (class_type) { bool alternate_defn = false; if (class_type->GetID() != decl_ctx_die.GetID() || decl_ctx_die.GetContainingDWOModuleDIE()) { alternate_defn = true; // We uniqued the parent class of this function to another // class // so we now need to associate all dies under "decl_ctx_die" // to // DIEs in the DIE for "class_type"... SymbolFileDWARF *class_symfile = NULL; DWARFDIE class_type_die; SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { class_symfile = debug_map_symfile->GetSymbolFileByOSOIndex( SymbolFileDWARFDebugMap::GetOSOIndexFromUserID( class_type->GetID())); class_type_die = class_symfile->DebugInfo()->GetDIE( DIERef(class_type->GetID(), dwarf)); } else { class_symfile = dwarf; class_type_die = dwarf->DebugInfo()->GetDIE( DIERef(class_type->GetID(), dwarf)); } if (class_type_die) { DWARFDIECollection failures; CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die, class_type, failures); // FIXME do something with these failures that's smarter // than // just dropping them on the ground. Unfortunately classes // don't // like having stuff added to them after their definitions // are // complete... type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); break; } } } if (specification_die_form.IsValid()) { // We have a specification which we are going to base our // function // prototype off of, so we need this type to be completed so // that the // m_die_to_decl_ctx for the method in the specification has a // valid // clang decl context. class_type->GetForwardCompilerType(); // If we have a specification, then the function type should // have been // made with the specification and not with this die. DWARFDIE spec_die = dwarf->DebugInfo()->GetDIE( DIERef(specification_die_form)); clang::DeclContext *spec_clang_decl_ctx = GetClangDeclContextForDIE(spec_die); if (spec_clang_decl_ctx) { LinkDeclContextToDIE(spec_clang_decl_ctx, die); } else { dwarf->GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8" PRIx64 ") has no decl\n", die.GetID(), specification_die_form.Reference()); } type_handled = true; } else if (abstract_origin_die_form.IsValid()) { // We have a specification which we are going to base our // function // prototype off of, so we need this type to be completed so // that the // m_die_to_decl_ctx for the method in the abstract origin has // a valid // clang decl context. class_type->GetForwardCompilerType(); DWARFDIE abs_die = dwarf->DebugInfo()->GetDIE( DIERef(abstract_origin_die_form)); clang::DeclContext *abs_clang_decl_ctx = GetClangDeclContextForDIE(abs_die); if (abs_clang_decl_ctx) { LinkDeclContextToDIE(abs_clang_decl_ctx, die); } else { dwarf->GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8" PRIx64 ") has no decl\n", die.GetID(), abstract_origin_die_form.Reference()); } type_handled = true; } else { CompilerType class_opaque_type = class_type->GetForwardCompilerType(); if (ClangASTContext::IsCXXClassType(class_opaque_type)) { if (class_opaque_type.IsBeingDefined() || alternate_defn) { if (!is_static && !die.HasChildren()) { // We have a C++ member function with no children (this // pointer!) // and clang will get mad if we try and make a function // that isn't // well formed in the DWARF, so we will just skip it... type_handled = true; } else { bool add_method = true; if (alternate_defn) { // If an alternate definition for the class exists, // then add the method only if an // equivalent is not already present. clang::CXXRecordDecl *record_decl = m_ast.GetAsCXXRecordDecl( class_opaque_type.GetOpaqueQualType()); if (record_decl) { for (auto method_iter = record_decl->method_begin(); method_iter != record_decl->method_end(); method_iter++) { clang::CXXMethodDecl *method_decl = *method_iter; if (method_decl->getNameInfo().getAsString() == std::string(type_name_cstr)) { if (method_decl->getType() == ClangUtil::GetQualType(clang_type)) { add_method = false; LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext( method_decl), die); type_handled = true; break; } } } } } if (add_method) { llvm::PrettyStackTraceFormat stack_trace( "SymbolFileDWARF::ParseType() is adding a method " "%s to class %s in DIE 0x%8.8" PRIx64 " from %s", type_name_cstr, class_type->GetName().GetCString(), die.GetID(), dwarf->GetObjectFile() ->GetFileSpec() .GetPath() .c_str()); const bool is_attr_used = false; // Neither GCC 4.2 nor clang++ currently set a valid // accessibility // in the DWARF for C++ methods... Default to public // for now... if (accessibility == eAccessNone) accessibility = eAccessPublic; clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( class_opaque_type.GetOpaqueQualType(), type_name_cstr, clang_type, accessibility, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); type_handled = cxx_method_decl != NULL; if (type_handled) { LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext( cxx_method_decl), die); ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); if (!object_pointer_name.empty()) { metadata.SetObjectPtrName( object_pointer_name.c_str()); if (log) log->Printf( "Setting object pointer name: %s on method " "object %p.\n", object_pointer_name.c_str(), static_cast(cxx_method_decl)); } m_ast.SetMetadata(cxx_method_decl, metadata); } else { ignore_containing_context = true; } } } } else { // We were asked to parse the type for a method in a // class, yet the // class hasn't been asked to complete itself through the // clang::ExternalASTSource protocol, so we need to just // have the // class complete itself and do things the right way, then // our // DIE should then have an entry in the // dwarf->GetDIEToType() map. First // we need to modify the dwarf->GetDIEToType() so it // doesn't think we are // trying to parse this DIE anymore... dwarf->GetDIEToType()[die.GetDIE()] = NULL; // Now we get the full type to force our class type to // complete itself // using the clang::ExternalASTSource protocol which will // parse all // base classes and all methods (including the method for // this DIE). class_type->GetFullCompilerType(); // The type for this DIE should have been filled in the // function call above type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); break; } // FIXME This is fixing some even uglier behavior but we // really need to // uniq the methods of each class as well as the class // itself. // type_handled = true; } } } } } } if (!type_handled) { clang::FunctionDecl *function_decl = nullptr; if (abstract_origin_die_form.IsValid()) { DWARFDIE abs_die = dwarf->DebugInfo()->GetDIE(DIERef(abstract_origin_die_form)); SymbolContext sc; if (dwarf->ResolveType(abs_die)) { function_decl = llvm::dyn_cast_or_null( GetCachedClangDeclContextForDIE(abs_die)); if (function_decl) { LinkDeclContextToDIE(function_decl, die); } } } if (!function_decl) { // We just have a function that isn't part of a class function_decl = m_ast.CreateFunctionDeclaration( ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, type_name_cstr, clang_type, storage, is_inline); // if (template_param_infos.GetSize() > // 0) // { // clang::FunctionTemplateDecl // *func_template_decl = // CreateFunctionTemplateDecl // (containing_decl_ctx, // function_decl, // type_name_cstr, // template_param_infos); // // CreateFunctionTemplateSpecializationInfo // (function_decl, // func_template_decl, // template_param_infos); // } // Add the decl to our DIE to decl context map lldbassert(function_decl); if (function_decl) { LinkDeclContextToDIE(function_decl, die); if (!function_param_decls.empty()) m_ast.SetFunctionParameters(function_decl, &function_param_decls.front(), function_param_decls.size()); ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); if (!object_pointer_name.empty()) { metadata.SetObjectPtrName(object_pointer_name.c_str()); if (log) log->Printf("Setting object pointer name: %s on function " "object %p.", object_pointer_name.c_str(), static_cast(function_decl)); } m_ast.SetMetadata(function_decl, metadata); } } } } type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateFull)); assert(type_sp.get()); } break; case DW_TAG_array_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue type_die_form; int64_t first_index = 0; uint32_t byte_stride = 0; uint32_t bit_stride = 0; bool is_vector = false; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_byte_size: break; // byte_size = form_value.Unsigned(); break; case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; case DW_AT_GNU_vector: is_vector = form_value.Boolean(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_description: case DW_AT_ordering: case DW_AT_start_scope: case DW_AT_visibility: case DW_AT_specification: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); DIERef type_die_ref(type_die_form); Type *element_type = dwarf->ResolveTypeUID(type_die_ref); if (element_type) { std::vector element_orders; ParseChildArrayInfo(sc, die, first_index, element_orders, byte_stride, bit_stride); if (byte_stride == 0 && bit_stride == 0) byte_stride = element_type->GetByteSize(); CompilerType array_element_type = element_type->GetForwardCompilerType(); if (ClangASTContext::IsCXXClassType(array_element_type) && array_element_type.GetCompleteType() == false) { ModuleSP module_sp = die.GetModule(); if (module_sp) { if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module_sp->ReportError( "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " "class/union/struct element type DIE 0x%8.8x that is a " "forward declaration, not a complete definition.\nTry " "compiling the source file with -fno-limit-debug-info or " "disable -gmodule", die.GetOffset(), type_die_ref.die_offset); else module_sp->ReportError( "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " "class/union/struct element type DIE 0x%8.8x that is a " "forward declaration, not a complete definition.\nPlease " "file a bug against the compiler and include the " "preprocessed output for %s", die.GetOffset(), type_die_ref.die_offset, die.GetLLDBCompileUnit() ? die.GetLLDBCompileUnit()->GetPath().c_str() : "the source file"); } // We have no choice other than to pretend that the element class // type // is complete. If we don't do this, clang will crash when trying // to layout the class. Since we provide layout assistance, all // ivars in this class and other classes will be fine, this is // the best we can do short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( array_element_type)) { ClangASTContext::CompleteTagDeclarationDefinition( array_element_type); } else { module_sp->ReportError("DWARF DIE at 0x%8.8x was not able to " "start its definition.\nPlease file a " "bug and attach the file at the start " "of this error message", type_die_ref.die_offset); } } uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; if (element_orders.size() > 0) { uint64_t num_elements = 0; std::vector::const_reverse_iterator pos; std::vector::const_reverse_iterator end = element_orders.rend(); for (pos = element_orders.rbegin(); pos != end; ++pos) { num_elements = *pos; clang_type = m_ast.CreateArrayType(array_element_type, num_elements, is_vector); array_element_type = clang_type; array_element_bit_stride = num_elements ? array_element_bit_stride * num_elements : array_element_bit_stride; } } else { clang_type = m_ast.CreateArrayType(array_element_type, 0, is_vector); } ConstString empty_name; type_sp.reset(new Type( die.GetID(), dwarf, empty_name, array_element_bit_stride / 8, NULL, DIERef(type_die_form).GetUID(dwarf), Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateFull)); type_sp->SetEncodingType(element_type); } } } break; case DW_TAG_ptr_to_member_type: { DWARFFormValue type_die_form; DWARFFormValue containing_type_die_form; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_type: type_die_form = form_value; break; case DW_AT_containing_type: containing_type_die_form = form_value; break; } } } Type *pointee_type = dwarf->ResolveTypeUID(DIERef(type_die_form)); Type *class_type = dwarf->ResolveTypeUID(DIERef(containing_type_die_form)); CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType(); CompilerType class_clang_type = class_type->GetLayoutCompilerType(); clang_type = ClangASTContext::CreateMemberPointerType( class_clang_type, pointee_clang_type); byte_size = clang_type.GetByteSize(nullptr); type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, NULL, clang_type, Type::eResolveStateForward)); } break; } default: dwarf->GetObjectFile()->GetModule()->ReportError( "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and " "attach the file at the start of this error message", die.GetOffset(), tag, DW_TAG_value_to_name(tag)); break; } if (type_sp.get()) { DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die); dw_tag_t sc_parent_tag = sc_parent_die.Tag(); SymbolContextScope *symbol_context_scope = NULL; if (sc_parent_tag == DW_TAG_compile_unit) { symbol_context_scope = sc.comp_unit; } else if (sc.function != NULL && sc_parent_die) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID()); if (symbol_context_scope == NULL) symbol_context_scope = sc.function; } if (symbol_context_scope != NULL) { type_sp->SetSymbolContextScope(symbol_context_scope); } // We are ready to put this type into the uniqued list up at the module // level type_list->Insert(type_sp); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); } } else if (type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); } } return type_sp; } // DWARF parsing functions class DWARFASTParserClang::DelayedAddObjCClassProperty { public: DelayedAddObjCClassProperty( const CompilerType &class_opaque_type, const char *property_name, const CompilerType &property_opaque_type, // The property type is only // required if you don't have an // ivar decl clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name, const char *property_getter_name, uint32_t property_attributes, const ClangASTMetadata *metadata) : m_class_opaque_type(class_opaque_type), m_property_name(property_name), m_property_opaque_type(property_opaque_type), m_ivar_decl(ivar_decl), m_property_setter_name(property_setter_name), m_property_getter_name(property_getter_name), m_property_attributes(property_attributes) { if (metadata != NULL) { m_metadata_ap.reset(new ClangASTMetadata()); *m_metadata_ap = *metadata; } } DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) { *this = rhs; } DelayedAddObjCClassProperty & operator=(const DelayedAddObjCClassProperty &rhs) { m_class_opaque_type = rhs.m_class_opaque_type; m_property_name = rhs.m_property_name; m_property_opaque_type = rhs.m_property_opaque_type; m_ivar_decl = rhs.m_ivar_decl; m_property_setter_name = rhs.m_property_setter_name; m_property_getter_name = rhs.m_property_getter_name; m_property_attributes = rhs.m_property_attributes; if (rhs.m_metadata_ap.get()) { m_metadata_ap.reset(new ClangASTMetadata()); *m_metadata_ap = *rhs.m_metadata_ap; } return *this; } bool Finalize() { return ClangASTContext::AddObjCClassProperty( m_class_opaque_type, m_property_name, m_property_opaque_type, m_ivar_decl, m_property_setter_name, m_property_getter_name, m_property_attributes, m_metadata_ap.get()); } private: CompilerType m_class_opaque_type; const char *m_property_name; CompilerType m_property_opaque_type; clang::ObjCIvarDecl *m_ivar_decl; const char *m_property_setter_name; const char *m_property_getter_name; uint32_t m_property_attributes; std::unique_ptr m_metadata_ap; }; bool DWARFASTParserClang::ParseTemplateDIE( const DWARFDIE &die, ClangASTContext::TemplateParameterInfos &template_param_infos) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); const char *name = nullptr; CompilerType clang_type; uint64_t uval64 = 0; bool uval64_valid = false; if (num_attributes > 0) { DWARFFormValue form_value; for (size_t i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); switch (attr) { case DW_AT_name: if (attributes.ExtractFormValueAtIndex(i, form_value)) name = form_value.AsCString(); break; case DW_AT_type: if (attributes.ExtractFormValueAtIndex(i, form_value)) { Type *lldb_type = die.ResolveTypeUID(DIERef(form_value)); if (lldb_type) clang_type = lldb_type->GetForwardCompilerType(); } break; case DW_AT_const_value: if (attributes.ExtractFormValueAtIndex(i, form_value)) { uval64_valid = true; uval64 = form_value.Unsigned(); } break; default: break; } } clang::ASTContext *ast = m_ast.getASTContext(); if (!clang_type) clang_type = m_ast.GetBasicType(eBasicTypeVoid); if (clang_type) { bool is_signed = false; if (name && name[0]) template_param_infos.names.push_back(name); else template_param_infos.names.push_back(NULL); // Get the signed value for any integer or enumeration if available clang_type.IsIntegerOrEnumerationType(is_signed); if (tag == DW_TAG_template_value_parameter && uval64_valid) { llvm::APInt apint(clang_type.GetBitSize(nullptr), uval64, is_signed); template_param_infos.args.push_back( clang::TemplateArgument(*ast, llvm::APSInt(apint, !is_signed), ClangUtil::GetQualType(clang_type))); } else { template_param_infos.args.push_back( clang::TemplateArgument(ClangUtil::GetQualType(clang_type))); } } else { return false; } } } return true; default: break; } return false; } bool DWARFASTParserClang::ParseTemplateParameterInfos( const DWARFDIE &parent_die, ClangASTContext::TemplateParameterInfos &template_param_infos) { if (!parent_die) return false; Args template_parameter_names; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: ParseTemplateDIE(die, template_param_infos); break; default: break; } } if (template_param_infos.args.empty()) return false; return template_param_infos.args.size() == template_param_infos.names.size(); } bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, CompilerType &clang_type) { SymbolFileDWARF *dwarf = die.GetDWARF(); std::lock_guard guard( dwarf->GetObjectFile()->GetModule()->GetMutex()); // Disable external storage for this type so we don't get anymore // clang::ExternalASTSource queries for this type. m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); if (!die) return false; #if defined LLDB_CONFIGURATION_DEBUG //---------------------------------------------------------------------- // For debugging purposes, the LLDB_DWARF_DONT_COMPLETE_TYPENAMES // environment variable can be set with one or more typenames separated // by ';' characters. This will cause this function to not complete any // types whose names match. // // Examples of setting this environment variable: // // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo;Bar;Baz //---------------------------------------------------------------------- const char *dont_complete_typenames_cstr = getenv("LLDB_DWARF_DONT_COMPLETE_TYPENAMES"); if (dont_complete_typenames_cstr && dont_complete_typenames_cstr[0]) { const char *die_name = die.GetName(); if (die_name && die_name[0]) { const char *match = strstr(dont_complete_typenames_cstr, die_name); if (match) { size_t die_name_length = strlen(die_name); while (match) { const char separator_char = ';'; const char next_char = match[die_name_length]; if (next_char == '\0' || next_char == separator_char) { if (match == dont_complete_typenames_cstr || match[-1] == separator_char) return false; } match = strstr(match + 1, die_name); } } } } #endif const dw_tag_t tag = die.Tag(); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION)); if (log) dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", die.GetID(), die.GetTagAsCString(), type->GetName().AsCString()); assert(clang_type); DWARFAttributes attributes; switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { ClangASTImporter::LayoutInfo layout_info; { if (die.HasChildren()) { LanguageType class_language = eLanguageTypeUnknown; if (ClangASTContext::IsObjCObjectOrInterfaceType(clang_type)) { class_language = eLanguageTypeObjC; // For objective C we don't start the definition when // the class is created. ClangASTContext::StartTagDeclarationDefinition(clang_type); } int tag_decl_kind = -1; AccessType default_accessibility = eAccessNone; if (tag == DW_TAG_structure_type) { tag_decl_kind = clang::TTK_Struct; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_union_type) { tag_decl_kind = clang::TTK_Union; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_class_type) { tag_decl_kind = clang::TTK_Class; default_accessibility = eAccessPrivate; } SymbolContext sc(die.GetLLDBCompileUnit()); std::vector base_classes; std::vector member_accessibilities; bool is_a_class = false; // Parse members and base classes first DWARFDIECollection member_function_dies; DelayedPropertyList delayed_properties; ParseChildMembers(sc, die, clang_type, class_language, base_classes, member_accessibilities, member_function_dies, delayed_properties, default_accessibility, is_a_class, layout_info); // Now parse any methods if there were any... size_t num_functions = member_function_dies.Size(); if (num_functions > 0) { for (size_t i = 0; i < num_functions; ++i) { dwarf->ResolveType(member_function_dies.GetDIEAtIndex(i)); } } if (class_language == eLanguageTypeObjC) { ConstString class_name(clang_type.GetTypeName()); if (class_name) { DIEArray method_die_offsets; dwarf->GetObjCMethodDIEOffsets(class_name, method_die_offsets); if (!method_die_offsets.empty()) { DWARFDebugInfo *debug_info = dwarf->DebugInfo(); const size_t num_matches = method_die_offsets.size(); for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = method_die_offsets[i]; DWARFDIE method_die = debug_info->GetDIE(die_ref); if (method_die) method_die.ResolveType(); } } for (DelayedPropertyList::iterator pi = delayed_properties.begin(), pe = delayed_properties.end(); pi != pe; ++pi) pi->Finalize(); } } // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we // need to tell the clang type it is actually a class. if (class_language != eLanguageTypeObjC) { if (is_a_class && tag_decl_kind != clang::TTK_Class) m_ast.SetTagTypeKind(ClangUtil::GetQualType(clang_type), clang::TTK_Class); } // Since DW_TAG_structure_type gets used for both classes // and structures, we may need to set any DW_TAG_member // fields to have a "private" access if none was specified. // When we parsed the child members we tracked that actual // accessibility value for each DW_TAG_member in the // "member_accessibilities" array. If the value for the // member is zero, then it was set to the "default_accessibility" // which for structs was "public". Below we correct this // by setting any fields to "private" that weren't correctly // set. if (is_a_class && !member_accessibilities.empty()) { // This is a class and all members that didn't have // their access specified are private. m_ast.SetDefaultAccessForRecordFields( m_ast.GetAsRecordDecl(clang_type), eAccessPrivate, &member_accessibilities.front(), member_accessibilities.size()); } if (!base_classes.empty()) { // Make sure all base classes refer to complete types and not // forward declarations. If we don't do this, clang will crash // with an assertion in the call to // clang_type.SetBaseClassesForClassType() for (auto &base_class : base_classes) { clang::TypeSourceInfo *type_source_info = base_class->getTypeSourceInfo(); if (type_source_info) { CompilerType base_class_type( &m_ast, type_source_info->getType().getAsOpaquePtr()); if (base_class_type.GetCompleteType() == false) { auto module = dwarf->GetObjectFile()->GetModule(); module->ReportError(":: Class '%s' has a base class '%s' which " "does not have a complete definition.", die.GetName(), base_class_type.GetTypeName().GetCString()); if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module->ReportError(":: Try compiling the source file with " "-fno-limit-debug-info."); // We have no choice other than to pretend that the base class // is complete. If we don't do this, clang will crash when we // call setBases() inside of // "clang_type.SetBaseClassesForClassType()" // below. Since we provide layout assistance, all ivars in this // class and other classes will be fine, this is the best we can // do // short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( base_class_type)) { ClangASTContext::CompleteTagDeclarationDefinition( base_class_type); } } } } m_ast.SetBaseClassesForClassType(clang_type.GetOpaqueQualType(), &base_classes.front(), base_classes.size()); // Clang will copy each CXXBaseSpecifier in "base_classes" // so we have to free them all. ClangASTContext::DeleteBaseClassSpecifiers(&base_classes.front(), base_classes.size()); } } } ClangASTContext::BuildIndirectFields(clang_type); ClangASTContext::CompleteTagDeclarationDefinition(clang_type); if (!layout_info.field_offsets.empty() || !layout_info.base_offsets.empty() || !layout_info.vbase_offsets.empty()) { if (type) layout_info.bit_size = type->GetByteSize() * 8; if (layout_info.bit_size == 0) layout_info.bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; clang::CXXRecordDecl *record_decl = m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); if (record_decl) { if (log) { ModuleSP module_sp = dwarf->GetObjectFile()->GetModule(); if (module_sp) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = %p) " "caching layout info for record_decl = %p, bit_size = %" PRIu64 ", alignment = %" PRIu64 ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])", static_cast(clang_type.GetOpaqueQualType()), static_cast(record_decl), layout_info.bit_size, layout_info.alignment, static_cast(layout_info.field_offsets.size()), static_cast(layout_info.base_offsets.size()), static_cast(layout_info.vbase_offsets.size())); uint32_t idx; { llvm::DenseMap::const_iterator pos, end = layout_info.field_offsets.end(); for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end; ++pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) field[%u] = { bit_offset=%u, name='%s' }", static_cast(clang_type.GetOpaqueQualType()), idx, static_cast(pos->second), pos->first->getNameAsString().c_str()); } } { llvm::DenseMap::const_iterator base_pos, base_end = layout_info.base_offsets.end(); for (idx = 0, base_pos = layout_info.base_offsets.begin(); base_pos != base_end; ++base_pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) base[%u] = { byte_offset=%u, name='%s' }", clang_type.GetOpaqueQualType(), idx, (uint32_t)base_pos->second.getQuantity(), base_pos->first->getNameAsString().c_str()); } } { llvm::DenseMap::const_iterator vbase_pos, vbase_end = layout_info.vbase_offsets.end(); for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin(); vbase_pos != vbase_end; ++vbase_pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) vbase[%u] = { byte_offset=%u, name='%s' }", static_cast(clang_type.GetOpaqueQualType()), idx, static_cast(vbase_pos->second.getQuantity()), vbase_pos->first->getNameAsString().c_str()); } } } } GetClangASTImporter().InsertRecordDecl(record_decl, layout_info); } } } return (bool)clang_type; case DW_TAG_enumeration_type: if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { if (die.HasChildren()) { SymbolContext sc(die.GetLLDBCompileUnit()); bool is_signed = false; clang_type.IsIntegerType(is_signed); ParseChildEnumerators(sc, clang_type, is_signed, type->GetByteSize(), die); } ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } return (bool)clang_type; default: assert(false && "not a forward clang type decl!"); break; } return false; } std::vector DWARFASTParserClang::GetDIEForDeclContext( lldb_private::CompilerDeclContext decl_context) { std::vector result; for (auto it = m_decl_ctx_to_die.find( (clang::DeclContext *)decl_context.GetOpaqueDeclContext()); it != m_decl_ctx_to_die.end(); it++) result.push_back(it->second); return result; } CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) { clang::Decl *clang_decl = GetClangDeclForDIE(die); if (clang_decl != nullptr) return CompilerDecl(&m_ast, clang_decl); return CompilerDecl(); } CompilerDeclContext DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die); if (clang_decl_ctx) return CompilerDeclContext(&m_ast, clang_decl_ctx); return CompilerDeclContext(); } CompilerDeclContext DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); if (clang_decl_ctx) return CompilerDeclContext(&m_ast, clang_decl_ctx); return CompilerDeclContext(); } size_t DWARFASTParserClang::ParseChildEnumerators( const SymbolContext &sc, lldb_private::CompilerType &clang_type, bool is_signed, uint32_t enumerator_byte_size, const DWARFDIE &parent_die) { if (!parent_die) return 0; size_t enumerators_added = 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); if (tag == DW_TAG_enumerator) { DWARFAttributes attributes; const size_t num_child_attributes = die.GetAttributes(attributes); if (num_child_attributes > 0) { const char *name = NULL; bool got_value = false; int64_t enum_value = 0; Declaration decl; uint32_t i; for (i = 0; i < num_child_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_const_value: got_value = true; if (is_signed) enum_value = form_value.Signed(); else enum_value = form_value.Unsigned(); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_description: default: case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_sibling: break; } } } if (name && name[0] && got_value) { m_ast.AddEnumerationValueToEnumerationType( clang_type.GetOpaqueQualType(), m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType()), decl, name, enum_value, enumerator_byte_size * 8); ++enumerators_added; } } } } return enumerators_added; } #if defined(LLDB_CONFIGURATION_DEBUG) || defined(LLDB_CONFIGURATION_RELEASE) class DIEStack { public: void Push(const DWARFDIE &die) { m_dies.push_back(die); } void LogDIEs(Log *log) { StreamString log_strm; const size_t n = m_dies.size(); log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n); for (size_t i = 0; i < n; i++) { std::string qualified_name; const DWARFDIE &die = m_dies[i]; die.GetQualifiedName(qualified_name); log_strm.Printf("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", (uint64_t)i, die.GetOffset(), die.GetTagAsCString(), qualified_name.c_str()); } log->PutCString(log_strm.GetData()); } void Pop() { m_dies.pop_back(); } class ScopedPopper { public: ScopedPopper(DIEStack &die_stack) : m_die_stack(die_stack), m_valid(false) {} void Push(const DWARFDIE &die) { m_valid = true; m_die_stack.Push(die); } ~ScopedPopper() { if (m_valid) m_die_stack.Pop(); } protected: DIEStack &m_die_stack; bool m_valid; }; protected: typedef std::vector Stack; Stack m_dies; }; #endif Function *DWARFASTParserClang::ParseFunctionFromDWARF(const SymbolContext &sc, const DWARFDIE &die) { DWARFRangeList func_ranges; const char *name = NULL; const char *mangled = NULL; int decl_file = 0; int decl_line = 0; int decl_column = 0; int call_file = 0; int call_line = 0; int call_column = 0; DWARFExpression frame_base(die.GetCU()); const dw_tag_t tag = die.Tag(); if (tag != DW_TAG_subprogram) return NULL; if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, &frame_base)) { // Union of all ranges in the function DIE (if the function is // discontiguous) AddressRange func_range; lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0); lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0); if (lowest_func_addr != LLDB_INVALID_ADDRESS && lowest_func_addr <= highest_func_addr) { ModuleSP module_sp(die.GetModule()); func_range.GetBaseAddress().ResolveAddressUsingFileSections( lowest_func_addr, module_sp->GetSectionList()); if (func_range.GetBaseAddress().IsValid()) func_range.SetByteSize(highest_func_addr - lowest_func_addr); } if (func_range.GetBaseAddress().IsValid()) { Mangled func_name; if (mangled) func_name.SetValue(ConstString(mangled), true); else if (die.GetParent().Tag() == DW_TAG_compile_unit && Language::LanguageIsCPlusPlus(die.GetLanguage()) && name && strcmp(name, "main") != 0) { // If the mangled name is not present in the DWARF, generate the // demangled name // using the decl context. We skip if the function is "main" as its name // is // never mangled. bool is_static = false; bool is_variadic = false; bool has_template_params = false; unsigned type_quals = 0; std::vector param_types; std::vector param_decls; DWARFDeclContext decl_ctx; StreamString sstr; die.GetDWARFDeclContext(decl_ctx); sstr << decl_ctx.GetQualifiedName(); clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); ParseChildParameters(sc, containing_decl_ctx, die, true, is_static, is_variadic, has_template_params, param_types, param_decls, type_quals); sstr << "("; for (size_t i = 0; i < param_types.size(); i++) { if (i > 0) sstr << ", "; sstr << param_types[i].GetTypeName(); } if (is_variadic) sstr << ", ..."; sstr << ")"; if (type_quals & clang::Qualifiers::Const) sstr << " const"; func_name.SetValue(ConstString(sstr.GetString()), false); } else func_name.SetValue(ConstString(name), false); FunctionSP func_sp; std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); SymbolFileDWARF *dwarf = die.GetDWARF(); // Supply the type _only_ if it has already been parsed Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE()); assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); if (dwarf->FixupAddress(func_range.GetBaseAddress())) { const user_id_t func_user_id = die.GetID(); func_sp.reset(new Function(sc.comp_unit, func_user_id, // UserID is the DIE offset func_user_id, func_name, func_type, func_range)); // first address range if (func_sp.get() != NULL) { if (frame_base.IsValid()) func_sp->GetFrameBaseExpression() = frame_base; sc.comp_unit->AddFunction(func_sp); return func_sp.get(); } } } } return NULL; } bool DWARFASTParserClang::ParseChildMembers( const SymbolContext &sc, const DWARFDIE &parent_die, CompilerType &class_clang_type, const LanguageType class_language, std::vector &base_classes, std::vector &member_accessibilities, DWARFDIECollection &member_function_dies, DelayedPropertyList &delayed_properties, AccessType &default_accessibility, bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) { if (!parent_die) return 0; // Get the parent byte size so we can verify any members will fit const uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); const uint64_t parent_bit_size = parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8; uint32_t member_idx = 0; BitfieldInfo last_field_info; ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); ClangASTContext *ast = llvm::dyn_cast_or_null(class_clang_type.GetTypeSystem()); if (ast == nullptr) return 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_member: case DW_TAG_APPLE_property: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { Declaration decl; // DWARFExpression location; const char *name = NULL; const char *prop_name = NULL; const char *prop_getter_name = NULL; const char *prop_setter_name = NULL; uint32_t prop_attributes = 0; bool is_artificial = false; DWARFFormValue encoding_form; AccessType accessibility = eAccessNone; uint32_t member_byte_offset = (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; size_t byte_size = 0; int64_t bit_offset = 0; uint64_t data_bit_offset = UINT64_MAX; size_t bit_size = 0; bool is_external = false; // On DW_TAG_members, this means the member is static uint32_t i; for (i = 0; i < num_attributes && !is_artificial; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_bit_offset: bit_offset = form_value.Signed(); break; case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_data_bit_offset: data_bit_offset = form_value.Unsigned(); break; case DW_AT_data_member_location: if (form_value.BlockData()) { Value initialValue(0); Value memberOffset(0); const DWARFDataExtractor &debug_info_data = die.GetDWARF()->get_debug_info_data(); uint32_t block_length = form_value.Unsigned(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); if (DWARFExpression::Evaluate( nullptr, // ExecutionContext * nullptr, // ClangExpressionVariableList * nullptr, // ClangExpressionDeclMap * nullptr, // RegisterContext * module_sp, debug_info_data, die.GetCU(), block_offset, block_length, eRegisterKindDWARF, &initialValue, nullptr, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); } } else { // With DWARF 3 and later, if the value is an integer constant, // this form value is the offset in bytes from the beginning // of the containing entity. member_byte_offset = form_value.Unsigned(); } break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_APPLE_property_name: prop_name = form_value.AsCString(); break; case DW_AT_APPLE_property_getter: prop_getter_name = form_value.AsCString(); break; case DW_AT_APPLE_property_setter: prop_setter_name = form_value.AsCString(); break; case DW_AT_APPLE_property_attribute: prop_attributes = form_value.Unsigned(); break; case DW_AT_external: is_external = form_value.Boolean(); break; default: case DW_AT_declaration: case DW_AT_description: case DW_AT_mutable: case DW_AT_visibility: case DW_AT_sibling: break; } } } if (prop_name) { ConstString fixed_getter; ConstString fixed_setter; // Check if the property getter/setter were provided as full // names. We want basenames, so we extract them. if (prop_getter_name && prop_getter_name[0] == '-') { ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true); prop_getter_name = prop_getter_method.GetSelector().GetCString(); } if (prop_setter_name && prop_setter_name[0] == '-') { ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true); prop_setter_name = prop_setter_method.GetSelector().GetCString(); } // If the names haven't been provided, they need to be // filled in. if (!prop_getter_name) { prop_getter_name = prop_name; } if (!prop_setter_name && prop_name[0] && !(prop_attributes & DW_APPLE_PROPERTY_readonly)) { StreamString ss; ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]); fixed_setter.SetString(ss.GetString()); prop_setter_name = fixed_setter.GetCString(); } } // Clang has a DWARF generation bug where sometimes it // represents fields that are references with bad byte size // and bit size/offset information such as: // // DW_AT_byte_size( 0x00 ) // DW_AT_bit_size( 0x40 ) // DW_AT_bit_offset( 0xffffffffffffffc0 ) // // So check the bit offset to make sure it is sane, and if // the values are not sane, remove them. If we don't do this // then we will end up with a crash if we try to use this // type in an expression when clang becomes unhappy with its // recycled debug info. if (byte_size == 0 && bit_offset < 0) { bit_size = 0; bit_offset = 0; } // FIXME: Make Clang ignore Objective-C accessibility for expressions if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) accessibility = eAccessNone; if (member_idx == 0 && !is_artificial && name && (strstr(name, "_vptr$") == name)) { // Not all compilers will mark the vtable pointer // member as artificial (llvm-gcc). We can't have // the virtual members in our classes otherwise it // throws off all child offsets since we end up // having and extra pointer sized member in our // class layouts. is_artificial = true; } // Handle static members if (is_external && member_byte_offset == UINT32_MAX) { Type *var_type = die.ResolveTypeUID(DIERef(encoding_form)); if (var_type) { if (accessibility == eAccessNone) accessibility = eAccessPublic; ClangASTContext::AddVariableToRecordType( class_clang_type, name, var_type->GetLayoutCompilerType(), accessibility); } break; } if (is_artificial == false) { Type *member_type = die.ResolveTypeUID(DIERef(encoding_form)); clang::FieldDecl *field_decl = NULL; if (tag == DW_TAG_member) { if (member_type) { if (accessibility == eAccessNone) accessibility = default_accessibility; member_accessibilities.push_back(accessibility); uint64_t field_bit_offset = (member_byte_offset == UINT32_MAX ? 0 : (member_byte_offset * 8)); if (bit_size > 0) { BitfieldInfo this_field_info; this_field_info.bit_offset = field_bit_offset; this_field_info.bit_size = bit_size; ///////////////////////////////////////////////////////////// // How to locate a field given the DWARF debug information // // AT_byte_size indicates the size of the word in which the // bit offset must be interpreted. // // AT_data_member_location indicates the byte offset of the // word from the base address of the structure. // // AT_bit_offset indicates how many bits into the word // (according to the host endianness) the low-order bit of // the field starts. AT_bit_offset can be negative. // // AT_bit_size indicates the size of the field in bits. ///////////////////////////////////////////////////////////// if (data_bit_offset != UINT64_MAX) { this_field_info.bit_offset = data_bit_offset; } else { if (byte_size == 0) byte_size = member_type->GetByteSize(); ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); if (objfile->GetByteOrder() == eByteOrderLittle) { this_field_info.bit_offset += byte_size * 8; this_field_info.bit_offset -= (bit_offset + bit_size); } else { this_field_info.bit_offset += bit_offset; } } if ((this_field_info.bit_offset >= parent_bit_size) || !last_field_info.NextBitfieldOffsetIsValid( this_field_info.bit_offset)) { ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); objfile->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid " "bit offset (0x%8.8" PRIx64 ") member will be ignored. Please file a bug against the " "compiler and include the preprocessed output for %s\n", die.GetID(), DW_TAG_value_to_name(tag), name, this_field_info.bit_offset, sc.comp_unit ? sc.comp_unit->GetPath().c_str() : "the source file"); this_field_info.Clear(); continue; } // Update the field bit offset we will report for layout field_bit_offset = this_field_info.bit_offset; // If the member to be emitted did not start on a character // boundary and there is // empty space between the last field and this one, then we need // to emit an // anonymous member filling up the space up to its start. There // are three cases // here: // // 1 If the previous member ended on a character boundary, then // we can emit an // anonymous member starting at the most recent character // boundary. // // 2 If the previous member did not end on a character boundary // and the distance // from the end of the previous member to the current member // is less than a // word width, then we can emit an anonymous member starting // right after the // previous member and right before this member. // // 3 If the previous member did not end on a character boundary // and the distance // from the end of the previous member to the current member // is greater than // or equal a word width, then we act as in Case 1. const uint64_t character_width = 8; const uint64_t word_width = 32; // Objective-C has invalid DW_AT_bit_offset values in older // versions // of clang, so we have to be careful and only insert unnamed // bitfields // if we have a new enough clang. bool detect_unnamed_bitfields = true; if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) detect_unnamed_bitfields = die.GetCU()->Supports_unnamed_objc_bitfields(); if (detect_unnamed_bitfields) { BitfieldInfo anon_field_info; if ((this_field_info.bit_offset % character_width) != 0) // not char aligned { uint64_t last_field_end = 0; if (last_field_info.IsValid()) last_field_end = last_field_info.bit_offset + last_field_info.bit_size; if (this_field_info.bit_offset != last_field_end) { if (((last_field_end % character_width) == 0) || // case 1 (this_field_info.bit_offset - last_field_end >= word_width)) // case 3 { anon_field_info.bit_size = this_field_info.bit_offset % character_width; anon_field_info.bit_offset = this_field_info.bit_offset - anon_field_info.bit_size; } else // case 2 { anon_field_info.bit_size = this_field_info.bit_offset - last_field_end; anon_field_info.bit_offset = last_field_end; } } } if (anon_field_info.IsValid()) { clang::FieldDecl *unnamed_bitfield_decl = ClangASTContext::AddFieldToRecordType( class_clang_type, NULL, m_ast.GetBuiltinTypeForEncodingAndBitSize( eEncodingSint, word_width), accessibility, anon_field_info.bit_size); layout_info.field_offsets.insert(std::make_pair( unnamed_bitfield_decl, anon_field_info.bit_offset)); } } last_field_info = this_field_info; } else { last_field_info.Clear(); } CompilerType member_clang_type = member_type->GetLayoutCompilerType(); if (!member_clang_type.IsCompleteType()) member_clang_type.GetCompleteType(); { // Older versions of clang emit array[0] and array[1] in the // same way (). // If the current field is at the end of the structure, then // there is definitely no room for extra // elements and we override the type to array[0]. CompilerType member_array_element_type; uint64_t member_array_size; bool member_array_is_incomplete; if (member_clang_type.IsArrayType( &member_array_element_type, &member_array_size, &member_array_is_incomplete) && !member_array_is_incomplete) { uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); if (member_byte_offset >= parent_byte_size) { if (member_array_size != 1 && (member_array_size != 0 || member_byte_offset > parent_byte_size)) { module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which extends beyond the bounds of 0x%8.8" PRIx64, die.GetID(), name, encoding_form.Reference(), parent_die.GetID()); } member_clang_type = m_ast.CreateArrayType( member_array_element_type, 0, false); } } } if (ClangASTContext::IsCXXClassType(member_clang_type) && member_clang_type.GetCompleteType() == false) { if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type is a forward declaration, not a " "complete definition.\nTry compiling the source file " "with -fno-limit-debug-info", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name); else module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type is a forward declaration, not a " "complete definition.\nPlease file a bug against the " "compiler and include the preprocessed output for %s", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name, sc.comp_unit ? sc.comp_unit->GetPath().c_str() : "the source file"); // We have no choice other than to pretend that the member class // is complete. If we don't do this, clang will crash when // trying // to layout the class. Since we provide layout assistance, all // ivars in this class and other classes will be fine, this is // the best we can do short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( member_clang_type)) { ClangASTContext::CompleteTagDeclarationDefinition( member_clang_type); } else { module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type claims to be a C++ class but we " "were not able to start its definition.\nPlease file a " "bug and attach the file at the start of this error " "message", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name); } } field_decl = ClangASTContext::AddFieldToRecordType( class_clang_type, name, member_clang_type, accessibility, bit_size); m_ast.SetMetadataAsUserID(field_decl, die.GetID()); layout_info.field_offsets.insert( std::make_pair(field_decl, field_bit_offset)); } else { if (name) module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which was unable to be parsed", die.GetID(), name, encoding_form.Reference()); else module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8" PRIx64 " which was unable to be parsed", die.GetID(), encoding_form.Reference()); } } if (prop_name != NULL && member_type) { clang::ObjCIvarDecl *ivar_decl = NULL; if (field_decl) { ivar_decl = clang::dyn_cast(field_decl); assert(ivar_decl != NULL); } ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); delayed_properties.push_back(DelayedAddObjCClassProperty( class_clang_type, prop_name, member_type->GetLayoutCompilerType(), ivar_decl, prop_setter_name, prop_getter_name, prop_attributes, &metadata)); if (ivar_decl) m_ast.SetMetadataAsUserID(ivar_decl, die.GetID()); } } } ++member_idx; } break; case DW_TAG_subprogram: // Let the type parsing code handle this one for us. member_function_dies.Append(die); break; case DW_TAG_inheritance: { is_a_class = true; if (default_accessibility == eAccessNone) default_accessibility = eAccessPrivate; // TODO: implement DW_TAG_inheritance type parsing DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { Declaration decl; DWARFExpression location(die.GetCU()); DWARFFormValue encoding_form; AccessType accessibility = default_accessibility; bool is_virtual = false; bool is_base_of_class = true; off_t member_byte_offset = 0; uint32_t i; for (i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_data_member_location: if (form_value.BlockData()) { Value initialValue(0); Value memberOffset(0); const DWARFDataExtractor &debug_info_data = die.GetDWARF()->get_debug_info_data(); uint32_t block_length = form_value.Unsigned(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); if (DWARFExpression::Evaluate( nullptr, nullptr, nullptr, nullptr, module_sp, debug_info_data, die.GetCU(), block_offset, block_length, eRegisterKindDWARF, &initialValue, nullptr, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); } } else { // With DWARF 3 and later, if the value is an integer constant, // this form value is the offset in bytes from the beginning // of the containing entity. member_byte_offset = form_value.Unsigned(); } break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_virtuality: is_virtual = form_value.Boolean(); break; case DW_AT_sibling: break; default: break; } } } Type *base_class_type = die.ResolveTypeUID(DIERef(encoding_form)); if (base_class_type == NULL) { module_sp->ReportError("0x%8.8x: DW_TAG_inheritance failed to " "resolve the base class at 0x%8.8" PRIx64 " from enclosing type 0x%8.8x. \nPlease file " "a bug and attach the file at the start of " "this error message", die.GetOffset(), encoding_form.Reference(), parent_die.GetOffset()); break; } CompilerType base_class_clang_type = base_class_type->GetFullCompilerType(); assert(base_class_clang_type); if (class_language == eLanguageTypeObjC) { ast->SetObjCSuperClass(class_clang_type, base_class_clang_type); } else { base_classes.push_back(ast->CreateBaseClassSpecifier( base_class_clang_type.GetOpaqueQualType(), accessibility, is_virtual, is_base_of_class)); if (is_virtual) { // Do not specify any offset for virtual inheritance. The DWARF // produced by clang doesn't // give us a constant offset, but gives us a DWARF expressions that // requires an actual object // in memory. the DW_AT_data_member_location for a virtual base // class looks like: // DW_AT_data_member_location( DW_OP_dup, DW_OP_deref, // DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref, // DW_OP_plus ) // Given this, there is really no valid response we can give to // clang for virtual base // class offsets, and this should eventually be removed from // LayoutRecordType() in the external // AST source in clang. } else { layout_info.base_offsets.insert(std::make_pair( ast->GetAsCXXRecordDecl( base_class_clang_type.GetOpaqueQualType()), clang::CharUnits::fromQuantity(member_byte_offset))); } } } } break; default: break; } } return true; } size_t DWARFASTParserClang::ParseChildParameters( const SymbolContext &sc, clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die, bool skip_artificial, bool &is_static, bool &is_variadic, bool &has_template_params, std::vector &function_param_types, std::vector &function_param_decls, unsigned &type_quals) { if (!parent_die) return 0; size_t arg_idx = 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_formal_parameter: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { const char *name = NULL; Declaration decl; DWARFFormValue param_type_die_form; bool is_artificial = false; // one of None, Auto, Register, Extern, Static, PrivateExtern clang::StorageClass storage = clang::SC_None; uint32_t i; for (i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_type: param_type_die_form = form_value; break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_location: // if (form_value.BlockData()) // { // const DWARFDataExtractor& // debug_info_data = debug_info(); // uint32_t block_length = // form_value.Unsigned(); // DWARFDataExtractor // location(debug_info_data, // form_value.BlockData() - // debug_info_data.GetDataStart(), // block_length); // } // else // { // } // break; case DW_AT_const_value: case DW_AT_default_value: case DW_AT_description: case DW_AT_endianity: case DW_AT_is_optional: case DW_AT_segment: case DW_AT_variable_parameter: default: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } bool skip = false; if (skip_artificial) { if (is_artificial) { // In order to determine if a C++ member function is // "const" we have to look at the const-ness of "this"... // Ugly, but that if (arg_idx == 0) { if (DeclKindIsCXXClass(containing_decl_ctx->getDeclKind())) { // Often times compilers omit the "this" name for the // specification DIEs, so we can't rely upon the name // being in the formal parameter DIE... if (name == NULL || ::strcmp(name, "this") == 0) { Type *this_type = die.ResolveTypeUID(DIERef(param_type_die_form)); if (this_type) { uint32_t encoding_mask = this_type->GetEncodingMask(); if (encoding_mask & Type::eEncodingIsPointerUID) { is_static = false; if (encoding_mask & (1u << Type::eEncodingIsConstUID)) type_quals |= clang::Qualifiers::Const; if (encoding_mask & (1u << Type::eEncodingIsVolatileUID)) type_quals |= clang::Qualifiers::Volatile; } } } } } skip = true; } else { // HACK: Objective C formal parameters "self" and "_cmd" // are not marked as artificial in the DWARF... CompileUnit *comp_unit = die.GetLLDBCompileUnit(); if (comp_unit) { switch (comp_unit->GetLanguage()) { case eLanguageTypeObjC: case eLanguageTypeObjC_plus_plus: if (name && name[0] && (strcmp(name, "self") == 0 || strcmp(name, "_cmd") == 0)) skip = true; break; default: break; } } } } if (!skip) { Type *type = die.ResolveTypeUID(DIERef(param_type_die_form)); if (type) { function_param_types.push_back(type->GetForwardCompilerType()); clang::ParmVarDecl *param_var_decl = m_ast.CreateParameterDeclaration( name, type->GetForwardCompilerType(), storage); assert(param_var_decl); function_param_decls.push_back(param_var_decl); m_ast.SetMetadataAsUserID(param_var_decl, die.GetID()); } } } arg_idx++; } break; case DW_TAG_unspecified_parameters: is_variadic = true; break; case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: // The one caller of this was never using the template_param_infos, // and the local variable was taking up a large amount of stack space // in SymbolFileDWARF::ParseType() so this was removed. If we ever need // the template params back, we can add them back. // ParseTemplateDIE (dwarf_cu, die, template_param_infos); has_template_params = true; break; default: break; } } return arg_idx; } void DWARFASTParserClang::ParseChildArrayInfo( const SymbolContext &sc, const DWARFDIE &parent_die, int64_t &first_index, std::vector &element_orders, uint32_t &byte_stride, uint32_t &bit_stride) { if (!parent_die) return; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_subrange_type: { DWARFAttributes attributes; const size_t num_child_attributes = die.GetAttributes(attributes); if (num_child_attributes > 0) { uint64_t num_elements = 0; uint64_t lower_bound = 0; uint64_t upper_bound = 0; bool upper_bound_valid = false; uint32_t i; for (i = 0; i < num_child_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_name: break; case DW_AT_count: num_elements = form_value.Unsigned(); break; case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; case DW_AT_lower_bound: lower_bound = form_value.Unsigned(); break; case DW_AT_upper_bound: upper_bound_valid = true; upper_bound = form_value.Unsigned(); break; default: case DW_AT_abstract_origin: case DW_AT_accessibility: case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_declaration: case DW_AT_description: case DW_AT_sibling: case DW_AT_threads_scaled: case DW_AT_type: case DW_AT_visibility: break; } } } if (num_elements == 0) { if (upper_bound_valid && upper_bound >= lower_bound) num_elements = upper_bound - lower_bound + 1; } element_orders.push_back(num_elements); } } break; } } } Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) { if (die) { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { DWARFFormValue type_die_form; for (size_t i = 0; i < num_attributes; ++i) { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attr == DW_AT_type && attributes.ExtractFormValueAtIndex(i, form_value)) return dwarf->ResolveTypeUID(dwarf->GetDIE(DIERef(form_value)), true); } } } return nullptr; } clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) { if (!die) return nullptr; switch (die.Tag()) { case DW_TAG_variable: case DW_TAG_constant: case DW_TAG_formal_parameter: case DW_TAG_imported_declaration: case DW_TAG_imported_module: break; default: return nullptr; } DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE()); if (cache_pos != m_die_to_decl.end()) return cache_pos->second; if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) { clang::Decl *decl = GetClangDeclForDIE(spec_die); m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } if (DWARFDIE abstract_origin_die = die.GetReferencedDIE(DW_AT_abstract_origin)) { clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die); m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } clang::Decl *decl = nullptr; switch (die.Tag()) { case DW_TAG_variable: case DW_TAG_constant: case DW_TAG_formal_parameter: { SymbolFileDWARF *dwarf = die.GetDWARF(); Type *type = GetTypeForDIE(die); if (dwarf && type) { const char *name = die.GetName(); clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); decl = m_ast.CreateVariableDeclaration( decl_context, name, ClangUtil::GetQualType(type->GetForwardCompilerType())); } break; } case DW_TAG_imported_declaration: { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); if (imported_uid) { CompilerDecl imported_decl = imported_uid.GetDecl(); if (imported_decl) { clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); if (clang::NamedDecl *clang_imported_decl = llvm::dyn_cast( (clang::Decl *)imported_decl.GetOpaqueDecl())) decl = m_ast.CreateUsingDeclaration(decl_context, clang_imported_decl); } } break; } case DW_TAG_imported_module: { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); if (imported_uid) { CompilerDeclContext imported_decl_ctx = imported_uid.GetDeclContext(); if (imported_decl_ctx) { clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); if (clang::NamespaceDecl *ns_decl = ClangASTContext::DeclContextGetAsNamespaceDecl( imported_decl_ctx)) decl = m_ast.CreateUsingDirectiveDeclaration(decl_context, ns_decl); } } break; } default: break; } m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } clang::DeclContext * DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) { if (die) { clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die); if (decl_ctx) return decl_ctx; bool try_parsing_type = true; switch (die.Tag()) { case DW_TAG_compile_unit: decl_ctx = m_ast.GetTranslationUnitDecl(); try_parsing_type = false; break; case DW_TAG_namespace: decl_ctx = ResolveNamespaceDIE(die); try_parsing_type = false; break; case DW_TAG_lexical_block: - decl_ctx = (clang::DeclContext *)ResolveBlockDIE(die); + decl_ctx = GetDeclContextForBlock(die); try_parsing_type = false; break; default: break; } if (decl_ctx == nullptr && try_parsing_type) { Type *type = die.GetDWARF()->ResolveType(die); if (type) decl_ctx = GetCachedClangDeclContextForDIE(die); } if (decl_ctx) { LinkDeclContextToDIE(decl_ctx, die); return decl_ctx; } } return nullptr; +} + +static bool IsSubroutine(const DWARFDIE &die) { + switch (die.Tag()) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + return true; + default: + return false; + } +} + +static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) { + for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) { + if (IsSubroutine(candidate)) { + if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { + return candidate; + } else { + return DWARFDIE(); + } + } + } + assert(!"Shouldn't call GetContainingFunctionWithAbstractOrigin on something " + "not in a function"); + return DWARFDIE(); +} + +static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) { + for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid(); + candidate = candidate.GetSibling()) { + if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { + return candidate; + } + } + return DWARFDIE(); +} + +static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block, + const DWARFDIE &function) { + assert(IsSubroutine(function)); + for (DWARFDIE context = block; context != function.GetParent(); + context = context.GetParent()) { + assert(!IsSubroutine(context) || context == function); + if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) { + return child; + } + } + return DWARFDIE(); +} + +clang::DeclContext * +DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) { + assert(die.Tag() == DW_TAG_lexical_block); + DWARFDIE containing_function_with_abstract_origin = + GetContainingFunctionWithAbstractOrigin(die); + if (!containing_function_with_abstract_origin) { + return (clang::DeclContext *)ResolveBlockDIE(die); + } + DWARFDIE child = FindFirstChildWithAbstractOrigin( + die, containing_function_with_abstract_origin); + CompilerDeclContext decl_context = + GetDeclContextContainingUIDFromDWARF(child); + return (clang::DeclContext *)decl_context.GetOpaqueDeclContext(); } clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) { if (die && die.Tag() == DW_TAG_lexical_block) { clang::BlockDecl *decl = llvm::cast_or_null(m_die_to_decl_ctx[die.GetDIE()]); if (!decl) { DWARFDIE decl_context_die; clang::DeclContext *decl_context = GetClangDeclContextContainingDIE(die, &decl_context_die); decl = m_ast.CreateBlockDeclaration(decl_context); if (decl) LinkDeclContextToDIE((clang::DeclContext *)decl, die); } return decl; } return nullptr; } clang::NamespaceDecl * DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) { if (die && die.Tag() == DW_TAG_namespace) { // See if we already parsed this namespace DIE and associated it with a // uniqued namespace declaration clang::NamespaceDecl *namespace_decl = static_cast(m_die_to_decl_ctx[die.GetDIE()]); if (namespace_decl) return namespace_decl; else { const char *namespace_name = die.GetName(); clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); namespace_decl = m_ast.GetUniqueNamespaceDeclaration(namespace_name, containing_decl_ctx); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); if (log) { SymbolFileDWARF *dwarf = die.GetDWARF(); if (namespace_name) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace with DW_AT_name(\"%s\") => " "clang::NamespaceDecl *%p (original = %p)", static_cast(m_ast.getASTContext()), die.GetID(), namespace_name, static_cast(namespace_decl), static_cast(namespace_decl->getOriginalNamespace())); } else { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p " "(original = %p)", static_cast(m_ast.getASTContext()), die.GetID(), static_cast(namespace_decl), static_cast(namespace_decl->getOriginalNamespace())); } } if (namespace_decl) LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die); return namespace_decl; } } return nullptr; } clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE( const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die); if (decl_ctx_die_copy) *decl_ctx_die_copy = decl_ctx_die; if (decl_ctx_die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(decl_ctx_die); if (clang_decl_ctx) return clang_decl_ctx; } return m_ast.GetTranslationUnitDecl(); } clang::DeclContext * DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) { if (die) { DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE()); if (pos != m_die_to_decl_ctx.end()) return pos->second; } return nullptr; } void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die) { m_die_to_decl_ctx[die.GetDIE()] = decl_ctx; // There can be many DIEs for a single decl context // m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE()); m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die)); } bool DWARFASTParserClang::CopyUniqueClassMethodTypes( const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die, lldb_private::Type *class_type, DWARFDIECollection &failures) { if (!class_type || !src_class_die || !dst_class_die) return false; if (src_class_die.Tag() != dst_class_die.Tag()) return false; // We need to complete the class type so we can get all of the method types // parsed so we can then unique those types to their equivalent counterparts // in "dst_cu" and "dst_class_die" class_type->GetFullCompilerType(); DWARFDIE src_die; DWARFDIE dst_die; UniqueCStringMap src_name_to_die; UniqueCStringMap dst_name_to_die; UniqueCStringMap src_name_to_die_artificial; UniqueCStringMap dst_name_to_die_artificial; for (src_die = src_class_die.GetFirstChild(); src_die.IsValid(); src_die = src_die.GetSibling()) { if (src_die.Tag() == DW_TAG_subprogram) { // Make sure this is a declaration and not a concrete instance by looking // for DW_AT_declaration set to 1. Sometimes concrete function instances // are placed inside the class definitions and shouldn't be included in // the list of things are are tracking here. if (src_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { const char *src_name = src_die.GetMangledName(); if (src_name) { ConstString src_const_name(src_name); if (src_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) src_name_to_die_artificial.Append(src_const_name.GetStringRef(), src_die); else src_name_to_die.Append(src_const_name.GetStringRef(), src_die); } } } } for (dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid(); dst_die = dst_die.GetSibling()) { if (dst_die.Tag() == DW_TAG_subprogram) { // Make sure this is a declaration and not a concrete instance by looking // for DW_AT_declaration set to 1. Sometimes concrete function instances // are placed inside the class definitions and shouldn't be included in // the list of things are are tracking here. if (dst_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { const char *dst_name = dst_die.GetMangledName(); if (dst_name) { ConstString dst_const_name(dst_name); if (dst_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) dst_name_to_die_artificial.Append(dst_const_name.GetStringRef(), dst_die); else dst_name_to_die.Append(dst_const_name.GetStringRef(), dst_die); } } } } const uint32_t src_size = src_name_to_die.GetSize(); const uint32_t dst_size = dst_name_to_die.GetSize(); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | // DWARF_LOG_TYPE_COMPLETION)); // Is everything kosher so we can go through the members at top speed? bool fast_path = true; if (src_size != dst_size) { if (src_size != 0 && dst_size != 0) { if (log) log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, " "but they didn't have the same size (src=%d, dst=%d)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_size, dst_size); } fast_path = false; } uint32_t idx; if (fast_path) { for (idx = 0; idx < src_size; ++idx) { src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); if (src_die.Tag() != dst_die.Tag()) { if (log) log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " "but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_die.GetOffset(), src_die.GetTagAsCString(), dst_die.GetOffset(), dst_die.GetTagAsCString()); fast_path = false; } const char *src_name = src_die.GetMangledName(); const char *dst_name = dst_die.GetMangledName(); // Make sure the names match if (src_name == dst_name || (strcmp(src_name, dst_name) == 0)) continue; if (log) log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " "but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_die.GetOffset(), src_name, dst_die.GetOffset(), dst_name); fast_path = false; } } DWARFASTParserClang *src_dwarf_ast_parser = (DWARFASTParserClang *)src_die.GetDWARFParser(); DWARFASTParserClang *dst_dwarf_ast_parser = (DWARFASTParserClang *)dst_die.GetDWARFParser(); // Now do the work of linking the DeclContexts and Types. if (fast_path) { // We can do this quickly. Just run across the tables index-for-index since // we know each node has matching names and tags. for (idx = 0; idx < src_size; ++idx) { src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x for " "0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf( "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } } else { // We must do this slowly. For each member of the destination, look // up a member in the source with the same name, check its tag, and // unique them if everything matches up. Report failures. if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) { src_name_to_die.Sort(); for (idx = 0; idx < dst_size; ++idx) { llvm::StringRef dst_name = dst_name_to_die.GetCStringAtIndex(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); src_die = src_name_to_die.Find(dst_name, DWARFDIE()); if (src_die && (src_die.Tag() == dst_die.Tag())) { clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x " "for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf("uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } else { if (log) log->Printf("warning: couldn't find a match for 0x%8.8x", dst_die.GetOffset()); failures.Append(dst_die); } } } } const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize(); const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize(); if (src_size_artificial && dst_size_artificial) { dst_name_to_die_artificial.Sort(); for (idx = 0; idx < src_size_artificial; ++idx) { llvm::StringRef src_name_artificial = src_name_to_die_artificial.GetCStringAtIndex(idx); src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE()); if (dst_die) { // Both classes have the artificial types, link them clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x " "for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf( "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } } } if (dst_size_artificial) { for (idx = 0; idx < dst_size_artificial; ++idx) { llvm::StringRef dst_name_artificial = dst_name_to_die_artificial.GetCStringAtIndex(idx); dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx); if (log) log->Printf("warning: need to create artificial method for 0x%8.8x for " "method '%s'", dst_die.GetOffset(), dst_name_artificial.str().c_str()); failures.Append(dst_die); } } return (failures.Size() != 0); } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h (revision 317454) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h (revision 317455) @@ -1,159 +1,161 @@ //===-- DWARFASTParserClang.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SymbolFileDWARF_DWARFASTParserClang_h_ #define SymbolFileDWARF_DWARFASTParserClang_h_ // C Includes // C++ Includes // Other libraries and framework includes #include "clang/AST/CharUnits.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" // Project includes #include "DWARFASTParser.h" #include "DWARFDefines.h" #include "lldb/Core/ClangForward.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" class DWARFDebugInfoEntry; class DWARFDIECollection; class DWARFASTParserClang : public DWARFASTParser { public: DWARFASTParserClang(lldb_private::ClangASTContext &ast); ~DWARFASTParserClang() override; // DWARFASTParser interface. lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, const DWARFDIE &die, lldb_private::Log *log, bool *type_is_new_ptr) override; lldb_private::Function * ParseFunctionFromDWARF(const lldb_private::SymbolContext &sc, const DWARFDIE &die) override; bool CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, lldb_private::CompilerType &compiler_type) override; lldb_private::CompilerDecl GetDeclForUIDFromDWARF(const DWARFDIE &die) override; std::vector GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) override; lldb_private::CompilerDeclContext GetDeclContextForUIDFromDWARF(const DWARFDIE &die) override; lldb_private::CompilerDeclContext GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) override; lldb_private::ClangASTImporter &GetClangASTImporter(); protected: class DelayedAddObjCClassProperty; typedef std::vector DelayedPropertyList; + clang::DeclContext *GetDeclContextForBlock(const DWARFDIE &die); + clang::BlockDecl *ResolveBlockDIE(const DWARFDIE &die); clang::NamespaceDecl *ResolveNamespaceDIE(const DWARFDIE &die); bool ParseTemplateDIE(const DWARFDIE &die, lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); bool ParseTemplateParameterInfos( const DWARFDIE &parent_die, lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); bool ParseChildMembers(const lldb_private::SymbolContext &sc, const DWARFDIE &die, lldb_private::CompilerType &class_compiler_type, const lldb::LanguageType class_language, std::vector &base_classes, std::vector &member_accessibilities, DWARFDIECollection &member_function_dies, DelayedPropertyList &delayed_properties, lldb::AccessType &default_accessibility, bool &is_a_class, lldb_private::ClangASTImporter::LayoutInfo &layout_info); size_t ParseChildParameters(const lldb_private::SymbolContext &sc, clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die, bool skip_artificial, bool &is_static, bool &is_variadic, bool &has_template_params, std::vector &function_args, std::vector &function_param_decls, unsigned &type_quals); void ParseChildArrayInfo(const lldb_private::SymbolContext &sc, const DWARFDIE &parent_die, int64_t &first_index, std::vector &element_orders, uint32_t &byte_stride, uint32_t &bit_stride); size_t ParseChildEnumerators(const lldb_private::SymbolContext &sc, lldb_private::CompilerType &compiler_type, bool is_signed, uint32_t enumerator_byte_size, const DWARFDIE &parent_die); lldb_private::Type *GetTypeForDIE(const DWARFDIE &die); clang::Decl *GetClangDeclForDIE(const DWARFDIE &die); clang::DeclContext *GetClangDeclContextForDIE(const DWARFDIE &die); clang::DeclContext *GetClangDeclContextContainingDIE(const DWARFDIE &die, DWARFDIE *decl_ctx_die); bool CopyUniqueClassMethodTypes(const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die, lldb_private::Type *class_type, DWARFDIECollection &failures); clang::DeclContext *GetCachedClangDeclContextForDIE(const DWARFDIE &die); void LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die); void LinkDeclToDIE(clang::Decl *decl, const DWARFDIE &die); lldb::TypeSP ParseTypeFromDWO(const DWARFDIE &die, lldb_private::Log *log); //---------------------------------------------------------------------- // Return true if this type is a declaration to a type in an external // module. //---------------------------------------------------------------------- lldb::ModuleSP GetModuleForType(const DWARFDIE &die); typedef llvm::SmallPtrSet DIEPointerSet; typedef llvm::DenseMap DIEToDeclContextMap; // typedef llvm::DenseMap // DeclContextToDIEMap; typedef std::multimap DeclContextToDIEMap; typedef llvm::DenseMap DIEToDeclMap; typedef llvm::DenseMap DeclToDIEMap; lldb_private::ClangASTContext &m_ast; DIEToDeclMap m_die_to_decl; DeclToDIEMap m_decl_to_die; DIEToDeclContextMap m_die_to_decl_ctx; DeclContextToDIEMap m_decl_ctx_to_die; std::unique_ptr m_clang_ast_importer_ap; }; #endif // SymbolFileDWARF_DWARFASTParserClang_h_ Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 317454) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 317455) @@ -1,4287 +1,4293 @@ //===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "SymbolFileDWARF.h" // Other libraries and framework includes #include "llvm/Support/Casting.h" #include "llvm/Support/Threading.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/StreamString.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Target/Language.h" #include "lldb/Utility/TaskPool.h" #include "DWARFASTParser.h" #include "DWARFASTParserClang.h" #include "DWARFCompileUnit.h" #include "DWARFDIECollection.h" #include "DWARFDebugAbbrev.h" #include "DWARFDebugAranges.h" #include "DWARFDebugInfo.h" #include "DWARFDebugLine.h" #include "DWARFDebugMacro.h" #include "DWARFDebugPubnames.h" #include "DWARFDebugRanges.h" #include "DWARFDeclContext.h" #include "DWARFFormValue.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" #include "llvm/Support/FileSystem.h" #include #include #include //#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN #ifdef ENABLE_DEBUG_PRINTF #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif using namespace lldb; using namespace lldb_private; // static inline bool // child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag) //{ // switch (tag) // { // default: // break; // case DW_TAG_subprogram: // case DW_TAG_inlined_subroutine: // case DW_TAG_class_type: // case DW_TAG_structure_type: // case DW_TAG_union_type: // return true; // } // return false; //} // namespace { PropertyDefinition g_properties[] = { {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr, nullptr, "If the DW_AT_comp_dir matches any of these paths the symbolic " "links will be resolved at DWARF parse time."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertySymLinkPaths }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return SymbolFileDWARF::GetPluginNameStatic(); } PluginProperties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } FileSpecList &GetSymLinkPaths() { OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList( nullptr, true, ePropertySymLinkPaths); assert(option_value); return option_value->GetCurrentValue(); } }; typedef std::shared_ptr SymbolFileDWARFPropertiesSP; static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() { static const auto g_settings_sp(std::make_shared()); return g_settings_sp; } } // anonymous namespace end static const char *removeHostnameFromPathname(const char *path_from_dwarf) { if (!path_from_dwarf || !path_from_dwarf[0]) { return path_from_dwarf; } const char *colon_pos = strchr(path_from_dwarf, ':'); if (nullptr == colon_pos) { return path_from_dwarf; } const char *slash_pos = strchr(path_from_dwarf, '/'); if (slash_pos && (slash_pos < colon_pos)) { return path_from_dwarf; } // check whether we have a windows path, and so the first character // is a drive-letter not a hostname. if (colon_pos == path_from_dwarf + 1 && isalpha(*path_from_dwarf) && strlen(path_from_dwarf) > 2 && '\\' == path_from_dwarf[2]) { return path_from_dwarf; } return colon_pos + 1; } static const char *resolveCompDir(const char *path_from_dwarf) { if (!path_from_dwarf) return nullptr; // DWARF2/3 suggests the form hostname:pathname for compilation directory. // Remove the host part if present. const char *local_path = removeHostnameFromPathname(path_from_dwarf); if (!local_path) return nullptr; bool is_symlink = false; FileSpec local_path_spec(local_path, false); const auto &file_specs = GetGlobalPluginProperties()->GetSymLinkPaths(); for (size_t i = 0; i < file_specs.GetSize() && !is_symlink; ++i) is_symlink = FileSpec::Equal(file_specs.GetFileSpecAtIndex(i), local_path_spec, true); if (!is_symlink) return local_path; namespace fs = llvm::sys::fs; if (fs::get_file_type(local_path_spec.GetPath(), false) != fs::file_type::symlink_file) return local_path; FileSpec resolved_local_path_spec; const auto error = FileSystem::Readlink(local_path_spec, resolved_local_path_spec); if (error.Success()) return resolved_local_path_spec.GetCString(); return nullptr; } void SymbolFileDWARF::Initialize() { LogChannelDWARF::Initialize(); PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); } void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForSymbolFilePlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForSymbolFilePlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the dwarf symbol-file plug-in."), is_global_setting); } } void SymbolFileDWARF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); LogChannelDWARF::Terminate(); } lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() { static ConstString g_name("dwarf"); return g_name; } const char *SymbolFileDWARF::GetPluginDescriptionStatic() { return "DWARF and DWARF3 debug symbol file reader."; } SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) { return new SymbolFileDWARF(obj_file); } TypeList *SymbolFileDWARF::GetTypeList() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetTypeList(); else return m_obj_file->GetModule()->GetTypeList(); } void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset, dw_offset_t max_die_offset, uint32_t type_mask, TypeSet &type_set) { if (die) { const dw_offset_t die_offset = die.GetOffset(); if (die_offset >= max_die_offset) return; if (die_offset >= min_die_offset) { const dw_tag_t tag = die.Tag(); bool add_type = false; switch (tag) { case DW_TAG_array_type: add_type = (type_mask & eTypeClassArray) != 0; break; case DW_TAG_unspecified_type: case DW_TAG_base_type: add_type = (type_mask & eTypeClassBuiltin) != 0; break; case DW_TAG_class_type: add_type = (type_mask & eTypeClassClass) != 0; break; case DW_TAG_structure_type: add_type = (type_mask & eTypeClassStruct) != 0; break; case DW_TAG_union_type: add_type = (type_mask & eTypeClassUnion) != 0; break; case DW_TAG_enumeration_type: add_type = (type_mask & eTypeClassEnumeration) != 0; break; case DW_TAG_subroutine_type: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: add_type = (type_mask & eTypeClassFunction) != 0; break; case DW_TAG_pointer_type: add_type = (type_mask & eTypeClassPointer) != 0; break; case DW_TAG_rvalue_reference_type: case DW_TAG_reference_type: add_type = (type_mask & eTypeClassReference) != 0; break; case DW_TAG_typedef: add_type = (type_mask & eTypeClassTypedef) != 0; break; case DW_TAG_ptr_to_member_type: add_type = (type_mask & eTypeClassMemberPointer) != 0; break; } if (add_type) { const bool assert_not_being_parsed = true; Type *type = ResolveTypeUID(die, assert_not_being_parsed); if (type) { if (type_set.find(type) == type_set.end()) type_set.insert(type); } } } for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set); } } } size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope, uint32_t type_mask, TypeList &type_list) { TypeSet type_set; CompileUnit *comp_unit = NULL; DWARFCompileUnit *dwarf_cu = NULL; if (sc_scope) comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); if (comp_unit) { dwarf_cu = GetDWARFCompileUnit(comp_unit); if (dwarf_cu == 0) return 0; GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), type_mask, type_set); } else { DWARFDebugInfo *info = DebugInfo(); if (info) { const size_t num_cus = info->GetNumCompileUnits(); for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) { dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set); } } } } std::set compiler_type_set; size_t num_types_added = 0; for (Type *type : type_set) { CompilerType compiler_type = type->GetForwardCompilerType(); if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) { compiler_type_set.insert(compiler_type); type_list.Insert(type->shared_from_this()); ++num_types_added; } } return num_types_added; } //---------------------------------------------------------------------- // Gets the first parent that is a lexical block, function or inlined // subroutine, or compile unit. //---------------------------------------------------------------------- DWARFDIE SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) { DWARFDIE die; for (die = child_die.GetParent(); die; die = die.GetParent()) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_compile_unit: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: return die; } } return DWARFDIE(); } SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile) : SymbolFile(objfile), UserID(0), // Used by SymbolFileDWARFDebugMap to when // this class parses .o files to contain // the .o file index/ID m_debug_map_module_wp(), m_debug_map_symfile(NULL), m_data_debug_abbrev(), m_data_debug_aranges(), m_data_debug_frame(), m_data_debug_info(), m_data_debug_line(), m_data_debug_macro(), m_data_debug_loc(), m_data_debug_ranges(), m_data_debug_str(), m_data_apple_names(), m_data_apple_types(), m_data_apple_namespaces(), m_abbr(), m_info(), m_line(), m_apple_names_ap(), m_apple_types_ap(), m_apple_namespaces_ap(), m_apple_objc_ap(), m_function_basename_index(), m_function_fullname_index(), m_function_method_index(), m_function_selector_index(), m_objc_class_selectors_index(), m_global_index(), m_type_index(), m_namespace_index(), m_indexed(false), m_using_apple_tables(false), m_fetched_external_modules(false), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate), m_ranges(), m_unique_ast_type_map() {} SymbolFileDWARF::~SymbolFileDWARF() {} static const ConstString &GetDWARFMachOSegmentName() { static ConstString g_dwarf_section_name("__DWARF"); return g_dwarf_section_name; } UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetUniqueDWARFASTTypeMap(); else return m_unique_ast_type_map; } TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); TypeSystem *type_system; if (debug_map_symfile) { type_system = debug_map_symfile->GetTypeSystemForLanguage(language); } else { type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language); if (type_system) type_system->SetSymbolFile(this); } return type_system; } void SymbolFileDWARF::InitializeObject() { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const SectionList *section_list = module_sp->GetSectionList(); const Section *section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); // Memory map the DWARF mach-o segment so we have everything mmap'ed // to keep our heap memory usage down. if (section) m_obj_file->MemoryMapSectionData(section, m_dwarf_data); } get_apple_names_data(); if (m_data_apple_names.m_data.GetByteSize() > 0) { m_apple_names_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_names.m_data, get_debug_str_data(), ".apple_names")); if (m_apple_names_ap->IsValid()) m_using_apple_tables = true; else m_apple_names_ap.reset(); } get_apple_types_data(); if (m_data_apple_types.m_data.GetByteSize() > 0) { m_apple_types_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_types.m_data, get_debug_str_data(), ".apple_types")); if (m_apple_types_ap->IsValid()) m_using_apple_tables = true; else m_apple_types_ap.reset(); } get_apple_namespaces_data(); if (m_data_apple_namespaces.m_data.GetByteSize() > 0) { m_apple_namespaces_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_namespaces.m_data, get_debug_str_data(), ".apple_namespaces")); if (m_apple_namespaces_ap->IsValid()) m_using_apple_tables = true; else m_apple_namespaces_ap.reset(); } get_apple_objc_data(); if (m_data_apple_objc.m_data.GetByteSize() > 0) { m_apple_objc_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_objc.m_data, get_debug_str_data(), ".apple_objc")); if (m_apple_objc_ap->IsValid()) m_using_apple_tables = true; else m_apple_objc_ap.reset(); } } bool SymbolFileDWARF::SupportedVersion(uint16_t version) { return version == 2 || version == 3 || version == 4; } uint32_t SymbolFileDWARF::CalculateAbilities() { uint32_t abilities = 0; if (m_obj_file != NULL) { const Section *section = NULL; const SectionList *section_list = m_obj_file->GetSectionList(); if (section_list == NULL) return 0; uint64_t debug_abbrev_file_size = 0; uint64_t debug_info_file_size = 0; uint64_t debug_line_file_size = 0; section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); if (section) section_list = §ion->GetChildren(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get(); if (section != NULL) { debug_info_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true) .get(); if (section) debug_abbrev_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) .get(); if (section) debug_line_file_size = section->GetFileSize(); } else { const char *symfile_dir_cstr = m_obj_file->GetFileSpec().GetDirectory().GetCString(); if (symfile_dir_cstr) { if (strcasestr(symfile_dir_cstr, ".dsym")) { if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) { // We have a dSYM file that didn't have a any debug info. // If the string table has a size of 1, then it was made from // an executable with no debug info, or from an executable that // was stripped. section = section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true) .get(); if (section && section->GetFileSize() == 1) { m_obj_file->GetModule()->ReportWarning( "empty dSYM file detected, dSYM was created with an " "executable with no debug info."); } } } } } if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; if (debug_line_file_size > 0) abilities |= LineTables; } return abilities; } const DWARFDataExtractor & SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type, DWARFDataSegment &data_segment) { llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] { this->LoadSectionData(sect_type, std::ref(data_segment.m_data)); }); return data_segment.m_data; } void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type, DWARFDataExtractor &data) { ModuleSP module_sp(m_obj_file->GetModule()); const SectionList *section_list = module_sp->GetSectionList(); if (section_list) { SectionSP section_sp(section_list->FindSectionByType(sect_type, true)); if (section_sp) { // See if we memory mapped the DWARF segment? if (m_dwarf_data.GetByteSize()) { data.SetData(m_dwarf_data, section_sp->GetOffset(), section_sp->GetFileSize()); } else { if (m_obj_file->ReadSectionData(section_sp.get(), data) == 0) data.Clear(); } } } } const DWARFDataExtractor &SymbolFileDWARF::get_debug_abbrev_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAbbrev, m_data_debug_abbrev); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_addr_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAddr, m_data_debug_addr); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_aranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAranges, m_data_debug_aranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_frame_data() { return GetCachedSectionData(eSectionTypeDWARFDebugFrame, m_data_debug_frame); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_info_data() { return GetCachedSectionData(eSectionTypeDWARFDebugInfo, m_data_debug_info); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_line_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLine, m_data_debug_line); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_macro_data() { return GetCachedSectionData(eSectionTypeDWARFDebugMacro, m_data_debug_macro); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_ranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugRanges, m_data_debug_ranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStr, m_data_debug_str); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_offsets_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStrOffsets, m_data_debug_str_offsets); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_names_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNames, m_data_apple_names); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_types_data() { return GetCachedSectionData(eSectionTypeDWARFAppleTypes, m_data_apple_types); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_namespaces_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNamespaces, m_data_apple_namespaces); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_objc_data() { return GetCachedSectionData(eSectionTypeDWARFAppleObjC, m_data_apple_objc); } DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() { if (m_abbr.get() == NULL) { const DWARFDataExtractor &debug_abbrev_data = get_debug_abbrev_data(); if (debug_abbrev_data.GetByteSize() > 0) { m_abbr.reset(new DWARFDebugAbbrev()); if (m_abbr.get()) m_abbr->Parse(debug_abbrev_data); } } return m_abbr.get(); } const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const { return m_abbr.get(); } DWARFDebugInfo *SymbolFileDWARF::DebugInfo() { if (m_info.get() == NULL) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_info_data().GetByteSize() > 0) { m_info.reset(new DWARFDebugInfo()); if (m_info.get()) { m_info->SetDwarfData(this); } } } return m_info.get(); } const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const { return m_info.get(); } DWARFCompileUnit * SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { if (!comp_unit) return nullptr; DWARFDebugInfo *info = DebugInfo(); if (info) { // Just a normal DWARF file whose user ID for the compile unit is // the DWARF offset itself DWARFCompileUnit *dwarf_cu = info->GetCompileUnit((dw_offset_t)comp_unit->GetID()); if (dwarf_cu && dwarf_cu->GetUserData() == NULL) dwarf_cu->SetUserData(comp_unit); return dwarf_cu; } return NULL; } DWARFDebugRanges *SymbolFileDWARF::DebugRanges() { if (m_ranges.get() == NULL) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_ranges_data().GetByteSize() > 0) { m_ranges.reset(new DWARFDebugRanges()); if (m_ranges.get()) m_ranges->Extract(this); } } return m_ranges.get(); } const DWARFDebugRanges *SymbolFileDWARF::DebugRanges() const { return m_ranges.get(); } lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { CompUnitSP cu_sp; if (dwarf_cu) { CompileUnit *comp_unit = (CompileUnit *)dwarf_cu->GetUserData(); if (comp_unit) { // We already parsed this compile unit, had out a shared pointer to it cu_sp = comp_unit->shared_from_this(); } else { if (dwarf_cu->GetSymbolFileDWARF() != this) { return dwarf_cu->GetSymbolFileDWARF()->ParseCompileUnit(dwarf_cu, cu_idx); } else if (dwarf_cu->GetOffset() == 0 && GetDebugMapSymfile()) { // Let the debug map create the compile unit cu_sp = m_debug_map_symfile->GetCompileUnit(this); dwarf_cu->SetUserData(cu_sp.get()); } else { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { FileSpec cu_file_spec{cu_die.GetName(), false}; if (cu_file_spec) { // If we have a full path to the compile unit, we don't need to // resolve // the file. This can be expensive e.g. when the source files are // NFS mounted. if (cu_file_spec.IsRelative()) { const char *cu_comp_dir{ cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)}; cu_file_spec.PrependPathComponent(resolveCompDir(cu_comp_dir)); } std::string remapped_file; if (module_sp->RemapSourceFile(cu_file_spec.GetPath(), remapped_file)) cu_file_spec.SetFile(remapped_file, false); } LanguageType cu_language = DWARFCompileUnit::LanguageTypeFromDWARF( cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0)); bool is_optimized = dwarf_cu->GetIsOptimized(); cu_sp.reset(new CompileUnit( module_sp, dwarf_cu, cu_file_spec, dwarf_cu->GetID(), cu_language, is_optimized ? eLazyBoolYes : eLazyBoolNo)); if (cu_sp) { // If we just created a compile unit with an invalid file spec, // try and get the // first entry in the supports files from the line table as that // should be the // compile unit. if (!cu_file_spec) { cu_file_spec = cu_sp->GetSupportFiles().GetFileSpecAtIndex(1); if (cu_file_spec) { (FileSpec &)(*cu_sp) = cu_file_spec; // Also fix the invalid file spec which was copied from the // compile unit. cu_sp->GetSupportFiles().Replace(0, cu_file_spec); } } dwarf_cu->SetUserData(cu_sp.get()); // Figure out the compile unit index if we weren't given one if (cu_idx == UINT32_MAX) DebugInfo()->GetCompileUnit(dwarf_cu->GetOffset(), &cu_idx); m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( cu_idx, cu_sp); } } } } } } return cu_sp; } uint32_t SymbolFileDWARF::GetNumCompileUnits() { DWARFDebugInfo *info = DebugInfo(); if (info) return info->GetNumCompileUnits(); return 0; } CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { CompUnitSP cu_sp; DWARFDebugInfo *info = DebugInfo(); if (info) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) cu_sp = ParseCompileUnit(dwarf_cu, cu_idx); } return cu_sp; } Function *SymbolFileDWARF::ParseCompileUnitFunction(const SymbolContext &sc, const DWARFDIE &die) { if (die.IsValid()) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) return dwarf_ast->ParseFunctionFromDWARF(sc, die); } } return nullptr; } bool SymbolFileDWARF::FixupAddress(Address &addr) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { return debug_map_symfile->LinkOSOAddress(addr); } // This is a normal DWARF file, no address fixups need to happen return true; } lldb::LanguageType SymbolFileDWARF::ParseCompileUnitLanguage(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetLanguageType(); else return eLanguageTypeUnknown; } size_t SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) { assert(sc.comp_unit); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { DWARFDIECollection function_dies; const size_t num_functions = dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies); size_t func_idx; for (func_idx = 0; func_idx < num_functions; ++func_idx) { DWARFDIE die = function_dies.GetDIEAtIndex(func_idx); if (sc.comp_unit->FindFunctionByUID(die.GetID()).get() == NULL) { if (ParseCompileUnitFunction(sc, die)) ++functions_added; } } // FixupTypes(); } return functions_added; } bool SymbolFileDWARF::ParseCompileUnitSupportFiles( const SymbolContext &sc, FileSpecList &support_files) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { const char *cu_comp_dir = resolveCompDir( cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)); const dw_offset_t stmt_list = cu_die.GetAttributeValueAsUnsigned( DW_AT_stmt_list, DW_INVALID_OFFSET); if (stmt_list != DW_INVALID_OFFSET) { // All file indexes in DWARF are one based and a file of index zero is // supposed to be the compile unit itself. support_files.Append(*sc.comp_unit); return DWARFDebugLine::ParseSupportFiles( sc.comp_unit->GetModule(), get_debug_line_data(), cu_comp_dir, stmt_list, support_files); } } } return false; } bool SymbolFileDWARF::ParseCompileUnitIsOptimized( const lldb_private::SymbolContext &sc) { DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetIsOptimized(); return false; } bool SymbolFileDWARF::ParseImportedModules( const lldb_private::SymbolContext &sc, std::vector &imported_modules) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (ClangModulesDeclVendor::LanguageSupportsClangModules( sc.comp_unit->GetLanguage())) { UpdateExternalModuleListIfNeeded(); if (sc.comp_unit) { const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die) { for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { if (child_die.Tag() == DW_TAG_imported_declaration) { if (DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import)) { if (module_die.Tag() == DW_TAG_module) { if (const char *name = module_die.GetAttributeValueAsString( DW_AT_name, nullptr)) { ConstString const_name(name); imported_modules.push_back(const_name); } } } } } } } else { for (const auto &pair : m_external_type_modules) { imported_modules.push_back(pair.first); } } } } return false; } struct ParseDWARFLineTableCallbackInfo { LineTable *line_table; std::unique_ptr sequence_ap; lldb::addr_t addr_mask; }; //---------------------------------------------------------------------- // ParseStatementTableCallback //---------------------------------------------------------------------- static void ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State &state, void *userData) { if (state.row == DWARFDebugLine::State::StartParsingLineTable) { // Just started parsing the line table } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) { // Done parsing line table, nothing to do for the cleanup } else { ParseDWARFLineTableCallbackInfo *info = (ParseDWARFLineTableCallbackInfo *)userData; LineTable *line_table = info->line_table; // If this is our first time here, we need to create a // sequence container. if (!info->sequence_ap.get()) { info->sequence_ap.reset(line_table->CreateLineSequenceContainer()); assert(info->sequence_ap.get()); } line_table->AppendLineEntryToSequence( info->sequence_ap.get(), state.address & info->addr_mask, state.line, state.column, state.file, state.is_stmt, state.basic_block, state.prologue_end, state.epilogue_begin, state.end_sequence); if (state.end_sequence) { // First, put the current sequence into the line table. line_table->InsertSequence(info->sequence_ap.get()); // Then, empty it to prepare for the next sequence. info->sequence_ap->Clear(); } } } bool SymbolFileDWARF::ParseCompileUnitLineTable(const SymbolContext &sc) { assert(sc.comp_unit); if (sc.comp_unit->GetLineTable() != NULL) return true; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (dwarf_cu_die) { const dw_offset_t cu_line_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list, DW_INVALID_OFFSET); if (cu_line_offset != DW_INVALID_OFFSET) { std::unique_ptr line_table_ap(new LineTable(sc.comp_unit)); if (line_table_ap.get()) { ParseDWARFLineTableCallbackInfo info; info.line_table = line_table_ap.get(); /* * MIPS: * The SymbolContext may not have a valid target, thus we may not be * able * to call Address::GetOpcodeLoadAddress() which would clear the bit * #0 * for MIPS. Use ArchSpec to clear the bit #0. */ ArchSpec arch; GetObjectFile()->GetArchitecture(arch); switch (arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: info.addr_mask = ~((lldb::addr_t)1); break; default: info.addr_mask = ~((lldb::addr_t)0); break; } lldb::offset_t offset = cu_line_offset; DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { // We have an object file that has a line table with addresses // that are not linked. We need to link the line table and convert // the addresses that are relative to the .o file into addresses // for the main executable. sc.comp_unit->SetLineTable( debug_map_symfile->LinkOSOLineTable(this, line_table_ap.get())); } else { sc.comp_unit->SetLineTable(line_table_ap.release()); return true; } } } } } return false; } lldb_private::DebugMacrosSP SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) { auto iter = m_debug_macros_map.find(*offset); if (iter != m_debug_macros_map.end()) return iter->second; const DWARFDataExtractor &debug_macro_data = get_debug_macro_data(); if (debug_macro_data.GetByteSize() == 0) return DebugMacrosSP(); lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros()); m_debug_macros_map[*offset] = debug_macros_sp; const DWARFDebugMacroHeader &header = DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset); DWARFDebugMacroEntry::ReadMacroEntries(debug_macro_data, get_debug_str_data(), header.OffsetIs64Bit(), offset, this, debug_macros_sp); return debug_macros_sp; } bool SymbolFileDWARF::ParseCompileUnitDebugMacros(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu == nullptr) return false; const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (!dwarf_cu_die) return false; lldb::offset_t sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) return false; sc.comp_unit->SetDebugMacros(ParseDebugMacros(§_offset)); return true; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc, Block *parent_block, const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) { size_t blocks_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: case DW_TAG_lexical_block: { Block *block = NULL; if (tag == DW_TAG_subprogram) { // Skip any DW_TAG_subprogram DIEs that are inside // of a normal or inlined functions. These will be // parsed on their own as separate entities. if (depth > 0) break; block = parent_block; } else { BlockSP block_sp(new Block(die.GetID())); parent_block->AddChild(block_sp); block = block_sp.get(); } DWARFRangeList ranges; const char *name = NULL; const char *mangled_name = NULL; int decl_file = 0; int decl_line = 0; int decl_column = 0; int call_file = 0; int call_line = 0; int call_column = 0; if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, nullptr)) { if (tag == DW_TAG_subprogram) { assert(subprogram_low_pc == LLDB_INVALID_ADDRESS); subprogram_low_pc = ranges.GetMinRangeBase(0); } else if (tag == DW_TAG_inlined_subroutine) { // We get called here for inlined subroutines in two ways. // The first time is when we are making the Function object // for this inlined concrete instance. Since we're creating a top // level block at // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we // need to // adjust the containing address. // The second time is when we are parsing the blocks inside the // function that contains // the inlined concrete instance. Since these will be blocks inside // the containing "real" // function the offset will be for that function. if (subprogram_low_pc == LLDB_INVALID_ADDRESS) { subprogram_low_pc = ranges.GetMinRangeBase(0); } } const size_t num_ranges = ranges.GetSize(); for (size_t i = 0; i < num_ranges; ++i) { const DWARFRangeList::Entry &range = ranges.GetEntryRef(i); const addr_t range_base = range.GetRangeBase(); if (range_base >= subprogram_low_pc) block->AddRange(Block::Range(range_base - subprogram_low_pc, range.GetByteSize())); else { GetObjectFile()->GetModule()->ReportError( "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64 ") which has a base that is less than the function's low PC " "0x%" PRIx64 ". Please file a bug and attach the file at the " "start of this error message", block->GetID(), range_base, range.GetRangeEnd(), subprogram_low_pc); } } block->FinalizeRanges(); if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) { std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); std::unique_ptr call_ap; if (call_file != 0 || call_line != 0 || call_column != 0) call_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), call_line, call_column)); block->SetInlinedFunctionInfo(name, mangled_name, decl_ap.get(), call_ap.get()); } ++blocks_added; if (die.HasChildren()) { blocks_added += ParseFunctionBlocks(sc, block, die.GetFirstChild(), subprogram_low_pc, depth + 1); } } } break; default: break; } // Only parse siblings of the block if we are not at depth zero. A depth // of zero indicates we are currently parsing the top level // DW_TAG_subprogram DIE if (depth == 0) die.Clear(); else die = die.GetSibling(); } return blocks_added; } bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) { if (parent_die) { for (DWARFDIE die = parent_die.GetFirstChild(); die; die = die.GetSibling()) { dw_tag_t tag = die.Tag(); bool check_virtuality = false; switch (tag) { case DW_TAG_inheritance: case DW_TAG_subprogram: check_virtuality = true; break; default: break; } if (check_virtuality) { if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0) return true; } } } return false; } void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) { TypeSystem *type_system = decl_ctx.GetTypeSystem(); DWARFASTParser *ast_parser = type_system->GetDWARFParser(); std::vector decl_ctx_die_list = ast_parser->GetDIEForDeclContext(decl_ctx); for (DWARFDIE decl_ctx_die : decl_ctx_die_list) for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl; decl = decl.GetSibling()) ast_parser->GetDeclForUIDFromDWARF(decl); } SymbolFileDWARF *SymbolFileDWARF::GetDWARFForUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile(); if (debug_map) return debug_map->GetSymbolFileByOSOIndex( debug_map->GetOSOIndexFromUserID(uid)); return this; } DWARFDIE SymbolFileDWARF::GetDIEFromUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARF *dwarf = GetDWARFForUID(uid); if (dwarf) return dwarf->GetDIE(DIERef(uid, dwarf)); return DWARFDIE(); } CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDecl(); return CompilerDecl(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDeclContext(); return CompilerDeclContext(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetContainingDeclContext(); return CompilerDeclContext(); } Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE type_die = GetDIEFromUID(type_uid); if (type_die) return type_die.ResolveType(); else return nullptr; } Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) { return ResolveType(GetDIE(die_ref), true); } Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die, bool assert_not_being_parsed) { if (die) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'", die.GetOffset(), die.GetTagAsCString(), die.GetName()); // We might be coming in in the middle of a type tree (a class // within a class, an enum within a class), so parse any needed // parent DIEs before we get to this one... DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die); if (decl_ctx_die) { if (log) { switch (decl_ctx_die.Tag()) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { // Get the type, which could be a forward declaration if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' " "resolve parent forward type for 0x%8.8x", die.GetOffset(), die.GetTagAsCString(), die.GetName(), decl_ctx_die.GetOffset()); } break; default: break; } } } return ResolveType(die); } return NULL; } // This function is used when SymbolFileDWARFDebugMap owns a bunch of // SymbolFileDWARF objects to detect if this DWARF file is the one that // can resolve a compiler_type. bool SymbolFileDWARF::HasForwardDeclForClangType( const CompilerType &compiler_type) { CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); if (GetForwardDeclClangTypeToDie().count( compiler_type_no_qualifiers.GetOpaqueQualType())) { return true; } TypeSystem *type_system = compiler_type.GetTypeSystem(); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(type_system); if (!clang_type_system) return false; DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); return ast_parser->GetClangASTImporter().CanImport(compiler_type); } bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { std::lock_guard guard( GetObjectFile()->GetModule()->GetMutex()); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(compiler_type.GetTypeSystem()); if (clang_type_system) { DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); if (ast_parser && ast_parser->GetClangASTImporter().CanImport(compiler_type)) return ast_parser->GetClangASTImporter().CompleteType(compiler_type); } // We have a struct/union/class/enum that needs to be fully resolved. CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); auto die_it = GetForwardDeclClangTypeToDie().find( compiler_type_no_qualifiers.GetOpaqueQualType()); if (die_it == GetForwardDeclClangTypeToDie().end()) { // We have already resolved this type... return true; } DWARFDIE dwarf_die = GetDIE(die_it->getSecond()); if (dwarf_die) { // Once we start resolving this type, remove it from the forward declaration // map in case anyone child members or other types require this type to get // resolved. // The type will get resolved when all of the calls to // SymbolFileDWARF::ResolveClangOpaqueTypeDefinition // are done. GetForwardDeclClangTypeToDie().erase(die_it); Type *type = GetDIEToType().lookup(dwarf_die.GetDIE()); Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | DWARF_LOG_TYPE_COMPLETION)); if (log) GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", dwarf_die.GetID(), dwarf_die.GetTagAsCString(), type->GetName().AsCString()); assert(compiler_type); DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser(); if (dwarf_ast) return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type); } return false; } Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die, bool assert_not_being_parsed, bool resolve_function_context) { if (die) { Type *type = GetTypeForDIE(die, resolve_function_context).get(); if (assert_not_being_parsed) { if (type != DIE_IS_BEING_PARSED) return type; GetObjectFile()->GetModule()->ReportError( "Parsing a die that is being parsed die: 0x%8.8x: %s %s", die.GetOffset(), die.GetTagAsCString(), die.GetName()); } else return type; } return nullptr; } CompileUnit * SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { // Check if the symbol vendor already knows about this compile unit? if (dwarf_cu->GetUserData() == NULL) { // The symbol vendor doesn't know about this compile unit, we // need to parse and add it to the symbol vendor object. return ParseCompileUnit(dwarf_cu, cu_idx).get(); } return (CompileUnit *)dwarf_cu->GetUserData(); } size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name, DIEArray &method_die_offsets) { method_die_offsets.clear(); if (m_using_apple_tables) { if (m_apple_objc_ap.get()) m_apple_objc_ap->FindByName(class_name.GetCString(), method_die_offsets); } else { if (!m_indexed) Index(); m_objc_class_selectors_index.Find(class_name, method_die_offsets); } return method_die_offsets.size(); } bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) { sc.Clear(false); if (die) { // Check if the symbol vendor already knows about this compile unit? sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, die); if (sc.function) { sc.module_sp = sc.function->CalculateSymbolContextModule(); return true; } } return false; } lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) { UpdateExternalModuleListIfNeeded(); const auto &pos = m_external_type_modules.find(name); if (pos != m_external_type_modules.end()) return pos->second; else return lldb::ModuleSP(); } DWARFDIE SymbolFileDWARF::GetDIE(const DIERef &die_ref) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) return debug_info->GetDIE(die_ref); else return DWARFDIE(); } std::unique_ptr SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( DWARFCompileUnit &dwarf_cu, const DWARFDebugInfoEntry &cu_die) { // If we are using a dSYM file, we never want the standard DWO files since // the -gmodule support uses the same DWO machanism to specify full debug // info files for modules. if (GetDebugMapSymfile()) return nullptr; const char *dwo_name = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_GNU_dwo_name, nullptr); if (!dwo_name) return nullptr; FileSpec dwo_file(dwo_name, true); if (dwo_file.IsRelative()) { const char *comp_dir = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_comp_dir, nullptr); if (!comp_dir) return nullptr; dwo_file.SetFile(comp_dir, true); dwo_file.AppendPathComponent(dwo_name); } if (!dwo_file.Exists()) return nullptr; const lldb::offset_t file_offset = 0; DataBufferSP dwo_file_data_sp; lldb::offset_t dwo_file_data_offset = 0; ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin( GetObjectFile()->GetModule(), &dwo_file, file_offset, dwo_file.GetByteSize(), dwo_file_data_sp, dwo_file_data_offset); if (dwo_obj_file == nullptr) return nullptr; return llvm::make_unique(dwo_obj_file, &dwarf_cu); } void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { if (m_fetched_external_modules) return; m_fetched_external_modules = true; DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die && die.HasChildren() == false) { const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); if (name) { ConstString const_name(name); if (m_external_type_modules.find(const_name) == m_external_type_modules.end()) { ModuleSP module_sp; const char *dwo_path = die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr); if (dwo_path) { ModuleSpec dwo_module_spec; dwo_module_spec.GetFileSpec().SetFile(dwo_path, false); if (dwo_module_spec.GetFileSpec().IsRelative()) { const char *comp_dir = die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr); if (comp_dir) { dwo_module_spec.GetFileSpec().SetFile(comp_dir, true); dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path); } } dwo_module_spec.GetArchitecture() = m_obj_file->GetModule()->GetArchitecture(); // printf ("Loading dwo = '%s'\n", dwo_path); Error error = ModuleList::GetSharedModule( dwo_module_spec, module_sp, NULL, NULL, NULL); if (!module_sp) { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: unable to locate module needed for external types: " "%s\nerror: %s\nDebugging will be degraded due to missing " "types. Rebuilding your project will regenerate the needed " "module files.", die.GetOffset(), dwo_module_spec.GetFileSpec().GetPath().c_str(), error.AsCString("unknown error")); } } m_external_type_modules[const_name] = module_sp; } } } } } SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { if (!m_global_aranges_ap) { m_global_aranges_ap.reset(new GlobalVariableMap()); ModuleSP module_sp = GetObjectFile()->GetModule(); if (module_sp) { const size_t num_cus = module_sp->GetNumCompileUnits(); for (size_t i = 0; i < num_cus; ++i) { CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i); if (cu_sp) { VariableListSP globals_sp = cu_sp->GetVariableList(true); if (globals_sp) { const size_t num_globals = globals_sp->GetSize(); for (size_t g = 0; g < num_globals; ++g) { VariableSP var_sp = globals_sp->GetVariableAtIndex(g); if (var_sp && !var_sp->GetLocationIsConstantValueData()) { const DWARFExpression &location = var_sp->LocationExpression(); Value location_result; Error error; if (location.Evaluate(nullptr, nullptr, nullptr, LLDB_INVALID_ADDRESS, nullptr, nullptr, location_result, &error)) { if (location_result.GetValueType() == Value::eValueTypeFileAddress) { lldb::addr_t file_addr = location_result.GetScalar().ULongLong(); lldb::addr_t byte_size = 1; if (var_sp->GetType()) byte_size = var_sp->GetType()->GetByteSize(); m_global_aranges_ap->Append(GlobalVariableMap::Entry( file_addr, byte_size, var_sp.get())); } } } } } } } } m_global_aranges_ap->Sort(); } return *m_global_aranges_ap; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, uint32_t resolve_scope, SymbolContext &sc) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::" "ResolveSymbolContext (so_addr = { " "section = %p, offset = 0x%" PRIx64 " }, resolve_scope = 0x%8.8x)", static_cast(so_addr.GetSection().get()), so_addr.GetOffset(), resolve_scope); uint32_t resolved = 0; if (resolve_scope & (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock | eSymbolContextLineEntry | eSymbolContextVariable)) { lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const dw_offset_t cu_offset = debug_info->GetCompileUnitAranges().FindAddress(file_vm_addr); if (cu_offset == DW_INVALID_OFFSET) { // Global variables are not in the compile unit address ranges. The only // way to // currently find global variables is to iterate over the // .debug_pubnames or the // __apple_names table and find all items in there that point to // DW_TAG_variable // DIEs and then find the address that matches. if (resolve_scope & eSymbolContextVariable) { GlobalVariableMap &map = GetGlobalAranges(); const GlobalVariableMap::Entry *entry = map.FindEntryThatContains(file_vm_addr); if (entry && entry->data) { Variable *variable = entry->data; SymbolContextScope *scc = variable->GetSymbolContextScope(); if (scc) { scc->CalculateSymbolContext(&sc); sc.variable = variable; } return sc.GetResolvedMask(); } } } else { uint32_t cu_idx = DW_INVALID_INDEX; DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnit(cu_offset, &cu_idx); if (dwarf_cu) { sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { resolved |= eSymbolContextCompUnit; bool force_check_line_table = false; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit->FindFunctionByUID(function_die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } else { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. force_check_line_table = true; } if (sc.function != NULL) { resolved |= eSymbolContextFunction; if (resolve_scope & eSymbolContextBlock) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else sc.block = block.FindBlockByID(function_die.GetID()); if (sc.block) resolved |= eSymbolContextBlock; } } } if ((resolve_scope & eSymbolContextLineEntry) || force_check_line_table) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL) { // And address that makes it into this function should be in // terms // of this debug file if there is no debug map, or it will be an // address in the .o file which needs to be fixed up to be in // terms // of the debug map executable. Either way, calling // FixupAddress() // will work for us. Address exe_so_addr(so_addr); if (FixupAddress(exe_so_addr)) { if (line_table->FindLineEntryByAddress(exe_so_addr, sc.line_entry)) { resolved |= eSymbolContextLineEntry; } } } } if (force_check_line_table && !(resolved & eSymbolContextLineEntry)) { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. sc.comp_unit = NULL; resolved &= ~eSymbolContextCompUnit; } } else { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: compile unit %u failed to create a valid " "lldb_private::CompileUnit class.", cu_offset, cu_idx); } } } } } return resolved; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { const uint32_t prev_size = sc_list.GetSize(); if (resolve_scope & eSymbolContextCompUnit) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { uint32_t cu_idx; DWARFCompileUnit *dwarf_cu = NULL; for (cu_idx = 0; (dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) { CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); const bool full_match = (bool)file_spec.GetDirectory(); bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Equal(file_spec, *dc_cu, full_match); if (check_inlines || file_spec_matches_cu_file_spec) { SymbolContext sc(m_obj_file->GetModule()); sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { uint32_t file_idx = UINT32_MAX; // If we are looking for inline functions only and we don't // find it in the support files, we are done. if (check_inlines) { file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx == UINT32_MAX) continue; } if (line != 0) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL && line != 0) { // We will have already looked up the file index if // we are searching for inline entries. if (!check_inlines) file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx != UINT32_MAX) { uint32_t found_line; uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_idx, line, false, &sc.line_entry); found_line = sc.line_entry.line; while (line_idx != UINT32_MAX) { sc.function = NULL; sc.block = NULL; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); if (file_vm_addr != LLDB_INVALID_ADDRESS) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit ->FindFunctionByUID(function_die.GetID()) .get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } if (sc.function != NULL) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else if (function_die) sc.block = block.FindBlockByID(function_die.GetID()); } } } sc_list.Append(sc); line_idx = line_table->FindLineEntryIndexByFileIndex( line_idx + 1, file_idx, found_line, true, &sc.line_entry); } } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } if (!check_inlines) break; } } } } } return sc_list.GetSize() - prev_size; } void SymbolFileDWARF::Index() { if (m_indexed) return; m_indexed = true; Timer scoped_timer( LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::Index (%s)", GetObjectFile()->GetFileSpec().GetFilename().AsCString("")); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const uint32_t num_compile_units = GetNumCompileUnits(); if (num_compile_units == 0) return; std::vector function_basename_index(num_compile_units); std::vector function_fullname_index(num_compile_units); std::vector function_method_index(num_compile_units); std::vector function_selector_index(num_compile_units); std::vector objc_class_selectors_index(num_compile_units); std::vector global_index(num_compile_units); std::vector type_index(num_compile_units); std::vector namespace_index(num_compile_units); std::vector clear_cu_dies(num_compile_units, false); auto parser_fn = [debug_info, &function_basename_index, &function_fullname_index, &function_method_index, &function_selector_index, &objc_class_selectors_index, &global_index, &type_index, &namespace_index](uint32_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { dwarf_cu->Index( function_basename_index[cu_idx], function_fullname_index[cu_idx], function_method_index[cu_idx], function_selector_index[cu_idx], objc_class_selectors_index[cu_idx], global_index[cu_idx], type_index[cu_idx], namespace_index[cu_idx]); } return cu_idx; }; auto extract_fn = [debug_info](uint32_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { // dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the // DIEs for a compile unit have already been parsed. return std::make_pair(cu_idx, dwarf_cu->ExtractDIEsIfNeeded(false) > 1); } return std::make_pair(cu_idx, false); }; // Create a task runner that extracts dies for each DWARF compile unit in a // separate thread TaskRunner> task_runner_extract; for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) task_runner_extract.AddTask(extract_fn, cu_idx); //---------------------------------------------------------------------- // First figure out which compile units didn't have their DIEs already // parsed and remember this. If no DIEs were parsed prior to this index // function call, we are going to want to clear the CU dies after we // are done indexing to make sure we don't pull in all DWARF dies, but // we need to wait until all compile units have been indexed in case // a DIE in one compile unit refers to another and the indexes accesses // those DIEs. //---------------------------------------------------------------------- while (true) { auto f = task_runner_extract.WaitForNextCompletedTask(); if (!f.valid()) break; unsigned cu_idx; bool clear; std::tie(cu_idx, clear) = f.get(); clear_cu_dies[cu_idx] = clear; } // Now create a task runner that can index each DWARF compile unit in a // separate // thread so we can index quickly. TaskRunner task_runner; for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) task_runner.AddTask(parser_fn, cu_idx); while (true) { std::future f = task_runner.WaitForNextCompletedTask(); if (!f.valid()) break; uint32_t cu_idx = f.get(); m_function_basename_index.Append(function_basename_index[cu_idx]); m_function_fullname_index.Append(function_fullname_index[cu_idx]); m_function_method_index.Append(function_method_index[cu_idx]); m_function_selector_index.Append(function_selector_index[cu_idx]); m_objc_class_selectors_index.Append(objc_class_selectors_index[cu_idx]); m_global_index.Append(global_index[cu_idx]); m_type_index.Append(type_index[cu_idx]); m_namespace_index.Append(namespace_index[cu_idx]); } TaskPool::RunTasks([&]() { m_function_basename_index.Finalize(); }, [&]() { m_function_fullname_index.Finalize(); }, [&]() { m_function_method_index.Finalize(); }, [&]() { m_function_selector_index.Finalize(); }, [&]() { m_objc_class_selectors_index.Finalize(); }, [&]() { m_global_index.Finalize(); }, [&]() { m_type_index.Finalize(); }, [&]() { m_namespace_index.Finalize(); }); //---------------------------------------------------------------------- // Keep memory down by clearing DIEs for any compile units if indexing // caused us to load the compile unit's DIEs. //---------------------------------------------------------------------- for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { if (clear_cu_dies[cu_idx]) debug_info->GetCompileUnitAtIndex(cu_idx)->ClearDIEs(true); } #if defined(ENABLE_DEBUG_PRINTF) StreamFile s(stdout, false); s.Printf("DWARF index for '%s':", GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); #endif } } bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile( const lldb_private::CompilerDeclContext *decl_ctx) { if (decl_ctx == nullptr || !decl_ctx->IsValid()) { // Invalid namespace decl which means we aren't matching only things // in this symbol file, so return true to indicate it matches this // symbol file. return true; } TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem(); TypeSystem *type_system = GetTypeSystemForLanguage( decl_ctx_type_system->GetMinimumLanguage(nullptr)); if (decl_ctx_type_system == type_system) return true; // The type systems match, return true // The namespace AST was valid, and it does not match... Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "Valid namespace does not match symbol file"); return false; } uint32_t SymbolFileDWARF::FindGlobalVariables( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables)", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { const char *name_cstr = name.GetCString(); llvm::StringRef basename; llvm::StringRef context; if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename)) basename = name_cstr; m_apple_names_ap->FindByName(basename.data(), die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); bool done = false; for (size_t i = 0; i < num_die_matches && !done; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { switch (die.Tag()) { default: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_try_block: case DW_TAG_catch_block: break; case DW_TAG_variable: { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); if (parent_decl_ctx) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_parent_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (!actual_parent_decl_ctx || actual_parent_decl_ctx != *parent_decl_ctx) continue; } } ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) done = true; } break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } } // Return the number of variable that were appended to the list const uint32_t num_matches = variables.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables) => %u", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", append=%u, " "max_matches=%u, variables)", regex.GetText().str().c_str(), append, max_matches); } DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(regex, die_offsets); } SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) break; } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for regex '%s')\n", die_ref.die_offset, regex.GetText().str().c_str()); } } } } // Return the number of variable that were appended to the list return variables.GetSize() - original_size; } bool SymbolFileDWARF::ResolveFunction(const DIERef &die_ref, bool include_inlines, SymbolContextList &sc_list) { DWARFDIE die = DebugInfo()->GetDIE(die_ref); return ResolveFunction(die, include_inlines, sc_list); } bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, bool include_inlines, SymbolContextList &sc_list) { SymbolContext sc; if (!orig_die) return false; // If we were passed a die that is not a function, just return false... if (!(orig_die.Tag() == DW_TAG_subprogram || (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine))) return false; DWARFDIE die = orig_die; DWARFDIE inlined_die; if (die.Tag() == DW_TAG_inlined_subroutine) { inlined_die = die; while (1) { die = die.GetParent(); if (die) { if (die.Tag() == DW_TAG_subprogram) break; } else break; } } assert(die && die.Tag() == DW_TAG_subprogram); if (GetFunction(die, sc)) { Address addr; // Parse all blocks if needed if (inlined_die) { Block &function_block = sc.function->GetBlock(true); sc.block = function_block.FindBlockByID(inlined_die.GetID()); if (sc.block == NULL) sc.block = function_block.FindBlockByID(inlined_die.GetOffset()); if (sc.block == NULL || sc.block->GetStartAddress(addr) == false) addr.Clear(); } else { sc.block = NULL; addr = sc.function->GetAddressRange().GetBaseAddress(); } if (addr.IsValid()) { sc_list.Append(sc); return true; } } return false; } void SymbolFileDWARF::FindFunctions(const ConstString &name, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(name, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(regex, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions( const RegularExpression ®ex, const DWARFMappedHash::MemoryTable &memory_table, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; DWARFMappedHash::DIEInfoArray hash_data_array; if (memory_table.AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::ParseFunctions(const DIEArray &die_offsets, bool include_inlines, SymbolContextList &sc_list) { const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) ResolveFunction(die_offsets[i], include_inlines, sc_list); } } bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx, const DWARFDIE &die) { // If we have no parent decl context to match this DIE matches, and if the // parent // decl context isn't valid, we aren't trying to look for any particular decl // context so any die matches. if (decl_ctx == nullptr || !decl_ctx->IsValid()) return true; if (die) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (actual_decl_ctx) return actual_decl_ctx == *decl_ctx; } } return false; } uint32_t SymbolFileDWARF::FindFunctions(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::FindFunctions (name = '%s')", name.AsCString()); // eFunctionNameTypeAuto should be pre-resolved by a call to // Module::LookupInfo::LookupInfo() assert((name_type_mask & eFunctionNameTypeAuto) == 0); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, append=%u, sc_list)", name.GetCString(), name_type_mask, append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; // If name is empty then we won't find anything. if (name.IsEmpty()) return 0; // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. const char *name_cstr = name.GetCString(); const uint32_t original_size = sc_list.GetSize(); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; std::set resolved_dies; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DIEArray die_offsets; uint32_t num_matches = 0; if (name_type_mask & eFunctionNameTypeFull) { // If they asked for the full name, match what they typed. At some // point we may // want to canonicalize this (strip double spaces, etc. For now, we // just add all the // dies that we find by exact match. num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } } if (name_type_mask & eFunctionNameTypeSelector) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no selectors in namespaces num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); // Now make sure these are actually ObjC methods. In this case we can // simply look up the name, // and if it is an ObjC method name, we're good. for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { const char *die_name = die.GetName(); if (ObjCLanguage::IsPossibleObjCMethodName(die_name)) { if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } else { GetObjectFile()->GetModule()->ReportError( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } if (((name_type_mask & eFunctionNameTypeMethod) && !parent_decl_ctx) || name_type_mask & eFunctionNameTypeBase) { // The apple_names table stores just the "base name" of C++ methods in // the table. So we have to // extract the base name, look that up, and if there is any other // information in the name we were // passed in we have to post-filter based on that. // FIXME: Arrange the logic above so that we don't calculate the base // name twice: num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end() && ResolveFunction(die, include_inlines, sc_list)) { bool keep_die = true; if ((name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeMethod)) != (eFunctionNameTypeBase | eFunctionNameTypeMethod)) { // We are looking for either basenames or methods, so we need to // trim out the ones we won't want by looking at the type SymbolContext sc; if (sc_list.GetLastContext(sc)) { if (sc.block) { // We have an inlined function } else if (sc.function) { Type *type = sc.function->GetType(); if (type) { CompilerDeclContext decl_ctx = GetDeclContextContainingUID(type->GetID()); if (decl_ctx.IsStructUnionOrClass()) { if (name_type_mask & eFunctionNameTypeBase) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } else { if (name_type_mask & eFunctionNameTypeMethod) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } } else { GetObjectFile()->GetModule()->ReportWarning( "function at die offset 0x%8.8x had no function type", die_ref.die_offset); } } } } if (keep_die) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); if (name_type_mask & eFunctionNameTypeFull) { FindFunctions(name, m_function_fullname_index, include_inlines, sc_list); // FIXME Temporary workaround for global/anonymous namespace // functions debugging FreeBSD and Linux binaries. // If we didn't find any functions in the global namespace try // looking in the basename index but ignore any returned // functions that have a namespace but keep functions which // have an anonymous namespace // TODO: The arch in the object file isn't correct for MSVC // binaries on windows, we should find a way to make it // correct and handle those symbols as well. if (sc_list.GetSize() == original_size) { ArchSpec arch; if (!parent_decl_ctx && GetObjectFile()->GetArchitecture(arch) && arch.GetTriple().isOSBinFormatELF()) { SymbolContextList temp_sc_list; FindFunctions(name, m_function_basename_index, include_inlines, temp_sc_list); SymbolContext sc; for (uint32_t i = 0; i < temp_sc_list.GetSize(); i++) { if (temp_sc_list.GetContextAtIndex(i, sc)) { ConstString mangled_name = sc.GetFunctionName(Mangled::ePreferMangled); ConstString demangled_name = sc.GetFunctionName(Mangled::ePreferDemangled); // Mangled names on Linux and FreeBSD are of the form: // _ZN18function_namespace13function_nameEv. if (strncmp(mangled_name.GetCString(), "_ZN", 3) || !strncmp(demangled_name.GetCString(), "(anonymous namespace)", 21)) { sc_list.Append(sc); } } } } } } DIEArray die_offsets; if (name_type_mask & eFunctionNameTypeBase) { uint32_t num_base = m_function_basename_index.Find(name, die_offsets); for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } die_offsets.clear(); } if (name_type_mask & eFunctionNameTypeMethod) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no methods in namespaces uint32_t num_base = m_function_method_index.Find(name, die_offsets); { for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } } die_offsets.clear(); } if ((name_type_mask & eFunctionNameTypeSelector) && (!parent_decl_ctx || !parent_decl_ctx->IsValid())) { FindFunctions(name, m_function_selector_index, include_inlines, sc_list); } } // Return the number of variable that were appended to the list const uint32_t num_matches = sc_list.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => " "%u", name.GetCString(), name_type_mask, include_inlines, append, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARF::FindFunctions (regex = '%s')", regex.GetText().str().c_str()); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)", regex.GetText().str().c_str(), append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. uint32_t original_size = sc_list.GetSize(); if (m_using_apple_tables) { if (m_apple_names_ap.get()) FindFunctions(regex, *m_apple_names_ap, include_inlines, sc_list); } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); FindFunctions(regex, m_function_basename_index, include_inlines, sc_list); FindFunctions(regex, m_function_fullname_index, include_inlines, sc_list); } // Return the number of variable that were appended to the list return sc_list.GetSize() - original_size; } void SymbolFileDWARF::GetMangledNamesForFunction( const std::string &scope_qualified_name, std::vector &mangled_names) { DWARFDebugInfo *info = DebugInfo(); uint32_t num_comp_units = 0; if (info) num_comp_units = info->GetNumCompileUnits(); for (uint32_t i = 0; i < num_comp_units; i++) { DWARFCompileUnit *cu = info->GetCompileUnitAtIndex(i); if (cu == nullptr) continue; SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile(); if (dwo) dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names); } NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find(scope_qualified_name); if (iter == m_function_scope_qualified_name_map.end()) return; DIERefSetSP set_sp = (*iter).second; std::set::iterator set_iter; for (set_iter = set_sp->begin(); set_iter != set_sp->end(); set_iter++) { DWARFDIE die = DebugInfo()->GetDIE(*set_iter); mangled_names.push_back(ConstString(die.GetMangledName())); } } uint32_t SymbolFileDWARF::FindTypes( const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { // If we aren't appending the results to this list, then clear the list if (!append) types.Clear(); // Make sure we haven't already searched this SymbolFile before... if (searched_symbol_files.count(this)) return 0; else searched_symbol_files.insert(this); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { if (parent_decl_ctx) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "%p (\"%s\"), append=%u, max_matches=%u, type_list)", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches); else GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "NULL, append=%u, max_matches=%u, type_list)", name.GetCString(), append, max_matches); } if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { const uint32_t initial_types_size = types.GetSize(); for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); if (types.GetSize() >= max_matches) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } const uint32_t num_matches = types.GetSize() - initial_types_size; if (log && num_matches) { if (parent_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches, num_matches); } else { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= NULL, append=%u, max_matches=%u, type_list) => %u", name.GetCString(), append, max_matches, num_matches); } } return num_matches; } else { UpdateExternalModuleListIfNeeded(); for (const auto &pair : m_external_type_modules) { ModuleSP external_module_sp = pair.second; if (external_module_sp) { SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor(); if (sym_vendor) { const uint32_t num_external_matches = sym_vendor->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); if (num_external_matches) return num_external_matches; } } } } return 0; } size_t SymbolFileDWARF::FindTypes(const std::vector &context, bool append, TypeMap &types) { if (!append) types.Clear(); if (context.empty()) return 0; DIEArray die_offsets; ConstString name = context.back().name; if (!name) return 0; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { size_t num_matches = 0; for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { std::vector die_context; die.GetDWOContext(die_context); if (die_context != context) continue; Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); ++num_matches; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } return num_matches; } return 0; } CompilerDeclContext SymbolFileDWARF::FindNamespace(const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")", name.GetCString()); } CompilerDeclContext namespace_decl_ctx; if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return namespace_decl_ctx; DWARFDebugInfo *info = DebugInfo(); if (info) { DIEArray die_offsets; // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (m_using_apple_tables) { if (m_apple_namespaces_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_namespaces_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_namespace_index.Find(name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die); if (namespace_decl_ctx) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_namespaces accelerator table had bad die 0x%8.8x for " "'%s')\n", die_ref.die_offset, name.GetCString()); } } } } } if (log && namespace_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => " "CompilerDeclContext(%p/%p) \"%s\"", name.GetCString(), static_cast(namespace_decl_ctx.GetTypeSystem()), static_cast(namespace_decl_ctx.GetOpaqueDeclContext()), namespace_decl_ctx.GetName().AsCString("")); } return namespace_decl_ctx; } TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die, bool resolve_function_context) { TypeSP type_sp; if (die) { Type *type_ptr = GetDIEToType().lookup(die.GetDIE()); if (type_ptr == NULL) { CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(die.GetCU()); assert(lldb_cu); SymbolContext sc(lldb_cu); const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE(); while (parent_die != nullptr) { if (parent_die->Tag() == DW_TAG_subprogram) break; parent_die = parent_die->GetParent(); } SymbolContext sc_backup = sc; if (resolve_function_context && parent_die != nullptr && !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc)) sc = sc_backup; type_sp = ParseType(sc, die, NULL); } else if (type_ptr != DIE_IS_BEING_PARSED) { // Grab the existing type from the master types lists type_sp = type_ptr->shared_from_this(); } } return type_sp; } DWARFDIE SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) { if (orig_die) { DWARFDIE die = orig_die; while (die) { // If this is the original DIE that we are searching for a declaration // for, then don't look in the cache as we don't want our own decl // context to be our decl context... if (orig_die != die) { switch (die.Tag()) { case DW_TAG_compile_unit: case DW_TAG_namespace: case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: case DW_TAG_lexical_block: case DW_TAG_subprogram: return die; - + case DW_TAG_inlined_subroutine: { + DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); + if (abs_die) { + return abs_die; + } + break; + } default: break; } } DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification); if (spec_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die); if (decl_ctx_die) return decl_ctx_die; } DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); if (abs_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die); if (decl_ctx_die) return decl_ctx_die; } die = die.GetParent(); } } return DWARFDIE(); } Symbol * SymbolFileDWARF::GetObjCClassSymbol(const ConstString &objc_class_name) { Symbol *objc_class_symbol = NULL; if (m_obj_file) { Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo, Symtab::eVisibilityAny); } } return objc_class_symbol; } // Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If // they don't // then we can end up looking through all class types for a complete type and // never find // the full definition. We need to know if this attribute is supported, so we // determine // this here and cache th result. We also need to worry about the debug map // DWARF file // if we are doing darwin DWARF in .o file debugging. bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type( DWARFCompileUnit *cu) { if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type()) m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; else { DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu != cu && dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; break; } } } if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo && GetDebugMapSymfile()) return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this); } return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; } // This function can be used when a DIE is found that is a forward declaration // DIE and we want to try and find a type that has the complete definition. TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE( const DWARFDIE &die, const ConstString &type_name, bool must_be_implementation) { TypeSP type_sp; if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name))) return type_sp; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = type_name.GetCString(); m_apple_types_ap->FindCompleteObjCClassByName(name_cstr, die_offsets, must_be_implementation); } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE itself! if (type_die != die) { switch (type_die.Tag()) { case DW_TAG_class_type: case DW_TAG_structure_type: try_resolving_type = true; break; default: break; } } if (try_resolving_type) { if (must_be_implementation && type_die.Supports_DW_AT_APPLE_objc_complete_type()) try_resolving_type = type_die.GetAttributeValueAsUnsigned( DW_AT_APPLE_objc_complete_type, 0); if (try_resolving_type) { Type *resolved_type = ResolveType(type_die, false, true); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", die.GetID(), m_obj_file->GetFileSpec().GetFilename().AsCString( ""), type_die.GetID(), type_cu->GetID()); if (die) GetDIEToType()[die.GetDIE()] = resolved_type; type_sp = resolved_type->shared_from_this(); break; } } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } return type_sp; } //---------------------------------------------------------------------- // This function helps to ensure that the declaration contexts match for // two different DIEs. Often times debug information will refer to a // forward declaration of a type (the equivalent of "struct my_struct;". // There will often be a declaration of that type elsewhere that has the // full definition. When we go looking for the full type "my_struct", we // will find one or more matches in the accelerator tables and we will // then need to make sure the type was in the same declaration context // as the original DIE. This function can efficiently compare two DIEs // and will return true when the declaration context matches, and false // when they don't. //---------------------------------------------------------------------- bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2) { if (die1 == die2) return true; DWARFDIECollection decl_ctx_1; DWARFDIECollection decl_ctx_2; // The declaration DIE stack is a stack of the declaration context // DIEs all the way back to the compile unit. If a type "T" is // declared inside a class "B", and class "B" is declared inside // a class "A" and class "A" is in a namespace "lldb", and the // namespace is in a compile unit, there will be a stack of DIEs: // // [0] DW_TAG_class_type for "B" // [1] DW_TAG_class_type for "A" // [2] DW_TAG_namespace for "lldb" // [3] DW_TAG_compile_unit for the source file. // // We grab both contexts and make sure that everything matches // all the way back to the compiler unit. // First lets grab the decl contexts for both DIEs die1.GetDeclContextDIEs(decl_ctx_1); die2.GetDeclContextDIEs(decl_ctx_2); // Make sure the context arrays have the same size, otherwise // we are done const size_t count1 = decl_ctx_1.Size(); const size_t count2 = decl_ctx_2.Size(); if (count1 != count2) return false; // Make sure the DW_TAG values match all the way back up the // compile unit. If they don't, then we are done. DWARFDIE decl_ctx_die1; DWARFDIE decl_ctx_die2; size_t i; for (i = 0; i < count1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag()) return false; } #if defined LLDB_CONFIGURATION_DEBUG // Make sure the top item in the decl context die array is always // DW_TAG_compile_unit. If it isn't then something went wrong in // the DWARFDIE::GetDeclContextDIEs() function... assert(decl_ctx_1.GetDIEAtIndex(count1 - 1).Tag() == DW_TAG_compile_unit); #endif // Always skip the compile unit when comparing by only iterating up to // "count - 1". Here we compare the names as we go. for (i = 0; i < count1 - 1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); const char *name1 = decl_ctx_die1.GetName(); const char *name2 = decl_ctx_die2.GetName(); // If the string was from a DW_FORM_strp, then the pointer will often // be the same! if (name1 == name2) continue; // Name pointers are not equal, so only compare the strings // if both are not NULL. if (name1 && name2) { // If the strings don't compare, we are done... if (strcmp(name1, name2) != 0) return false; } else { // One name was NULL while the other wasn't return false; } } // We made it through all of the checks and the declaration contexts // are equal. return true; } TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext( const DWARFDeclContext &dwarf_decl_ctx) { TypeSP type_sp; const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize(); if (dwarf_decl_ctx_count > 0) { const ConstString type_name(dwarf_decl_ctx[0].name); const dw_tag_t tag = dwarf_decl_ctx[0].tag; if (type_name) { Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION | DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%" "s, qualified-name='%s')", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName()); } DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeTag); const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeQualNameHash); if (has_tag && has_qualified_name_hash) { const char *qualified_name = dwarf_decl_ctx.GetQualifiedName(); const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB(qualified_name); if (log) GetObjectFile()->GetModule()->LogMessage( log, "FindByNameAndTagAndQualifiedNameHash()"); m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash( type_name.GetCString(), tag, qualified_name_hash, die_offsets); } else if (has_tag) { if (log) GetObjectFile()->GetModule()->LogMessage(log, "FindByNameAndTag()"); m_apple_types_ap->FindByNameAndTag(type_name.GetCString(), tag, die_offsets); } else { m_apple_types_ap->FindByName(type_name.GetCString(), die_offsets); } } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); // Get the type system that we are looking to find a type for. We will use // this // to ensure any matches we find are in a language that this type system // supports const LanguageType language = dwarf_decl_ctx.GetLanguage(); TypeSystem *type_system = (language == eLanguageTypeUnknown) ? nullptr : GetTypeSystemForLanguage(language); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { // Make sure type_die's langauge matches the type system we are // looking for. // We don't want to find a "Foo" type from Java if we are looking // for a "Foo" // type for C, C++, ObjC, or ObjC++. if (type_system && !type_system->SupportsLanguage(type_die.GetLanguage())) continue; bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE // itself! const dw_tag_t type_tag = type_die.Tag(); // Make sure the tags match if (type_tag == tag) { // The tags match, lets try resolving this type try_resolving_type = true; } else { // The tags don't match, but we need to watch our for a // forward declaration for a struct and ("struct foo") // ends up being a class ("class foo { ... };") or // vice versa. switch (type_tag) { case DW_TAG_class_type: // We had a "class foo", see if we ended up with a "struct foo { // ... };" try_resolving_type = (tag == DW_TAG_structure_type); break; case DW_TAG_structure_type: // We had a "struct foo", see if we ended up with a "class foo { // ... };" try_resolving_type = (tag == DW_TAG_class_type); break; default: // Tags don't match, don't event try to resolve // using this type whose name matches.... break; } } if (try_resolving_type) { DWARFDeclContext type_dwarf_decl_ctx; type_die.GetDWARFDeclContext(type_dwarf_decl_ctx); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') trying die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), type_dwarf_decl_ctx.GetQualifiedName()); } // Make sure the decl contexts match all the way up if (dwarf_decl_ctx == type_dwarf_decl_ctx) { Type *resolved_type = ResolveType(type_die, false); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { type_sp = resolved_type->shared_from_this(); break; } } } else { if (log) { std::string qualified_name; type_die.GetQualifiedName(qualified_name); GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') ignoring die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), qualified_name.c_str()); } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } } } return type_sp; } TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, bool *type_is_new_ptr) { TypeSP type_sp; if (die) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) { Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr); if (type_sp) { TypeList *type_list = GetTypeList(); if (type_list) type_list->Insert(type_sp); if (die.Tag() == DW_TAG_subprogram) { DIERef die_ref = die.GetDIERef(); std::string scope_qualified_name(GetDeclContextForUID(die.GetID()) .GetScopeQualifiedName() .AsCString("")); if (scope_qualified_name.size()) { NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find( scope_qualified_name); if (iter != m_function_scope_qualified_name_map.end()) (*iter).second->insert(die_ref); else { DIERefSetSP new_set(new std::set); new_set->insert(die_ref); m_function_scope_qualified_name_map.emplace( std::make_pair(scope_qualified_name, new_set)); } } } } } } } return type_sp; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc, const DWARFDIE &orig_die, bool parse_siblings, bool parse_children) { size_t types_added = 0; DWARFDIE die = orig_die; while (die) { bool type_is_new = false; if (ParseType(sc, die, &type_is_new).get()) { if (type_is_new) ++types_added; } if (parse_children && die.HasChildren()) { if (die.Tag() == DW_TAG_subprogram) { SymbolContext child_sc(sc); child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true); } else types_added += ParseTypes(sc, die.GetFirstChild(), true, true); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return types_added; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc) { assert(sc.comp_unit && sc.function); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset); if (function_die) { ParseFunctionBlocks(sc, &sc.function->GetBlock(false), function_die, LLDB_INVALID_ADDRESS, 0); } } return functions_added; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc) { // At least a compile unit must be valid assert(sc.comp_unit); size_t types_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (sc.function) { dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE func_die = dwarf_cu->GetDIE(function_die_offset); if (func_die && func_die.HasChildren()) { types_added = ParseTypes(sc, func_die.GetFirstChild(), true, true); } } else { DWARFDIE dwarf_cu_die = dwarf_cu->DIE(); if (dwarf_cu_die && dwarf_cu_die.HasChildren()) { types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true); } } } return types_added; } size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) { if (sc.comp_unit != NULL) { DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; if (sc.function) { DWARFDIE function_die = info->GetDIE(DIERef(sc.function->GetID(), this)); const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress( DW_AT_low_pc, LLDB_INVALID_ADDRESS); if (func_lo_pc != LLDB_INVALID_ADDRESS) { const size_t num_variables = ParseVariables( sc, function_die.GetFirstChild(), func_lo_pc, true, true); // Let all blocks know they have parse all their variables sc.function->GetBlock(false).SetDidParseVariables(true, true); return num_variables; } } else if (sc.comp_unit) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnit(sc.comp_unit->GetID()); if (dwarf_cu == NULL) return 0; uint32_t vars_added = 0; VariableListSP variables(sc.comp_unit->GetVariableList(false)); if (variables.get() == NULL) { variables.reset(new VariableList()); sc.comp_unit->SetVariableList(variables); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsInRange( dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } } else { // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (!m_indexed) Index(); m_global_index.FindAllEntriesForCompileUnit(dwarf_cu->GetOffset(), die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { VariableSP var_sp( ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS)); if (var_sp) { variables->AddVariableIfUnique(var_sp); ++vars_added; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_names accelerator table had bad die 0x%8.8x)\n", die_ref.die_offset); } } } } } return vars_added; } } return 0; } VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc) { if (die.GetDWARF() != this) return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc); VariableSP var_sp; if (!die) return var_sp; var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) return var_sp; // Already been parsed! const dw_tag_t tag = die.Tag(); ModuleSP module = GetObjectFile()->GetModule(); if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); DWARFDIE spec_die; if (num_attributes > 0) { const char *name = NULL; const char *mangled = NULL; Declaration decl; uint32_t i; DWARFFormValue type_die_form; DWARFExpression location(die.GetCU()); bool is_external = false; bool is_artificial = false; bool location_is_const_value_data = false; bool has_explicit_location = false; DWARFFormValue const_value; Variable::RangeList scope_ranges; // AccessType accessibility = eAccessNone; for (i = 0; i < num_attributes; ++i) { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(); break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_external: is_external = form_value.Boolean(); break; case DW_AT_const_value: // If we have already found a DW_AT_location attribute, ignore this // attribute. if (!has_explicit_location) { location_is_const_value_data = true; // The constant value will be either a block, a data value or a // string. const DWARFDataExtractor &debug_info_data = get_debug_info_data(); if (DWARFFormValue::IsBlockForm(form_value.Form())) { // Retrieve the value as a block expression. uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, debug_info_data, block_offset, block_length); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // Retrieve the value as a data expression. DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i)->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); if (data_length == 0) { const uint8_t *data_pointer = form_value.BlockData(); if (data_pointer) { form_value.Unsigned(); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // we need to get the byte size of the type later after we // create the variable const_value = form_value; } } else location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { // Retrieve the value as a string expression. if (form_value.Form() == DW_FORM_strp) { DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i) ->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { const char *str = form_value.AsCString(); uint32_t string_offset = str - (const char *)debug_info_data.GetDataStart(); uint32_t string_length = strlen(str) + 1; location.CopyOpcodeData(module, debug_info_data, string_offset, string_length); } } } break; case DW_AT_location: { location_is_const_value_data = false; has_explicit_location = true; if (DWARFFormValue::IsBlockForm(form_value.Form())) { const DWARFDataExtractor &debug_info_data = get_debug_info_data(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, get_debug_info_data(), block_offset, block_length); } else { const DWARFDataExtractor &debug_loc_data = get_debug_loc_data(); const dw_offset_t debug_loc_offset = form_value.Unsigned(); size_t loc_list_length = DWARFExpression::LocationListSize( die.GetCU(), debug_loc_data, debug_loc_offset); if (loc_list_length > 0) { location.CopyOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); assert(func_low_pc != LLDB_INVALID_ADDRESS); location.SetLocationListSlide( func_low_pc - attributes.CompileUnitAtIndex(i)->GetBaseAddress()); } } } break; case DW_AT_specification: spec_die = GetDIE(DIERef(form_value)); break; case DW_AT_start_scope: { if (form_value.Form() == DW_FORM_sec_offset) { DWARFRangeList dwarf_scope_ranges; const DWARFDebugRanges *debug_ranges = DebugRanges(); debug_ranges->FindRanges(die.GetCU()->GetRangesBase(), form_value.Unsigned(), dwarf_scope_ranges); // All DW_AT_start_scope are relative to the base address of the // compile unit. We add the compile unit base address to make // sure all the addresses are properly fixed up. for (size_t i = 0, count = dwarf_scope_ranges.GetSize(); i < count; ++i) { const DWARFRangeList::Entry &range = dwarf_scope_ranges.GetEntryRef(i); scope_ranges.Append(range.GetRangeBase() + die.GetCU()->GetBaseAddress(), range.GetByteSize()); } } else { // TODO: Handle the case when DW_AT_start_scope have form // constant. The // dwarf spec is a bit ambiguous about what is the expected // behavior in // case the enclosing block have a non coninious address range and // the // DW_AT_start_scope entry have a form constant. GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_start_scope has unsupported form type (0x%x)\n", die.GetID(), form_value.Form()); } scope_ranges.Sort(); scope_ranges.CombineConsecutiveRanges(); } break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: case DW_AT_description: case DW_AT_endianity: case DW_AT_segment: case DW_AT_visibility: default: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); const dw_tag_t parent_tag = die.GetParent().Tag(); bool is_static_member = parent_tag == DW_TAG_compile_unit && (parent_context_die.Tag() == DW_TAG_class_type || parent_context_die.Tag() == DW_TAG_structure_type); ValueType scope = eValueTypeInvalid; const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); SymbolContextScope *symbol_context_scope = NULL; bool has_explicit_mangled = mangled != nullptr; if (!mangled) { // LLDB relies on the mangled name (DW_TAG_linkage_name or // DW_AT_MIPS_linkage_name) to // generate fully qualified names of global variables with commands like // "frame var j". // For example, if j were an int variable holding a value 4 and declared // in a namespace // B which in turn is contained in a namespace A, the command "frame var // j" returns // "(int) A::B::j = 4". If the compiler does not emit a linkage name, we // should be able // to generate a fully qualified name from the declaration context. if (parent_tag == DW_TAG_compile_unit && Language::LanguageIsCPlusPlus(die.GetLanguage())) { DWARFDeclContext decl_ctx; die.GetDWARFDeclContext(decl_ctx); mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString(); } } if (tag == DW_TAG_formal_parameter) scope = eValueTypeVariableArgument; else { // DWARF doesn't specify if a DW_TAG_variable is a local, global // or static variable, so we have to do a little digging: // 1) DW_AT_linkage_name implies static lifetime (but may be missing) // 2) An empty DW_AT_location is an (optimized-out) static lifetime var. // 3) DW_AT_location containing a DW_OP_addr implies static lifetime. // Clang likes to combine small global variables into the same symbol // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus // so we need to look through the whole expression. bool is_static_lifetime = has_explicit_mangled || (has_explicit_location && !location.IsValid()); // Check if the location has a DW_OP_addr with any address value... lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; if (!location_is_const_value_data) { bool op_error = false; location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error); if (op_error) { StreamString strm; location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0, NULL); GetObjectFile()->GetModule()->ReportError( "0x%8.8x: %s has an invalid location: %s", die.GetOffset(), die.GetTagAsCString(), strm.GetData()); } if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) is_static_lifetime = true; } SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (is_static_lifetime) { if (is_external) scope = eValueTypeVariableGlobal; else scope = eValueTypeVariableStatic; if (debug_map_symfile) { // When leaving the DWARF in the .o files on darwin, // when we have a global variable that wasn't initialized, // the .o file might not have allocated a virtual // address for the global variable. In this case it will // have created a symbol for the global variable // that is undefined/data and external and the value will // be the byte size of the variable. When we do the // address map in SymbolFileDWARFDebugMap we rely on // having an address, we need to do some magic here // so we can get the correct address for our global // variable. The address for all of these entries // will be zero, and there will be an undefined symbol // in this object file, and the executable will have // a matching symbol with a good address. So here we // dig up the correct address and replace it in the // location for the variable, and set the variable's // symbol context scope to be that of the main executable // so the file address will resolve correctly. bool linked_oso_file_addr = false; if (is_external && location_DW_OP_addr == 0) { // we have a possible uninitialized extern global ConstString const_name(mangled ? mangled : name); ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); if (debug_map_objfile) { Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); if (debug_map_symtab) { Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType( const_name, eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityExtern); if (exe_symbol) { if (exe_symbol->ValueIsAddress()) { const addr_t exe_file_addr = exe_symbol->GetAddressRef().GetFileAddress(); if (exe_file_addr != LLDB_INVALID_ADDRESS) { if (location.Update_DW_OP_addr(exe_file_addr)) { linked_oso_file_addr = true; symbol_context_scope = exe_symbol; } } } } } } } if (!linked_oso_file_addr) { // The DW_OP_addr is not zero, but it contains a .o file address // which // needs to be linked up correctly. const lldb::addr_t exe_file_addr = debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); if (exe_file_addr != LLDB_INVALID_ADDRESS) { // Update the file address for this variable location.Update_DW_OP_addr(exe_file_addr); } else { // Variable didn't make it into the final executable return var_sp; } } } } else { if (location_is_const_value_data) scope = eValueTypeVariableStatic; else { scope = eValueTypeVariableLocal; if (debug_map_symfile) { // We need to check for TLS addresses that we need to fixup if (location.ContainsThreadLocalStorage()) { location.LinkThreadLocalStorage( debug_map_symfile->GetObjectFile()->GetModule(), [this, debug_map_symfile]( lldb::addr_t unlinked_file_addr) -> lldb::addr_t { return debug_map_symfile->LinkOSOFileAddress( this, unlinked_file_addr); }); scope = eValueTypeVariableThreadLocal; } } } } } if (symbol_context_scope == NULL) { switch (parent_tag) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (symbol_context_scope == NULL) symbol_context_scope = sc.function; } break; default: symbol_context_scope = sc.comp_unit; break; } } if (symbol_context_scope) { SymbolFileTypeSP type_sp( new SymbolFileType(*this, DIERef(type_die_form).GetUID(this))); if (const_value.Form() && type_sp && type_sp->GetType()) location.CopyOpcodeData(const_value.Unsigned(), type_sp->GetType()->GetByteSize(), die.GetCU()->GetAddressByteSize()); var_sp.reset(new Variable(die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, scope_ranges, &decl, location, is_external, is_artificial, is_static_member)); var_sp->SetLocationIsConstantValueData(location_is_const_value_data); } else { // Not ready to parse this variable yet. It might be a global // or static variable that is in a function scope and the function // in the symbol context wasn't filled in yet return var_sp; } } // Cache var_sp even if NULL (the variable was just a specification or // was missing vital information to be able to be displayed in the debugger // (missing location due to optimization, etc)) so we don't re-parse // this DIE over and over later... GetDIEToVariable()[die.GetDIE()] = var_sp; if (spec_die) GetDIEToVariable()[spec_die.GetDIE()] = var_sp; } return var_sp; } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) { // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref), spec_block_die_offset); } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DWARFDIE &die, dw_offset_t spec_block_die_offset) { if (die) { switch (die.Tag()) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: { if (die.GetAttributeValueAsReference( DW_AT_specification, DW_INVALID_OFFSET) == spec_block_die_offset) return die; if (die.GetAttributeValueAsReference(DW_AT_abstract_origin, DW_INVALID_OFFSET) == spec_block_die_offset) return die; } break; } // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { DWARFDIE result_die = FindBlockContainingSpecification(child_die, spec_block_die_offset); if (result_die) return result_die; } } return DWARFDIE(); } size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc, const DWARFDIE &orig_die, const lldb::addr_t func_low_pc, bool parse_siblings, bool parse_children, VariableList *cc_variable_list) { if (!orig_die) return 0; VariableListSP variable_list_sp; size_t vars_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); // Check to see if we have already parsed this variable or constant? VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) { if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); } else { // We haven't already parsed it, lets do that now. if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { if (variable_list_sp.get() == NULL) { DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die); dw_tag_t parent_tag = sc_parent_die.Tag(); switch (parent_tag) { case DW_TAG_compile_unit: if (sc.comp_unit != NULL) { variable_list_sp = sc.comp_unit->GetVariableList(false); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); sc.comp_unit->SetVariableList(variable_list_sp); } } else { GetObjectFile()->GetModule()->ReportError( "parent 0x%8.8" PRIx64 " %s with no valid compile unit in " "symbol context for 0x%8.8" PRIx64 " %s.\n", sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(), orig_die.GetID(), orig_die.GetTagAsCString()); } break; case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function != NULL) { // Check to see if we already have parsed the variables for the // given scope Block *block = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (block == NULL) { // This must be a specification or abstract origin with // a concrete block counterpart in the current function. We need // to find the concrete block so we can correctly add the // variable to it const DWARFDIE concrete_block_die = FindBlockContainingSpecification( DIERef(sc.function->GetID(), this), sc_parent_die.GetOffset()); if (concrete_block_die) block = sc.function->GetBlock(true).FindBlockByID( concrete_block_die.GetID()); } if (block != NULL) { const bool can_create = false; variable_list_sp = block->GetBlockVariableList(can_create); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); block->SetVariableList(variable_list_sp); } } } break; default: GetObjectFile()->GetModule()->ReportError( "didn't find appropriate parent DIE for variable list for " "0x%8.8" PRIx64 " %s.\n", orig_die.GetID(), orig_die.GetTagAsCString()); break; } } if (variable_list_sp) { VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); if (var_sp) { variable_list_sp->AddVariableIfUnique(var_sp); if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); ++vars_added; } } } } bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); if (!skip_children && parse_children && die.HasChildren()) { vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true, true, cc_variable_list); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return vars_added; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); } uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; } void SymbolFileDWARF::DumpIndexes() { StreamFile s(stdout, false); s.Printf( "DWARF index for (%s) '%s':", GetObjectFile()->GetModule()->GetArchitecture().GetArchitectureName(), GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); } SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { if (m_debug_map_symfile == NULL && !m_debug_map_module_wp.expired()) { lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); if (module_sp) { SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); if (sym_vendor) m_debug_map_symfile = (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile(); } } return m_debug_map_symfile; } DWARFExpression::LocationListFormat SymbolFileDWARF::GetLocationListFormat() const { return DWARFExpression::RegularLocationList; } Index: vendor/lldb/dist/tools/debugserver/source/RNBRemote.cpp =================================================================== --- vendor/lldb/dist/tools/debugserver/source/RNBRemote.cpp (revision 317454) +++ vendor/lldb/dist/tools/debugserver/source/RNBRemote.cpp (revision 317455) @@ -1,6287 +1,6287 @@ //===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Created by Greg Clayton on 12/12/07. // //===----------------------------------------------------------------------===// #include "RNBRemote.h" #include #include #include #include #include #include #include #if defined(__APPLE__) #include #include #endif #include "DNB.h" #include "DNBDataRef.h" #include "DNBLog.h" #include "DNBThreadResumeActions.h" #include "DarwinLogCollector.h" #include "DarwinLogEvent.h" #include "JSON.h" #include "JSONGenerator.h" #include "JSONGenerator.h" #include "MacOSX/Genealogy.h" #include "OsLogger.h" #include "RNBContext.h" #include "RNBServices.h" #include "RNBSocket.h" #include "StdStringExtractor.h" #if defined(HAVE_LIBCOMPRESSION) #include #endif #if defined(HAVE_LIBZ) #include #endif #include // for endianness predefines #include #include #include //---------------------------------------------------------------------- // constants //---------------------------------------------------------------------- static const std::string OS_LOG_EVENTS_KEY_NAME("events"); static const std::string JSON_ASYNC_TYPE_KEY_NAME("type"); static const DarwinLogEventVector::size_type DARWIN_LOG_MAX_EVENTS_PER_PACKET = 10; //---------------------------------------------------------------------- // std::iostream formatting macros //---------------------------------------------------------------------- #define RAW_HEXBASE std::setfill('0') << std::hex << std::right #define HEXBASE '0' << 'x' << RAW_HEXBASE #define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x)) #define RAWHEX16 RAW_HEXBASE << std::setw(4) #define RAWHEX32 RAW_HEXBASE << std::setw(8) #define RAWHEX64 RAW_HEXBASE << std::setw(16) #define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x)) #define HEX16 HEXBASE << std::setw(4) #define HEX32 HEXBASE << std::setw(8) #define HEX64 HEXBASE << std::setw(16) #define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x) * 2) << (x) #define HEX(x) HEXBASE << std::setw(sizeof(x) * 2) << (x) #define RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (x) #define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x) #define STRING_WIDTH(w) std::setfill(' ') << std::setw(w) #define LEFT_STRING_WIDTH(s, w) \ std::left << std::setfill(' ') << std::setw(w) << (s) << std::right #define DECIMAL std::dec << std::setfill(' ') #define DECIMAL_WIDTH(w) DECIMAL << std::setw(w) #define FLOAT(n, d) \ std::setfill(' ') << std::setw((n) + (d) + 1) << std::setprecision(d) \ << std::showpoint << std::fixed #define INDENT_WITH_SPACES(iword_idx) \ std::setfill(' ') << std::setw((iword_idx)) << "" #define INDENT_WITH_TABS(iword_idx) \ std::setfill('\t') << std::setw((iword_idx)) << "" // Class to handle communications via gdb remote protocol. //---------------------------------------------------------------------- // Prototypes //---------------------------------------------------------------------- static std::string binary_encode_string(const std::string &s); //---------------------------------------------------------------------- // Decode a single hex character and return the hex value as a number or // -1 if "ch" is not a hex character. //---------------------------------------------------------------------- static inline int xdigit_to_sint(char ch) { if (ch >= 'a' && ch <= 'f') return 10 + ch - 'a'; if (ch >= 'A' && ch <= 'F') return 10 + ch - 'A'; if (ch >= '0' && ch <= '9') return ch - '0'; return -1; } //---------------------------------------------------------------------- // Decode a single hex ASCII byte. Return -1 on failure, a value 0-255 // on success. //---------------------------------------------------------------------- static inline int decoded_hex_ascii_char(const char *p) { const int hi_nibble = xdigit_to_sint(p[0]); if (hi_nibble == -1) return -1; const int lo_nibble = xdigit_to_sint(p[1]); if (lo_nibble == -1) return -1; return (uint8_t)((hi_nibble << 4) + lo_nibble); } //---------------------------------------------------------------------- // Decode a hex ASCII string back into a string //---------------------------------------------------------------------- static std::string decode_hex_ascii_string(const char *p, uint32_t max_length = UINT32_MAX) { std::string arg; if (p) { for (const char *c = p; ((c - p) / 2) < max_length; c += 2) { int ch = decoded_hex_ascii_char(c); if (ch == -1) break; else arg.push_back(ch); } } return arg; } uint64_t decode_uint64(const char *p, int base, char **end = nullptr, uint64_t fail_value = 0) { nub_addr_t addr = strtoull(p, end, 16); if (addr == 0 && errno != 0) return fail_value; return addr; } extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); #if defined(__APPLE__) && \ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) // from System.framework/Versions/B/PrivateHeaders/sys/codesign.h extern "C" { #define CS_OPS_STATUS 0 /* return status */ #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize); // from rootless.h bool rootless_allows_task_for_pid(pid_t pid); // from sys/csr.h typedef uint32_t csr_config_t; #define CSR_ALLOW_TASK_FOR_PID (1 << 2) int csr_check(csr_config_t mask); } #endif RNBRemote::RNBRemote() : m_ctx(), m_comm(), m_arch(), m_continue_thread(-1), m_thread(-1), m_mutex(), m_dispatch_queue_offsets(), m_dispatch_queue_offsets_addr(INVALID_NUB_ADDRESS), m_qSymbol_index(UINT32_MAX), m_packets_recvd(0), m_packets(), m_rx_packets(), m_rx_partial_data(), m_rx_pthread(0), m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4), m_extended_mode(false), m_noack_mode(false), m_thread_suffix_supported(false), m_list_threads_in_stop_reply(false), m_compression_minsize(384), m_enable_compression_next_send_packet(false), m_compression_mode(compression_types::none) { DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__); CreatePacketTable(); } RNBRemote::~RNBRemote() { DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__); StopReadRemoteDataThread(); } void RNBRemote::CreatePacketTable() { // Step required to add new packets: // 1 - Add new enumeration to RNBRemote::PacketEnum // 2 - Create the RNBRemote::HandlePacket_ function if a new function is // needed // 3 - Register the Packet definition with any needed callbacks in this // function // - If no response is needed for a command, then use NULL for the // normal callback // - If the packet is not supported while the target is running, use // NULL for the async callback // 4 - If the packet is a standard packet (starts with a '$' character // followed by the payload and then '#' and checksum, then you are done // else go on to step 5 // 5 - if the packet is a fixed length packet: // - modify the switch statement for the first character in the payload // in RNBRemote::CommDataReceived so it doesn't reject the new packet // type as invalid // - modify the switch statement for the first character in the payload // in RNBRemote::GetPacketPayload and make sure the payload of the // packet // is returned correctly std::vector &t = m_packets; t.push_back(Packet(ack, NULL, NULL, "+", "ACK")); t.push_back(Packet(nack, NULL, NULL, "-", "!ACK")); t.push_back(Packet(read_memory, &RNBRemote::HandlePacket_m, NULL, "m", "Read memory")); t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p", "Read one register")); t.push_back(Packet(read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g", "Read registers")); t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M", "Write memory")); t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P", "Write one register")); t.push_back(Packet(write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G", "Write registers")); t.push_back(Packet(insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0", "Insert memory breakpoint")); t.push_back(Packet(remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0", "Remove memory breakpoint")); t.push_back(Packet(single_step, &RNBRemote::HandlePacket_s, NULL, "s", "Single step")); t.push_back(Packet(cont, &RNBRemote::HandlePacket_c, NULL, "c", "continue")); t.push_back(Packet(single_step_with_sig, &RNBRemote::HandlePacket_S, NULL, "S", "Single step with signal")); t.push_back( Packet(set_thread, &RNBRemote::HandlePacket_H, NULL, "H", "Set thread")); t.push_back(Packet(halt, &RNBRemote::HandlePacket_last_signal, &RNBRemote::HandlePacket_stop_process, "\x03", "^C")); // t.push_back (Packet (use_extended_mode, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode")); t.push_back(Packet(why_halted, &RNBRemote::HandlePacket_last_signal, NULL, "?", "Why did target halt")); t.push_back( Packet(set_argv, &RNBRemote::HandlePacket_A, NULL, "A", "Set argv")); // t.push_back (Packet (set_bp, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear // breakpoint")); t.push_back(Packet(continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C", "Continue with signal")); t.push_back(Packet(detach, &RNBRemote::HandlePacket_D, NULL, "D", "Detach gdb from remote system")); // t.push_back (Packet (step_inferior_one_cycle, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one // clock cycle")); // t.push_back (Packet (signal_and_step_inf_one_cycle, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then // step one clock cycle")); t.push_back(Packet(kill, &RNBRemote::HandlePacket_k, NULL, "k", "Kill")); // t.push_back (Packet (restart, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior")); // t.push_back (Packet (search_mem_backwards, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory // backwards")); t.push_back(Packet(thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T", "Is thread alive")); t.push_back(Packet(query_supported_features, &RNBRemote::HandlePacket_qSupported, NULL, "qSupported", "Query about supported features")); t.push_back(Packet(vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach", "Attach to a new process")); t.push_back(Packet(vattachwait, &RNBRemote::HandlePacket_v, NULL, "vAttachWait", "Wait for a process to start up then attach to it")); t.push_back(Packet(vattachorwait, &RNBRemote::HandlePacket_v, NULL, "vAttachOrWait", "Attach to the process or if it doesn't " "exist, wait for the process to start up " "then attach to it")); t.push_back(Packet(vattachname, &RNBRemote::HandlePacket_v, NULL, "vAttachName", "Attach to an existing process by name")); t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont;", "Verbose resume with thread actions")); t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont?", "List valid continue-with-thread-actions actions")); t.push_back(Packet(read_data_from_memory, &RNBRemote::HandlePacket_x, NULL, "x", "Read data from memory")); t.push_back(Packet(write_data_to_memory, &RNBRemote::HandlePacket_X, NULL, "X", "Write data to memory")); // t.push_back (Packet (insert_hardware_bp, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware // breakpoint")); // t.push_back (Packet (remove_hardware_bp, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware // breakpoint")); t.push_back(Packet(insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z2", "Insert write watchpoint")); t.push_back(Packet(remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z2", "Remove write watchpoint")); t.push_back(Packet(insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z3", "Insert read watchpoint")); t.push_back(Packet(remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z3", "Remove read watchpoint")); t.push_back(Packet(insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z4", "Insert access watchpoint")); t.push_back(Packet(remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z4", "Remove access watchpoint")); t.push_back(Packet(query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL, "qRcmd", "Monitor command")); t.push_back(Packet(query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL, "qC", "Query current thread ID")); t.push_back(Packet(query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:", "Echo the packet back to allow the debugger to sync up " "with this server")); t.push_back(Packet(query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL, "qGetPid", "Query process id")); t.push_back(Packet(query_thread_ids_first, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo", "Get list of active threads (first req)")); t.push_back(Packet(query_thread_ids_subsequent, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo", "Get list of active threads (subsequent req)")); // APPLE LOCAL: qThreadStopInfo // syntax: qThreadStopInfoTTTT // TTTT is hex thread ID t.push_back(Packet(query_thread_stop_info, &RNBRemote::HandlePacket_qThreadStopInfo, NULL, "qThreadStopInfo", "Get detailed info on why the specified thread stopped")); t.push_back(Packet(query_thread_extra_info, &RNBRemote::HandlePacket_qThreadExtraInfo, NULL, "qThreadExtraInfo", "Get printable status of a thread")); // t.push_back (Packet (query_image_offsets, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset // of loaded program")); t.push_back(Packet( query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess, NULL, "qLaunchSuccess", "Report the success or failure of the launch attempt")); t.push_back( Packet(query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL, "qRegisterInfo", "Dynamically discover remote register context information.")); t.push_back(Packet( query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr, NULL, "qShlibInfoAddr", "Returns the address that contains info needed " "for getting shared library notifications")); t.push_back(Packet(query_step_packet_supported, &RNBRemote::HandlePacket_qStepPacketSupported, NULL, "qStepPacketSupported", "Replys with OK if the 's' packet is supported.")); t.push_back( Packet(query_vattachorwait_supported, &RNBRemote::HandlePacket_qVAttachOrWaitSupported, NULL, "qVAttachOrWaitSupported", "Replys with OK if the 'vAttachOrWait' packet is supported.")); t.push_back( Packet(query_sync_thread_state_supported, &RNBRemote::HandlePacket_qSyncThreadStateSupported, NULL, "qSyncThreadStateSupported", "Replys with OK if the 'QSyncThreadState:' packet is supported.")); t.push_back(Packet( query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back(Packet( query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion, NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back(Packet( query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back(Packet( query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol, NULL, "qSymbol:", "Notify that host debugger is ready to do symbol lookups")); t.push_back(Packet(json_query_thread_extended_info, &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); t.push_back(Packet(json_query_get_loaded_dynamic_libraries_infos, &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos, NULL, "jGetLoadedDynamicLibrariesInfos", "Replies with JSON data of all the shared libraries " "loaded in this process.")); t.push_back( Packet(json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo, NULL, "jThreadsInfo", "Replies with JSON data with information about all threads.")); t.push_back(Packet(json_query_get_shared_cache_info, &RNBRemote::HandlePacket_jGetSharedCacheInfo, NULL, "jGetSharedCacheInfo", "Replies with JSON data about the " "location and uuid of the shared " "cache in the inferior process.")); t.push_back(Packet(start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode, NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); t.push_back(Packet(prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported, NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', " "'G', 'p', and 'P') support having the thread ID appended " "to the end of the command")); t.push_back(Packet(set_logging_mode, &RNBRemote::HandlePacket_QSetLogging, NULL, "QSetLogging:", "Check if register packets ('g', " "'G', 'p', and 'P' support having " "the thread ID prefix")); t.push_back(Packet( set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize, NULL, "QSetMaxPacketSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle")); t.push_back(Packet( set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize, NULL, "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized payload gdb can handle")); t.push_back( Packet(set_environment_variable, &RNBRemote::HandlePacket_QEnvironment, NULL, "QEnvironment:", "Add an environment variable to the inferior's environment")); t.push_back( Packet(set_environment_variable_hex, &RNBRemote::HandlePacket_QEnvironmentHexEncoded, NULL, "QEnvironmentHexEncoded:", "Add an environment variable to the inferior's environment")); t.push_back(Packet(set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch, NULL, "QLaunchArch:", "Set the architecture to use when " "launching a process for hosts that " "can run multiple architecture " "slices from universal files.")); t.push_back(Packet(set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR, NULL, "QSetDisableASLR:", "Set whether to disable ASLR when launching the process " "with the set argv ('A') packet")); t.push_back(Packet(set_stdin, &RNBRemote::HandlePacket_QSetSTDIO, NULL, "QSetSTDIN:", "Set the standard input for a process to be " "launched with the 'A' packet")); t.push_back(Packet(set_stdout, &RNBRemote::HandlePacket_QSetSTDIO, NULL, "QSetSTDOUT:", "Set the standard output for a process to " "be launched with the 'A' packet")); t.push_back(Packet(set_stderr, &RNBRemote::HandlePacket_QSetSTDIO, NULL, "QSetSTDERR:", "Set the standard error for a process to " "be launched with the 'A' packet")); t.push_back(Packet(set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir, NULL, "QSetWorkingDir:", "Set the working directory for a " "process to be launched with the " "'A' packet")); t.push_back(Packet(set_list_threads_in_stop_reply, &RNBRemote::HandlePacket_QListThreadsInStopReply, NULL, "QListThreadsInStopReply", "Set if the 'threads' key should be added to the stop " "reply packets with a list of all thread IDs.")); t.push_back(Packet( sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState, NULL, "QSyncThreadState:", "Do whatever is necessary to make sure 'thread' is " "in a safe state to call functions on.")); // t.push_back (Packet (pass_signals_to_inferior, // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify // which signals are passed to the inferior")); t.push_back(Packet(allocate_memory, &RNBRemote::HandlePacket_AllocateMemory, NULL, "_M", "Allocate memory in the inferior process.")); t.push_back(Packet(deallocate_memory, &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m", "Deallocate memory in the inferior process.")); t.push_back(Packet( save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL, "QSaveRegisterState", "Save the register state for the current thread " "and return a decimal save ID.")); t.push_back(Packet(restore_register_state, &RNBRemote::HandlePacket_RestoreRegisterState, NULL, "QRestoreRegisterState:", "Restore the register state given a save ID previously " "returned from a call to QSaveRegisterState.")); t.push_back(Packet( memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, "qMemoryRegionInfo", "Return size and attributes of a memory region that " "contains the given address")); t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData, NULL, "qGetProfileData", "Return profiling data of the current target.")); t.push_back(Packet(set_enable_profiling, &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target.")); t.push_back(Packet(enable_compression, &RNBRemote::HandlePacket_QEnableCompression, NULL, "QEnableCompression:", "Enable compression for the remainder of the connection")); t.push_back(Packet(watchpoint_support_info, &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints")); t.push_back(Packet(set_process_event, &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed " "to the process, can be set before " "the process is started, or after.")); t.push_back( Packet(set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the " "process it is controlling if it loses connection to lldb.")); t.push_back(Packet( speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received.")); t.push_back(Packet(query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, "qXfer:", "Support the qXfer packet.")); t.push_back( Packet(query_supported_async_json_packets, &RNBRemote::HandlePacket_qStructuredDataPlugins, NULL, "qStructuredDataPlugins", "Query for the structured data plugins supported by the remote.")); t.push_back( Packet(configure_darwin_log, &RNBRemote::HandlePacket_QConfigureDarwinLog, NULL, "QConfigureDarwinLog:", "Configure the DarwinLog structured data plugin support.")); } void RNBRemote::FlushSTDIO() { if (m_ctx.HasValidProcessID()) { nub_process_t pid = m_ctx.ProcessID(); char buf[256]; nub_size_t count; do { count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf)); if (count > 0) { SendSTDOUTPacket(buf, count); } } while (count > 0); do { count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf)); if (count > 0) { SendSTDERRPacket(buf, count); } } while (count > 0); } } void RNBRemote::SendAsyncProfileData() { if (m_ctx.HasValidProcessID()) { nub_process_t pid = m_ctx.ProcessID(); char buf[1024]; nub_size_t count; do { count = DNBProcessGetAvailableProfileData(pid, buf, sizeof(buf)); if (count > 0) { SendAsyncProfileDataPacket(buf, count); } } while (count > 0); } } void RNBRemote::SendAsyncDarwinLogData() { DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): enter", __FUNCTION__); if (!m_ctx.HasValidProcessID()) { DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): ignoring due to" "invalid process id", __FUNCTION__); return; } nub_process_t pid = m_ctx.ProcessID(); DarwinLogEventVector::size_type entry_count = 0; // NOTE: the current looping structure here does nothing // to guarantee that we can send off async packets faster // than we generate them. It will keep sending as long // as there's data to send. do { DarwinLogEventVector events = DNBProcessGetAvailableDarwinLogEvents(pid); entry_count = events.size(); DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop enter", __FUNCTION__); for (DarwinLogEventVector::size_type base_entry = 0; base_entry < entry_count; base_entry += DARWIN_LOG_MAX_EVENTS_PER_PACKET) { DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): inner loop enter", __FUNCTION__); // We limit the total number of entries we pack // into a single JSON async packet just so it // doesn't get too large. JSONGenerator::Dictionary async_dictionary; // Specify the type of the JSON async data we're sending. async_dictionary.AddStringItem(JSON_ASYNC_TYPE_KEY_NAME, "DarwinLog"); // Create an array entry in the dictionary to hold all // the events going in this packet. JSONGenerator::ArraySP events_array(new JSONGenerator::Array()); async_dictionary.AddItem(OS_LOG_EVENTS_KEY_NAME, events_array); // We bundle up to DARWIN_LOG_MAX_EVENTS_PER_PACKET events in // a single packet. const auto inner_loop_bound = std::min(base_entry + DARWIN_LOG_MAX_EVENTS_PER_PACKET, entry_count); for (DarwinLogEventVector::size_type i = base_entry; i < inner_loop_bound; ++i) { DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): adding " "entry index %lu to the JSON packet", __FUNCTION__, i); events_array->AddItem(events[i]); } // Send off the packet. DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): sending JSON " "packet, %lu entries remain", __FUNCTION__, entry_count - inner_loop_bound); SendAsyncJSONPacket(async_dictionary); } DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop exit", __FUNCTION__); } while (entry_count > 0); DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): exit", __PRETTY_FUNCTION__); } rnb_err_t RNBRemote::SendHexEncodedBytePacket(const char *header, const void *buf, size_t buf_len, const char *footer) { std::ostringstream packet_sstrm; // Append the header cstr if there was one if (header && header[0]) packet_sstrm << header; nub_size_t i; const uint8_t *ubuf8 = (const uint8_t *)buf; for (i = 0; i < buf_len; i++) { packet_sstrm << RAWHEX8(ubuf8[i]); } // Append the footer cstr if there was one if (footer && footer[0]) packet_sstrm << footer; return SendPacket(packet_sstrm.str()); } rnb_err_t RNBRemote::SendSTDOUTPacket(char *buf, nub_size_t buf_size) { if (buf_size == 0) return rnb_success; return SendHexEncodedBytePacket("O", buf, buf_size, NULL); } rnb_err_t RNBRemote::SendSTDERRPacket(char *buf, nub_size_t buf_size) { if (buf_size == 0) return rnb_success; return SendHexEncodedBytePacket("O", buf, buf_size, NULL); } // This makes use of asynchronous bit 'A' in the gdb remote protocol. rnb_err_t RNBRemote::SendAsyncProfileDataPacket(char *buf, nub_size_t buf_size) { if (buf_size == 0) return rnb_success; std::string packet("A"); packet.append(buf, buf_size); return SendPacket(packet); } rnb_err_t RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) { std::ostringstream stream; // We're choosing something that is easy to spot if we somehow get one // of these coming out at the wrong time (i.e. when the remote side // is not waiting for a process control completion response). stream << "JSON-async:"; dictionary.Dump(stream); const std::string payload = binary_encode_string(stream.str()); return SendPacket(payload); } // Given a std::string packet contents to send, possibly encode/compress it. // If compression is enabled, the returned std::string will be in one of two // forms: // // N // C: // // If compression is not requested, the original packet contents are returned std::string RNBRemote::CompressString(const std::string &orig) { std::string compressed; compression_types compression_type = GetCompressionType(); if (compression_type != compression_types::none) { bool compress_this_packet = false; if (orig.size() > m_compression_minsize) { compress_this_packet = true; } if (compress_this_packet) { const size_t encoded_data_buf_size = orig.size() + 128; std::vector encoded_data(encoded_data_buf_size); size_t compressed_size = 0; #if defined(HAVE_LIBCOMPRESSION) if (compression_decode_buffer && compression_type == compression_types::lz4) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), orig.size(), nullptr, COMPRESSION_LZ4_RAW); } if (compression_decode_buffer && compression_type == compression_types::zlib_deflate) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), orig.size(), nullptr, COMPRESSION_ZLIB); } if (compression_decode_buffer && compression_type == compression_types::lzma) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), orig.size(), nullptr, COMPRESSION_LZMA); } if (compression_decode_buffer && compression_type == compression_types::lzfse) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, (uint8_t *)orig.c_str(), orig.size(), nullptr, COMPRESSION_LZFSE); } #endif #if defined(HAVE_LIBZ) if (compressed_size == 0 && compression_type == compression_types::zlib_deflate) { z_stream stream; memset(&stream, 0, sizeof(z_stream)); stream.next_in = (Bytef *)orig.c_str(); stream.avail_in = (uInt)orig.size(); stream.next_out = (Bytef *)encoded_data.data(); stream.avail_out = (uInt)encoded_data_buf_size; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; deflateInit2(&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); int compress_status = deflate(&stream, Z_FINISH); deflateEnd(&stream); if (compress_status == Z_STREAM_END && stream.total_out > 0) { compressed_size = stream.total_out; } } #endif if (compressed_size > 0) { compressed.clear(); compressed.reserve(compressed_size); compressed = "C"; char numbuf[16]; snprintf(numbuf, sizeof(numbuf), "%zu:", orig.size()); numbuf[sizeof(numbuf) - 1] = '\0'; compressed.append(numbuf); for (size_t i = 0; i < compressed_size; i++) { uint8_t byte = encoded_data[i]; if (byte == '#' || byte == '$' || byte == '}' || byte == '*' || byte == '\0') { compressed.push_back(0x7d); compressed.push_back(byte ^ 0x20); } else { compressed.push_back(byte); } } } else { compressed = "N" + orig; } } else { compressed = "N" + orig; } } else { compressed = orig; } return compressed; } rnb_err_t RNBRemote::SendPacket(const std::string &s) { DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, s.c_str()); std::string s_compressed = CompressString(s); std::string sendpacket = "$" + s_compressed + "#"; int cksum = 0; char hexbuf[5]; if (m_noack_mode) { sendpacket += "00"; } else { for (size_t i = 0; i != s_compressed.size(); ++i) cksum += s_compressed[i]; snprintf(hexbuf, sizeof hexbuf, "%02x", cksum & 0xff); sendpacket += hexbuf; } rnb_err_t err = m_comm.Write(sendpacket.c_str(), sendpacket.size()); if (err != rnb_success) return err; if (m_noack_mode) return rnb_success; std::string reply; RNBRemote::Packet packet; err = GetPacket(reply, packet, true); if (err != rnb_success) { DNBLogThreadedIf(LOG_RNB_REMOTE, "%8d RNBRemote::%s (%s) got error trying to get reply...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str()); return err; } DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str(), reply.c_str()); if (packet.type == ack) return rnb_success; // Should we try to resend the packet at this layer? // if (packet.command == nack) return rnb_err; } /* Get a packet via gdb remote protocol. Strip off the prefix/suffix, verify the checksum to make sure a valid packet was received, send an ACK if they match. */ rnb_err_t RNBRemote::GetPacketPayload(std::string &return_packet) { // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); PThreadMutex::Locker locker(m_mutex); if (m_rx_packets.empty()) { // Only reset the remote command available event if we have no more packets m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available); // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets // available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), // __FUNCTION__); return rnb_err; } // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, // m_rx_packets.size()); return_packet.swap(m_rx_packets.front()); m_rx_packets.pop_front(); locker.Reset(); // Release our lock on the mutex if (m_rx_packets.empty()) { // Reset the remote command available event if we have no more packets m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available); } // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, // return_packet.c_str()); switch (return_packet[0]) { case '+': case '-': case '\x03': break; case '$': { long packet_checksum = 0; if (!m_noack_mode) { for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i) { char checksum_char = tolower(return_packet[i]); if (!isxdigit(checksum_char)) { m_comm.Write("-", 1); DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet " "with invalid checksum characters: " "%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); return rnb_err; } } packet_checksum = strtol(&return_packet[return_packet.size() - 2], NULL, 16); } return_packet.erase(0, 1); // Strip the leading '$' return_packet.erase(return_packet.size() - 3); // Strip the #XX checksum if (!m_noack_mode) { // Compute the checksum int computed_checksum = 0; for (std::string::iterator it = return_packet.begin(); it != return_packet.end(); ++it) { computed_checksum += *it; } if (packet_checksum == (computed_checksum & 0xff)) { // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for // '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), // __FUNCTION__, return_packet.c_str()); m_comm.Write("+", 1); } else { DNBLogThreadedIf( LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: " "packet checksum mismatch (0x%2.2lx != 0x%2.2x))", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str(), packet_checksum, computed_checksum); m_comm.Write("-", 1); return rnb_err; } } } break; default: DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s tossing unexpected packet???? %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); if (!m_noack_mode) m_comm.Write("-", 1); return rnb_err; } return rnb_success; } rnb_err_t RNBRemote::HandlePacket_UNIMPLEMENTED(const char *p) { DNBLogThreadedIf(LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p ? p : "NULL"); return SendPacket(""); } rnb_err_t RNBRemote::HandlePacket_ILLFORMED(const char *file, int line, const char *p, const char *description) { DNBLogThreadedIf(LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file, line, __FUNCTION__, p); return SendPacket("E03"); } rnb_err_t RNBRemote::GetPacket(std::string &packet_payload, RNBRemote::Packet &packet_info, bool wait) { std::string payload; rnb_err_t err = GetPacketPayload(payload); if (err != rnb_success) { PThreadEvent &events = m_ctx.Events(); nub_event_t set_events = events.GetEventBits(); // TODO: add timeout version of GetPacket?? We would then need to pass // that timeout value along to DNBProcessTimedWaitForEvent. if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0)) return err; const nub_event_t events_to_wait_for = RNBContext::event_read_packet_available | RNBContext::event_read_thread_exiting; while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0) { if (set_events & RNBContext::event_read_packet_available) { // Try the queue again now that we got an event err = GetPacketPayload(payload); if (err == rnb_success) break; } if (set_events & RNBContext::event_read_thread_exiting) err = rnb_not_connected; if (err == rnb_not_connected) return err; } while (err == rnb_err) ; if (set_events == 0) err = rnb_not_connected; } if (err == rnb_success) { Packet::iterator it; for (it = m_packets.begin(); it != m_packets.end(); ++it) { if (payload.compare(0, it->abbrev.size(), it->abbrev) == 0) break; } // A packet we don't have an entry for. This can happen when we // get a packet that we don't know about or support. We just reply // accordingly and go on. if (it == m_packets.end()) { DNBLogThreadedIf(LOG_RNB_PACKETS, "unimplemented packet: '%s'", payload.c_str()); HandlePacket_UNIMPLEMENTED(payload.c_str()); return rnb_err; } else { packet_info = *it; packet_payload = payload; } } return err; } rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) { DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); static DNBTimer g_packetTimer(true); rnb_err_t err = rnb_err; std::string packet_data; RNBRemote::Packet packet_info; err = GetPacket(packet_data, packet_info, false); if (err == rnb_success) { if (!packet_data.empty() && isprint(packet_data[0])) DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (\"%s\");", packet_data.c_str()); else DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (%s);", packet_info.printable_name.c_str()); HandlePacketCallback packet_callback = packet_info.async; if (packet_callback != NULL) { if (type != NULL) *type = packet_info.type; return (this->*packet_callback)(packet_data.c_str()); } } return err; } rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) { static DNBTimer g_packetTimer(true); // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); rnb_err_t err = rnb_err; std::string packet_data; RNBRemote::Packet packet_info; err = GetPacket(packet_data, packet_info, false); if (err == rnb_success) { DNBLogThreadedIf(LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");", packet_data.c_str()); HandlePacketCallback packet_callback = packet_info.normal; if (packet_callback != NULL) { if (type != NULL) *type = packet_info.type; return (this->*packet_callback)(packet_data.c_str()); } else { // Do not fall through to end of this function, if we have valid // packet_info and it has a NULL callback, then we need to respect // that it may not want any response or anything to be done. return err; } } return rnb_err; } void RNBRemote::CommDataReceived(const std::string &new_data) { // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); { // Put the packet data into the buffer in a thread safe fashion PThreadMutex::Locker locker(m_mutex); std::string data; // See if we have any left over data from a previous call to this // function? if (!m_rx_partial_data.empty()) { // We do, so lets start with that data data.swap(m_rx_partial_data); } // Append the new incoming data data += new_data; // Parse up the packets into gdb remote packets size_t idx = 0; const size_t data_size = data.size(); while (idx < data_size) { // end_idx must be one past the last valid packet byte. Start // it off with an invalid value that is the same as the current // index. size_t end_idx = idx; switch (data[idx]) { case '+': // Look for ack case '-': // Look for cancel case '\x03': // ^C to halt target end_idx = idx + 1; // The command is one byte long... break; case '$': // Look for a standard gdb packet? end_idx = data.find('#', idx + 1); if (end_idx == std::string::npos || end_idx + 3 > data_size) { end_idx = std::string::npos; } else { // Add two for the checksum bytes and 1 to point to the // byte just past the end of this packet end_idx += 3; } break; default: break; } if (end_idx == std::string::npos) { // Not all data may be here for the packet yet, save it for // next time through this function. m_rx_partial_data += data.substr(idx); // DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for // later[%u, npos): // '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), // __FUNCTION__, idx, m_rx_partial_data.c_str()); idx = end_idx; } else if (idx < end_idx) { m_packets_recvd++; // Hack to get rid of initial '+' ACK??? if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+') { // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first // ACK away....[%u, npos): // '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), // __FUNCTION__, idx); } else { // We have a valid packet... m_rx_packets.push_back(data.substr(idx, end_idx - idx)); DNBLogThreadedIf(LOG_RNB_PACKETS, "getpkt: %s", m_rx_packets.back().c_str()); } idx = end_idx; } else { DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s tossing junk byte at %c", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, data[idx]); idx = idx + 1; } } } if (!m_rx_packets.empty()) { // Let the main thread know we have received a packet // DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s called // events.SetEvent(RNBContext::event_read_packet_available)", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); PThreadEvent &events = m_ctx.Events(); events.SetEvents(RNBContext::event_read_packet_available); } } rnb_err_t RNBRemote::GetCommData() { // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); std::string comm_data; rnb_err_t err = m_comm.Read(comm_data); if (err == rnb_success) { if (!comm_data.empty()) CommDataReceived(comm_data); } return err; } void RNBRemote::StartReadRemoteDataThread() { DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); PThreadEvent &events = m_ctx.Events(); if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0) { events.ResetEvents(RNBContext::event_read_thread_exiting); int err = ::pthread_create(&m_rx_pthread, NULL, ThreadFunctionReadRemoteData, this); if (err == 0) { // Our thread was successfully kicked off, wait for it to // set the started event so we can safely continue events.WaitForSetEvents(RNBContext::event_read_thread_running); } else { events.ResetEvents(RNBContext::event_read_thread_running); events.SetEvents(RNBContext::event_read_thread_exiting); } } } void RNBRemote::StopReadRemoteDataThread() { DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); PThreadEvent &events = m_ctx.Events(); if ((events.GetEventBits() & RNBContext::event_read_thread_running) == RNBContext::event_read_thread_running) { m_comm.Disconnect(true); struct timespec timeout_abstime; DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); // Wait for 2 seconds for the remote data thread to exit if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting, &timeout_abstime) == 0) { // Kill the remote data thread??? } } } void *RNBRemote::ThreadFunctionReadRemoteData(void *arg) { // Keep a shared pointer reference so this doesn't go away on us before the // thread is killed. DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...", __FUNCTION__, arg); RNBRemoteSP remoteSP(g_remoteSP); if (remoteSP.get() != NULL) { #if defined(__APPLE__) pthread_setname_np("read gdb-remote packets thread"); #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) struct sched_param thread_param; int thread_sched_policy; if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) { thread_param.sched_priority = 47; pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); } #endif #endif RNBRemote *remote = remoteSP.get(); PThreadEvent &events = remote->Context().Events(); events.SetEvents(RNBContext::event_read_thread_running); // START: main receive remote command thread loop bool done = false; while (!done) { rnb_err_t err = remote->GetCommData(); switch (err) { case rnb_success: break; case rnb_err: DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBSocket::GetCommData returned error %u", err); done = true; break; case rnb_not_connected: DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBSocket::GetCommData returned not connected..."); done = true; break; } } // START: main receive remote command thread loop events.ResetEvents(RNBContext::event_read_thread_running); events.SetEvents(RNBContext::event_read_thread_exiting); } DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...", __FUNCTION__, arg); return NULL; } // If we fail to get back a valid CPU type for the remote process, // make a best guess for the CPU type based on the currently running // debugserver binary -- the debugger may not handle the case of an // un-specified process CPU type correctly. static cpu_type_t best_guess_cpu_type() { #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) if (sizeof(char *) == 8) { return CPU_TYPE_ARM64; } else { return CPU_TYPE_ARM; } #elif defined(__i386__) || defined(__x86_64__) if (sizeof(char *) == 8) { return CPU_TYPE_X86_64; } else { return CPU_TYPE_I386; } #endif return 0; } /* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes (8-bit bytes). This encoding uses 0x7d ('}') as an escape character for 0x7d ('}'), 0x23 ('#'), 0x24 ('$'), 0x2a ('*'). LEN is the number of bytes to be processed. If a character is escaped, it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte (end of string). */ std::vector decode_binary_data(const char *str, size_t len) { std::vector bytes; if (len == 0) { return bytes; } if (len == (size_t)-1) len = strlen(str); while (len--) { unsigned char c = *str++; if (c == 0x7d && len > 0) { len--; c = *str++ ^ 0x20; } bytes.push_back(c); } return bytes; } // Quote any meta characters in a std::string as per the binary // packet convention in the gdb-remote protocol. static std::string binary_encode_string(const std::string &s) { std::string output; const size_t s_size = s.size(); const char *s_chars = s.c_str(); for (size_t i = 0; i < s_size; i++) { unsigned char ch = *(s_chars + i); if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { output.push_back('}'); // 0x7d output.push_back(ch ^ 0x20); } else { output.push_back(ch); } } return output; } // If the value side of a key-value pair in JSON is a string, // and that string has a " character in it, the " character must // be escaped. std::string json_string_quote_metachars(const std::string &s) { if (s.find('"') == std::string::npos) return s; std::string output; const size_t s_size = s.size(); const char *s_chars = s.c_str(); for (size_t i = 0; i < s_size; i++) { unsigned char ch = *(s_chars + i); if (ch == '"') { output.push_back('\\'); } output.push_back(ch); } return output; } typedef struct register_map_entry { uint32_t debugserver_regnum; // debugserver register number uint32_t offset; // Offset in bytes into the register context data with no // padding between register values DNBRegisterInfo nub_info; // debugnub register info std::vector value_regnums; std::vector invalidate_regnums; } register_map_entry_t; // If the notion of registers differs from what is handed out by the // architecture, then flavors can be defined here. static std::vector g_dynamic_register_map; static register_map_entry_t *g_reg_entries = NULL; static size_t g_num_reg_entries = 0; void RNBRemote::Initialize() { DNBInitialize(); } bool RNBRemote::InitializeRegisters(bool force) { pid_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return false; DNBLogThreadedIf( LOG_RNB_PROC, "RNBRemote::%s() getting native registers from DNB interface", __FUNCTION__); // Discover the registers by querying the DNB interface and letting it // state the registers that it would like to export. This allows the // registers to be discovered using multiple qRegisterInfo calls to get // all register information after the architecture for the process is // determined. if (force) { g_dynamic_register_map.clear(); g_reg_entries = NULL; g_num_reg_entries = 0; } if (g_dynamic_register_map.empty()) { nub_size_t num_reg_sets = 0; const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets); assert(num_reg_sets > 0 && reg_sets != NULL); uint32_t regnum = 0; uint32_t reg_data_offset = 0; typedef std::map NameToRegNum; NameToRegNum name_to_regnum; for (nub_size_t set = 0; set < num_reg_sets; ++set) { if (reg_sets[set].registers == NULL) continue; for (uint32_t reg = 0; reg < reg_sets[set].num_registers; ++reg) { register_map_entry_t reg_entry = { regnum++, // register number starts at zero and goes up with no gaps reg_data_offset, // Offset into register context data, no gaps // between registers reg_sets[set].registers[reg], // DNBRegisterInfo {}, {}, }; name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum; if (reg_entry.nub_info.value_regs == NULL) { reg_data_offset += reg_entry.nub_info.size; } g_dynamic_register_map.push_back(reg_entry); } } // Now we must find any registers whose values are in other registers and // fix up // the offsets since we removed all gaps... for (auto ®_entry : g_dynamic_register_map) { if (reg_entry.nub_info.value_regs) { uint32_t new_offset = UINT32_MAX; for (size_t i = 0; reg_entry.nub_info.value_regs[i] != NULL; ++i) { const char *name = reg_entry.nub_info.value_regs[i]; auto pos = name_to_regnum.find(name); if (pos != name_to_regnum.end()) { regnum = pos->second; reg_entry.value_regnums.push_back(regnum); if (regnum < g_dynamic_register_map.size()) { // The offset for value_regs registers is the offset within the // register with the lowest offset const uint32_t reg_offset = g_dynamic_register_map[regnum].offset + reg_entry.nub_info.offset; if (new_offset > reg_offset) new_offset = reg_offset; } } } if (new_offset != UINT32_MAX) { reg_entry.offset = new_offset; } else { DNBLogThreaded("no offset was calculated entry for register %s", reg_entry.nub_info.name); reg_entry.offset = UINT32_MAX; } } if (reg_entry.nub_info.update_regs) { for (size_t i = 0; reg_entry.nub_info.update_regs[i] != NULL; ++i) { const char *name = reg_entry.nub_info.update_regs[i]; auto pos = name_to_regnum.find(name); if (pos != name_to_regnum.end()) { regnum = pos->second; reg_entry.invalidate_regnums.push_back(regnum); } } } } // for (auto ®_entry: g_dynamic_register_map) // { // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s", // reg_entry.offset, // reg_entry.nub_info.size, // reg_entry.nub_info.value_regs != NULL, // reg_entry.nub_info.name); // } g_reg_entries = g_dynamic_register_map.data(); g_num_reg_entries = g_dynamic_register_map.size(); } return true; } /* The inferior has stopped executing; send a packet to gdb to let it know. */ void RNBRemote::NotifyThatProcessStopped(void) { RNBRemote::HandlePacket_last_signal(NULL); return; } /* 'A arglen,argnum,arg,...' Update the inferior context CTX with the program name and arg list. The documentation for this packet is underwhelming but my best reading of this is that it is a series of (len, position #, arg)'s, one for each argument with "arg" hex encoded (two 0-9a-f chars?). Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either is sufficient to get around the "," position separator escape issue. e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is 6,0,676462,4,1,2d71,10,2,612e6f7574 Note that "argnum" and "arglen" are numbers in base 10. Again, that's not documented either way but I'm assuming it's so. */ rnb_err_t RNBRemote::HandlePacket_A(const char *p) { if (p == NULL || *p == '\0') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Null packet for 'A' pkt"); } p++; if (*p == '\0' || !isdigit(*p)) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "arglen not specified on 'A' pkt"); } /* I promise I don't modify it anywhere in this function. strtoul()'s 2nd arg has to be non-const which makes it problematic to step through the string easily. */ char *buf = const_cast(p); RNBContext &ctx = Context(); while (*buf != '\0') { unsigned long arglen, argnum; std::string arg; char *c; errno = 0; arglen = strtoul(buf, &c, 10); if (errno != 0 && arglen == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "arglen not a number on 'A' pkt"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "arglen not followed by comma on 'A' pkt"); } buf = c + 1; errno = 0; argnum = strtoul(buf, &c, 10); if (errno != 0 && argnum == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "argnum not a number on 'A' pkt"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "arglen not followed by comma on 'A' pkt"); } buf = c + 1; c = buf; buf = buf + arglen; while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0') { char smallbuf[3]; smallbuf[0] = *c; smallbuf[1] = *(c + 1); smallbuf[2] = '\0'; errno = 0; int ch = static_cast(strtoul(smallbuf, NULL, 16)); if (errno != 0 && ch == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "non-hex char in arg on 'A' pkt"); } arg.push_back(ch); c += 2; } ctx.PushArgument(arg.c_str()); if (*buf == ',') buf++; } SendPacket("OK"); return rnb_success; } /* 'H c t' Set the thread for subsequent actions; 'c' for step/continue ops, 'g' for other ops. -1 means all threads, 0 means any thread. */ rnb_err_t RNBRemote::HandlePacket_H(const char *p) { p++; // skip 'H' if (*p != 'c' && *p != 'g') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Missing 'c' or 'g' type in H packet"); } if (!m_ctx.HasValidProcessID()) { // We allow gdb to connect to a server that hasn't started running // the target yet. gdb still wants to ask questions about it and // freaks out if it gets an error. So just return OK here. } errno = 0; nub_thread_t tid = strtoul(p + 1, NULL, 16); if (errno != 0 && tid == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid thread number in H packet"); } if (*p == 'c') SetContinueThread(tid); if (*p == 'g') SetCurrentThread(tid); return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_qLaunchSuccess(const char *p) { if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Error() == 0) return SendPacket("OK"); std::ostringstream ret_str; std::string status_str; ret_str << "E" << m_ctx.LaunchStatusAsString(status_str); return SendPacket(ret_str.str()); } rnb_err_t RNBRemote::HandlePacket_qShlibInfoAddr(const char *p) { if (m_ctx.HasValidProcessID()) { nub_addr_t shlib_info_addr = DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID()); if (shlib_info_addr != INVALID_NUB_ADDRESS) { std::ostringstream ostrm; ostrm << RAW_HEXBASE << shlib_info_addr; return SendPacket(ostrm.str()); } } return SendPacket("E44"); } rnb_err_t RNBRemote::HandlePacket_qStepPacketSupported(const char *p) { // Normally the "s" packet is mandatory, yet in gdb when using ARM, they // get around the need for this packet by implementing software single // stepping from gdb. Current versions of debugserver do support the "s" // packet, yet some older versions do not. We need a way to tell if this // packet is supported so we can disable software single stepping in gdb // for remote targets (so the "s" packet will get used). return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_qSyncThreadStateSupported(const char *p) { // We support attachOrWait meaning attach if the process exists, otherwise // wait to attach. return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_qVAttachOrWaitSupported(const char *p) { // We support attachOrWait meaning attach if the process exists, otherwise // wait to attach. return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_qThreadStopInfo(const char *p) { p += strlen("qThreadStopInfo"); nub_thread_t tid = strtoul(p, 0, 16); return SendStopReplyPacketForThread(tid); } rnb_err_t RNBRemote::HandlePacket_qThreadInfo(const char *p) { // We allow gdb to connect to a server that hasn't started running // the target yet. gdb still wants to ask questions about it and // freaks out if it gets an error. So just return OK here. nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("OK"); // Only "qfThreadInfo" and "qsThreadInfo" get into this function so // we only need to check the second byte to tell which is which if (p[1] == 'f') { nub_size_t numthreads = DNBProcessGetNumThreads(pid); std::ostringstream ostrm; ostrm << "m"; bool first = true; for (nub_size_t i = 0; i < numthreads; ++i) { if (first) first = false; else ostrm << ","; nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i); ostrm << std::hex << th; } return SendPacket(ostrm.str()); } else { return SendPacket("l"); } } rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) { // We allow gdb to connect to a server that hasn't started running // the target yet. gdb still wants to ask questions about it and // freaks out if it gets an error. So just return OK here. nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("OK"); /* This is supposed to return a string like 'Runnable' or 'Blocked on Mutex'. The returned string is formatted like the "A" packet - a sequence of letters encoded in as 2-hex-chars-per-letter. */ p += strlen("qThreadExtraInfo"); if (*p++ != ',') return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Illformed qThreadExtraInfo packet"); errno = 0; nub_thread_t tid = strtoul(p, NULL, 16); if (errno != 0 && tid == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Invalid thread number in qThreadExtraInfo packet"); } const char *threadInfo = DNBThreadGetInfo(pid, tid); if (threadInfo != NULL && threadInfo[0]) { return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL); } else { // "OK" == 4f6b // Return "OK" as a ASCII hex byte stream if things go wrong return SendPacket("4f6b"); } return SendPacket(""); } const char *k_space_delimiters = " \t"; static void skip_spaces(std::string &line) { if (!line.empty()) { size_t space_pos = line.find_first_not_of(k_space_delimiters); if (space_pos > 0) line.erase(0, space_pos); } } static std::string get_identifier(std::string &line) { std::string word; skip_spaces(line); const size_t line_size = line.size(); size_t end_pos; for (end_pos = 0; end_pos < line_size; ++end_pos) { if (end_pos == 0) { if (isalpha(line[end_pos]) || line[end_pos] == '_') continue; } else if (isalnum(line[end_pos]) || line[end_pos] == '_') continue; break; } word.assign(line, 0, end_pos); line.erase(0, end_pos); return word; } static std::string get_operator(std::string &line) { std::string op; skip_spaces(line); if (!line.empty()) { if (line[0] == '=') { op = '='; line.erase(0, 1); } } return op; } static std::string get_value(std::string &line) { std::string value; skip_spaces(line); if (!line.empty()) { value.swap(line); } return value; } extern void FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args); extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); rnb_err_t RNBRemote::HandlePacket_qRcmd(const char *p) { const char *c = p + strlen("qRcmd,"); std::string line; while (c[0] && c[1]) { char smallbuf[3] = {c[0], c[1], '\0'}; errno = 0; int ch = static_cast(strtoul(smallbuf, NULL, 16)); if (errno != 0 && ch == 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "non-hex char in payload of qRcmd packet"); line.push_back(ch); c += 2; } if (*c == '\0') { std::string command = get_identifier(line); if (command.compare("set") == 0) { std::string variable = get_identifier(line); std::string op = get_operator(line); std::string value = get_value(line); if (variable.compare("logfile") == 0) { FILE *log_file = fopen(value.c_str(), "w"); if (log_file) { DNBLogSetLogCallback(FileLogCallback, log_file); return SendPacket("OK"); } return SendPacket("E71"); } else if (variable.compare("logmask") == 0) { char *end; errno = 0; uint32_t logmask = static_cast(strtoul(value.c_str(), &end, 0)); if (errno == 0 && end && *end == '\0') { DNBLogSetLogMask(logmask); if (!DNBLogGetLogCallback()) DNBLogSetLogCallback(ASLLogCallback, NULL); return SendPacket("OK"); } errno = 0; logmask = static_cast(strtoul(value.c_str(), &end, 16)); if (errno == 0 && end && *end == '\0') { DNBLogSetLogMask(logmask); return SendPacket("OK"); } return SendPacket("E72"); } return SendPacket("E70"); } return SendPacket("E69"); } return SendPacket("E73"); } rnb_err_t RNBRemote::HandlePacket_qC(const char *p) { nub_thread_t tid; std::ostringstream rep; // If we haven't run the process yet, we tell the debugger the // pid is 0. That way it can know to tell use to run later on. if (!m_ctx.HasValidProcessID()) tid = 0; else { // Grab the current thread. tid = DNBProcessGetCurrentThread(m_ctx.ProcessID()); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. SetCurrentThread(tid); } rep << "QC" << std::hex << tid; return SendPacket(rep.str()); } rnb_err_t RNBRemote::HandlePacket_qEcho(const char *p) { // Just send the exact same packet back that we received to // synchronize the response packets after a previous packet // timed out. This allows the debugger to get back on track // with responses after a packet timeout. return SendPacket(p); } rnb_err_t RNBRemote::HandlePacket_qGetPid(const char *p) { nub_process_t pid; std::ostringstream rep; // If we haven't run the process yet, we tell the debugger the // pid is 0. That way it can know to tell use to run later on. if (m_ctx.HasValidProcessID()) pid = m_ctx.ProcessID(); else pid = 0; rep << std::hex << pid; return SendPacket(rep.str()); } rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) { if (g_num_reg_entries == 0) InitializeRegisters(); p += strlen("qRegisterInfo"); nub_size_t num_reg_sets = 0; const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo(&num_reg_sets); uint32_t reg_num = static_cast(strtoul(p, 0, 16)); if (reg_num < g_num_reg_entries) { const register_map_entry_t *reg_entry = &g_reg_entries[reg_num]; std::ostringstream ostrm; if (reg_entry->nub_info.name) ostrm << "name:" << reg_entry->nub_info.name << ';'; if (reg_entry->nub_info.alt) ostrm << "alt-name:" << reg_entry->nub_info.alt << ';'; ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';'; ostrm << "offset:" << std::dec << reg_entry->offset << ';'; switch (reg_entry->nub_info.type) { case Uint: ostrm << "encoding:uint;"; break; case Sint: ostrm << "encoding:sint;"; break; case IEEE754: ostrm << "encoding:ieee754;"; break; case Vector: ostrm << "encoding:vector;"; break; } switch (reg_entry->nub_info.format) { case Binary: ostrm << "format:binary;"; break; case Decimal: ostrm << "format:decimal;"; break; case Hex: ostrm << "format:hex;"; break; case Float: ostrm << "format:float;"; break; case VectorOfSInt8: ostrm << "format:vector-sint8;"; break; case VectorOfUInt8: ostrm << "format:vector-uint8;"; break; case VectorOfSInt16: ostrm << "format:vector-sint16;"; break; case VectorOfUInt16: ostrm << "format:vector-uint16;"; break; case VectorOfSInt32: ostrm << "format:vector-sint32;"; break; case VectorOfUInt32: ostrm << "format:vector-uint32;"; break; case VectorOfFloat32: ostrm << "format:vector-float32;"; break; case VectorOfUInt128: ostrm << "format:vector-uint128;"; break; }; if (reg_set_info && reg_entry->nub_info.set < num_reg_sets) ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';'; if (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM) ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';'; if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM) ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';'; switch (reg_entry->nub_info.reg_generic) { case GENERIC_REGNUM_FP: ostrm << "generic:fp;"; break; case GENERIC_REGNUM_PC: ostrm << "generic:pc;"; break; case GENERIC_REGNUM_SP: ostrm << "generic:sp;"; break; case GENERIC_REGNUM_RA: ostrm << "generic:ra;"; break; case GENERIC_REGNUM_FLAGS: ostrm << "generic:flags;"; break; case GENERIC_REGNUM_ARG1: ostrm << "generic:arg1;"; break; case GENERIC_REGNUM_ARG2: ostrm << "generic:arg2;"; break; case GENERIC_REGNUM_ARG3: ostrm << "generic:arg3;"; break; case GENERIC_REGNUM_ARG4: ostrm << "generic:arg4;"; break; case GENERIC_REGNUM_ARG5: ostrm << "generic:arg5;"; break; case GENERIC_REGNUM_ARG6: ostrm << "generic:arg6;"; break; case GENERIC_REGNUM_ARG7: ostrm << "generic:arg7;"; break; case GENERIC_REGNUM_ARG8: ostrm << "generic:arg8;"; break; default: break; } if (!reg_entry->value_regnums.empty()) { ostrm << "container-regs:"; for (size_t i = 0, n = reg_entry->value_regnums.size(); i < n; ++i) { if (i > 0) ostrm << ','; ostrm << RAW_HEXBASE << reg_entry->value_regnums[i]; } ostrm << ';'; } if (!reg_entry->invalidate_regnums.empty()) { ostrm << "invalidate-regs:"; for (size_t i = 0, n = reg_entry->invalidate_regnums.size(); i < n; ++i) { if (i > 0) ostrm << ','; ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i]; } ostrm << ';'; } return SendPacket(ostrm.str()); } return SendPacket("E45"); } /* This expects a packet formatted like QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE; with the "QSetLogging:" already removed from the start. Maybe in the future this packet will include other keyvalue pairs like QSetLogging:bitmask=LOG_ALL;mode=asl; */ rnb_err_t set_logging(const char *p) { int bitmask = 0; while (p && *p != '\0') { if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) { p += sizeof("bitmask=") - 1; while (p && *p != '\0' && *p != ';') { if (*p == '|') p++; // to regenerate the LOG_ entries (not including the LOG_RNB entries) // $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v // 'LOG_HI|LOG_LO' | awk '{print $2}'` // do // echo " else if (strncmp (p, \"$logname\", sizeof // (\"$logname\") - 1) == 0)" // echo " {" // echo " p += sizeof (\"$logname\") - 1;" // echo " bitmask |= $logname;" // echo " }" // done if (strncmp(p, "LOG_VERBOSE", sizeof("LOG_VERBOSE") - 1) == 0) { p += sizeof("LOG_VERBOSE") - 1; bitmask |= LOG_VERBOSE; } else if (strncmp(p, "LOG_PROCESS", sizeof("LOG_PROCESS") - 1) == 0) { p += sizeof("LOG_PROCESS") - 1; bitmask |= LOG_PROCESS; } else if (strncmp(p, "LOG_THREAD", sizeof("LOG_THREAD") - 1) == 0) { p += sizeof("LOG_THREAD") - 1; bitmask |= LOG_THREAD; } else if (strncmp(p, "LOG_EXCEPTIONS", sizeof("LOG_EXCEPTIONS") - 1) == 0) { p += sizeof("LOG_EXCEPTIONS") - 1; bitmask |= LOG_EXCEPTIONS; } else if (strncmp(p, "LOG_SHLIB", sizeof("LOG_SHLIB") - 1) == 0) { p += sizeof("LOG_SHLIB") - 1; bitmask |= LOG_SHLIB; } else if (strncmp(p, "LOG_MEMORY", sizeof("LOG_MEMORY") - 1) == 0) { p += sizeof("LOG_MEMORY") - 1; bitmask |= LOG_MEMORY; } else if (strncmp(p, "LOG_MEMORY_DATA_SHORT", sizeof("LOG_MEMORY_DATA_SHORT") - 1) == 0) { p += sizeof("LOG_MEMORY_DATA_SHORT") - 1; bitmask |= LOG_MEMORY_DATA_SHORT; } else if (strncmp(p, "LOG_MEMORY_DATA_LONG", sizeof("LOG_MEMORY_DATA_LONG") - 1) == 0) { p += sizeof("LOG_MEMORY_DATA_LONG") - 1; bitmask |= LOG_MEMORY_DATA_LONG; } else if (strncmp(p, "LOG_MEMORY_PROTECTIONS", sizeof("LOG_MEMORY_PROTECTIONS") - 1) == 0) { p += sizeof("LOG_MEMORY_PROTECTIONS") - 1; bitmask |= LOG_MEMORY_PROTECTIONS; } else if (strncmp(p, "LOG_BREAKPOINTS", sizeof("LOG_BREAKPOINTS") - 1) == 0) { p += sizeof("LOG_BREAKPOINTS") - 1; bitmask |= LOG_BREAKPOINTS; } else if (strncmp(p, "LOG_EVENTS", sizeof("LOG_EVENTS") - 1) == 0) { p += sizeof("LOG_EVENTS") - 1; bitmask |= LOG_EVENTS; } else if (strncmp(p, "LOG_WATCHPOINTS", sizeof("LOG_WATCHPOINTS") - 1) == 0) { p += sizeof("LOG_WATCHPOINTS") - 1; bitmask |= LOG_WATCHPOINTS; } else if (strncmp(p, "LOG_STEP", sizeof("LOG_STEP") - 1) == 0) { p += sizeof("LOG_STEP") - 1; bitmask |= LOG_STEP; } else if (strncmp(p, "LOG_TASK", sizeof("LOG_TASK") - 1) == 0) { p += sizeof("LOG_TASK") - 1; bitmask |= LOG_TASK; } else if (strncmp(p, "LOG_ALL", sizeof("LOG_ALL") - 1) == 0) { p += sizeof("LOG_ALL") - 1; bitmask |= LOG_ALL; } else if (strncmp(p, "LOG_DEFAULT", sizeof("LOG_DEFAULT") - 1) == 0) { p += sizeof("LOG_DEFAULT") - 1; bitmask |= LOG_DEFAULT; } // end of auto-generated entries else if (strncmp(p, "LOG_NONE", sizeof("LOG_NONE") - 1) == 0) { p += sizeof("LOG_NONE") - 1; bitmask = 0; } else if (strncmp(p, "LOG_RNB_MINIMAL", sizeof("LOG_RNB_MINIMAL") - 1) == 0) { p += sizeof("LOG_RNB_MINIMAL") - 1; bitmask |= LOG_RNB_MINIMAL; } else if (strncmp(p, "LOG_RNB_MEDIUM", sizeof("LOG_RNB_MEDIUM") - 1) == 0) { p += sizeof("LOG_RNB_MEDIUM") - 1; bitmask |= LOG_RNB_MEDIUM; } else if (strncmp(p, "LOG_RNB_MAX", sizeof("LOG_RNB_MAX") - 1) == 0) { p += sizeof("LOG_RNB_MAX") - 1; bitmask |= LOG_RNB_MAX; } else if (strncmp(p, "LOG_RNB_COMM", sizeof("LOG_RNB_COMM") - 1) == 0) { p += sizeof("LOG_RNB_COMM") - 1; bitmask |= LOG_RNB_COMM; } else if (strncmp(p, "LOG_RNB_REMOTE", sizeof("LOG_RNB_REMOTE") - 1) == 0) { p += sizeof("LOG_RNB_REMOTE") - 1; bitmask |= LOG_RNB_REMOTE; } else if (strncmp(p, "LOG_RNB_EVENTS", sizeof("LOG_RNB_EVENTS") - 1) == 0) { p += sizeof("LOG_RNB_EVENTS") - 1; bitmask |= LOG_RNB_EVENTS; } else if (strncmp(p, "LOG_RNB_PROC", sizeof("LOG_RNB_PROC") - 1) == 0) { p += sizeof("LOG_RNB_PROC") - 1; bitmask |= LOG_RNB_PROC; } else if (strncmp(p, "LOG_RNB_PACKETS", sizeof("LOG_RNB_PACKETS") - 1) == 0) { p += sizeof("LOG_RNB_PACKETS") - 1; bitmask |= LOG_RNB_PACKETS; } else if (strncmp(p, "LOG_RNB_ALL", sizeof("LOG_RNB_ALL") - 1) == 0) { p += sizeof("LOG_RNB_ALL") - 1; bitmask |= LOG_RNB_ALL; } else if (strncmp(p, "LOG_RNB_DEFAULT", sizeof("LOG_RNB_DEFAULT") - 1) == 0) { p += sizeof("LOG_RNB_DEFAULT") - 1; bitmask |= LOG_RNB_DEFAULT; } else if (strncmp(p, "LOG_DARWIN_LOG", sizeof("LOG_DARWIN_LOG") - 1) == 0) { p += sizeof("LOG_DARWIN_LOG") - 1; bitmask |= LOG_DARWIN_LOG; } else if (strncmp(p, "LOG_RNB_NONE", sizeof("LOG_RNB_NONE") - 1) == 0) { p += sizeof("LOG_RNB_NONE") - 1; bitmask = 0; } else { /* Unrecognized logging bit; ignore it. */ const char *c = strchr(p, '|'); if (c) { p = c; } else { c = strchr(p, ';'); if (c) { p = c; } else { // Improperly terminated word; just go to end of str p = strchr(p, '\0'); } } } } // Did we get a properly formatted logging bitmask? if (p && *p == ';') { // Enable DNB logging. // Use the existing log callback if one was already configured. if (!DNBLogGetLogCallback()) { // Use the os_log()-based logger if available; otherwise, // fallback to ASL. auto log_callback = OsLogger::GetLogFunction(); if (log_callback) DNBLogSetLogCallback(log_callback, nullptr); else DNBLogSetLogCallback(ASLLogCallback, nullptr); } // Update logging to use the configured log channel bitmask. DNBLogSetLogMask(bitmask); p++; } } // We're not going to support logging to a file for now. All logging // goes through ASL or the previously arranged log callback. #if 0 else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0) { p += sizeof ("mode=") - 1; if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0) { DNBLogToASL (); p += sizeof ("asl;") - 1; } else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0) { DNBLogToFile (); p += sizeof ("file;") - 1; } else { // Ignore unknown argument const char *c = strchr (p, ';'); if (c) p = c + 1; else p = strchr (p, '\0'); } } else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0) { p += sizeof ("filename=") - 1; const char *c = strchr (p, ';'); if (c == NULL) { c = strchr (p, '\0'); continue; } char *fn = (char *) alloca (c - p + 1); strncpy (fn, p, c - p); fn[c - p] = '\0'; // A file name of "asl" is special and is another way to indicate // that logging should be done via ASL, not by file. if (strcmp (fn, "asl") == 0) { DNBLogToASL (); } else { FILE *f = fopen (fn, "w"); if (f) { DNBLogSetLogFile (f); DNBEnableLogging (f, DNBLogGetLogMask ()); DNBLogToFile (); } } p = c + 1; } #endif /* #if 0 to enforce ASL logging only. */ else { // Ignore unknown argument const char *c = strchr(p, ';'); if (c) p = c + 1; else p = strchr(p, '\0'); } } return rnb_success; } rnb_err_t RNBRemote::HandlePacket_QThreadSuffixSupported(const char *p) { m_thread_suffix_supported = true; return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QStartNoAckMode(const char *p) { // Send the OK packet first so the correct checksum is appended... rnb_err_t result = SendPacket("OK"); m_noack_mode = true; return result; } rnb_err_t RNBRemote::HandlePacket_QSetLogging(const char *p) { p += sizeof("QSetLogging:") - 1; rnb_err_t result = set_logging(p); if (result == rnb_success) return SendPacket("OK"); else return SendPacket("E35"); } rnb_err_t RNBRemote::HandlePacket_QSetDisableASLR(const char *p) { extern int g_disable_aslr; p += sizeof("QSetDisableASLR:") - 1; switch (*p) { case '0': g_disable_aslr = 0; break; case '1': g_disable_aslr = 1; break; default: return SendPacket("E56"); } return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QSetSTDIO(const char *p) { // Only set stdin/out/err if we don't already have a process if (!m_ctx.HasValidProcessID()) { bool success = false; // Check the seventh character since the packet will be one of: // QSetSTDIN // QSetSTDOUT // QSetSTDERR StdStringExtractor packet(p); packet.SetFilePos(7); char ch = packet.GetChar(); while (packet.GetChar() != ':') /* Do nothing. */; switch (ch) { case 'I': // STDIN packet.GetHexByteString(m_ctx.GetSTDIN()); success = !m_ctx.GetSTDIN().empty(); break; case 'O': // STDOUT packet.GetHexByteString(m_ctx.GetSTDOUT()); success = !m_ctx.GetSTDOUT().empty(); break; case 'E': // STDERR packet.GetHexByteString(m_ctx.GetSTDERR()); success = !m_ctx.GetSTDERR().empty(); break; default: break; } if (success) return SendPacket("OK"); return SendPacket("E57"); } return SendPacket("E58"); } rnb_err_t RNBRemote::HandlePacket_QSetWorkingDir(const char *p) { // Only set the working directory if we don't already have a process if (!m_ctx.HasValidProcessID()) { StdStringExtractor packet(p += sizeof("QSetWorkingDir:") - 1); if (packet.GetHexByteString(m_ctx.GetWorkingDir())) { struct stat working_dir_stat; if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1) { m_ctx.GetWorkingDir().clear(); return SendPacket("E61"); // Working directory doesn't exist... } else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR) { return SendPacket("OK"); } else { m_ctx.GetWorkingDir().clear(); return SendPacket("E62"); // Working directory isn't a directory... } } return SendPacket("E59"); // Invalid path } return SendPacket( "E60"); // Already had a process, too late to set working dir } rnb_err_t RNBRemote::HandlePacket_QSyncThreadState(const char *p) { if (!m_ctx.HasValidProcessID()) { // We allow gdb to connect to a server that hasn't started running // the target yet. gdb still wants to ask questions about it and // freaks out if it gets an error. So just return OK here. return SendPacket("OK"); } errno = 0; p += strlen("QSyncThreadState:"); nub_thread_t tid = strtoul(p, NULL, 16); if (errno != 0 && tid == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Invalid thread number in QSyncThreadState packet"); } if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid)) return SendPacket("OK"); else return SendPacket("E61"); } rnb_err_t RNBRemote::HandlePacket_QSetDetachOnError(const char *p) { p += sizeof("QSetDetachOnError:") - 1; bool should_detach = true; switch (*p) { case '0': should_detach = false; break; case '1': should_detach = true; break; default: return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Invalid value for QSetDetachOnError - should be 0 or 1"); break; } m_ctx.SetDetachOnError(should_detach); return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_qStructuredDataPlugins(const char *p) { // We'll return a JSON array of supported packet types. // The type is significant. For each of the supported // packet types that have been enabled, there will be a // 'J' async packet sent to the client with payload data. // This payload data will be a JSON dictionary, and the // top level dictionary will contain a string field with // its value set to the relevant packet type from this list. JSONGenerator::Array supported_json_packets; // Check for DarwinLog (libtrace os_log/activity support). if (DarwinLogCollector::IsSupported()) supported_json_packets.AddItem( JSONGenerator::StringSP(new JSONGenerator::String("DarwinLog"))); // Send back the array. std::ostringstream stream; supported_json_packets.Dump(stream); return SendPacket(stream.str()); } rnb_err_t RNBRemote::HandlePacket_QConfigureDarwinLog(const char *p) { if (!DarwinLogCollector::IsSupported()) { // We should never have been given this request. return SendPacket("E89"); } // Ensure we have a process. We expect a separate configure request for // each process launched/attached. const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E94"); // Get the configuration dictionary. p += strlen("QConfigureDarwinLog:"); // The configuration dictionary is binary encoded. std::vector unescaped_config_data = decode_binary_data(p, -1); std::string unescaped_config_string((const char *)&unescaped_config_data[0], unescaped_config_data.size()); DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog: received config data: \"%s\"", unescaped_config_string.c_str()); auto configuration_sp = JSONParser(unescaped_config_string.c_str()).ParseJSONValue(); if (!configuration_sp) { // Malformed request - we require configuration data // indicating whether we're enabling or disabling. return SendPacket("E90"); } if (!JSONObject::classof(configuration_sp.get())) { // Configuration data is not of the right type. return SendPacket("E91"); } JSONObject &config_dict = *static_cast(configuration_sp.get()); // Check if we're enabling or disabling. auto enabled_sp = config_dict.GetObject("enabled"); if (!enabled_sp) { // Missing required "enabled" field. return SendPacket("E92"); } if (!JSONTrue::classof(enabled_sp.get()) && !JSONFalse::classof(enabled_sp.get())) { // Should be a boolean type, but wasn't. return SendPacket("E93"); } const bool enabling = JSONTrue::classof(enabled_sp.get()); // TODO - handle other configuration parameters here. // Shut down any active activity stream for the process. DarwinLogCollector::CancelStreamForProcess(pid); if (enabling) { // Look up the procecess. if (!DarwinLogCollector::StartCollectingForProcess(pid, config_dict)) return SendPacket("E95"); } return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QListThreadsInStopReply(const char *p) { // If this packet is received, it allows us to send an extra key/value // pair in the stop reply packets where we will list all of the thread IDs // separated by commas: // // "threads:10a,10b,10c;" // // This will get included in the stop reply packet as something like: // // "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;" // // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and // speed things up a bit. // // Send the OK packet first so the correct checksum is appended... rnb_err_t result = SendPacket("OK"); m_list_threads_in_stop_reply = true; return result; } rnb_err_t RNBRemote::HandlePacket_QSetMaxPayloadSize(const char *p) { /* The number of characters in a packet payload that gdb is prepared to accept. The packet-start char, packet-end char, 2 checksum chars and terminating null character are not included in this size. */ p += sizeof("QSetMaxPayloadSize:") - 1; errno = 0; uint32_t size = static_cast(strtoul(p, NULL, 16)); if (errno != 0 && size == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Invalid length in QSetMaxPayloadSize packet"); } m_max_payload_size = size; return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QSetMaxPacketSize(const char *p) { /* This tells us the largest packet that gdb can handle. i.e. the size of gdb's packet-reading buffer. QSetMaxPayloadSize is preferred because it is less ambiguous. */ p += sizeof("QSetMaxPacketSize:") - 1; errno = 0; uint32_t size = static_cast(strtoul(p, NULL, 16)); if (errno != 0 && size == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in QSetMaxPacketSize packet"); } m_max_payload_size = size - 5; return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QEnvironment(const char *p) { /* This sets the environment for the target program. The packet is of the form: QEnvironment:VARIABLE=VALUE */ DNBLogThreadedIf( LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p); p += sizeof("QEnvironment:") - 1; RNBContext &ctx = Context(); ctx.PushEnvironment(p); return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QEnvironmentHexEncoded(const char *p) { /* This sets the environment for the target program. The packet is of the form: QEnvironmentHexEncoded:VARIABLE=VALUE The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with special meaning in the remote protocol won't break it. */ DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p); p += sizeof("QEnvironmentHexEncoded:") - 1; std::string arg; const char *c; c = p; while (*c != '\0') { if (*(c + 1) == '\0') { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt"); } char smallbuf[3]; smallbuf[0] = *c; smallbuf[1] = *(c + 1); smallbuf[2] = '\0'; errno = 0; int ch = static_cast(strtoul(smallbuf, NULL, 16)); if (errno != 0 && ch == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt"); } arg.push_back(ch); c += 2; } RNBContext &ctx = Context(); if (arg.length() > 0) ctx.PushEnvironment(arg.c_str()); return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_QLaunchArch(const char *p) { p += sizeof("QLaunchArch:") - 1; if (DNBSetArchitecture(p)) return SendPacket("OK"); return SendPacket("E63"); } rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) { p += sizeof("QSetProcessEvent:") - 1; // If the process is running, then send the event to the process, otherwise // store it in the context. if (Context().HasValidProcessID()) { if (DNBProcessSendEvent(Context().ProcessID(), p)) return SendPacket("OK"); else return SendPacket("E80"); } else { Context().PushProcessEvent(p); } return SendPacket("OK"); } void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size, bool swap) { int i; const uint8_t *p = (const uint8_t *)buf; if (swap) { for (i = static_cast(buf_size) - 1; i >= 0; i--) ostrm << RAWHEX8(p[i]); } else { for (size_t i = 0; i < buf_size; i++) ostrm << RAWHEX8(p[i]); } } void append_hexified_string(std::ostream &ostrm, const std::string &string) { size_t string_size = string.size(); const char *string_buf = string.c_str(); for (size_t i = 0; i < string_size; i++) { ostrm << RAWHEX8(*(string_buf + i)); } } void register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid, nub_thread_t tid, const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr) { if (reg != NULL) { DNBRegisterValue reg_value; if (reg_value_ptr == NULL) { if (DNBThreadGetRegisterValueByID(pid, tid, reg->nub_info.set, reg->nub_info.reg, ®_value)) reg_value_ptr = ®_value; } if (reg_value_ptr) { append_hex_value(ostrm, reg_value_ptr->value.v_uint8, reg->nub_info.size, false); } else { // If we fail to read a register value, check if it has a default // fail value. If it does, return this instead in case some of // the registers are not available on the current system. if (reg->nub_info.size > 0) { std::basic_string zeros(reg->nub_info.size, '\0'); append_hex_value(ostrm, zeros.data(), zeros.size(), false); } } } } void debugserver_regnum_with_fixed_width_hex_register_value( std::ostream &ostrm, nub_process_t pid, nub_thread_t tid, const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr) { // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX // gdb register number, and VVVVVVVV is the correct number of hex bytes // as ASCII for the register value. if (reg != NULL) { ostrm << RAWHEX8(reg->debugserver_regnum) << ':'; register_value_in_hex_fixed_width(ostrm, pid, tid, reg, reg_value_ptr); ostrm << ';'; } } void RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo( nub_process_t pid, nub_addr_t dispatch_qaddr, nub_addr_t &dispatch_queue_t, std::string &queue_name, uint64_t &queue_width, uint64_t &queue_serialnum) const { queue_name.clear(); queue_width = 0; queue_serialnum = 0; if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS && dispatch_qaddr != 0) { dispatch_queue_t = DNBProcessMemoryReadPointer(pid, dispatch_qaddr); if (dispatch_queue_t) { queue_width = DNBProcessMemoryReadInteger( pid, dispatch_queue_t + dqo_width, dqo_width_size, 0); queue_serialnum = DNBProcessMemoryReadInteger( pid, dispatch_queue_t + dqo_serialnum, dqo_serialnum_size, 0); if (dqo_version >= 4) { // libdispatch versions 4+, pointer to dispatch name is in the // queue structure. nub_addr_t pointer_to_label_address = dispatch_queue_t + dqo_label; nub_addr_t label_addr = DNBProcessMemoryReadPointer(pid, pointer_to_label_address); if (label_addr) queue_name = DNBProcessMemoryReadCString(pid, label_addr); } else { // libdispatch versions 1-3, dispatch name is a fixed width char array // in the queue structure. queue_name = DNBProcessMemoryReadCStringFixed( pid, dispatch_queue_t + dqo_label, dqo_label_size); } } } } struct StackMemory { uint8_t bytes[2 * sizeof(nub_addr_t)]; nub_size_t length; }; typedef std::map StackMemoryMap; static void ReadStackMemory(nub_process_t pid, nub_thread_t tid, StackMemoryMap &stack_mmap, uint32_t backtrace_limit = 256) { DNBRegisterValue reg_value; if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_FP, ®_value)) { uint32_t frame_count = 0; uint64_t fp = 0; if (reg_value.info.size == 4) fp = reg_value.value.uint32; else fp = reg_value.value.uint64; while (fp != 0) { // Make sure we never recurse more than 256 times so we don't recurse too // far or // store up too much memory in the expedited cache if (++frame_count > backtrace_limit) break; const nub_size_t read_size = reg_value.info.size * 2; StackMemory stack_memory; stack_memory.length = read_size; if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) != read_size) break; // Make sure we don't try to put the same stack memory in more than once if (stack_mmap.find(fp) != stack_mmap.end()) break; // Put the entry into the cache stack_mmap[fp] = stack_memory; // Dereference the frame pointer to get to the previous frame pointer if (reg_value.info.size == 4) fp = ((uint32_t *)stack_memory.bytes)[0]; else fp = ((uint64_t *)stack_memory.bytes)[0]; } } } rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) { const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E50"); struct DNBThreadStopInfo tid_stop_info; /* Fill the remaining space in this packet with as many registers as we can stuff in there. */ if (DNBThreadGetStopReason(pid, tid, &tid_stop_info)) { const bool did_exec = tid_stop_info.reason == eStopTypeExec; if (did_exec) { RNBRemote::InitializeRegisters(true); // Reset any symbols that need resetting when we exec m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS; m_dispatch_queue_offsets.Clear(); } std::ostringstream ostrm; // Output the T packet with the thread ostrm << 'T'; int signum = tid_stop_info.details.signal.signo; DNBLogThreadedIf( LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, signum, tid_stop_info.details.exception.type); // Translate any mach exceptions to gdb versions, unless they are // common exceptions like a breakpoint or a soft signal. switch (tid_stop_info.details.exception.type) { default: signum = 0; break; case EXC_BREAKPOINT: signum = SIGTRAP; break; case EXC_BAD_ACCESS: signum = TARGET_EXC_BAD_ACCESS; break; case EXC_BAD_INSTRUCTION: signum = TARGET_EXC_BAD_INSTRUCTION; break; case EXC_ARITHMETIC: signum = TARGET_EXC_ARITHMETIC; break; case EXC_EMULATION: signum = TARGET_EXC_EMULATION; break; case EXC_SOFTWARE: if (tid_stop_info.details.exception.data_count == 2 && tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL) signum = static_cast(tid_stop_info.details.exception.data[1]); else signum = TARGET_EXC_SOFTWARE; break; } ostrm << RAWHEX8(signum & 0xff); ostrm << std::hex << "thread:" << tid << ';'; const char *thread_name = DNBThreadGetName(pid, tid); if (thread_name && thread_name[0]) { size_t thread_name_len = strlen(thread_name); if (::strcspn(thread_name, "$#+-;:") == thread_name_len) ostrm << std::hex << "name:" << thread_name << ';'; else { // the thread name contains special chars, send as hex bytes ostrm << std::hex << "hexname:"; uint8_t *u_thread_name = (uint8_t *)thread_name; for (size_t i = 0; i < thread_name_len; i++) ostrm << RAWHEX8(u_thread_name[i]); ostrm << ';'; } } // If a 'QListThreadsInStopReply' was sent to enable this feature, we // will send all thread IDs back in the "threads" key whose value is // a list of hex thread IDs separated by commas: // "threads:10a,10b,10c;" // This will save the debugger from having to send a pair of qfThreadInfo // and qsThreadInfo packets, but it also might take a lot of room in the // stop reply packet, so it must be enabled only on systems where there // are no limits on packet lengths. if (m_list_threads_in_stop_reply) { const nub_size_t numthreads = DNBProcessGetNumThreads(pid); if (numthreads > 0) { std::vector pc_values; ostrm << std::hex << "threads:"; for (nub_size_t i = 0; i < numthreads; ++i) { nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i); if (i > 0) ostrm << ','; ostrm << std::hex << th; DNBRegisterValue pc_regval; if (DNBThreadGetRegisterValueByID(pid, th, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, &pc_regval)) { uint64_t pc = INVALID_NUB_ADDRESS; if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS) { if (pc_regval.info.size == 4) { pc = pc_regval.value.uint32; } else if (pc_regval.info.size == 8) { pc = pc_regval.value.uint64; } if (pc != INVALID_NUB_ADDRESS) { pc_values.push_back(pc); } } } } ostrm << ';'; // If we failed to get any of the thread pc values, the size of our // vector will not // be the same as the # of threads. Don't provide any expedited thread // pc values in // that case. This should not happen. if (pc_values.size() == numthreads) { ostrm << std::hex << "thread-pcs:"; for (nub_size_t i = 0; i < numthreads; ++i) { if (i > 0) ostrm << ','; ostrm << std::hex << pc_values[i]; } ostrm << ';'; } } // Include JSON info that describes the stop reason for any threads // that actually have stop reasons. We use the new "jstopinfo" key // whose values is hex ascii JSON that contains the thread IDs // thread stop info only for threads that have stop reasons. Only send // this if we have more than one thread otherwise this packet has all // the info it needs. if (numthreads > 1) { const bool threads_with_valid_stop_info_only = true; JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only); if (threads_info_sp) { ostrm << std::hex << "jstopinfo:"; std::ostringstream json_strm; threads_info_sp->Dump(json_strm); append_hexified_string(ostrm, json_strm.str()); ostrm << ';'; } } } if (g_num_reg_entries == 0) InitializeRegisters(); if (g_reg_entries != NULL) { DNBRegisterValue reg_value; for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) { // Expedite all registers in the first register set that aren't // contained in other registers if (g_reg_entries[reg].nub_info.set == 1 && g_reg_entries[reg].nub_info.value_regs == NULL) { if (!DNBThreadGetRegisterValueByID( pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, ®_value)) continue; debugserver_regnum_with_fixed_width_hex_register_value( ostrm, pid, tid, &g_reg_entries[reg], ®_value); } } } if (did_exec) { ostrm << "reason:exec;"; } else if (tid_stop_info.details.exception.type) { ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ';'; ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ';'; for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ';'; } // Add expedited stack memory so stack backtracing doesn't need to read // anything from the // frame pointer chain. StackMemoryMap stack_mmap; ReadStackMemory(pid, tid, stack_mmap, 2); if (!stack_mmap.empty()) { for (const auto &stack_memory : stack_mmap) { ostrm << "memory:" << HEXBASE << stack_memory.first << '='; append_hex_value(ostrm, stack_memory.second.bytes, stack_memory.second.length, false); ostrm << ';'; } } return SendPacket(ostrm.str()); } return SendPacket("E51"); } /* '?' The stop reply packet - tell gdb what the status of the inferior is. Often called the questionmark_packet. */ rnb_err_t RNBRemote::HandlePacket_last_signal(const char *unused) { if (!m_ctx.HasValidProcessID()) { // Inferior is not yet specified/running return SendPacket("E02"); } nub_process_t pid = m_ctx.ProcessID(); nub_state_t pid_state = DNBProcessGetState(pid); switch (pid_state) { case eStateAttaching: case eStateLaunching: case eStateRunning: case eStateStepping: case eStateDetached: return rnb_success; // Ignore case eStateSuspended: case eStateStopped: case eStateCrashed: { nub_thread_t tid = DNBProcessGetCurrentThread(pid); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. SetCurrentThread(tid); SendStopReplyPacketForThread(tid); } break; case eStateInvalid: case eStateUnloaded: case eStateExited: { char pid_exited_packet[16] = ""; int pid_status = 0; // Process exited with exit status if (!DNBProcessGetExitStatus(pid, &pid_status)) pid_status = 0; if (pid_status) { if (WIFEXITED(pid_status)) snprintf(pid_exited_packet, sizeof(pid_exited_packet), "W%02x", WEXITSTATUS(pid_status)); else if (WIFSIGNALED(pid_status)) snprintf(pid_exited_packet, sizeof(pid_exited_packet), "X%02x", WEXITSTATUS(pid_status)); else if (WIFSTOPPED(pid_status)) snprintf(pid_exited_packet, sizeof(pid_exited_packet), "S%02x", WSTOPSIG(pid_status)); } // If we have an empty exit packet, lets fill one in to be safe. if (!pid_exited_packet[0]) { strncpy(pid_exited_packet, "W00", sizeof(pid_exited_packet) - 1); pid_exited_packet[sizeof(pid_exited_packet) - 1] = '\0'; } const char *exit_info = DNBProcessGetExitInfo(pid); if (exit_info != NULL && *exit_info != '\0') { std::ostringstream exit_packet; exit_packet << pid_exited_packet; exit_packet << ';'; exit_packet << RAW_HEXBASE << "description"; exit_packet << ':'; for (size_t i = 0; exit_info[i] != '\0'; i++) exit_packet << RAWHEX8(exit_info[i]); exit_packet << ';'; return SendPacket(exit_packet.str()); } else return SendPacket(pid_exited_packet); } break; } return rnb_success; } rnb_err_t RNBRemote::HandlePacket_M(const char *p) { if (p == NULL || p[0] == '\0' || strlen(p) < 3) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short M packet"); } char *c; p++; errno = 0; nub_addr_t addr = strtoull(p, &c, 16); if (errno != 0 && addr == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid address in M packet"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma sep missing in M packet"); } /* Advance 'p' to the length part of the packet. */ p += (c - p) + 1; errno = 0; unsigned long length = strtoul(p, &c, 16); if (errno != 0 && length == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in M packet"); } if (length == 0) { return SendPacket("OK"); } if (*c != ':') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Missing colon in M packet"); } /* Advance 'p' to the data part of the packet. */ p += (c - p) + 1; size_t datalen = strlen(p); if (datalen & 0x1) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Uneven # of hex chars for data in M packet"); } if (datalen == 0) { return SendPacket("OK"); } uint8_t *buf = (uint8_t *)alloca(datalen / 2); uint8_t *i = buf; while (*p != '\0' && *(p + 1) != '\0') { char hexbuf[3]; hexbuf[0] = *p; hexbuf[1] = *(p + 1); hexbuf[2] = '\0'; errno = 0; uint8_t byte = strtoul(hexbuf, NULL, 16); if (errno != 0 && byte == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid hex byte in M packet"); } *i++ = byte; p += 2; } nub_size_t wrote = DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, length, buf); if (wrote != length) return SendPacket("E09"); else return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_m(const char *p) { if (p == NULL || p[0] == '\0' || strlen(p) < 3) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short m packet"); } char *c; p++; errno = 0; nub_addr_t addr = strtoull(p, &c, 16); if (errno != 0 && addr == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid address in m packet"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma sep missing in m packet"); } /* Advance 'p' to the length part of the packet. */ p += (c - p) + 1; errno = 0; auto length = strtoul(p, NULL, 16); if (errno != 0 && length == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in m packet"); } if (length == 0) { return SendPacket(""); } std::string buf(length, '\0'); if (buf.empty()) { return SendPacket("E78"); } nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]); if (bytes_read == 0) { return SendPacket("E08"); } // "The reply may contain fewer bytes than requested if the server was able // to read only part of the region of memory." length = bytes_read; std::ostringstream ostrm; for (unsigned long i = 0; i < length; i++) ostrm << RAWHEX8(buf[i]); return SendPacket(ostrm.str()); } // Read memory, sent it up as binary data. // Usage: xADDR,LEN // ADDR and LEN are both base 16. // Responds with 'OK' for zero-length request // or // // DATA // // where DATA is the binary data payload. rnb_err_t RNBRemote::HandlePacket_x(const char *p) { if (p == NULL || p[0] == '\0' || strlen(p) < 3) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet"); } char *c; p++; errno = 0; nub_addr_t addr = strtoull(p, &c, 16); if (errno != 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid address in X packet"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma sep missing in X packet"); } /* Advance 'p' to the number of bytes to be read. */ p += (c - p) + 1; errno = 0; auto length = strtoul(p, NULL, 16); if (errno != 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in x packet"); } // zero length read means this is a test of whether that packet is implemented // or not. if (length == 0) { return SendPacket("OK"); } std::vector buf(length); if (buf.capacity() != length) { return SendPacket("E79"); } nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]); if (bytes_read == 0) { return SendPacket("E80"); } std::vector buf_quoted; buf_quoted.reserve(bytes_read + 30); for (nub_size_t i = 0; i < bytes_read; i++) { if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') { buf_quoted.push_back(0x7d); buf_quoted.push_back(buf[i] ^ 0x20); } else { buf_quoted.push_back(buf[i]); } } length = buf_quoted.size(); std::ostringstream ostrm; for (unsigned long i = 0; i < length; i++) ostrm << buf_quoted[i]; return SendPacket(ostrm.str()); } rnb_err_t RNBRemote::HandlePacket_X(const char *p) { if (p == NULL || p[0] == '\0' || strlen(p) < 3) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet"); } char *c; p++; errno = 0; nub_addr_t addr = strtoull(p, &c, 16); if (errno != 0 && addr == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid address in X packet"); } if (*c != ',') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma sep missing in X packet"); } /* Advance 'p' to the length part of the packet. NB this is the length of the packet including any escaped chars. The data payload may be a little bit smaller after decoding. */ p += (c - p) + 1; errno = 0; auto length = strtoul(p, NULL, 16); if (errno != 0 && length == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in X packet"); } // I think gdb sends a zero length write request to test whether this // packet is accepted. if (length == 0) { return SendPacket("OK"); } std::vector data = decode_binary_data(c, -1); std::vector::const_iterator it; uint8_t *buf = (uint8_t *)alloca(data.size()); uint8_t *i = buf; for (it = data.begin(); it != data.end(); ++it) { *i++ = *it; } nub_size_t wrote = DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, data.size(), buf); if (wrote != data.size()) return SendPacket("E08"); return SendPacket("OK"); } /* 'g' -- read registers Get the contents of the registers for the current thread, send them to gdb. Should the setting of the Hg packet determine which thread's registers are returned? */ rnb_err_t RNBRemote::HandlePacket_g(const char *p) { std::ostringstream ostrm; if (!m_ctx.HasValidProcessID()) { return SendPacket("E11"); } if (g_num_reg_entries == 0) InitializeRegisters(); nub_process_t pid = m_ctx.ProcessID(); nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p + 1); if (tid == INVALID_NUB_THREAD) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in p packet"); // Get the register context size first by calling with NULL buffer nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0); if (reg_ctx_size) { // Now allocate enough space for the entire register context std::vector reg_ctx; reg_ctx.resize(reg_ctx_size); // Now read the register context reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, ®_ctx[0], reg_ctx.size()); if (reg_ctx_size) { append_hex_value(ostrm, reg_ctx.data(), reg_ctx.size(), false); return SendPacket(ostrm.str()); } } return SendPacket("E74"); } /* 'G XXX...' -- write registers How is the thread for these specified, beyond "the current thread"? Does gdb actually use the Hg packet to set this? */ rnb_err_t RNBRemote::HandlePacket_G(const char *p) { if (!m_ctx.HasValidProcessID()) { return SendPacket("E11"); } if (g_num_reg_entries == 0) InitializeRegisters(); StdStringExtractor packet(p); packet.SetFilePos(1); // Skip the 'G' nub_process_t pid = m_ctx.ProcessID(); nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); if (tid == INVALID_NUB_THREAD) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in p packet"); // Get the register context size first by calling with NULL buffer nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0); if (reg_ctx_size) { // Now allocate enough space for the entire register context std::vector reg_ctx; reg_ctx.resize(reg_ctx_size); const nub_size_t bytes_extracted = packet.GetHexBytes(®_ctx[0], reg_ctx.size(), 0xcc); if (bytes_extracted == reg_ctx.size()) { // Now write the register context reg_ctx_size = DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size()); if (reg_ctx_size == reg_ctx.size()) return SendPacket("OK"); else return SendPacket("E55"); } else { DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu " "bytes, size mismatch\n", p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size); return SendPacket("E64"); } } return SendPacket("E65"); } static bool RNBRemoteShouldCancelCallback(void *not_used) { RNBRemoteSP remoteSP(g_remoteSP); if (remoteSP.get() != NULL) { RNBRemote *remote = remoteSP.get(); if (remote->Comm().IsConnected()) return false; else return true; } return true; } // FORMAT: _MXXXXXX,PPP // XXXXXX: big endian hex chars // PPP: permissions can be any combo of r w x chars // // RESPONSE: XXXXXX // XXXXXX: hex address of the newly allocated memory // EXX: error code // // EXAMPLES: // _M123000,rw // _M123000,rwx // _M123000,xw rnb_err_t RNBRemote::HandlePacket_AllocateMemory(const char *p) { StdStringExtractor packet(p); packet.SetFilePos(2); // Skip the "_M" nub_addr_t size = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0); if (size != 0) { if (packet.GetChar() == ',') { uint32_t permissions = 0; char ch; bool success = true; while (success && (ch = packet.GetChar()) != '\0') { switch (ch) { case 'r': permissions |= eMemoryPermissionsReadable; break; case 'w': permissions |= eMemoryPermissionsWritable; break; case 'x': permissions |= eMemoryPermissionsExecutable; break; default: success = false; break; } } if (success) { nub_addr_t addr = DNBProcessMemoryAllocate(m_ctx.ProcessID(), size, permissions); if (addr != INVALID_NUB_ADDRESS) { std::ostringstream ostrm; ostrm << RAW_HEXBASE << addr; return SendPacket(ostrm.str()); } } } } return SendPacket("E53"); } // FORMAT: _mXXXXXX // XXXXXX: address that was previously allocated // // RESPONSE: XXXXXX // OK: address was deallocated // EXX: error code // // EXAMPLES: // _m123000 rnb_err_t RNBRemote::HandlePacket_DeallocateMemory(const char *p) { StdStringExtractor packet(p); packet.SetFilePos(2); // Skip the "_m" nub_addr_t addr = packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS); if (addr != INVALID_NUB_ADDRESS) { if (DNBProcessMemoryDeallocate(m_ctx.ProcessID(), addr)) return SendPacket("OK"); } return SendPacket("E54"); } // FORMAT: QSaveRegisterState;thread:TTTT; (when thread suffix is supported) // FORMAT: QSaveRegisterState (when thread suffix is NOT // supported) // TTTT: thread ID in hex // // RESPONSE: // SAVEID: Where SAVEID is a decimal number that represents the save ID // that can be passed back into a "QRestoreRegisterState" packet // EXX: error code // // EXAMPLES: // QSaveRegisterState;thread:1E34; (when thread suffix is supported) // QSaveRegisterState (when thread suffix is NOT // supported) rnb_err_t RNBRemote::HandlePacket_SaveRegisterState(const char *p) { nub_process_t pid = m_ctx.ProcessID(); nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); if (tid == INVALID_NUB_THREAD) { if (m_thread_suffix_supported) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet"); else return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread was is set with the Hg packet"); } // Get the register context size first by calling with NULL buffer const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid); if (save_id != 0) { char response[64]; snprintf(response, sizeof(response), "%u", save_id); return SendPacket(response); } else { return SendPacket("E75"); } } // FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT; (when thread suffix is // supported) // FORMAT: QRestoreRegisterState:SAVEID (when thread suffix is NOT // supported) // TTTT: thread ID in hex // SAVEID: a decimal number that represents the save ID that was // returned from a call to "QSaveRegisterState" // // RESPONSE: // OK: successfully restored registers for the specified thread // EXX: error code // // EXAMPLES: // QRestoreRegisterState:1;thread:1E34; (when thread suffix is // supported) // QRestoreRegisterState:1 (when thread suffix is NOT // supported) rnb_err_t RNBRemote::HandlePacket_RestoreRegisterState(const char *p) { nub_process_t pid = m_ctx.ProcessID(); nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); if (tid == INVALID_NUB_THREAD) { if (m_thread_suffix_supported) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet"); else return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread was is set with the Hg packet"); } StdStringExtractor packet(p); packet.SetFilePos( strlen("QRestoreRegisterState:")); // Skip the "QRestoreRegisterState:" const uint32_t save_id = packet.GetU32(0); if (save_id != 0) { // Get the register context size first by calling with NULL buffer if (DNBThreadRestoreRegisterState(pid, tid, save_id)) return SendPacket("OK"); else return SendPacket("E77"); } return SendPacket("E76"); } static bool GetProcessNameFrom_vAttach(const char *&p, std::string &attach_name) { bool return_val = true; while (*p != '\0') { char smallbuf[3]; smallbuf[0] = *p; smallbuf[1] = *(p + 1); smallbuf[2] = '\0'; errno = 0; int ch = static_cast(strtoul(smallbuf, NULL, 16)); if (errno != 0 && ch == 0) { return_val = false; break; } attach_name.push_back(ch); p += 2; } return return_val; } rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet // size--debugger can always use less char buf[256]; snprintf(buf, sizeof(buf), "qXfer:features:read+;PacketSize=%x;qEcho+", max_packet_size); bool enable_compression = false; (void)enable_compression; -#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCHOS == 1) || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) || (defined (TARGET_OS_TVOS) && TARGET_OS_TVOS == 1) +#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) enable_compression = true; #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (enable_compression && compression_decode_buffer != NULL) { strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;" "DefaultCompressionMinSize="); char numbuf[16]; snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize); numbuf[sizeof(numbuf) - 1] = '\0'; strcat(buf, numbuf); } #elif defined(HAVE_LIBZ) if (enable_compression) { strcat(buf, ";SupportedCompressions=zlib-deflate;DefaultCompressionMinSize="); char numbuf[16]; snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize); numbuf[sizeof(numbuf) - 1] = '\0'; strcat(buf, numbuf); } #endif return SendPacket(buf); } /* vAttach;pid Attach to a new process with the specified process ID. pid is a hexadecimal integer identifying the process. If the stub is currently controlling a process, it is killed. The attached process is stopped.This packet is only available in extended mode (see extended mode). Reply: "ENN" for an error "Any Stop Reply Packet" for success */ rnb_err_t RNBRemote::HandlePacket_v(const char *p) { if (strcmp(p, "vCont;c") == 0) { // Simple continue return RNBRemote::HandlePacket_c("c"); } else if (strcmp(p, "vCont;s") == 0) { // Simple step return RNBRemote::HandlePacket_s("s"); } else if (strstr(p, "vCont") == p) { DNBThreadResumeActions thread_actions; char *c = (char *)(p += strlen("vCont")); char *c_end = c + strlen(c); if (*c == '?') return SendPacket("vCont;c;C;s;S"); while (c < c_end && *c == ';') { ++c; // Skip the semi-colon DNBThreadResumeAction thread_action; thread_action.tid = INVALID_NUB_THREAD; thread_action.state = eStateInvalid; thread_action.signal = 0; thread_action.addr = INVALID_NUB_ADDRESS; char action = *c++; switch (action) { case 'C': errno = 0; thread_action.signal = static_cast(strtoul(c, &c, 16)); if (errno != 0) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse signal in vCont packet"); // Fall through to next case... case 'c': // Continue thread_action.state = eStateRunning; break; case 'S': errno = 0; thread_action.signal = static_cast(strtoul(c, &c, 16)); if (errno != 0) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse signal in vCont packet"); // Fall through to next case... case 's': // Step thread_action.state = eStateStepping; break; default: HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Unsupported action in vCont packet"); break; } if (*c == ':') { errno = 0; thread_action.tid = strtoul(++c, &c, 16); if (errno != 0) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse thread number in vCont packet"); } thread_actions.Append(thread_action); } // If a default action for all other threads wasn't mentioned // then we should stop the threads thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst(), thread_actions.GetSize()); return rnb_success; } else if (strstr(p, "vAttach") == p) { nub_process_t attach_pid = INVALID_NUB_PROCESS; // attach_pid will be set to 0 if the attach fails nub_process_t pid_attaching_to = INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified char err_str[1024] = {'\0'}; std::string attach_name; if (strstr(p, "vAttachWait;") == p) { p += strlen("vAttachWait;"); if (!GetProcessNameFrom_vAttach(p, attach_name)) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachWait' pkt"); } const bool ignore_existing = true; attach_pid = DNBProcessAttachWait( attach_name.c_str(), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback); } else if (strstr(p, "vAttachOrWait;") == p) { p += strlen("vAttachOrWait;"); if (!GetProcessNameFrom_vAttach(p, attach_name)) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachOrWait' pkt"); } const bool ignore_existing = false; attach_pid = DNBProcessAttachWait( attach_name.c_str(), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback); } else if (strstr(p, "vAttachName;") == p) { p += strlen("vAttachName;"); if (!GetProcessNameFrom_vAttach(p, attach_name)) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachName' pkt"); } attach_pid = DNBProcessAttachByName(attach_name.c_str(), NULL, err_str, sizeof(err_str)); } else if (strstr(p, "vAttach;") == p) { p += strlen("vAttach;"); char *end = NULL; pid_attaching_to = static_cast( strtoul(p, &end, 16)); // PID will be in hex, so use base 16 to decode if (p != end && *end == '\0') { // Wait at most 30 second for attach struct timespec attach_timeout_abstime; DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0); attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime, err_str, sizeof(err_str)); } } else { return HandlePacket_UNIMPLEMENTED(p); } if (attach_pid != INVALID_NUB_PROCESS) { if (m_ctx.ProcessID() != attach_pid) m_ctx.SetProcessID(attach_pid); // Send a stop reply packet to indicate we successfully attached! NotifyThatProcessStopped(); return rnb_success; } else { m_ctx.LaunchStatus().SetError(-1, DNBError::Generic); if (err_str[0]) m_ctx.LaunchStatus().SetErrorString(err_str); else m_ctx.LaunchStatus().SetErrorString("attach failed"); #if defined(__APPLE__) && \ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty()) { pid_attaching_to = DNBProcessGetPIDByName(attach_name.c_str()); } if (pid_attaching_to != INVALID_NUB_PROCESS && strcmp(err_str, "No such process") != 0) { // csr_check(CSR_ALLOW_TASK_FOR_PID) will be nonzero if System Integrity // Protection is in effect. if (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) { bool attach_failed_due_to_sip = false; if (rootless_allows_task_for_pid(pid_attaching_to) == 0) { attach_failed_due_to_sip = true; } if (attach_failed_due_to_sip == false) { int csops_flags = 0; int retval = ::csops(pid_attaching_to, CS_OPS_STATUS, &csops_flags, sizeof(csops_flags)); if (retval != -1 && (csops_flags & CS_RESTRICT)) { attach_failed_due_to_sip = true; } } if (attach_failed_due_to_sip) { SendPacket("E87"); // E87 is the magic value which says that we are // not allowed to attach DNBLogError("Attach failed because process does not allow " "attaching: \"%s\".", err_str); return rnb_err; } } } #endif SendPacket("E01"); // E01 is our magic error value for attach failed. DNBLogError("Attach failed: \"%s\".", err_str); return rnb_err; } } // All other failures come through here return HandlePacket_UNIMPLEMENTED(p); } /* 'T XX' -- status of thread Check if the specified thread is alive. The thread number is in hex? */ rnb_err_t RNBRemote::HandlePacket_T(const char *p) { p++; if (p == NULL || *p == '\0') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in T packet"); } if (!m_ctx.HasValidProcessID()) { return SendPacket("E15"); } errno = 0; nub_thread_t tid = strtoul(p, NULL, 16); if (errno != 0 && tid == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse thread number in T packet"); } nub_state_t state = DNBThreadGetState(m_ctx.ProcessID(), tid); if (state == eStateInvalid || state == eStateExited || state == eStateCrashed) { return SendPacket("E16"); } return SendPacket("OK"); } rnb_err_t RNBRemote::HandlePacket_z(const char *p) { if (p == NULL || *p == '\0') return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in z packet"); if (!m_ctx.HasValidProcessID()) return SendPacket("E15"); char packet_cmd = *p++; char break_type = *p++; if (*p++ != ',') return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma separator missing in z packet"); char *c = NULL; nub_process_t pid = m_ctx.ProcessID(); errno = 0; nub_addr_t addr = strtoull(p, &c, 16); if (errno != 0 && addr == 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid address in z packet"); p = c; if (*p++ != ',') return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Comma separator missing in z packet"); errno = 0; auto byte_size = strtoul(p, &c, 16); if (errno != 0 && byte_size == 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Invalid length in z packet"); if (packet_cmd == 'Z') { // set switch (break_type) { case '0': // set software breakpoint case '1': // set hardware breakpoint { // gdb can send multiple Z packets for the same address and // these calls must be ref counted. bool hardware = (break_type == '1'); if (DNBBreakpointSet(pid, addr, byte_size, hardware)) { // We successfully created a breakpoint, now lets full out // a ref count structure with the breakID and add it to our // map. return SendPacket("OK"); } else { // We failed to set the software breakpoint return SendPacket("E09"); } } break; case '2': // set write watchpoint case '3': // set read watchpoint case '4': // set access watchpoint { bool hardware = true; uint32_t watch_flags = 0; if (break_type == '2') watch_flags = WATCH_TYPE_WRITE; else if (break_type == '3') watch_flags = WATCH_TYPE_READ; else watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE; if (DNBWatchpointSet(pid, addr, byte_size, watch_flags, hardware)) { return SendPacket("OK"); } else { // We failed to set the watchpoint return SendPacket("E09"); } } break; default: break; } } else if (packet_cmd == 'z') { // remove switch (break_type) { case '0': // remove software breakpoint case '1': // remove hardware breakpoint if (DNBBreakpointClear(pid, addr)) { return SendPacket("OK"); } else { return SendPacket("E08"); } break; case '2': // remove write watchpoint case '3': // remove read watchpoint case '4': // remove access watchpoint if (DNBWatchpointClear(pid, addr)) { return SendPacket("OK"); } else { return SendPacket("E08"); } break; default: break; } } return HandlePacket_UNIMPLEMENTED(p); } // Extract the thread number from the thread suffix that might be appended to // thread specific packets. This will only be enabled if // m_thread_suffix_supported // is true. nub_thread_t RNBRemote::ExtractThreadIDFromThreadSuffix(const char *p) { if (m_thread_suffix_supported) { nub_thread_t tid = INVALID_NUB_THREAD; if (p) { const char *tid_cstr = strstr(p, "thread:"); if (tid_cstr) { tid_cstr += strlen("thread:"); tid = strtoul(tid_cstr, NULL, 16); } } return tid; } return GetCurrentThread(); } /* 'p XX' print the contents of register X */ rnb_err_t RNBRemote::HandlePacket_p(const char *p) { if (g_num_reg_entries == 0) InitializeRegisters(); if (p == NULL || *p == '\0') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in p packet"); } if (!m_ctx.HasValidProcessID()) { return SendPacket("E15"); } nub_process_t pid = m_ctx.ProcessID(); errno = 0; char *tid_cstr = NULL; uint32_t reg = static_cast(strtoul(p + 1, &tid_cstr, 16)); if (errno != 0 && reg == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse register number in p packet"); } nub_thread_t tid = ExtractThreadIDFromThreadSuffix(tid_cstr); if (tid == INVALID_NUB_THREAD) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in p packet"); const register_map_entry_t *reg_entry; if (reg < g_num_reg_entries) reg_entry = &g_reg_entries[reg]; else reg_entry = NULL; std::ostringstream ostrm; if (reg_entry == NULL) { DNBLogError( "RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n", p, reg); ostrm << "00000000"; } else if (reg_entry->nub_info.reg == (uint32_t)-1) { if (reg_entry->nub_info.size > 0) { std::basic_string zeros(reg_entry->nub_info.size, '\0'); append_hex_value(ostrm, zeros.data(), zeros.size(), false); } } else { register_value_in_hex_fixed_width(ostrm, pid, tid, reg_entry, NULL); } return SendPacket(ostrm.str()); } /* 'Pnn=rrrrr' Set register number n to value r. n and r are hex strings. */ rnb_err_t RNBRemote::HandlePacket_P(const char *p) { if (g_num_reg_entries == 0) InitializeRegisters(); if (p == NULL || *p == '\0') { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Empty P packet"); } if (!m_ctx.HasValidProcessID()) { return SendPacket("E28"); } nub_process_t pid = m_ctx.ProcessID(); StdStringExtractor packet(p); const char cmd_char = packet.GetChar(); // Register ID is always in big endian const uint32_t reg = packet.GetHexMaxU32(false, UINT32_MAX); const char equal_char = packet.GetChar(); if (cmd_char != 'P') return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Improperly formed P packet"); if (reg == UINT32_MAX) return SendPacket("E29"); if (equal_char != '=') return SendPacket("E30"); const register_map_entry_t *reg_entry; if (reg >= g_num_reg_entries) return SendPacket("E47"); reg_entry = &g_reg_entries[reg]; if (reg_entry->nub_info.set == (uint32_t)-1 && reg_entry->nub_info.reg == (uint32_t)-1) { DNBLogError( "RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n", p, reg); return SendPacket("E48"); } DNBRegisterValue reg_value; reg_value.info = reg_entry->nub_info; packet.GetHexBytes(reg_value.value.v_sint8, reg_entry->nub_info.size, 0xcc); nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); if (tid == INVALID_NUB_THREAD) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "No thread specified in p packet"); if (!DNBThreadSetRegisterValueByID(pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, ®_value)) { return SendPacket("E32"); } return SendPacket("OK"); } /* 'c [addr]' Continue, optionally from a specified address. */ rnb_err_t RNBRemote::HandlePacket_c(const char *p) { const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E23"); DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS}; if (*(p + 1) != '\0') { action.tid = GetContinueThread(); errno = 0; action.addr = strtoull(p + 1, NULL, 16); if (errno != 0 && action.addr == 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse address in c packet"); } DNBThreadResumeActions thread_actions; thread_actions.Append(action); thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0); if (!DNBProcessResume(pid, thread_actions.GetFirst(), thread_actions.GetSize())) return SendPacket("E25"); // Don't send an "OK" packet; response is the stopped/exited message. return rnb_success; } rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) { /* This packet will find memory attributes (e.g. readable, writable, executable, stack, jitted code) for the memory region containing a given address and return that information. Users of this packet must be prepared for three results: Region information is returned Region information is unavailable for this address because the address is in unmapped memory Region lookup cannot be performed on this platform or process is not yet launched This packet isn't implemented Examples of use: qMemoryRegionInfo:3a55140 start:3a50000,size:100000,permissions:rwx qMemoryRegionInfo:0 error:address in unmapped region qMemoryRegionInfo:3a551140 (on a different platform) error:region lookup cannot be performed qMemoryRegionInfo OK // this packet is implemented by the remote nub */ p += sizeof("qMemoryRegionInfo") - 1; if (*p == '\0') return SendPacket("OK"); if (*p++ != ':') return SendPacket("E67"); if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) p += 2; errno = 0; uint64_t address = strtoul(p, NULL, 16); if (errno != 0 && address == 0) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Invalid address in qMemoryRegionInfo packet"); } DNBRegionInfo region_info = {0, 0, 0}; DNBProcessMemoryRegionInfo(m_ctx.ProcessID(), address, ®ion_info); std::ostringstream ostrm; // start:3a50000,size:100000,permissions:rwx ostrm << "start:" << std::hex << region_info.addr << ';'; if (region_info.size > 0) ostrm << "size:" << std::hex << region_info.size << ';'; if (region_info.permissions) { ostrm << "permissions:"; if (region_info.permissions & eMemoryPermissionsReadable) ostrm << 'r'; if (region_info.permissions & eMemoryPermissionsWritable) ostrm << 'w'; if (region_info.permissions & eMemoryPermissionsExecutable) ostrm << 'x'; ostrm << ';'; } return SendPacket(ostrm.str()); } // qGetProfileData;scan_type:0xYYYYYYY rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) { nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("OK"); StdStringExtractor packet(p += sizeof("qGetProfileData")); DNBProfileDataScanType scan_type = eProfileAll; std::string name; std::string value; while (packet.GetNameColonValue(name, value)) { if (name.compare("scan_type") == 0) { std::istringstream iss(value); uint32_t int_value = 0; if (iss >> std::hex >> int_value) { scan_type = (DNBProfileDataScanType)int_value; } } } std::string data = DNBProcessGetProfileData(pid, scan_type); if (!data.empty()) { return SendPacket(data.c_str()); } else { return SendPacket("OK"); } } // QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY rnb_err_t RNBRemote::HandlePacket_SetEnableAsyncProfiling(const char *p) { nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("OK"); StdStringExtractor packet(p += sizeof("QSetEnableAsyncProfiling")); bool enable = false; uint64_t interval_usec = 0; DNBProfileDataScanType scan_type = eProfileAll; std::string name; std::string value; while (packet.GetNameColonValue(name, value)) { if (name.compare("enable") == 0) { enable = strtoul(value.c_str(), NULL, 10) > 0; } else if (name.compare("interval_usec") == 0) { interval_usec = strtoul(value.c_str(), NULL, 10); } else if (name.compare("scan_type") == 0) { std::istringstream iss(value); uint32_t int_value = 0; if (iss >> std::hex >> int_value) { scan_type = (DNBProfileDataScanType)int_value; } } } if (interval_usec == 0) { enable = 0; } DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type); return SendPacket("OK"); } // QEnableCompression:type:;minsize:; // // type: must be a type previously reported by the qXfer:features: // SupportedCompressions list // // minsize: is optional; by default the qXfer:features: // DefaultCompressionMinSize value is used // debugserver may have a better idea of what a good minimum packet size to // compress is than lldb. rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) { p += sizeof("QEnableCompression:") - 1; size_t new_compression_minsize = m_compression_minsize; const char *new_compression_minsize_str = strstr(p, "minsize:"); if (new_compression_minsize_str) { new_compression_minsize_str += strlen("minsize:"); errno = 0; new_compression_minsize = strtoul(new_compression_minsize_str, NULL, 10); if (errno != 0 || new_compression_minsize == ULONG_MAX) { new_compression_minsize = m_compression_minsize; } } #if defined(HAVE_LIBCOMPRESSION) if (compression_decode_buffer != NULL) { if (strstr(p, "type:zlib-deflate;") != nullptr) { EnableCompressionNextSendPacket(compression_types::zlib_deflate); m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } else if (strstr(p, "type:lz4;") != nullptr) { EnableCompressionNextSendPacket(compression_types::lz4); m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } else if (strstr(p, "type:lzma;") != nullptr) { EnableCompressionNextSendPacket(compression_types::lzma); m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } else if (strstr(p, "type:lzfse;") != nullptr) { EnableCompressionNextSendPacket(compression_types::lzfse); m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } } #endif #if defined(HAVE_LIBZ) if (strstr(p, "type:zlib-deflate;") != nullptr) { EnableCompressionNextSendPacket(compression_types::zlib_deflate); m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } #endif return SendPacket("E88"); } rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) { p += strlen("qSpeedTest:response_size:"); char *end = NULL; errno = 0; uint64_t response_size = ::strtoul(p, &end, 16); if (errno != 0) return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Didn't find response_size value at right offset"); else if (*end == ';') { static char g_data[4 * 1024 * 1024 + 16] = "data:"; memset(g_data + 5, 'a', response_size); g_data[response_size + 5] = '\0'; return SendPacket(g_data); } else { return SendPacket("E79"); } } rnb_err_t RNBRemote::HandlePacket_WatchpointSupportInfo(const char *p) { /* This packet simply returns the number of supported hardware watchpoints. Examples of use: qWatchpointSupportInfo: num:4 qWatchpointSupportInfo OK // this packet is implemented by the remote nub */ p += sizeof("qWatchpointSupportInfo") - 1; if (*p == '\0') return SendPacket("OK"); if (*p++ != ':') return SendPacket("E67"); errno = 0; uint32_t num = DNBWatchpointGetNumSupportedHWP(m_ctx.ProcessID()); std::ostringstream ostrm; // size:4 ostrm << "num:" << std::dec << num << ';'; return SendPacket(ostrm.str()); } /* 'C sig [;addr]' Resume with signal sig, optionally at address addr. */ rnb_err_t RNBRemote::HandlePacket_C(const char *p) { const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E36"); DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS}; int process_signo = -1; if (*(p + 1) != '\0') { action.tid = GetContinueThread(); char *end = NULL; errno = 0; process_signo = static_cast(strtoul(p + 1, &end, 16)); if (errno != 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse signal in C packet"); else if (*end == ';') { errno = 0; action.addr = strtoull(end + 1, NULL, 16); if (errno != 0 && action.addr == 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse address in C packet"); } } DNBThreadResumeActions thread_actions; thread_actions.Append(action); thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, action.signal); if (!DNBProcessSignal(pid, process_signo)) return SendPacket("E52"); if (!DNBProcessResume(pid, thread_actions.GetFirst(), thread_actions.GetSize())) return SendPacket("E38"); /* Don't send an "OK" packet; response is the stopped/exited message. */ return rnb_success; } //---------------------------------------------------------------------- // 'D' packet // Detach from gdb. //---------------------------------------------------------------------- rnb_err_t RNBRemote::HandlePacket_D(const char *p) { if (m_ctx.HasValidProcessID()) { if (DNBProcessDetach(m_ctx.ProcessID())) SendPacket("OK"); else SendPacket("E"); } else { SendPacket("E"); } return rnb_success; } /* 'k' Kill the inferior process. */ rnb_err_t RNBRemote::HandlePacket_k(const char *p) { DNBLog("Got a 'k' packet, killing the inferior process."); // No response to should be sent to the kill packet if (m_ctx.HasValidProcessID()) DNBProcessKill(m_ctx.ProcessID()); SendPacket("X09"); return rnb_success; } rnb_err_t RNBRemote::HandlePacket_stop_process(const char *p) { //#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test //exiting on interrupt #if defined(TEST_EXIT_ON_INTERRUPT) rnb_err_t err = HandlePacket_k(p); m_comm.Disconnect(true); return err; #else if (!DNBProcessInterrupt(m_ctx.ProcessID())) { // If we failed to interrupt the process, then send a stop // reply packet as the process was probably already stopped DNBLogThreaded("RNBRemote::HandlePacket_stop_process() sending extra stop " "reply because DNBProcessInterrupt returned false"); HandlePacket_last_signal(NULL); } return rnb_success; #endif } /* 's' Step the inferior process. */ rnb_err_t RNBRemote::HandlePacket_s(const char *p) { const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E32"); // Hardware supported stepping not supported on arm nub_thread_t tid = GetContinueThread(); if (tid == 0 || tid == (nub_thread_t)-1) tid = GetCurrentThread(); if (tid == INVALID_NUB_THREAD) return SendPacket("E33"); DNBThreadResumeActions thread_actions; thread_actions.AppendAction(tid, eStateStepping); // Make all other threads stop when we are stepping thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); if (!DNBProcessResume(pid, thread_actions.GetFirst(), thread_actions.GetSize())) return SendPacket("E49"); // Don't send an "OK" packet; response is the stopped/exited message. return rnb_success; } /* 'S sig [;addr]' Step with signal sig, optionally at address addr. */ rnb_err_t RNBRemote::HandlePacket_S(const char *p) { const nub_process_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) return SendPacket("E36"); DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateStepping, 0, INVALID_NUB_ADDRESS}; if (*(p + 1) != '\0') { char *end = NULL; errno = 0; action.signal = static_cast(strtoul(p + 1, &end, 16)); if (errno != 0) return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse signal in S packet"); else if (*end == ';') { errno = 0; action.addr = strtoull(end + 1, NULL, 16); if (errno != 0 && action.addr == 0) { return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Could not parse address in S packet"); } } } action.tid = GetContinueThread(); if (action.tid == 0 || action.tid == (nub_thread_t)-1) return SendPacket("E40"); nub_state_t tstate = DNBThreadGetState(pid, action.tid); if (tstate == eStateInvalid || tstate == eStateExited) return SendPacket("E37"); DNBThreadResumeActions thread_actions; thread_actions.Append(action); // Make all other threads stop when we are stepping thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); if (!DNBProcessResume(pid, thread_actions.GetFirst(), thread_actions.GetSize())) return SendPacket("E39"); // Don't send an "OK" packet; response is the stopped/exited message. return rnb_success; } static const char *GetArchName(const uint32_t cputype, const uint32_t cpusubtype) { switch (cputype) { case CPU_TYPE_ARM: switch (cpusubtype) { case 5: return "armv4"; case 6: return "armv6"; case 7: return "armv5t"; case 8: return "xscale"; case 9: return "armv7"; case 10: return "armv7f"; case 11: return "armv7s"; case 12: return "armv7k"; case 14: return "armv6m"; case 15: return "armv7m"; case 16: return "armv7em"; default: return "arm"; } break; case CPU_TYPE_ARM64: return "arm64"; case CPU_TYPE_I386: return "i386"; case CPU_TYPE_X86_64: switch (cpusubtype) { default: return "x86_64"; case 8: return "x86_64h"; } break; } return NULL; } static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype, uint32_t &is_64_bit_capable, bool &promoted_to_64) { static uint32_t g_host_cputype = 0; static uint32_t g_host_cpusubtype = 0; static uint32_t g_is_64_bit_capable = 0; static bool g_promoted_to_64 = false; if (g_host_cputype == 0) { g_promoted_to_64 = false; size_t len = sizeof(uint32_t); if (::sysctlbyname("hw.cputype", &g_host_cputype, &len, NULL, 0) == 0) { len = sizeof(uint32_t); if (::sysctlbyname("hw.cpu64bit_capable", &g_is_64_bit_capable, &len, NULL, 0) == 0) { if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0)) { g_promoted_to_64 = true; g_host_cputype |= CPU_ARCH_ABI64; } } } len = sizeof(uint32_t); if (::sysctlbyname("hw.cpusubtype", &g_host_cpusubtype, &len, NULL, 0) == 0) { if (g_promoted_to_64 && g_host_cputype == CPU_TYPE_X86_64 && g_host_cpusubtype == CPU_SUBTYPE_486) g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL; } } cputype = g_host_cputype; cpusubtype = g_host_cpusubtype; is_64_bit_capable = g_is_64_bit_capable; promoted_to_64 = g_promoted_to_64; return g_host_cputype != 0; } rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) { std::ostringstream strm; uint32_t cputype = 0; uint32_t cpusubtype = 0; uint32_t is_64_bit_capable = 0; bool promoted_to_64 = false; if (GetHostCPUType(cputype, cpusubtype, is_64_bit_capable, promoted_to_64)) { strm << "cputype:" << std::dec << cputype << ';'; strm << "cpusubtype:" << std::dec << cpusubtype << ';'; } // The OS in the triple should be "ios" or "macosx" which doesn't match our // "Darwin" which gets returned from "kern.ostype", so we need to hardcode // this for now. if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 strm << "ostype:tvos;"; #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 strm << "ostype:watchos;"; #else strm << "ostype:ios;"; #endif // On armv7 we use "synchronous" watchpoints which means the exception is // delivered before the instruction executes. strm << "watchpoint_exceptions_received:before;"; } else { strm << "ostype:macosx;"; strm << "watchpoint_exceptions_received:after;"; } // char ostype[64]; // len = sizeof(ostype); // if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) // { // len = strlen(ostype); // std::transform (ostype, ostype + len, ostype, tolower); // strm << "ostype:" << std::dec << ostype << ';'; // } strm << "vendor:apple;"; uint64_t major, minor, patch; if (DNBGetOSVersionNumbers(&major, &minor, &patch)) { strm << "os_version:" << major << "." << minor; if (patch != UINT64_MAX) strm << "." << patch; strm << ";"; } #if defined(__LITTLE_ENDIAN__) strm << "endian:little;"; #elif defined(__BIG_ENDIAN__) strm << "endian:big;"; #elif defined(__PDP_ENDIAN__) strm << "endian:pdp;"; #endif if (promoted_to_64) strm << "ptrsize:8;"; else strm << "ptrsize:" << std::dec << sizeof(void *) << ';'; #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 strm << "default_packet_timeout:10;"; #endif return SendPacket(strm.str()); } void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name, bool has_attributes) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << name; if (!has_attributes) s << '>' << std::endl; } void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) { if (empty) s << '/'; s << '>' << std::endl; } void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << '/' << name << '>' << std::endl; } void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent, const char *name, const char *value, bool close = true) { if (value) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << name << '>' << value; if (close) XMLElementEnd(s, 0, name); } } void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent, const char *name, uint64_t value, bool close = true) { if (indent) s << INDENT_WITH_SPACES(indent); s << '<' << name << '>' << DECIMAL << value; if (close) XMLElementEnd(s, 0, name); } void XMLAttributeString(std::ostringstream &s, const char *name, const char *value, const char *default_value = NULL) { if (value) { if (default_value && strcmp(value, default_value) == 0) return; // No need to emit the attribute because it matches the default // value s << ' ' << name << "=\"" << value << "\""; } } void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name, uint64_t value) { s << ' ' << name << "=\"" << DECIMAL << value << "\""; } void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num, nub_size_t num_reg_sets, const DNBRegisterSetInfo *reg_set_info, const register_map_entry_t ®) { const char *default_lldb_encoding = "uint"; const char *lldb_encoding = default_lldb_encoding; const char *gdb_group = "general"; const char *default_gdb_type = "int"; const char *gdb_type = default_gdb_type; const char *default_lldb_format = "hex"; const char *lldb_format = default_lldb_format; const char *lldb_set = NULL; switch (reg.nub_info.type) { case Uint: lldb_encoding = "uint"; break; case Sint: lldb_encoding = "sint"; break; case IEEE754: lldb_encoding = "ieee754"; if (reg.nub_info.set > 0) gdb_group = "float"; break; case Vector: lldb_encoding = "vector"; if (reg.nub_info.set > 0) gdb_group = "vector"; break; } switch (reg.nub_info.format) { case Binary: lldb_format = "binary"; break; case Decimal: lldb_format = "decimal"; break; case Hex: lldb_format = "hex"; break; case Float: gdb_type = "float"; lldb_format = "float"; break; case VectorOfSInt8: gdb_type = "float"; lldb_format = "vector-sint8"; break; case VectorOfUInt8: gdb_type = "float"; lldb_format = "vector-uint8"; break; case VectorOfSInt16: gdb_type = "float"; lldb_format = "vector-sint16"; break; case VectorOfUInt16: gdb_type = "float"; lldb_format = "vector-uint16"; break; case VectorOfSInt32: gdb_type = "float"; lldb_format = "vector-sint32"; break; case VectorOfUInt32: gdb_type = "float"; lldb_format = "vector-uint32"; break; case VectorOfFloat32: gdb_type = "float"; lldb_format = "vector-float32"; break; case VectorOfUInt128: gdb_type = "float"; lldb_format = "vector-uint128"; break; }; if (reg_set_info && reg.nub_info.set < num_reg_sets) lldb_set = reg_set_info[reg.nub_info.set].name; uint32_t indent = 2; XMLElementStart(s, indent, "reg", true); XMLAttributeString(s, "name", reg.nub_info.name); XMLAttributeUnsignedDecimal(s, "regnum", reg_num); XMLAttributeUnsignedDecimal(s, "offset", reg.offset); XMLAttributeUnsignedDecimal(s, "bitsize", reg.nub_info.size * 8); XMLAttributeString(s, "group", gdb_group); XMLAttributeString(s, "type", gdb_type, default_gdb_type); XMLAttributeString(s, "altname", reg.nub_info.alt); XMLAttributeString(s, "encoding", lldb_encoding, default_lldb_encoding); XMLAttributeString(s, "format", lldb_format, default_lldb_format); XMLAttributeUnsignedDecimal(s, "group_id", reg.nub_info.set); if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM) XMLAttributeUnsignedDecimal(s, "ehframe_regnum", reg.nub_info.reg_ehframe); if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM) XMLAttributeUnsignedDecimal(s, "dwarf_regnum", reg.nub_info.reg_dwarf); const char *lldb_generic = NULL; switch (reg.nub_info.reg_generic) { case GENERIC_REGNUM_FP: lldb_generic = "fp"; break; case GENERIC_REGNUM_PC: lldb_generic = "pc"; break; case GENERIC_REGNUM_SP: lldb_generic = "sp"; break; case GENERIC_REGNUM_RA: lldb_generic = "ra"; break; case GENERIC_REGNUM_FLAGS: lldb_generic = "flags"; break; case GENERIC_REGNUM_ARG1: lldb_generic = "arg1"; break; case GENERIC_REGNUM_ARG2: lldb_generic = "arg2"; break; case GENERIC_REGNUM_ARG3: lldb_generic = "arg3"; break; case GENERIC_REGNUM_ARG4: lldb_generic = "arg4"; break; case GENERIC_REGNUM_ARG5: lldb_generic = "arg5"; break; case GENERIC_REGNUM_ARG6: lldb_generic = "arg6"; break; case GENERIC_REGNUM_ARG7: lldb_generic = "arg7"; break; case GENERIC_REGNUM_ARG8: lldb_generic = "arg8"; break; default: break; } XMLAttributeString(s, "generic", lldb_generic); bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty(); if (!empty) { if (!reg.value_regnums.empty()) { std::ostringstream regnums; bool first = true; regnums << DECIMAL; for (auto regnum : reg.value_regnums) { if (!first) regnums << ','; regnums << regnum; first = false; } XMLAttributeString(s, "value_regnums", regnums.str().c_str()); } if (!reg.invalidate_regnums.empty()) { std::ostringstream regnums; bool first = true; regnums << DECIMAL; for (auto regnum : reg.invalidate_regnums) { if (!first) regnums << ','; regnums << regnum; first = false; } XMLAttributeString(s, "invalidate_regnums", regnums.str().c_str()); } } XMLElementStartEndAttributes(s, true); } void GenerateTargetXMLRegisters(std::ostringstream &s) { nub_size_t num_reg_sets = 0; const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets); uint32_t cputype = DNBGetRegisterCPUType(); if (cputype) { XMLElementStart(s, 0, "feature", true); std::ostringstream name_strm; name_strm << "com.apple.debugserver." << GetArchName(cputype, 0); XMLAttributeString(s, "name", name_strm.str().c_str()); XMLElementStartEndAttributes(s, false); for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num) // for (const auto ®: g_dynamic_register_map) { GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_sets, g_reg_entries[reg_num]); } XMLElementEnd(s, 0, "feature"); if (num_reg_sets > 0) { XMLElementStart(s, 0, "groups", false); for (uint32_t set = 1; set < num_reg_sets; ++set) { XMLElementStart(s, 2, "group", true); XMLAttributeUnsignedDecimal(s, "id", set); XMLAttributeString(s, "name", reg_sets[set].name); XMLElementStartEndAttributes(s, true); } XMLElementEnd(s, 0, "groups"); } } } static const char *g_target_xml_header = R"( )"; static const char *g_target_xml_footer = ""; static std::string g_target_xml; void UpdateTargetXML() { std::ostringstream s; s << g_target_xml_header << std::endl; // Set the architecture // s << "" << arch "" << std::endl; // Set the OSABI // s << "abi-name" GenerateTargetXMLRegisters(s); s << g_target_xml_footer << std::endl; // Save the XML output in case it gets retrieved in chunks g_target_xml = s.str(); } rnb_err_t RNBRemote::HandlePacket_qXfer(const char *command) { const char *p = command; p += strlen("qXfer:"); const char *sep = strchr(p, ':'); if (sep) { std::string object(p, sep - p); // "auxv", "backtrace", "features", etc p = sep + 1; sep = strchr(p, ':'); if (sep) { std::string rw(p, sep - p); // "read" or "write" p = sep + 1; sep = strchr(p, ':'); if (sep) { std::string annex(p, sep - p); // "read" or "write" p = sep + 1; sep = strchr(p, ','); if (sep) { std::string offset_str(p, sep - p); // read the length as a string p = sep + 1; std::string length_str(p); // read the offset as a string char *end = nullptr; const uint64_t offset = strtoul(offset_str.c_str(), &end, 16); // convert offset_str to a offset if (*end == '\0') { const uint64_t length = strtoul( length_str.c_str(), &end, 16); // convert length_str to a length if (*end == '\0') { if (object == "features" && rw == "read" && annex == "target.xml") { std::ostringstream xml_out; if (offset == 0) { InitializeRegisters(true); UpdateTargetXML(); if (g_target_xml.empty()) return SendPacket("E83"); if (length > g_target_xml.size()) { xml_out << 'l'; // No more data xml_out << binary_encode_string(g_target_xml); } else { xml_out << 'm'; // More data needs to be read with a // subsequent call xml_out << binary_encode_string( std::string(g_target_xml, offset, length)); } } else { // Retrieving target XML in chunks if (offset < g_target_xml.size()) { std::string chunk(g_target_xml, offset, length); if (chunk.size() < length) xml_out << 'l'; // No more data else xml_out << 'm'; // More data needs to be read with a // subsequent call xml_out << binary_encode_string(chunk.data()); } } return SendPacket(xml_out.str()); } // Well formed, put not supported return HandlePacket_UNIMPLEMENTED(command); } } } } else { SendPacket("E85"); } } else { SendPacket("E86"); } } return SendPacket("E82"); } rnb_err_t RNBRemote::HandlePacket_qGDBServerVersion(const char *p) { std::ostringstream strm; #if defined(DEBUGSERVER_PROGRAM_NAME) strm << "name:" DEBUGSERVER_PROGRAM_NAME ";"; #else strm << "name:debugserver;"; #endif strm << "version:" << DEBUGSERVER_VERSION_NUM << ";"; return SendPacket(strm.str()); } // A helper function that retrieves a single integer value from // a one-level-deep JSON dictionary of key-value pairs. e.g. // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] // uint64_t get_integer_value_for_key_name_from_json(const char *key, const char *json_string) { uint64_t retval = INVALID_NUB_ADDRESS; std::string key_with_quotes = "\""; key_with_quotes += key; key_with_quotes += "\""; const char *c = strstr(json_string, key_with_quotes.c_str()); if (c) { c += key_with_quotes.size(); while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == ':') { c++; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; errno = 0; retval = strtoul(c, NULL, 10); if (errno != 0) { retval = INVALID_NUB_ADDRESS; } } } return retval; } // A helper function that retrieves a boolean value from // a one-level-deep JSON dictionary of key-value pairs. e.g. // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}] // Returns true if it was able to find the key name, and sets the 'value' // argument to the value found. bool get_boolean_value_for_key_name_from_json(const char *key, const char *json_string, bool &value) { std::string key_with_quotes = "\""; key_with_quotes += key; key_with_quotes += "\""; const char *c = strstr(json_string, key_with_quotes.c_str()); if (c) { c += key_with_quotes.size(); while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == ':') { c++; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (strncmp(c, "true", 4) == 0) { value = true; return true; } else if (strncmp(c, "false", 5) == 0) { value = false; return true; } } } return false; } // A helper function that reads an array of uint64_t's from // a one-level-deep JSON dictionary of key-value pairs. e.g. // jGetLoadedDynamicLibrariesInfos:{"solib_addrs":[31345823,7768020384,7310483024]}] // Returns true if it was able to find the key name, false if it did not. // "ints" will have all integers found in the array appended to it. bool get_array_of_ints_value_for_key_name_from_json( const char *key, const char *json_string, std::vector &ints) { std::string key_with_quotes = "\""; key_with_quotes += key; key_with_quotes += "\""; const char *c = strstr(json_string, key_with_quotes.c_str()); if (c) { c += key_with_quotes.size(); while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == ':') { c++; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == '[') { c++; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; while (1) { if (!isdigit(*c)) { return true; } errno = 0; char *endptr; uint64_t value = strtoul(c, &endptr, 10); if (errno == 0) { ints.push_back(value); } else { break; } if (endptr == c || endptr == nullptr || *endptr == '\0') { break; } c = endptr; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == ',') c++; while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) c++; if (*c == ']') { return true; } } } } } return false; } JSONGenerator::ObjectSP RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) { JSONGenerator::ArraySP threads_array_sp; if (m_ctx.HasValidProcessID()) { threads_array_sp.reset(new JSONGenerator::Array()); nub_process_t pid = m_ctx.ProcessID(); nub_size_t numthreads = DNBProcessGetNumThreads(pid); for (nub_size_t i = 0; i < numthreads; ++i) { nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, i); struct DNBThreadStopInfo tid_stop_info; const bool stop_info_valid = DNBThreadGetStopReason(pid, tid, &tid_stop_info); // If we are doing stop info only, then we only show threads that have a // valid stop reason if (threads_with_valid_stop_info_only) { if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid) continue; } JSONGenerator::DictionarySP thread_dict_sp( new JSONGenerator::Dictionary()); thread_dict_sp->AddIntegerItem("tid", tid); std::string reason_value("none"); if (stop_info_valid) { switch (tid_stop_info.reason) { case eStopTypeInvalid: break; case eStopTypeSignal: if (tid_stop_info.details.signal.signo != 0) { thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo); reason_value = "signal"; } break; case eStopTypeException: if (tid_stop_info.details.exception.type != 0) { reason_value = "exception"; thread_dict_sp->AddIntegerItem( "metype", tid_stop_info.details.exception.type); JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array()); for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { medata_array_sp->AddItem( JSONGenerator::IntegerSP(new JSONGenerator::Integer( tid_stop_info.details.exception.data[i]))); } thread_dict_sp->AddItem("medata", medata_array_sp); } break; case eStopTypeExec: reason_value = "exec"; break; } } thread_dict_sp->AddStringItem("reason", reason_value); if (threads_with_valid_stop_info_only == false) { const char *thread_name = DNBThreadGetName(pid, tid); if (thread_name && thread_name[0]) thread_dict_sp->AddStringItem("name", thread_name); thread_identifier_info_data_t thread_ident_info; if (DNBThreadGetIdentifierInfo(pid, tid, &thread_ident_info)) { if (thread_ident_info.dispatch_qaddr != 0) { thread_dict_sp->AddIntegerItem("qaddr", thread_ident_info.dispatch_qaddr); const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets(); if (dispatch_queue_offsets) { std::string queue_name; uint64_t queue_width = 0; uint64_t queue_serialnum = 0; nub_addr_t dispatch_queue_t = INVALID_NUB_ADDRESS; dispatch_queue_offsets->GetThreadQueueInfo( pid, thread_ident_info.dispatch_qaddr, dispatch_queue_t, queue_name, queue_width, queue_serialnum); if (dispatch_queue_t == 0 && queue_name.empty() && queue_serialnum == 0) { thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue", false); } else { thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue", true); } if (dispatch_queue_t != INVALID_NUB_ADDRESS && dispatch_queue_t != 0) thread_dict_sp->AddIntegerItem("dispatch_queue_t", dispatch_queue_t); if (!queue_name.empty()) thread_dict_sp->AddStringItem("qname", queue_name); if (queue_width == 1) thread_dict_sp->AddStringItem("qkind", "serial"); else if (queue_width > 1) thread_dict_sp->AddStringItem("qkind", "concurrent"); if (queue_serialnum > 0) thread_dict_sp->AddIntegerItem("qserialnum", queue_serialnum); } } } DNBRegisterValue reg_value; if (g_reg_entries != NULL) { JSONGenerator::DictionarySP registers_dict_sp( new JSONGenerator::Dictionary()); for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) { // Expedite all registers in the first register set that aren't // contained in other registers if (g_reg_entries[reg].nub_info.set == 1 && g_reg_entries[reg].nub_info.value_regs == NULL) { if (!DNBThreadGetRegisterValueByID( pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, ®_value)) continue; std::ostringstream reg_num; reg_num << std::dec << g_reg_entries[reg].debugserver_regnum; // Encode native byte ordered bytes as hex ascii registers_dict_sp->AddBytesAsHexASCIIString( reg_num.str(), reg_value.value.v_uint8, g_reg_entries[reg].nub_info.size); } } thread_dict_sp->AddItem("registers", registers_dict_sp); } // Add expedited stack memory so stack backtracing doesn't need to read // anything from the // frame pointer chain. StackMemoryMap stack_mmap; ReadStackMemory(pid, tid, stack_mmap); if (!stack_mmap.empty()) { JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array()); for (const auto &stack_memory : stack_mmap) { JSONGenerator::DictionarySP stack_memory_sp( new JSONGenerator::Dictionary()); stack_memory_sp->AddIntegerItem("address", stack_memory.first); stack_memory_sp->AddBytesAsHexASCIIString( "bytes", stack_memory.second.bytes, stack_memory.second.length); memory_array_sp->AddItem(stack_memory_sp); } thread_dict_sp->AddItem("memory", memory_array_sp); } } threads_array_sp->AddItem(thread_dict_sp); } } return threads_array_sp; } rnb_err_t RNBRemote::HandlePacket_jThreadsInfo(const char *p) { JSONGenerator::ObjectSP threads_info_sp; std::ostringstream json; std::ostringstream reply_strm; // If we haven't run the process yet, return an error. if (m_ctx.HasValidProcessID()) { const bool threads_with_valid_stop_info_only = false; JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only); if (threads_info_sp) { std::ostringstream strm; threads_info_sp->Dump(strm); std::string binary_packet = binary_encode_string(strm.str()); if (!binary_packet.empty()) return SendPacket(binary_packet.c_str()); } } return SendPacket("E85"); } rnb_err_t RNBRemote::HandlePacket_jThreadExtendedInfo(const char *p) { nub_process_t pid; std::ostringstream json; // If we haven't run the process yet, return an error. if (!m_ctx.HasValidProcessID()) { return SendPacket("E81"); } pid = m_ctx.ProcessID(); const char thread_extended_info_str[] = {"jThreadExtendedInfo:{"}; if (strncmp(p, thread_extended_info_str, sizeof(thread_extended_info_str) - 1) == 0) { p += strlen(thread_extended_info_str); uint64_t tid = get_integer_value_for_key_name_from_json("thread", p); uint64_t plo_pthread_tsd_base_address_offset = get_integer_value_for_key_name_from_json( "plo_pthread_tsd_base_address_offset", p); uint64_t plo_pthread_tsd_base_offset = get_integer_value_for_key_name_from_json("plo_pthread_tsd_base_offset", p); uint64_t plo_pthread_tsd_entry_size = get_integer_value_for_key_name_from_json("plo_pthread_tsd_entry_size", p); uint64_t dti_qos_class_index = get_integer_value_for_key_name_from_json("dti_qos_class_index", p); // Commented out the two variables below as they are not being used // uint64_t dti_queue_index = // get_integer_value_for_key_name_from_json ("dti_queue_index", p); // uint64_t dti_voucher_index = // get_integer_value_for_key_name_from_json ("dti_voucher_index", p); if (tid != INVALID_NUB_ADDRESS) { nub_addr_t pthread_t_value = DNBGetPThreadT(pid, tid); uint64_t tsd_address = INVALID_NUB_ADDRESS; if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS && plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) { tsd_address = DNBGetTSDAddressForThread( pid, tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); } bool timed_out = false; Genealogy::ThreadActivitySP thread_activity_sp; // If the pthread_t value is invalid, or if we were able to fetch the // thread's TSD base // and got an invalid value back, then we have a thread in early startup // or shutdown and // it's possible that gathering the genealogy information for this thread // go badly. // Ideally fetching this info for a thread in these odd states shouldn't // matter - but // we've seen some problems with these new SPI and threads in edge-casey // states. double genealogy_fetch_time = 0; if (pthread_t_value != INVALID_NUB_ADDRESS && tsd_address != INVALID_NUB_ADDRESS) { DNBTimer timer(false); thread_activity_sp = DNBGetGenealogyInfoForThread(pid, tid, timed_out); genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0; } std::unordered_set process_info_indexes; // an array of the process info #'s seen json << "{"; bool need_to_print_comma = false; if (thread_activity_sp && timed_out == false) { const Genealogy::Activity *activity = &thread_activity_sp->current_activity; bool need_vouchers_comma_sep = false; json << "\"activity_query_timed_out\":false,"; if (genealogy_fetch_time != 0) { // If we append the floating point value with << we'll get it in // scientific // notation. char floating_point_ascii_buffer[64]; floating_point_ascii_buffer[0] = '\0'; snprintf(floating_point_ascii_buffer, sizeof(floating_point_ascii_buffer), "%f", genealogy_fetch_time); if (strlen(floating_point_ascii_buffer) > 0) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"activity_query_duration\":" << floating_point_ascii_buffer; } } if (activity->activity_id != 0) { if (need_to_print_comma) json << ","; need_to_print_comma = true; need_vouchers_comma_sep = true; json << "\"activity\":{"; json << "\"start\":" << activity->activity_start << ","; json << "\"id\":" << activity->activity_id << ","; json << "\"parent_id\":" << activity->parent_id << ","; json << "\"name\":\"" << json_string_quote_metachars(activity->activity_name) << "\","; json << "\"reason\":\"" << json_string_quote_metachars(activity->reason) << "\""; json << "}"; } if (thread_activity_sp->messages.size() > 0) { need_to_print_comma = true; if (need_vouchers_comma_sep) json << ","; need_vouchers_comma_sep = true; json << "\"trace_messages\":["; bool printed_one_message = false; for (auto iter = thread_activity_sp->messages.begin(); iter != thread_activity_sp->messages.end(); ++iter) { if (printed_one_message) json << ","; else printed_one_message = true; json << "{"; json << "\"timestamp\":" << iter->timestamp << ","; json << "\"activity_id\":" << iter->activity_id << ","; json << "\"trace_id\":" << iter->trace_id << ","; json << "\"thread\":" << iter->thread << ","; json << "\"type\":" << (int)iter->type << ","; json << "\"process_info_index\":" << iter->process_info_index << ","; process_info_indexes.insert(iter->process_info_index); json << "\"message\":\"" << json_string_quote_metachars(iter->message) << "\""; json << "}"; } json << "]"; } if (thread_activity_sp->breadcrumbs.size() == 1) { need_to_print_comma = true; if (need_vouchers_comma_sep) json << ","; need_vouchers_comma_sep = true; json << "\"breadcrumb\":{"; for (auto iter = thread_activity_sp->breadcrumbs.begin(); iter != thread_activity_sp->breadcrumbs.end(); ++iter) { json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ","; json << "\"activity_id\":" << iter->activity_id << ","; json << "\"timestamp\":" << iter->timestamp << ","; json << "\"name\":\"" << json_string_quote_metachars(iter->name) << "\""; } json << "}"; } if (process_info_indexes.size() > 0) { need_to_print_comma = true; if (need_vouchers_comma_sep) json << ","; need_vouchers_comma_sep = true; bool printed_one_process_info = false; for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter) { if (printed_one_process_info) json << ","; Genealogy::ProcessExecutableInfoSP image_info_sp; uint32_t idx = *iter; image_info_sp = DNBGetGenealogyImageInfo(pid, idx); if (image_info_sp) { if (!printed_one_process_info) { json << "\"process_infos\":["; printed_one_process_info = true; } json << "{"; char uuid_buf[37]; uuid_unparse_upper(image_info_sp->image_uuid, uuid_buf); json << "\"process_info_index\":" << idx << ","; json << "\"image_path\":\"" << json_string_quote_metachars(image_info_sp->image_path) << "\","; json << "\"image_uuid\":\"" << uuid_buf << "\""; json << "}"; } } if (printed_one_process_info) json << "]"; } } else { if (timed_out) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"activity_query_timed_out\":true"; if (genealogy_fetch_time != 0) { // If we append the floating point value with << we'll get it in // scientific // notation. char floating_point_ascii_buffer[64]; floating_point_ascii_buffer[0] = '\0'; snprintf(floating_point_ascii_buffer, sizeof(floating_point_ascii_buffer), "%f", genealogy_fetch_time); if (strlen(floating_point_ascii_buffer) > 0) { json << ","; json << "\"activity_query_duration\":" << floating_point_ascii_buffer; } } } } if (tsd_address != INVALID_NUB_ADDRESS) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"tsd_address\":" << tsd_address; if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) { ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread( pid, tid, tsd_address, dti_qos_class_index); if (requested_qos.IsValid()) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"requested_qos\":{"; json << "\"enum_value\":" << requested_qos.enum_value << ","; json << "\"constant_name\":\"" << json_string_quote_metachars(requested_qos.constant_name) << "\","; json << "\"printable_name\":\"" << json_string_quote_metachars(requested_qos.printable_name) << "\""; json << "}"; } } } if (pthread_t_value != INVALID_NUB_ADDRESS) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"pthread_t\":" << pthread_t_value; } nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT(pid, tid); if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) { if (need_to_print_comma) json << ","; need_to_print_comma = true; json << "\"dispatch_queue_t\":" << dispatch_queue_t_value; } json << "}"; std::string json_quoted = binary_encode_string(json.str()); return SendPacket(json_quoted); } } return SendPacket("OK"); } // This packet may be called in one of three ways: // // jGetLoadedDynamicLibrariesInfos:{"image_count":40,"image_list_address":4295244704} // Look for an array of the old dyld_all_image_infos style of binary infos // at the image_list_address. // This an array of {void* load_addr, void* mod_date, void* pathname} // // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true} // Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to // get a list of all the // libraries loaded // // jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]} // Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to // get the information // about the libraries loaded at these addresses. // rnb_err_t RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p) { nub_process_t pid; // If we haven't run the process yet, return an error. if (!m_ctx.HasValidProcessID()) { return SendPacket("E83"); } pid = m_ctx.ProcessID(); const char get_loaded_dynamic_libraries_infos_str[] = { "jGetLoadedDynamicLibrariesInfos:{"}; if (strncmp(p, get_loaded_dynamic_libraries_infos_str, sizeof(get_loaded_dynamic_libraries_infos_str) - 1) == 0) { p += strlen(get_loaded_dynamic_libraries_infos_str); JSONGenerator::ObjectSP json_sp; std::vector macho_addresses; bool fetch_all_solibs = false; if (get_boolean_value_for_key_name_from_json("fetch_all_solibs", p, fetch_all_solibs) && fetch_all_solibs) { json_sp = DNBGetAllLoadedLibrariesInfos(pid); } else if (get_array_of_ints_value_for_key_name_from_json( "solib_addresses", p, macho_addresses)) { json_sp = DNBGetLibrariesInfoForAddresses(pid, macho_addresses); } else { nub_addr_t image_list_address = get_integer_value_for_key_name_from_json("image_list_address", p); nub_addr_t image_count = get_integer_value_for_key_name_from_json("image_count", p); if (image_list_address != INVALID_NUB_ADDRESS && image_count != INVALID_NUB_ADDRESS) { json_sp = DNBGetLoadedDynamicLibrariesInfos(pid, image_list_address, image_count); } } if (json_sp.get()) { std::ostringstream json_str; json_sp->Dump(json_str); if (json_str.str().size() > 0) { std::string json_str_quoted = binary_encode_string(json_str.str()); return SendPacket(json_str_quoted.c_str()); } else { SendPacket("E84"); } } } return SendPacket("OK"); } // This packet does not currently take any arguments. So the behavior is // jGetSharedCacheInfo:{} // send information about the inferior's shared cache // jGetSharedCacheInfo: // send "OK" to indicate that this packet is supported rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) { nub_process_t pid; // If we haven't run the process yet, return an error. if (!m_ctx.HasValidProcessID()) { return SendPacket("E85"); } pid = m_ctx.ProcessID(); const char get_shared_cache_info_str[] = {"jGetSharedCacheInfo:{"}; if (strncmp(p, get_shared_cache_info_str, sizeof(get_shared_cache_info_str) - 1) == 0) { JSONGenerator::ObjectSP json_sp = DNBGetSharedCacheInfo(pid); if (json_sp.get()) { std::ostringstream json_str; json_sp->Dump(json_str); if (json_str.str().size() > 0) { std::string json_str_quoted = binary_encode_string(json_str.str()); return SendPacket(json_str_quoted.c_str()); } else { SendPacket("E86"); } } } return SendPacket("OK"); } static bool MachHeaderIsMainExecutable(nub_process_t pid, uint32_t addr_size, nub_addr_t mach_header_addr, mach_header &mh) { DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, " "addr_size = %u, mach_header_addr = " "0x%16.16llx)", pid, addr_size, mach_header_addr); const nub_size_t bytes_read = DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh); if (bytes_read == sizeof(mh)) { DNBLogThreadedIf( LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = " "%u, mach_header_addr = 0x%16.16llx): mh = {\n magic = " "0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = " "%u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = " "0x%8.8x }", pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype, mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags); if ((addr_size == 4 && mh.magic == MH_MAGIC) || (addr_size == 8 && mh.magic == MH_MAGIC_64)) { if (mh.filetype == MH_EXECUTE) { DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = " "%u, addr_size = %u, mach_header_addr = " "0x%16.16llx) -> this is the " "executable!!!", pid, addr_size, mach_header_addr); return true; } } } return false; } static nub_addr_t GetMachHeaderForMainExecutable(const nub_process_t pid, const uint32_t addr_size, mach_header &mh) { struct AllImageInfos { uint32_t version; uint32_t dylib_info_count; uint64_t dylib_info_addr; }; uint64_t mach_header_addr = 0; const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress(pid); uint8_t bytes[256]; nub_size_t bytes_read = 0; DNBDataRef data(bytes, sizeof(bytes), false); DNBDataRef::offset_t offset = 0; data.SetPointerSize(addr_size); //---------------------------------------------------------------------- // When we are sitting at __dyld_start, the kernel has placed the // address of the mach header of the main executable on the stack. If we // read the SP and dereference a pointer, we might find the mach header // for the executable. We also just make sure there is only 1 thread // since if we are at __dyld_start we shouldn't have multiple threads. //---------------------------------------------------------------------- if (DNBProcessGetNumThreads(pid) == 1) { nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0); if (tid != INVALID_NUB_THREAD) { DNBRegisterValue sp_value; if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_SP, &sp_value)) { uint64_t sp = addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32; bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes); if (bytes_read == addr_size) { offset = 0; mach_header_addr = data.GetPointer(&offset); if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) return mach_header_addr; } } } } //---------------------------------------------------------------------- // Check the dyld_all_image_info structure for a list of mach header // since it is a very easy thing to check //---------------------------------------------------------------------- if (shlib_addr != INVALID_NUB_ADDRESS) { bytes_read = DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes); if (bytes_read > 0) { AllImageInfos aii; offset = 0; aii.version = data.Get32(&offset); aii.dylib_info_count = data.Get32(&offset); if (aii.dylib_info_count > 0) { aii.dylib_info_addr = data.GetPointer(&offset); if (aii.dylib_info_addr != 0) { const size_t image_info_byte_size = 3 * addr_size; for (uint32_t i = 0; i < aii.dylib_info_count; ++i) { bytes_read = DNBProcessMemoryRead(pid, aii.dylib_info_addr + i * image_info_byte_size, image_info_byte_size, bytes); if (bytes_read != image_info_byte_size) break; offset = 0; mach_header_addr = data.GetPointer(&offset); if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) return mach_header_addr; } } } } } //---------------------------------------------------------------------- // We failed to find the executable's mach header from the all image // infos and by dereferencing the stack pointer. Now we fall back to // enumerating the memory regions and looking for regions that are // executable. //---------------------------------------------------------------------- DNBRegionInfo region_info; mach_header_addr = 0; while (DNBProcessMemoryRegionInfo(pid, mach_header_addr, ®ion_info)) { if (region_info.size == 0) break; if (region_info.permissions & eMemoryPermissionsExecutable) { DNBLogThreadedIf( LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: " "checking region for executable mach header", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-'); if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) return mach_header_addr; } else { DNBLogThreadedIf( LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-'); } // Set the address to the next mapped region mach_header_addr = region_info.addr + region_info.size; } bzero(&mh, sizeof(mh)); return INVALID_NUB_ADDRESS; } rnb_err_t RNBRemote::HandlePacket_qSymbol(const char *command) { const char *p = command; p += strlen("qSymbol:"); const char *sep = strchr(p, ':'); std::string symbol_name; std::string symbol_value_str; // Extract the symbol value if there is one if (sep > p) symbol_value_str.assign(p, sep - p); p = sep + 1; if (*p) { // We have a symbol name symbol_name = decode_hex_ascii_string(p); if (!symbol_value_str.empty()) { nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16); if (symbol_name == "dispatch_queue_offsets") m_dispatch_queue_offsets_addr = symbol_value; } ++m_qSymbol_index; } else { // No symbol name, set our symbol index to zero so we can // read any symbols that we need m_qSymbol_index = 0; } symbol_name.clear(); if (m_qSymbol_index == 0) { if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS) symbol_name = "dispatch_queue_offsets"; else ++m_qSymbol_index; } // // Lookup next symbol when we have one... // if (m_qSymbol_index == 1) // { // } if (symbol_name.empty()) { // Done with symbol lookups return SendPacket("OK"); } else { std::ostringstream reply; reply << "qSymbol:"; for (size_t i = 0; i < symbol_name.size(); ++i) reply << RAWHEX8(symbol_name[i]); return SendPacket(reply.str().c_str()); } } // Note that all numeric values returned by qProcessInfo are hex encoded, // including the pid and the cpu type. rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) { nub_process_t pid; std::ostringstream rep; // If we haven't run the process yet, return an error. if (!m_ctx.HasValidProcessID()) return SendPacket("E68"); pid = m_ctx.ProcessID(); rep << "pid:" << std::hex << pid << ';'; int procpid_mib[4]; procpid_mib[0] = CTL_KERN; procpid_mib[1] = KERN_PROC; procpid_mib[2] = KERN_PROC_PID; procpid_mib[3] = pid; struct kinfo_proc proc_kinfo; size_t proc_kinfo_size = sizeof(struct kinfo_proc); if (::sysctl(procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) { if (proc_kinfo_size > 0) { rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';'; rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid << ';'; rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid << ';'; rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid << ';'; if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) rep << "effective-gid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';'; } } cpu_type_t cputype = DNBProcessGetCPUType(pid); if (cputype == 0) { DNBLog("Unable to get the process cpu_type, making a best guess."); cputype = best_guess_cpu_type(); } uint32_t addr_size = 0; if (cputype != 0) { rep << "cputype:" << std::hex << cputype << ";"; if (cputype & CPU_ARCH_ABI64) addr_size = 8; else addr_size = 4; } bool host_cpu_is_64bit = false; uint32_t is64bit_capable; size_t is64bit_capable_len = sizeof(is64bit_capable); if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) host_cpu_is_64bit = is64bit_capable != 0; uint32_t cpusubtype; size_t cpusubtype_len = sizeof(cpusubtype); if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &cpusubtype_len, NULL, 0) == 0) { // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu // subtype // for i386... if (host_cpu_is_64bit) { if (cputype == CPU_TYPE_X86) { cpusubtype = 3; // CPU_SUBTYPE_I386_ALL } else if (cputype == CPU_TYPE_ARM) { // We can query a process' cputype but we cannot query a process' // cpusubtype. // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit // process) and we // need to override the host cpusubtype (which is in the // CPU_SUBTYPE_ARM64 subtype namespace) // with a reasonable CPU_SUBTYPE_ARMV7 subtype. cpusubtype = 11; // CPU_SUBTYPE_ARM_V7S } } rep << "cpusubtype:" << std::hex << cpusubtype << ';'; } bool os_handled = false; if (addr_size > 0) { rep << "ptrsize:" << std::dec << addr_size << ';'; #if (defined(__x86_64__) || defined(__i386__)) // Try and get the OS type by looking at the load commands in the main // executable and looking for a LC_VERSION_MIN load command. This is the // most reliable way to determine the "ostype" value when on desktop. mach_header mh; nub_addr_t exe_mach_header_addr = GetMachHeaderForMainExecutable(pid, addr_size, mh); if (exe_mach_header_addr != INVALID_NUB_ADDRESS) { uint64_t load_command_addr = exe_mach_header_addr + ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header)); load_command lc; for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i) { const nub_size_t bytes_read = DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc); uint32_t raw_cmd = lc.cmd & ~LC_REQ_DYLD; if (bytes_read != sizeof(lc)) break; switch (raw_cmd) { case LC_VERSION_MIN_IPHONEOS: os_handled = true; rep << "ostype:ios;"; DNBLogThreadedIf(LOG_RNB_PROC, "LC_VERSION_MIN_IPHONEOS -> 'ostype:ios;'"); break; case LC_VERSION_MIN_MACOSX: os_handled = true; rep << "ostype:macosx;"; DNBLogThreadedIf(LOG_RNB_PROC, "LC_VERSION_MIN_MACOSX -> 'ostype:macosx;'"); break; #if defined(LC_VERSION_MIN_TVOS) case LC_VERSION_MIN_TVOS: os_handled = true; rep << "ostype:tvos;"; DNBLogThreadedIf(LOG_RNB_PROC, "LC_VERSION_MIN_TVOS -> 'ostype:tvos;'"); break; #endif #if defined(LC_VERSION_MIN_WATCHOS) case LC_VERSION_MIN_WATCHOS: os_handled = true; rep << "ostype:watchos;"; DNBLogThreadedIf(LOG_RNB_PROC, "LC_VERSION_MIN_WATCHOS -> 'ostype:watchos;'"); break; #endif default: break; } load_command_addr = load_command_addr + lc.cmdsize; } } #endif } // If we weren't able to find the OS in a LC_VERSION_MIN load command, try // to set it correctly by using the cpu type and other tricks if (!os_handled) { // The OS in the triple should be "ios" or "macosx" which doesn't match our // "Darwin" which gets returned from "kern.ostype", so we need to hardcode // this for now. if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 rep << "ostype:tvos;"; #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 rep << "ostype:watchos;"; #else rep << "ostype:ios;"; #endif } else { bool is_ios_simulator = false; if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64) { // Check for iOS simulator binaries by getting the process argument // and environment and checking for SIMULATOR_UDID in the environment int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, (int)pid}; uint8_t arg_data[8192]; size_t arg_data_size = sizeof(arg_data); if (::sysctl(proc_args_mib, 3, arg_data, &arg_data_size, NULL, 0) == 0) { DNBDataRef data(arg_data, arg_data_size, false); DNBDataRef::offset_t offset = 0; uint32_t argc = data.Get32(&offset); const char *cstr; cstr = data.GetCStr(&offset); if (cstr) { // Skip NULLs while (1) { const char *p = data.PeekCStr(offset); if ((p == NULL) || (*p != '\0')) break; ++offset; } // Now skip all arguments for (uint32_t i = 0; i < argc; ++i) { data.GetCStr(&offset); } // Now iterate across all environment variables while ((cstr = data.GetCStr(&offset))) { if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) { is_ios_simulator = true; break; } if (cstr[0] == '\0') break; } } } } if (is_ios_simulator) { #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 rep << "ostype:tvos;"; #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 rep << "ostype:watchos;"; #else rep << "ostype:ios;"; #endif } else { rep << "ostype:macosx;"; } } } rep << "vendor:apple;"; #if defined(__LITTLE_ENDIAN__) rep << "endian:little;"; #elif defined(__BIG_ENDIAN__) rep << "endian:big;"; #elif defined(__PDP_ENDIAN__) rep << "endian:pdp;"; #endif if (addr_size == 0) { #if (defined(__x86_64__) || defined(__i386__)) && defined(x86_THREAD_STATE) nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid); kern_return_t kr; x86_thread_state_t gp_regs; mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT; kr = thread_get_state(static_cast(thread), x86_THREAD_STATE, (thread_state_t)&gp_regs, &gp_count); if (kr == KERN_SUCCESS) { if (gp_regs.tsh.flavor == x86_THREAD_STATE64) rep << "ptrsize:8;"; else rep << "ptrsize:4;"; } #elif defined(__arm__) rep << "ptrsize:4;"; #elif (defined(__arm64__) || defined(__aarch64__)) && \ defined(ARM_UNIFIED_THREAD_STATE) nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid); kern_return_t kr; arm_unified_thread_state_t gp_regs; mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT; kr = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&gp_regs, &gp_count); if (kr == KERN_SUCCESS) { if (gp_regs.ash.flavor == ARM_THREAD_STATE64) rep << "ptrsize:8;"; else rep << "ptrsize:4;"; } #endif } return SendPacket(rep.str()); } const RNBRemote::DispatchQueueOffsets *RNBRemote::GetDispatchQueueOffsets() { if (!m_dispatch_queue_offsets.IsValid() && m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS && m_ctx.HasValidProcessID()) { nub_process_t pid = m_ctx.ProcessID(); nub_size_t bytes_read = DNBProcessMemoryRead( pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets), &m_dispatch_queue_offsets); if (bytes_read != sizeof(m_dispatch_queue_offsets)) m_dispatch_queue_offsets.Clear(); } if (m_dispatch_queue_offsets.IsValid()) return &m_dispatch_queue_offsets; else return nullptr; } void RNBRemote::EnableCompressionNextSendPacket(compression_types type) { m_compression_mode = type; m_enable_compression_next_send_packet = true; } compression_types RNBRemote::GetCompressionType() { // The first packet we send back to the debugger after a QEnableCompression // request // should be uncompressed -- so we can indicate whether the compression was // enabled // or not via OK / Enn returns. After that, all packets sent will be using // the // compression protocol. if (m_enable_compression_next_send_packet) { // One time, we send back "None" as our compression type m_enable_compression_next_send_packet = false; return compression_types::none; } return m_compression_mode; } Index: vendor/lldb/dist/unittests/Host/SocketAddressTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Host/SocketAddressTest.cpp (revision 317454) +++ vendor/lldb/dist/unittests/Host/SocketAddressTest.cpp (revision 317455) @@ -1,70 +1,91 @@ //===-- SocketAddressTest.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "lldb/Host/SocketAddress.h" namespace { -class SocketAddressTest : public ::testing::Test {}; -} +class SocketAddressTest : public testing::Test { +public: + static void SetUpTestCase() { +#ifdef _MSC_VER + WSADATA data; + ASSERT_EQ(0, WSAStartup(MAKEWORD(2, 2), &data)); +#endif + } + static void TearDownTestCase() { +#ifdef _MSC_VER + ASSERT_EQ(0, WSACleanup()); +#endif + } +}; +} // namespace using namespace lldb_private; TEST_F(SocketAddressTest, Set) { SocketAddress sa; ASSERT_TRUE(sa.SetToLocalhost(AF_INET, 1138)); ASSERT_STREQ("127.0.0.1", sa.GetIPAddress().c_str()); ASSERT_EQ(1138, sa.GetPort()); ASSERT_TRUE(sa.SetToAnyAddress(AF_INET, 0)); ASSERT_STREQ("0.0.0.0", sa.GetIPAddress().c_str()); ASSERT_EQ(0, sa.GetPort()); ASSERT_TRUE(sa.SetToLocalhost(AF_INET6, 1139)); ASSERT_TRUE(sa.GetIPAddress() == "::1" || sa.GetIPAddress() == "0:0:0:0:0:0:0:1") << "Address was: " << sa.GetIPAddress(); ASSERT_EQ(1139, sa.GetPort()); +} + +TEST_F(SocketAddressTest, GetAddressInfo) { + auto addr = SocketAddress::GetAddressInfo("127.0.0.1", nullptr, AF_UNSPEC, + SOCK_STREAM, IPPROTO_TCP); + ASSERT_EQ(1u, addr.size()); + EXPECT_EQ(AF_INET, addr[0].GetFamily()); + EXPECT_EQ("127.0.0.1", addr[0].GetIPAddress()); } #ifdef _WIN32 // we need to test our inet_ntop implementation for Windows XP const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); TEST_F(SocketAddressTest, inet_ntop) { const uint8_t address4[4] = {255, 0, 1, 100}; const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 255, 0}; char buffer[INET6_ADDRSTRLEN]; memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop(AF_INET6, address6, buffer, sizeof(buffer))); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop(AF_INET6, address6, buffer, 31)); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ(nullptr, inet_ntop(AF_INET6, address6, buffer, 0)); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ(nullptr, inet_ntop(AF_INET6, address6, buffer, 30)); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ("255.0.1.100", inet_ntop(AF_INET, address4, buffer, sizeof(buffer))); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ("255.0.1.100", inet_ntop(AF_INET, address4, buffer, 12)); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ(nullptr, inet_ntop(AF_INET, address4, buffer, 0)); memset(buffer, 'x', sizeof(buffer)); EXPECT_STREQ(nullptr, inet_ntop(AF_INET, address4, buffer, 11)); } #endif Index: vendor/lldb/dist/www/build.html =================================================================== --- vendor/lldb/dist/www/build.html (revision 317454) +++ vendor/lldb/dist/www/build.html (revision 317455) @@ -1,515 +1,508 @@ Building LLDB
The LLDB Debugger

Continuous Integration

The following LLVM buildbots build and test LLDB trunk:

Building LLDB

Building LLDB on Windows

Required Dependencies

  • Visual Studio 2015 or greater
  • Windows SDK 8.0 or higher. In general it is best to use the latest available version.
  • Python 3.5 or higher or higher. Earlier versions of Python can be made to work by compiling your own distribution from source, but this workflow is unsupported and you are own your own.
  • Ninja build tool (strongly recommended)
  • GnuWin32
  • SWIG for Windows (version 3+)

Optional Dependencies

  • Python Tools for Visual Studio. If you plan to debug test failures or even write new tests at all, PTVS is an indispensable debugging extension to VS that enables full editing and debugging support for Python (including mixed native/managed debugging)

Preliminaries

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

  1. Install Visual Studio and the Windows SDK.

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

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

Building LLDB

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

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

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

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

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

  • LLDB_TEST_DEBUG_TEST_CRASHES (Default=0): If set to 1, will cause Windows to generate a crash dialog whenever lldb.exe or the python extension module crashes while running the test suite. If set to 0, LLDB will silently crash. Setting to 1 allows a developer to attach a JIT debugger at the time of a crash, rather than having to reproduce a failure or use a crash dump.
  • PYTHON_HOME (Required): Path to the folder where the Python distribution is installed. For example, C:\Python35
  • LLDB_RELOCATABLE_PYTHON (Default=0): When this is 0, LLDB will bind statically to the location specified in the PYTHON_HOME CMake variable, ignoring any value of PYTHONHOME set in the environment. This is most useful for developers who simply want to run LLDB after they build it. If you wish to move a build of LLDB to a different machine where Python will be in a different location, setting LLDB_RELOCATABLE_PYTHON to 1 will cause Python to use its default mechanism for finding the python installation at runtime (looking for installed Pythons, or using the PYTHONHOME environment variable if it is specified).
  • LLDB_TEST_COMPILER: The test suite needs to be able to find a copy of clang.exe that it can use to compile inferior programs. Note that MSVC is not supported here, it must be a path to a clang executable. Note that using a release clang.exe is strongly recommended here, as it will make the test suite run much faster. This can be a path to any recent clang.exe, including one you built yourself.
Sample command line:
cmake -G Ninja -DLLDB_TEST_DEBUG_TEST_CRASHES=1 -DPYTHON_HOME=C:\Python35 -DLLDB_TEST_COMPILER=d:\src\llvmbuild\ninja_release\bin\clang.exe ..\..\llvm

Working with both Ninja and MSVC

Compiling with ninja is both faster and simpler than compiling with MSVC, but chances are you still want to debug LLDB with MSVC (at least until we can debug LLDB on Windows with LLDB!). One solution to this is to run cmake twice and generate the output into two different folders. One for compiling (the ninja folder), and one for editing / browsing / debugging (the MSVC folder).

To do this, simply run `cmake -G Ninja <arguments>` from one folder, and `cmake -G "Visual Studio 14 2015" <arguments>` in another folder. Then you can open the .sln file in Visual Studio, set lldb as the startup project, and use F5 to run it. You need only edit the project settings to set the executable and the working directory to point to binaries inside of the ninja tree.

Building LLDB on Mac OS X

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

Preliminaries

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

Building LLDB

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

Building LLDB on Linux, FreeBSD and NetBSD

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

Preliminaries

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

Supported compilers for building LLDB on Linux include:

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

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

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

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

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

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

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

On a Debian or Ubuntu system one might run:

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

or

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

On FreeBSD one might run:

> pkg install swig python

On NetBSD one might run:

> pkgin install swig python27 cmake ninja-build

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

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

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


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

Building LLDB

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

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

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

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

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

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

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

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

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

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

To build with CMake

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

  • Ninja
  • Unix Makefiles

Using CMake + Ninja

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

> cmake ../llvm -G Ninja
> ninja lldb
> ninja check-lldb

If you want to debug the lldb that you're building -- that is, build it with debug info enabled -- pass two additional arguments to cmake before running ninja:

> cmake ../llvm -G Ninja -DLLDB_EXPORT_ALL_SYMBOLS=1 -DCMAKE_BUILD_TYPE=Debug

Using CMake + Unix Makefiles

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

> cmake ..
> make
> make check-lldb

Building API reference documentation

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

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

Additional Notes

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

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

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

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

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

> python -c 'import lldb'

Cross-compiling LLDB

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

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

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

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

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

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

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

- -

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

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

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

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

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

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

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

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

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

Example 2: Cross-compiling for Android on Linux

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

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

- from inside the unzipped NDK. Toolchains for other architectures can be produced in - a similar manner. + The NDK also contains a cmake toolchain file, which makes + configuring the build much simpler. The compiler, include and + library paths will be configured by the toolchain file and all you + need to do is to select the architecture (ANDROID_ABI) and + platform level (ANDROID_PLATFORM, should be at least 21). You will + also need to set ANDROID_ALLOW_UNDEFINED_SYMBOLS=On, as the + toolchain file defaults to "no undefined symbols in shared + libraries", which is not compatible with some llvm libraries. The + first version of NDK which supports this approach is r14.

-

- For Android we provide a Android.cmake script which sets a lot of the required - options automatically. A cmake build can therefore be prepared with the following parameters: + For example, the following arguments are sufficient to configure + an android arm64 build:

- -DCMAKE_TOOLCHAIN_FILE=cmake/platforms/Android.cmake \
- -DANDROID_TOOLCHAIN_DIR=$HOME/Toolchains/x86-android-toolchain \
- -DANDROID_ABI=x86 \
- -DLLVM_HOST_TRIPLE=i386-unknown-linux-android \
- -DLLVM_TABLEGEN=<path-to-host>/bin/llvm-tblgen \
- -DCLANG_TABLEGEN=<path-to-host>/bin/clang-tblgen + -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
+ -DANDROID_ABI=arm64-v8a \
+ -DANDROID_PLATFORM=android-21 \
+ -DANDROID_ALLOW_UNDEFINED_SYMBOLS=On \
+ -DLLVM_HOST_TRIPLE=aarch64-unknown-linux-android \
+ -DCROSS_TOOLCHAIN_FLAGS_NATIVE='-DCMAKE_C_COMPILER=cc;-DCMAKE_CXX_COMPILER=c++'

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