Index: vendor/lldb/dist/cmake/modules/LLDBConfig.cmake =================================================================== --- vendor/lldb/dist/cmake/modules/LLDBConfig.cmake (revision 321193) +++ vendor/lldb/dist/cmake/modules/LLDBConfig.cmake (revision 321194) @@ -1,405 +1,415 @@ 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() +set(LLDB_DEFAULT_DISABLE_PYTHON 0) +set(LLDB_DEFAULT_DISABLE_CURSES 0) + 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) +elseif(IOS) + set(LLDB_DEFAULT_DISABLE_PYTHON 1) endif() +if(IOS) + add_definitions(-DNO_XPC_SERVICES) +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 (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) +if (APPLE) + if(NOT IOS) + find_library(CARBON_LIBRARY Carbon) + find_library(CORE_SERVICES_LIBRARY CoreServices) + find_library(DEBUG_SYMBOLS_LIBRARY DebugSymbols PATHS "/System/Library/PrivateFrameworks") + endif() 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}) + list(APPEND system_libs xml2 + ${CURSES_LIBRARIES} + ${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() # 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(LLDBGenerateConfig) Index: vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake =================================================================== --- vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake (revision 321193) +++ vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake (revision 321194) @@ -1,136 +1,135 @@ # If we are not building as a part of LLVM, build LLDB as an # standalone project, using LLVM as an external library: if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) project(lldb) - cmake_minimum_required(VERSION 2.8.12.2) if (POLICY CMP0022) cmake_policy(SET CMP0022 NEW) # automatic when 2.8.12 is required endif() option(LLVM_INSTALL_TOOLCHAIN_ONLY "Only include toolchain files in the 'install' target." OFF) # Rely on llvm-config. set(CONFIG_OUTPUT) find_program(LLVM_CONFIG "llvm-config") if(LLVM_CONFIG) message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}") set(CONFIG_COMMAND ${LLVM_CONFIG} "--assertion-mode" "--bindir" "--libdir" "--includedir" "--prefix" "--src-root" "--cmakedir") execute_process( COMMAND ${CONFIG_COMMAND} RESULT_VARIABLE HAD_ERROR OUTPUT_VARIABLE CONFIG_OUTPUT ) if(NOT HAD_ERROR) string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT}) else() string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") message(STATUS "${CONFIG_COMMAND_STR}") message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") endif() else() message(FATAL_ERROR "llvm-config not found -- ${LLVM_CONFIG}") endif() list(GET CONFIG_OUTPUT 0 ENABLE_ASSERTIONS) list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR) list(GET CONFIG_OUTPUT 2 LIBRARY_DIR) list(GET CONFIG_OUTPUT 3 INCLUDE_DIR) list(GET CONFIG_OUTPUT 4 LLVM_OBJ_ROOT) list(GET CONFIG_OUTPUT 5 MAIN_SRC_DIR) list(GET CONFIG_OUTPUT 6 LLVM_CMAKE_PATH) if(NOT MSVC_IDE) set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS} CACHE BOOL "Enable assertions") # Assertions should follow llvm-config's. mark_as_advanced(LLVM_ENABLE_ASSERTIONS) endif() set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin") set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib") set(LLVM_MAIN_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") set(LLVM_DIR ${LLVM_OBJ_ROOT}/cmake/modules/CMakeFiles CACHE PATH "Path to LLVM build tree CMake files") set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) set(LLVMCONFIG_FILE "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") if(EXISTS ${LLVMCONFIG_FILE}) file(TO_CMAKE_PATH "${LLVM_CMAKE_PATH}" LLVM_CMAKE_PATH) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") include(${LLVMCONFIG_FILE}) else() message(FATAL_ERROR "Not found: ${LLVMCONFIG_FILE}") endif() # They are used as destination of target generators. set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) if(WIN32 OR CYGWIN) # DLL platform -- put DLLs into bin. set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) else() set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) endif() include(AddLLVM) include(HandleLLVMOptions) include(CheckAtomic) if (PYTHON_EXECUTABLE STREQUAL "") set(Python_ADDITIONAL_VERSIONS 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) include(FindPythonInterp) if( NOT PYTHONINTERP_FOUND ) message(FATAL_ERROR "Unable to find Python interpreter, required for builds and testing. Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") endif() else() message("-- Found PythonInterp: ${PYTHON_EXECUTABLE}") endif() # Import CMake library targets from LLVM and Clang. include("${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm/LLVMConfig.cmake") # cmake/clang/ClangConfig.cmake is not created when LLVM and Cland are built together. if (EXISTS "${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/clang/ClangConfig.cmake") include("${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/clang/ClangConfig.cmake") endif() set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") set(LLVM_BINARY_DIR ${CMAKE_BINARY_DIR}) set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}") # Next three include directories are needed when llvm-config is located in build directory. # LLVM and Cland are assumed to be built together if (EXISTS "${LLVM_OBJ_ROOT}/include") include_directories("${LLVM_OBJ_ROOT}/include") endif() if (EXISTS "${LLVM_MAIN_SRC_DIR}/tools/clang/include") include_directories("${LLVM_MAIN_SRC_DIR}/tools/clang/include") endif() if (EXISTS "${LLVM_OBJ_ROOT}/tools/clang/include") include_directories("${LLVM_OBJ_ROOT}/tools/clang/include") endif() link_directories("${LLVM_LIBRARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) set(LLDB_BUILT_STANDALONE 1) endif() Index: vendor/lldb/dist/include/lldb/Host/MainLoop.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/MainLoop.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Host/MainLoop.h (revision 321194) @@ -1,112 +1,112 @@ //===-- MainLoop.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef lldb_Host_MainLoop_h_ #define lldb_Host_MainLoop_h_ #include "lldb/Host/Config.h" #include "lldb/Host/MainLoopBase.h" - #include "llvm/ADT/DenseMap.h" +#include #if !HAVE_PPOLL && !HAVE_SYS_EVENT_H #define SIGNAL_POLLING_UNSUPPORTED 1 #endif namespace lldb_private { // Implementation of the MainLoopBase class. It can monitor file descriptors for // readability using ppoll, kqueue, poll or WSAPoll. On Windows it only supports // polling sockets, and will not work on generic file handles or pipes. On // systems without kqueue or ppoll handling singnals is not supported. In // addition to the common base, this class provides the ability to invoke a // given handler when a signal is received. // // Since this class is primarily intended to be used for single-threaded // processing, it does not attempt to perform any internal synchronisation and // any concurrent accesses must be protected externally. However, it is // perfectly legitimate to have more than one instance of this class running on // separate threads, or even a single thread (with some limitations on signal // monitoring). // TODO: Add locking if this class is to be used in a multi-threaded context. class MainLoop : public MainLoopBase { private: class SignalHandle; public: typedef std::unique_ptr SignalHandleUP; MainLoop(); ~MainLoop() override; ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Status &error) override; // Listening for signals from multiple MainLoop instances is perfectly safe as // long as they don't try to listen for the same signal. The callback function // is invoked when the control returns to the Run() function, not when the // hander is executed. This mean that you can treat the callback as a normal // function and perform things which would not be safe in a signal handler. // However, since the callback is not invoked synchronously, you cannot use // this mechanism to handle SIGSEGV and the like. SignalHandleUP RegisterSignal(int signo, const Callback &callback, Status &error); Status Run() override; // This should only be performed from a callback. Do not attempt to terminate // the processing from another thread. // TODO: Add synchronization if we want to be terminated from another thread. void RequestTermination() override { m_terminate_request = true; } protected: void UnregisterReadObject(IOObject::WaitableHandle handle) override; void UnregisterSignal(int signo); private: void ProcessReadObject(IOObject::WaitableHandle handle); void ProcessSignal(int signo); class SignalHandle { public: ~SignalHandle() { m_mainloop.UnregisterSignal(m_signo); } private: SignalHandle(MainLoop &mainloop, int signo) : m_mainloop(mainloop), m_signo(signo) {} MainLoop &m_mainloop; int m_signo; friend class MainLoop; DISALLOW_COPY_AND_ASSIGN(SignalHandle); }; struct SignalInfo { Callback callback; #if HAVE_SIGACTION struct sigaction old_action; #endif bool was_blocked : 1; }; class RunImpl; llvm::DenseMap m_read_fds; llvm::DenseMap m_signals; #if HAVE_SYS_EVENT_H int m_kqueue; #endif bool m_terminate_request : 1; }; } // namespace lldb_private #endif // lldb_Host_MainLoop_h_ Index: vendor/lldb/dist/include/lldb/Host/PosixApi.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/PosixApi.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Host/PosixApi.h (revision 321194) @@ -1,21 +1,23 @@ //===-- PosixApi.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Host_PosixApi_h #define liblldb_Host_PosixApi_h // This file defines platform specific functions, macros, and types necessary // to provide a minimum level of compatibility across all platforms to rely // on various posix api functionality. #if defined(_WIN32) #include "lldb/Host/windows/PosixApi.h" +#else +#include #endif #endif Index: vendor/lldb/dist/include/lldb/Host/SocketAddress.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/SocketAddress.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Host/SocketAddress.h (revision 321194) @@ -1,231 +1,236 @@ //===-- 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, 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; //------------------------------------------------------------------ + // Returns true if the socket is INADDR_LOOPBACK + //------------------------------------------------------------------ + bool IsLocalhost() 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/Host/common/NativeProcessProtocol.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/common/NativeProcessProtocol.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Host/common/NativeProcessProtocol.h (revision 321194) @@ -1,477 +1,476 @@ //===-- NativeProcessProtocol.h ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeProcessProtocol_h_ #define liblldb_NativeProcessProtocol_h_ #include "lldb/Host/Host.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/TraceOptions.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include #include "NativeBreakpointList.h" #include "NativeWatchpointList.h" namespace lldb_private { class MemoryRegionInfo; class ResumeActionList; //------------------------------------------------------------------ // NativeProcessProtocol //------------------------------------------------------------------ -class NativeProcessProtocol - : public std::enable_shared_from_this { +class NativeProcessProtocol { friend class SoftwareBreakpoint; public: virtual ~NativeProcessProtocol() {} virtual Status Resume(const ResumeActionList &resume_actions) = 0; virtual Status Halt() = 0; virtual Status Detach() = 0; //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status Signal(int signo) = 0; //------------------------------------------------------------------ /// Tells a process to interrupt all operations as if by a Ctrl-C. /// /// The default implementation will send a local host's equivalent of /// a SIGSTOP to the process via the NativeProcessProtocol::Signal() /// operation. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status Interrupt(); virtual Status Kill() = 0; //------------------------------------------------------------------ // Tells a process not to stop the inferior on given signals // and just reinject them back. //------------------------------------------------------------------ virtual Status IgnoreSignals(llvm::ArrayRef signals); //---------------------------------------------------------------------- // Memory and memory region functions //---------------------------------------------------------------------- virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); virtual Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) = 0; virtual Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) = 0; virtual Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) = 0; virtual Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) = 0; virtual Status DeallocateMemory(lldb::addr_t addr) = 0; virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0; virtual bool IsAlive() const; virtual size_t UpdateThreads() = 0; virtual bool GetArchitecture(ArchSpec &arch) const = 0; //---------------------------------------------------------------------- // Breakpoint functions //---------------------------------------------------------------------- virtual Status SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) = 0; virtual Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false); virtual Status EnableBreakpoint(lldb::addr_t addr); virtual Status DisableBreakpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Hardware Breakpoint functions //---------------------------------------------------------------------- virtual const HardwareBreakpointMap &GetHardwareBreakpointMap() const; virtual Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size); virtual Status RemoveHardwareBreakpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Watchpoint functions //---------------------------------------------------------------------- virtual const NativeWatchpointList::WatchpointMap &GetWatchpointMap() const; virtual llvm::Optional> GetHardwareDebugSupportInfo() const; virtual Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware); virtual Status RemoveWatchpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- lldb::pid_t GetID() const { return m_pid; } lldb::StateType GetState() const; bool IsRunning() const { return m_state == lldb::eStateRunning || IsStepping(); } bool IsStepping() const { return m_state == lldb::eStateStepping; } bool CanResume() const { return m_state == lldb::eStateStopped; } bool GetByteOrder(lldb::ByteOrder &byte_order) const; virtual llvm::ErrorOr> GetAuxvData() const = 0; //---------------------------------------------------------------------- // Exit Status //---------------------------------------------------------------------- virtual llvm::Optional GetExitStatus(); virtual bool SetExitStatus(WaitStatus status, bool bNotifyStateChange); //---------------------------------------------------------------------- // Access to threads //---------------------------------------------------------------------- NativeThreadProtocolSP GetThreadAtIndex(uint32_t idx); NativeThreadProtocolSP GetThreadByID(lldb::tid_t tid); void SetCurrentThreadID(lldb::tid_t tid) { m_current_thread_id = tid; } lldb::tid_t GetCurrentThreadID() { return m_current_thread_id; } NativeThreadProtocolSP GetCurrentThread() { return GetThreadByID(m_current_thread_id); } //---------------------------------------------------------------------- // Access to inferior stdio //---------------------------------------------------------------------- virtual int GetTerminalFileDescriptor() { return m_terminal_fd; } //---------------------------------------------------------------------- // Stop id interface //---------------------------------------------------------------------- uint32_t GetStopID() const; // --------------------------------------------------------------------- // Callbacks for low-level process state changes // --------------------------------------------------------------------- class NativeDelegate { public: virtual ~NativeDelegate() {} virtual void InitializeDelegate(NativeProcessProtocol *process) = 0; virtual void ProcessStateChanged(NativeProcessProtocol *process, lldb::StateType state) = 0; virtual void DidExec(NativeProcessProtocol *process) = 0; }; //------------------------------------------------------------------ /// Register a native delegate. /// /// Clients can register nofication callbacks by passing in a /// NativeDelegate impl and passing it into this function. /// /// Note: it is required that the lifetime of the /// native_delegate outlive the NativeProcessProtocol. /// /// @param[in] native_delegate /// A NativeDelegate impl to be called when certain events /// happen within the NativeProcessProtocol or related threads. /// /// @return /// true if the delegate was registered successfully; /// false if the delegate was already registered. /// /// @see NativeProcessProtocol::NativeDelegate. //------------------------------------------------------------------ bool RegisterNativeDelegate(NativeDelegate &native_delegate); //------------------------------------------------------------------ /// Unregister a native delegate previously registered. /// /// @param[in] native_delegate /// A NativeDelegate impl previously registered with this process. /// /// @return Returns \b true if the NativeDelegate was /// successfully removed from the process, \b false otherwise. /// /// @see NativeProcessProtocol::NativeDelegate //------------------------------------------------------------------ bool UnregisterNativeDelegate(NativeDelegate &native_delegate); virtual Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) = 0; virtual Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) = 0; class Factory { public: virtual ~Factory(); //------------------------------------------------------------------ /// Launch a process for debugging. /// /// @param[in] launch_info /// Information required to launch the process. /// /// @param[in] native_delegate /// The delegate that will receive messages regarding the /// inferior. Must outlive the NativeProcessProtocol /// instance. /// /// @param[in] mainloop /// The mainloop instance with which the process can register /// callbacks. Must outlive the NativeProcessProtocol /// instance. /// /// @return /// A NativeProcessProtocol shared pointer if the operation succeeded or /// an error object if it failed. //------------------------------------------------------------------ - virtual llvm::Expected + virtual llvm::Expected> Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop) const = 0; //------------------------------------------------------------------ /// Attach to an existing process. /// /// @param[in] pid /// pid of the process locatable /// /// @param[in] native_delegate /// The delegate that will receive messages regarding the /// inferior. Must outlive the NativeProcessProtocol /// instance. /// /// @param[in] mainloop /// The mainloop instance with which the process can register /// callbacks. Must outlive the NativeProcessProtocol /// instance. /// /// @return /// A NativeProcessProtocol shared pointer if the operation succeeded or /// an error object if it failed. //------------------------------------------------------------------ - virtual llvm::Expected + virtual llvm::Expected> Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop) const = 0; }; //------------------------------------------------------------------ /// StartTracing API for starting a tracing instance with the /// TraceOptions on a specific thread or process. /// /// @param[in] config /// The configuration to use when starting tracing. /// /// @param[out] error /// Status indicates what went wrong. /// /// @return /// The API returns a user_id which can be used to get trace /// data, trace configuration or stopping the trace instance. /// The user_id is a key to identify and operate with a tracing /// instance. It may refer to the complete process or a single /// thread. //------------------------------------------------------------------ virtual lldb::user_id_t StartTrace(const TraceOptions &config, Status &error) { error.SetErrorString("Not implemented"); return LLDB_INVALID_UID; } //------------------------------------------------------------------ /// StopTracing API as the name suggests stops a tracing instance. /// /// @param[in] traceid /// The user id of the trace intended to be stopped. Now a /// user_id may map to multiple threads in which case this API /// could be used to stop the tracing for a specific thread by /// supplying its thread id. /// /// @param[in] thread /// Thread is needed when the complete process is being traced /// and the user wishes to stop tracing on a particular thread. /// /// @return /// Status indicating what went wrong. //------------------------------------------------------------------ virtual Status StopTrace(lldb::user_id_t traceid, lldb::tid_t thread = LLDB_INVALID_THREAD_ID) { return Status("Not implemented"); } //------------------------------------------------------------------ /// This API provides the trace data collected in the form of raw /// data. /// /// @param[in] traceid thread /// The traceid and thread provide the context for the trace /// instance. /// /// @param[in] buffer /// The buffer provides the destination buffer where the trace /// data would be read to. The buffer should be truncated to the /// filled length by this function. /// /// @param[in] offset /// There is possibility to read partially the trace data from /// a specified offset where in such cases the buffer provided /// may be smaller than the internal trace collection container. /// /// @return /// The size of the data actually read. //------------------------------------------------------------------ virtual Status GetData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset = 0) { return Status("Not implemented"); } //------------------------------------------------------------------ /// Similar API as above except it aims to provide any extra data /// useful for decoding the actual trace data. //------------------------------------------------------------------ virtual Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset = 0) { return Status("Not implemented"); } //------------------------------------------------------------------ /// API to query the TraceOptions for a given user id /// /// @param[in] traceid /// The user id of the tracing instance. /// /// @param[in] config /// The thread id of the tracing instance, in case configuration /// for a specific thread is needed should be specified in the /// config. /// /// @param[out] error /// Status indicates what went wrong. /// /// @param[out] config /// The actual configuration being used for tracing. //------------------------------------------------------------------ virtual Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) { return Status("Not implemented"); } protected: lldb::pid_t m_pid; std::vector m_threads; lldb::tid_t m_current_thread_id = LLDB_INVALID_THREAD_ID; mutable std::recursive_mutex m_threads_mutex; lldb::StateType m_state = lldb::eStateInvalid; mutable std::recursive_mutex m_state_mutex; llvm::Optional m_exit_status; std::recursive_mutex m_delegates_mutex; std::vector m_delegates; NativeBreakpointList m_breakpoint_list; NativeWatchpointList m_watchpoint_list; HardwareBreakpointMap m_hw_breakpoints_map; int m_terminal_fd; uint32_t m_stop_id = 0; // Set of signal numbers that LLDB directly injects back to inferior // without stopping it. llvm::DenseSet m_signals_to_ignore; // lldb_private::Host calls should be used to launch a process for debugging, // and // then the process should be attached to. When attaching to a process // lldb_private::Host calls should be used to locate the process to attach to, // and then this function should be called. NativeProcessProtocol(lldb::pid_t pid, int terminal_fd, NativeDelegate &delegate); // ----------------------------------------------------------- // Internal interface for state handling // ----------------------------------------------------------- void SetState(lldb::StateType state, bool notify_delegates = true); // Derived classes need not implement this. It can be used as a // hook to clear internal caches that should be invalidated when // stop ids change. // // Note this function is called with the state mutex obtained // by the caller. virtual void DoStopIDBumped(uint32_t newBumpId); // ----------------------------------------------------------- // Internal interface for software breakpoints // ----------------------------------------------------------- Status SetSoftwareBreakpoint(lldb::addr_t addr, uint32_t size_hint); virtual Status GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) = 0; // ----------------------------------------------------------- /// Notify the delegate that an exec occurred. /// /// Provide a mechanism for a delegate to clear out any exec- /// sensitive data. // ----------------------------------------------------------- void NotifyDidExec(); NativeThreadProtocolSP GetThreadByIDUnlocked(lldb::tid_t tid); // ----------------------------------------------------------- // Static helper methods for derived classes. // ----------------------------------------------------------- static Status ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch); private: void SynchronouslyNotifyProcessStateChanged(lldb::StateType state); }; } // namespace lldb_private #endif // #ifndef liblldb_NativeProcessProtocol_h_ Index: vendor/lldb/dist/include/lldb/Host/common/NativeThreadProtocol.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/common/NativeThreadProtocol.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Host/common/NativeThreadProtocol.h (revision 321194) @@ -1,72 +1,72 @@ //===-- NativeThreadProtocol.h ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeThreadProtocol_h_ #define liblldb_NativeThreadProtocol_h_ #include #include "lldb/Host/Debug.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" namespace lldb_private { //------------------------------------------------------------------ // NativeThreadProtocol //------------------------------------------------------------------ class NativeThreadProtocol : public std::enable_shared_from_this { public: - NativeThreadProtocol(NativeProcessProtocol *process, lldb::tid_t tid); + NativeThreadProtocol(NativeProcessProtocol &process, lldb::tid_t tid); virtual ~NativeThreadProtocol() {} virtual std::string GetName() = 0; virtual lldb::StateType GetState() = 0; virtual NativeRegisterContextSP GetRegisterContext() = 0; virtual Status ReadRegister(uint32_t reg, RegisterValue ®_value); virtual Status WriteRegister(uint32_t reg, const RegisterValue ®_value); virtual Status SaveAllRegisters(lldb::DataBufferSP &data_sp); virtual Status RestoreAllRegisters(lldb::DataBufferSP &data_sp); virtual bool GetStopReason(ThreadStopInfo &stop_info, std::string &description) = 0; lldb::tid_t GetID() const { return m_tid; } - NativeProcessProtocolSP GetProcess(); + NativeProcessProtocol &GetProcess() { return m_process; } // --------------------------------------------------------------------- // Thread-specific watchpoints // --------------------------------------------------------------------- virtual Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) = 0; virtual Status RemoveWatchpoint(lldb::addr_t addr) = 0; // --------------------------------------------------------------------- // Thread-specific Hardware Breakpoint routines // --------------------------------------------------------------------- virtual Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) = 0; virtual Status RemoveHardwareBreakpoint(lldb::addr_t addr) = 0; protected: - NativeProcessProtocolWP m_process_wp; + NativeProcessProtocol &m_process; lldb::tid_t m_tid; }; } #endif // #ifndef liblldb_NativeThreadProtocol_h_ Index: vendor/lldb/dist/include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- vendor/lldb/dist/include/lldb/Interpreter/CommandInterpreter.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Interpreter/CommandInterpreter.h (revision 321194) @@ -1,558 +1,558 @@ //===-- CommandInterpreter.h ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_CommandInterpreter_h_ #define liblldb_CommandInterpreter_h_ // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Event.h" #include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandAlias.h" #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StringList.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private.h" namespace lldb_private { class CommandInterpreterRunOptions { public: //------------------------------------------------------------------ /// Construct a CommandInterpreterRunOptions object. /// This class is used to control all the instances where we run multiple /// commands, e.g. /// HandleCommands, HandleCommandsFromFile, RunCommandInterpreter. /// The meanings of the options in this object are: /// /// @param[in] stop_on_continue /// If \b true execution will end on the first command that causes the /// process in the /// execution context to continue. If \false, we won't check the execution /// status. /// @param[in] stop_on_error /// If \b true execution will end on the first command that causes an /// error. /// @param[in] stop_on_crash /// If \b true when a command causes the target to run, and the end of the /// run is a /// signal or exception, stop executing the commands. /// @param[in] echo_commands /// If \b true echo the command before executing it. If \false, execute /// silently. /// @param[in] print_results /// If \b true print the results of the command after executing it. If /// \false, execute silently. /// @param[in] add_to_history /// If \b true add the commands to the command history. If \false, don't /// add them. //------------------------------------------------------------------ CommandInterpreterRunOptions(LazyBool stop_on_continue, LazyBool stop_on_error, LazyBool stop_on_crash, LazyBool echo_commands, LazyBool print_results, LazyBool add_to_history) : m_stop_on_continue(stop_on_continue), m_stop_on_error(stop_on_error), m_stop_on_crash(stop_on_crash), m_echo_commands(echo_commands), m_print_results(print_results), m_add_to_history(add_to_history) {} CommandInterpreterRunOptions() : m_stop_on_continue(eLazyBoolCalculate), m_stop_on_error(eLazyBoolCalculate), m_stop_on_crash(eLazyBoolCalculate), m_echo_commands(eLazyBoolCalculate), m_print_results(eLazyBoolCalculate), m_add_to_history(eLazyBoolCalculate) {} void SetSilent(bool silent) { LazyBool value = silent ? eLazyBoolNo : eLazyBoolYes; m_echo_commands = value; m_print_results = value; m_add_to_history = value; } // These return the default behaviors if the behavior is not // eLazyBoolCalculate. // But I've also left the ivars public since for different ways of running the // interpreter you might want to force different defaults... In that case, // just grab // the LazyBool ivars directly and do what you want with eLazyBoolCalculate. bool GetStopOnContinue() const { return DefaultToNo(m_stop_on_continue); } void SetStopOnContinue(bool stop_on_continue) { m_stop_on_continue = stop_on_continue ? eLazyBoolYes : eLazyBoolNo; } bool GetStopOnError() const { return DefaultToNo(m_stop_on_continue); } void SetStopOnError(bool stop_on_error) { m_stop_on_error = stop_on_error ? eLazyBoolYes : eLazyBoolNo; } bool GetStopOnCrash() const { return DefaultToNo(m_stop_on_crash); } void SetStopOnCrash(bool stop_on_crash) { m_stop_on_crash = stop_on_crash ? eLazyBoolYes : eLazyBoolNo; } bool GetEchoCommands() const { return DefaultToYes(m_echo_commands); } void SetEchoCommands(bool echo_commands) { m_echo_commands = echo_commands ? eLazyBoolYes : eLazyBoolNo; } bool GetPrintResults() const { return DefaultToYes(m_print_results); } void SetPrintResults(bool print_results) { m_print_results = print_results ? eLazyBoolYes : eLazyBoolNo; } bool GetAddToHistory() const { return DefaultToYes(m_add_to_history); } void SetAddToHistory(bool add_to_history) { m_add_to_history = add_to_history ? eLazyBoolYes : eLazyBoolNo; } LazyBool m_stop_on_continue; LazyBool m_stop_on_error; LazyBool m_stop_on_crash; LazyBool m_echo_commands; LazyBool m_print_results; LazyBool m_add_to_history; private: static bool DefaultToYes(LazyBool flag) { switch (flag) { case eLazyBoolNo: return false; default: return true; } } static bool DefaultToNo(LazyBool flag) { switch (flag) { case eLazyBoolYes: return true; default: return false; } } }; class CommandInterpreter : public Broadcaster, public Properties, public IOHandlerDelegate { public: enum { eBroadcastBitThreadShouldExit = (1 << 0), eBroadcastBitResetPrompt = (1 << 1), eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit eBroadcastBitAsynchronousOutputData = (1 << 3), eBroadcastBitAsynchronousErrorData = (1 << 4) }; enum ChildrenTruncatedWarningStatus // tristate boolean to manage children // truncation warning { eNoTruncation = 0, // never truncated eUnwarnedTruncation = 1, // truncated but did not notify eWarnedTruncation = 2 // truncated and notified }; enum CommandTypes { eCommandTypesBuiltin = 0x0001, // native commands such as "frame" eCommandTypesUserDef = 0x0002, // scripted commands eCommandTypesAliases = 0x0004, // aliases such as "po" eCommandTypesHidden = 0x0008, // commands prefixed with an underscore eCommandTypesAllThem = 0xFFFF // all commands }; CommandInterpreter(Debugger &debugger, lldb::ScriptLanguage script_language, bool synchronous_execution); ~CommandInterpreter() override; // These two functions fill out the Broadcaster interface: static ConstString &GetStaticBroadcasterClass(); ConstString &GetBroadcasterClass() const override { return GetStaticBroadcasterClass(); } void SourceInitFile(bool in_cwd, CommandReturnObject &result); bool AddCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace); bool AddUserCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace); lldb::CommandObjectSP GetCommandSPExact(llvm::StringRef cmd, bool include_aliases) const; CommandObject *GetCommandObject(llvm::StringRef cmd, StringList *matches = nullptr) const; bool CommandExists(llvm::StringRef cmd) const; bool AliasExists(llvm::StringRef cmd) const; bool UserCommandExists(llvm::StringRef cmd) const; CommandAlias *AddAlias(llvm::StringRef alias_name, lldb::CommandObjectSP &command_obj_sp, llvm::StringRef args_string = llvm::StringRef()); // Remove a command if it is removable (python or regex command) bool RemoveCommand(llvm::StringRef cmd); bool RemoveAlias(llvm::StringRef alias_name); bool GetAliasFullName(llvm::StringRef cmd, std::string &full_name) const; bool RemoveUser(llvm::StringRef alias_name); void RemoveAllUser() { m_user_dict.clear(); } const CommandAlias *GetAlias(llvm::StringRef alias_name) const; CommandObject *BuildAliasResult(llvm::StringRef alias_name, std::string &raw_input_string, std::string &alias_result, CommandReturnObject &result); bool HandleCommand(const char *command_line, LazyBool add_to_history, CommandReturnObject &result, ExecutionContext *override_context = nullptr, bool repeat_on_empty_command = true, bool no_context_switching = false); //------------------------------------------------------------------ /// Execute a list of commands in sequence. /// /// @param[in] commands /// The list of commands to execute. /// @param[in,out] context /// The execution context in which to run the commands. Can be nullptr in /// which case the default /// context will be used. /// @param[in] options /// This object holds the options used to control when to stop, whether to /// execute commands, /// etc. /// @param[out] result /// This is marked as succeeding with no output if all commands execute /// safely, /// and failed with some explanation if we aborted executing the commands /// at some point. //------------------------------------------------------------------ void HandleCommands(const StringList &commands, ExecutionContext *context, CommandInterpreterRunOptions &options, CommandReturnObject &result); //------------------------------------------------------------------ /// Execute a list of commands from a file. /// /// @param[in] file /// The file from which to read in commands. /// @param[in,out] context /// The execution context in which to run the commands. Can be nullptr in /// which case the default /// context will be used. /// @param[in] options /// This object holds the options used to control when to stop, whether to /// execute commands, /// etc. /// @param[out] result /// This is marked as succeeding with no output if all commands execute /// safely, /// and failed with some explanation if we aborted executing the commands /// at some point. //------------------------------------------------------------------ void HandleCommandsFromFile(FileSpec &file, ExecutionContext *context, CommandInterpreterRunOptions &options, CommandReturnObject &result); CommandObject *GetCommandObjectForCommand(llvm::StringRef &command_line); // This handles command line completion. You are given a pointer to the // command string buffer, to the current cursor, // and to the end of the string (in case it is not NULL terminated). // You also passed in an StringList object to fill with the returns. // The first element of the array will be filled with the string that you // would need to insert at // the cursor point to complete the cursor point to the longest common // matching prefix. // If you want to limit the number of elements returned, set // max_return_elements to the number of elements // you want returned. Otherwise set max_return_elements to -1. // If you want to start some way into the match list, then set // match_start_point to the desired start // point. // Returns: // -1 if the completion character should be inserted // -2 if the entire command line should be deleted and replaced with // matches.GetStringAtIndex(0) // INT_MAX if the number of matches is > max_return_elements, but it is // expensive to compute. // Otherwise, returns the number of matches. // // FIXME: Only max_return_elements == -1 is supported at present. int HandleCompletion(const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, StringList &matches); // This version just returns matches, and doesn't compute the substring. It // is here so the // Help command can call it for the first argument. // word_complete tells whether the completions are considered a "complete" // response (so the // completer should complete the quote & put a space after the word. int HandleCompletionMatches(Args &input, int &cursor_index, int &cursor_char_position, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches); int GetCommandNamesMatchingPartialString(const char *cmd_cstr, bool include_aliases, StringList &matches); void GetHelp(CommandReturnObject &result, uint32_t types = eCommandTypesAllThem); void GetAliasHelp(const char *alias_name, StreamString &help_string); void OutputFormattedHelpText(Stream &strm, llvm::StringRef prefix, llvm::StringRef help_text); void OutputFormattedHelpText(Stream &stream, llvm::StringRef command_word, llvm::StringRef separator, llvm::StringRef help_text, size_t max_word_len); // this mimics OutputFormattedHelpText but it does perform a much simpler // formatting, basically ensuring line alignment. This is only good if you // have some complicated layout for your help text and want as little help as // reasonable in properly displaying it. Most of the times, you simply want // to type some text and have it printed in a reasonable way on screen. If // so, use OutputFormattedHelpText void OutputHelpText(Stream &stream, llvm::StringRef command_word, llvm::StringRef separator, llvm::StringRef help_text, uint32_t max_word_len); Debugger &GetDebugger() { return m_debugger; } ExecutionContext GetExecutionContext() { const bool thread_and_frame_only_if_stopped = true; return m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped); } void UpdateExecutionContext(ExecutionContext *override_context); lldb::PlatformSP GetPlatform(bool prefer_target_platform); const char *ProcessEmbeddedScriptCommands(const char *arg); void UpdatePrompt(llvm::StringRef prompt); bool Confirm(llvm::StringRef message, bool default_answer); void LoadCommandDictionary(); void Initialize(); void Clear(); void SetScriptLanguage(lldb::ScriptLanguage lang); bool HasCommands() const; bool HasAliases() const; bool HasUserCommands() const; bool HasAliasOptions() const; void BuildAliasCommandArgs(CommandObject *alias_cmd_obj, const char *alias_name, Args &cmd_args, std::string &raw_input_string, CommandReturnObject &result); int GetOptionArgumentPosition(const char *in_string); ScriptInterpreter *GetScriptInterpreter(bool can_create = true); void SetScriptInterpreter(); void SkipLLDBInitFiles(bool skip_lldbinit_files) { m_skip_lldbinit_files = skip_lldbinit_files; } void SkipAppInitFiles(bool skip_app_init_files) { m_skip_app_init_files = m_skip_lldbinit_files; } bool GetSynchronous(); void FindCommandsForApropos(llvm::StringRef word, StringList &commands_found, StringList &commands_help, bool search_builtin_commands, bool search_user_commands, bool search_alias_commands); bool GetBatchCommandMode() { return m_batch_command_mode; } bool SetBatchCommandMode(bool value) { const bool old_value = m_batch_command_mode; m_batch_command_mode = value; return old_value; } void ChildrenTruncated() { if (m_truncation_warning == eNoTruncation) m_truncation_warning = eUnwarnedTruncation; } bool TruncationWarningNecessary() { return (m_truncation_warning == eUnwarnedTruncation); } void TruncationWarningGiven() { m_truncation_warning = eWarnedTruncation; } const char *TruncationWarningText() { return "*** Some of your variables have more members than the debugger " "will show by default. To show all of them, you can either use the " "--show-all-children option to %s or raise the limit by changing " "the target.max-children-count setting.\n"; } const CommandHistory &GetCommandHistory() const { return m_command_history; } CommandHistory &GetCommandHistory() { return m_command_history; } bool IsActive(); void RunCommandInterpreter(bool auto_handle_events, bool spawn_thread, CommandInterpreterRunOptions &options); void GetLLDBCommandsFromIOHandler(const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton); void GetPythonCommandsFromIOHandler(const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton); const char *GetCommandPrefix(); //------------------------------------------------------------------ // Properties //------------------------------------------------------------------ bool GetExpandRegexAliases() const; bool GetPromptOnQuit() const; void SetPromptOnQuit(bool b); void ResolveCommand(const char *command_line, CommandReturnObject &result); bool GetStopCmdSourceOnError() const; uint32_t GetNumErrors() const { return m_num_errors; } bool GetQuitRequested() const { return m_quit_requested; } lldb::IOHandlerSP GetIOHandler(bool force_create = false, CommandInterpreterRunOptions *options = nullptr); bool GetStoppedForCrash() const { return m_stopped_for_crash; } bool GetSpaceReplPrompts() const; protected: friend class Debugger; //------------------------------------------------------------------ // IOHandlerDelegate functions //------------------------------------------------------------------ void IOHandlerInputComplete(IOHandler &io_handler, std::string &line) override; ConstString IOHandlerGetControlSequence(char ch) override { if (ch == 'd') return ConstString("quit\n"); return ConstString(); } bool IOHandlerInterrupt(IOHandler &io_handler) override; size_t GetProcessOutput(); void SetSynchronous(bool value); lldb::CommandObjectSP GetCommandSP(llvm::StringRef cmd, bool include_aliases = true, bool exact = true, StringList *matches = nullptr) const; private: Status PreprocessCommand(std::string &command); // Completely resolves aliases and abbreviations, returning a pointer to the // final command object and updating command_line to the fully substituted // and translated command. CommandObject *ResolveCommandImpl(std::string &command_line, CommandReturnObject &result); void FindCommandsForApropos(llvm::StringRef word, StringList &commands_found, StringList &commands_help, CommandObject::CommandMap &command_map); Debugger &m_debugger; // The debugger session that this interpreter is // associated with ExecutionContextRef m_exe_ctx_ref; // The current execution context to use // when handling commands bool m_synchronous_execution; bool m_skip_lldbinit_files; bool m_skip_app_init_files; CommandObject::CommandMap m_command_dict; // Stores basic built-in commands // (they cannot be deleted, removed // or overwritten). CommandObject::CommandMap m_alias_dict; // Stores user aliases/abbreviations for commands CommandObject::CommandMap m_user_dict; // Stores user-defined commands CommandHistory m_command_history; std::string m_repeat_command; // Stores the command that will be executed for // an empty command string. lldb::ScriptInterpreterSP m_script_interpreter_sp; - std::mutex m_script_interpreter_mutex; + std::recursive_mutex m_script_interpreter_mutex; lldb::IOHandlerSP m_command_io_handler_sp; char m_comment_char; bool m_batch_command_mode; ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated // children and whether // the user has been told uint32_t m_command_source_depth; std::vector m_command_source_flags; uint32_t m_num_errors; bool m_quit_requested; bool m_stopped_for_crash; }; } // namespace lldb_private #endif // liblldb_CommandInterpreter_h_ Index: vendor/lldb/dist/include/lldb/Utility/DataExtractor.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/DataExtractor.h (revision 321193) +++ vendor/lldb/dist/include/lldb/Utility/DataExtractor.h (revision 321194) @@ -1,1157 +1,1158 @@ //===-- DataExtractor.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_UTILITY_DATAEXTRACTOR_H #define LLDB_UTILITY_DATAEXTRACTOR_H #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" // for ByteOrder #include "lldb/lldb-forward.h" // for DataBufferSP #include "lldb/lldb-types.h" +#include #include #include namespace lldb_private { class Log; } namespace lldb_private { class Stream; } namespace llvm { template class SmallVectorImpl; } // C++ Includes namespace lldb_private { //---------------------------------------------------------------------- /// @class DataExtractor DataExtractor.h "lldb/Core/DataExtractor.h" /// @brief An data extractor class. /// /// DataExtractor is a class that can extract data (swapping if needed) /// from a data buffer. The data buffer can be caller owned, or can be /// shared data that can be shared between multiple DataExtractor /// instances. Multiple DataExtractor objects can share the same data, /// yet extract values in different address sizes and byte order modes. /// Each object can have a unique position in the shared data and extract /// data from different offsets. /// /// @see DataBuffer //---------------------------------------------------------------------- class DataExtractor { public: //------------------------------------------------------------------ /// @typedef DataExtractor::Type /// @brief Type enumerations used in the dump routines. //------------------------------------------------------------------ typedef enum { TypeUInt8, ///< Format output as unsigned 8 bit integers TypeChar, ///< Format output as characters TypeUInt16, ///< Format output as unsigned 16 bit integers TypeUInt32, ///< Format output as unsigned 32 bit integers TypeUInt64, ///< Format output as unsigned 64 bit integers TypePointer, ///< Format output as pointers TypeULEB128, ///< Format output as ULEB128 numbers TypeSLEB128 ///< Format output as SLEB128 numbers } Type; //------------------------------------------------------------------ /// Default constructor. /// /// Initialize all members to a default empty state. //------------------------------------------------------------------ DataExtractor(); //------------------------------------------------------------------ /// Construct with a buffer that is owned by the caller. /// /// This constructor allows us to use data that is owned by the /// caller. The data must stay around as long as this object is /// valid. /// /// @param[in] data /// A pointer to caller owned data. /// /// @param[in] data_length /// The length in bytes of \a data. /// /// @param[in] byte_order /// A byte order of the data that we are extracting from. /// /// @param[in] addr_size /// A new address byte size value. /// /// @param[in] target_byte_size /// A size of a target byte in 8-bit host bytes //------------------------------------------------------------------ DataExtractor(const void *data, lldb::offset_t data_length, lldb::ByteOrder byte_order, uint32_t addr_size, uint32_t target_byte_size = 1); //------------------------------------------------------------------ /// Construct with shared data. /// /// Copies the data shared pointer which adds a reference to the /// contained in \a data_sp. The shared data reference is reference /// counted to ensure the data lives as long as anyone still has a /// valid shared pointer to the data in \a data_sp. /// /// @param[in] data_sp /// A shared pointer to data. /// /// @param[in] byte_order /// A byte order of the data that we are extracting from. /// /// @param[in] addr_size /// A new address byte size value. /// /// @param[in] target_byte_size /// A size of a target byte in 8-bit host bytes //------------------------------------------------------------------ DataExtractor(const lldb::DataBufferSP &data_sp, lldb::ByteOrder byte_order, uint32_t addr_size, uint32_t target_byte_size = 1); //------------------------------------------------------------------ /// Construct with a subset of \a data. /// /// Initialize this object with a subset of the data bytes in \a /// data. If \a data contains shared data, then a reference to the /// shared data will be added to ensure the shared data stays around /// as long as any objects have references to the shared data. The /// byte order value and the address size settings are copied from \a /// data. If \a offset is not a valid offset in \a data, then no /// reference to the shared data will be added. If there are not /// \a length bytes available in \a data starting at \a offset, /// the length will be truncated to contain as many bytes as /// possible. /// /// @param[in] data /// Another DataExtractor object that contains data. /// /// @param[in] offset /// The offset into \a data at which the subset starts. /// /// @param[in] length /// The length in bytes of the subset of data. /// /// @param[in] target_byte_size /// A size of a target byte in 8-bit host bytes //------------------------------------------------------------------ DataExtractor(const DataExtractor &data, lldb::offset_t offset, lldb::offset_t length, uint32_t target_byte_size = 1); DataExtractor(const DataExtractor &rhs); //------------------------------------------------------------------ /// Assignment operator. /// /// Copies all data, byte order and address size settings from \a rhs into /// this object. If \a rhs contains shared data, a reference to that /// shared data will be added. /// /// @param[in] rhs /// Another DataExtractor object to copy. /// /// @return /// A const reference to this object. //------------------------------------------------------------------ const DataExtractor &operator=(const DataExtractor &rhs); //------------------------------------------------------------------ /// Destructor /// /// If this object contains a valid shared data reference, the /// reference count on the data will be decremented, and if zero, /// the data will be freed. //------------------------------------------------------------------ virtual ~DataExtractor(); uint32_t getTargetByteSize() const { return m_target_byte_size; } //------------------------------------------------------------------ /// Clears the object state. /// /// Clears the object contents back to a default invalid state, and /// release any references to shared data that this object may /// contain. //------------------------------------------------------------------ void Clear(); //------------------------------------------------------------------ /// Dumps the binary data as \a type objects to stream \a s (or to /// Log() if \a s is nullptr) starting \a offset bytes into the data /// and stopping after dumping \a length bytes. The offset into the /// data is displayed at the beginning of each line and can be /// offset by base address \a base_addr. \a num_per_line objects /// will be displayed on each line. /// /// @param[in] s /// The stream to dump the output to. If nullptr the output will /// be dumped to Log(). /// /// @param[in] offset /// The offset into the data at which to start dumping. /// /// @param[in] length /// The number of bytes to dump. /// /// @param[in] base_addr /// The base address that gets added to the offset displayed on /// each line. /// /// @param[in] num_per_line /// The number of \a type objects to display on each line. /// /// @param[in] type /// The type of objects to use when dumping data from this /// object. See DataExtractor::Type. /// /// @param[in] type_format /// The optional format to use for the \a type objects. If this /// is nullptr, the default format for the \a type will be used. /// /// @return /// The offset at which dumping ended. //------------------------------------------------------------------ lldb::offset_t PutToLog(Log *log, lldb::offset_t offset, lldb::offset_t length, uint64_t base_addr, uint32_t num_per_line, Type type, const char *type_format = nullptr) const; //------------------------------------------------------------------ /// Dump a UUID value at \a offset. /// /// Dump a UUID starting at \a offset bytes into this object's data. /// If the stream \a s is nullptr, the output will be sent to Log(). /// /// @param[in] s /// The stream to dump the output to. If nullptr the output will /// be dumped to Log(). /// /// @param[in] offset /// The offset into the data at which to extract and dump a /// UUID value. //------------------------------------------------------------------ void DumpUUID(Stream *s, lldb::offset_t offset) const; //------------------------------------------------------------------ /// Extract an arbitrary number of bytes in the specified byte /// order. /// /// Attemps to extract \a length bytes starting at \a offset bytes /// into this data in the requested byte order (\a dst_byte_order) /// and place the results in \a dst. \a dst must be at least \a /// length bytes long. /// /// @param[in] offset /// The offset in bytes into the contained data at which to /// start extracting. /// /// @param[in] length /// The number of bytes to extract. /// /// @param[in] dst_byte_order /// A byte order of the data that we want when the value in /// copied to \a dst. /// /// @param[out] dst /// The buffer that will receive the extracted value if there /// are enough bytes available in the current data. /// /// @return /// The number of bytes that were extracted which will be \a /// length when the value is successfully extracted, or zero /// if there aren't enough bytes at the specified offset. //------------------------------------------------------------------ size_t ExtractBytes(lldb::offset_t offset, lldb::offset_t length, lldb::ByteOrder dst_byte_order, void *dst) const; //------------------------------------------------------------------ /// Extract an address from \a *offset_ptr. /// /// Extract a single address from the data and update the offset /// pointed to by \a offset_ptr. The size of the extracted address /// comes from the \a m_addr_size member variable and should be /// set correctly prior to extracting any address values. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted address value. //------------------------------------------------------------------ uint64_t GetAddress(lldb::offset_t *offset_ptr) const; uint64_t GetAddress_unchecked(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Get the current address size. /// /// Return the size in bytes of any address values this object will /// extract. /// /// @return /// The size in bytes of address values that will be extracted. //------------------------------------------------------------------ uint32_t GetAddressByteSize() const { return m_addr_size; } //------------------------------------------------------------------ /// Get the number of bytes contained in this object. /// /// @return /// The total number of bytes of data this object refers to. //------------------------------------------------------------------ uint64_t GetByteSize() const { return m_end - m_start; } //------------------------------------------------------------------ /// Extract a C string from \a *offset_ptr. /// /// Returns a pointer to a C String from the data at the offset /// pointed to by \a offset_ptr. A variable length NULL terminated C /// string will be extracted and the \a offset_ptr will be /// updated with the offset of the byte that follows the NULL /// terminator byte. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// A pointer to the C string value in the data. If the offset /// pointed to by \a offset_ptr is out of bounds, or if the /// offset plus the length of the C string is out of bounds, /// nullptr will be returned. //------------------------------------------------------------------ const char *GetCStr(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract a C string from \a *offset_ptr with field size \a len. /// /// Returns a pointer to a C String from the data at the offset /// pointed to by \a offset_ptr, with a field length of \a len. /// A NULL terminated C string will be extracted and the \a offset_ptr /// will be updated with the offset of the byte that follows the fixed /// length field. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// A pointer to the C string value in the data. If the offset /// pointed to by \a offset_ptr is out of bounds, or if the /// offset plus the length of the field is out of bounds, or if /// the field does not contain a NULL terminator byte, nullptr will /// be returned. const char *GetCStr(lldb::offset_t *offset_ptr, lldb::offset_t len) const; //------------------------------------------------------------------ /// Extract \a length bytes from \a *offset_ptr. /// /// Returns a pointer to a bytes in this object's data at the offset /// pointed to by \a offset_ptr. If \a length is zero or too large, /// then the offset pointed to by \a offset_ptr will not be updated /// and nullptr will be returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] length /// The optional length of a string to extract. If the value is /// zero, a NULL terminated C string will be extracted. /// /// @return /// A pointer to the bytes in this object's data if the offset /// and length are valid, or nullptr otherwise. //------------------------------------------------------------------ const void *GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const { const uint8_t *ptr = PeekData(*offset_ptr, length); if (ptr) *offset_ptr += length; return ptr; } //------------------------------------------------------------------ /// Copy \a length bytes from \a *offset, without swapping bytes. /// /// @param[in] offset /// The offset into this data from which to start copying /// /// @param[in] length /// The length of the data to copy from this object /// /// @param[out] dst /// The buffer to place the output data. /// /// @return /// Returns the number of bytes that were copied, or zero if /// anything goes wrong. //------------------------------------------------------------------ lldb::offset_t CopyData(lldb::offset_t offset, lldb::offset_t length, void *dst) const; //------------------------------------------------------------------ /// Copy \a dst_len bytes from \a *offset_ptr and ensure the copied /// data is treated as a value that can be swapped to match the /// specified byte order. /// /// For values that are larger than the supported integer sizes, /// this function can be used to extract data in a specified byte /// order. It can also be used to copy a smaller integer value from /// to a larger value. The extra bytes left over will be padded /// correctly according to the byte order of this object and the /// \a dst_byte_order. This can be very handy when say copying a /// partial data value into a register. /// /// @param[in] src_offset /// The offset into this data from which to start copying an /// endian entity /// /// @param[in] src_len /// The length of the endian data to copy from this object /// into the \a dst object /// /// @param[out] dst /// The buffer where to place the endian data. The data might /// need to be byte swapped (and appropriately padded with /// zeroes if \a src_len != \a dst_len) if \a dst_byte_order /// does not match the byte order in this object. /// /// @param[in] dst_len /// The length number of bytes that the endian value will /// occupy is \a dst. /// /// @param[in] byte_order /// The byte order that the endian value should be in the \a dst /// buffer. /// /// @return /// Returns the number of bytes that were copied, or zero if /// anything goes wrong. //------------------------------------------------------------------ lldb::offset_t CopyByteOrderedData(lldb::offset_t src_offset, lldb::offset_t src_len, void *dst, lldb::offset_t dst_len, lldb::ByteOrder dst_byte_order) const; //------------------------------------------------------------------ /// Get the data end pointer. /// /// @return /// Returns a pointer to the next byte contained in this /// object's data, or nullptr of there is no data in this object. //------------------------------------------------------------------ const uint8_t *GetDataEnd() const { return m_end; } //------------------------------------------------------------------ /// Get the shared data offset. /// /// Get the offset of the first byte of data in the shared data (if /// any). /// /// @return /// If this object contains shared data, this function returns /// the offset in bytes into that shared data, zero otherwise. //------------------------------------------------------------------ size_t GetSharedDataOffset() const; //------------------------------------------------------------------ /// Get the data start pointer. /// /// @return /// Returns a pointer to the first byte contained in this /// object's data, or nullptr of there is no data in this object. //------------------------------------------------------------------ const uint8_t *GetDataStart() const { return m_start; } //------------------------------------------------------------------ /// Extract a float from \a *offset_ptr. /// /// Extract a single float value. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The floating value that was extracted, or zero on failure. //------------------------------------------------------------------ float GetFloat(lldb::offset_t *offset_ptr) const; double GetDouble(lldb::offset_t *offset_ptr) const; long double GetLongDouble(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract an integer of size \a byte_size from \a *offset_ptr. /// /// Extract a single integer value and update the offset pointed to /// by \a offset_ptr. The size of the extracted integer is specified /// by the \a byte_size argument. \a byte_size should have a value /// >= 1 and <= 4 since the return value is only 32 bits wide. Any /// \a byte_size values less than 1 or greater than 4 will result in /// nothing being extracted, and zero being returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] byte_size /// The size in byte of the integer to extract. /// /// @return /// The integer value that was extracted, or zero on failure. //------------------------------------------------------------------ uint32_t GetMaxU32(lldb::offset_t *offset_ptr, size_t byte_size) const; //------------------------------------------------------------------ /// Extract an unsigned integer of size \a byte_size from \a /// *offset_ptr. /// /// Extract a single unsigned integer value and update the offset /// pointed to by \a offset_ptr. The size of the extracted integer /// is specified by the \a byte_size argument. \a byte_size should /// have a value greater than or equal to one and less than or equal /// to eight since the return value is 64 bits wide. Any /// \a byte_size values less than 1 or greater than 8 will result in /// nothing being extracted, and zero being returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] byte_size /// The size in byte of the integer to extract. /// /// @return /// The unsigned integer value that was extracted, or zero on /// failure. //------------------------------------------------------------------ uint64_t GetMaxU64(lldb::offset_t *offset_ptr, size_t byte_size) const; uint64_t GetMaxU64_unchecked(lldb::offset_t *offset_ptr, size_t byte_size) const; //------------------------------------------------------------------ /// Extract an signed integer of size \a byte_size from \a *offset_ptr. /// /// Extract a single signed integer value (sign extending if required) /// and update the offset pointed to by \a offset_ptr. The size of /// the extracted integer is specified by the \a byte_size argument. /// \a byte_size should have a value greater than or equal to one /// and less than or equal to eight since the return value is 64 /// bits wide. Any \a byte_size values less than 1 or greater than /// 8 will result in nothing being extracted, and zero being returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] byte_size /// The size in byte of the integer to extract. /// /// @return /// The sign extended signed integer value that was extracted, /// or zero on failure. //------------------------------------------------------------------ int64_t GetMaxS64(lldb::offset_t *offset_ptr, size_t size) const; //------------------------------------------------------------------ /// Extract an unsigned integer of size \a byte_size from \a /// *offset_ptr, then extract the bitfield from this value if /// \a bitfield_bit_size is non-zero. /// /// Extract a single unsigned integer value and update the offset /// pointed to by \a offset_ptr. The size of the extracted integer /// is specified by the \a byte_size argument. \a byte_size should /// have a value greater than or equal to one and less than or equal /// to 8 since the return value is 64 bits wide. Any /// \a byte_size values less than 1 or greater than 8 will result in /// nothing being extracted, and zero being returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] byte_size /// The size in byte of the integer to extract. /// /// @param[in] bitfield_bit_size /// The size in bits of the bitfield value to extract, or zero /// to just extract the entire integer value. /// /// @param[in] bitfield_bit_offset /// The bit offset of the bitfield value in the extracted /// integer. For little-endian data, this is the offset of /// the LSB of the bitfield from the LSB of the integer. /// For big-endian data, this is the offset of the MSB of the /// bitfield from the MSB of the integer. /// /// @return /// The unsigned bitfield integer value that was extracted, or /// zero on failure. //------------------------------------------------------------------ uint64_t GetMaxU64Bitfield(lldb::offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const; //------------------------------------------------------------------ /// Extract an signed integer of size \a byte_size from \a /// *offset_ptr, then extract and signe extend the bitfield from /// this value if \a bitfield_bit_size is non-zero. /// /// Extract a single signed integer value (sign extending if required) /// and update the offset pointed to by \a offset_ptr. The size of /// the extracted integer is specified by the \a byte_size argument. /// \a byte_size should have a value greater than or equal to one /// and less than or equal to eight since the return value is 64 /// bits wide. Any \a byte_size values less than 1 or greater than /// 8 will result in nothing being extracted, and zero being returned. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[in] byte_size /// The size in bytes of the integer to extract. /// /// @param[in] bitfield_bit_size /// The size in bits of the bitfield value to extract, or zero /// to just extract the entire integer value. /// /// @param[in] bitfield_bit_offset /// The bit offset of the bitfield value in the extracted /// integer. For little-endian data, this is the offset of /// the LSB of the bitfield from the LSB of the integer. /// For big-endian data, this is the offset of the MSB of the /// bitfield from the MSB of the integer. /// /// @return /// The signed bitfield integer value that was extracted, or /// zero on failure. //------------------------------------------------------------------ int64_t GetMaxS64Bitfield(lldb::offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const; //------------------------------------------------------------------ /// Extract an pointer from \a *offset_ptr. /// /// Extract a single pointer from the data and update the offset /// pointed to by \a offset_ptr. The size of the extracted pointer /// comes from the \a m_addr_size member variable and should be /// set correctly prior to extracting any pointer values. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted pointer value as a 64 integer. //------------------------------------------------------------------ uint64_t GetPointer(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Get the current byte order value. /// /// @return /// The current byte order value from this object's internal /// state. //------------------------------------------------------------------ lldb::ByteOrder GetByteOrder() const { return m_byte_order; } //------------------------------------------------------------------ /// Extract a uint8_t value from \a *offset_ptr. /// /// Extract a single uint8_t from the binary data at the offset /// pointed to by \a offset_ptr, and advance the offset on success. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted uint8_t value. //------------------------------------------------------------------ uint8_t GetU8(lldb::offset_t *offset_ptr) const; uint8_t GetU8_unchecked(lldb::offset_t *offset_ptr) const { uint8_t val = m_start[*offset_ptr]; *offset_ptr += 1; return val; } uint16_t GetU16_unchecked(lldb::offset_t *offset_ptr) const; uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const; uint64_t GetU64_unchecked(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract \a count uint8_t values from \a *offset_ptr. /// /// Extract \a count uint8_t values from the binary data at the /// offset pointed to by \a offset_ptr, and advance the offset on /// success. The extracted values are copied into \a dst. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[out] dst /// A buffer to copy \a count uint8_t values into. \a dst must /// be large enough to hold all requested data. /// /// @param[in] count /// The number of uint8_t values to extract. /// /// @return /// \a dst if all values were properly extracted and copied, /// nullptr otherwise. //------------------------------------------------------------------ void *GetU8(lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; //------------------------------------------------------------------ /// Extract a uint16_t value from \a *offset_ptr. /// /// Extract a single uint16_t from the binary data at the offset /// pointed to by \a offset_ptr, and update the offset on success. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted uint16_t value. //------------------------------------------------------------------ uint16_t GetU16(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract \a count uint16_t values from \a *offset_ptr. /// /// Extract \a count uint16_t values from the binary data at the /// offset pointed to by \a offset_ptr, and advance the offset on /// success. The extracted values are copied into \a dst. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[out] dst /// A buffer to copy \a count uint16_t values into. \a dst must /// be large enough to hold all requested data. /// /// @param[in] count /// The number of uint16_t values to extract. /// /// @return /// \a dst if all values were properly extracted and copied, /// nullptr otherwise. //------------------------------------------------------------------ void *GetU16(lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; //------------------------------------------------------------------ /// Extract a uint32_t value from \a *offset_ptr. /// /// Extract a single uint32_t from the binary data at the offset /// pointed to by \a offset_ptr, and update the offset on success. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted uint32_t value. //------------------------------------------------------------------ uint32_t GetU32(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract \a count uint32_t values from \a *offset_ptr. /// /// Extract \a count uint32_t values from the binary data at the /// offset pointed to by \a offset_ptr, and advance the offset on /// success. The extracted values are copied into \a dst. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[out] dst /// A buffer to copy \a count uint32_t values into. \a dst must /// be large enough to hold all requested data. /// /// @param[in] count /// The number of uint32_t values to extract. /// /// @return /// \a dst if all values were properly extracted and copied, /// nullptr otherwise. //------------------------------------------------------------------ void *GetU32(lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; //------------------------------------------------------------------ /// Extract a uint64_t value from \a *offset_ptr. /// /// Extract a single uint64_t from the binary data at the offset /// pointed to by \a offset_ptr, and update the offset on success. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted uint64_t value. //------------------------------------------------------------------ uint64_t GetU64(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract \a count uint64_t values from \a *offset_ptr. /// /// Extract \a count uint64_t values from the binary data at the /// offset pointed to by \a offset_ptr, and advance the offset on /// success. The extracted values are copied into \a dst. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @param[out] dst /// A buffer to copy \a count uint64_t values into. \a dst must /// be large enough to hold all requested data. /// /// @param[in] count /// The number of uint64_t values to extract. /// /// @return /// \a dst if all values were properly extracted and copied, /// nullptr otherwise. //------------------------------------------------------------------ void *GetU64(lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; //------------------------------------------------------------------ /// Extract a signed LEB128 value from \a *offset_ptr. /// /// Extracts an signed LEB128 number from this object's data /// starting at the offset pointed to by \a offset_ptr. The offset /// pointed to by \a offset_ptr will be updated with the offset of /// the byte following the last extracted byte. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted signed integer value. //------------------------------------------------------------------ int64_t GetSLEB128(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Extract a unsigned LEB128 value from \a *offset_ptr. /// /// Extracts an unsigned LEB128 number from this object's data /// starting at the offset pointed to by \a offset_ptr. The offset /// pointed to by \a offset_ptr will be updated with the offset of /// the byte following the last extracted byte. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return /// The extracted unsigned integer value. //------------------------------------------------------------------ uint64_t GetULEB128(lldb::offset_t *offset_ptr) const; lldb::DataBufferSP &GetSharedDataBuffer() { return m_data_sp; } //------------------------------------------------------------------ /// Peek at a C string at \a offset. /// /// Peeks at a string in the contained data. No verification is done /// to make sure the entire string lies within the bounds of this /// object's data, only \a offset is verified to be a valid offset. /// /// @param[in] offset /// An offset into the data. /// /// @return /// A non-nullptr C string pointer if \a offset is a valid offset, /// nullptr otherwise. //------------------------------------------------------------------ const char *PeekCStr(lldb::offset_t offset) const; //------------------------------------------------------------------ /// Peek at a bytes at \a offset. /// /// Returns a pointer to \a length bytes at \a offset as long as /// there are \a length bytes available starting at \a offset. /// /// @return /// A non-nullptr data pointer if \a offset is a valid offset and /// there are \a length bytes available at that offset, nullptr /// otherwise. //------------------------------------------------------------------ const uint8_t *PeekData(lldb::offset_t offset, lldb::offset_t length) const { if (ValidOffsetForDataOfSize(offset, length)) return m_start + offset; return nullptr; } //------------------------------------------------------------------ /// Set the address byte size. /// /// Set the size in bytes that will be used when extracting any /// address and pointer values from data contained in this object. /// /// @param[in] addr_size /// The size in bytes to use when extracting addresses. //------------------------------------------------------------------ void SetAddressByteSize(uint32_t addr_size) { #ifdef LLDB_CONFIGURATION_DEBUG assert(addr_size == 4 || addr_size == 8); #endif m_addr_size = addr_size; } //------------------------------------------------------------------ /// Set data with a buffer that is caller owned. /// /// Use data that is owned by the caller when extracting values. /// The data must stay around as long as this object, or any object /// that copies a subset of this object's data, is valid. If \a /// bytes is nullptr, or \a length is zero, this object will contain /// no data. /// /// @param[in] bytes /// A pointer to caller owned data. /// /// @param[in] length /// The length in bytes of \a bytes. /// /// @param[in] byte_order /// A byte order of the data that we are extracting from. /// /// @return /// The number of bytes that this object now contains. //------------------------------------------------------------------ lldb::offset_t SetData(const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order); //------------------------------------------------------------------ /// Adopt a subset of \a data. /// /// Set this object's data to be a subset of the data bytes in \a /// data. If \a data contains shared data, then a reference to the /// shared data will be added to ensure the shared data stays around /// as long as any objects have references to the shared data. The /// byte order and the address size settings are copied from \a /// data. If \a offset is not a valid offset in \a data, then no /// reference to the shared data will be added. If there are not /// \a length bytes available in \a data starting at \a offset, /// the length will be truncated to contains as many bytes as /// possible. /// /// @param[in] data /// Another DataExtractor object that contains data. /// /// @param[in] offset /// The offset into \a data at which the subset starts. /// /// @param[in] length /// The length in bytes of the subset of \a data. /// /// @return /// The number of bytes that this object now contains. //------------------------------------------------------------------ lldb::offset_t SetData(const DataExtractor &data, lldb::offset_t offset, lldb::offset_t length); //------------------------------------------------------------------ /// Adopt a subset of shared data in \a data_sp. /// /// Copies the data shared pointer which adds a reference to the /// contained in \a data_sp. The shared data reference is reference /// counted to ensure the data lives as long as anyone still has a /// valid shared pointer to the data in \a data_sp. The byte order /// and address byte size settings remain the same. If /// \a offset is not a valid offset in \a data_sp, then no reference /// to the shared data will be added. If there are not \a length /// bytes available in \a data starting at \a offset, the length /// will be truncated to contains as many bytes as possible. /// /// @param[in] data_sp /// A shared pointer to data. /// /// @param[in] offset /// The offset into \a data_sp at which the subset starts. /// /// @param[in] length /// The length in bytes of the subset of \a data_sp. /// /// @return /// The number of bytes that this object now contains. //------------------------------------------------------------------ lldb::offset_t SetData(const lldb::DataBufferSP &data_sp, lldb::offset_t offset = 0, lldb::offset_t length = LLDB_INVALID_OFFSET); //------------------------------------------------------------------ /// Set the byte_order value. /// /// Sets the byte order of the data to extract. Extracted values /// will be swapped if necessary when decoding. /// /// @param[in] byte_order /// The byte order value to use when extracting data. //------------------------------------------------------------------ void SetByteOrder(lldb::ByteOrder byte_order) { m_byte_order = byte_order; } //------------------------------------------------------------------ /// Skip an LEB128 number at \a *offset_ptr. /// /// Skips a LEB128 number (signed or unsigned) from this object's /// data starting at the offset pointed to by \a offset_ptr. The /// offset pointed to by \a offset_ptr will be updated with the /// offset of the byte following the last extracted byte. /// /// @param[in,out] offset_ptr /// A pointer to an offset within the data that will be advanced /// by the appropriate number of bytes if the value is extracted /// correctly. If the offset is out of bounds or there are not /// enough bytes to extract this value, the offset will be left /// unmodified. /// /// @return // The number of bytes consumed during the extraction. //------------------------------------------------------------------ uint32_t Skip_LEB128(lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ /// Test the validity of \a offset. /// /// @return /// \b true if \a offset is a valid offset into the data in this /// object, \b false otherwise. //------------------------------------------------------------------ bool ValidOffset(lldb::offset_t offset) const { return offset < GetByteSize(); } //------------------------------------------------------------------ /// Test the availability of \a length bytes of data from \a offset. /// /// @return /// \b true if \a offset is a valid offset and there are \a /// length bytes available at that offset, \b false otherwise. //------------------------------------------------------------------ bool ValidOffsetForDataOfSize(lldb::offset_t offset, lldb::offset_t length) const { return length <= BytesLeft(offset); } size_t Copy(DataExtractor &dest_data) const; bool Append(DataExtractor &rhs); bool Append(void *bytes, lldb::offset_t length); lldb::offset_t BytesLeft(lldb::offset_t offset) const { const lldb::offset_t size = GetByteSize(); if (size > offset) return size - offset; return 0; } void Checksum(llvm::SmallVectorImpl &dest, uint64_t max_data = 0); protected: //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ const uint8_t *m_start; ///< A pointer to the first byte of data. const uint8_t *m_end; ///< A pointer to the byte that is past the end of the data. lldb::ByteOrder m_byte_order; ///< The byte order of the data we are extracting from. uint32_t m_addr_size; ///< The address size to use when extracting pointers or /// addresses mutable lldb::DataBufferSP m_data_sp; ///< The shared pointer to data that can /// be shared among multiple instances const uint32_t m_target_byte_size; }; } // namespace lldb_private #endif // liblldb_DataExtractor_h_ Index: vendor/lldb/dist/include/lldb/lldb-private-forward.h =================================================================== --- vendor/lldb/dist/include/lldb/lldb-private-forward.h (revision 321193) +++ vendor/lldb/dist/include/lldb/lldb-private-forward.h (revision 321194) @@ -1,44 +1,40 @@ //===-- lldb-private-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_private_forward_h_ #define LLDB_lldb_private_forward_h_ #if defined(__cplusplus) #include namespace lldb_private { // --------------------------------------------------------------- // Class forward decls. // --------------------------------------------------------------- class NativeBreakpoint; class NativeBreakpointList; class NativeProcessProtocol; class NativeRegisterContext; class NativeThreadProtocol; class ResumeActionList; class UnixSignals; // --------------------------------------------------------------- // SP/WP decls. // --------------------------------------------------------------- typedef std::shared_ptr NativeBreakpointSP; -typedef std::shared_ptr - NativeProcessProtocolSP; -typedef std::weak_ptr - NativeProcessProtocolWP; typedef std::shared_ptr NativeRegisterContextSP; typedef std::shared_ptr NativeThreadProtocolSP; } #endif // #if defined(__cplusplus) #endif // #ifndef LLDB_lldb_private_forward_h_ Index: vendor/lldb/dist/include/lldb/lldb-types.h =================================================================== --- vendor/lldb/dist/include/lldb/lldb-types.h (revision 321193) +++ vendor/lldb/dist/include/lldb/lldb-types.h (revision 321194) @@ -1,120 +1,96 @@ //===-- lldb-types.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_lldb_types_h_ #define LLDB_lldb_types_h_ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" -#include -#include #include //---------------------------------------------------------------------- // All host systems must define: -// lldb::condition_t The native condition type (or a substitute class) -// for conditions on the host system. -// lldb::mutex_t The native mutex type for mutex objects on the host -// system. // lldb::thread_t The native thread type for spawned threads on the // system // lldb::thread_arg_t The type of the one any only thread creation // argument for the host system // lldb::thread_result_t The return type that gets returned when a thread // finishes. // lldb::thread_func_t The function prototype used to spawn a thread on the // host system. // #define LLDB_INVALID_PROCESS_ID ... // #define LLDB_INVALID_THREAD_ID ... // #define LLDB_INVALID_HOST_THREAD ... -// #define IS_VALID_LLDB_HOST_THREAD ... //---------------------------------------------------------------------- // TODO: Add a bunch of ifdefs to determine the host system and what // things should be defined. Currently MacOSX is being assumed by default // since that is what lldb was first developed for. -#ifndef _MSC_VER -#include -#include -#endif - #ifdef _WIN32 #include namespace lldb { -typedef void *mutex_t; -typedef void *condition_t; typedef void *rwlock_t; typedef void *process_t; // Process type is HANDLE typedef void *thread_t; // Host thread type typedef void *file_t; // Host file type -typedef void *pipe_t; // Host pipe type typedef unsigned int __w64 socket_t; // Host socket type -typedef uint32_t thread_key_t; typedef void *thread_arg_t; // Host thread argument type typedef unsigned thread_result_t; // Host thread result type typedef thread_result_t (*thread_func_t)(void *); // Host thread function type } #else #include namespace lldb { //---------------------------------------------------------------------- // MacOSX Types //---------------------------------------------------------------------- -typedef ::pthread_mutex_t mutex_t; -typedef pthread_cond_t condition_t; typedef pthread_rwlock_t rwlock_t; typedef uint64_t process_t; // Process type is just a pid. typedef pthread_t thread_t; // Host thread type typedef int file_t; // Host file type -typedef int pipe_t; // Host pipe type typedef int socket_t; // Host socket type -typedef pthread_key_t thread_key_t; typedef void *thread_arg_t; // Host thread argument type typedef void *thread_result_t; // Host thread result type typedef void *(*thread_func_t)(void *); // Host thread function type } // namespace lldb #endif namespace lldb { typedef void (*LogOutputCallback)(const char *, void *baton); typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); typedef bool (*CommandOverrideCallbackWithResult)( void *baton, const char **argv, lldb_private::CommandReturnObject &result); typedef bool (*ExpressionCancelCallback)(ExpressionEvaluationPhase phase, void *baton); } #define LLDB_INVALID_PROCESS ((lldb::process_t)-1) #define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL) -#define IS_VALID_LLDB_HOST_THREAD(t) ((t) != LLDB_INVALID_HOST_THREAD) - -#define LLDB_INVALID_HOST_TIME \ - { 0, 0 } namespace lldb { typedef uint64_t addr_t; typedef uint64_t user_id_t; typedef uint64_t pid_t; typedef uint64_t tid_t; typedef uint64_t offset_t; typedef int32_t break_id_t; typedef int32_t watch_id_t; typedef void *opaque_compiler_type_t; typedef uint64_t queue_id_t; } #endif // LLDB_lldb_types_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 321193) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 321194) @@ -1,127 +1,103 @@ """ Test stopping at a breakpoint in an expression, and unwinding from there. """ from __future__ import print_function import unittest2 import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class UnwindFromExpressionTest(TestBase): mydir = TestBase.compute_mydir(__file__) main_spec = lldb.SBFileSpec("main.cpp", False) def build_and_run_to_bkpt(self): self.build() - exe = os.path.join(os.getcwd(), "a.out") - - target = self.dbg.CreateTarget(exe) - self.assertTrue(target, VALID_TARGET) - - # Create the breakpoint. - breakpoint = target.BreakpointCreateBySourceRegex( - "// Set a breakpoint here to get started", self.main_spec) - self.assertTrue(breakpoint, VALID_BREAKPOINT) - - # Launch the process, and do not stop at the entry point. - process = target.LaunchSimple( - None, None, self.get_process_working_directory()) - - if not process: - self.fail("SBTarget.LaunchProcess() failed") - - if process.GetState() != lldb.eStateStopped: - self.fail("Process should be in the 'stopped' state, " - "instead the actual state is: '%s'" % - lldbutil.state_type_to_str(process.GetState())) - - self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( - process, breakpoint) - self.assertIsNotNone( - self.thread, "Expected one thread to be stopped at the breakpoint") + (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "// Set a breakpoint here to get started", self.main_spec) # Next set a breakpoint in this function, set up Expression options to stop on # breakpoint hits, and call the function. self.fun_bkpt = self.target().BreakpointCreateBySourceRegex( "// Stop inside the function here.", self.main_spec) self.assertTrue(self.fun_bkpt, VALID_BREAKPOINT) @no_debug_info_test @expectedFailureAll(bugnumber="llvm.org/pr33164") def test_conditional_bktp(self): """ Test conditional breakpoint handling in the IgnoreBreakpoints = False case """ self.build_and_run_to_bkpt() self.fun_bkpt.SetCondition("0") # Should not get hit options = lldb.SBExpressionOptions() options.SetIgnoreBreakpoints(False) options.SetUnwindOnError(False) main_frame = self.thread.GetFrameAtIndex(0) val = main_frame.EvaluateExpression("second_function(47)", options) self.assertTrue( val.GetError().Success(), "We did complete the execution.") self.assertEquals(47, val.GetValueAsSigned()) @add_test_categories(['pyapi']) @expectedFailureAll(oslist=["windows"]) def test_unwind_expression(self): """Test unwinding from an expression.""" self.build_and_run_to_bkpt() # Run test with varying one thread timeouts to also test the halting # logic in the IgnoreBreakpoints = False case self.do_unwind_test(self.thread, self.fun_bkpt, 1000) self.do_unwind_test(self.thread, self.fun_bkpt, 100000) def do_unwind_test(self, thread, bkpt, timeout): # # Use Python API to evaluate expressions while stopped in a stack frame. # main_frame = thread.GetFrameAtIndex(0) options = lldb.SBExpressionOptions() options.SetIgnoreBreakpoints(False) options.SetUnwindOnError(False) options.SetOneThreadTimeoutInMicroSeconds(timeout) val = main_frame.EvaluateExpression("a_function_to_call()", options) self.assertTrue( val.GetError().Fail(), "We did not complete the execution.") error_str = val.GetError().GetCString() self.assertTrue( "Execution was interrupted, reason: breakpoint" in error_str, "And the reason was right.") thread = lldbutil.get_one_thread_stopped_at_breakpoint( self.process(), bkpt) self.assertTrue( thread.IsValid(), "We are indeed stopped at our breakpoint") # Now unwind the expression, and make sure we got back to where we # started. error = thread.UnwindInnermostExpression() self.assertTrue(error.Success(), "We succeeded in unwinding") cur_frame = thread.GetFrameAtIndex(0) self.assertTrue( cur_frame.IsEqual(main_frame), "We got back to the main frame.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py (revision 321193) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py (revision 321194) @@ -1,82 +1,49 @@ """ Test that breakpoints set on a bad address say they are bad. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class BadAddressBreakpointTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def test_bad_address_breakpoints(self): """Test that breakpoints set on a bad address say they are bad.""" self.build() self.address_breakpoints() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def address_breakpoints(self): """Test that breakpoints set on a bad address say they are bad.""" - exe = os.path.join(os.getcwd(), "a.out") - # Create a target by the debugger. - target = self.dbg.CreateTarget(exe) - self.assertTrue(target, VALID_TARGET) + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Set a breakpoint here", lldb.SBFileSpec("main.c")) - # Now create a breakpoint on main.c by name 'c'. - breakpoint = target.BreakpointCreateBySourceRegex( - "Set a breakpoint here", lldb.SBFileSpec("main.c")) - self.assertTrue(breakpoint and - breakpoint.GetNumLocations() == 1, - VALID_BREAKPOINT) - - # Get the breakpoint location from breakpoint after we verified that, - # indeed, it has one location. - location = breakpoint.GetLocationAtIndex(0) - self.assertTrue(location and - location.IsEnabled(), - VALID_BREAKPOINT_LOCATION) - - launch_info = lldb.SBLaunchInfo(None) - - error = lldb.SBError() - - process = target.Launch(launch_info, error) - self.assertTrue(process, PROCESS_IS_VALID) - - # Did we hit our breakpoint? - from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint - threads = get_threads_stopped_at_breakpoint(process, breakpoint) - self.assertTrue( - len(threads) == 1, - "There should be a thread stopped at our breakpoint") - - # The hit count for the breakpoint should be 1. - self.assertTrue(breakpoint.GetHitCount() == 1) - # Now see if we can read from 0. If I can't do that, I don't have a good way to know # what an illegal address is... - error.Clear() + error = lldb.SBError() ptr = process.ReadPointerFromMemory(0x0, error) if not error.Success(): bkpt = target.BreakpointCreateByAddress(0x0) for bp_loc in bkpt: self.assertTrue(bp_loc.IsResolved() == False) else: self.fail( "Could not find an illegal address at which to set a bad breakpoint.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoints/TestConsecutiveBreakpoints.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoints/TestConsecutiveBreakpoints.py (revision 321193) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoints/TestConsecutiveBreakpoints.py (revision 321194) @@ -1,124 +1,104 @@ """ Test that we handle breakpoints on consecutive instructions correctly. """ from __future__ import print_function import unittest2 import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ConsecutiveBreakpointsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def prepare_test(self): self.build() - exe = os.path.join(os.getcwd(), "a.out") - # Create a target by the debugger. - self.target = self.dbg.CreateTarget(exe) - self.assertTrue(self.target, VALID_TARGET) - - breakpoint1 = self.target.BreakpointCreateBySourceRegex( - "Set breakpoint here", lldb.SBFileSpec("main.cpp")) - self.assertTrue( - breakpoint1 and breakpoint1.GetNumLocations() == 1, - VALID_BREAKPOINT) - - # Now launch the process, and do not stop at entry point. - self.process = self.target.LaunchSimple( - None, None, self.get_process_working_directory()) - self.assertIsNotNone(self.process, PROCESS_IS_VALID) - - # We should be stopped at the first breakpoint - self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( - self.process, breakpoint1) - self.assertIsNotNone( - self.thread, - "Expected one thread to be stopped at breakpoint 1") + (self.target, self.process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set breakpoint here", lldb.SBFileSpec("main.cpp")) # Set breakpoint to the next instruction frame = self.thread.GetFrameAtIndex(0) address = frame.GetPCAddress() instructions = self.target.ReadInstructions(address, 2) self.assertTrue(len(instructions) == 2) self.bkpt_address = instructions[1].GetAddress() self.breakpoint2 = self.target.BreakpointCreateByAddress( self.bkpt_address.GetLoadAddress(self.target)) self.assertTrue( self.breakpoint2 and self.breakpoint2.GetNumLocations() == 1, VALID_BREAKPOINT) def finish_test(self): # Run the process until termination self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateExited) @no_debug_info_test def test_continue(self): """Test that continue stops at the second breakpoint.""" self.prepare_test() self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateStopped) # We should be stopped at the second breakpoint self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( self.process, self.breakpoint2) self.assertIsNotNone( self.thread, "Expected one thread to be stopped at breakpoint 2") self.finish_test() @no_debug_info_test def test_single_step(self): """Test that single step stops at the second breakpoint.""" self.prepare_test() step_over = False self.thread.StepInstruction(step_over) self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals( self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress( self.target), self.bkpt_address.GetLoadAddress( self.target)) self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( self.process, self.breakpoint2) self.assertIsNotNone( self.thread, "Expected one thread to be stopped at breakpoint 2") self.finish_test() @no_debug_info_test def test_single_step_thread_specific(self): """Test that single step stops, even though the second breakpoint is not valid.""" self.prepare_test() # Choose a thread other than the current one. A non-existing thread is # fine. thread_index = self.process.GetNumThreads() + 1 self.assertFalse(self.process.GetThreadAtIndex(thread_index).IsValid()) self.breakpoint2.SetThreadIndex(thread_index) step_over = False self.thread.StepInstruction(step_over) self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals( self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress( self.target), self.bkpt_address.GetLoadAddress( self.target)) self.assertEquals( self.thread.GetStopReason(), lldb.eStopReasonPlanComplete, "Stop reason should be 'plan complete'") self.finish_test() Index: vendor/lldb/dist/source/Host/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Host/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/source/Host/CMakeLists.txt (revision 321194) @@ -1,173 +1,177 @@ macro(add_host_subdirectory group) list(APPEND HOST_SOURCES ${ARGN}) source_group(${group} FILES ${ARGN}) endmacro() add_host_subdirectory(common common/File.cpp common/FileCache.cpp common/FileSystem.cpp common/GetOptInc.cpp common/Host.cpp common/HostInfoBase.cpp common/HostNativeThreadBase.cpp common/HostProcess.cpp common/HostThread.cpp common/LockFileBase.cpp common/MainLoop.cpp common/MonitoringProcessLauncher.cpp common/NativeBreakpoint.cpp common/NativeBreakpointList.cpp common/NativeWatchpointList.cpp common/NativeProcessProtocol.cpp common/NativeRegisterContext.cpp common/NativeThreadProtocol.cpp common/OptionParser.cpp common/PipeBase.cpp common/ProcessRunLock.cpp common/PseudoTerminal.cpp common/Socket.cpp common/SocketAddress.cpp common/SoftwareBreakpoint.cpp common/StringConvert.cpp common/Symbols.cpp common/TCPSocket.cpp common/Terminal.cpp common/ThreadLauncher.cpp common/XML.cpp common/UDPSocket.cpp ) # Keep track of whether we want to provide a define for the # Python's architecture-specific lib path (i.e. where a # Python lldb module would go). set (get_python_libdir 0) if (NOT LLDB_DISABLE_LIBEDIT) add_host_subdirectory(common common/Editline.cpp ) endif() add_host_subdirectory(posix posix/ConnectionFileDescriptorPosix.cpp ) if(NOT LLDB_DISABLE_PYTHON) list(APPEND LLDB_PLUGINS lldbPluginScriptInterpreterPython) endif() if (CMAKE_SYSTEM_NAME MATCHES "Windows") add_host_subdirectory(windows windows/ConnectionGenericFileWindows.cpp windows/EditLineWin.cpp windows/FileSystem.cpp windows/Host.cpp windows/HostInfoWindows.cpp windows/HostProcessWindows.cpp windows/HostThreadWindows.cpp windows/LockFileWindows.cpp windows/PipeWindows.cpp windows/ProcessLauncherWindows.cpp windows/ProcessRunLock.cpp windows/Windows.cpp ) else() if (NOT LLDB_DISABLE_PYTHON) # We'll grab the arch-specific python libdir on POSIX systems. set (get_python_libdir 1) endif() add_host_subdirectory(posix posix/DomainSocket.cpp posix/FileSystem.cpp posix/HostInfoPosix.cpp posix/HostProcessPosix.cpp posix/HostThreadPosix.cpp posix/LockFilePosix.cpp posix/PipePosix.cpp posix/ProcessLauncherPosixFork.cpp ) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) add_host_subdirectory(macosx macosx/Host.mm macosx/HostInfoMacOSX.mm macosx/HostThreadMacOSX.mm macosx/Symbols.cpp macosx/cfcpp/CFCBundle.cpp macosx/cfcpp/CFCData.cpp macosx/cfcpp/CFCMutableArray.cpp macosx/cfcpp/CFCMutableDictionary.cpp macosx/cfcpp/CFCMutableSet.cpp macosx/cfcpp/CFCString.cpp ) + if(IOS) + set_property(SOURCE macosx/Host.mm APPEND PROPERTY + COMPILE_DEFINITIONS "NO_XPC_SERVICES=1") + endif() elseif (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") add_host_subdirectory(linux linux/AbstractSocket.cpp linux/Host.cpp linux/HostInfoLinux.cpp linux/LibcGlue.cpp linux/Support.cpp ) if (CMAKE_SYSTEM_NAME MATCHES "Android") add_host_subdirectory(android android/HostInfoAndroid.cpp android/LibcGlue.cpp ) endif() elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") add_host_subdirectory(freebsd freebsd/Host.cpp freebsd/HostInfoFreeBSD.cpp ) elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") add_host_subdirectory(netbsd netbsd/Host.cpp netbsd/HostInfoNetBSD.cpp ) elseif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") add_host_subdirectory(openbsd openbsd/Host.cpp openbsd/HostInfoOpenBSD.cpp ) endif() endif() if (${get_python_libdir}) # Call a python script to gather the arch-specific libdir for # modules like the lldb module. execute_process( COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py RESULT_VARIABLE get_libdir_status OUTPUT_VARIABLE relative_libdir ) if (get_libdir_status EQUAL 0) add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}") endif() endif() if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") set(EXTRA_LIBS kvm) endif () add_lldb_library(lldbHost ${HOST_SOURCES} LINK_LIBS lldbCore lldbInterpreter lldbSymbol lldbTarget lldbUtility ${LLDB_PLUGINS} ${EXTRA_LIBS} LINK_COMPONENTS Support ) Index: vendor/lldb/dist/source/Host/common/File.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/File.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/File.cpp (revision 321194) @@ -1,841 +1,842 @@ //===-- File.cpp ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/File.h" #include #include #include #include #include #ifdef _WIN32 #include "lldb/Host/windows/windows.h" #else #include #include #include +#include #endif #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" // for llvm::sys::Process::FileDescriptorHasColors() #include "lldb/Host/Config.h" #include "lldb/Host/Host.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" using namespace lldb; using namespace lldb_private; static const char *GetStreamOpenModeFromOptions(uint32_t options) { if (options & File::eOpenOptionAppend) { if (options & File::eOpenOptionRead) { if (options & File::eOpenOptionCanCreateNewOnly) return "a+x"; else return "a+"; } else if (options & File::eOpenOptionWrite) { if (options & File::eOpenOptionCanCreateNewOnly) return "ax"; else return "a"; } } else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite) { if (options & File::eOpenOptionCanCreate) { if (options & File::eOpenOptionCanCreateNewOnly) return "w+x"; else return "w+"; } else return "r+"; } else if (options & File::eOpenOptionRead) { return "r"; } else if (options & File::eOpenOptionWrite) { return "w"; } return NULL; } int File::kInvalidDescriptor = -1; FILE *File::kInvalidStream = NULL; File::File(const char *path, uint32_t options, uint32_t permissions) : IOObject(eFDTypeFile, false), m_descriptor(kInvalidDescriptor), m_stream(kInvalidStream), m_options(), m_own_stream(false), m_is_interactive(eLazyBoolCalculate), m_is_real_terminal(eLazyBoolCalculate) { Open(path, options, permissions); } File::File(const FileSpec &filespec, uint32_t options, uint32_t permissions) : IOObject(eFDTypeFile, false), m_descriptor(kInvalidDescriptor), m_stream(kInvalidStream), m_options(0), m_own_stream(false), m_is_interactive(eLazyBoolCalculate), m_is_real_terminal(eLazyBoolCalculate) { if (filespec) { Open(filespec.GetPath().c_str(), options, permissions); } } File::~File() { Close(); } int File::GetDescriptor() const { if (DescriptorIsValid()) return m_descriptor; // Don't open the file descriptor if we don't need to, just get it from the // stream if we have one. if (StreamIsValid()) { #if defined(LLVM_ON_WIN32) return _fileno(m_stream); #else return fileno(m_stream); #endif } // Invalid descriptor and invalid stream, return invalid descriptor. return kInvalidDescriptor; } IOObject::WaitableHandle File::GetWaitableHandle() { return m_descriptor; } void File::SetDescriptor(int fd, bool transfer_ownership) { if (IsValid()) Close(); m_descriptor = fd; m_should_close_fd = transfer_ownership; } FILE *File::GetStream() { if (!StreamIsValid()) { if (DescriptorIsValid()) { const char *mode = GetStreamOpenModeFromOptions(m_options); if (mode) { if (!m_should_close_fd) { // We must duplicate the file descriptor if we don't own it because // when you call fdopen, the stream will own the fd #ifdef _WIN32 m_descriptor = ::_dup(GetDescriptor()); #else m_descriptor = dup(GetDescriptor()); #endif m_should_close_fd = true; } m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor, mode); // If we got a stream, then we own the stream and should no // longer own the descriptor because fclose() will close it for us if (m_stream) { m_own_stream = true; m_should_close_fd = false; } } } } return m_stream; } void File::SetStream(FILE *fh, bool transfer_ownership) { if (IsValid()) Close(); m_stream = fh; m_own_stream = transfer_ownership; } static int DoOpen(const char *path, int flags, int mode) { #ifdef _MSC_VER std::wstring wpath; if (!llvm::ConvertUTF8toWide(path, wpath)) return -1; int result; ::_wsopen_s(&result, wpath.c_str(), flags, _SH_DENYNO, mode); return result; #else return ::open(path, flags, mode); #endif } Status File::Open(const char *path, uint32_t options, uint32_t permissions) { Status error; if (IsValid()) Close(); int oflag = 0; const bool read = options & eOpenOptionRead; const bool write = options & eOpenOptionWrite; if (write) { if (read) oflag |= O_RDWR; else oflag |= O_WRONLY; if (options & eOpenOptionAppend) oflag |= O_APPEND; if (options & eOpenOptionTruncate) oflag |= O_TRUNC; if (options & eOpenOptionCanCreate) oflag |= O_CREAT; if (options & eOpenOptionCanCreateNewOnly) oflag |= O_CREAT | O_EXCL; } else if (read) { oflag |= O_RDONLY; #ifndef _WIN32 if (options & eOpenOptionDontFollowSymlinks) oflag |= O_NOFOLLOW; #endif } #ifndef _WIN32 if (options & eOpenOptionNonBlocking) oflag |= O_NONBLOCK; if (options & eOpenOptionCloseOnExec) oflag |= O_CLOEXEC; #else oflag |= O_BINARY; #endif mode_t mode = 0; if (oflag & O_CREAT) { if (permissions & lldb::eFilePermissionsUserRead) mode |= S_IRUSR; if (permissions & lldb::eFilePermissionsUserWrite) mode |= S_IWUSR; if (permissions & lldb::eFilePermissionsUserExecute) mode |= S_IXUSR; if (permissions & lldb::eFilePermissionsGroupRead) mode |= S_IRGRP; if (permissions & lldb::eFilePermissionsGroupWrite) mode |= S_IWGRP; if (permissions & lldb::eFilePermissionsGroupExecute) mode |= S_IXGRP; if (permissions & lldb::eFilePermissionsWorldRead) mode |= S_IROTH; if (permissions & lldb::eFilePermissionsWorldWrite) mode |= S_IWOTH; if (permissions & lldb::eFilePermissionsWorldExecute) mode |= S_IXOTH; } m_descriptor = llvm::sys::RetryAfterSignal(-1, DoOpen, path, oflag, mode); if (!DescriptorIsValid()) error.SetErrorToErrno(); else { m_should_close_fd = true; m_options = options; } return error; } uint32_t File::GetPermissions(const FileSpec &file_spec, Status &error) { if (file_spec) { error.Clear(); auto Perms = llvm::sys::fs::getPermissions(file_spec.GetPath()); if (Perms) return *Perms; error = Status(Perms.getError()); return 0; } else error.SetErrorString("empty file spec"); return 0; } uint32_t File::GetPermissions(Status &error) const { int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { struct stat file_stats; if (::fstat(fd, &file_stats) == -1) error.SetErrorToErrno(); else { error.Clear(); return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); } } else { error.SetErrorString("invalid file descriptor"); } return 0; } Status File::Close() { Status error; if (StreamIsValid() && m_own_stream) { if (::fclose(m_stream) == EOF) error.SetErrorToErrno(); } if (DescriptorIsValid() && m_should_close_fd) { if (::close(m_descriptor) != 0) error.SetErrorToErrno(); } m_descriptor = kInvalidDescriptor; m_stream = kInvalidStream; m_options = 0; m_own_stream = false; m_should_close_fd = false; m_is_interactive = eLazyBoolCalculate; m_is_real_terminal = eLazyBoolCalculate; return error; } void File::Clear() { m_stream = nullptr; m_descriptor = -1; m_options = 0; m_own_stream = false; m_is_interactive = m_supports_colors = m_is_real_terminal = eLazyBoolCalculate; } Status File::GetFileSpec(FileSpec &file_spec) const { Status error; #ifdef F_GETPATH if (IsValid()) { char path[PATH_MAX]; if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) error.SetErrorToErrno(); else file_spec.SetFile(path, false); } else { error.SetErrorString("invalid file handle"); } #elif defined(__linux__) char proc[64]; char path[PATH_MAX]; if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) error.SetErrorString("cannot resolve file descriptor"); else { ssize_t len; if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) error.SetErrorToErrno(); else { path[len] = '\0'; file_spec.SetFile(path, false); } } #else error.SetErrorString("File::GetFileSpec is not supported on this platform"); #endif if (error.Fail()) file_spec.Clear(); return error; } off_t File::SeekFromStart(off_t offset, Status *error_ptr) { off_t result = 0; if (DescriptorIsValid()) { result = ::lseek(m_descriptor, offset, SEEK_SET); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (StreamIsValid()) { result = ::fseek(m_stream, offset, SEEK_SET); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (error_ptr) { error_ptr->SetErrorString("invalid file handle"); } return result; } off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) { off_t result = -1; if (DescriptorIsValid()) { result = ::lseek(m_descriptor, offset, SEEK_CUR); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (StreamIsValid()) { result = ::fseek(m_stream, offset, SEEK_CUR); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (error_ptr) { error_ptr->SetErrorString("invalid file handle"); } return result; } off_t File::SeekFromEnd(off_t offset, Status *error_ptr) { off_t result = -1; if (DescriptorIsValid()) { result = ::lseek(m_descriptor, offset, SEEK_END); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (StreamIsValid()) { result = ::fseek(m_stream, offset, SEEK_END); if (error_ptr) { if (result == -1) error_ptr->SetErrorToErrno(); else error_ptr->Clear(); } } else if (error_ptr) { error_ptr->SetErrorString("invalid file handle"); } return result; } Status File::Flush() { Status error; if (StreamIsValid()) { if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF) error.SetErrorToErrno(); } else if (!DescriptorIsValid()) { error.SetErrorString("invalid file handle"); } return error; } Status File::Sync() { Status error; if (DescriptorIsValid()) { #ifdef _WIN32 int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); if (err == 0) error.SetErrorToGenericError(); #else if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1) error.SetErrorToErrno(); #endif } else { error.SetErrorString("invalid file handle"); } return error; } #if defined(__APPLE__) // Darwin kernels only can read/write <= INT_MAX bytes #define MAX_READ_SIZE INT_MAX #define MAX_WRITE_SIZE INT_MAX #endif Status File::Read(void *buf, size_t &num_bytes) { Status error; #if defined(MAX_READ_SIZE) if (num_bytes > MAX_READ_SIZE) { uint8_t *p = (uint8_t *)buf; size_t bytes_left = num_bytes; // Init the num_bytes read to zero num_bytes = 0; while (bytes_left > 0) { size_t curr_num_bytes; if (bytes_left > MAX_READ_SIZE) curr_num_bytes = MAX_READ_SIZE; else curr_num_bytes = bytes_left; error = Read(p + num_bytes, curr_num_bytes); // Update how many bytes were read num_bytes += curr_num_bytes; if (bytes_left < curr_num_bytes) bytes_left = 0; else bytes_left -= curr_num_bytes; if (error.Fail()) break; } return error; } #endif ssize_t bytes_read = -1; if (DescriptorIsValid()) { bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes); if (bytes_read == -1) { error.SetErrorToErrno(); num_bytes = 0; } else num_bytes = bytes_read; } else if (StreamIsValid()) { bytes_read = ::fread(buf, 1, num_bytes, m_stream); if (bytes_read == 0) { if (::feof(m_stream)) error.SetErrorString("feof"); else if (::ferror(m_stream)) error.SetErrorString("ferror"); num_bytes = 0; } else num_bytes = bytes_read; } else { num_bytes = 0; error.SetErrorString("invalid file handle"); } return error; } Status File::Write(const void *buf, size_t &num_bytes) { Status error; #if defined(MAX_WRITE_SIZE) if (num_bytes > MAX_WRITE_SIZE) { const uint8_t *p = (const uint8_t *)buf; size_t bytes_left = num_bytes; // Init the num_bytes written to zero num_bytes = 0; while (bytes_left > 0) { size_t curr_num_bytes; if (bytes_left > MAX_WRITE_SIZE) curr_num_bytes = MAX_WRITE_SIZE; else curr_num_bytes = bytes_left; error = Write(p + num_bytes, curr_num_bytes); // Update how many bytes were read num_bytes += curr_num_bytes; if (bytes_left < curr_num_bytes) bytes_left = 0; else bytes_left -= curr_num_bytes; if (error.Fail()) break; } return error; } #endif ssize_t bytes_written = -1; if (DescriptorIsValid()) { bytes_written = llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes); if (bytes_written == -1) { error.SetErrorToErrno(); num_bytes = 0; } else num_bytes = bytes_written; } else if (StreamIsValid()) { bytes_written = ::fwrite(buf, 1, num_bytes, m_stream); if (bytes_written == 0) { if (::feof(m_stream)) error.SetErrorString("feof"); else if (::ferror(m_stream)) error.SetErrorString("ferror"); num_bytes = 0; } else num_bytes = bytes_written; } else { num_bytes = 0; error.SetErrorString("invalid file handle"); } return error; } Status File::Read(void *buf, size_t &num_bytes, off_t &offset) { Status error; #if defined(MAX_READ_SIZE) if (num_bytes > MAX_READ_SIZE) { uint8_t *p = (uint8_t *)buf; size_t bytes_left = num_bytes; // Init the num_bytes read to zero num_bytes = 0; while (bytes_left > 0) { size_t curr_num_bytes; if (bytes_left > MAX_READ_SIZE) curr_num_bytes = MAX_READ_SIZE; else curr_num_bytes = bytes_left; error = Read(p + num_bytes, curr_num_bytes, offset); // Update how many bytes were read num_bytes += curr_num_bytes; if (bytes_left < curr_num_bytes) bytes_left = 0; else bytes_left -= curr_num_bytes; if (error.Fail()) break; } return error; } #endif #ifndef _WIN32 int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { ssize_t bytes_read = llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset); if (bytes_read < 0) { num_bytes = 0; error.SetErrorToErrno(); } else { offset += bytes_read; num_bytes = bytes_read; } } else { num_bytes = 0; error.SetErrorString("invalid file handle"); } #else long cur = ::lseek(m_descriptor, 0, SEEK_CUR); SeekFromStart(offset); error = Read(buf, num_bytes); if (!error.Fail()) SeekFromStart(cur); #endif return error; } Status File::Read(size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp) { Status error; if (num_bytes > 0) { int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { struct stat file_stats; if (::fstat(fd, &file_stats) == 0) { if (file_stats.st_size > offset) { const size_t bytes_left = file_stats.st_size - offset; if (num_bytes > bytes_left) num_bytes = bytes_left; size_t num_bytes_plus_nul_char = num_bytes + (null_terminate ? 1 : 0); std::unique_ptr data_heap_ap; data_heap_ap.reset(new DataBufferHeap()); data_heap_ap->SetByteSize(num_bytes_plus_nul_char); if (data_heap_ap.get()) { error = Read(data_heap_ap->GetBytes(), num_bytes, offset); if (error.Success()) { // Make sure we read exactly what we asked for and if we got // less, adjust the array if (num_bytes_plus_nul_char < data_heap_ap->GetByteSize()) data_heap_ap->SetByteSize(num_bytes_plus_nul_char); data_buffer_sp.reset(data_heap_ap.release()); return error; } } } else error.SetErrorString("file is empty"); } else error.SetErrorToErrno(); } else error.SetErrorString("invalid file handle"); } else error.SetErrorString("invalid file handle"); num_bytes = 0; data_buffer_sp.reset(); return error; } Status File::Write(const void *buf, size_t &num_bytes, off_t &offset) { Status error; #if defined(MAX_WRITE_SIZE) if (num_bytes > MAX_WRITE_SIZE) { const uint8_t *p = (const uint8_t *)buf; size_t bytes_left = num_bytes; // Init the num_bytes written to zero num_bytes = 0; while (bytes_left > 0) { size_t curr_num_bytes; if (bytes_left > MAX_WRITE_SIZE) curr_num_bytes = MAX_WRITE_SIZE; else curr_num_bytes = bytes_left; error = Write(p + num_bytes, curr_num_bytes, offset); // Update how many bytes were read num_bytes += curr_num_bytes; if (bytes_left < curr_num_bytes) bytes_left = 0; else bytes_left -= curr_num_bytes; if (error.Fail()) break; } return error; } #endif int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { #ifndef _WIN32 ssize_t bytes_written = llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset); if (bytes_written < 0) { num_bytes = 0; error.SetErrorToErrno(); } else { offset += bytes_written; num_bytes = bytes_written; } #else long cur = ::lseek(m_descriptor, 0, SEEK_CUR); error = Write(buf, num_bytes); long after = ::lseek(m_descriptor, 0, SEEK_CUR); if (!error.Fail()) SeekFromStart(cur); offset = after; #endif } else { num_bytes = 0; error.SetErrorString("invalid file handle"); } return error; } //------------------------------------------------------------------ // Print some formatted output to the stream. //------------------------------------------------------------------ size_t File::Printf(const char *format, ...) { va_list args; va_start(args, format); size_t result = PrintfVarArg(format, args); va_end(args); return result; } //------------------------------------------------------------------ // Print some formatted output to the stream. //------------------------------------------------------------------ size_t File::PrintfVarArg(const char *format, va_list args) { size_t result = 0; if (DescriptorIsValid()) { char *s = NULL; result = vasprintf(&s, format, args); if (s != NULL) { if (result > 0) { size_t s_len = result; Write(s, s_len); result = s_len; } free(s); } } else if (StreamIsValid()) { result = ::vfprintf(m_stream, format, args); } return result; } mode_t File::ConvertOpenOptionsForPOSIXOpen(uint32_t open_options) { mode_t mode = 0; if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) mode |= O_RDWR; else if (open_options & eOpenOptionWrite) mode |= O_WRONLY; if (open_options & eOpenOptionAppend) mode |= O_APPEND; if (open_options & eOpenOptionTruncate) mode |= O_TRUNC; if (open_options & eOpenOptionNonBlocking) mode |= O_NONBLOCK; if (open_options & eOpenOptionCanCreateNewOnly) mode |= O_CREAT | O_EXCL; else if (open_options & eOpenOptionCanCreate) mode |= O_CREAT; return mode; } void File::CalculateInteractiveAndTerminal() { const int fd = GetDescriptor(); if (fd >= 0) { m_is_interactive = eLazyBoolNo; m_is_real_terminal = eLazyBoolNo; #if defined(_WIN32) if (_isatty(fd)) { m_is_interactive = eLazyBoolYes; m_is_real_terminal = eLazyBoolYes; } #else if (isatty(fd)) { m_is_interactive = eLazyBoolYes; struct winsize window_size; if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) { if (window_size.ws_col > 0) { m_is_real_terminal = eLazyBoolYes; if (llvm::sys::Process::FileDescriptorHasColors(fd)) m_supports_colors = eLazyBoolYes; } } } #endif } } bool File::GetIsInteractive() { if (m_is_interactive == eLazyBoolCalculate) CalculateInteractiveAndTerminal(); return m_is_interactive == eLazyBoolYes; } bool File::GetIsRealTerminal() { if (m_is_real_terminal == eLazyBoolCalculate) CalculateInteractiveAndTerminal(); return m_is_real_terminal == eLazyBoolYes; } bool File::GetIsTerminalWithColors() { if (m_supports_colors == eLazyBoolCalculate) CalculateInteractiveAndTerminal(); return m_supports_colors == eLazyBoolYes; } Index: vendor/lldb/dist/source/Host/common/Host.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Host.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/Host.cpp (revision 321194) @@ -1,683 +1,684 @@ //===-- 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 #ifndef _WIN32 #include #include #include #include #include #include #endif #if defined(__APPLE__) #include #include #include #endif #if defined(__linux__) || defined(__FreeBSD__) || \ defined(__FreeBSD_kernel__) || defined(__APPLE__) || \ defined(__NetBSD__) || defined(__OpenBSD__) #if !defined(__ANDROID__) #include #endif #include #include #endif #if defined(__FreeBSD__) #include #endif #if defined(__NetBSD__) #include #endif // C++ Includes +#include // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/MonitoringProcessLauncher.h" #include "lldb/Host/Predicate.h" #include "lldb/Host/ProcessLauncher.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private-forward.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #if defined(_WIN32) #include "lldb/Host/windows/ConnectionGenericFileWindows.h" #include "lldb/Host/windows/ProcessLauncherWindows.h" #else #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #endif #if defined(__APPLE__) #ifndef _POSIX_SPAWN_DISABLE_ASLR #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 #endif extern "C" { int __pthread_chdir(const char *path); int __pthread_fchdir(int fildes); } #endif using namespace lldb; using namespace lldb_private; #if !defined(__APPLE__) && !defined(_WIN32) struct MonitorInfo { lldb::pid_t pid; // The process ID to monitor Host::MonitorChildProcessCallback callback; // The callback function to call when "pid" exits or signals bool monitor_signals; // If true, call the callback when "pid" gets signaled. }; static thread_result_t MonitorChildProcessThreadFunction(void *arg); HostThread Host::StartMonitoringChildProcess( const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, bool monitor_signals) { MonitorInfo *info_ptr = new MonitorInfo(); info_ptr->pid = pid; info_ptr->callback = callback; info_ptr->monitor_signals = monitor_signals; char thread_name[256]; ::snprintf(thread_name, sizeof(thread_name), "", pid); return ThreadLauncher::LaunchThread( thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL); } #ifndef __linux__ //------------------------------------------------------------------ // Scoped class that will disable thread canceling when it is // constructed, and exception safely restore the previous value it // when it goes out of scope. //------------------------------------------------------------------ class ScopedPThreadCancelDisabler { public: ScopedPThreadCancelDisabler() { // Disable the ability for this thread to be cancelled int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state); if (err != 0) m_old_state = -1; } ~ScopedPThreadCancelDisabler() { // Restore the ability for this thread to be cancelled to what it // previously was. if (m_old_state != -1) ::pthread_setcancelstate(m_old_state, 0); } private: int m_old_state; // Save the old cancelability state. }; #endif // __linux__ #ifdef __linux__ #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) static __thread volatile sig_atomic_t g_usr1_called; #else static thread_local volatile sig_atomic_t g_usr1_called; #endif static void SigUsr1Handler(int) { g_usr1_called = 1; } #endif // __linux__ static bool CheckForMonitorCancellation() { #ifdef __linux__ if (g_usr1_called) { g_usr1_called = 0; return true; } #else ::pthread_testcancel(); #endif return false; } static thread_result_t MonitorChildProcessThreadFunction(void *arg) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); const char *function = __FUNCTION__; if (log) log->Printf("%s (arg = %p) thread starting...", function, arg); MonitorInfo *info = (MonitorInfo *)arg; const Host::MonitorChildProcessCallback callback = info->callback; const bool monitor_signals = info->monitor_signals; assert(info->pid <= UINT32_MAX); const ::pid_t pid = monitor_signals ? -1 * getpgid(info->pid) : info->pid; delete info; int status = -1; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #define __WALL 0 #endif const int options = __WALL; #ifdef __linux__ // This signal is only used to interrupt the thread from waitpid struct sigaction sigUsr1Action; memset(&sigUsr1Action, 0, sizeof(sigUsr1Action)); sigUsr1Action.sa_handler = SigUsr1Handler; ::sigaction(SIGUSR1, &sigUsr1Action, nullptr); #endif // __linux__ while (1) { log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i)...", function, pid, options); if (CheckForMonitorCancellation()) break; // Get signals from all children with same process group of pid const ::pid_t wait_pid = ::waitpid(pid, &status, options); if (CheckForMonitorCancellation()) break; if (wait_pid == -1) { if (errno == EINTR) continue; else { LLDB_LOG(log, "arg = {0}, thread exiting because waitpid failed ({1})...", arg, llvm::sys::StrError()); break; } } else if (wait_pid > 0) { bool exited = false; int signal = 0; int exit_status = 0; const char *status_cstr = NULL; if (WIFSTOPPED(status)) { signal = WSTOPSIG(status); status_cstr = "STOPPED"; } else if (WIFEXITED(status)) { exit_status = WEXITSTATUS(status); status_cstr = "EXITED"; exited = true; } else if (WIFSIGNALED(status)) { signal = WTERMSIG(status); status_cstr = "SIGNALED"; if (wait_pid == abs(pid)) { exited = true; exit_status = -1; } } else { status_cstr = "(\?\?\?)"; } // Scope for pthread_cancel_disabler { #ifndef __linux__ ScopedPThreadCancelDisabler pthread_cancel_disabler; #endif log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i) => pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", function, pid, options, wait_pid, status, status_cstr, signal, exit_status); if (exited || (signal != 0 && monitor_signals)) { bool callback_return = false; if (callback) callback_return = callback(wait_pid, exited, signal, exit_status); // If our process exited, then this thread should exit if (exited && wait_pid == abs(pid)) { if (log) log->Printf("%s (arg = %p) thread exiting because pid received " "exit signal...", __FUNCTION__, arg); break; } // If the callback returns true, it means this process should // exit if (callback_return) { if (log) log->Printf("%s (arg = %p) thread exiting because callback " "returned true...", __FUNCTION__, arg); break; } } } } } log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s (arg = %p) thread exiting...", __FUNCTION__, arg); return NULL; } #endif // #if !defined (__APPLE__) && !defined (_WIN32) #if !defined(__APPLE__) void Host::SystemLog(SystemLogType type, const char *format, va_list args) { vfprintf(stderr, format, args); } #endif void Host::SystemLog(SystemLogType type, const char *format, ...) { va_list args; va_start(args, format); SystemLog(type, format, args); va_end(args); } lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); } #ifndef _WIN32 lldb::thread_t Host::GetCurrentThread() { return lldb::thread_t(pthread_self()); } const char *Host::GetSignalAsCString(int signo) { switch (signo) { case SIGHUP: return "SIGHUP"; // 1 hangup case SIGINT: return "SIGINT"; // 2 interrupt case SIGQUIT: return "SIGQUIT"; // 3 quit case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught) case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught) case SIGABRT: return "SIGABRT"; // 6 abort() #if defined(SIGPOLL) #if !defined(SIGIO) || (SIGPOLL != SIGIO) // Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to // fail with 'multiple define cases with same value' case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) #endif #endif #if defined(SIGEMT) case SIGEMT: return "SIGEMT"; // 7 EMT instruction #endif case SIGFPE: return "SIGFPE"; // 8 floating point exception case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored) case SIGBUS: return "SIGBUS"; // 10 bus error case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation case SIGSYS: return "SIGSYS"; // 12 bad argument to system call case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it case SIGALRM: return "SIGALRM"; // 14 alarm clock case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty case SIGCONT: return "SIGCONT"; // 19 continue a stopped process case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP) #if defined(SIGIO) case SIGIO: return "SIGIO"; // 23 input/output possible signal #endif case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm case SIGPROF: return "SIGPROF"; // 27 profiling time alarm #if defined(SIGWINCH) case SIGWINCH: return "SIGWINCH"; // 28 window size changes #endif #if defined(SIGINFO) case SIGINFO: return "SIGINFO"; // 29 information request #endif case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1 case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2 default: break; } return NULL; } #endif #if !defined(__APPLE__) // see Host.mm bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) { bundle.Clear(); return false; } bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #endif #ifndef _WIN32 FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) module_filespec.SetFile(info.dli_fname, true); } #endif return module_filespec; } #endif #if !defined(__linux__) bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { return false; } #endif struct ShellInfo { ShellInfo() : process_reaped(false), pid(LLDB_INVALID_PROCESS_ID), signo(-1), status(-1) {} lldb_private::Predicate process_reaped; lldb::pid_t pid; int signo; int status; }; static bool MonitorShellCommand(std::shared_ptr shell_info, lldb::pid_t pid, bool exited, // True if the process did exit int signo, // Zero for no signal int status) // Exit value of process if signal is zero { shell_info->pid = pid; shell_info->signo = signo; shell_info->status = status; // Let the thread running Host::RunShellCommand() know that the process // exited and that ShellInfo has been filled in by broadcasting to it shell_info->process_reaped.SetValue(true, eBroadcastAlways); return true; } Status Host::RunShellCommand(const char *command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, uint32_t timeout_sec, bool run_in_default_shell) { return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr, command_output_ptr, timeout_sec, run_in_default_shell); } Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, uint32_t timeout_sec, bool run_in_default_shell) { Status error; ProcessLaunchInfo launch_info; launch_info.SetArchitecture(HostInfo::GetArchitecture()); if (run_in_default_shell) { // Run the command in a shell launch_info.SetShell(HostInfo::GetDefaultShell()); launch_info.GetArguments().AppendArguments(args); const bool localhost = true; const bool will_debug = false; const bool first_arg_is_full_shell_command = false; launch_info.ConvertArgumentsForLaunchingInShell( error, localhost, will_debug, first_arg_is_full_shell_command, 0); } else { // No shell, just run it const bool first_arg_is_executable = true; launch_info.SetArguments(args, first_arg_is_executable); } if (working_dir) launch_info.SetWorkingDirectory(working_dir); llvm::SmallString output_file_path; if (command_output_ptr) { // Create a temporary file to get the stdout/stderr and redirect the // output of the command into this file. We will later read this file // if all goes well and fill the data into "command_output_ptr" FileSpec tmpdir_file_spec; if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%"); llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), output_file_path); } else { llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path); } } FileSpec output_file_spec{output_file_path.c_str(), false}; launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); if (output_file_spec) { launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false, true); launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); } else { launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); } std::shared_ptr shell_info_sp(new ShellInfo()); const bool monitor_signals = false; launch_info.SetMonitorProcessCallback( std::bind(MonitorShellCommand, shell_info_sp, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), monitor_signals); error = LaunchProcess(launch_info); const lldb::pid_t pid = launch_info.GetProcessID(); if (error.Success() && pid == LLDB_INVALID_PROCESS_ID) error.SetErrorString("failed to get process ID"); if (error.Success()) { bool timed_out = false; shell_info_sp->process_reaped.WaitForValueEqualTo( true, std::chrono::seconds(timeout_sec), &timed_out); if (timed_out) { error.SetErrorString("timed out waiting for shell command to complete"); // Kill the process since it didn't complete within the timeout specified Kill(pid, SIGKILL); // Wait for the monitor callback to get the message timed_out = false; shell_info_sp->process_reaped.WaitForValueEqualTo( true, std::chrono::seconds(1), &timed_out); } else { if (status_ptr) *status_ptr = shell_info_sp->status; if (signo_ptr) *signo_ptr = shell_info_sp->signo; if (command_output_ptr) { command_output_ptr->clear(); uint64_t file_size = output_file_spec.GetByteSize(); if (file_size > 0) { if (file_size > command_output_ptr->max_size()) { error.SetErrorStringWithFormat( "shell command output is too large to fit into a std::string"); } else { auto Buffer = DataBufferLLVM::CreateFromPath(output_file_spec.GetPath()); if (error.Success()) command_output_ptr->assign(Buffer->GetChars(), Buffer->GetByteSize()); } } } } } llvm::sys::fs::remove(output_file_spec.GetPath()); return error; } // The functions below implement process launching for non-Apple-based platforms #if !defined(__APPLE__) Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { std::unique_ptr delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); #else delegate_launcher.reset(new ProcessLauncherPosixFork()); #endif MonitoringProcessLauncher launcher(std::move(delegate_launcher)); Status error; HostProcess process = launcher.LaunchProcess(launch_info, error); // TODO(zturner): It would be better if the entire HostProcess were returned // instead of writing // it into this structure. launch_info.SetProcessID(process.GetProcessId()); return error; } #endif // !defined(__APPLE__) #ifndef _WIN32 void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); } #endif #if !defined(__APPLE__) bool Host::OpenFileInExternalEditor(const FileSpec &file_spec, uint32_t line_no) { return false; } #endif const UnixSignalsSP &Host::GetUnixSignals() { static const auto s_unix_signals_sp = UnixSignals::Create(HostInfo::GetArchitecture()); return s_unix_signals_sp; } std::unique_ptr Host::CreateDefaultConnection(llvm::StringRef url) { #if defined(_WIN32) if (url.startswith("file://")) return std::unique_ptr(new ConnectionGenericFile()); #endif return std::unique_ptr(new ConnectionFileDescriptor()); } #if defined(LLVM_ON_UNIX) WaitStatus WaitStatus::Decode(int wstatus) { if (WIFEXITED(wstatus)) return {Exit, uint8_t(WEXITSTATUS(wstatus))}; else if (WIFSIGNALED(wstatus)) return {Signal, uint8_t(WTERMSIG(wstatus))}; else if (WIFSTOPPED(wstatus)) return {Stop, uint8_t(WSTOPSIG(wstatus))}; llvm_unreachable("Unknown wait status"); } #endif void llvm::format_provider::format(const WaitStatus &WS, raw_ostream &OS, StringRef Options) { if (Options == "g") { char type; switch (WS.type) { case WaitStatus::Exit: type = 'W'; break; case WaitStatus::Signal: type = 'X'; break; case WaitStatus::Stop: type = 'S'; break; } OS << formatv("{0}{1:x-2}", type, WS.status); return; } assert(Options.empty()); const char *desc; switch(WS.type) { case WaitStatus::Exit: desc = "Exited with status"; break; case WaitStatus::Signal: desc = "Killed by signal"; break; case WaitStatus::Stop: desc = "Stopped by signal"; break; } OS << desc << " " << int(WS.status); } Index: vendor/lldb/dist/source/Host/common/MainLoop.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/MainLoop.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/MainLoop.cpp (revision 321194) @@ -1,386 +1,387 @@ //===-- MainLoop.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Config/llvm-config.h" #include "lldb/Host/MainLoop.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Utility/Status.h" #include #include #include #include #include #include // Multiplexing is implemented using kqueue on systems that support it (BSD // variants including OSX). On linux we use ppoll, while android uses pselect // (ppoll is present but not implemented properly). On windows we use WSApoll // (which does not support signals). #if HAVE_SYS_EVENT_H #include #elif defined(LLVM_ON_WIN32) #include #else #include #endif #ifdef LLVM_ON_WIN32 #define POLL WSAPoll #else #define POLL poll #endif #ifdef __ANDROID__ #define FORCE_PSELECT #endif #if SIGNAL_POLLING_UNSUPPORTED #ifdef LLVM_ON_WIN32 typedef int sigset_t; typedef int siginfo_t; #endif int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout_ts, const sigset_t *) { int timeout = (timeout_ts == nullptr) ? -1 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000); return POLL(fds, nfds, timeout); } #endif using namespace lldb; using namespace lldb_private; static sig_atomic_t g_signal_flags[NSIG]; static void SignalHandler(int signo, siginfo_t *info, void *) { assert(signo < NSIG); g_signal_flags[signo] = 1; } class MainLoop::RunImpl { public: RunImpl(MainLoop &loop); ~RunImpl() = default; Status Poll(); void ProcessEvents(); private: MainLoop &loop; #if HAVE_SYS_EVENT_H std::vector in_events; struct kevent out_events[4]; int num_events = -1; #else #ifdef FORCE_PSELECT fd_set read_fd_set; #else std::vector read_fds; #endif sigset_t get_sigmask(); #endif }; #if HAVE_SYS_EVENT_H MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) { in_events.reserve(loop.m_read_fds.size()); } Status MainLoop::RunImpl::Poll() { in_events.resize(loop.m_read_fds.size()); unsigned i = 0; for (auto &fd : loop.m_read_fds) EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0); num_events = kevent(loop.m_kqueue, in_events.data(), in_events.size(), out_events, llvm::array_lengthof(out_events), nullptr); if (num_events < 0) return Status("kevent() failed with error %d\n", num_events); return Status(); } void MainLoop::RunImpl::ProcessEvents() { assert(num_events >= 0); for (int i = 0; i < num_events; ++i) { if (loop.m_terminate_request) return; switch (out_events[i].filter) { case EVFILT_READ: loop.ProcessReadObject(out_events[i].ident); break; case EVFILT_SIGNAL: loop.ProcessSignal(out_events[i].ident); break; default: llvm_unreachable("Unknown event"); } } } #else MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) { #ifndef FORCE_PSELECT read_fds.reserve(loop.m_read_fds.size()); #endif } sigset_t MainLoop::RunImpl::get_sigmask() { #if SIGNAL_POLLING_UNSUPPORTED return 0; #else sigset_t sigmask; int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask); assert(ret == 0); (void) ret; for (const auto &sig : loop.m_signals) sigdelset(&sigmask, sig.first); return sigmask; #endif } #ifdef FORCE_PSELECT Status MainLoop::RunImpl::Poll() { FD_ZERO(&read_fd_set); int nfds = 0; for (const auto &fd : loop.m_read_fds) { FD_SET(fd.first, &read_fd_set); nfds = std::max(nfds, fd.first + 1); } sigset_t sigmask = get_sigmask(); if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); return Status(); } #else Status MainLoop::RunImpl::Poll() { read_fds.clear(); sigset_t sigmask = get_sigmask(); for (const auto &fd : loop.m_read_fds) { struct pollfd pfd; pfd.fd = fd.first; pfd.events = POLLIN; pfd.revents = 0; read_fds.push_back(pfd); } if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); return Status(); } #endif void MainLoop::RunImpl::ProcessEvents() { #ifdef FORCE_PSELECT // Collect first all readable file descriptors into a separate vector and then // iterate over it to invoke callbacks. Iterating directly over // loop.m_read_fds is not possible because the callbacks can modify the // container which could invalidate the iterator. std::vector fds; for (const auto &fd : loop.m_read_fds) if (FD_ISSET(fd.first, &read_fd_set)) fds.push_back(fd.first); for (const auto &handle : fds) { #else for (const auto &fd : read_fds) { if ((fd.revents & POLLIN) == 0) continue; IOObject::WaitableHandle handle = fd.fd; #endif if (loop.m_terminate_request) return; loop.ProcessReadObject(handle); } std::vector signals; for (const auto &entry : loop.m_signals) if (g_signal_flags[entry.first] != 0) signals.push_back(entry.first); for (const auto &signal : signals) { if (loop.m_terminate_request) return; g_signal_flags[signal] = 0; loop.ProcessSignal(signal); } } #endif MainLoop::MainLoop() { #if HAVE_SYS_EVENT_H m_kqueue = kqueue(); assert(m_kqueue >= 0); #endif } MainLoop::~MainLoop() { #if HAVE_SYS_EVENT_H close(m_kqueue); #endif assert(m_read_fds.size() == 0); assert(m_signals.size() == 0); } MainLoop::ReadHandleUP MainLoop::RegisterReadObject(const IOObjectSP &object_sp, const Callback &callback, Status &error) { #ifdef LLVM_ON_WIN32 if (object_sp->GetFdType() != IOObject:: eFDTypeSocket) { error.SetErrorString("MainLoop: non-socket types unsupported on Windows"); return nullptr; } #endif if (!object_sp || !object_sp->IsValid()) { error.SetErrorString("IO object is not valid."); return nullptr; } const bool inserted = m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second; if (!inserted) { error.SetErrorStringWithFormat("File descriptor %d already monitored.", object_sp->GetWaitableHandle()); return nullptr; } return CreateReadHandle(object_sp); } // We shall block the signal, then install the signal handler. The signal will // be unblocked in // the Run() function to check for signal delivery. MainLoop::SignalHandleUP MainLoop::RegisterSignal(int signo, const Callback &callback, Status &error) { #ifdef SIGNAL_POLLING_UNSUPPORTED error.SetErrorString("Signal polling is not supported on this platform."); return nullptr; #else if (m_signals.find(signo) != m_signals.end()) { error.SetErrorStringWithFormat("Signal %d already monitored.", signo); return nullptr; } SignalInfo info; info.callback = callback; struct sigaction new_action; new_action.sa_sigaction = &SignalHandler; new_action.sa_flags = SA_SIGINFO; sigemptyset(&new_action.sa_mask); sigaddset(&new_action.sa_mask, signo); sigset_t old_set; g_signal_flags[signo] = 0; // Even if using kqueue, the signal handler will still be invoked, so it's // important to replace it with our "bening" handler. int ret = sigaction(signo, &new_action, &info.old_action); assert(ret == 0 && "sigaction failed"); #if HAVE_SYS_EVENT_H struct kevent ev; EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); assert(ret == 0); #endif // If we're using kqueue, the signal needs to be unblocked in order to recieve // it. If using pselect/ppoll, we need to block it, and later unblock it as a // part of the system call. ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK, &new_action.sa_mask, &old_set); assert(ret == 0 && "pthread_sigmask failed"); info.was_blocked = sigismember(&old_set, signo); m_signals.insert({signo, info}); return SignalHandleUP(new SignalHandle(*this, signo)); #endif } void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) { bool erased = m_read_fds.erase(handle); UNUSED_IF_ASSERT_DISABLED(erased); assert(erased); } void MainLoop::UnregisterSignal(int signo) { #if SIGNAL_POLLING_UNSUPPORTED Status("Signal polling is not supported on this platform."); #else auto it = m_signals.find(signo); assert(it != m_signals.end()); sigaction(signo, &it->second.old_action, nullptr); sigset_t set; sigemptyset(&set); sigaddset(&set, signo); int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set, nullptr); assert(ret == 0); (void)ret; #if HAVE_SYS_EVENT_H struct kevent ev; EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0); ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); assert(ret == 0); #endif m_signals.erase(it); #endif } Status MainLoop::Run() { m_terminate_request = false; Status error; RunImpl impl(*this); // run until termination or until we run out of things to listen to while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) { error = impl.Poll(); if (error.Fail()) return error; impl.ProcessEvents(); if (m_terminate_request) return Status(); } return Status(); } void MainLoop::ProcessSignal(int signo) { auto it = m_signals.find(signo); if (it != m_signals.end()) it->second.callback(*this); // Do the work } void MainLoop::ProcessReadObject(IOObject::WaitableHandle handle) { auto it = m_read_fds.find(handle); if (it != m_read_fds.end()) it->second(*this); // Do the work } Index: vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp (revision 321194) @@ -1,448 +1,439 @@ //===-- NativeRegisterContext.cpp -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Utility/Log.h" #include "lldb/Host/PosixApi.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" using namespace lldb; using namespace lldb_private; NativeRegisterContext::NativeRegisterContext(NativeThreadProtocol &thread, uint32_t concrete_frame_idx) : m_thread(thread), m_concrete_frame_idx(concrete_frame_idx) {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- NativeRegisterContext::~NativeRegisterContext() {} // FIXME revisit invalidation, process stop ids, etc. Right now we don't // support caching in NativeRegisterContext. We can do this later by // utilizing NativeProcessProtocol::GetStopID () and adding a stop id to // NativeRegisterContext. // void // NativeRegisterContext::InvalidateIfNeeded (bool force) // { // ProcessSP process_sp (m_thread.GetProcess()); // bool invalidate = force; // uint32_t process_stop_id = UINT32_MAX; // if (process_sp) // process_stop_id = process_sp->GetStopID(); // else // invalidate = true; // if (!invalidate) // invalidate = process_stop_id != GetStopID(); // if (invalidate) // { // InvalidateAllRegisters (); // SetStopID (process_stop_id); // } // } const RegisterInfo * NativeRegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name, uint32_t start_idx) { if (reg_name.empty()) return nullptr; const uint32_t num_registers = GetRegisterCount(); for (uint32_t reg = start_idx; reg < num_registers; ++reg) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); if (reg_name.equals_lower(reg_info->name) || reg_name.equals_lower(reg_info->alt_name)) return reg_info; } return nullptr; } const RegisterInfo *NativeRegisterContext::GetRegisterInfo(uint32_t kind, uint32_t num) { const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num); if (reg_num == LLDB_INVALID_REGNUM) return nullptr; return GetRegisterInfoAtIndex(reg_num); } const char *NativeRegisterContext::GetRegisterName(uint32_t reg) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); if (reg_info) return reg_info->name; return nullptr; } const char *NativeRegisterContext::GetRegisterSetNameForRegisterAtIndex( uint32_t reg_index) const { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) return nullptr; for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) { const RegisterSet *const reg_set = GetRegisterSet(set_index); if (!reg_set) continue; for (uint32_t reg_num_index = 0; reg_num_index < reg_set->num_registers; ++reg_num_index) { const uint32_t reg_num = reg_set->registers[reg_num_index]; // FIXME double check we're checking the right register kind here. if (reg_info->kinds[RegisterKind::eRegisterKindLLDB] == reg_num) { // The given register is a member of this register set. Return the // register set name. return reg_set->name; } } } // Didn't find it. return nullptr; } lldb::addr_t NativeRegisterContext::GetPC(lldb::addr_t fail_value) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (log) log->Printf("NativeRegisterContext::%s using reg index %" PRIu32 " (default %" PRIu64 ")", __FUNCTION__, reg, fail_value); const uint64_t retval = ReadRegisterAsUnsigned(reg, fail_value); if (log) log->Printf("NativeRegisterContext::%s " PRIu32 " retval %" PRIu64, __FUNCTION__, retval); return retval; } lldb::addr_t NativeRegisterContext::GetPCfromBreakpointLocation(lldb::addr_t fail_value) { return GetPC(fail_value); } Status NativeRegisterContext::SetPC(lldb::addr_t pc) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); return WriteRegisterFromUnsigned(reg, pc); } lldb::addr_t NativeRegisterContext::GetSP(lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); return ReadRegisterAsUnsigned(reg, fail_value); } Status NativeRegisterContext::SetSP(lldb::addr_t sp) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); return WriteRegisterFromUnsigned(reg, sp); } lldb::addr_t NativeRegisterContext::GetFP(lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); return ReadRegisterAsUnsigned(reg, fail_value); } Status NativeRegisterContext::SetFP(lldb::addr_t fp) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); return WriteRegisterFromUnsigned(reg, fp); } lldb::addr_t NativeRegisterContext::GetReturnAddress(lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); return ReadRegisterAsUnsigned(reg, fail_value); } lldb::addr_t NativeRegisterContext::GetFlags(lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); return ReadRegisterAsUnsigned(reg, fail_value); } lldb::addr_t NativeRegisterContext::ReadRegisterAsUnsigned(uint32_t reg, lldb::addr_t fail_value) { if (reg != LLDB_INVALID_REGNUM) return ReadRegisterAsUnsigned(GetRegisterInfoAtIndex(reg), fail_value); return fail_value; } uint64_t NativeRegisterContext::ReadRegisterAsUnsigned(const RegisterInfo *reg_info, lldb::addr_t fail_value) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); if (reg_info) { RegisterValue value; Status error = ReadRegister(reg_info, value); if (error.Success()) { if (log) log->Printf("NativeRegisterContext::%s ReadRegister() succeeded, value " "%" PRIu64, __FUNCTION__, value.GetAsUInt64()); return value.GetAsUInt64(); } else { if (log) log->Printf("NativeRegisterContext::%s ReadRegister() failed, error %s", __FUNCTION__, error.AsCString()); } } else { if (log) log->Printf("NativeRegisterContext::%s ReadRegister() null reg_info", __FUNCTION__); } return fail_value; } Status NativeRegisterContext::WriteRegisterFromUnsigned(uint32_t reg, uint64_t uval) { if (reg == LLDB_INVALID_REGNUM) return Status("NativeRegisterContext::%s (): reg is invalid", __FUNCTION__); return WriteRegisterFromUnsigned(GetRegisterInfoAtIndex(reg), uval); } Status NativeRegisterContext::WriteRegisterFromUnsigned(const RegisterInfo *reg_info, uint64_t uval) { assert(reg_info); if (!reg_info) return Status("reg_info is nullptr"); RegisterValue value; if (!value.SetUInt(uval, reg_info->byte_size)) return Status("RegisterValue::SetUInt () failed"); return WriteRegister(reg_info, value); } lldb::tid_t NativeRegisterContext::GetThreadID() const { return m_thread.GetID(); } uint32_t NativeRegisterContext::NumSupportedHardwareBreakpoints() { return 0; } uint32_t NativeRegisterContext::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { return LLDB_INVALID_INDEX32; } Status NativeRegisterContext::ClearAllHardwareBreakpoints() { return Status("not implemented"); } bool NativeRegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) { return false; } Status NativeRegisterContext::GetHardwareBreakHitIndex(uint32_t &bp_index, lldb::addr_t trap_addr) { bp_index = LLDB_INVALID_INDEX32; return Status("not implemented"); } uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints() { return 0; } uint32_t NativeRegisterContext::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) { return LLDB_INVALID_INDEX32; } bool NativeRegisterContext::ClearHardwareWatchpoint(uint32_t hw_index) { return false; } Status NativeRegisterContext::ClearAllHardwareWatchpoints() { return Status("not implemented"); } Status NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { is_hit = false; return Status("not implemented"); } Status NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { wp_index = LLDB_INVALID_INDEX32; return Status("not implemented"); } Status NativeRegisterContext::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) { is_vacant = false; return Status("not implemented"); } lldb::addr_t NativeRegisterContext::GetWatchpointAddress(uint32_t wp_index) { return LLDB_INVALID_ADDRESS; } lldb::addr_t NativeRegisterContext::GetWatchpointHitAddress(uint32_t wp_index) { return LLDB_INVALID_ADDRESS; } bool NativeRegisterContext::HardwareSingleStep(bool enable) { return false; } Status NativeRegisterContext::ReadRegisterValueFromMemory( const RegisterInfo *reg_info, lldb::addr_t src_addr, size_t src_len, RegisterValue ®_value) { Status error; if (reg_info == nullptr) { error.SetErrorString("invalid register info argument."); return error; } // Moving from addr into a register // // Case 1: src_len == dst_len // // |AABBCCDD| Address contents // |AABBCCDD| Register contents // // Case 2: src_len > dst_len // // Status! (The register should always be big enough to hold the data) // // Case 3: src_len < dst_len // // |AABB| Address contents // |AABB0000| Register contents [on little-endian hardware] // |0000AABB| Register contents [on big-endian hardware] if (src_len > RegisterValue::kMaxRegisterByteSize) { error.SetErrorString("register too small to receive memory data"); return error; } const size_t dst_len = reg_info->byte_size; if (src_len > dst_len) { error.SetErrorStringWithFormat( "%" PRIu64 " bytes is too big to store in register %s (%" PRIu64 " bytes)", static_cast(src_len), reg_info->name, static_cast(dst_len)); return error; } - NativeProcessProtocolSP process_sp(m_thread.GetProcess()); - if (!process_sp) { - error.SetErrorString("invalid process"); - return error; - } - + NativeProcessProtocol &process = m_thread.GetProcess(); uint8_t src[RegisterValue::kMaxRegisterByteSize]; // Read the memory size_t bytes_read; - error = process_sp->ReadMemory(src_addr, src, src_len, bytes_read); + error = process.ReadMemory(src_addr, src, src_len, bytes_read); if (error.Fail()) return error; // Make sure the memory read succeeded... if (bytes_read != src_len) { // This might happen if we read _some_ bytes but not all error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes", static_cast(bytes_read), static_cast(src_len)); return error; } // We now have a memory buffer that contains the part or all of the register // value. Set the register value using this memory data. // TODO: we might need to add a parameter to this function in case the byte // order of the memory data doesn't match the process. For now we are assuming // they are the same. lldb::ByteOrder byte_order; - if (!process_sp->GetByteOrder(byte_order)) { + if (process.GetByteOrder(byte_order)) { error.SetErrorString("NativeProcessProtocol::GetByteOrder () failed"); return error; } reg_value.SetFromMemoryData(reg_info, src, src_len, byte_order, error); return error; } Status NativeRegisterContext::WriteRegisterValueToMemory( const RegisterInfo *reg_info, lldb::addr_t dst_addr, size_t dst_len, const RegisterValue ®_value) { uint8_t dst[RegisterValue::kMaxRegisterByteSize]; Status error; - NativeProcessProtocolSP process_sp(m_thread.GetProcess()); - if (process_sp) { + NativeProcessProtocol &process = m_thread.GetProcess(); - // TODO: we might need to add a parameter to this function in case the byte - // order of the memory data doesn't match the process. For now we are - // assuming - // they are the same. - lldb::ByteOrder byte_order; - if (!process_sp->GetByteOrder(byte_order)) - return Status("NativeProcessProtocol::GetByteOrder () failed"); + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are + // assuming + // they are the same. + lldb::ByteOrder byte_order; + if (!process.GetByteOrder(byte_order)) + return Status("NativeProcessProtocol::GetByteOrder () failed"); - const size_t bytes_copied = - reg_value.GetAsMemoryData(reg_info, dst, dst_len, byte_order, error); + const size_t bytes_copied = + reg_value.GetAsMemoryData(reg_info, dst, dst_len, byte_order, error); - if (error.Success()) { - if (bytes_copied == 0) { - error.SetErrorString("byte copy failed."); - } else { - size_t bytes_written; - error = - process_sp->WriteMemory(dst_addr, dst, bytes_copied, bytes_written); - if (error.Fail()) - return error; + if (error.Success()) { + if (bytes_copied == 0) { + error.SetErrorString("byte copy failed."); + } else { + size_t bytes_written; + error = process.WriteMemory(dst_addr, dst, bytes_copied, bytes_written); + if (error.Fail()) + return error; - if (bytes_written != bytes_copied) { - // This might happen if we read _some_ bytes but not all - error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64 - " bytes", - static_cast(bytes_written), - static_cast(bytes_copied)); - } + if (bytes_written != bytes_copied) { + // This might happen if we read _some_ bytes but not all + error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64 + " bytes", + static_cast(bytes_written), + static_cast(bytes_copied)); } } - } else - error.SetErrorString("invalid process"); + } return error; } uint32_t NativeRegisterContext::ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num) const { const uint32_t num_regs = GetRegisterCount(); assert(kind < kNumRegisterKinds); for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); if (reg_info->kinds[kind] == num) return reg_idx; } return LLDB_INVALID_REGNUM; } Index: vendor/lldb/dist/source/Host/common/NativeThreadProtocol.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/NativeThreadProtocol.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/NativeThreadProtocol.cpp (revision 321194) @@ -1,68 +1,64 @@ //===-- NativeThreadProtocol.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/common/NativeThreadProtocol.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/SoftwareBreakpoint.h" using namespace lldb; using namespace lldb_private; -NativeThreadProtocol::NativeThreadProtocol(NativeProcessProtocol *process, +NativeThreadProtocol::NativeThreadProtocol(NativeProcessProtocol &process, lldb::tid_t tid) - : m_process_wp(process->shared_from_this()), m_tid(tid) {} + : m_process(process), m_tid(tid) {} Status NativeThreadProtocol::ReadRegister(uint32_t reg, RegisterValue ®_value) { NativeRegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) return Status("no register context"); const RegisterInfo *const reg_info = register_context_sp->GetRegisterInfoAtIndex(reg); if (!reg_info) return Status("no register info for reg num %" PRIu32, reg); return register_context_sp->ReadRegister(reg_info, reg_value); ; } Status NativeThreadProtocol::WriteRegister(uint32_t reg, const RegisterValue ®_value) { NativeRegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) return Status("no register context"); const RegisterInfo *const reg_info = register_context_sp->GetRegisterInfoAtIndex(reg); if (!reg_info) return Status("no register info for reg num %" PRIu32, reg); return register_context_sp->WriteRegister(reg_info, reg_value); } Status NativeThreadProtocol::SaveAllRegisters(lldb::DataBufferSP &data_sp) { NativeRegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) return Status("no register context"); return register_context_sp->WriteAllRegisterValues(data_sp); } Status NativeThreadProtocol::RestoreAllRegisters(lldb::DataBufferSP &data_sp) { NativeRegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) return Status("no register context"); return register_context_sp->ReadAllRegisterValues(data_sp); -} - -NativeProcessProtocolSP NativeThreadProtocol::GetProcess() { - return m_process_wp.lock(); } Index: vendor/lldb/dist/source/Host/common/Socket.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Socket.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/Socket.cpp (revision 321194) @@ -1,464 +1,465 @@ //===-- Socket.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Socket.h" #include "lldb/Host/Config.h" #include "lldb/Host/Host.h" #include "lldb/Host/SocketAddress.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Host/common/UDPSocket.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "llvm/ADT/STLExtras.h" #ifndef LLDB_DISABLE_POSIX #include "lldb/Host/posix/DomainSocket.h" #include #include #include #include #include #include +#include #endif #ifdef __linux__ #include "lldb/Host/linux/AbstractSocket.h" #endif #ifdef __ANDROID__ #include #include #include #include #include #include #include #endif // __ANDROID__ using namespace lldb; using namespace lldb_private; #if defined(_WIN32) typedef const char *set_socket_option_arg_type; typedef char *get_socket_option_arg_type; const NativeSocket Socket::kInvalidSocketValue = INVALID_SOCKET; #else // #if defined(_WIN32) typedef const void *set_socket_option_arg_type; typedef void *get_socket_option_arg_type; const NativeSocket Socket::kInvalidSocketValue = -1; #endif // #if defined(_WIN32) namespace { bool IsInterrupted() { #if defined(_WIN32) return ::WSAGetLastError() == WSAEINTR; #else return errno == EINTR; #endif } } Socket::Socket(SocketProtocol protocol, bool should_close, bool child_processes_inherit) : IOObject(eFDTypeSocket, should_close), m_protocol(protocol), m_socket(kInvalidSocketValue), m_child_processes_inherit(child_processes_inherit) {} Socket::~Socket() { Close(); } std::unique_ptr Socket::Create(const SocketProtocol protocol, bool child_processes_inherit, Status &error) { error.Clear(); std::unique_ptr socket_up; switch (protocol) { case ProtocolTcp: socket_up = llvm::make_unique(true, child_processes_inherit); break; case ProtocolUdp: socket_up = llvm::make_unique(true, child_processes_inherit); break; case ProtocolUnixDomain: #ifndef LLDB_DISABLE_POSIX socket_up = llvm::make_unique(true, child_processes_inherit); #else error.SetErrorString( "Unix domain sockets are not supported on this platform."); #endif break; case ProtocolUnixAbstract: #ifdef __linux__ socket_up = llvm::make_unique(child_processes_inherit); #else error.SetErrorString( "Abstract domain sockets are not supported on this platform."); #endif break; } if (error.Fail()) socket_up.reset(); return socket_up; } Status Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION)); if (log) log->Printf("Socket::%s (host/port = %s)", __FUNCTION__, host_and_port.data()); Status error; std::unique_ptr connect_socket( Create(ProtocolTcp, child_processes_inherit, error)); if (error.Fail()) return error; error = connect_socket->Connect(host_and_port); if (error.Success()) socket = connect_socket.release(); return error; } Status Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket, Predicate *predicate, int backlog) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("Socket::%s (%s)", __FUNCTION__, host_and_port.data()); Status error; std::string host_str; std::string port_str; int32_t port = INT32_MIN; if (!DecodeHostAndPort(host_and_port, host_str, port_str, port, &error)) return error; std::unique_ptr listen_socket( new TCPSocket(true, child_processes_inherit)); if (error.Fail()) return error; error = listen_socket->Listen(host_and_port, backlog); if (error.Success()) { // We were asked to listen on port zero which means we // must now read the actual port that was given to us // as port zero is a special code for "find an open port // for me". if (port == 0) port = listen_socket->GetLocalPortNumber(); // Set the port predicate since when doing a listen://: // it often needs to accept the incoming connection which is a blocking // system call. Allowing access to the bound port using a predicate allows // us to wait for the port predicate to be set to a non-zero value from // another thread in an efficient manor. if (predicate) predicate->SetValue(port, eBroadcastAlways); socket = listen_socket.release(); } return error; } Status Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("Socket::%s (host/port = %s)", __FUNCTION__, host_and_port.data()); return UDPSocket::Connect(host_and_port, child_processes_inherit, socket); } Status Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Status error; std::unique_ptr connect_socket( Create(ProtocolUnixDomain, child_processes_inherit, error)); if (error.Fail()) return error; error = connect_socket->Connect(name); if (error.Success()) socket = connect_socket.release(); return error; } Status Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Status error; std::unique_ptr listen_socket( Create(ProtocolUnixDomain, child_processes_inherit, error)); if (error.Fail()) return error; error = listen_socket->Listen(name, 5); if (error.Fail()) return error; error = listen_socket->Accept(socket); return error; } Status Socket::UnixAbstractConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Status error; std::unique_ptr connect_socket( Create(ProtocolUnixAbstract, child_processes_inherit, error)); if (error.Fail()) return error; error = connect_socket->Connect(name); if (error.Success()) socket = connect_socket.release(); return error; } Status Socket::UnixAbstractAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Status error; std::unique_ptr listen_socket( Create(ProtocolUnixAbstract, child_processes_inherit, error)); if (error.Fail()) return error; error = listen_socket->Listen(name, 5); if (error.Fail()) return error; error = listen_socket->Accept(socket); return error; } bool Socket::DecodeHostAndPort(llvm::StringRef host_and_port, std::string &host_str, std::string &port_str, int32_t &port, Status *error_ptr) { static RegularExpression g_regex( llvm::StringRef("([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)")); RegularExpression::Match regex_match(2); if (g_regex.Execute(host_and_port, ®ex_match)) { if (regex_match.GetMatchAtIndex(host_and_port.data(), 1, host_str) && regex_match.GetMatchAtIndex(host_and_port.data(), 2, port_str)) { // IPv6 addresses are wrapped in [] when specified with ports if (host_str.front() == '[' && host_str.back() == ']') host_str = host_str.substr(1, host_str.size() - 2); bool ok = false; port = StringConvert::ToUInt32(port_str.c_str(), UINT32_MAX, 10, &ok); if (ok && port <= UINT16_MAX) { if (error_ptr) error_ptr->Clear(); return true; } // port is too large if (error_ptr) error_ptr->SetErrorStringWithFormat( "invalid host:port specification: '%s'", host_and_port.data()); return false; } } // If this was unsuccessful, then check if it's simply a signed 32-bit // integer, representing // a port with an empty host. host_str.clear(); port_str.clear(); bool ok = false; port = StringConvert::ToUInt32(host_and_port.data(), UINT32_MAX, 10, &ok); if (ok && port < UINT16_MAX) { port_str = host_and_port; if (error_ptr) error_ptr->Clear(); return true; } if (error_ptr) error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port.data()); return false; } IOObject::WaitableHandle Socket::GetWaitableHandle() { // TODO: On Windows, use WSAEventSelect return m_socket; } Status Socket::Read(void *buf, size_t &num_bytes) { Status error; int bytes_received = 0; do { bytes_received = ::recv(m_socket, static_cast(buf), num_bytes, 0); } while (bytes_received < 0 && IsInterrupted()); if (bytes_received < 0) { SetLastError(error); num_bytes = 0; } else num_bytes = bytes_received; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION)); if (log) { log->Printf("%p Socket::Read() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", static_cast(this), static_cast(m_socket), buf, static_cast(num_bytes), static_cast(bytes_received), error.AsCString()); } return error; } Status Socket::Write(const void *buf, size_t &num_bytes) { Status error; int bytes_sent = 0; do { bytes_sent = Send(buf, num_bytes); } while (bytes_sent < 0 && IsInterrupted()); if (bytes_sent < 0) { SetLastError(error); num_bytes = 0; } else num_bytes = bytes_sent; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION)); if (log) { log->Printf("%p Socket::Write() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", static_cast(this), static_cast(m_socket), buf, static_cast(num_bytes), static_cast(bytes_sent), error.AsCString()); } return error; } Status Socket::PreDisconnect() { Status error; return error; } Status Socket::Close() { Status error; if (!IsValid() || !m_should_close_fd) return error; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p Socket::Close (fd = %i)", static_cast(this), m_socket); #if defined(_WIN32) bool success = !!closesocket(m_socket); #else bool success = !!::close(m_socket); #endif // A reference to a FD was passed in, set it to an invalid value m_socket = kInvalidSocketValue; if (!success) { SetLastError(error); } return error; } int Socket::GetOption(int level, int option_name, int &option_value) { get_socket_option_arg_type option_value_p = reinterpret_cast(&option_value); socklen_t option_value_size = sizeof(int); return ::getsockopt(m_socket, level, option_name, option_value_p, &option_value_size); } int Socket::SetOption(int level, int option_name, int option_value) { set_socket_option_arg_type option_value_p = reinterpret_cast(&option_value); return ::setsockopt(m_socket, level, option_name, option_value_p, sizeof(option_value)); } size_t Socket::Send(const void *buf, const size_t num_bytes) { return ::send(m_socket, static_cast(buf), num_bytes, 0); } void Socket::SetLastError(Status &error) { #if defined(_WIN32) error.SetError(::WSAGetLastError(), lldb::eErrorTypeWin32); #else error.SetErrorToErrno(); #endif } NativeSocket Socket::CreateSocket(const int domain, const int type, const int protocol, bool child_processes_inherit, Status &error) { error.Clear(); auto socket_type = type; #ifdef SOCK_CLOEXEC if (!child_processes_inherit) socket_type |= SOCK_CLOEXEC; #endif auto sock = ::socket(domain, socket_type, protocol); if (sock == kInvalidSocketValue) SetLastError(error); return sock; } NativeSocket Socket::AcceptSocket(NativeSocket sockfd, struct sockaddr *addr, socklen_t *addrlen, bool child_processes_inherit, Status &error) { error.Clear(); #if defined(ANDROID_USE_ACCEPT_WORKAROUND) // Hack: // This enables static linking lldb-server to an API 21 libc, but still having // it run on older devices. It is necessary because API 21 libc's // implementation of accept() uses the accept4 syscall(), which is not // available in older kernels. Using an older libc would fix this issue, but // introduce other ones, as the old libraries were quite buggy. int fd = syscall(__NR_accept, sockfd, addr, addrlen); if (fd >= 0 && !child_processes_inherit) { int flags = ::fcntl(fd, F_GETFD); if (flags != -1 && ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1) return fd; SetLastError(error); close(fd); } return fd; #elif defined(SOCK_CLOEXEC) int flags = 0; if (!child_processes_inherit) { flags |= SOCK_CLOEXEC; } NativeSocket fd = ::accept4(sockfd, addr, addrlen, flags); #else NativeSocket fd = ::accept(sockfd, addr, addrlen); #endif if (fd == kInvalidSocketValue) SetLastError(error); return fd; } Index: vendor/lldb/dist/source/Host/common/SocketAddress.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/SocketAddress.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/SocketAddress.cpp (revision 321194) @@ -1,338 +1,345 @@ //===-- SocketAddress.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Note: This file is used on Darwin by debugserver, so it needs to remain as // self contained as possible, and devoid of references to LLVM unless // there is compelling reason. // //===----------------------------------------------------------------------===// #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 && size_t(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; struct addrinfo *service_info_list = NULL; 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::IsLocalhost() const { + return (GetFamily() == AF_INET) + ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK) + : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_loopback, + 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/common/TCPSocket.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/TCPSocket.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/common/TCPSocket.cpp (revision 321194) @@ -1,296 +1,297 @@ //===-- TCPSocket.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/common/TCPSocket.h" #include "lldb/Host/Config.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Log.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/raw_ostream.h" #ifndef LLDB_DISABLE_POSIX #include #include #include #endif #if defined(LLVM_ON_WIN32) #include #endif #ifdef LLVM_ON_WIN32 #define CLOSE_SOCKET closesocket typedef const char *set_socket_option_arg_type; #else +#include #define CLOSE_SOCKET ::close typedef const void *set_socket_option_arg_type; #endif using namespace lldb; using namespace lldb_private; namespace { const int kType = SOCK_STREAM; } TCPSocket::TCPSocket(bool should_close, bool child_processes_inherit) : Socket(ProtocolTcp, should_close, child_processes_inherit) {} TCPSocket::TCPSocket(NativeSocket socket, const TCPSocket &listen_socket) : Socket(ProtocolTcp, listen_socket.m_should_close_fd, listen_socket.m_child_processes_inherit) { m_socket = socket; } TCPSocket::TCPSocket(NativeSocket socket, bool should_close, bool child_processes_inherit) : Socket(ProtocolTcp, should_close, child_processes_inherit) { m_socket = socket; } TCPSocket::~TCPSocket() { CloseListenSockets(); } bool TCPSocket::IsValid() const { return m_socket != kInvalidSocketValue || m_listen_sockets.size() != 0; } // Return the port number that is being used by the socket. uint16_t TCPSocket::GetLocalPortNumber() const { if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0) return sock_addr.GetPort(); } else if (!m_listen_sockets.empty()) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getsockname(m_listen_sockets.begin()->first, sock_addr, &sock_addr_len) == 0) return sock_addr.GetPort(); } return 0; } std::string TCPSocket::GetLocalIPAddress() const { // We bound to port zero, so we need to figure out which port we actually // bound to if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0) return sock_addr.GetIPAddress(); } return ""; } uint16_t TCPSocket::GetRemotePortNumber() const { if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0) return sock_addr.GetPort(); } return 0; } std::string TCPSocket::GetRemoteIPAddress() const { // We bound to port zero, so we need to figure out which port we actually // bound to if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0) return sock_addr.GetIPAddress(); } return ""; } Status TCPSocket::CreateSocket(int domain) { Status error; if (IsValid()) error = Close(); if (error.Fail()) return error; m_socket = Socket::CreateSocket(domain, kType, IPPROTO_TCP, m_child_processes_inherit, error); return error; } Status TCPSocket::Connect(llvm::StringRef name) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION)); if (log) log->Printf("TCPSocket::%s (host/port = %s)", __FUNCTION__, name.data()); Status error; std::string host_str; std::string port_str; int32_t port = INT32_MIN; if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) return error; auto addresses = lldb_private::SocketAddress::GetAddressInfo( host_str.c_str(), NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); for (auto address : addresses) { error = CreateSocket(address.GetFamily()); if (error.Fail()) continue; address.SetPort(port); if (-1 == ::connect(GetNativeSocket(), &address.sockaddr(), address.GetLength())) { CLOSE_SOCKET(GetNativeSocket()); continue; } SetOptionNoDelay(); error.Clear(); return error; } error.SetErrorString("Failed to connect port"); return error; } Status TCPSocket::Listen(llvm::StringRef name, int backlog) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("TCPSocket::%s (%s)", __FUNCTION__, name.data()); Status error; std::string host_str; std::string port_str; int32_t port = INT32_MIN; if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) return error; if (host_str == "*") host_str = "0.0.0.0"; auto addresses = lldb_private::SocketAddress::GetAddressInfo( host_str.c_str(), NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); for (auto address : addresses) { int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP, m_child_processes_inherit, error); if (error.Fail()) { error.Clear(); continue; } // enable local address reuse int option_value = 1; set_socket_option_arg_type option_value_p = reinterpret_cast(&option_value); ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, option_value_p, sizeof(option_value)); address.SetPort(port); int err = ::bind(fd, &address.sockaddr(), address.GetLength()); if (-1 != err) err = ::listen(fd, backlog); if (-1 == err) { CLOSE_SOCKET(fd); continue; } if (port == 0) { socklen_t sa_len = address.GetLength(); if (getsockname(fd, &address.sockaddr(), &sa_len) == 0) port = address.GetPort(); } m_listen_sockets[fd] = address; } if (m_listen_sockets.size() == 0) error.SetErrorString("Failed to connect port"); return error; } void TCPSocket::CloseListenSockets() { for (auto socket : m_listen_sockets) CLOSE_SOCKET(socket.first); m_listen_sockets.clear(); } Status TCPSocket::Accept(Socket *&conn_socket) { Status error; if (m_listen_sockets.size() == 0) { error.SetErrorString("No open listening sockets!"); return error; } int sock = -1; int listen_sock = -1; lldb_private::SocketAddress AcceptAddr; MainLoop accept_loop; std::vector handles; for (auto socket : m_listen_sockets) { auto fd = socket.first; auto inherit = this->m_child_processes_inherit; auto io_sp = IOObjectSP(new TCPSocket(socket.first, false, inherit)); handles.emplace_back(accept_loop.RegisterReadObject( io_sp, [fd, inherit, &sock, &AcceptAddr, &error, &listen_sock](MainLoopBase &loop) { socklen_t sa_len = AcceptAddr.GetMaxLength(); sock = AcceptSocket(fd, &AcceptAddr.sockaddr(), &sa_len, inherit, error); listen_sock = fd; loop.RequestTermination(); }, error)); if (error.Fail()) return error; } bool accept_connection = false; std::unique_ptr accepted_socket; // Loop until we are happy with our connection while (!accept_connection) { accept_loop.Run(); if (error.Fail()) return error; lldb_private::SocketAddress &AddrIn = m_listen_sockets[listen_sock]; if (!AddrIn.IsAnyAddr() && AcceptAddr != AddrIn) { CLOSE_SOCKET(sock); llvm::errs() << llvm::formatv( "error: rejecting incoming connection from {0} (expecting {1})", AcceptAddr.GetIPAddress(), AddrIn.GetIPAddress()); continue; } accept_connection = true; accepted_socket.reset(new TCPSocket(sock, *this)); } if (!accepted_socket) return error; // Keep our TCP packets coming without any delays. accepted_socket->SetOptionNoDelay(); error.Clear(); conn_socket = accepted_socket.release(); return error; } int TCPSocket::SetOptionNoDelay() { return SetOption(IPPROTO_TCP, TCP_NODELAY, 1); } int TCPSocket::SetOptionReuseAddress() { return SetOption(SOL_SOCKET, SO_REUSEADDR, 1); } Index: vendor/lldb/dist/source/Host/linux/Host.cpp =================================================================== --- vendor/lldb/dist/source/Host/linux/Host.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/linux/Host.cpp (revision 321194) @@ -1,310 +1,311 @@ //===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #include #include #include #include #include #include #include +#include // C++ Includes // Other libraries and framework includes #include "llvm/Support/ScopedPrinter.h" // Project includes #include "lldb/Target/Process.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/linux/Support.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Symbol/ObjectFile.h" using namespace lldb; using namespace lldb_private; namespace { enum class ProcessState { Unknown, DiskSleep, Paging, Running, Sleeping, TracedOrStopped, Zombie, }; } static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, ProcessState &State, ::pid_t &TracerPid) { auto BufferOrError = getProcFile(Pid, "status"); if (!BufferOrError) return false; llvm::StringRef Rest = BufferOrError.get()->getBuffer(); while(!Rest.empty()) { llvm::StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); if (Line.consume_front("Gid:")) { // Real, effective, saved set, and file system GIDs. Read the first two. Line = Line.ltrim(); uint32_t RGid, EGid; Line.consumeInteger(10, RGid); Line = Line.ltrim(); Line.consumeInteger(10, EGid); ProcessInfo.SetGroupID(RGid); ProcessInfo.SetEffectiveGroupID(EGid); } else if (Line.consume_front("Uid:")) { // Real, effective, saved set, and file system UIDs. Read the first two. Line = Line.ltrim(); uint32_t RUid, EUid; Line.consumeInteger(10, RUid); Line = Line.ltrim(); Line.consumeInteger(10, EUid); ProcessInfo.SetUserID(RUid); ProcessInfo.SetEffectiveUserID(EUid); } else if (Line.consume_front("PPid:")) { ::pid_t PPid; Line.ltrim().consumeInteger(10, PPid); ProcessInfo.SetParentProcessID(PPid); } else if (Line.consume_front("State:")) { char S = Line.ltrim().front(); switch (S) { case 'R': State = ProcessState::Running; break; case 'S': State = ProcessState::Sleeping; break; case 'D': State = ProcessState::DiskSleep; break; case 'Z': State = ProcessState::Zombie; break; case 'T': State = ProcessState::TracedOrStopped; break; case 'W': State = ProcessState::Paging; break; } } else if (Line.consume_front("TracerPid:")) { Line = Line.ltrim(); Line.consumeInteger(10, TracerPid); } } return true; } static bool IsDirNumeric(const char *dname) { for (; *dname; dname++) { if (!isdigit(*dname)) return false; } return true; } static bool GetELFProcessCPUType(llvm::StringRef exe_path, ProcessInstanceInfo &process_info) { // Clear the architecture. process_info.GetArchitecture().Clear(); ModuleSpecList specs; FileSpec filespec(exe_path, false); const size_t num_specs = ObjectFile::GetModuleSpecifications(filespec, 0, 0, specs); // GetModuleSpecifications() could fail if the executable has been deleted or // is locked. // But it shouldn't return more than 1 architecture. assert(num_specs <= 1 && "Linux plugin supports only a single architecture"); if (num_specs == 1) { ModuleSpec module_spec; if (specs.GetModuleSpecAtIndex(0, module_spec) && module_spec.GetArchitecture().IsValid()) { process_info.GetArchitecture() = module_spec.GetArchitecture(); return true; } } return false; } static bool GetProcessAndStatInfo(::pid_t pid, ProcessInstanceInfo &process_info, ProcessState &State, ::pid_t &tracerpid) { tracerpid = 0; process_info.Clear(); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); // We can't use getProcFile here because proc/[pid]/exe is a symbolic link. llvm::SmallString<64> ProcExe; (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe); std::string ExePath(PATH_MAX, '\0'); ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX); if (len <= 0) { LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid, Status(errno, eErrorTypePOSIX)); return false; } ExePath.resize(len); // If the binary has been deleted, the link name has " (deleted)" appended. // Remove if there. llvm::StringRef PathRef = ExePath; PathRef.consume_back(" (deleted)"); GetELFProcessCPUType(PathRef, process_info); // Get the process environment. auto BufferOrError = getProcFile(pid, "environ"); if (!BufferOrError) return false; std::unique_ptr Environ = std::move(*BufferOrError); // Get the command line used to start the process. BufferOrError = getProcFile(pid, "cmdline"); if (!BufferOrError) return false; std::unique_ptr Cmdline = std::move(*BufferOrError); // Get User and Group IDs and get tracer pid. if (!GetStatusInfo(pid, process_info, State, tracerpid)) return false; process_info.SetProcessID(pid); process_info.GetExecutableFile().SetFile(PathRef, false); process_info.GetArchitecture().MergeFrom(HostInfo::GetArchitecture()); llvm::StringRef Rest = Environ->getBuffer(); while (!Rest.empty()) { llvm::StringRef Var; std::tie(Var, Rest) = Rest.split('\0'); process_info.GetEnvironmentEntries().AppendArgument(Var); } llvm::StringRef Arg0; std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0'); process_info.SetArg0(Arg0); while (!Rest.empty()) { llvm::StringRef Arg; std::tie(Arg, Rest) = Rest.split('\0'); process_info.GetArguments().AppendArgument(Arg); } return true; } uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { static const char procdir[] = "/proc/"; DIR *dirproc = opendir(procdir); if (dirproc) { struct dirent *direntry = NULL; const uid_t our_uid = getuid(); const lldb::pid_t our_pid = getpid(); bool all_users = match_info.GetMatchAllUsers(); while ((direntry = readdir(dirproc)) != NULL) { if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name)) continue; lldb::pid_t pid = atoi(direntry->d_name); // Skip this process. if (pid == our_pid) continue; ::pid_t tracerpid; ProcessState State; ProcessInstanceInfo process_info; if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid)) continue; // Skip if process is being debugged. if (tracerpid != 0) continue; if (State == ProcessState::Zombie) continue; // Check for user match if we're not matching all users and not running as // root. if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) continue; if (match_info.Matches(process_info)) { process_infos.Append(process_info); } } closedir(dirproc); } return process_infos.GetSize(); } bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { bool tids_changed = false; static const char procdir[] = "/proc/"; static const char taskdir[] = "/task/"; std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir; DIR *dirproc = opendir(process_task_dir.c_str()); if (dirproc) { struct dirent *direntry = NULL; while ((direntry = readdir(dirproc)) != NULL) { if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name)) continue; lldb::tid_t tid = atoi(direntry->d_name); TidMap::iterator it = tids_to_attach.find(tid); if (it == tids_to_attach.end()) { tids_to_attach.insert(TidPair(tid, false)); tids_changed = true; } } closedir(dirproc); } return tids_changed; } bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { ::pid_t tracerpid; ProcessState State; return GetProcessAndStatInfo(pid, process_info, State, tracerpid); } size_t Host::GetEnvironment(StringList &env) { char **host_env = environ; char *env_entry; size_t i; for (i = 0; (env_entry = host_env[i]) != NULL; ++i) env.AppendString(env_entry); return i; } Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { return Status("unimplemented"); } Index: vendor/lldb/dist/source/Host/linux/HostInfoLinux.cpp =================================================================== --- vendor/lldb/dist/source/Host/linux/HostInfoLinux.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/linux/HostInfoLinux.cpp (revision 321194) @@ -1,243 +1,244 @@ //===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/linux/HostInfoLinux.h" #include "lldb/Utility/Log.h" #include "llvm/Support/Threading.h" #include #include #include #include +#include #include #include // std::once using namespace lldb_private; namespace { struct HostInfoLinuxFields { HostInfoLinuxFields() : m_os_major(0), m_os_minor(0), m_os_update(0) {} std::string m_distribution_id; uint32_t m_os_major; uint32_t m_os_minor; uint32_t m_os_update; }; HostInfoLinuxFields *g_fields = nullptr; } void HostInfoLinux::Initialize() { HostInfoPosix::Initialize(); g_fields = new HostInfoLinuxFields(); } bool HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { static bool success = false; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { struct utsname un; if (uname(&un) == 0) { int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); if (status == 3) success = true; else { // Some kernels omit the update version, so try looking for just "X.Y" // and // set update to 0. g_fields->m_os_update = 0; status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); if (status == 2) success = true; } } }); major = g_fields->m_os_major; minor = g_fields->m_os_minor; update = g_fields->m_os_update; return success; } bool HostInfoLinux::GetOSBuildString(std::string &s) { struct utsname un; ::memset(&un, 0, sizeof(utsname)); s.clear(); if (uname(&un) < 0) return false; s.assign(un.release); return true; } bool HostInfoLinux::GetOSKernelDescription(std::string &s) { struct utsname un; ::memset(&un, 0, sizeof(utsname)); s.clear(); if (uname(&un) < 0) return false; s.assign(un.version); return true; } llvm::StringRef HostInfoLinux::GetDistributionId() { // Try to run 'lbs_release -i', and use that response // for the distribution id. static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); if (log) log->Printf("attempting to determine Linux distribution..."); // check if the lsb_release command exists at one of the // following paths const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { const char *const get_distribution_info_exe = exe_paths[exe_index]; if (access(get_distribution_info_exe, F_OK)) { // this exe doesn't exist, move on to next exe if (log) log->Printf("executable doesn't exist: %s", get_distribution_info_exe); continue; } // execute the distribution-retrieval command, read output std::string get_distribution_id_command(get_distribution_info_exe); get_distribution_id_command += " -i"; FILE *file = popen(get_distribution_id_command.c_str(), "r"); if (!file) { if (log) log->Printf("failed to run command: \"%s\", cannot retrieve " "platform information", get_distribution_id_command.c_str()); break; } // retrieve the distribution id string. char distribution_id[256] = {'\0'}; if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) { if (log) log->Printf("distribution id command returned \"%s\"", distribution_id); const char *const distributor_id_key = "Distributor ID:\t"; if (strstr(distribution_id, distributor_id_key)) { // strip newlines std::string id_string(distribution_id + strlen(distributor_id_key)); id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); // lower case it and convert whitespace to underscores std::transform( id_string.begin(), id_string.end(), id_string.begin(), [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); g_fields->m_distribution_id = id_string; if (log) log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); } else { if (log) log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); } } else { if (log) log->Printf("failed to retrieve distribution id, \"%s\" returned no" " lines", get_distribution_id_command.c_str()); } // clean up the file pclose(file); } }); return g_fields->m_distribution_id; } FileSpec HostInfoLinux::GetProgramFileSpec() { static FileSpec g_program_filespec; if (!g_program_filespec) { char exe_path[PATH_MAX]; ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); if (len > 0) { exe_path[len] = 0; g_program_filespec.SetFile(exe_path, false); } } return g_program_filespec; } bool HostInfoLinux::ComputeSupportExeDirectory(FileSpec &file_spec) { if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && file_spec.IsAbsolute() && file_spec.Exists()) return true; file_spec.GetDirectory() = GetProgramFileSpec().GetDirectory(); return !file_spec.GetDirectory().IsEmpty(); } bool HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) { FileSpec temp_file("/usr/lib/lldb/plugins", true); file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); return true; } bool HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) { // XDG Base Directory Specification // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. const char *xdg_data_home = getenv("XDG_DATA_HOME"); if (xdg_data_home && xdg_data_home[0]) { std::string user_plugin_dir(xdg_data_home); user_plugin_dir += "/lldb"; file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); } else file_spec.GetDirectory().SetCString("~/.local/share/lldb"); return true; } void HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) { HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); const char *distribution_id = GetDistributionId().data(); // On Linux, "unknown" in the vendor slot isn't what we want for the default // triple. It's probably an artifact of config.guess. if (arch_32.IsValid()) { arch_32.SetDistributionId(distribution_id); if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) arch_32.GetTriple().setVendorName(llvm::StringRef()); } if (arch_64.IsValid()) { arch_64.SetDistributionId(distribution_id); if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) arch_64.GetTriple().setVendorName(llvm::StringRef()); } } Index: vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp (revision 321194) @@ -1,784 +1,785 @@ //===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(__APPLE__) // Enable this special support for Apple builds where we can have unlimited // select bounds. We tried switching to poll() and kqueue and we were panicing // the kernel, so we have to stick with select for now. #define _DARWIN_UNLIMITED_SELECT #endif #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" #include "lldb/Host/Config.h" #include "lldb/Host/Socket.h" #include "lldb/Host/SocketAddress.h" #include "lldb/Utility/SelectHelper.h" #include "lldb/Utility/Timeout.h" // C Includes #include #include #include #include #include #ifndef LLDB_DISABLE_POSIX #include +#include #endif // C++ Includes #include // Other libraries and framework includes #include "llvm/Support/Errno.h" #include "llvm/Support/ErrorHandling.h" #if defined(__APPLE__) #include "llvm/ADT/SmallVector.h" #endif // Project includes #include "lldb/Host/Host.h" #include "lldb/Host/Socket.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" using namespace lldb; using namespace lldb_private; const char *ConnectionFileDescriptor::LISTEN_SCHEME = "listen"; const char *ConnectionFileDescriptor::ACCEPT_SCHEME = "accept"; const char *ConnectionFileDescriptor::UNIX_ACCEPT_SCHEME = "unix-accept"; const char *ConnectionFileDescriptor::CONNECT_SCHEME = "connect"; const char *ConnectionFileDescriptor::TCP_CONNECT_SCHEME = "tcp-connect"; const char *ConnectionFileDescriptor::UDP_SCHEME = "udp"; const char *ConnectionFileDescriptor::UNIX_CONNECT_SCHEME = "unix-connect"; const char *ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME = "unix-abstract-connect"; const char *ConnectionFileDescriptor::FD_SCHEME = "fd"; const char *ConnectionFileDescriptor::FILE_SCHEME = "file"; namespace { llvm::Optional GetURLAddress(llvm::StringRef url, llvm::StringRef scheme) { if (!url.consume_front(scheme)) return llvm::None; if (!url.consume_front("://")) return llvm::None; return url; } } ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(child_processes_inherit) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast(this)); } ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(false) { m_write_sp.reset(new File(fd, owns_fd)); m_read_sp.reset(new File(fd, false)); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = " "%i, owns_fd = %i)", static_cast(this), fd, owns_fd); OpenCommandPipe(); } ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(false) { InitializeSocket(socket); } ConnectionFileDescriptor::~ConnectionFileDescriptor() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast(this)); Disconnect(NULL); CloseCommandPipe(); } void ConnectionFileDescriptor::OpenCommandPipe() { CloseCommandPipe(); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); // Make the command file descriptor here: Status result = m_pipe.CreateNew(m_child_processes_inherit); if (!result.Success()) { if (log) log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not " "make pipe: %s", static_cast(this), result.AsCString()); } else { if (log) log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success " "readfd=%d writefd=%d", static_cast(this), m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor()); } } void ConnectionFileDescriptor::CloseCommandPipe() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast(this)); m_pipe.Close(); } bool ConnectionFileDescriptor::IsConnected() const { return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); } ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, Status *error_ptr) { std::lock_guard guard(m_mutex); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast(this), path.str().c_str()); OpenCommandPipe(); if (!path.empty()) { llvm::Optional addr; if ((addr = GetURLAddress(path, LISTEN_SCHEME))) { // listen://HOST:PORT return SocketListenAndAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, ACCEPT_SCHEME))) { // unix://SOCKNAME return NamedSocketAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_ACCEPT_SCHEME))) { // unix://SOCKNAME return NamedSocketAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, CONNECT_SCHEME))) { return ConnectTCP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, TCP_CONNECT_SCHEME))) { return ConnectTCP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UDP_SCHEME))) { return ConnectUDP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_CONNECT_SCHEME))) { // unix-connect://SOCKNAME return NamedSocketConnect(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_ABSTRACT_CONNECT_SCHEME))) { // unix-abstract-connect://SOCKNAME return UnixAbstractSocketConnect(*addr, error_ptr); } #ifndef LLDB_DISABLE_POSIX else if ((addr = GetURLAddress(path, FD_SCHEME))) { // Just passing a native file descriptor within this current process // that is already opened (possibly from a service or other source). int fd = -1; if (!addr->getAsInteger(0, fd)) { // We have what looks to be a valid file descriptor, but we // should make sure it is. We currently are doing this by trying to // get the flags from the file descriptor and making sure it // isn't a bad fd. errno = 0; int flags = ::fcntl(fd, F_GETFL, 0); if (flags == -1 || errno == EBADF) { if (error_ptr) error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", path.str().c_str()); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; } else { // Don't take ownership of a file descriptor that gets passed // to us since someone else opened the file descriptor and // handed it to us. // TODO: Since are using a URL to open connection we should // eventually parse options using the web standard where we // have "fd://123?opt1=value;opt2=value" and we can have an // option be "owns=1" or "owns=0" or something like this to // allow us to specify this. For now, we assume we must // assume we don't own it. std::unique_ptr tcp_socket; tcp_socket.reset(new TCPSocket(fd, false, false)); // Try and get a socket option from this file descriptor to // see if this is a socket and set m_is_socket accordingly. int resuse; bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); if (is_socket) { m_read_sp = std::move(tcp_socket); m_write_sp = m_read_sp; } else { m_read_sp.reset(new File(fd, false)); m_write_sp.reset(new File(fd, false)); } m_uri = *addr; return eConnectionStatusSuccess; } } if (error_ptr) error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"", path.str().c_str()); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; } else if ((addr = GetURLAddress(path, FILE_SCHEME))) { std::string addr_str = addr->str(); // file:///PATH int fd = llvm::sys::RetryAfterSignal(-1, ::open, addr_str.c_str(), O_RDWR); if (fd == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); return eConnectionStatusError; } if (::isatty(fd)) { // Set up serial terminal emulation struct termios options; ::tcgetattr(fd, &options); // Set port speed to maximum ::cfsetospeed(&options, B115200); ::cfsetispeed(&options, B115200); // Raw input, disable echo and signals options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Make sure only one character is needed to return from a read options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; ::tcsetattr(fd, TCSANOW, &options); } int flags = ::fcntl(fd, F_GETFL, 0); if (flags >= 0) { if ((flags & O_NONBLOCK) == 0) { flags |= O_NONBLOCK; ::fcntl(fd, F_SETFL, flags); } } m_read_sp.reset(new File(fd, true)); m_write_sp.reset(new File(fd, false)); return eConnectionStatusSuccess; } #endif if (error_ptr) error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", path.str().c_str()); return eConnectionStatusError; } if (error_ptr) error_ptr->SetErrorString("invalid connect arguments"); return eConnectionStatusError; } bool ConnectionFileDescriptor::InterruptRead() { size_t bytes_written = 0; Status result = m_pipe.Write("i", 1, bytes_written); return result.Success(); } ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast(this)); ConnectionStatus status = eConnectionStatusSuccess; if (!IsConnected()) { if (log) log->Printf( "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast(this)); return eConnectionStatusSuccess; } if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) static_cast(*m_read_sp).PreDisconnect(); // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite // likely // because somebody is doing a blocking read on our file descriptor. If // that's the case, // then send the "q" char to the command file channel so the read will wake up // and the connection // will then know to shut down. m_shutting_down = true; std::unique_lock locker(m_mutex, std::defer_lock); if (!locker.try_lock()) { if (m_pipe.CanWrite()) { size_t bytes_written = 0; Status result = m_pipe.Write("q", 1, bytes_written); if (log) log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get " "the lock, sent 'q' to %d, error = '%s'.", static_cast(this), m_pipe.GetWriteFileDescriptor(), result.AsCString()); } else if (log) { log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the " "lock, but no command pipe is available.", static_cast(this)); } locker.lock(); } Status error = m_read_sp->Close(); Status error2 = m_write_sp->Close(); if (error.Fail() || error2.Fail()) status = eConnectionStatusError; if (error_ptr) *error_ptr = error.Fail() ? error : error2; // Close any pipes we were using for async interrupts m_pipe.Close(); m_uri.clear(); m_shutting_down = false; return status; } size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len, const Timeout &timeout, ConnectionStatus &status, Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); std::unique_lock locker(m_mutex, std::defer_lock); if (!locker.try_lock()) { if (log) log->Printf("%p ConnectionFileDescriptor::Read () failed to get the " "connection lock.", static_cast(this)); if (error_ptr) error_ptr->SetErrorString("failed to get the connection lock for read."); status = eConnectionStatusTimedOut; return 0; } if (m_shutting_down) { status = eConnectionStatusError; return 0; } status = BytesAvailable(timeout, error_ptr); if (status != eConnectionStatusSuccess) return 0; Status error; size_t bytes_read = dst_len; error = m_read_sp->Read(dst, bytes_read); if (log) { log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", static_cast(this), static_cast(m_read_sp->GetWaitableHandle()), static_cast(dst), static_cast(dst_len), static_cast(bytes_read), error.AsCString()); } if (bytes_read == 0) { error.Clear(); // End-of-file. Do not automatically close; pass along for // the end-of-file handlers. status = eConnectionStatusEndOfFile; } if (error_ptr) *error_ptr = error; if (error.Fail()) { uint32_t error_value = error.GetError(); switch (error_value) { case EAGAIN: // The file was marked for non-blocking I/O, and no data were // ready to be read. if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) status = eConnectionStatusTimedOut; else status = eConnectionStatusSuccess; return 0; case EFAULT: // Buf points outside the allocated address space. case EINTR: // A read from a slow device was interrupted before any data // arrived by the delivery of a signal. case EINVAL: // The pointer associated with fildes was negative. case EIO: // An I/O error occurred while reading from the file system. // The process group is orphaned. // The file is a regular file, nbyte is greater than 0, // the starting position is before the end-of-file, and // the starting position is greater than or equal to the // offset maximum established for the open file // descriptor associated with fildes. case EISDIR: // An attempt is made to read a directory. case ENOBUFS: // An attempt to allocate a memory buffer fails. case ENOMEM: // Insufficient memory is available. status = eConnectionStatusError; break; // Break to close.... case ENOENT: // no such file or directory case EBADF: // fildes is not a valid file or socket descriptor open for // reading. case ENXIO: // An action is requested of a device that does not exist.. // A requested action cannot be performed by the device. case ECONNRESET: // The connection is closed by the peer during a read // attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a // socket. status = eConnectionStatusTimedOut; return 0; default: LLDB_LOG(log, "this = {0}, unexpected error: {1}", this, llvm::sys::StrError(error_value)); status = eConnectionStatusError; break; // Break to close.... } return 0; } return bytes_read; } size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf( "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast(this), static_cast(src), static_cast(src_len)); if (!IsConnected()) { if (error_ptr) error_ptr->SetErrorString("not connected"); status = eConnectionStatusNoConnection; return 0; } Status error; size_t bytes_sent = src_len; error = m_write_sp->Write(src, bytes_sent); if (log) { log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", static_cast(this), static_cast(m_write_sp->GetWaitableHandle()), static_cast(src), static_cast(src_len), static_cast(bytes_sent), error.AsCString()); } if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EAGAIN: case EINTR: status = eConnectionStatusSuccess; return 0; case ECONNRESET: // The connection is closed by the peer during a read // attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... default: status = eConnectionStatusError; break; // Break to close.... } return 0; } status = eConnectionStatusSuccess; return bytes_sent; } std::string ConnectionFileDescriptor::GetURI() { return m_uri; } // This ConnectionFileDescriptor::BytesAvailable() uses select() via // SelectHelper // // PROS: // - select is consistent across most unix platforms // - The Apple specific version allows for unlimited fds in the fd_sets by // setting the _DARWIN_UNLIMITED_SELECT define prior to including the // required header files. // CONS: // - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. // This implementation will assert if it runs into that hard limit to let // users know that another ConnectionFileDescriptor::BytesAvailable() should // be used or a new version of ConnectionFileDescriptor::BytesAvailable() // should be written for the system that is running into the limitations. ConnectionStatus ConnectionFileDescriptor::BytesAvailable(const Timeout &timeout, Status *error_ptr) { // Don't need to take the mutex here separately since we are only called from // Read. If we // ever get used more generally we will need to lock here as well. Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout); // Make a copy of the file descriptors to make sure we don't // have another thread change these values out from under us // and cause problems in the loop below where like in FS_SET() const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); const int pipe_fd = m_pipe.GetReadFileDescriptor(); if (handle != IOObject::kInvalidHandleValue) { SelectHelper select_helper; if (timeout) select_helper.SetTimeout(*timeout); select_helper.FDSetRead(handle); #if defined(_MSC_VER) // select() won't accept pipes on Windows. The entire Windows codepath // needs to be // converted over to using WaitForMultipleObjects and event HANDLEs, but for // now at least // this will allow ::select() to not return an error. const bool have_pipe_fd = false; #else const bool have_pipe_fd = pipe_fd >= 0; #endif if (have_pipe_fd) select_helper.FDSetRead(pipe_fd); while (handle == m_read_sp->GetWaitableHandle()) { Status error = select_helper.Select(); if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EBADF: // One of the descriptor sets specified an invalid // descriptor. return eConnectionStatusLostConnection; case EINVAL: // The specified time limit is invalid. One of its // components is negative or too large. default: // Other unknown error return eConnectionStatusError; case ETIMEDOUT: return eConnectionStatusTimedOut; case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO case EINTR: // A signal was delivered before the time limit // expired and before any of the selected events // occurred. break; // Lets keep reading to until we timeout } } else { if (select_helper.FDIsSetRead(handle)) return eConnectionStatusSuccess; if (select_helper.FDIsSetRead(pipe_fd)) { // There is an interrupt or exit command in the command pipe // Read the data from that pipe: char c; ssize_t bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1); assert(bytes_read == 1); (void)bytes_read; switch (c) { case 'q': if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() " "got data: %c from the command channel.", static_cast(this), c); return eConnectionStatusEndOfFile; case 'i': // Interrupt the current read return eConnectionStatusInterrupted; } } } } } if (error_ptr) error_ptr->SetErrorString("not connected"); return eConnectionStatusLostConnection; } ConnectionStatus ConnectionFileDescriptor::NamedSocketAccept(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } lldb::ConnectionStatus ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixAbstractConnect(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s, Status *error_ptr) { m_port_predicate.SetValue(0, eBroadcastNever); Socket *socket = nullptr; m_waiting_for_accept = true; Status error = Socket::TcpListen(s, m_child_processes_inherit, socket, &m_port_predicate); if (error_ptr) *error_ptr = error; if (error.Fail()) return eConnectionStatusError; std::unique_ptr listening_socket_up; listening_socket_up.reset(socket); socket = nullptr; error = listening_socket_up->Accept(socket); listening_socket_up.reset(); if (error_ptr) *error_ptr = error; if (error.Fail()) return eConnectionStatusError; InitializeSocket(socket); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::TcpConnect(s, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(s); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UdpConnect(s, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(s); return eConnectionStatusSuccess; } uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) { uint16_t bound_port = 0; if (timeout_sec == UINT32_MAX) m_port_predicate.WaitForValueNotEqualTo(0, bound_port); else m_port_predicate.WaitForValueNotEqualTo(0, bound_port, std::chrono::seconds(timeout_sec)); return bound_port; } bool ConnectionFileDescriptor::GetChildProcessesInherit() const { return m_child_processes_inherit; } void ConnectionFileDescriptor::SetChildProcessesInherit( bool child_processes_inherit) { m_child_processes_inherit = child_processes_inherit; } void ConnectionFileDescriptor::InitializeSocket(Socket *socket) { assert(socket->GetSocketProtocol() == Socket::ProtocolTcp); TCPSocket *tcp_socket = static_cast(socket); m_write_sp.reset(socket); m_read_sp = m_write_sp; StreamString strm; strm.Printf("connect://%s:%u", tcp_socket->GetRemoteIPAddress().c_str(), tcp_socket->GetRemotePortNumber()); m_uri = strm.GetString(); } Index: vendor/lldb/dist/source/Host/posix/FileSystem.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/FileSystem.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/posix/FileSystem.cpp (revision 321194) @@ -1,79 +1,80 @@ //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/FileSystem.h" // C includes #include #include #include #include #include +#include #ifdef __linux__ #include #include #include #endif #if defined(__NetBSD__) #include #endif // lldb Includes #include "lldb/Host/Host.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "llvm/Support/FileSystem.h" using namespace lldb; using namespace lldb_private; const char *FileSystem::DEV_NULL = "/dev/null"; Status FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { Status error; if (::symlink(dst.GetCString(), src.GetCString()) == -1) error.SetErrorToErrno(); return error; } Status FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { Status error; char buf[PATH_MAX]; ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1); if (count < 0) error.SetErrorToErrno(); else { buf[count] = '\0'; // Success dst.SetFile(buf, false); } return error; } Status FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) { char resolved_path[PATH_MAX]; if (!src.GetPath(resolved_path, sizeof(resolved_path))) { return Status("Couldn't get the canonical path for %s", src.GetCString()); } char real_path[PATH_MAX + 1]; if (realpath(resolved_path, real_path) == nullptr) { Status err; err.SetErrorToErrno(); return err; } dst = FileSpec(real_path, false); return Status(); } FILE *FileSystem::Fopen(const char *path, const char *mode) { return ::fopen(path, mode); } Index: vendor/lldb/dist/source/Host/posix/HostProcessPosix.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/HostProcessPosix.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/posix/HostProcessPosix.cpp (revision 321194) @@ -1,93 +1,95 @@ //===-- HostProcessPosix.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Host.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/posix/HostProcessPosix.h" #include "llvm/ADT/STLExtras.h" +#include #include +#include using namespace lldb_private; namespace { const int kInvalidPosixProcess = 0; } HostProcessPosix::HostProcessPosix() : HostNativeProcessBase(kInvalidPosixProcess) {} HostProcessPosix::HostProcessPosix(lldb::process_t process) : HostNativeProcessBase(process) {} HostProcessPosix::~HostProcessPosix() {} Status HostProcessPosix::Signal(int signo) const { if (m_process == kInvalidPosixProcess) { Status error; error.SetErrorString("HostProcessPosix refers to an invalid process"); return error; } return HostProcessPosix::Signal(m_process, signo); } Status HostProcessPosix::Signal(lldb::process_t process, int signo) { Status error; if (-1 == ::kill(process, signo)) error.SetErrorToErrno(); return error; } Status HostProcessPosix::Terminate() { return Signal(SIGKILL); } Status HostProcessPosix::GetMainModule(FileSpec &file_spec) const { Status error; // Use special code here because proc/[pid]/exe is a symbolic link. char link_path[PATH_MAX]; if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) != 1) { error.SetErrorString("Unable to build /proc//exe string"); return error; } error = FileSystem::Readlink(FileSpec{link_path, false}, file_spec); if (!error.Success()) return error; // If the binary has been deleted, the link name has " (deleted)" appended. // Remove if there. if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)")) { const char *filename = file_spec.GetFilename().GetCString(); static const size_t deleted_len = strlen(" (deleted)"); const size_t len = file_spec.GetFilename().GetLength(); file_spec.GetFilename().SetCStringWithLength(filename, len - deleted_len); } return error; } lldb::pid_t HostProcessPosix::GetProcessId() const { return m_process; } bool HostProcessPosix::IsRunning() const { if (m_process == kInvalidPosixProcess) return false; // Send this process the null signal. If it succeeds the process is running. Status error = Signal(0); return error.Success(); } HostThread HostProcessPosix::StartMonitoring( const Host::MonitorChildProcessCallback &callback, bool monitor_signals) { return Host::StartMonitoringChildProcess(callback, m_process, monitor_signals); } Index: vendor/lldb/dist/source/Host/posix/LockFilePosix.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/LockFilePosix.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/posix/LockFilePosix.cpp (revision 321194) @@ -1,60 +1,61 @@ //===-- LockFilePosix.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/posix/LockFilePosix.h" #include +#include using namespace lldb; using namespace lldb_private; namespace { Status fileLock(int fd, int cmd, int lock_type, const uint64_t start, const uint64_t len) { struct flock fl; fl.l_type = lock_type; fl.l_whence = SEEK_SET; fl.l_start = start; fl.l_len = len; fl.l_pid = ::getpid(); Status error; if (::fcntl(fd, cmd, &fl) == -1) error.SetErrorToErrno(); return error; } } // namespace LockFilePosix::LockFilePosix(int fd) : LockFileBase(fd) {} LockFilePosix::~LockFilePosix() { Unlock(); } Status LockFilePosix::DoWriteLock(const uint64_t start, const uint64_t len) { return fileLock(m_fd, F_SETLKW, F_WRLCK, start, len); } Status LockFilePosix::DoTryWriteLock(const uint64_t start, const uint64_t len) { return fileLock(m_fd, F_SETLK, F_WRLCK, start, len); } Status LockFilePosix::DoReadLock(const uint64_t start, const uint64_t len) { return fileLock(m_fd, F_SETLKW, F_RDLCK, start, len); } Status LockFilePosix::DoTryReadLock(const uint64_t start, const uint64_t len) { return fileLock(m_fd, F_SETLK, F_RDLCK, start, len); } Status LockFilePosix::DoUnlock() { return fileLock(m_fd, F_SETLK, F_UNLCK, m_start, m_len); } Index: vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp (revision 321193) +++ vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp (revision 321194) @@ -1,232 +1,234 @@ //===-- ProcessLauncherLinux.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/Pipe.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "llvm/Support/Errno.h" #include #include #include +#include #include +#include #ifdef __ANDROID__ #include #define PT_TRACE_ME PTRACE_TRACEME #endif #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 #include #elif defined(__linux__) #include #endif using namespace lldb; using namespace lldb_private; static void FixupEnvironment(Args &env) { #ifdef __ANDROID__ // If there is no PATH variable specified inside the environment then set the // path to /system/bin. It is required because the default path used by // execve() is wrong on android. static const char *path = "PATH="; for (auto &entry : env.entries()) { if (entry.ref.startswith(path)) return; } env.AppendArgument(llvm::StringRef("PATH=/system/bin")); #endif } static void LLVM_ATTRIBUTE_NORETURN ExitWithError(int error_fd, const char *operation) { int err = errno; llvm::raw_fd_ostream os(error_fd, true); os << operation << " failed: " << llvm::sys::StrError(err); os.flush(); _exit(1); } static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) { #if defined(__linux__) if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) { const unsigned long personality_get_current = 0xffffffff; int value = personality(personality_get_current); if (value == -1) ExitWithError(error_fd, "personality get"); value = personality(ADDR_NO_RANDOMIZE | value); if (value == -1) ExitWithError(error_fd, "personality set"); } #endif } static void DupDescriptor(int error_fd, const FileSpec &file_spec, int fd, int flags) { int target_fd = ::open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) ExitWithError(error_fd, "DupDescriptor-open"); if (target_fd == fd) return; if (::dup2(target_fd, fd) == -1) ExitWithError(error_fd, "DupDescriptor-dup2"); ::close(target_fd); return; } static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd, const ProcessLaunchInfo &info) { // First, make sure we disable all logging. If we are logging to stdout, our // logs can be mistaken for inferior output. Log::DisableAllLogChannels(); // Do not inherit setgid powers. if (setgid(getgid()) != 0) ExitWithError(error_fd, "setgid"); if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) { if (setpgid(0, 0) != 0) ExitWithError(error_fd, "setpgid"); } for (size_t i = 0; i < info.GetNumFileActions(); ++i) { const FileAction &action = *info.GetFileActionAtIndex(i); switch (action.GetAction()) { case FileAction::eFileActionClose: if (close(action.GetFD()) != 0) ExitWithError(error_fd, "close"); break; case FileAction::eFileActionDuplicate: if (dup2(action.GetFD(), action.GetActionArgument()) == -1) ExitWithError(error_fd, "dup2"); break; case FileAction::eFileActionOpen: DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(), action.GetActionArgument()); break; case FileAction::eFileActionNone: break; } } const char **argv = info.GetArguments().GetConstArgumentVector(); // Change working directory if (info.GetWorkingDirectory() && 0 != ::chdir(info.GetWorkingDirectory().GetCString())) ExitWithError(error_fd, "chdir"); DisableASLRIfRequested(error_fd, info); Args env = info.GetEnvironmentEntries(); FixupEnvironment(env); const char **envp = env.GetConstArgumentVector(); // Clear the signal mask to prevent the child from being affected by // any masking done by the parent. sigset_t set; if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) ExitWithError(error_fd, "pthread_sigmask"); if (info.GetFlags().Test(eLaunchFlagDebug)) { // HACK: // Close everything besides stdin, stdout, and stderr that has no file // action to avoid leaking. Only do this when debugging, as elsewhere we // actually rely on // passing open descriptors to child processes. for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) if (!info.GetFileActionForFD(fd) && fd != error_fd) close(fd); // Start tracing this child that is about to exec. if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); } // Execute. We should never return... execve(argv[0], const_cast(argv), const_cast(envp)); #if defined(__linux__) if (errno == ETXTBSY) { // On android M and earlier we can get this error because the adb deamon can // hold a write // handle on the executable even after it has finished uploading it. This // state lasts // only a short time and happens only when there are many concurrent adb // commands being // issued, such as when running the test suite. (The file remains open when // someone does // an "adb shell" command in the fork() child before it has had a chance to // exec.) Since // this state should clear up quickly, wait a while and then give it one // more go. usleep(50000); execve(argv[0], const_cast(argv), const_cast(envp)); } #endif // ...unless exec fails. In which case we definitely need to end the child // here. ExitWithError(error_fd, "execve"); } HostProcess ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) { char exe_path[PATH_MAX]; launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); // A pipe used by the child process to report errors. PipePosix pipe; const bool child_processes_inherit = false; error = pipe.CreateNew(child_processes_inherit); if (error.Fail()) return HostProcess(); ::pid_t pid = ::fork(); if (pid == -1) { // Fork failed error.SetErrorStringWithFormatv("Fork failed with error message: {0}", llvm::sys::StrError()); return HostProcess(LLDB_INVALID_PROCESS_ID); } if (pid == 0) { // child process pipe.CloseReadFileDescriptor(); ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info); } // parent process pipe.CloseWriteFileDescriptor(); char buf[1000]; int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf); if (r == 0) return HostProcess(pid); // No error. We're done. error.SetErrorString(buf); waitpid(pid, nullptr, 0); return HostProcess(); } Index: vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp (revision 321193) +++ vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp (revision 321194) @@ -1,3078 +1,3078 @@ //===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "CommandObjectScript.h" #include "lldb/Interpreter/CommandObjectRegexCommand.h" #include "../Commands/CommandObjectApropos.h" #include "../Commands/CommandObjectArgs.h" #include "../Commands/CommandObjectBreakpoint.h" #include "../Commands/CommandObjectBugreport.h" #include "../Commands/CommandObjectCommands.h" #include "../Commands/CommandObjectDisassemble.h" #include "../Commands/CommandObjectExpression.h" #include "../Commands/CommandObjectFrame.h" #include "../Commands/CommandObjectGUI.h" #include "../Commands/CommandObjectHelp.h" #include "../Commands/CommandObjectLanguage.h" #include "../Commands/CommandObjectLog.h" #include "../Commands/CommandObjectMemory.h" #include "../Commands/CommandObjectPlatform.h" #include "../Commands/CommandObjectPlugin.h" #include "../Commands/CommandObjectProcess.h" #include "../Commands/CommandObjectQuit.h" #include "../Commands/CommandObjectRegister.h" #include "../Commands/CommandObjectSettings.h" #include "../Commands/CommandObjectSource.h" #include "../Commands/CommandObjectSyntax.h" #include "../Commands/CommandObjectTarget.h" #include "../Commands/CommandObjectThread.h" #include "../Commands/CommandObjectType.h" #include "../Commands/CommandObjectVersion.h" #include "../Commands/CommandObjectWatchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" #ifndef LLDB_DISABLE_LIBEDIT #include "lldb/Host/Editline.h" #endif #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" #include "lldb/Target/Process.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/CleanUp.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" using namespace lldb; using namespace lldb_private; static const char *k_white_space = " \t\v"; static PropertyDefinition g_properties[] = { {"expand-regex-aliases", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, regular expression alias commands will show the " "expanded command that will be executed. This can be used to " "debug new regular expression alias commands."}, {"prompt-on-quit", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will prompt you before quitting if there are any live " "processes being debugged. If false, LLDB will quit without asking in any " "case."}, {"stop-command-source-on-error", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will stop running a 'command source' " "script upon encountering an error."}, {"space-repl-prompts", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, blank lines will be printed between between REPL submissions."}, {nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}}; enum { ePropertyExpandRegexAliases = 0, ePropertyPromptOnQuit = 1, ePropertyStopCmdSourceOnError = 2, eSpaceReplPrompts = 3 }; ConstString &CommandInterpreter::GetStaticBroadcasterClass() { static ConstString class_name("lldb.commandInterpreter"); return class_name; } CommandInterpreter::CommandInterpreter(Debugger &debugger, ScriptLanguage script_language, bool synchronous_execution) : Broadcaster(debugger.GetBroadcasterManager(), CommandInterpreter::GetStaticBroadcasterClass().AsCString()), Properties(OptionValuePropertiesSP( new OptionValueProperties(ConstString("interpreter")))), IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), m_debugger(debugger), m_synchronous_execution(synchronous_execution), m_skip_lldbinit_files(false), m_skip_app_init_files(false), m_script_interpreter_sp(), m_command_io_handler_sp(), m_comment_char('#'), m_batch_command_mode(false), m_truncation_warning(eNoTruncation), m_command_source_depth(0), m_num_errors(0), m_quit_requested(false), m_stopped_for_crash(false) { debugger.SetScriptLanguage(script_language); SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit"); SetEventName(eBroadcastBitResetPrompt, "reset-prompt"); SetEventName(eBroadcastBitQuitCommandReceived, "quit"); CheckInWithManager(); m_collection_sp->Initialize(g_properties); } bool CommandInterpreter::GetExpandRegexAliases() const { const uint32_t idx = ePropertyExpandRegexAliases; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool CommandInterpreter::GetPromptOnQuit() const { const uint32_t idx = ePropertyPromptOnQuit; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void CommandInterpreter::SetPromptOnQuit(bool b) { const uint32_t idx = ePropertyPromptOnQuit; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } void CommandInterpreter::ResolveCommand(const char *command_line, CommandReturnObject &result) { std::string command = command_line; if (ResolveCommandImpl(command, result) != nullptr) { result.AppendMessageWithFormat("%s", command.c_str()); result.SetStatus(eReturnStatusSuccessFinishResult); } } bool CommandInterpreter::GetStopCmdSourceOnError() const { const uint32_t idx = ePropertyStopCmdSourceOnError; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool CommandInterpreter::GetSpaceReplPrompts() const { const uint32_t idx = eSpaceReplPrompts; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void CommandInterpreter::Initialize() { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); CommandReturnObject result; LoadCommandDictionary(); // An alias arguments vector to reuse - reset it before use... OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector); // Set up some initial aliases. CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit", false); if (cmd_obj_sp) { AddAlias("q", cmd_obj_sp); AddAlias("exit", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-attach", false); if (cmd_obj_sp) AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("process detach", false); if (cmd_obj_sp) { AddAlias("detach", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("process continue", false); if (cmd_obj_sp) { AddAlias("c", cmd_obj_sp); AddAlias("continue", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-break", false); if (cmd_obj_sp) AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-tbreak", false); if (cmd_obj_sp) AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("thread step-inst", false); if (cmd_obj_sp) { AddAlias("stepi", cmd_obj_sp); AddAlias("si", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-inst-over", false); if (cmd_obj_sp) { AddAlias("nexti", cmd_obj_sp); AddAlias("ni", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-in", false); if (cmd_obj_sp) { AddAlias("s", cmd_obj_sp); AddAlias("step", cmd_obj_sp); CommandAlias *sif_alias = AddAlias( "sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1"); if (sif_alias) { sif_alias->SetHelp("Step through the current block, stopping if you step " "directly into a function whose name matches the " "TargetFunctionName."); sif_alias->SetSyntax("sif "); } } cmd_obj_sp = GetCommandSPExact("thread step-over", false); if (cmd_obj_sp) { AddAlias("n", cmd_obj_sp); AddAlias("next", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-out", false); if (cmd_obj_sp) { AddAlias("finish", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("frame select", false); if (cmd_obj_sp) { AddAlias("f", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread select", false); if (cmd_obj_sp) { AddAlias("t", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-jump", false); if (cmd_obj_sp) { AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); } cmd_obj_sp = GetCommandSPExact("_regexp-list", false); if (cmd_obj_sp) { AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); } cmd_obj_sp = GetCommandSPExact("_regexp-env", false); if (cmd_obj_sp) AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("memory read", false); if (cmd_obj_sp) AddAlias("x", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("_regexp-up", false); if (cmd_obj_sp) AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-down", false); if (cmd_obj_sp) AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-display", false); if (cmd_obj_sp) AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("disassemble", false); if (cmd_obj_sp) AddAlias("dis", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("disassemble", false); if (cmd_obj_sp) AddAlias("di", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("_regexp-undisplay", false); if (cmd_obj_sp) AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-bt", false); if (cmd_obj_sp) AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("target create", false); if (cmd_obj_sp) AddAlias("file", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("target modules", false); if (cmd_obj_sp) AddAlias("image", cmd_obj_sp); alias_arguments_vector_sp.reset(new OptionArgVector); cmd_obj_sp = GetCommandSPExact("expression", false); if (cmd_obj_sp) { AddAlias("p", cmd_obj_sp, "--")->SetHelpLong(""); AddAlias("print", cmd_obj_sp, "--")->SetHelpLong(""); AddAlias("call", cmd_obj_sp, "--")->SetHelpLong(""); if (auto po = AddAlias("po", cmd_obj_sp, "-O --")) { po->SetHelp("Evaluate an expression on the current thread. Displays any " "returned value with formatting " "controlled by the type's author."); po->SetHelpLong(""); } AddAlias("parray", cmd_obj_sp, "--element-count %1 --")->SetHelpLong(""); AddAlias("poarray", cmd_obj_sp, "--object-description --element-count %1 --") ->SetHelpLong(""); } cmd_obj_sp = GetCommandSPExact("process kill", false); if (cmd_obj_sp) { AddAlias("kill", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("process launch", false); if (cmd_obj_sp) { alias_arguments_vector_sp.reset(new OptionArgVector); #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) AddAlias("r", cmd_obj_sp, "--"); AddAlias("run", cmd_obj_sp, "--"); #else #if defined(__APPLE__) std::string shell_option; shell_option.append("--shell-expand-args"); shell_option.append(" true"); shell_option.append(" --"); AddAlias("r", cmd_obj_sp, "--shell-expand-args true --"); AddAlias("run", cmd_obj_sp, "--shell-expand-args true --"); #else StreamString defaultshell; defaultshell.Printf("--shell=%s --", HostInfo::GetDefaultShell().GetPath().c_str()); AddAlias("r", cmd_obj_sp, defaultshell.GetString()); AddAlias("run", cmd_obj_sp, defaultshell.GetString()); #endif #endif } cmd_obj_sp = GetCommandSPExact("target symbols add", false); if (cmd_obj_sp) { AddAlias("add-dsym", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("breakpoint set", false); if (cmd_obj_sp) { AddAlias("rbreak", cmd_obj_sp, "--func-regex %1"); } } void CommandInterpreter::Clear() { m_command_io_handler_sp.reset(); if (m_script_interpreter_sp) m_script_interpreter_sp->Clear(); } const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) { // This function has not yet been implemented. // Look for any embedded script command // If found, // get interpreter object from the command dictionary, // call execute_one_command on it, // get the results as a string, // substitute that string for current stuff. return arg; } void CommandInterpreter::LoadCommandDictionary() { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); lldb::ScriptLanguage script_language = m_debugger.GetScriptLanguage(); m_command_dict["apropos"] = CommandObjectSP(new CommandObjectApropos(*this)); m_command_dict["breakpoint"] = CommandObjectSP(new CommandObjectMultiwordBreakpoint(*this)); m_command_dict["bugreport"] = CommandObjectSP(new CommandObjectMultiwordBugreport(*this)); m_command_dict["command"] = CommandObjectSP(new CommandObjectMultiwordCommands(*this)); m_command_dict["disassemble"] = CommandObjectSP(new CommandObjectDisassemble(*this)); m_command_dict["expression"] = CommandObjectSP(new CommandObjectExpression(*this)); m_command_dict["frame"] = CommandObjectSP(new CommandObjectMultiwordFrame(*this)); m_command_dict["gui"] = CommandObjectSP(new CommandObjectGUI(*this)); m_command_dict["help"] = CommandObjectSP(new CommandObjectHelp(*this)); m_command_dict["log"] = CommandObjectSP(new CommandObjectLog(*this)); m_command_dict["memory"] = CommandObjectSP(new CommandObjectMemory(*this)); m_command_dict["platform"] = CommandObjectSP(new CommandObjectPlatform(*this)); m_command_dict["plugin"] = CommandObjectSP(new CommandObjectPlugin(*this)); m_command_dict["process"] = CommandObjectSP(new CommandObjectMultiwordProcess(*this)); m_command_dict["quit"] = CommandObjectSP(new CommandObjectQuit(*this)); m_command_dict["register"] = CommandObjectSP(new CommandObjectRegister(*this)); m_command_dict["script"] = CommandObjectSP(new CommandObjectScript(*this, script_language)); m_command_dict["settings"] = CommandObjectSP(new CommandObjectMultiwordSettings(*this)); m_command_dict["source"] = CommandObjectSP(new CommandObjectMultiwordSource(*this)); m_command_dict["target"] = CommandObjectSP(new CommandObjectMultiwordTarget(*this)); m_command_dict["thread"] = CommandObjectSP(new CommandObjectMultiwordThread(*this)); m_command_dict["type"] = CommandObjectSP(new CommandObjectType(*this)); m_command_dict["version"] = CommandObjectSP(new CommandObjectVersion(*this)); m_command_dict["watchpoint"] = CommandObjectSP(new CommandObjectMultiwordWatchpoint(*this)); m_command_dict["language"] = CommandObjectSP(new CommandObjectLanguage(*this)); const char *break_regexes[][2] = { {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2"}, {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"}, {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"}, {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"}, {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}, {"^(-.*)$", "breakpoint set %1"}, {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%2' --shlib '%1'"}, {"^\\&(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1' --skip-prologue=0"}, {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}}; size_t num_regexes = llvm::array_lengthof(break_regexes); std::unique_ptr break_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-break", "Set a breakpoint using one of several shorthand formats.\n", "\n" "_regexp-break :\n" " main.c:12 // Break at line 12 of " "main.c\n\n" "_regexp-break \n" " 12 // Break at line 12 of current " "file\n\n" "_regexp-break 0x
\n" " 0x1234000 // Break at address " "0x1234000\n\n" "_regexp-break \n" " main // Break in 'main' after the " "prologue\n\n" "_regexp-break &\n" " &main // Break at first instruction " "in 'main'\n\n" "_regexp-break `\n" " libc.so`malloc // Break in 'malloc' from " "'libc.so'\n\n" "_regexp-break //\n" " /break here/ // Break on source lines in " "current file\n" " // containing text 'break " "here'.\n", 2, CommandCompletions::eSymbolCompletion | CommandCompletions::eSourceFileCompletion, false)); if (break_regex_cmd_ap.get()) { bool success = true; for (size_t i = 0; i < num_regexes; i++) { success = break_regex_cmd_ap->AddRegexCommand(break_regexes[i][0], break_regexes[i][1]); if (!success) break; } success = break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); if (success) { CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); m_command_dict[break_regex_cmd_sp->GetCommandName()] = break_regex_cmd_sp; } } std::unique_ptr tbreak_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-tbreak", "Set a one-shot breakpoint using one of several shorthand formats.\n", "\n" "_regexp-break :\n" " main.c:12 // Break at line 12 of " "main.c\n\n" "_regexp-break \n" " 12 // Break at line 12 of current " "file\n\n" "_regexp-break 0x
\n" " 0x1234000 // Break at address " "0x1234000\n\n" "_regexp-break \n" " main // Break in 'main' after the " "prologue\n\n" "_regexp-break &\n" " &main // Break at first instruction " "in 'main'\n\n" "_regexp-break `\n" " libc.so`malloc // Break in 'malloc' from " "'libc.so'\n\n" "_regexp-break //\n" " /break here/ // Break on source lines in " "current file\n" " // containing text 'break " "here'.\n", 2, CommandCompletions::eSymbolCompletion | CommandCompletions::eSourceFileCompletion, false)); if (tbreak_regex_cmd_ap.get()) { bool success = true; for (size_t i = 0; i < num_regexes; i++) { // If you add a resultant command string longer than 1024 characters be // sure to increase the size of this buffer. char buffer[1024]; int num_printed = snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o"); assert(num_printed < 1024); UNUSED_IF_ASSERT_DISABLED(num_printed); success = tbreak_regex_cmd_ap->AddRegexCommand(break_regexes[i][0], buffer); if (!success) break; } success = tbreak_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); if (success) { CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_ap.release()); m_command_dict[tbreak_regex_cmd_sp->GetCommandName()] = tbreak_regex_cmd_sp; } } std::unique_ptr attach_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-attach", "Attach to process by ID or name.", "_regexp-attach | ", 2, 0, false)); if (attach_regex_cmd_ap.get()) { if (attach_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "process attach --pid %1") && attach_regex_cmd_ap->AddRegexCommand( "^(-.*|.* -.*)$", "process attach %1") && // Any options that are // specified get passed to // 'process attach' attach_regex_cmd_ap->AddRegexCommand("^(.+)$", "process attach --name '%1'") && attach_regex_cmd_ap->AddRegexCommand("^$", "process attach")) { CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_ap.release()); m_command_dict[attach_regex_cmd_sp->GetCommandName()] = attach_regex_cmd_sp; } } std::unique_ptr down_regex_cmd_ap( new CommandObjectRegexCommand(*this, "_regexp-down", "Select a newer stack frame. Defaults to " "moving one frame, a numeric argument can " "specify an arbitrary number.", "_regexp-down []", 2, 0, false)); if (down_regex_cmd_ap.get()) { if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") && down_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r -%1")) { CommandObjectSP down_regex_cmd_sp(down_regex_cmd_ap.release()); m_command_dict[down_regex_cmd_sp->GetCommandName()] = down_regex_cmd_sp; } } std::unique_ptr up_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-up", "Select an older stack frame. Defaults to moving one " "frame, a numeric argument can specify an arbitrary number.", "_regexp-up []", 2, 0, false)); if (up_regex_cmd_ap.get()) { if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") && up_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) { CommandObjectSP up_regex_cmd_sp(up_regex_cmd_ap.release()); m_command_dict[up_regex_cmd_sp->GetCommandName()] = up_regex_cmd_sp; } } std::unique_ptr display_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-display", "Evaluate an expression at every stop (see 'help target stop-hook'.)", "_regexp-display expression", 2, 0, false)); if (display_regex_cmd_ap.get()) { if (display_regex_cmd_ap->AddRegexCommand( "^(.+)$", "target stop-hook add -o \"expr -- %1\"")) { CommandObjectSP display_regex_cmd_sp(display_regex_cmd_ap.release()); m_command_dict[display_regex_cmd_sp->GetCommandName()] = display_regex_cmd_sp; } } std::unique_ptr undisplay_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-undisplay", "Stop displaying expression at every " "stop (specified by stop-hook index.)", "_regexp-undisplay stop-hook-number", 2, 0, false)); if (undisplay_regex_cmd_ap.get()) { if (undisplay_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "target stop-hook delete %1")) { CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_ap.release()); m_command_dict[undisplay_regex_cmd_sp->GetCommandName()] = undisplay_regex_cmd_sp; } } std::unique_ptr connect_gdb_remote_cmd_ap( new CommandObjectRegexCommand( *this, "gdb-remote", "Connect to a process via remote GDB server. " "If no host is specifed, localhost is assumed.", "gdb-remote [:]", 2, 0, false)); if (connect_gdb_remote_cmd_ap.get()) { if (connect_gdb_remote_cmd_ap->AddRegexCommand( "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$", "process connect --plugin gdb-remote connect://%1:%2") && connect_gdb_remote_cmd_ap->AddRegexCommand( "^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1")) { CommandObjectSP command_sp(connect_gdb_remote_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr connect_kdp_remote_cmd_ap( new CommandObjectRegexCommand( *this, "kdp-remote", "Connect to a process via remote KDP server. " "If no UDP port is specified, port 41139 is " "assumed.", "kdp-remote [:]", 2, 0, false)); if (connect_kdp_remote_cmd_ap.get()) { if (connect_kdp_remote_cmd_ap->AddRegexCommand( "^([^:]+:[[:digit:]]+)$", "process connect --plugin kdp-remote udp://%1") && connect_kdp_remote_cmd_ap->AddRegexCommand( "^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) { CommandObjectSP command_sp(connect_kdp_remote_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr bt_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-bt", "Show the current thread's call stack. Any numeric argument " "displays at most that many " "frames. The argument 'all' displays all threads.", "bt [ | all]", 2, 0, false)); if (bt_regex_cmd_ap.get()) { // accept but don't document "bt -c " -- before bt was a regex // command if you wanted to backtrace // three frames you would do "bt -c 3" but the intention is to have this // emulate the gdb "bt" command and // so now "bt 3" is the preferred form, in line with gdb. if (bt_regex_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "thread backtrace -c %1") && bt_regex_cmd_ap->AddRegexCommand("^-c ([[:digit:]]+)$", "thread backtrace -c %1") && bt_regex_cmd_ap->AddRegexCommand("^all$", "thread backtrace all") && bt_regex_cmd_ap->AddRegexCommand("^$", "thread backtrace")) { CommandObjectSP command_sp(bt_regex_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr list_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-list", "List relevant source code using one of several shorthand formats.", "\n" "_regexp-list : // List around specific file/line\n" "_regexp-list // List current file around specified " "line\n" "_regexp-list // List specified function\n" "_regexp-list 0x
// List around specified address\n" "_regexp-list -[] // List previous lines\n" "_regexp-list // List subsequent lines", 2, CommandCompletions::eSourceFileCompletion, false)); if (list_regex_cmd_ap.get()) { if (list_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "source list --line %1") && list_regex_cmd_ap->AddRegexCommand( "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]" "]*$", "source list --file '%1' --line %2") && list_regex_cmd_ap->AddRegexCommand( "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "source list --address %1") && list_regex_cmd_ap->AddRegexCommand("^-[[:space:]]*$", "source list --reverse") && list_regex_cmd_ap->AddRegexCommand( "^-([[:digit:]]+)[[:space:]]*$", "source list --reverse --count %1") && list_regex_cmd_ap->AddRegexCommand("^(.+)$", "source list --name \"%1\"") && list_regex_cmd_ap->AddRegexCommand("^$", "source list")) { CommandObjectSP list_regex_cmd_sp(list_regex_cmd_ap.release()); m_command_dict[list_regex_cmd_sp->GetCommandName()] = list_regex_cmd_sp; } } std::unique_ptr env_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-env", "Shorthand for viewing and setting environment variables.", "\n" "_regexp-env // Show enrivonment\n" "_regexp-env = // Set an environment variable", 2, 0, false)); if (env_regex_cmd_ap.get()) { if (env_regex_cmd_ap->AddRegexCommand("^$", "settings show target.env-vars") && env_regex_cmd_ap->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$", "settings set target.env-vars %1")) { CommandObjectSP env_regex_cmd_sp(env_regex_cmd_ap.release()); m_command_dict[env_regex_cmd_sp->GetCommandName()] = env_regex_cmd_sp; } } std::unique_ptr jump_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-jump", "Set the program counter to a new address.", "\n" "_regexp-jump \n" "_regexp-jump + | -\n" "_regexp-jump :\n" "_regexp-jump *\n", 2, 0, false)); if (jump_regex_cmd_ap.get()) { if (jump_regex_cmd_ap->AddRegexCommand("^\\*(.*)$", "thread jump --addr %1") && jump_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "thread jump --line %1") && jump_regex_cmd_ap->AddRegexCommand("^([^:]+):([0-9]+)$", "thread jump --file %1 --line %2") && jump_regex_cmd_ap->AddRegexCommand("^([+\\-][0-9]+)$", "thread jump --by %1")) { CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_ap.release()); m_command_dict[jump_regex_cmd_sp->GetCommandName()] = jump_regex_cmd_sp; } } } int CommandInterpreter::GetCommandNamesMatchingPartialString( const char *cmd_str, bool include_aliases, StringList &matches) { AddNamesMatchingPartialString(m_command_dict, cmd_str, matches); if (include_aliases) { AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches); } return matches.GetSize(); } CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, bool exact, StringList *matches) const { CommandObjectSP command_sp; std::string cmd = cmd_str; if (HasCommands()) { auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) command_sp = pos->second; } if (include_aliases && HasAliases()) { auto alias_pos = m_alias_dict.find(cmd); if (alias_pos != m_alias_dict.end()) command_sp = alias_pos->second; } if (HasUserCommands()) { auto pos = m_user_dict.find(cmd); if (pos != m_user_dict.end()) command_sp = pos->second; } if (!exact && !command_sp) { // We will only get into here if we didn't find any exact matches. CommandObjectSP user_match_sp, alias_match_sp, real_match_sp; StringList local_matches; if (matches == nullptr) matches = &local_matches; unsigned int num_cmd_matches = 0; unsigned int num_alias_matches = 0; unsigned int num_user_matches = 0; // Look through the command dictionaries one by one, and if we get only one // match from any of // them in toto, then return that, otherwise return an empty CommandObjectSP // and the list of matches. if (HasCommands()) { num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str, *matches); } if (num_cmd_matches == 1) { cmd.assign(matches->GetStringAtIndex(0)); auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) real_match_sp = pos->second; } if (include_aliases && HasAliases()) { num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str, *matches); } if (num_alias_matches == 1) { cmd.assign(matches->GetStringAtIndex(num_cmd_matches)); auto alias_pos = m_alias_dict.find(cmd); if (alias_pos != m_alias_dict.end()) alias_match_sp = alias_pos->second; } if (HasUserCommands()) { num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str, *matches); } if (num_user_matches == 1) { cmd.assign( matches->GetStringAtIndex(num_cmd_matches + num_alias_matches)); auto pos = m_user_dict.find(cmd); if (pos != m_user_dict.end()) user_match_sp = pos->second; } // If we got exactly one match, return that, otherwise return the match // list. if (num_user_matches + num_cmd_matches + num_alias_matches == 1) { if (num_cmd_matches) return real_match_sp; else if (num_alias_matches) return alias_match_sp; else return user_match_sp; } } else if (matches && command_sp) { matches->AppendString(cmd_str); } return command_sp; } bool CommandInterpreter::AddCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) assert((this == &cmd_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); if (name.empty()) return false; std::string name_sstr(name); auto name_iter = m_command_dict.find(name_sstr); if (name_iter != m_command_dict.end()) { if (!can_replace || !name_iter->second->IsRemovable()) return false; name_iter->second = cmd_sp; } else { m_command_dict[name_sstr] = cmd_sp; } return true; } bool CommandInterpreter::AddUserCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) assert((this == &cmd_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); if (!name.empty()) { // do not allow replacement of internal commands if (CommandExists(name)) { if (can_replace == false) return false; if (m_command_dict[name]->IsRemovable() == false) return false; } if (UserCommandExists(name)) { if (can_replace == false) return false; if (m_user_dict[name]->IsRemovable() == false) return false; } m_user_dict[name] = cmd_sp; return true; } return false; } CommandObjectSP CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str, bool include_aliases) const { Args cmd_words(cmd_str); // Break up the command string into words, in case // it's a multi-word command. CommandObjectSP ret_val; // Possibly empty return value. if (cmd_str.empty()) return ret_val; if (cmd_words.GetArgumentCount() == 1) return GetCommandSP(cmd_str, include_aliases, true, nullptr); else { // We have a multi-word command (seemingly), so we need to do more work. // First, get the cmd_obj_sp for the first word in the command. CommandObjectSP cmd_obj_sp = GetCommandSP(llvm::StringRef(cmd_words.GetArgumentAtIndex(0)), include_aliases, true, nullptr); if (cmd_obj_sp.get() != nullptr) { // Loop through the rest of the words in the command (everything passed in // was supposed to be part of a // command name), and find the appropriate sub-command SP for each command // word.... size_t end = cmd_words.GetArgumentCount(); for (size_t j = 1; j < end; ++j) { if (cmd_obj_sp->IsMultiwordObject()) { cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(j)); if (cmd_obj_sp.get() == nullptr) // The sub-command name was invalid. Fail and return the empty // 'ret_val'. return ret_val; } else // We have more words in the command name, but we don't have a // multiword object. Fail and return empty 'ret_val'. return ret_val; } // We successfully looped through all the command words and got valid // command objects for them. Assign the last object retrieved to // 'ret_val'. ret_val = cmd_obj_sp; } } return ret_val; } CommandObject *CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, StringList *matches) const { CommandObject *command_obj = GetCommandSP(cmd_str, false, true, matches).get(); // If we didn't find an exact match to the command string in the commands, // look in // the aliases. if (command_obj) return command_obj; command_obj = GetCommandSP(cmd_str, true, true, matches).get(); if (command_obj) return command_obj; // If there wasn't an exact match then look for an inexact one in just the // commands command_obj = GetCommandSP(cmd_str, false, false, nullptr).get(); // Finally, if there wasn't an inexact match among the commands, look for an // inexact // match in both the commands and aliases. if (command_obj) { if (matches) matches->AppendString(command_obj->GetCommandName()); return command_obj; } return GetCommandSP(cmd_str, true, false, matches).get(); } bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const { return m_command_dict.find(cmd) != m_command_dict.end(); } bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd, std::string &full_name) const { bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end()); if (exact_match) { full_name.assign(cmd); return exact_match; } else { StringList matches; size_t num_alias_matches; num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd, matches); if (num_alias_matches == 1) { // Make sure this isn't shadowing a command in the regular command space: StringList regular_matches; const bool include_aliases = false; const bool exact = false; CommandObjectSP cmd_obj_sp( GetCommandSP(cmd, include_aliases, exact, ®ular_matches)); if (cmd_obj_sp || regular_matches.GetSize() > 0) return false; else { full_name.assign(matches.GetStringAtIndex(0)); return true; } } else return false; } } bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const { return m_alias_dict.find(cmd) != m_alias_dict.end(); } bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const { return m_user_dict.find(cmd) != m_user_dict.end(); } CommandAlias * CommandInterpreter::AddAlias(llvm::StringRef alias_name, lldb::CommandObjectSP &command_obj_sp, llvm::StringRef args_string) { if (command_obj_sp.get()) assert((this == &command_obj_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); std::unique_ptr command_alias_up( new CommandAlias(*this, command_obj_sp, args_string, alias_name)); if (command_alias_up && command_alias_up->IsValid()) { m_alias_dict[alias_name] = CommandObjectSP(command_alias_up.get()); return command_alias_up.release(); } return nullptr; } bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) { auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) { m_alias_dict.erase(pos); return true; } return false; } bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd) { auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) { if (pos->second->IsRemovable()) { // Only regular expression objects or python commands are removable m_command_dict.erase(pos); return true; } } return false; } bool CommandInterpreter::RemoveUser(llvm::StringRef alias_name) { CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name); if (pos != m_user_dict.end()) { m_user_dict.erase(pos); return true; } return false; } void CommandInterpreter::GetHelp(CommandReturnObject &result, uint32_t cmd_types) { llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue()); if (!help_prologue.empty()) { OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(), help_prologue); } CommandObject::CommandMap::const_iterator pos; size_t max_len = FindLongestCommandWord(m_command_dict); if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) { result.AppendMessage("Debugger commands:"); result.AppendMessage(""); for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) { if (!(cmd_types & eCommandTypesHidden) && (pos->first.compare(0, 1, "_") == 0)) continue; OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", pos->second->GetHelp(), max_len); } result.AppendMessage(""); } if (!m_alias_dict.empty() && ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) { result.AppendMessageWithFormat( "Current command abbreviations " "(type '%shelp command alias' for more info):\n", GetCommandPrefix()); result.AppendMessage(""); max_len = FindLongestCommandWord(m_alias_dict); for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end(); ++alias_pos) { OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--", alias_pos->second->GetHelp(), max_len); } result.AppendMessage(""); } if (!m_user_dict.empty() && ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) { result.AppendMessage("Current user-defined commands:"); result.AppendMessage(""); max_len = FindLongestCommandWord(m_user_dict); for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) { OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", pos->second->GetHelp(), max_len); } result.AppendMessage(""); } result.AppendMessageWithFormat( "For more information on any command, type '%shelp '.\n", GetCommandPrefix()); } CommandObject *CommandInterpreter::GetCommandObjectForCommand( llvm::StringRef &command_string) { // This function finds the final, lowest-level, alias-resolved command object // whose 'Execute' function will // eventually be invoked by the given command line. CommandObject *cmd_obj = nullptr; size_t start = command_string.find_first_not_of(k_white_space); size_t end = 0; bool done = false; while (!done) { if (start != std::string::npos) { // Get the next word from command_string. end = command_string.find_first_of(k_white_space, start); if (end == std::string::npos) end = command_string.size(); std::string cmd_word = command_string.substr(start, end - start); if (cmd_obj == nullptr) // Since cmd_obj is NULL we are on our first time through this loop. // Check to see if cmd_word is a valid command or alias. cmd_obj = GetCommandObject(cmd_word); else if (cmd_obj->IsMultiwordObject()) { // Our current object is a multi-word object; see if the cmd_word is a // valid sub-command for our object. CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject(cmd_word.c_str()); if (sub_cmd_obj) cmd_obj = sub_cmd_obj; else // cmd_word was not a valid sub-command word, so we are done done = true; } else // We have a cmd_obj and it is not a multi-word object, so we are done. done = true; // If we didn't find a valid command object, or our command object is not // a multi-word object, or we are at the end of the command_string, then // we are done. Otherwise, find the start of the next word. if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size()) done = true; else start = command_string.find_first_not_of(k_white_space, end); } else // Unable to find any more words. done = true; } command_string = command_string.substr(end); return cmd_obj; } static const char *k_valid_command_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; static void StripLeadingSpaces(std::string &s) { if (!s.empty()) { size_t pos = s.find_first_not_of(k_white_space); if (pos == std::string::npos) s.clear(); else if (pos == 0) return; s.erase(0, pos); } } static size_t FindArgumentTerminator(const std::string &s) { const size_t s_len = s.size(); size_t offset = 0; while (offset < s_len) { size_t pos = s.find("--", offset); if (pos == std::string::npos) break; if (pos > 0) { if (isspace(s[pos - 1])) { // Check if the string ends "\s--" (where \s is a space character) // or if we have "\s--\s". if ((pos + 2 >= s_len) || isspace(s[pos + 2])) { return pos; } } } offset = pos + 2; } return std::string::npos; } static bool ExtractCommand(std::string &command_string, std::string &command, std::string &suffix, char "e_char) { command.clear(); suffix.clear(); StripLeadingSpaces(command_string); bool result = false; quote_char = '\0'; if (!command_string.empty()) { const char first_char = command_string[0]; if (first_char == '\'' || first_char == '"') { quote_char = first_char; const size_t end_quote_pos = command_string.find(quote_char, 1); if (end_quote_pos == std::string::npos) { command.swap(command_string); command_string.erase(); } else { command.assign(command_string, 1, end_quote_pos - 1); if (end_quote_pos + 1 < command_string.size()) command_string.erase(0, command_string.find_first_not_of( k_white_space, end_quote_pos + 1)); else command_string.erase(); } } else { const size_t first_space_pos = command_string.find_first_of(k_white_space); if (first_space_pos == std::string::npos) { command.swap(command_string); command_string.erase(); } else { command.assign(command_string, 0, first_space_pos); command_string.erase(0, command_string.find_first_not_of( k_white_space, first_space_pos)); } } result = true; } if (!command.empty()) { // actual commands can't start with '-' or '_' if (command[0] != '-' && command[0] != '_') { size_t pos = command.find_first_not_of(k_valid_command_chars); if (pos > 0 && pos != std::string::npos) { suffix.assign(command.begin() + pos, command.end()); command.erase(pos); } } } return result; } CommandObject *CommandInterpreter::BuildAliasResult( llvm::StringRef alias_name, std::string &raw_input_string, std::string &alias_result, CommandReturnObject &result) { CommandObject *alias_cmd_obj = nullptr; Args cmd_args(raw_input_string); alias_cmd_obj = GetCommandObject(alias_name); StreamString result_str; if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) { alias_result.clear(); return alias_cmd_obj; } std::pair desugared = ((CommandAlias *)alias_cmd_obj)->Desugar(); OptionArgVectorSP option_arg_vector_sp = desugared.second; alias_cmd_obj = desugared.first.get(); std::string alias_name_str = alias_name; if ((cmd_args.GetArgumentCount() == 0) || (alias_name_str.compare(cmd_args.GetArgumentAtIndex(0)) != 0)) cmd_args.Unshift(alias_name_str); result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str()); if (!option_arg_vector_sp.get()) { alias_result = result_str.GetString(); return alias_cmd_obj; } OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); int value_type; std::string option; std::string value; for (const auto &entry : *option_arg_vector) { std::tie(option, value_type, value) = entry; if (option == "") { result_str.Printf(" %s", value.c_str()); continue; } result_str.Printf(" %s", option.c_str()); if (value_type == OptionParser::eNoArgument) continue; if (value_type != OptionParser::eOptionalArgument) result_str.Printf(" "); int index = GetOptionArgumentPosition(value.c_str()); if (index == 0) result_str.Printf("%s", value.c_str()); else if (static_cast(index) >= cmd_args.GetArgumentCount()) { result.AppendErrorWithFormat("Not enough arguments provided; you " "need at least %d arguments to use " "this alias.\n", index); result.SetStatus(eReturnStatusFailed); return nullptr; } else { size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index)); if (strpos != std::string::npos) raw_input_string = raw_input_string.erase( strpos, strlen(cmd_args.GetArgumentAtIndex(index))); result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index)); } } alias_result = result_str.GetString(); return alias_cmd_obj; } Status CommandInterpreter::PreprocessCommand(std::string &command) { // The command preprocessor needs to do things to the command // line before any parsing of arguments or anything else is done. // The only current stuff that gets preprocessed is anything enclosed // in backtick ('`') characters is evaluated as an expression and // the result of the expression must be a scalar that can be substituted // into the command. An example would be: // (lldb) memory read `$rsp + 20` Status error; // Status for any expressions that might not evaluate size_t start_backtick; size_t pos = 0; while ((start_backtick = command.find('`', pos)) != std::string::npos) { if (start_backtick > 0 && command[start_backtick - 1] == '\\') { // The backtick was preceded by a '\' character, remove the slash // and don't treat the backtick as the start of an expression command.erase(start_backtick - 1, 1); // No need to add one to start_backtick since we just deleted a char pos = start_backtick; } else { const size_t expr_content_start = start_backtick + 1; const size_t end_backtick = command.find('`', expr_content_start); if (end_backtick == std::string::npos) return error; else if (end_backtick == expr_content_start) { // Empty expression (two backticks in a row) command.erase(start_backtick, 2); } else { std::string expr_str(command, expr_content_start, end_backtick - expr_content_start); ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); // Get a dummy target to allow for calculator mode while processing // backticks. // This also helps break the infinite loop caused when target is null. if (!target) target = m_debugger.GetDummyTarget(); if (target) { ValueObjectSP expr_result_valobj_sp; EvaluateExpressionOptions options; options.SetCoerceToId(false); options.SetUnwindOnError(true); options.SetIgnoreBreakpoints(true); options.SetKeepInMemory(false); options.SetTryAllThreads(true); options.SetTimeout(llvm::None); ExpressionResults expr_result = target->EvaluateExpression( expr_str.c_str(), exe_ctx.GetFramePtr(), expr_result_valobj_sp, options); if (expr_result == eExpressionCompleted) { Scalar scalar; if (expr_result_valobj_sp) expr_result_valobj_sp = expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable( expr_result_valobj_sp->GetDynamicValueType(), true); if (expr_result_valobj_sp->ResolveValue(scalar)) { command.erase(start_backtick, end_backtick - start_backtick + 1); StreamString value_strm; const bool show_type = false; scalar.GetValue(&value_strm, show_type); size_t value_string_size = value_strm.GetSize(); if (value_string_size) { command.insert(start_backtick, value_strm.GetString()); pos = start_backtick + value_string_size; continue; } else { error.SetErrorStringWithFormat("expression value didn't result " "in a scalar value for the " "expression '%s'", expr_str.c_str()); } } else { error.SetErrorStringWithFormat("expression value didn't result " "in a scalar value for the " "expression '%s'", expr_str.c_str()); } } else { if (expr_result_valobj_sp) error = expr_result_valobj_sp->GetError(); if (error.Success()) { switch (expr_result) { case eExpressionSetupError: error.SetErrorStringWithFormat( "expression setup error for the expression '%s'", expr_str.c_str()); break; case eExpressionParseError: error.SetErrorStringWithFormat( "expression parse error for the expression '%s'", expr_str.c_str()); break; case eExpressionResultUnavailable: error.SetErrorStringWithFormat( "expression error fetching result for the expression '%s'", expr_str.c_str()); break; case eExpressionCompleted: break; case eExpressionDiscarded: error.SetErrorStringWithFormat( "expression discarded for the expression '%s'", expr_str.c_str()); break; case eExpressionInterrupted: error.SetErrorStringWithFormat( "expression interrupted for the expression '%s'", expr_str.c_str()); break; case eExpressionHitBreakpoint: error.SetErrorStringWithFormat( "expression hit breakpoint for the expression '%s'", expr_str.c_str()); break; case eExpressionTimedOut: error.SetErrorStringWithFormat( "expression timed out for the expression '%s'", expr_str.c_str()); break; case eExpressionStoppedForDebug: error.SetErrorStringWithFormat("expression stop at entry point " "for debugging for the " "expression '%s'", expr_str.c_str()); break; } } } } } if (error.Fail()) break; } } return error; } bool CommandInterpreter::HandleCommand(const char *command_line, LazyBool lazy_add_to_history, CommandReturnObject &result, ExecutionContext *override_context, bool repeat_on_empty_command, bool no_context_switching) { std::string command_string(command_line); std::string original_command_string(command_line); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMANDS)); llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")", command_line); if (log) log->Printf("Processing command: %s", command_line); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "Handling command: %s.", command_line); if (!no_context_switching) UpdateExecutionContext(override_context); bool add_to_history; if (lazy_add_to_history == eLazyBoolCalculate) add_to_history = (m_command_source_depth == 0); else add_to_history = (lazy_add_to_history == eLazyBoolYes); bool empty_command = false; bool comment_command = false; if (command_string.empty()) empty_command = true; else { const char *k_space_characters = "\t\n\v\f\r "; size_t non_space = command_string.find_first_not_of(k_space_characters); // Check for empty line or comment line (lines whose first // non-space character is the comment character for this interpreter) if (non_space == std::string::npos) empty_command = true; else if (command_string[non_space] == m_comment_char) comment_command = true; else if (command_string[non_space] == CommandHistory::g_repeat_char) { llvm::StringRef search_str(command_string); search_str = search_str.drop_front(non_space); if (auto hist_str = m_command_history.FindString(search_str)) { add_to_history = false; command_string = *hist_str; original_command_string = *hist_str; } else { result.AppendErrorWithFormat("Could not find entry: %s in history", command_string.c_str()); result.SetStatus(eReturnStatusFailed); return false; } } } if (empty_command) { if (repeat_on_empty_command) { if (m_command_history.IsEmpty()) { result.AppendError("empty command"); result.SetStatus(eReturnStatusFailed); return false; } else { command_line = m_repeat_command.c_str(); command_string = command_line; original_command_string = command_line; if (m_repeat_command.empty()) { result.AppendErrorWithFormat("No auto repeat.\n"); result.SetStatus(eReturnStatusFailed); return false; } } add_to_history = false; } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } } else if (comment_command) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } Status error(PreprocessCommand(command_string)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } // Phase 1. // Before we do ANY kind of argument processing, we need to figure out what // the real/final command object is for the specified command. This gets // complicated by the fact that the user could have specified an alias, and, // in translating the alias, there may also be command options and/or even // data (including raw text strings) that need to be found and inserted into // the command line as part of the translation. So this first step is plain // look-up and replacement, resulting in: // 1. the command object whose Execute method will actually be called // 2. a revised command string, with all substitutions and replacements // taken care of // From 1 above, we can determine whether the Execute function wants raw // input or not. CommandObject *cmd_obj = ResolveCommandImpl(command_string, result); // Although the user may have abbreviated the command, the command_string now // has the command expanded to the full name. For example, if the input // was "br s -n main", command_string is now "breakpoint set -n main". if (log) { llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : ""; log->Printf("HandleCommand, cmd_obj : '%s'", command_name.str().c_str()); log->Printf("HandleCommand, (revised) command_string: '%s'", command_string.c_str()); const bool wants_raw_input = (cmd_obj != NULL) ? cmd_obj->WantsRawCommandString() : false; log->Printf("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False"); } // Phase 2. // Take care of things like setting up the history command & calling the // appropriate Execute method on the // CommandObject, with the appropriate arguments. if (cmd_obj != nullptr) { if (add_to_history) { Args command_args(command_string); const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0); if (repeat_command != nullptr) m_repeat_command.assign(repeat_command); else m_repeat_command.assign(original_command_string); m_command_history.AppendString(original_command_string); } std::string remainder; const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size(); if (actual_cmd_name_len < command_string.length()) remainder = command_string.substr(actual_cmd_name_len); // Remove any initial spaces size_t pos = remainder.find_first_not_of(k_white_space); if (pos != 0 && pos != std::string::npos) remainder.erase(0, pos); if (log) log->Printf( "HandleCommand, command line after removing command name(s): '%s'", remainder.c_str()); cmd_obj->Execute(remainder.c_str(), result); } else { // We didn't find the first command object, so complete the first argument. Args command_args(command_string); StringList matches; int num_matches; int cursor_index = 0; int cursor_char_position = strlen(command_args.GetArgumentAtIndex(0)); bool word_complete; num_matches = HandleCompletionMatches(command_args, cursor_index, cursor_char_position, 0, -1, word_complete, matches); if (num_matches > 0) { std::string error_msg; error_msg.assign("ambiguous command '"); error_msg.append(command_args.GetArgumentAtIndex(0)); error_msg.append("'."); error_msg.append(" Possible completions:"); for (int i = 0; i < num_matches; i++) { error_msg.append("\n\t"); error_msg.append(matches.GetStringAtIndex(i)); } error_msg.append("\n"); result.AppendRawError(error_msg.c_str()); } else result.AppendErrorWithFormat("Unrecognized command '%s'.\n", command_args.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); } if (log) log->Printf("HandleCommand, command %s", (result.Succeeded() ? "succeeded" : "did not succeed")); return result.Succeeded(); } int CommandInterpreter::HandleCompletionMatches( Args &parsed_line, int &cursor_index, int &cursor_char_position, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) { int num_command_matches = 0; bool look_for_subcommand = false; // For any of the command completions a unique match will be a complete word. word_complete = true; if (cursor_index == -1) { // We got nothing on the command line, so return the list of commands bool include_aliases = true; num_command_matches = GetCommandNamesMatchingPartialString("", include_aliases, matches); } else if (cursor_index == 0) { // The cursor is in the first argument, so just do a lookup in the // dictionary. CommandObject *cmd_obj = GetCommandObject(parsed_line.GetArgumentAtIndex(0), &matches); num_command_matches = matches.GetSize(); if (num_command_matches == 1 && cmd_obj && cmd_obj->IsMultiwordObject() && matches.GetStringAtIndex(0) != nullptr && strcmp(parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) { if (parsed_line.GetArgumentCount() == 1) { word_complete = true; } else { look_for_subcommand = true; num_command_matches = 0; matches.DeleteStringAtIndex(0); parsed_line.AppendArgument(llvm::StringRef()); cursor_index++; cursor_char_position = 0; } } } if (cursor_index > 0 || look_for_subcommand) { // We are completing further on into a commands arguments, so find the // command and tell it // to complete the command. // First see if there is a matching initial command: CommandObject *command_object = GetCommandObject(parsed_line.GetArgumentAtIndex(0)); if (command_object == nullptr) { return 0; } else { parsed_line.Shift(); cursor_index--; num_command_matches = command_object->HandleCompletion( parsed_line, cursor_index, cursor_char_position, match_start_point, max_return_elements, word_complete, matches); } } return num_command_matches; } int CommandInterpreter::HandleCompletion( const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, StringList &matches) { // We parse the argument up to the cursor, so the last argument in parsed_line // is // the one containing the cursor, and the cursor is after the last character. Args parsed_line(llvm::StringRef(current_line, last_char - current_line)); Args partial_parsed_line( llvm::StringRef(current_line, cursor - current_line)); // Don't complete comments, and if the line we are completing is just the // history repeat character, // substitute the appropriate history line. const char *first_arg = parsed_line.GetArgumentAtIndex(0); if (first_arg) { if (first_arg[0] == m_comment_char) return 0; else if (first_arg[0] == CommandHistory::g_repeat_char) { if (auto hist_str = m_command_history.FindString(first_arg)) { matches.Clear(); matches.InsertStringAtIndex(0, *hist_str); return -2; } else return 0; } } int num_args = partial_parsed_line.GetArgumentCount(); int cursor_index = partial_parsed_line.GetArgumentCount() - 1; int cursor_char_position; if (cursor_index == -1) cursor_char_position = 0; else cursor_char_position = strlen(partial_parsed_line.GetArgumentAtIndex(cursor_index)); if (cursor > current_line && cursor[-1] == ' ') { // We are just after a space. If we are in an argument, then we will // continue // parsing, but if we are between arguments, then we have to complete // whatever the next // element would be. // We can distinguish the two cases because if we are in an argument (e.g. // because the space is // protected by a quote) then the space will also be in the parsed // argument... const char *current_elem = partial_parsed_line.GetArgumentAtIndex(cursor_index); if (cursor_char_position == 0 || current_elem[cursor_char_position - 1] != ' ') { parsed_line.InsertArgumentAtIndex(cursor_index + 1, llvm::StringRef(), '\0'); cursor_index++; cursor_char_position = 0; } } int num_command_matches; matches.Clear(); // Only max_return_elements == -1 is supported at present: assert(max_return_elements == -1); bool word_complete; num_command_matches = HandleCompletionMatches( parsed_line, cursor_index, cursor_char_position, match_start_point, max_return_elements, word_complete, matches); if (num_command_matches <= 0) return num_command_matches; if (num_args == 0) { // If we got an empty string, insert nothing. matches.InsertStringAtIndex(0, ""); } else { // Now figure out if there is a common substring, and if so put that in // element 0, otherwise // put an empty string in element 0. std::string command_partial_str; if (cursor_index >= 0) command_partial_str = parsed_line[cursor_index].ref.take_front(cursor_char_position); std::string common_prefix; matches.LongestCommonPrefix(common_prefix); const size_t partial_name_len = command_partial_str.size(); common_prefix.erase(0, partial_name_len); // If we matched a unique single command, add a space... // Only do this if the completer told us this was a complete word, // however... if (num_command_matches == 1 && word_complete) { char quote_char = parsed_line[cursor_index].quote; common_prefix = Args::EscapeLLDBCommandArgument(common_prefix, quote_char); if (quote_char != '\0') common_prefix.push_back(quote_char); common_prefix.push_back(' '); } matches.InsertStringAtIndex(0, common_prefix.c_str()); } return num_command_matches; } CommandInterpreter::~CommandInterpreter() {} void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) { EventSP prompt_change_event_sp( new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt))); ; BroadcastEvent(prompt_change_event_sp); if (m_command_io_handler_sp) m_command_io_handler_sp->SetPrompt(new_prompt); } bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) { // Check AutoConfirm first: if (m_debugger.GetAutoConfirm()) return default_answer; IOHandlerConfirm *confirm = new IOHandlerConfirm(m_debugger, message, default_answer); IOHandlerSP io_handler_sp(confirm); m_debugger.RunIOHandler(io_handler_sp); return confirm->GetResponse(); } const CommandAlias * CommandInterpreter::GetAlias(llvm::StringRef alias_name) const { OptionArgVectorSP ret_val; auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) return (CommandAlias *)pos->second.get(); return nullptr; } bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); } bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); } bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); } bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); } void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj, const char *alias_name, Args &cmd_args, std::string &raw_input_string, CommandReturnObject &result) { OptionArgVectorSP option_arg_vector_sp = GetAlias(alias_name)->GetOptionArguments(); bool wants_raw_input = alias_cmd_obj->WantsRawCommandString(); // Make sure that the alias name is the 0th element in cmd_args std::string alias_name_str = alias_name; if (alias_name_str.compare(cmd_args.GetArgumentAtIndex(0)) != 0) cmd_args.Unshift(alias_name_str); Args new_args(alias_cmd_obj->GetCommandName()); if (new_args.GetArgumentCount() == 2) new_args.Shift(); if (option_arg_vector_sp.get()) { if (wants_raw_input) { // We have a command that both has command options and takes raw input. // Make *sure* it has a // " -- " in the right place in the raw_input_string. size_t pos = raw_input_string.find(" -- "); if (pos == std::string::npos) { // None found; assume it goes at the beginning of the raw input string raw_input_string.insert(0, " -- "); } } OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); const size_t old_size = cmd_args.GetArgumentCount(); std::vector used(old_size + 1, false); used[0] = true; int value_type; std::string option; std::string value; for (const auto &option_entry : *option_arg_vector) { std::tie(option, value_type, value) = option_entry; if (option == "") { if (!wants_raw_input || (value != "--")) { // Since we inserted this above, make sure we don't insert it twice new_args.AppendArgument(value); } continue; } if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(option); if (value == "") continue; int index = GetOptionArgumentPosition(value.c_str()); if (index == 0) { // value was NOT a positional argument; must be a real value if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(value); else { char buffer[255]; ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(), value.c_str()); new_args.AppendArgument(llvm::StringRef(buffer)); } } else if (static_cast(index) >= cmd_args.GetArgumentCount()) { result.AppendErrorWithFormat("Not enough arguments provided; you " "need at least %d arguments to use " "this alias.\n", index); result.SetStatus(eReturnStatusFailed); return; } else { // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index)); if (strpos != std::string::npos) { raw_input_string = raw_input_string.erase( strpos, strlen(cmd_args.GetArgumentAtIndex(index))); } if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index)); else { char buffer[255]; ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(), cmd_args.GetArgumentAtIndex(index)); new_args.AppendArgument(buffer); } used[index] = true; } } for (auto entry : llvm::enumerate(cmd_args.entries())) { if (!used[entry.index()] && !wants_raw_input) new_args.AppendArgument(entry.value().ref); } cmd_args.Clear(); cmd_args.SetArguments(new_args.GetArgumentCount(), new_args.GetConstArgumentVector()); } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); // This alias was not created with any options; nothing further needs to be // done, unless it is a command that // wants raw input, in which case we need to clear the rest of the data from // cmd_args, since its in the raw // input string. if (wants_raw_input) { cmd_args.Clear(); cmd_args.SetArguments(new_args.GetArgumentCount(), new_args.GetConstArgumentVector()); } return; } result.SetStatus(eReturnStatusSuccessFinishNoResult); return; } int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) { int position = 0; // Any string that isn't an argument position, i.e. '%' // followed by an integer, gets a position // of zero. const char *cptr = in_string; // Does it start with '%' if (cptr[0] == '%') { ++cptr; // Is the rest of it entirely digits? if (isdigit(cptr[0])) { const char *start = cptr; while (isdigit(cptr[0])) ++cptr; // We've gotten to the end of the digits; are we at the end of the string? if (cptr[0] == '\0') position = atoi(start); } } return position; } void CommandInterpreter::SourceInitFile(bool in_cwd, CommandReturnObject &result) { FileSpec init_file; if (in_cwd) { ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); if (target) { // In the current working directory we don't load any program specific // .lldbinit files, we only look for a ".lldbinit" file. if (m_skip_lldbinit_files) return; LoadCWDlldbinitFile should_load = target->TargetProperties::GetLoadCWDlldbinitFile(); if (should_load == eLoadCWDlldbinitWarn) { FileSpec dot_lldb(".lldbinit", true); llvm::SmallString<64> home_dir_path; llvm::sys::path::home_directory(home_dir_path); FileSpec homedir_dot_lldb(home_dir_path.c_str(), false); homedir_dot_lldb.AppendPathComponent(".lldbinit"); homedir_dot_lldb.ResolvePath(); if (dot_lldb.Exists() && dot_lldb.GetDirectory() != homedir_dot_lldb.GetDirectory()) { result.AppendErrorWithFormat( "There is a .lldbinit file in the current directory which is not " "being read.\n" "To silence this warning without sourcing in the local " ".lldbinit,\n" "add the following to the lldbinit file in your home directory:\n" " settings set target.load-cwd-lldbinit false\n" "To allow lldb to source .lldbinit files in the current working " "directory,\n" "set the value of this variable to true. Only do so if you " "understand and\n" "accept the security risk."); result.SetStatus(eReturnStatusFailed); return; } } else if (should_load == eLoadCWDlldbinitTrue) { init_file.SetFile("./.lldbinit", true); } } } else { // If we aren't looking in the current working directory we are looking // in the home directory. We will first see if there is an application // specific ".lldbinit" file whose name is "~/.lldbinit" followed by a // "-" and the name of the program. If this file doesn't exist, we fall // back to just the "~/.lldbinit" file. We also obey any requests to not // load the init files. llvm::SmallString<64> home_dir_path; llvm::sys::path::home_directory(home_dir_path); FileSpec profilePath(home_dir_path.c_str(), false); profilePath.AppendPathComponent(".lldbinit"); std::string init_file_path = profilePath.GetPath(); if (m_skip_app_init_files == false) { FileSpec program_file_spec(HostInfo::GetProgramFileSpec()); const char *program_name = program_file_spec.GetFilename().AsCString(); if (program_name) { char program_init_file_name[PATH_MAX]; ::snprintf(program_init_file_name, sizeof(program_init_file_name), "%s-%s", init_file_path.c_str(), program_name); init_file.SetFile(program_init_file_name, true); if (!init_file.Exists()) init_file.Clear(); } } if (!init_file && !m_skip_lldbinit_files) init_file.SetFile(init_file_path, false); } // If the file exists, tell HandleCommand to 'source' it; this will do the // actual broadcasting // of the commands back to any appropriate listener (see // CommandObjectSource::Execute for more details). if (init_file.Exists()) { const bool saved_batch = SetBatchCommandMode(true); CommandInterpreterRunOptions options; options.SetSilent(true); options.SetStopOnError(false); options.SetStopOnContinue(true); HandleCommandsFromFile(init_file, nullptr, // Execution context options, result); SetBatchCommandMode(saved_batch); } else { // nothing to be done if the file doesn't exist result.SetStatus(eReturnStatusSuccessFinishNoResult); } } const char *CommandInterpreter::GetCommandPrefix() { const char *prefix = GetDebugger().GetIOHandlerCommandPrefix(); return prefix == NULL ? "" : prefix; } PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) { PlatformSP platform_sp; if (prefer_target_platform) { ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); if (target) platform_sp = target->GetPlatform(); } if (!platform_sp) platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform(); return platform_sp; } void CommandInterpreter::HandleCommands(const StringList &commands, ExecutionContext *override_context, CommandInterpreterRunOptions &options, CommandReturnObject &result) { size_t num_lines = commands.GetSize(); // If we are going to continue past a "continue" then we need to run the // commands synchronously. // Make sure you reset this value anywhere you return from the function. bool old_async_execution = m_debugger.GetAsyncExecution(); // If we've been given an execution context, set it at the start, but don't // keep resetting it or we will // cause series of commands that change the context, then do an operation that // relies on that context to fail. if (override_context != nullptr) UpdateExecutionContext(override_context); if (!options.GetStopOnContinue()) { m_debugger.SetAsyncExecution(false); } for (size_t idx = 0; idx < num_lines; idx++) { const char *cmd = commands.GetStringAtIndex(idx); if (cmd[0] == '\0') continue; if (options.GetEchoCommands()) { // TODO: Add Stream support. result.AppendMessageWithFormat("%s %s\n", m_debugger.GetPrompt().str().c_str(), cmd); } CommandReturnObject tmp_result; // If override_context is not NULL, pass no_context_switching = true for // HandleCommand() since we updated our context already. // We might call into a regex or alias command, in which case the // add_to_history will get lost. This // m_command_source_depth dingus is the way we turn off adding to the // history in that case, so set it up here. if (!options.GetAddToHistory()) m_command_source_depth++; bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result, nullptr, /* override_context */ true, /* repeat_on_empty_command */ override_context != nullptr /* no_context_switching */); if (!options.GetAddToHistory()) m_command_source_depth--; if (options.GetPrintResults()) { if (tmp_result.Succeeded()) result.AppendMessage(tmp_result.GetOutputData()); } if (!success || !tmp_result.Succeeded()) { llvm::StringRef error_msg = tmp_result.GetErrorData(); if (error_msg.empty()) error_msg = ".\n"; if (options.GetStopOnError()) { result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' failed with %s", (uint64_t)idx, cmd, error_msg.str().c_str()); result.SetStatus(eReturnStatusFailed); m_debugger.SetAsyncExecution(old_async_execution); return; } else if (options.GetPrintResults()) { result.AppendMessageWithFormat( "Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd, error_msg.str().c_str()); } } if (result.GetImmediateOutputStream()) result.GetImmediateOutputStream()->Flush(); if (result.GetImmediateErrorStream()) result.GetImmediateErrorStream()->Flush(); // N.B. Can't depend on DidChangeProcessState, because the state coming into // the command execution // could be running (for instance in Breakpoint Commands. // So we check the return value to see if it is has running in it. if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) { if (options.GetStopOnContinue()) { // If we caused the target to proceed, and we're going to stop in that // case, set the // status in our real result before returning. This is an error if the // continue was not the // last command in the set of commands to be run. if (idx != num_lines - 1) result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' continued the target.\n", (uint64_t)idx + 1, cmd); else result.AppendMessageWithFormat("Command #%" PRIu64 " '%s' continued the target.\n", (uint64_t)idx + 1, cmd); result.SetStatus(tmp_result.GetStatus()); m_debugger.SetAsyncExecution(old_async_execution); return; } } // Also check for "stop on crash here: bool should_stop = false; if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash()) { TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) { StopReason reason = thread_sp->GetStopReason(); if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) { should_stop = true; break; } } } } if (should_stop) { if (idx != num_lines - 1) result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd); else result.AppendMessageWithFormat( "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd); result.SetStatus(tmp_result.GetStatus()); m_debugger.SetAsyncExecution(old_async_execution); return; } } } result.SetStatus(eReturnStatusSuccessFinishResult); m_debugger.SetAsyncExecution(old_async_execution); return; } // Make flags that we can pass into the IOHandler so our delegates can do the // right thing enum { eHandleCommandFlagStopOnContinue = (1u << 0), eHandleCommandFlagStopOnError = (1u << 1), eHandleCommandFlagEchoCommand = (1u << 2), eHandleCommandFlagPrintResult = (1u << 3), eHandleCommandFlagStopOnCrash = (1u << 4) }; void CommandInterpreter::HandleCommandsFromFile( FileSpec &cmd_file, ExecutionContext *context, CommandInterpreterRunOptions &options, CommandReturnObject &result) { if (cmd_file.Exists()) { StreamFileSP input_file_sp(new StreamFile()); std::string cmd_file_path = cmd_file.GetPath(); Status error = input_file_sp->GetFile().Open(cmd_file_path.c_str(), File::eOpenOptionRead); if (error.Success()) { Debugger &debugger = GetDebugger(); uint32_t flags = 0; if (options.m_stop_on_continue == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Stop on continue by default flags |= eHandleCommandFlagStopOnContinue; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnContinue) { flags |= eHandleCommandFlagStopOnContinue; } } else if (options.m_stop_on_continue == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnContinue; } if (options.m_stop_on_error == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { if (GetStopCmdSourceOnError()) flags |= eHandleCommandFlagStopOnError; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) { flags |= eHandleCommandFlagStopOnError; } } else if (options.m_stop_on_error == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnError; } if (options.GetStopOnCrash()) { if (m_command_source_flags.empty()) { // Echo command by default flags |= eHandleCommandFlagStopOnCrash; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) { flags |= eHandleCommandFlagStopOnCrash; } } if (options.m_echo_commands == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Echo command by default flags |= eHandleCommandFlagEchoCommand; } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) { flags |= eHandleCommandFlagEchoCommand; } } else if (options.m_echo_commands == eLazyBoolYes) { flags |= eHandleCommandFlagEchoCommand; } if (options.m_print_results == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Print output by default flags |= eHandleCommandFlagPrintResult; } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) { flags |= eHandleCommandFlagPrintResult; } } else if (options.m_print_results == eLazyBoolYes) { flags |= eHandleCommandFlagPrintResult; } if (flags & eHandleCommandFlagPrintResult) { debugger.GetOutputFile()->Printf("Executing commands in '%s'.\n", cmd_file_path.c_str()); } // Used for inheriting the right settings when "command source" might have // nested "command source" commands lldb::StreamFileSP empty_stream_sp; m_command_source_flags.push_back(flags); IOHandlerSP io_handler_sp(new IOHandlerEditline( debugger, IOHandler::Type::CommandInterpreter, input_file_sp, empty_stream_sp, // Pass in an empty stream so we inherit the top // input reader output stream empty_stream_sp, // Pass in an empty stream so we inherit the top // input reader error stream flags, nullptr, // Pass in NULL for "editline_name" so no history is saved, // or written debugger.GetPrompt(), llvm::StringRef(), false, // Not multi-line debugger.GetUseColor(), 0, *this)); const bool old_async_execution = debugger.GetAsyncExecution(); // Set synchronous execution if we are not stopping on continue if ((flags & eHandleCommandFlagStopOnContinue) == 0) debugger.SetAsyncExecution(false); m_command_source_depth++; debugger.RunIOHandler(io_handler_sp); if (!m_command_source_flags.empty()) m_command_source_flags.pop_back(); m_command_source_depth--; result.SetStatus(eReturnStatusSuccessFinishNoResult); debugger.SetAsyncExecution(old_async_execution); } else { result.AppendErrorWithFormat( "error: an error occurred read file '%s': %s\n", cmd_file_path.c_str(), error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Error reading commands from file %s - file not found.\n", cmd_file.GetFilename().AsCString("")); result.SetStatus(eReturnStatusFailed); return; } } ScriptInterpreter *CommandInterpreter::GetScriptInterpreter(bool can_create) { - std::lock_guard locker(m_script_interpreter_mutex); + std::lock_guard locker(m_script_interpreter_mutex); if (!m_script_interpreter_sp) { if (!can_create) return nullptr; lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage(); m_script_interpreter_sp = PluginManager::GetScriptInterpreterForLanguage(script_lang, *this); } return m_script_interpreter_sp.get(); } bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; } void CommandInterpreter::SetSynchronous(bool value) { m_synchronous_execution = value; } void CommandInterpreter::OutputFormattedHelpText(Stream &strm, llvm::StringRef prefix, llvm::StringRef help_text) { const uint32_t max_columns = m_debugger.GetTerminalWidth(); size_t line_width_max = max_columns - prefix.size(); if (line_width_max < 16) line_width_max = help_text.size() + prefix.size(); strm.IndentMore(prefix.size()); bool prefixed_yet = false; while (!help_text.empty()) { // Prefix the first line, indent subsequent lines to line up if (!prefixed_yet) { strm << prefix; prefixed_yet = true; } else strm.Indent(); // Never print more than the maximum on one line. llvm::StringRef this_line = help_text.substr(0, line_width_max); // Always break on an explicit newline. std::size_t first_newline = this_line.find_first_of("\n"); // Don't break on space/tab unless the text is too long to fit on one line. std::size_t last_space = llvm::StringRef::npos; if (this_line.size() != help_text.size()) last_space = this_line.find_last_of(" \t"); // Break at whichever condition triggered first. this_line = this_line.substr(0, std::min(first_newline, last_space)); strm.PutCString(this_line); strm.EOL(); // Remove whitespace / newlines after breaking. help_text = help_text.drop_front(this_line.size()).ltrim(); } strm.IndentLess(prefix.size()); } void CommandInterpreter::OutputFormattedHelpText(Stream &strm, llvm::StringRef word_text, llvm::StringRef separator, llvm::StringRef help_text, size_t max_word_len) { StreamString prefix_stream; prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(), (int)separator.size(), separator.data()); OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text); } void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text, llvm::StringRef separator, llvm::StringRef help_text, uint32_t max_word_len) { int indent_size = max_word_len + separator.size() + 2; strm.IndentMore(indent_size); StreamString text_strm; text_strm.Printf("%-*s ", (int)max_word_len, word_text.data()); text_strm << separator << " " << help_text; const uint32_t max_columns = m_debugger.GetTerminalWidth(); llvm::StringRef text = text_strm.GetString(); uint32_t chars_left = max_columns; auto nextWordLength = [](llvm::StringRef S) { size_t pos = S.find_first_of(' '); return pos == llvm::StringRef::npos ? S.size() : pos; }; while (!text.empty()) { if (text.front() == '\n' || (text.front() == ' ' && nextWordLength(text.ltrim(' ')) < chars_left)) { strm.EOL(); strm.Indent(); chars_left = max_columns - indent_size; if (text.front() == '\n') text = text.drop_front(); else text = text.ltrim(' '); } else { strm.PutChar(text.front()); --chars_left; text = text.drop_front(); } } strm.EOL(); strm.IndentLess(indent_size); } void CommandInterpreter::FindCommandsForApropos( llvm::StringRef search_word, StringList &commands_found, StringList &commands_help, CommandObject::CommandMap &command_map) { CommandObject::CommandMap::const_iterator pos; for (pos = command_map.begin(); pos != command_map.end(); ++pos) { llvm::StringRef command_name = pos->first; CommandObject *cmd_obj = pos->second.get(); const bool search_short_help = true; const bool search_long_help = false; const bool search_syntax = false; const bool search_options = false; if (command_name.contains_lower(search_word) || cmd_obj->HelpTextContainsWord(search_word, search_short_help, search_long_help, search_syntax, search_options)) { commands_found.AppendString(cmd_obj->GetCommandName()); commands_help.AppendString(cmd_obj->GetHelp()); } if (cmd_obj->IsMultiwordObject()) { CommandObjectMultiword *cmd_multiword = cmd_obj->GetAsMultiwordCommand(); FindCommandsForApropos(search_word, commands_found, commands_help, cmd_multiword->GetSubcommandDictionary()); } } } void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word, StringList &commands_found, StringList &commands_help, bool search_builtin_commands, bool search_user_commands, bool search_alias_commands) { CommandObject::CommandMap::const_iterator pos; if (search_builtin_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_command_dict); if (search_user_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_user_dict); if (search_alias_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_alias_dict); } void CommandInterpreter::UpdateExecutionContext( ExecutionContext *override_context) { if (override_context != nullptr) { m_exe_ctx_ref = *override_context; } else { const bool adopt_selected = true; m_exe_ctx_ref.SetTargetPtr(m_debugger.GetSelectedTarget().get(), adopt_selected); } } size_t CommandInterpreter::GetProcessOutput() { // The process has stuff waiting for stderr; get it and write it out to the // appropriate place. char stdio_buffer[1024]; size_t len; size_t total_bytes = 0; Status error; TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { while ((len = process_sp->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer), error)) > 0) { size_t bytes_written = len; m_debugger.GetOutputFile()->Write(stdio_buffer, bytes_written); total_bytes += len; } while ((len = process_sp->GetSTDERR(stdio_buffer, sizeof(stdio_buffer), error)) > 0) { size_t bytes_written = len; m_debugger.GetErrorFile()->Write(stdio_buffer, bytes_written); total_bytes += len; } } } return total_bytes; } void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, std::string &line) { const bool is_interactive = io_handler.GetIsInteractive(); if (is_interactive == false) { // When we are not interactive, don't execute blank lines. This will happen // sourcing a commands file. We don't want blank lines to repeat the // previous // command and cause any errors to occur (like redefining an alias, get an // error // and stop parsing the commands file). if (line.empty()) return; // When using a non-interactive file handle (like when sourcing commands // from a file) // we need to echo the command out so we don't just see the command output // and no // command... if (io_handler.GetFlags().Test(eHandleCommandFlagEchoCommand)) io_handler.GetOutputStreamFile()->Printf("%s%s\n", io_handler.GetPrompt(), line.c_str()); } lldb_private::CommandReturnObject result; HandleCommand(line.c_str(), eLazyBoolCalculate, result); // Now emit the command output text from the command we just executed if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) { // Display any STDOUT/STDERR _prior_ to emitting the command result text GetProcessOutput(); if (!result.GetImmediateOutputStream()) { llvm::StringRef output = result.GetOutputData(); if (!output.empty()) io_handler.GetOutputStreamFile()->PutCString(output); } // Now emit the command error text from the command we just executed if (!result.GetImmediateErrorStream()) { llvm::StringRef error = result.GetErrorData(); if (!error.empty()) io_handler.GetErrorStreamFile()->PutCString(error); } } switch (result.GetStatus()) { case eReturnStatusInvalid: case eReturnStatusSuccessFinishNoResult: case eReturnStatusSuccessFinishResult: case eReturnStatusStarted: break; case eReturnStatusSuccessContinuingNoResult: case eReturnStatusSuccessContinuingResult: if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue)) io_handler.SetIsDone(true); break; case eReturnStatusFailed: m_num_errors++; if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) io_handler.SetIsDone(true); break; case eReturnStatusQuit: m_quit_requested = true; io_handler.SetIsDone(true); break; } // Finally, if we're going to stop on crash, check that here: if (!m_quit_requested && result.GetDidChangeProcessState() && io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash)) { bool should_stop = false; TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) { StopReason reason = thread_sp->GetStopReason(); if ((reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) && !result.GetAbnormalStopWasExpected()) { should_stop = true; break; } } } } if (should_stop) { io_handler.SetIsDone(true); m_stopped_for_crash = true; } } } bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) { ExecutionContext exe_ctx(GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); if (process) { StateType state = process->GetState(); if (StateIsRunningState(state)) { process->Halt(); return true; // Don't do any updating when we are running } } ScriptInterpreter *script_interpreter = GetScriptInterpreter(false); if (script_interpreter) { if (script_interpreter->Interrupt()) return true; } return false; } void CommandInterpreter::GetLLDBCommandsFromIOHandler( const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton) { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp( new IOHandlerEditline(debugger, IOHandler::Type::CommandList, "lldb", // Name of input reader for history llvm::StringRef::withNullAsEmpty(prompt), // Prompt llvm::StringRef(), // Continuation prompt true, // Get multiple lines debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate if (io_handler_sp) { io_handler_sp->SetUserData(baton); if (asynchronously) debugger.PushIOHandler(io_handler_sp); else debugger.RunIOHandler(io_handler_sp); } } void CommandInterpreter::GetPythonCommandsFromIOHandler( const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton) { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp( new IOHandlerEditline(debugger, IOHandler::Type::PythonCode, "lldb-python", // Name of input reader for history llvm::StringRef::withNullAsEmpty(prompt), // Prompt llvm::StringRef(), // Continuation prompt true, // Get multiple lines debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate if (io_handler_sp) { io_handler_sp->SetUserData(baton); if (asynchronously) debugger.PushIOHandler(io_handler_sp); else debugger.RunIOHandler(io_handler_sp); } } bool CommandInterpreter::IsActive() { return m_debugger.IsTopIOHandler(m_command_io_handler_sp); } lldb::IOHandlerSP CommandInterpreter::GetIOHandler(bool force_create, CommandInterpreterRunOptions *options) { // Always re-create the IOHandlerEditline in case the input // changed. The old instance might have had a non-interactive // input and now it does or vice versa. if (force_create || !m_command_io_handler_sp) { // Always re-create the IOHandlerEditline in case the input // changed. The old instance might have had a non-interactive // input and now it does or vice versa. uint32_t flags = 0; if (options) { if (options->m_stop_on_continue == eLazyBoolYes) flags |= eHandleCommandFlagStopOnContinue; if (options->m_stop_on_error == eLazyBoolYes) flags |= eHandleCommandFlagStopOnError; if (options->m_stop_on_crash == eLazyBoolYes) flags |= eHandleCommandFlagStopOnCrash; if (options->m_echo_commands != eLazyBoolNo) flags |= eHandleCommandFlagEchoCommand; if (options->m_print_results != eLazyBoolNo) flags |= eHandleCommandFlagPrintResult; } else { flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult; } m_command_io_handler_sp.reset(new IOHandlerEditline( m_debugger, IOHandler::Type::CommandInterpreter, m_debugger.GetInputFile(), m_debugger.GetOutputFile(), m_debugger.GetErrorFile(), flags, "lldb", m_debugger.GetPrompt(), llvm::StringRef(), // Continuation prompt false, // Don't enable multiple line input, just single line commands m_debugger.GetUseColor(), 0, // Don't show line numbers *this)); } return m_command_io_handler_sp; } void CommandInterpreter::RunCommandInterpreter( bool auto_handle_events, bool spawn_thread, CommandInterpreterRunOptions &options) { // Always re-create the command interpreter when we run it in case // any file handles have changed. bool force_create = true; m_debugger.PushIOHandler(GetIOHandler(force_create, &options)); m_stopped_for_crash = false; if (auto_handle_events) m_debugger.StartEventHandlerThread(); if (spawn_thread) { m_debugger.StartIOHandlerThread(); } else { m_debugger.ExecuteIOHandlers(); if (auto_handle_events) m_debugger.StopEventHandlerThread(); } } CommandObject * CommandInterpreter::ResolveCommandImpl(std::string &command_line, CommandReturnObject &result) { std::string scratch_command(command_line); // working copy so we don't modify // command_line unless we succeed CommandObject *cmd_obj = nullptr; StreamString revised_command_line; bool wants_raw_input = false; size_t actual_cmd_name_len = 0; std::string next_word; StringList matches; bool done = false; while (!done) { char quote_char = '\0'; std::string suffix; ExtractCommand(scratch_command, next_word, suffix, quote_char); if (cmd_obj == nullptr) { std::string full_name; bool is_alias = GetAliasFullName(next_word, full_name); cmd_obj = GetCommandObject(next_word, &matches); bool is_real_command = (is_alias == false) || (cmd_obj != nullptr && cmd_obj->IsAlias() == false); if (!is_real_command) { matches.Clear(); std::string alias_result; cmd_obj = BuildAliasResult(full_name, scratch_command, alias_result, result); revised_command_line.Printf("%s", alias_result.c_str()); if (cmd_obj) { wants_raw_input = cmd_obj->WantsRawCommandString(); actual_cmd_name_len = cmd_obj->GetCommandName().size(); } } else { if (!cmd_obj) cmd_obj = GetCommandObject(next_word, &matches); if (cmd_obj) { llvm::StringRef cmd_name = cmd_obj->GetCommandName(); actual_cmd_name_len += cmd_name.size(); revised_command_line.Printf("%s", cmd_name.str().c_str()); wants_raw_input = cmd_obj->WantsRawCommandString(); } else { revised_command_line.Printf("%s", next_word.c_str()); } } } else { if (cmd_obj->IsMultiwordObject()) { CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject(next_word.c_str()); if (sub_cmd_obj) { // The subcommand's name includes the parent command's name, // so restart rather than append to the revised_command_line. llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName(); actual_cmd_name_len = sub_cmd_name.size() + 1; revised_command_line.Clear(); revised_command_line.Printf("%s", sub_cmd_name.str().c_str()); cmd_obj = sub_cmd_obj; wants_raw_input = cmd_obj->WantsRawCommandString(); } else { if (quote_char) revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); else revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); done = true; } } else { if (quote_char) revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); else revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); done = true; } } if (cmd_obj == nullptr) { const size_t num_matches = matches.GetSize(); if (matches.GetSize() > 1) { StreamString error_msg; error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", next_word.c_str()); for (uint32_t i = 0; i < num_matches; ++i) { error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); } result.AppendRawError(error_msg.GetString()); } else { // We didn't have only one match, otherwise we wouldn't get here. assert(num_matches == 0); result.AppendErrorWithFormat("'%s' is not a valid command.\n", next_word.c_str()); } result.SetStatus(eReturnStatusFailed); return nullptr; } if (cmd_obj->IsMultiwordObject()) { if (!suffix.empty()) { result.AppendErrorWithFormat( "command '%s' did not recognize '%s%s%s' as valid (subcommand " "might be invalid).\n", cmd_obj->GetCommandName().str().c_str(), next_word.empty() ? "" : next_word.c_str(), next_word.empty() ? " -- " : " ", suffix.c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } else { // If we found a normal command, we are done done = true; if (!suffix.empty()) { switch (suffix[0]) { case '/': // GDB format suffixes { Options *command_options = cmd_obj->GetOptions(); if (command_options && command_options->SupportsLongOption("gdb-format")) { std::string gdb_format_option("--gdb-format="); gdb_format_option += (suffix.c_str() + 1); std::string cmd = revised_command_line.GetString(); size_t arg_terminator_idx = FindArgumentTerminator(cmd); if (arg_terminator_idx != std::string::npos) { // Insert the gdb format option before the "--" that terminates // options gdb_format_option.append(1, ' '); cmd.insert(arg_terminator_idx, gdb_format_option); revised_command_line.Clear(); revised_command_line.PutCString(cmd); } else revised_command_line.Printf(" %s", gdb_format_option.c_str()); if (wants_raw_input && FindArgumentTerminator(cmd) == std::string::npos) revised_command_line.PutCString(" --"); } else { result.AppendErrorWithFormat( "the '%s' command doesn't support the --gdb-format option\n", cmd_obj->GetCommandName().str().c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } break; default: result.AppendErrorWithFormat( "unknown command shorthand suffix: '%s'\n", suffix.c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } } if (scratch_command.empty()) done = true; } if (!scratch_command.empty()) revised_command_line.Printf(" %s", scratch_command.c_str()); if (cmd_obj != NULL) command_line = revised_command_line.GetString(); return cmd_obj; } Index: vendor/lldb/dist/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp (revision 321194) @@ -1,154 +1,142 @@ //===-- AuxVector.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// C Includes -#include -#include -#include - -// C++ Includes -// Other libraries and framework includes +#include "AuxVector.h" #include "lldb/Target/Process.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Log.h" - -#if defined(__linux__) || defined(__FreeBSD__) -#include "Plugins/Process/elf-core/ProcessElfCore.h" -#endif - -#include "AuxVector.h" using namespace lldb; using namespace lldb_private; static bool GetMaxU64(DataExtractor &data, lldb::offset_t *offset_ptr, uint64_t *value, unsigned int byte_size) { lldb::offset_t saved_offset = *offset_ptr; *value = data.GetMaxU64(offset_ptr, byte_size); return *offset_ptr != saved_offset; } static bool ParseAuxvEntry(DataExtractor &data, AuxVector::Entry &entry, lldb::offset_t *offset_ptr, unsigned int byte_size) { if (!GetMaxU64(data, offset_ptr, &entry.type, byte_size)) return false; if (!GetMaxU64(data, offset_ptr, &entry.value, byte_size)) return false; return true; } DataBufferSP AuxVector::GetAuxvData() { if (m_process) return m_process->GetAuxvData(); else return DataBufferSP(); } void AuxVector::ParseAuxv(DataExtractor &data) { const unsigned int byte_size = m_process->GetAddressByteSize(); lldb::offset_t offset = 0; for (;;) { Entry entry; if (!ParseAuxvEntry(data, entry, &offset, byte_size)) break; if (entry.type == AUXV_AT_NULL) break; if (entry.type == AUXV_AT_IGNORE) continue; m_auxv.push_back(entry); } } AuxVector::AuxVector(Process *process) : m_process(process) { DataExtractor data; Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); data.SetData(GetAuxvData()); data.SetByteOrder(m_process->GetByteOrder()); data.SetAddressByteSize(m_process->GetAddressByteSize()); ParseAuxv(data); if (log) DumpToLog(log); } AuxVector::iterator AuxVector::FindEntry(EntryType type) const { for (iterator I = begin(); I != end(); ++I) { if (I->type == static_cast(type)) return I; } return end(); } void AuxVector::DumpToLog(Log *log) const { if (!log) return; log->PutCString("AuxVector: "); for (iterator I = begin(); I != end(); ++I) { log->Printf(" %s [%" PRIu64 "]: %" PRIx64, GetEntryName(*I), I->type, I->value); } } const char *AuxVector::GetEntryName(EntryType type) { const char *name = "AT_???"; #define ENTRY_NAME(_type) \ _type: \ name = #_type + 5 switch (type) { case ENTRY_NAME(AUXV_AT_NULL); break; case ENTRY_NAME(AUXV_AT_IGNORE); break; case ENTRY_NAME(AUXV_AT_EXECFD); break; case ENTRY_NAME(AUXV_AT_PHDR); break; case ENTRY_NAME(AUXV_AT_PHENT); break; case ENTRY_NAME(AUXV_AT_PHNUM); break; case ENTRY_NAME(AUXV_AT_PAGESZ); break; case ENTRY_NAME(AUXV_AT_BASE); break; case ENTRY_NAME(AUXV_AT_FLAGS); break; case ENTRY_NAME(AUXV_AT_ENTRY); break; case ENTRY_NAME(AUXV_AT_NOTELF); break; case ENTRY_NAME(AUXV_AT_UID); break; case ENTRY_NAME(AUXV_AT_EUID); break; case ENTRY_NAME(AUXV_AT_GID); break; case ENTRY_NAME(AUXV_AT_EGID); break; case ENTRY_NAME(AUXV_AT_CLKTCK); break; case ENTRY_NAME(AUXV_AT_PLATFORM); break; case ENTRY_NAME(AUXV_AT_HWCAP); break; case ENTRY_NAME(AUXV_AT_FPUCW); break; case ENTRY_NAME(AUXV_AT_DCACHEBSIZE); break; case ENTRY_NAME(AUXV_AT_ICACHEBSIZE); break; case ENTRY_NAME(AUXV_AT_UCACHEBSIZE); break; case ENTRY_NAME(AUXV_AT_IGNOREPPC); break; case ENTRY_NAME(AUXV_AT_SECURE); break; case ENTRY_NAME(AUXV_AT_BASE_PLATFORM); break; case ENTRY_NAME(AUXV_AT_RANDOM); break; case ENTRY_NAME(AUXV_AT_EXECFN); break; case ENTRY_NAME(AUXV_AT_SYSINFO); break; case ENTRY_NAME(AUXV_AT_SYSINFO_EHDR); break; case ENTRY_NAME(AUXV_AT_L1I_CACHESHAPE); break; case ENTRY_NAME(AUXV_AT_L1D_CACHESHAPE); break; case ENTRY_NAME(AUXV_AT_L2_CACHESHAPE); break; case ENTRY_NAME(AUXV_AT_L3_CACHESHAPE); break; } #undef ENTRY_NAME return name; } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp (revision 321194) @@ -1,682 +1,682 @@ //===-- ClangModulesDeclVendor.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Lookup.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Threading.h" // Project includes #include "ClangModulesDeclVendor.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" using namespace lldb_private; namespace { // Any Clang compiler requires a consumer for diagnostics. This one stores them // as strings // so we can provide them to the user in case a module failed to load. class StoringDiagnosticConsumer : public clang::DiagnosticConsumer { public: StoringDiagnosticConsumer(); void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) override; void ClearDiagnostics(); void DumpDiagnostics(Stream &error_stream); private: typedef std::pair IDAndDiagnostic; std::vector m_diagnostics; Log *m_log; }; // The private implementation of our ClangModulesDeclVendor. Contains all the // Clang state required // to load modules. class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor { public: ClangModulesDeclVendorImpl( llvm::IntrusiveRefCntPtr diagnostics_engine, std::shared_ptr compiler_invocation, std::unique_ptr compiler_instance, std::unique_ptr parser); ~ClangModulesDeclVendorImpl() override = default; bool AddModule(ModulePath &path, ModuleVector *exported_modules, Stream &error_stream) override; bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules, Stream &error_stream) override; uint32_t FindDecls(const ConstString &name, bool append, uint32_t max_matches, std::vector &decls) override; void ForEachMacro(const ModuleVector &modules, std::function handler) override; private: void ReportModuleExportsHelper(std::set &exports, clang::Module *module); void ReportModuleExports(ModuleVector &exports, clang::Module *module); clang::ModuleLoadResult DoGetModule(clang::ModuleIdPath path, bool make_visible); bool m_enabled = false; llvm::IntrusiveRefCntPtr m_diagnostics_engine; std::shared_ptr m_compiler_invocation; std::unique_ptr m_compiler_instance; std::unique_ptr m_parser; size_t m_source_location_index = 0; // used to give name components fake SourceLocations typedef std::vector ImportedModule; typedef std::map ImportedModuleMap; typedef std::set ImportedModuleSet; ImportedModuleMap m_imported_modules; ImportedModuleSet m_user_imported_modules; }; } // anonymous namespace StoringDiagnosticConsumer::StoringDiagnosticConsumer() { m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); } void StoringDiagnosticConsumer::HandleDiagnostic( clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { llvm::SmallVector diagnostic_string; info.FormatDiagnostic(diagnostic_string); m_diagnostics.push_back( IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(), diagnostic_string.size()))); } void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); } void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) { for (IDAndDiagnostic &diag : m_diagnostics) { switch (diag.first) { default: error_stream.PutCString(diag.second); error_stream.PutChar('\n'); break; case clang::DiagnosticsEngine::Level::Ignored: break; } } } static FileSpec GetResourceDir() { static FileSpec g_cached_resource_dir; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { HostInfo::GetLLDBPath(lldb::ePathTypeClangDir, g_cached_resource_dir); }); return g_cached_resource_dir; } ClangModulesDeclVendor::ClangModulesDeclVendor() {} ClangModulesDeclVendor::~ClangModulesDeclVendor() {} ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl( llvm::IntrusiveRefCntPtr diagnostics_engine, std::shared_ptr compiler_invocation, std::unique_ptr compiler_instance, std::unique_ptr parser) : m_diagnostics_engine(std::move(diagnostics_engine)), m_compiler_invocation(std::move(compiler_invocation)), m_compiler_instance(std::move(compiler_instance)), m_parser(std::move(parser)) {} void ClangModulesDeclVendorImpl::ReportModuleExportsHelper( std::set &exports, clang::Module *module) { if (exports.count(reinterpret_cast(module))) return; exports.insert(reinterpret_cast(module)); llvm::SmallVector sub_exports; module->getExportedModules(sub_exports); for (clang::Module *module : sub_exports) { ReportModuleExportsHelper(exports, module); } } void ClangModulesDeclVendorImpl::ReportModuleExports( ClangModulesDeclVendor::ModuleVector &exports, clang::Module *module) { std::set exports_set; ReportModuleExportsHelper(exports_set, module); for (ModuleID module : exports_set) { exports.push_back(module); } } bool ClangModulesDeclVendorImpl::AddModule(ModulePath &path, ModuleVector *exported_modules, Stream &error_stream) { // Fail early. if (m_compiler_instance->hadModuleLoaderFatalFailure()) { error_stream.PutCString("error: Couldn't load a module because the module " "loader is in a fatal state.\n"); return false; } // Check if we've already imported this module. std::vector imported_module; for (ConstString path_component : path) { imported_module.push_back(path_component); } { ImportedModuleMap::iterator mi = m_imported_modules.find(imported_module); if (mi != m_imported_modules.end()) { if (exported_modules) { ReportModuleExports(*exported_modules, mi->second); } return true; } } if (!m_compiler_instance->getPreprocessor() .getHeaderSearchInfo() .lookupModule(path[0].GetStringRef())) { error_stream.Printf("error: Header search couldn't locate module %s\n", path[0].AsCString()); return false; } llvm::SmallVector, 4> clang_path; { clang::SourceManager &source_manager = m_compiler_instance->getASTContext().getSourceManager(); for (ConstString path_component : path) { clang_path.push_back(std::make_pair( &m_compiler_instance->getASTContext().Idents.get( path_component.GetStringRef()), source_manager.getLocForStartOfFile(source_manager.getMainFileID()) .getLocWithOffset(m_source_location_index++))); } } StoringDiagnosticConsumer *diagnostic_consumer = static_cast( m_compiler_instance->getDiagnostics().getClient()); diagnostic_consumer->ClearDiagnostics(); clang::Module *top_level_module = DoGetModule(clang_path.front(), false); if (!top_level_module) { diagnostic_consumer->DumpDiagnostics(error_stream); error_stream.Printf("error: Couldn't load top-level module %s\n", path[0].AsCString()); return false; } clang::Module *submodule = top_level_module; for (size_t ci = 1; ci < path.size(); ++ci) { llvm::StringRef component = path[ci].GetStringRef(); submodule = submodule->findSubmodule(component.str()); if (!submodule) { diagnostic_consumer->DumpDiagnostics(error_stream); error_stream.Printf("error: Couldn't load submodule %s\n", component.str().c_str()); return false; } } clang::Module *requested_module = DoGetModule(clang_path, true); if (requested_module != nullptr) { if (exported_modules) { ReportModuleExports(*exported_modules, requested_module); } m_imported_modules[imported_module] = requested_module; m_enabled = true; return true; } return false; } bool ClangModulesDeclVendor::LanguageSupportsClangModules( lldb::LanguageType language) { switch (language) { default: return false; // C++ and friends to be added case lldb::LanguageType::eLanguageTypeC: case lldb::LanguageType::eLanguageTypeC11: case lldb::LanguageType::eLanguageTypeC89: case lldb::LanguageType::eLanguageTypeC99: case lldb::LanguageType::eLanguageTypeObjC: return true; } } bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit( CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules, Stream &error_stream) { if (LanguageSupportsClangModules(cu.GetLanguage())) { std::vector imported_modules = cu.GetImportedModules(); for (ConstString imported_module : imported_modules) { std::vector path; path.push_back(imported_module); if (!AddModule(path, &exported_modules, error_stream)) { return false; } } return true; } return true; } // ClangImporter::lookupValue uint32_t ClangModulesDeclVendorImpl::FindDecls(const ConstString &name, bool append, uint32_t max_matches, std::vector &decls) { if (!m_enabled) { return 0; } if (!append) decls.clear(); clang::IdentifierInfo &ident = m_compiler_instance->getASTContext().Idents.get(name.GetStringRef()); clang::LookupResult lookup_result( m_compiler_instance->getSema(), clang::DeclarationName(&ident), clang::SourceLocation(), clang::Sema::LookupOrdinaryName); m_compiler_instance->getSema().LookupName( lookup_result, m_compiler_instance->getSema().getScopeForContext( m_compiler_instance->getASTContext().getTranslationUnitDecl())); uint32_t num_matches = 0; for (clang::NamedDecl *named_decl : lookup_result) { if (num_matches >= max_matches) return num_matches; decls.push_back(named_decl); ++num_matches; } return num_matches; } void ClangModulesDeclVendorImpl::ForEachMacro( const ClangModulesDeclVendor::ModuleVector &modules, std::function handler) { if (!m_enabled) { return; } typedef std::map ModulePriorityMap; ModulePriorityMap module_priorities; ssize_t priority = 0; for (ModuleID module : modules) { module_priorities[module] = priority++; } if (m_compiler_instance->getPreprocessor().getExternalSource()) { m_compiler_instance->getPreprocessor() .getExternalSource() ->ReadDefinedMacros(); } for (clang::Preprocessor::macro_iterator mi = m_compiler_instance->getPreprocessor().macro_begin(), me = m_compiler_instance->getPreprocessor().macro_end(); mi != me; ++mi) { const clang::IdentifierInfo *ii = nullptr; { if (clang::IdentifierInfoLookup *lookup = m_compiler_instance->getPreprocessor() .getIdentifierTable() .getExternalIdentifierLookup()) { lookup->get(mi->first->getName()); } if (!ii) { ii = mi->first; } } ssize_t found_priority = -1; clang::MacroInfo *macro_info = nullptr; for (clang::ModuleMacro *module_macro : m_compiler_instance->getPreprocessor().getLeafModuleMacros(ii)) { clang::Module *module = module_macro->getOwningModule(); { ModulePriorityMap::iterator pi = module_priorities.find(reinterpret_cast(module)); if (pi != module_priorities.end() && pi->second > found_priority) { macro_info = module_macro->getMacroInfo(); found_priority = pi->second; } } clang::Module *top_level_module = module->getTopLevelModule(); if (top_level_module != module) { ModulePriorityMap::iterator pi = module_priorities.find( reinterpret_cast(top_level_module)); if ((pi != module_priorities.end()) && pi->second > found_priority) { macro_info = module_macro->getMacroInfo(); found_priority = pi->second; } } } if (macro_info) { std::string macro_expansion = "#define "; macro_expansion.append(mi->first->getName().str()); { if (macro_info->isFunctionLike()) { macro_expansion.append("("); bool first_arg = true; - for (clang::MacroInfo::arg_iterator ai = macro_info->arg_begin(), - ae = macro_info->arg_end(); - ai != ae; ++ai) { + for (auto pi = macro_info->param_begin(), + pe = macro_info->param_end(); + pi != pe; ++pi) { if (!first_arg) { macro_expansion.append(", "); } else { first_arg = false; } - macro_expansion.append((*ai)->getName().str()); + macro_expansion.append((*pi)->getName().str()); } if (macro_info->isC99Varargs()) { if (first_arg) { macro_expansion.append("..."); } else { macro_expansion.append(", ..."); } } else if (macro_info->isGNUVarargs()) { macro_expansion.append("..."); } macro_expansion.append(")"); } macro_expansion.append(" "); bool first_token = true; for (clang::MacroInfo::tokens_iterator ti = macro_info->tokens_begin(), te = macro_info->tokens_end(); ti != te; ++ti) { if (!first_token) { macro_expansion.append(" "); } else { first_token = false; } if (ti->isLiteral()) { if (const char *literal_data = ti->getLiteralData()) { std::string token_str(literal_data, ti->getLength()); macro_expansion.append(token_str); } else { bool invalid = false; const char *literal_source = m_compiler_instance->getSourceManager().getCharacterData( ti->getLocation(), &invalid); if (invalid) { lldbassert(0 && "Unhandled token kind"); macro_expansion.append(""); } else { macro_expansion.append( std::string(literal_source, ti->getLength())); } } } else if (const char *punctuator_spelling = clang::tok::getPunctuatorSpelling(ti->getKind())) { macro_expansion.append(punctuator_spelling); } else if (const char *keyword_spelling = clang::tok::getKeywordSpelling(ti->getKind())) { macro_expansion.append(keyword_spelling); } else { switch (ti->getKind()) { case clang::tok::TokenKind::identifier: macro_expansion.append(ti->getIdentifierInfo()->getName().str()); break; case clang::tok::TokenKind::raw_identifier: macro_expansion.append(ti->getRawIdentifier().str()); break; default: macro_expansion.append(ti->getName()); break; } } } if (handler(macro_expansion)) { return; } } } } } clang::ModuleLoadResult ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path, bool make_visible) { clang::Module::NameVisibilityKind visibility = make_visible ? clang::Module::AllVisible : clang::Module::Hidden; const bool is_inclusion_directive = false; return m_compiler_instance->loadModule(path.front().second, path, visibility, is_inclusion_directive); } static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer"; lldb_private::ClangModulesDeclVendor * ClangModulesDeclVendor::Create(Target &target) { // FIXME we should insure programmatically that the expression parser's // compiler and the modules runtime's // compiler are both initialized in the same way – preferably by the same // code. if (!target.GetPlatform()->SupportsModules()) return nullptr; const ArchSpec &arch = target.GetArchitecture(); std::vector compiler_invocation_arguments = { "clang", "-fmodules", "-fimplicit-module-maps", "-fcxx-modules", "-fsyntax-only", "-femit-all-decls", "-target", arch.GetTriple().str(), "-fmodules-validate-system-headers", "-Werror=non-modular-include-in-framework-module"}; target.GetPlatform()->AddClangModuleCompilationOptions( &target, compiler_invocation_arguments); compiler_invocation_arguments.push_back(ModuleImportBufferName); // Add additional search paths with { "-I", path } or { "-F", path } here. { llvm::SmallString<128> DefaultModuleCache; const bool erased_on_reboot = false; llvm::sys::path::system_temp_directory(erased_on_reboot, DefaultModuleCache); llvm::sys::path::append(DefaultModuleCache, "org.llvm.clang"); llvm::sys::path::append(DefaultModuleCache, "ModuleCache"); std::string module_cache_argument("-fmodules-cache-path="); module_cache_argument.append(DefaultModuleCache.str().str()); compiler_invocation_arguments.push_back(module_cache_argument); } FileSpecList &module_search_paths = target.GetClangModuleSearchPaths(); for (size_t spi = 0, spe = module_search_paths.GetSize(); spi < spe; ++spi) { const FileSpec &search_path = module_search_paths.GetFileSpecAtIndex(spi); std::string search_path_argument = "-I"; search_path_argument.append(search_path.GetPath()); compiler_invocation_arguments.push_back(search_path_argument); } { FileSpec clang_resource_dir = GetResourceDir(); if (llvm::sys::fs::is_directory(clang_resource_dir.GetPath())) { compiler_invocation_arguments.push_back("-resource-dir"); compiler_invocation_arguments.push_back(clang_resource_dir.GetPath()); } } llvm::IntrusiveRefCntPtr diagnostics_engine = clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions, new StoringDiagnosticConsumer); std::vector compiler_invocation_argument_cstrs; for (const std::string &arg : compiler_invocation_arguments) { compiler_invocation_argument_cstrs.push_back(arg.c_str()); } std::shared_ptr invocation = clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs, diagnostics_engine); if (!invocation) return nullptr; std::unique_ptr source_buffer = llvm::MemoryBuffer::getMemBuffer( "extern int __lldb __attribute__((unavailable));", ModuleImportBufferName); invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName, source_buffer.release()); std::unique_ptr instance( new clang::CompilerInstance); instance->setDiagnostics(diagnostics_engine.get()); instance->setInvocation(invocation); std::unique_ptr action(new clang::SyntaxOnlyAction); instance->setTarget(clang::TargetInfo::CreateTargetInfo( *diagnostics_engine, instance->getInvocation().TargetOpts)); if (!instance->hasTarget()) return nullptr; instance->getTarget().adjust(instance->getLangOpts()); if (!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) return nullptr; instance->getPreprocessor().enableIncrementalProcessing(); instance->createModuleManager(); instance->createSema(action->getTranslationUnitKind(), nullptr); const bool skipFunctionBodies = false; std::unique_ptr parser(new clang::Parser( instance->getPreprocessor(), instance->getSema(), skipFunctionBodies)); instance->getPreprocessor().EnterMainSourceFile(); parser->Initialize(); clang::Parser::DeclGroupPtrTy parsed; while (!parser->ParseTopLevelDecl(parsed)) ; return new ClangModulesDeclVendorImpl(std::move(diagnostics_engine), std::move(invocation), std::move(instance), std::move(parser)); } Index: vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp (revision 321194) @@ -1,630 +1,661 @@ //===-- CPlusPlusNameParser.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CPlusPlusNameParser.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Threading.h" using namespace lldb; using namespace lldb_private; using llvm::Optional; using llvm::None; using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction; using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName; namespace tok = clang::tok; Optional CPlusPlusNameParser::ParseAsFunctionDefinition() { m_next_token_index = 0; Optional result(None); // Try to parse the name as function without a return type specified // e.g. main(int, char*[]) { Bookmark start_position = SetBookmark(); result = ParseFunctionImpl(false); if (result && !HasMoreTokens()) return result; } // Try to parse the name as function with function pointer return type // e.g. void (*get_func(const char*))() result = ParseFuncPtr(true); if (result) return result; // Finally try to parse the name as a function with non-function return type // e.g. int main(int, char*[]) result = ParseFunctionImpl(true); if (HasMoreTokens()) return None; return result; } Optional CPlusPlusNameParser::ParseAsFullName() { m_next_token_index = 0; Optional name_ranges = ParseFullNameImpl(); if (!name_ranges) return None; if (HasMoreTokens()) return None; ParsedName result; result.basename = GetTextForRange(name_ranges.getValue().basename_range); result.context = GetTextForRange(name_ranges.getValue().context_range); return result; } bool CPlusPlusNameParser::HasMoreTokens() { return m_next_token_index < m_tokens.size(); } void CPlusPlusNameParser::Advance() { ++m_next_token_index; } void CPlusPlusNameParser::TakeBack() { --m_next_token_index; } bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) { if (!HasMoreTokens()) return false; if (!Peek().is(kind)) return false; Advance(); return true; } template bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) { if (!HasMoreTokens()) return false; if (!Peek().isOneOf(kinds...)) return false; Advance(); return true; } CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() { return Bookmark(m_next_token_index); } size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; } clang::Token &CPlusPlusNameParser::Peek() { assert(HasMoreTokens()); return m_tokens[m_next_token_index]; } Optional CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { Bookmark start_position = SetBookmark(); if (expect_return_type) { // Consume return type if it's expected. if (!ConsumeTypename()) return None; } auto maybe_name = ParseFullNameImpl(); if (!maybe_name) { return None; } size_t argument_start = GetCurrentPosition(); if (!ConsumeArguments()) { return None; } size_t qualifiers_start = GetCurrentPosition(); SkipFunctionQualifiers(); size_t end_position = GetCurrentPosition(); ParsedFunction result; result.name.basename = GetTextForRange(maybe_name.getValue().basename_range); result.name.context = GetTextForRange(maybe_name.getValue().context_range); result.arguments = GetTextForRange(Range(argument_start, qualifiers_start)); result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position)); start_position.Remove(); return result; } Optional CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) { Bookmark start_position = SetBookmark(); if (expect_return_type) { // Consume return type. if (!ConsumeTypename()) return None; } if (!ConsumeToken(tok::l_paren)) return None; if (!ConsumePtrsAndRefs()) return None; { Bookmark before_inner_function_pos = SetBookmark(); auto maybe_inner_function_name = ParseFunctionImpl(false); if (maybe_inner_function_name) if (ConsumeToken(tok::r_paren)) if (ConsumeArguments()) { SkipFunctionQualifiers(); start_position.Remove(); before_inner_function_pos.Remove(); return maybe_inner_function_name; } } auto maybe_inner_function_ptr_name = ParseFuncPtr(false); if (maybe_inner_function_ptr_name) if (ConsumeToken(tok::r_paren)) if (ConsumeArguments()) { SkipFunctionQualifiers(); start_position.Remove(); return maybe_inner_function_ptr_name; } return None; } bool CPlusPlusNameParser::ConsumeArguments() { return ConsumeBrackets(tok::l_paren, tok::r_paren); } bool CPlusPlusNameParser::ConsumeTemplateArgs() { Bookmark start_position = SetBookmark(); if (!HasMoreTokens() || Peek().getKind() != tok::less) return false; Advance(); // Consuming template arguments is a bit trickier than consuming function // arguments, because '<' '>' brackets are not always trivially balanced. // In some rare cases tokens '<' and '>' can appear inside template arguments // as arithmetic or shift operators not as template brackets. // Examples: std::enable_if<(10u)<(64), bool> // f> // Good thing that compiler makes sure that really ambiguous cases of // '>' usage should be enclosed within '()' brackets. int template_counter = 1; bool can_open_template = false; while (HasMoreTokens() && template_counter > 0) { tok::TokenKind kind = Peek().getKind(); switch (kind) { case tok::greatergreater: template_counter -= 2; can_open_template = false; Advance(); break; case tok::greater: --template_counter; can_open_template = false; Advance(); break; case tok::less: // '<' is an attempt to open a subteamplte // check if parser is at the point where it's actually possible, // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. // No need to do the same for '>' because compiler actually makes sure // that '>' always surrounded by brackets to avoid ambiguity. if (can_open_template) ++template_counter; can_open_template = false; Advance(); break; case tok::kw_operator: // C++ operator overloading. if (!ConsumeOperator()) return false; can_open_template = true; break; case tok::raw_identifier: can_open_template = true; Advance(); break; case tok::l_square: if (!ConsumeBrackets(tok::l_square, tok::r_square)) return false; can_open_template = false; break; case tok::l_paren: if (!ConsumeArguments()) return false; can_open_template = false; break; default: can_open_template = false; Advance(); break; } } assert(template_counter >= 0); if (template_counter > 0) { return false; } start_position.Remove(); return true; } bool CPlusPlusNameParser::ConsumeAnonymousNamespace() { Bookmark start_position = SetBookmark(); if (!ConsumeToken(tok::l_paren)) { return false; } constexpr llvm::StringLiteral g_anonymous("anonymous"); if (HasMoreTokens() && Peek().is(tok::raw_identifier) && Peek().getRawIdentifier() == g_anonymous) { Advance(); } else { return false; } if (!ConsumeToken(tok::kw_namespace)) { return false; } if (!ConsumeToken(tok::r_paren)) { return false; } start_position.Remove(); return true; } +bool CPlusPlusNameParser::ConsumeLambda() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_brace)) { + return false; + } + constexpr llvm::StringLiteral g_lambda("lambda"); + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == g_lambda) { + // Put the matched brace back so we can use ConsumeBrackets + TakeBack(); + } else { + return false; + } + + if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) { + return false; + } + + start_position.Remove(); + return true; +} + bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left, tok::TokenKind right) { Bookmark start_position = SetBookmark(); if (!HasMoreTokens() || Peek().getKind() != left) return false; Advance(); int counter = 1; while (HasMoreTokens() && counter > 0) { tok::TokenKind kind = Peek().getKind(); if (kind == right) --counter; else if (kind == left) ++counter; Advance(); } assert(counter >= 0); if (counter > 0) { return false; } start_position.Remove(); return true; } bool CPlusPlusNameParser::ConsumeOperator() { Bookmark start_position = SetBookmark(); if (!ConsumeToken(tok::kw_operator)) return false; if (!HasMoreTokens()) { return false; } const auto &token = Peek(); switch (token.getKind()) { case tok::kw_new: case tok::kw_delete: // This is 'new' or 'delete' operators. Advance(); // Check for array new/delete. if (HasMoreTokens() && Peek().is(tok::l_square)) { // Consume the '[' and ']'. if (!ConsumeBrackets(tok::l_square, tok::r_square)) return false; } break; #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ case tok::Token: \ Advance(); \ break; #define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly) #include "clang/Basic/OperatorKinds.def" #undef OVERLOADED_OPERATOR #undef OVERLOADED_OPERATOR_MULTI case tok::l_paren: // Call operator consume '(' ... ')'. if (ConsumeBrackets(tok::l_paren, tok::r_paren)) break; return false; case tok::l_square: // This is a [] operator. // Consume the '[' and ']'. if (ConsumeBrackets(tok::l_square, tok::r_square)) break; return false; default: // This might be a cast operator. if (ConsumeTypename()) break; return false; } start_position.Remove(); return true; } void CPlusPlusNameParser::SkipTypeQualifiers() { while (ConsumeToken(tok::kw_const, tok::kw_volatile)) ; } void CPlusPlusNameParser::SkipFunctionQualifiers() { while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp)) ; } bool CPlusPlusNameParser::ConsumeBuiltinType() { bool result = false; bool continue_parsing = true; // Built-in types can be made of a few keywords // like 'unsigned long long int'. This function // consumes all built-in type keywords without // checking if they make sense like 'unsigned char void'. while (continue_parsing && HasMoreTokens()) { switch (Peek().getKind()) { case tok::kw_short: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: case tok::kw_unsigned: case tok::kw_void: case tok::kw_char: case tok::kw_int: case tok::kw_half: case tok::kw_float: case tok::kw_double: case tok::kw___float128: case tok::kw_wchar_t: case tok::kw_bool: case tok::kw_char16_t: case tok::kw_char32_t: result = true; Advance(); break; default: continue_parsing = false; break; } } return result; } void CPlusPlusNameParser::SkipPtrsAndRefs() { // Ignoring result. ConsumePtrsAndRefs(); } bool CPlusPlusNameParser::ConsumePtrsAndRefs() { bool found = false; SkipTypeQualifiers(); while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const, tok::kw_volatile)) { found = true; SkipTypeQualifiers(); } return found; } bool CPlusPlusNameParser::ConsumeDecltype() { Bookmark start_position = SetBookmark(); if (!ConsumeToken(tok::kw_decltype)) return false; if (!ConsumeArguments()) return false; start_position.Remove(); return true; } bool CPlusPlusNameParser::ConsumeTypename() { Bookmark start_position = SetBookmark(); SkipTypeQualifiers(); if (!ConsumeBuiltinType() && !ConsumeDecltype()) { if (!ParseFullNameImpl()) return false; } SkipPtrsAndRefs(); start_position.Remove(); return true; } Optional CPlusPlusNameParser::ParseFullNameImpl() { // Name parsing state machine. enum class State { Beginning, // start of the name AfterTwoColons, // right after :: AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+) AfterTemplate, // right after template brackets () AfterOperator, // right after name of C++ operator }; Bookmark start_position = SetBookmark(); State state = State::Beginning; bool continue_parsing = true; Optional last_coloncolon_position = None; while (continue_parsing && HasMoreTokens()) { const auto &token = Peek(); switch (token.getKind()) { case tok::raw_identifier: // Just a name. if (state != State::Beginning && state != State::AfterTwoColons) { continue_parsing = false; break; } Advance(); state = State::AfterIdentifier; break; case tok::l_paren: { if (state == State::Beginning || state == State::AfterTwoColons) { // (anonymous namespace) if (ConsumeAnonymousNamespace()) { state = State::AfterIdentifier; break; } } // Type declared inside a function 'func()::Type' if (state != State::AfterIdentifier && state != State::AfterTemplate && state != State::AfterOperator) { continue_parsing = false; break; } Bookmark l_paren_position = SetBookmark(); // Consume the '(' ... ') [const]'. if (!ConsumeArguments()) { continue_parsing = false; break; } SkipFunctionQualifiers(); // Consume '::' size_t coloncolon_position = GetCurrentPosition(); if (!ConsumeToken(tok::coloncolon)) { continue_parsing = false; break; } l_paren_position.Remove(); last_coloncolon_position = coloncolon_position; state = State::AfterTwoColons; break; } + case tok::l_brace: + if (state == State::Beginning || state == State::AfterTwoColons) { + if (ConsumeLambda()) { + state = State::AfterIdentifier; + break; + } + } + continue_parsing = false; + break; case tok::coloncolon: // Type nesting delimiter. if (state != State::Beginning && state != State::AfterIdentifier && state != State::AfterTemplate) { continue_parsing = false; break; } last_coloncolon_position = GetCurrentPosition(); Advance(); state = State::AfterTwoColons; break; case tok::less: // Template brackets. if (state != State::AfterIdentifier && state != State::AfterOperator) { continue_parsing = false; break; } if (!ConsumeTemplateArgs()) { continue_parsing = false; break; } state = State::AfterTemplate; break; case tok::kw_operator: // C++ operator overloading. if (state != State::Beginning && state != State::AfterTwoColons) { continue_parsing = false; break; } if (!ConsumeOperator()) { continue_parsing = false; break; } state = State::AfterOperator; break; case tok::tilde: // Destructor. if (state != State::Beginning && state != State::AfterTwoColons) { continue_parsing = false; break; } Advance(); if (ConsumeToken(tok::raw_identifier)) { state = State::AfterIdentifier; } else { TakeBack(); continue_parsing = false; } break; default: continue_parsing = false; break; } } if (state == State::AfterIdentifier || state == State::AfterOperator || state == State::AfterTemplate) { ParsedNameRanges result; if (last_coloncolon_position) { result.context_range = Range(start_position.GetSavedPosition(), last_coloncolon_position.getValue()); result.basename_range = Range(last_coloncolon_position.getValue() + 1, GetCurrentPosition()); } else { result.basename_range = Range(start_position.GetSavedPosition(), GetCurrentPosition()); } start_position.Remove(); return result; } else { return None; } } llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) { if (range.empty()) return llvm::StringRef(); assert(range.begin_index < range.end_index); assert(range.begin_index < m_tokens.size()); assert(range.end_index <= m_tokens.size()); clang::Token &first_token = m_tokens[range.begin_index]; clang::Token &last_token = m_tokens[range.end_index - 1]; clang::SourceLocation start_loc = first_token.getLocation(); clang::SourceLocation end_loc = last_token.getLocation(); unsigned start_pos = start_loc.getRawEncoding(); unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength(); return m_text.take_front(end_pos).drop_front(start_pos); } static const clang::LangOptions &GetLangOptions() { static clang::LangOptions g_options; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { g_options.LineComment = true; g_options.C99 = true; g_options.C11 = true; g_options.CPlusPlus = true; g_options.CPlusPlus11 = true; g_options.CPlusPlus14 = true; g_options.CPlusPlus1z = true; }); return g_options; } static const llvm::StringMap &GetKeywordsMap() { static llvm::StringMap g_map{ #define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name}, #include "clang/Basic/TokenKinds.def" #undef KEYWORD }; return g_map; } void CPlusPlusNameParser::ExtractTokens() { clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(), m_text.data(), m_text.data() + m_text.size()); const auto &kw_map = GetKeywordsMap(); clang::Token token; for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof); lexer.LexFromRawLexer(token)) { if (token.is(clang::tok::raw_identifier)) { auto it = kw_map.find(token.getRawIdentifier()); if (it != kw_map.end()) { token.setKind(it->getValue()); } } m_tokens.push_back(token); } } Index: vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h =================================================================== --- vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h (revision 321194) @@ -1,179 +1,182 @@ //===-- CPlusPlusNameParser.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_CPlusPlusNameParser_h_ #define liblldb_CPlusPlusNameParser_h_ // C Includes // C++ Includes // Other libraries and framework includes #include "clang/Lex/Lexer.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" // Project includes #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" namespace lldb_private { // Helps to validate and obtain various parts of C++ definitions. class CPlusPlusNameParser { public: CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); } struct ParsedName { llvm::StringRef basename; llvm::StringRef context; }; struct ParsedFunction { ParsedName name; llvm::StringRef arguments; llvm::StringRef qualifiers; }; // Treats given text as a function definition and parses it. // Function definition might or might not have a return type and this should // change parsing result. // Examples: // main(int, chat const*) // T fun(int, bool) // std::vector::push_back(int) // int& map>::operator[](short) const // int (*get_function(const chat *))() llvm::Optional ParseAsFunctionDefinition(); // Treats given text as a potentially nested name of C++ entity (function, // class, field) and parses it. // Examples: // main // fun // std::vector::push_back // map>::operator[] // func(int, C&)::nested_class::method llvm::Optional ParseAsFullName(); private: // A C++ definition to parse. llvm::StringRef m_text; // Tokens extracted from m_text. llvm::SmallVector m_tokens; // Index of the next token to look at from m_tokens. size_t m_next_token_index = 0; // Range of tokens saved in m_next_token_index. struct Range { size_t begin_index = 0; size_t end_index = 0; Range() {} Range(size_t begin, size_t end) : begin_index(begin), end_index(end) { assert(end >= begin); } size_t size() const { return end_index - begin_index; } bool empty() const { return size() == 0; } }; struct ParsedNameRanges { Range basename_range; Range context_range; }; // Bookmark automatically restores parsing position (m_next_token_index) // when destructed unless it's manually removed with Remove(). class Bookmark { public: Bookmark(size_t &position) : m_position(position), m_position_value(position) {} Bookmark(const Bookmark &) = delete; Bookmark(Bookmark &&b) : m_position(b.m_position), m_position_value(b.m_position_value), m_restore(b.m_restore) { b.Remove(); } Bookmark &operator=(Bookmark &&) = delete; Bookmark &operator=(const Bookmark &) = delete; void Remove() { m_restore = false; } size_t GetSavedPosition() { return m_position_value; } ~Bookmark() { if (m_restore) { m_position = m_position_value; } } private: size_t &m_position; size_t m_position_value; bool m_restore = true; }; bool HasMoreTokens(); void Advance(); void TakeBack(); bool ConsumeToken(clang::tok::TokenKind kind); template bool ConsumeToken(Ts... kinds); Bookmark SetBookmark(); size_t GetCurrentPosition(); clang::Token &Peek(); bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right); llvm::Optional ParseFunctionImpl(bool expect_return_type); // Parses functions returning function pointers 'string (*f(int x))(float y)' llvm::Optional ParseFuncPtr(bool expect_return_type); // Consumes function arguments enclosed within '(' ... ')' bool ConsumeArguments(); // Consumes template arguments enclosed within '<' ... '>' bool ConsumeTemplateArgs(); // Consumes '(anonymous namespace)' bool ConsumeAnonymousNamespace(); + // Consumes '{lambda ...}' + bool ConsumeLambda(); + // Consumes operator declaration like 'operator *' or 'operator delete []' bool ConsumeOperator(); // Skips 'const' and 'volatile' void SkipTypeQualifiers(); // Skips 'const', 'volatile', '&', '&&' in the end of the function. void SkipFunctionQualifiers(); // Consumes built-in types like 'int' or 'unsigned long long int' bool ConsumeBuiltinType(); // Consumes types defined via decltype keyword. bool ConsumeDecltype(); // Skips 'const' and 'volatile' void SkipPtrsAndRefs(); // Consumes things like 'const * const &' bool ConsumePtrsAndRefs(); // Consumes full type name like 'Namespace::Class::Method()::InnerClass' bool ConsumeTypename(); llvm::Optional ParseFullNameImpl(); llvm::StringRef GetTextForRange(const Range &range); // Populate m_tokens by calling clang lexer on m_text. void ExtractTokens(); }; } // namespace lldb_private #endif // liblldb_CPlusPlusNameParser_h_ Index: vendor/lldb/dist/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp (revision 321194) @@ -1,889 +1,890 @@ //===-- PlatformRemoteGDBServer.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PlatformRemoteGDBServer.h" #include "lldb/Host/Config.h" // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UriParser.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::platform_gdb_server; static bool g_initialized = false; void PlatformRemoteGDBServer::Initialize() { Platform::Initialize(); if (g_initialized == false) { g_initialized = true; PluginManager::RegisterPlugin( PlatformRemoteGDBServer::GetPluginNameStatic(), PlatformRemoteGDBServer::GetDescriptionStatic(), PlatformRemoteGDBServer::CreateInstance); } } void PlatformRemoteGDBServer::Terminate() { if (g_initialized) { g_initialized = false; PluginManager::UnregisterPlugin(PlatformRemoteGDBServer::CreateInstance); } Platform::Terminate(); } PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force, const ArchSpec *arch) { bool create = force; if (!create) { create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); } if (create) return PlatformSP(new PlatformRemoteGDBServer()); return PlatformSP(); } ConstString PlatformRemoteGDBServer::GetPluginNameStatic() { static ConstString g_name("remote-gdb-server"); return g_name; } const char *PlatformRemoteGDBServer::GetDescriptionStatic() { return "A platform that uses the GDB remote protocol as the communication " "transport."; } const char *PlatformRemoteGDBServer::GetDescription() { if (m_platform_description.empty()) { if (IsConnected()) { // Send the get description packet } } if (!m_platform_description.empty()) return m_platform_description.c_str(); return GetDescriptionStatic(); } Status PlatformRemoteGDBServer::ResolveExecutable( const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { // copied from PlatformRemoteiOS Status error; // Nothing special to do here, just use the actual file and architecture ModuleSpec resolved_module_spec(module_spec); // Resolve any executable within an apk on Android? // Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); if (resolved_module_spec.GetFileSpec().Exists() || module_spec.GetUUID().IsValid()) { if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, module_search_paths_ptr, NULL, NULL); if (exe_module_sp && exe_module_sp->GetObjectFile()) return error; exe_module_sp.reset(); } // No valid architecture was specified or the exact arch wasn't // found so ask the platform for the architectures that we should be // using (in the correct order) and see if we can find a match that way StreamString arch_names; for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, resolved_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, module_search_paths_ptr, NULL, NULL); // Did we find an executable using one of the if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; else error.SetErrorToGenericError(); } if (idx > 0) arch_names.PutCString(", "); arch_names.PutCString( resolved_module_spec.GetArchitecture().GetArchitectureName()); } if (error.Fail() || !exe_module_sp) { if (resolved_module_spec.GetFileSpec().Readable()) { error.SetErrorStringWithFormat( "'%s' doesn't contain any '%s' platform architectures: %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), GetPluginName().GetCString(), arch_names.GetData()); } else { error.SetErrorStringWithFormat( "'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } else { error.SetErrorStringWithFormat( "'%s' does not exist", resolved_module_spec.GetFileSpec().GetPath().c_str()); } return error; } bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); const auto module_path = module_file_spec.GetPath(false); if (!m_gdb_client.GetModuleInfo(module_file_spec, arch, module_spec)) { if (log) log->Printf( "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s", __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str()); return false; } if (log) { StreamString stream; module_spec.Dump(stream); log->Printf( "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str(), stream.GetData()); } return true; } Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { // Default to the local case local_file = platform_file; return Status(); } //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ PlatformRemoteGDBServer::PlatformRemoteGDBServer() : Platform(false), // This is a remote platform m_gdb_client() {} //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ PlatformRemoteGDBServer::~PlatformRemoteGDBServer() {} bool PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) { ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture(); if (idx == 0) { arch = remote_arch; return arch.IsValid(); } else if (idx == 1 && remote_arch.IsValid() && remote_arch.GetTriple().isArch64Bit()) { arch.SetTriple(remote_arch.GetTriple().get32BitArchVariant()); return arch.IsValid(); } return false; } size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode( Target &target, BreakpointSite *bp_site) { // This isn't needed if the z/Z packets are supported in the GDB remote // server. But we might need a packet to detect this. return 0; } bool PlatformRemoteGDBServer::GetRemoteOSVersion() { uint32_t major, minor, update; if (m_gdb_client.GetOSVersion(major, minor, update)) { m_major_os_version = major; m_minor_os_version = minor; m_update_os_version = update; return true; } return false; } bool PlatformRemoteGDBServer::GetRemoteOSBuildString(std::string &s) { return m_gdb_client.GetOSBuildString(s); } bool PlatformRemoteGDBServer::GetRemoteOSKernelDescription(std::string &s) { return m_gdb_client.GetOSKernelDescription(s); } // Remote Platform subclasses need to override this function ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() { return m_gdb_client.GetSystemArchitecture(); } FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() { if (IsConnected()) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); FileSpec working_dir; if (m_gdb_client.GetWorkingDir(working_dir) && log) log->Printf( "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'", working_dir.GetCString()); return working_dir; } else { return Platform::GetRemoteWorkingDirectory(); } } bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory( const FileSpec &working_dir) { if (IsConnected()) { // Clear the working directory it case it doesn't get set correctly. This // will // for use to re-read it Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')", working_dir.GetCString()); return m_gdb_client.SetWorkingDir(working_dir) == 0; } else return Platform::SetRemoteWorkingDirectory(working_dir); } bool PlatformRemoteGDBServer::IsConnected() const { return m_gdb_client.IsConnected(); } Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { Status error; if (IsConnected()) { error.SetErrorStringWithFormat("the platform is already connected to '%s', " "execute 'platform disconnect' to close the " "current connection", GetHostname()); } else { if (args.GetArgumentCount() == 1) { m_gdb_client.SetConnection(new ConnectionFileDescriptor()); // we're going to reuse the hostname when we connect to the debugserver int port; std::string path; const char *url = args.GetArgumentAtIndex(0); if (!url) return Status("URL is null."); llvm::StringRef scheme, hostname, pathname; if (!UriParser::Parse(url, scheme, hostname, port, pathname)) return Status("Invalid URL: %s", url); m_platform_scheme = scheme; m_platform_hostname = hostname; path = pathname; const ConnectionStatus status = m_gdb_client.Connect(url, &error); if (status == eConnectionStatusSuccess) { if (m_gdb_client.HandshakeWithServer(&error)) { m_gdb_client.GetHostInfo(); // If a working directory was set prior to connecting, send it down // now if (m_working_dir) m_gdb_client.SetWorkingDir(m_working_dir); } else { m_gdb_client.Disconnect(); if (error.Success()) error.SetErrorString("handshake failed"); } } } else { error.SetErrorString( "\"platform connect\" takes a single argument: "); } } return error; } Status PlatformRemoteGDBServer::DisconnectRemote() { Status error; m_gdb_client.Disconnect(&error); m_remote_signals_sp.reset(); return error; } const char *PlatformRemoteGDBServer::GetHostname() { m_gdb_client.GetHostname(m_name); if (m_name.empty()) return NULL; return m_name.c_str(); } const char *PlatformRemoteGDBServer::GetUserName(uint32_t uid) { // Try and get a cache user name first const char *cached_user_name = Platform::GetUserName(uid); if (cached_user_name) return cached_user_name; std::string name; if (m_gdb_client.GetUserName(uid, name)) return SetCachedUserName(uid, name.c_str(), name.size()); SetUserNameNotFound(uid); // Negative cache so we don't keep sending packets return NULL; } const char *PlatformRemoteGDBServer::GetGroupName(uint32_t gid) { const char *cached_group_name = Platform::GetGroupName(gid); if (cached_group_name) return cached_group_name; std::string name; if (m_gdb_client.GetGroupName(gid, name)) return SetCachedGroupName(gid, name.c_str(), name.size()); SetGroupNameNotFound(gid); // Negative cache so we don't keep sending packets return NULL; } uint32_t PlatformRemoteGDBServer::FindProcesses( const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { return m_gdb_client.FindProcesses(match_info, process_infos); } bool PlatformRemoteGDBServer::GetProcessInfo( lldb::pid_t pid, ProcessInstanceInfo &process_info) { return m_gdb_client.GetProcessInfo(pid, process_info); } Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); Status error; if (log) log->Printf("PlatformRemoteGDBServer::%s() called", __FUNCTION__); auto num_file_actions = launch_info.GetNumFileActions(); for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) { const auto file_action = launch_info.GetFileActionAtIndex(i); if (file_action->GetAction() != FileAction::eFileActionOpen) continue; switch (file_action->GetFD()) { case STDIN_FILENO: m_gdb_client.SetSTDIN(file_action->GetFileSpec()); break; case STDOUT_FILENO: m_gdb_client.SetSTDOUT(file_action->GetFileSpec()); break; case STDERR_FILENO: m_gdb_client.SetSTDERR(file_action->GetFileSpec()); break; } } m_gdb_client.SetDisableASLR( launch_info.GetFlags().Test(eLaunchFlagDisableASLR)); m_gdb_client.SetDetachOnError( launch_info.GetFlags().Test(eLaunchFlagDetachOnError)); FileSpec working_dir = launch_info.GetWorkingDirectory(); if (working_dir) { m_gdb_client.SetWorkingDir(working_dir); } // Send the environment and the program + arguments after we connect const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector(); if (envp) { const char *env_entry; for (int i = 0; (env_entry = envp[i]); ++i) { if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0) break; } } ArchSpec arch_spec = launch_info.GetArchitecture(); const char *arch_triple = arch_spec.GetTriple().str().c_str(); m_gdb_client.SendLaunchArchPacket(arch_triple); if (log) log->Printf( "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", __FUNCTION__, arch_triple ? arch_triple : ""); int arg_packet_err; { // Scope for the scoped timeout object process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout( m_gdb_client, std::chrono::seconds(5)); arg_packet_err = m_gdb_client.SendArgumentsPacket(launch_info); } if (arg_packet_err == 0) { std::string error_str; if (m_gdb_client.GetLaunchSuccess(error_str)) { const auto pid = m_gdb_client.GetCurrentProcessID(false); if (pid != LLDB_INVALID_PROCESS_ID) { launch_info.SetProcessID(pid); if (log) log->Printf("PlatformRemoteGDBServer::%s() pid %" PRIu64 " launched successfully", __FUNCTION__, pid); } else { if (log) log->Printf("PlatformRemoteGDBServer::%s() launch succeeded but we " "didn't get a valid process id back!", __FUNCTION__); error.SetErrorString("failed to get PID"); } } else { error.SetErrorString(error_str.c_str()); if (log) log->Printf("PlatformRemoteGDBServer::%s() launch failed: %s", __FUNCTION__, error.AsCString()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } return error; } Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) { if (!KillSpawnedProcess(pid)) return Status("failed to kill remote spawned process"); return Status(); } lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess( ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Status &error) { lldb::ProcessSP process_sp; if (IsRemote()) { if (IsConnected()) { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; if (!LaunchGDBServer(debugserver_pid, connect_url)) { error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", GetHostname()); } else { if (target == NULL) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget(debugger, "", "", false, NULL, new_target_sp); target = new_target_sp.get(); } else error.Clear(); if (target && error.Success()) { debugger.GetTargetList().SetSelectedTarget(target); // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! process_sp = target->CreateProcess( launch_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) { error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); // Retry the connect remote one time... if (error.Fail()) error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); if (error.Success()) error = process_sp->Launch(launch_info); else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { printf("error: connect remote failed (%s)\n", error.AsCString()); KillSpawnedProcess(debugserver_pid); } } } } } else { error.SetErrorString("not connected to remote gdb server"); } } return process_sp; } bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url) { ArchSpec remote_arch = GetRemoteSystemArchitecture(); llvm::Triple &remote_triple = remote_arch.GetTriple(); uint16_t port = 0; std::string socket_name; bool launch_result = false; if (remote_triple.getVendor() == llvm::Triple::Apple && remote_triple.getOS() == llvm::Triple::IOS) { // When remote debugging to iOS, we use a USB mux that always talks // to localhost, so we will need the remote debugserver to accept // connections // only from localhost, no matter what our current hostname is launch_result = m_gdb_client.LaunchGDBServer("127.0.0.1", pid, port, socket_name); } else { // All other hosts should use their actual hostname launch_result = m_gdb_client.LaunchGDBServer(nullptr, pid, port, socket_name); } if (!launch_result) return false; connect_url = MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port, (socket_name.empty()) ? nullptr : socket_name.c_str()); return true; } bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { return m_gdb_client.KillSpawnedProcess(pid); } lldb::ProcessSP PlatformRemoteGDBServer::Attach( ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Status &error) { lldb::ProcessSP process_sp; if (IsRemote()) { if (IsConnected()) { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; if (!LaunchGDBServer(debugserver_pid, connect_url)) { error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", GetHostname()); } else { if (target == NULL) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget(debugger, "", "", false, NULL, new_target_sp); target = new_target_sp.get(); } else error.Clear(); if (target && error.Success()) { debugger.GetTargetList().SetSelectedTarget(target); // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! process_sp = target->CreateProcess( attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) { error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); if (error.Success()) { ListenerSP listener_sp = attach_info.GetHijackListener(); if (listener_sp) process_sp->HijackProcessEvents(listener_sp); error = process_sp->Attach(attach_info); } if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) { KillSpawnedProcess(debugserver_pid); } } } } } else { error.SetErrorString("not connected to remote gdb server"); } } return process_sp; } Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, uint32_t mode) { Status error = m_gdb_client.MakeDirectory(file_spec, mode); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) " "error = %u (%s)", file_spec.GetCString(), mode, error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { Status error = m_gdb_client.GetFilePermissions(file_spec, file_permissions); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::GetFilePermissions(path='%s', " "file_permissions=%o) error = %u (%s)", file_spec.GetCString(), file_permissions, error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { Status error = m_gdb_client.SetFilePermissions(file_spec, file_permissions); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::SetFilePermissions(path='%s', " "file_permissions=%o) error = %u (%s)", file_spec.GetCString(), file_permissions, error.GetError(), error.AsCString()); return error; } lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec, uint32_t flags, uint32_t mode, Status &error) { return m_gdb_client.OpenFile(file_spec, flags, mode, error); } bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) { return m_gdb_client.CloseFile(fd, error); } lldb::user_id_t PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) { return m_gdb_client.GetFileSize(file_spec); } uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error) { return m_gdb_client.ReadFile(fd, offset, dst, dst_len, error); } uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Status &error) { return m_gdb_client.WriteFile(fd, offset, src, src_len, error); } Status PlatformRemoteGDBServer::PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid, uint32_t gid) { return Platform::PutFile(source, destination, uid, gid); } Status PlatformRemoteGDBServer::CreateSymlink( const FileSpec &src, // The name of the link is in src const FileSpec &dst) // The symlink points to dst { Status error = m_gdb_client.CreateSymlink(src, dst); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') " "error = %u (%s)", src.GetCString(), dst.GetCString(), error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { Status error = m_gdb_client.Unlink(file_spec); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)", file_spec.GetCString(), error.GetError(), error.AsCString()); return error; } bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { return m_gdb_client.GetFileExists(file_spec); } Status PlatformRemoteGDBServer::RunShellCommand( const char *command, // Shouldn't be NULL const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the // process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { return m_gdb_client.RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); } void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() { m_trap_handlers.push_back(ConstString("_sigtramp")); } const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() { if (!IsConnected()) return Platform::GetRemoteUnixSignals(); if (m_remote_signals_sp) return m_remote_signals_sp; // If packet not implemented or JSON failed to parse, // we'll guess the signal set based on the remote architecture. m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture()); StringExtractorGDBRemote response; auto result = m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo", response, false); if (result != decltype(result)::Success || response.GetResponseType() != response.eResponse) return m_remote_signals_sp; auto object_sp = StructuredData::ParseJSON(response.GetStringRef()); if (!object_sp || !object_sp->IsValid()) return m_remote_signals_sp; auto array_sp = object_sp->GetAsArray(); if (!array_sp || !array_sp->IsValid()) return m_remote_signals_sp; auto remote_signals_sp = std::make_shared(); bool done = array_sp->ForEach( [&remote_signals_sp](StructuredData::Object *object) -> bool { if (!object || !object->IsValid()) return false; auto dict = object->GetAsDictionary(); if (!dict || !dict->IsValid()) return false; // Signal number and signal name are required. int signo; if (!dict->GetValueForKeyAsInteger("signo", signo)) return false; llvm::StringRef name; if (!dict->GetValueForKeyAsString("name", name)) return false; // We can live without short_name, description, etc. bool suppress{false}; auto object_sp = dict->GetValueForKey("suppress"); if (object_sp && object_sp->IsValid()) suppress = object_sp->GetBooleanValue(); bool stop{false}; object_sp = dict->GetValueForKey("stop"); if (object_sp && object_sp->IsValid()) stop = object_sp->GetBooleanValue(); bool notify{false}; object_sp = dict->GetValueForKey("notify"); if (object_sp && object_sp->IsValid()) notify = object_sp->GetBooleanValue(); std::string description{""}; object_sp = dict->GetValueForKey("description"); if (object_sp && object_sp->IsValid()) description = object_sp->GetStringValue(); remote_signals_sp->AddSignal(signo, name.str().c_str(), suppress, stop, notify, description.c_str()); return true; }); if (done) m_remote_signals_sp = std::move(remote_signals_sp); return m_remote_signals_sp; } std::string PlatformRemoteGDBServer::MakeGdbServerUrl( const std::string &platform_scheme, const std::string &platform_hostname, uint16_t port, const char *socket_name) { const char *override_scheme = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME"); const char *override_hostname = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME"); const char *port_offset_c_str = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET"); int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0; return MakeUrl(override_scheme ? override_scheme : platform_scheme.c_str(), override_hostname ? override_hostname : platform_hostname.c_str(), port + port_offset, socket_name); } std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme, const char *hostname, uint16_t port, const char *path) { StreamString result; result.Printf("%s://%s", scheme, hostname); if (port != 0) result.Printf(":%u", port); if (path) result.Write(path, strlen(path)); return result.GetString(); } lldb::ProcessSP PlatformRemoteGDBServer::ConnectProcess( llvm::StringRef connect_url, llvm::StringRef plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Status &error) { if (!IsRemote() || !IsConnected()) { error.SetErrorString("Not connected to remote gdb server"); return nullptr; } return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); } size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger, Status &error) { std::vector connection_urls; GetPendingGdbServerList(connection_urls); for (size_t i = 0; i < connection_urls.size(); ++i) { ConnectProcess(connection_urls[i].c_str(), "", debugger, nullptr, error); if (error.Fail()) return i; // We already connected to i process succsessfully } return connection_urls.size(); } size_t PlatformRemoteGDBServer::GetPendingGdbServerList( std::vector &connection_urls) { std::vector> remote_servers; m_gdb_client.QueryGDBServer(remote_servers); for (const auto &gdbserver : remote_servers) { const char *socket_name_cstr = gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); connection_urls.emplace_back( MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, gdbserver.first, socket_name_cstr)); } return connection_urls.size(); } Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp (revision 321194) @@ -1,2580 +1,2576 @@ //===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeProcessLinux.h" // C Includes #include #include #include #include // C++ Includes #include #include #include #include #include // Other libraries and framework includes #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/common/NativeBreakpoint.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/linux/Ptrace.h" #include "lldb/Host/linux/Uio.h" #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StringExtractor.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" #include "NativeThreadLinux.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Procfs.h" #include #include #include #include #include #include // Support hardware breakpoints in case it has not been defined #ifndef TRAP_HWBKPT #define TRAP_HWBKPT 4 #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; using namespace llvm; // Private bits we only need internally. static bool ProcessVmReadvSupported() { static bool is_supported; static llvm::once_flag flag; llvm::call_once(flag, [] { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); uint32_t source = 0x47424742; uint32_t dest = 0; struct iovec local, remote; remote.iov_base = &source; local.iov_base = &dest; remote.iov_len = local.iov_len = sizeof source; // We shall try if cross-process-memory reads work by attempting to read a // value from our own process. ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); is_supported = (res == sizeof(source) && source == dest); if (is_supported) LLDB_LOG(log, "Detected kernel support for process_vm_readv syscall. " "Fast memory reads enabled."); else LLDB_LOG(log, "syscall process_vm_readv failed (error: {0}). Fast memory " "reads disabled.", llvm::sys::StrError()); }); return is_supported; } namespace { void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (!log) return; if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDIN as is"); if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDOUT as is"); if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDERR as is"); int i = 0; for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; ++args, ++i) LLDB_LOG(log, "arg {0}: '{1}'", i, *args); } void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { uint8_t *ptr = (uint8_t *)bytes; const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); for (uint32_t i = 0; i < loop_count; i++) { s.Printf("[%x]", *ptr); ptr++; } } void PtraceDisplayBytes(int &req, void *data, size_t data_size) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (!log) return; StreamString buf; switch (req) { case PTRACE_POKETEXT: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); break; } case PTRACE_POKEDATA: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); break; } case PTRACE_POKEUSER: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); break; } case PTRACE_SETREGS: { DisplayBytes(buf, data, data_size); LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); break; } case PTRACE_SETFPREGS: { DisplayBytes(buf, data, data_size); LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); break; } case PTRACE_SETSIGINFO: { DisplayBytes(buf, data, sizeof(siginfo_t)); LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); break; } case PTRACE_SETREGSET: { // Extract iov_base from data, which is a pointer to the struct IOVEC DisplayBytes(buf, *(void **)data, data_size); LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); break; } default: {} } } static constexpr unsigned k_ptrace_word_size = sizeof(void *); static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size"); } // end of anonymous namespace // Simple helper function to ensure flags are enabled on the given file // descriptor. static Status EnsureFDFlags(int fd, int flags) { Status error; int status = fcntl(fd, F_GETFL); if (status == -1) { error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return error; } return error; } // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- -llvm::Expected +llvm::Expected> NativeProcessLinux::Factory::Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop) const { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); MaybeLogLaunchInfo(launch_info); Status status; ::pid_t pid = ProcessLauncherPosixFork() .LaunchProcess(launch_info, status) .GetProcessId(); LLDB_LOG(log, "pid = {0:x}", pid); if (status.Fail()) { LLDB_LOG(log, "failed to launch process: {0}", status); return status.ToError(); } // Wait for the child process to trap on its call to execve. int wstatus; ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); assert(wpid == pid); (void)wpid; if (!WIFSTOPPED(wstatus)) { LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", WaitStatus::Decode(wstatus)); return llvm::make_error("Could not sync with inferior process", llvm::inconvertibleErrorCode()); } LLDB_LOG(log, "inferior started, now in stopped state"); ArchSpec arch; if ((status = ResolveProcessArchitecture(pid, arch)).Fail()) return status.ToError(); // Set the architecture to the exe architecture. LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, arch.GetArchitectureName()); status = SetDefaultPtraceOpts(pid); if (status.Fail()) { LLDB_LOG(log, "failed to set default ptrace options: {0}", status); return status.ToError(); } - std::shared_ptr process_sp(new NativeProcessLinux( + return std::unique_ptr(new NativeProcessLinux( pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate, - arch, mainloop)); - process_sp->InitializeThreads({pid}); - return process_sp; + arch, mainloop, {pid})); } -llvm::Expected NativeProcessLinux::Factory::Attach( +llvm::Expected> +NativeProcessLinux::Factory::Attach( lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop) const { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); // Retrieve the architecture for the running process. ArchSpec arch; Status status = ResolveProcessArchitecture(pid, arch); if (!status.Success()) return status.ToError(); auto tids_or = NativeProcessLinux::Attach(pid); if (!tids_or) return tids_or.takeError(); - std::shared_ptr process_sp( - new NativeProcessLinux(pid, -1, native_delegate, arch, mainloop)); - process_sp->InitializeThreads(*tids_or); - return process_sp; + return std::unique_ptr(new NativeProcessLinux( + pid, -1, native_delegate, arch, mainloop, *tids_or)); } // ----------------------------------------------------------------------------- // Public Instance Methods // ----------------------------------------------------------------------------- NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate, - const ArchSpec &arch, MainLoop &mainloop) + const ArchSpec &arch, MainLoop &mainloop, + llvm::ArrayRef<::pid_t> tids) : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) { if (m_terminal_fd != -1) { Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); assert(status.Success()); } Status status; m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); assert(m_sigchld_handle && status.Success()); -} -void NativeProcessLinux::InitializeThreads(llvm::ArrayRef<::pid_t> tids) { for (const auto &tid : tids) { NativeThreadLinuxSP thread_sp = AddThread(tid); assert(thread_sp && "AddThread() returned a nullptr thread"); thread_sp->SetStoppedBySignal(SIGSTOP); ThreadWasCreated(*thread_sp); } // Let our process instance know the thread has stopped. SetCurrentThreadID(tids[0]); SetState(StateType::eStateStopped, false); // Proccess any signals we received before installing our handler SigchldHandler(); } llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); Status status; // Use a map to keep track of the threads which we have attached/need to // attach. Host::TidMap tids_to_attach; while (Host::FindProcessThreads(pid, tids_to_attach)) { for (Host::TidMap::iterator it = tids_to_attach.begin(); it != tids_to_attach.end();) { if (it->second == false) { lldb::tid_t tid = it->first; // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. if ((status = PtraceWrapper(PTRACE_ATTACH, tid)).Fail()) { // No such thread. The thread may have exited. // More error handling may be needed. if (status.GetError() == ESRCH) { it = tids_to_attach.erase(it); continue; } return status.ToError(); } int wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL); // Need to use __WALL otherwise we receive an error with errno=ECHLD // At this point we should have a thread stopped if waitpid succeeds. if (wpid < 0) { // No such thread. The thread may have exited. // More error handling may be needed. if (errno == ESRCH) { it = tids_to_attach.erase(it); continue; } return llvm::errorCodeToError( std::error_code(errno, std::generic_category())); } if ((status = SetDefaultPtraceOpts(tid)).Fail()) return status.ToError(); LLDB_LOG(log, "adding tid = {0}", tid); it->second = true; } // move the loop forward ++it; } } size_t tid_count = tids_to_attach.size(); if (tid_count == 0) return llvm::make_error("No such process", llvm::inconvertibleErrorCode()); std::vector<::pid_t> tids; tids.reserve(tid_count); for (const auto &p : tids_to_attach) tids.push_back(p.first); return std::move(tids); } Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { long ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. ptrace_opts |= PTRACE_O_TRACEEXIT; // Have the tracer trace threads which spawn in the inferior process. // TODO: if we want to support tracing the inferiors' child, add the // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) ptrace_opts |= PTRACE_O_TRACECLONE; // Have the tracer notify us before execve returns // (needed to disable legacy SIGTRAP generation) ptrace_opts |= PTRACE_O_TRACEEXEC; return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } // Handles all waitpid events from the inferior process. void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Certain activities differ based on whether the pid is the tid of the main // thread. const bool is_main_thread = (pid == GetID()); // Handle when the thread exits. if (exited) { LLDB_LOG(log, "got exit signal({0}) , tid = {1} ({2} main thread)", signal, pid, is_main_thread ? "is" : "is not"); // This is a thread that exited. Ensure we're not tracking it anymore. const bool thread_found = StopTrackingThread(pid); if (is_main_thread) { // We only set the exit status and notify the delegate if we haven't // already set the process // state to an exited state. We normally should have received a SIGTRAP | // (PTRACE_EVENT_EXIT << 8) // for the main thread. const bool already_notified = (GetState() == StateType::eStateExited) || (GetState() == StateType::eStateCrashed); if (!already_notified) { LLDB_LOG( log, "tid = {0} handling main thread exit ({1}), expected exit state " "already set but state was {2} instead, setting exit state now", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", GetState()); // The main thread exited. We're done monitoring. Report to delegate. SetExitStatus(status, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); } else LLDB_LOG(log, "tid = {0} main thread now exited (%s)", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); } else { // Do we want to report to the delegate in this case? I think not. If // this was an orderly thread exit, we would already have received the // SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal, and we would have done an // all-stop then. LLDB_LOG(log, "tid = {0} handling non-main thread exit (%s)", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); } return; } siginfo_t info; const auto info_err = GetSignalInfo(pid, &info); auto thread_sp = GetThreadByID(pid); if (!thread_sp) { // Normally, the only situation when we cannot find the thread is if we have // just received a new thread notification. This is indicated by // GetSignalInfo() returning si_code == SI_USER and si_pid == 0 LLDB_LOG(log, "received notification about an unknown tid {0}.", pid); if (info_err.Fail()) { LLDB_LOG(log, "(tid {0}) GetSignalInfo failed ({1}). " "Ingoring this notification.", pid, info_err); return; } LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code, info.si_pid); auto thread_sp = AddThread(pid); // Resume the newly created thread. ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); ThreadWasCreated(*thread_sp); return; } // Get details on the signal raised. if (info_err.Success()) { // We have retrieved the signal info. Dispatch appropriately. if (info.si_signo == SIGTRAP) MonitorSIGTRAP(info, *thread_sp); else MonitorSignal(info, *thread_sp, exited); } else { if (info_err.GetError() == EINVAL) { // This is a group stop reception for this tid. // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU // into the tracee, triggering the group-stop mechanism. Normally // receiving these would stop the process, pending a SIGCONT. Simulating // this state in a debugger is hard and is generally not needed (one use // case is debugging background task being managed by a shell). For // general use, it is sufficient to stop the process in a signal-delivery // stop which happens before the group stop. This done by MonitorSignal // and works correctly for all signals. LLDB_LOG(log, "received a group stop for pid {0} tid {1}. Transparent " "handling of group stops not supported, resuming the " "thread.", GetID(), pid); ResumeThread(*thread_sp, thread_sp->GetState(), LLDB_INVALID_SIGNAL_NUMBER); } else { // ptrace(GETSIGINFO) failed (but not due to group-stop). // A return value of ESRCH means the thread/process is no longer on the // system, so it was killed somehow outside of our control. Either way, // we can't do anything with it anymore. // Stop tracking the metadata for the thread since it's entirely off the // system now. const bool thread_found = StopTrackingThread(pid); LLDB_LOG(log, "GetSignalInfo failed: {0}, tid = {1}, signal = {2}, " "status = {3}, main_thread = {4}, thread_found: {5}", info_err, pid, signal, status, is_main_thread, thread_found); if (is_main_thread) { // Notify the delegate - our process is not available but appears to // have been killed outside // our control. Is eStateExited the right exit state in this case? SetExitStatus(status, true); SetState(StateType::eStateExited, true); } else { // This thread was pulled out from underneath us. Anything to do here? // Do we want to do an all stop? LLDB_LOG(log, "pid {0} tid {1} non-main thread exit occurred, didn't " "tell delegate anything since thread disappeared out " "from underneath us", GetID(), pid); } } } } void NativeProcessLinux::WaitForNewThread(::pid_t tid) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid); if (new_thread_sp) { // We are already tracking the thread - we got the event on the new thread // (see // MonitorSignal) before this one. We are done. return; } // The thread is not tracked yet, let's wait for it to appear. int status = -1; LLDB_LOG(log, "received thread creation event for tid {0}. tid not tracked " "yet, waiting for thread to appear...", tid); ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, __WALL); // Since we are waiting on a specific tid, this must be the creation event. // But let's do some checks just in case. if (wait_pid != tid) { LLDB_LOG(log, "waiting for tid {0} failed. Assuming the thread has " "disappeared in the meantime", tid); // The only way I know of this could happen is if the whole process was // SIGKILLed in the mean time. In any case, we can't do anything about that // now. return; } if (WIFEXITED(status)) { LLDB_LOG(log, "waiting for tid {0} returned an 'exited' event. Not " "tracking the thread.", tid); // Also a very improbable event. return; } LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid); new_thread_sp = AddThread(tid); ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); ThreadWasCreated(*new_thread_sp); } void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); const bool is_main_thread = (thread.GetID() == GetID()); assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); switch (info.si_code) { // TODO: these two cases are required if we want to support tracing of the // inferiors' children. We'd need this to debug a monitor. // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): { // This is the notification on the parent thread which informs us of new // thread // creation. // We don't want to do anything with the parent thread so we just resume it. // In case we // want to implement "break on thread creation" functionality, we would need // to stop // here. unsigned long event_message = 0; if (GetEventMessage(thread.GetID(), &event_message).Fail()) { LLDB_LOG(log, "pid {0} received thread creation event but " "GetEventMessage failed so we don't know the new tid", thread.GetID()); } else WaitForNewThread(event_message); ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); break; } case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): { NativeThreadLinuxSP main_thread_sp; LLDB_LOG(log, "received exec event, code = {0}", info.si_code ^ SIGTRAP); // Exec clears any pending notifications. m_pending_notification_tid = LLDB_INVALID_THREAD_ID; // Remove all but the main thread here. Linux fork creates a new process // which only copies the main thread. LLDB_LOG(log, "exec received, stop tracking all but main thread"); for (auto thread_sp : m_threads) { const bool is_main_thread = thread_sp && thread_sp->GetID() == GetID(); if (is_main_thread) { main_thread_sp = std::static_pointer_cast(thread_sp); LLDB_LOG(log, "found main thread with tid {0}, keeping", main_thread_sp->GetID()); } else { LLDB_LOG(log, "discarding non-main-thread tid {0} due to exec", thread_sp->GetID()); } } m_threads.clear(); if (main_thread_sp) { m_threads.push_back(main_thread_sp); SetCurrentThreadID(main_thread_sp->GetID()); main_thread_sp->SetStoppedByExec(); } else { SetCurrentThreadID(LLDB_INVALID_THREAD_ID); LLDB_LOG(log, "pid {0} no main thread found, discarded all threads, " "we're in a no-thread state!", GetID()); } // Tell coordinator about about the "new" (since exec) stopped main thread. ThreadWasCreated(*main_thread_sp); // Let our delegate know we have just exec'd. NotifyDidExec(); // If we have a main thread, indicate we are stopped. assert(main_thread_sp && "exec called during ptraced process but no main " "thread metadata tracked"); // Let the process know we're stopped. StopRunningThreads(main_thread_sp->GetID()); break; } case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): { // The inferior process or one of its threads is about to exit. // We don't want to do anything with the thread so we just resume it. In // case we // want to implement "break on thread exit" functionality, we would need to // stop // here. unsigned long data = 0; if (GetEventMessage(thread.GetID(), &data).Fail()) data = -1; LLDB_LOG(log, "received PTRACE_EVENT_EXIT, data = {0:x}, WIFEXITED={1}, " "WIFSIGNALED={2}, pid = {3}, main_thread = {4}", data, WIFEXITED(data), WIFSIGNALED(data), thread.GetID(), is_main_thread); if (is_main_thread) SetExitStatus(WaitStatus::Decode(data), true); StateType state = thread.GetState(); if (!StateIsRunningState(state)) { // Due to a kernel bug, we may sometimes get this stop after the inferior // gets a // SIGKILL. This confuses our state tracking logic in ResumeThread(), // since normally, // we should not be receiving any ptrace events while the inferior is // stopped. This // makes sure that the inferior is resumed and exits normally. state = eStateRunning; } ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER); break; } case 0: case TRAP_TRACE: // We receive this on single stepping. case TRAP_HWBKPT: // We receive this on watchpoint hit { // If a watchpoint was hit, report it uint32_t wp_index; Status error = thread.GetRegisterContext()->GetWatchpointHitIndex( wp_index, (uintptr_t)info.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, error = {1}", thread.GetID(), error); if (wp_index != LLDB_INVALID_INDEX32) { MonitorWatchpoint(thread, wp_index); break; } // If a breakpoint was hit, report it uint32_t bp_index; error = thread.GetRegisterContext()->GetHardwareBreakHitIndex( bp_index, (uintptr_t)info.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for hardware " "breakpoint hits, pid = {0}, error = {1}", thread.GetID(), error); if (bp_index != LLDB_INVALID_INDEX32) { MonitorBreakpoint(thread); break; } // Otherwise, report step over MonitorTrace(thread); break; } case SI_KERNEL: #if defined __mips__ // For mips there is no special signal for watchpoint // So we check for watchpoint in kernel trap { // If a watchpoint was hit, report it uint32_t wp_index; Status error = thread.GetRegisterContext()->GetWatchpointHitIndex( wp_index, LLDB_INVALID_ADDRESS); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, error = {1}", thread.GetID(), error); if (wp_index != LLDB_INVALID_INDEX32) { MonitorWatchpoint(thread, wp_index); break; } } // NO BREAK #endif case TRAP_BRKPT: MonitorBreakpoint(thread); break; case SIGTRAP: case (SIGTRAP | 0x80): LLDB_LOG( log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}, resuming", info.si_code, GetID(), thread.GetID()); // Ignore these signals until we know more about them. ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); break; default: LLDB_LOG(log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}", info.si_code, GetID(), thread.GetID()); MonitorSignal(info, thread, false); break; } } void NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); // This thread is currently stopped. thread.SetStoppedByTrace(); StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); // Mark the thread as stopped at breakpoint. thread.SetStoppedByBreakpoint(); Status error = FixupBreakpointPCAsNeeded(thread); if (error.Fail()) LLDB_LOG(log, "pid = {0} fixup: {1}", thread.GetID(), error); if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end()) thread.SetStoppedByTrace(); StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", thread.GetID(), wp_index); // Mark the thread as stopped at watchpoint. // The address is at (lldb::addr_t)info->si_addr if we need it. thread.SetStoppedByWatchpoint(wp_index); // We need to tell all other running threads before we notify the delegate // about this stop. StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited) { const int signo = info.si_signo; const bool is_from_llgs = info.si_pid == getpid(); Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on Linux. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. // Handle the signal. LLDB_LOG(log, "received signal {0} ({1}) with code {2}, (siginfo pid = {3}, " "waitpid pid = {4})", Host::GetSignalAsCString(signo), signo, info.si_code, thread.GetID()); // Check for thread stop notification. if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) { // This is a tgkill()-based stop. LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); // Check that we're not already marked with a stop reason. // Note this thread really shouldn't already be marked as stopped - if we // were, that would imply that the kernel signaled us with the thread // stopping which we handled and marked as stopped, and that, without an // intervening resume, we received another stop. It is more likely that we // are missing the marking of a run state somewhere if we find that the // thread was marked as stopped. const StateType thread_state = thread.GetState(); if (!StateIsStoppedState(thread_state, false)) { // An inferior thread has stopped because of a SIGSTOP we have sent it. // Generally, these are not important stops and we don't want to report // them as they are just used to stop other threads when one thread (the // one with the *real* stop reason) hits a breakpoint (watchpoint, // etc...). However, in the case of an asynchronous Interrupt(), this *is* // the real stop reason, so we leave the signal intact if this is the // thread that was chosen as the triggering thread. if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { if (m_pending_notification_tid == thread.GetID()) thread.SetStoppedBySignal(SIGSTOP, &info); else thread.SetStoppedWithNoReason(); SetCurrentThreadID(thread.GetID()); SignalIfAllThreadsStopped(); } else { // We can end up here if stop was initiated by LLGS but by this time a // thread stop has occurred - maybe initiated by another event. Status error = ResumeThread(thread, thread.GetState(), 0); if (error.Fail()) LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), error); } } else { LLDB_LOG(log, "pid {0} tid {1}, thread was already marked as a stopped " "state (state={2}), leaving stop signal as is", GetID(), thread.GetID(), thread_state); SignalIfAllThreadsStopped(); } // Done handling. return; } // Check if debugger should stop at this signal or just ignore it // and resume the inferior. if (m_signals_to_ignore.find(signo) != m_signals_to_ignore.end()) { ResumeThread(thread, thread.GetState(), signo); return; } // This thread is stopped. LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); thread.SetStoppedBySignal(signo, &info); // Send a stop to the debugger after we get all other threads to stop. StopRunningThreads(thread.GetID()); } namespace { struct EmulatorBaton { NativeProcessLinux *m_process; NativeRegisterContext *m_reg_context; // eRegisterKindDWARF -> RegsiterValue std::unordered_map m_register_values; EmulatorBaton(NativeProcessLinux *process, NativeRegisterContext *reg_context) : m_process(process), m_reg_context(reg_context) {} }; } // anonymous namespace static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, size_t length) { EmulatorBaton *emulator_baton = static_cast(baton); size_t bytes_read; emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); return bytes_read; } static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, const RegisterInfo *reg_info, RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); auto it = emulator_baton->m_register_values.find( reg_info->kinds[eRegisterKindDWARF]); if (it != emulator_baton->m_register_values.end()) { reg_value = it->second; return true; } // The emulator only fill in the dwarf regsiter numbers (and in some case // the generic register numbers). Get the full register info from the // register context based on the dwarf register numbers. const RegisterInfo *full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); Status error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); if (error.Success()) return true; return false; } static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; return true; } static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t length) { return length; } static lldb::addr_t ReadFlags(NativeRegisterContext *regsiter_context) { const RegisterInfo *flags_info = regsiter_context->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS); } Status NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) { Status error; NativeRegisterContextSP register_context_sp = thread.GetRegisterContext(); std::unique_ptr emulator_ap( EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); if (emulator_ap == nullptr) return Status("Instruction emulator not found!"); EmulatorBaton baton(this, register_context_sp.get()); emulator_ap->SetBaton(&baton); emulator_ap->SetReadMemCallback(&ReadMemoryCallback); emulator_ap->SetReadRegCallback(&ReadRegisterCallback); emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); if (!emulator_ap->ReadInstruction()) return Status("Read instruction failed!"); bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *reg_info_flags = register_context_sp->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); lldb::addr_t next_pc; lldb::addr_t next_flags; if (emulation_result) { assert(pc_it != baton.m_register_values.end() && "Emulation was successfull but PC wasn't updated"); next_pc = pc_it->second.GetAsUInt64(); if (flags_it != baton.m_register_values.end()) next_flags = flags_it->second.GetAsUInt64(); else next_flags = ReadFlags(register_context_sp.get()); } else if (pc_it == baton.m_register_values.end()) { // Emulate instruction failed and it haven't changed PC. Advance PC // with the size of the current opcode because the emulation of all // PC modifying instruction should be successful. The failure most // likely caused by a not supported instruction which don't modify PC. next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); next_flags = ReadFlags(register_context_sp.get()); } else { // The instruction emulation failed after it modified the PC. It is an // unknown error where we can't continue because the next instruction is // modifying the PC but we don't know how. return Status("Instruction emulation failed unexpectedly."); } if (m_arch.GetMachine() == llvm::Triple::arm) { if (next_flags & 0x20) { // Thumb mode error = SetSoftwareBreakpoint(next_pc, 2); } else { // Arm mode error = SetSoftwareBreakpoint(next_pc, 4); } } else if (m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) error = SetSoftwareBreakpoint(next_pc, 4); else { // No size hint is given for the next breakpoint error = SetSoftwareBreakpoint(next_pc, 0); } // If setting the breakpoint fails because next_pc is out of // the address space, ignore it and let the debugee segfault. if (error.GetError() == EIO || error.GetError() == EFAULT) { return Status(); } else if (error.Fail()) return error; m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); return Status(); } bool NativeProcessLinux::SupportHardwareSingleStepping() const { if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) return false; return true; } Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); bool software_single_step = !SupportHardwareSingleStepping(); if (software_single_step) { for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) continue; if (action->state == eStateStepping) { Status error = SetupSoftwareSingleStepping( static_cast(*thread_sp)); if (error.Fail()) return error; } } } for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) { LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), thread_sp->GetID()); continue; } LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", action->state, GetID(), thread_sp->GetID()); switch (action->state) { case eStateRunning: case eStateStepping: { // Run the thread, possibly feeding it the signal. const int signo = action->signal; ResumeThread(static_cast(*thread_sp), action->state, signo); break; } case eStateSuspended: case eStateStopped: llvm_unreachable("Unexpected state"); default: return Status("NativeProcessLinux::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread_sp->GetID()); } } return Status(); } Status NativeProcessLinux::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) error.SetErrorToErrno(); return error; } Status NativeProcessLinux::Detach() { Status error; // Stop monitoring the inferior. m_sigchld_handle.reset(); // Tell ptrace to detach from the process. if (GetID() == LLDB_INVALID_PROCESS_ID) return error; for (auto thread_sp : m_threads) { Status e = Detach(thread_sp->GetID()); if (e.Fail()) error = e; // Save the error, but still attempt to detach from other threads. } m_processor_trace_monitor.clear(); m_pt_proces_trace_id = LLDB_INVALID_UID; return error; } Status NativeProcessLinux::Signal(int signo) { Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, Host::GetSignalAsCString(signo), GetID()); if (kill(GetID(), signo)) error.SetErrorToErrno(); return error; } Status NativeProcessLinux::Interrupt() { // Pick a running thread (or if none, a not-dead stopped thread) as // the chosen thread that will be the stop-reason thread. Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); NativeThreadProtocolSP running_thread_sp; NativeThreadProtocolSP stopped_thread_sp; LLDB_LOG(log, "selecting running thread for interrupt target"); for (auto thread_sp : m_threads) { // The thread shouldn't be null but lets just cover that here. if (!thread_sp) continue; // If we have a running or stepping thread, we'll call that the // target of the interrupt. const auto thread_state = thread_sp->GetState(); if (thread_state == eStateRunning || thread_state == eStateStepping) { running_thread_sp = thread_sp; break; } else if (!stopped_thread_sp && StateIsStoppedState(thread_state, true)) { // Remember the first non-dead stopped thread. We'll use that as a backup // if there are no running threads. stopped_thread_sp = thread_sp; } } if (!running_thread_sp && !stopped_thread_sp) { Status error("found no running/stepping or live stopped threads as target " "for interrupt"); LLDB_LOG(log, "skipping due to error: {0}", error); return error; } NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp; LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), running_thread_sp ? "running" : "stopped", deferred_signal_thread_sp->GetID()); StopRunningThreads(deferred_signal_thread_sp->GetID()); return Status(); } Status NativeProcessLinux::Kill() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); Status error; switch (m_state) { case StateType::eStateInvalid: case StateType::eStateExited: case StateType::eStateCrashed: case StateType::eStateDetached: case StateType::eStateUnloaded: // Nothing to do - the process is already dead. LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), m_state); return error; case StateType::eStateConnected: case StateType::eStateAttaching: case StateType::eStateLaunching: case StateType::eStateStopped: case StateType::eStateRunning: case StateType::eStateStepping: case StateType::eStateSuspended: // We can try to kill a process in these states. break; } if (kill(GetID(), SIGKILL) != 0) { error.SetErrorToErrno(); return error; } return error; } static Status ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef &maps_line, MemoryRegionInfo &memory_region_info) { memory_region_info.Clear(); StringExtractor line_extractor(maps_line); // Format: {address_start_hex}-{address_end_hex} perms offset dev inode // pathname // perms: rwxp (letter is present if set, '-' if not, final character is // p=private, s=shared). // Parse out the starting address lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0); // Parse out hyphen separating start and end address from range. if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-')) return Status( "malformed /proc/{pid}/maps entry, missing dash between address range"); // Parse out the ending address lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address); // Parse out the space after the address. if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' ')) return Status( "malformed /proc/{pid}/maps entry, missing space after range"); // Save the range. memory_region_info.GetRange().SetRangeBase(start_address); memory_region_info.GetRange().SetRangeEnd(end_address); // Any memory region in /proc/{pid}/maps is by definition mapped into the // process. memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); // Parse out each permission entry. if (line_extractor.GetBytesLeft() < 4) return Status("malformed /proc/{pid}/maps entry, missing some portion of " "permissions"); // Handle read permission. const char read_perm_char = line_extractor.GetChar(); if (read_perm_char == 'r') memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); else if (read_perm_char == '-') memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps read permission char"); // Handle write permission. const char write_perm_char = line_extractor.GetChar(); if (write_perm_char == 'w') memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); else if (write_perm_char == '-') memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps write permission char"); // Handle execute permission. const char exec_perm_char = line_extractor.GetChar(); if (exec_perm_char == 'x') memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); else if (exec_perm_char == '-') memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps exec permission char"); line_extractor.GetChar(); // Read the private bit line_extractor.SkipSpaces(); // Skip the separator line_extractor.GetHexMaxU64(false, 0); // Read the offset line_extractor.GetHexMaxU64(false, 0); // Read the major device number line_extractor.GetChar(); // Read the device id separator line_extractor.GetHexMaxU64(false, 0); // Read the major device number line_extractor.SkipSpaces(); // Skip the separator line_extractor.GetU64(0, 10); // Read the inode number line_extractor.SkipSpaces(); const char *name = line_extractor.Peek(); if (name) memory_region_info.SetName(name); return Status(); } Status NativeProcessLinux::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { // FIXME review that the final memory region returned extends to the end of // the virtual address space, // with no perms if it is not mapped. // Use an approach that reads memory regions from /proc/{pid}/maps. // Assume proc maps entries are in ascending order. // FIXME assert if we find differently. if (m_supports_mem_region == LazyBool::eLazyBoolNo) { // We're done. return Status("unsupported"); } Status error = PopulateMemoryRegionCache(); if (error.Fail()) { return error; } lldb::addr_t prev_base_address = 0; // FIXME start by finding the last region that is <= target address using // binary search. Data is sorted. // There can be a ton of regions on pthreads apps with lots of threads. for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); ++it) { MemoryRegionInfo &proc_entry_info = it->first; // Sanity check assumption that /proc/{pid}/maps entries are ascending. assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && "descending /proc/pid/maps entries detected, unexpected"); prev_base_address = proc_entry_info.GetRange().GetRangeBase(); UNUSED_IF_ASSERT_DISABLED(prev_base_address); // If the target address comes before this entry, indicate distance to next // region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetByteSize( proc_entry_info.GetRange().GetRangeBase() - load_addr); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } else if (proc_entry_info.GetRange().Contains(load_addr)) { // The target address is within the memory region we're processing here. range_info = proc_entry_info; return error; } // The target memory address comes somewhere after the region we just // parsed. } // If we made it here, we didn't find an entry that contained the given // address. Return the // load_addr as start and the amount of bytes betwwen load address and the end // of the memory as // size. range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } Status NativeProcessLinux::PopulateMemoryRegionCache() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // If our cache is empty, pull the latest. There should always be at least // one memory region if memory region handling is supported. if (!m_mem_region_cache.empty()) { LLDB_LOG(log, "reusing {0} cached memory region entries", m_mem_region_cache.size()); return Status(); } auto BufferOrError = getProcFile(GetID(), "maps"); if (!BufferOrError) { m_supports_mem_region = LazyBool::eLazyBoolNo; return BufferOrError.getError(); } StringRef Rest = BufferOrError.get()->getBuffer(); while (! Rest.empty()) { StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); MemoryRegionInfo info; const Status parse_error = ParseMemoryRegionInfoFromProcMapsLine(Line, info); if (parse_error.Fail()) { LLDB_LOG(log, "failed to parse proc maps line '{0}': {1}", Line, parse_error); m_supports_mem_region = LazyBool::eLazyBoolNo; return parse_error; } m_mem_region_cache.emplace_back( info, FileSpec(info.GetName().GetCString(), true)); } if (m_mem_region_cache.empty()) { // No entries after attempting to read them. This shouldn't happen if // /proc/{pid}/maps is supported. Assume we don't support map entries // via procfs. m_supports_mem_region = LazyBool::eLazyBoolNo; LLDB_LOG(log, "failed to find any procfs maps entries, assuming no support " "for memory region metadata retrieval"); return Status("not supported"); } LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", m_mem_region_cache.size(), GetID()); // We support memory retrieval, remember that. m_supports_mem_region = LazyBool::eLazyBoolYes; return Status(); } void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "newBumpId={0}", newBumpId); LLDB_LOG(log, "clearing {0} entries from memory region cache", m_mem_region_cache.size()); m_mem_region_cache.clear(); } Status NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) { // FIXME implementing this requires the equivalent of // InferiorCallPOSIX::InferiorCallMmap, which depends on // functional ThreadPlans working with Native*Protocol. #if 1 return Status("not implemented yet"); #else addr = LLDB_INVALID_ADDRESS; unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; // TODO implement this directly in NativeProcessLinux // (and lift to NativeProcessPOSIX if/when that class is // refactored out). if (InferiorCallMmap(this, addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { m_addr_to_mmap_size[addr] = size; return Status(); } else { addr = LLDB_INVALID_ADDRESS; return Status("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString(permissions)); } #endif } Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { // FIXME see comments in AllocateMemory - required lower-level // bits not in place yet (ThreadPlans) return Status("not implemented"); } lldb::addr_t NativeProcessLinux::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; } size_t NativeProcessLinux::UpdateThreads() { // The NativeProcessLinux monitoring threads are always up to date // with respect to thread state and they keep the thread list // populated properly. All this method needs to do is return the // thread count. return m_threads.size(); } bool NativeProcessLinux::GetArchitecture(ArchSpec &arch) const { arch = m_arch; return true; } Status NativeProcessLinux::GetSoftwareBreakpointPCOffset( uint32_t &actual_opcode_size) { // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode[] = {0xCC}; static const uint8_t g_s390x_opcode[] = {0x00, 0x01}; switch (m_arch.GetMachine()) { case llvm::Triple::x86: case llvm::Triple::x86_64: actual_opcode_size = static_cast(sizeof(g_i386_opcode)); return Status(); case llvm::Triple::systemz: actual_opcode_size = static_cast(sizeof(g_s390x_opcode)); return Status(); case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::mips: case llvm::Triple::mipsel: // On these architectures the PC don't get updated for breakpoint hits actual_opcode_size = 0; return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { if (hardware) return SetHardwareBreakpoint(addr, size); else return SetSoftwareBreakpoint(addr, size); } Status NativeProcessLinux::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { if (hardware) return RemoveHardwareBreakpoint(addr); else return NativeProcessProtocol::RemoveBreakpoint(addr); } Status NativeProcessLinux::GetSoftwareBreakpointTrapOpcode( size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) { // FIXME put this behind a breakpoint protocol class that can be set per // architecture. Need MIPS support here. static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the // linux kernel does otherwise. static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; static const uint8_t g_i386_opcode[] = {0xCC}; static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d}; static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00}; static const uint8_t g_s390x_opcode[] = {0x00, 0x01}; static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde}; switch (m_arch.GetMachine()) { case llvm::Triple::aarch64: trap_opcode_bytes = g_aarch64_opcode; actual_opcode_size = sizeof(g_aarch64_opcode); return Status(); case llvm::Triple::arm: switch (trap_opcode_size_hint) { case 2: trap_opcode_bytes = g_thumb_breakpoint_opcode; actual_opcode_size = sizeof(g_thumb_breakpoint_opcode); return Status(); case 4: trap_opcode_bytes = g_arm_breakpoint_opcode; actual_opcode_size = sizeof(g_arm_breakpoint_opcode); return Status(); default: assert(false && "Unrecognised trap opcode size hint!"); return Status("Unrecognised trap opcode size hint!"); } case llvm::Triple::x86: case llvm::Triple::x86_64: trap_opcode_bytes = g_i386_opcode; actual_opcode_size = sizeof(g_i386_opcode); return Status(); case llvm::Triple::mips: case llvm::Triple::mips64: trap_opcode_bytes = g_mips64_opcode; actual_opcode_size = sizeof(g_mips64_opcode); return Status(); case llvm::Triple::mipsel: case llvm::Triple::mips64el: trap_opcode_bytes = g_mips64el_opcode; actual_opcode_size = sizeof(g_mips64el_opcode); return Status(); case llvm::Triple::systemz: trap_opcode_bytes = g_s390x_opcode; actual_opcode_size = sizeof(g_s390x_opcode); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGSEGV); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGSEGV"); break; case SI_KERNEL: // Linux will occasionally send spurious SI_KERNEL codes. // (this is poorly documented in sigaction) // One way to get this is via unaligned SIMD loads. reason = ProcessMessage::eInvalidAddress; // for lack of anything better break; case SEGV_MAPERR: reason = ProcessMessage::eInvalidAddress; break; case SEGV_ACCERR: reason = ProcessMessage::ePrivilegedAddress; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGILL); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGILL"); break; case ILL_ILLOPC: reason = ProcessMessage::eIllegalOpcode; break; case ILL_ILLOPN: reason = ProcessMessage::eIllegalOperand; break; case ILL_ILLADR: reason = ProcessMessage::eIllegalAddressingMode; break; case ILL_ILLTRP: reason = ProcessMessage::eIllegalTrap; break; case ILL_PRVOPC: reason = ProcessMessage::ePrivilegedOpcode; break; case ILL_PRVREG: reason = ProcessMessage::ePrivilegedRegister; break; case ILL_COPROC: reason = ProcessMessage::eCoprocessorError; break; case ILL_BADSTK: reason = ProcessMessage::eInternalStackError; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGFPE(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGFPE); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGFPE"); break; case FPE_INTDIV: reason = ProcessMessage::eIntegerDivideByZero; break; case FPE_INTOVF: reason = ProcessMessage::eIntegerOverflow; break; case FPE_FLTDIV: reason = ProcessMessage::eFloatDivideByZero; break; case FPE_FLTOVF: reason = ProcessMessage::eFloatOverflow; break; case FPE_FLTUND: reason = ProcessMessage::eFloatUnderflow; break; case FPE_FLTRES: reason = ProcessMessage::eFloatInexactResult; break; case FPE_FLTINV: reason = ProcessMessage::eFloatInvalidOperation; break; case FPE_FLTSUB: reason = ProcessMessage::eFloatSubscriptRange; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGBUS); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGBUS"); break; case BUS_ADRALN: reason = ProcessMessage::eIllegalAlignment; break; case BUS_ADRERR: reason = ProcessMessage::eIllegalAddress; break; case BUS_OBJERR: reason = ProcessMessage::eHardwareError; break; } return reason; } #endif Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { if (ProcessVmReadvSupported()) { // The process_vm_readv path is about 50 times faster than ptrace api. We // want to use // this syscall if it is supported. const ::pid_t pid = GetID(); struct iovec local_iov, remote_iov; local_iov.iov_base = buf; local_iov.iov_len = size; remote_iov.iov_base = reinterpret_cast(addr); remote_iov.iov_len = size; bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); const bool success = bytes_read == size; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "using process_vm_readv to read {0} bytes from inferior " "address {1:x}: {2}", size, addr, success ? "Success" : llvm::sys::StrError(errno)); if (success) return Status(); // else the call failed for some reason, let's retry the read using ptrace // api. } unsigned char *dst = static_cast(buf); size_t remainder; long data; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { Status error = NativeProcessLinux::PtraceWrapper( PTRACE_PEEKDATA, GetID(), (void *)addr, nullptr, 0, &data); if (error.Fail()) return error; remainder = size - bytes_read; remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; // Copy the data into our buffer memcpy(dst, &data, remainder); LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); addr += k_ptrace_word_size; dst += k_ptrace_word_size; } return Status(); } Status NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { Status error = ReadMemory(addr, buf, size, bytes_read); if (error.Fail()) return error; return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); } Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) { const unsigned char *src = static_cast(buf); size_t remainder; Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); for (bytes_written = 0; bytes_written < size; bytes_written += remainder) { remainder = size - bytes_written; remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; if (remainder == k_ptrace_word_size) { unsigned long data = 0; memcpy(&data, src, k_ptrace_word_size); LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void *)addr, (void *)data); if (error.Fail()) return error; } else { unsigned char buff[8]; size_t bytes_read; error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read); if (error.Fail()) return error; memcpy(buff, src, remainder); size_t bytes_written_rec; error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec); if (error.Fail()) return error; LLDB_LOG(log, "[{0:x}]:{1:x} ({2:x})", addr, *(const unsigned long *)src, *(unsigned long *)buff); } addr += k_ptrace_word_size; src += k_ptrace_word_size; } return error; } Status NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); } Status NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) { return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); } Status NativeProcessLinux::Detach(lldb::tid_t tid) { if (tid == LLDB_INVALID_THREAD_ID) return Status(); return PtraceWrapper(PTRACE_DETACH, tid); } bool NativeProcessLinux::HasThreadNoLock(lldb::tid_t thread_id) { for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); if (thread_sp->GetID() == thread_id) { // We have this thread. return true; } } // We don't have this thread. return false; } bool NativeProcessLinux::StopTrackingThread(lldb::tid_t thread_id) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0})", thread_id); bool found = false; for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { if (*it && ((*it)->GetID() == thread_id)) { m_threads.erase(it); found = true; break; } } if (found) StopTracingForThread(thread_id); SignalIfAllThreadsStopped(); return found; } NativeThreadLinuxSP NativeProcessLinux::AddThread(lldb::tid_t thread_id) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); assert(!HasThreadNoLock(thread_id) && "attempted to add a thread by id that already exists"); // If this is the first thread, save it as the current thread if (m_threads.empty()) SetCurrentThreadID(thread_id); - auto thread_sp = std::make_shared(this, thread_id); + auto thread_sp = std::make_shared(*this, thread_id); m_threads.push_back(thread_sp); if (m_pt_proces_trace_id != LLDB_INVALID_UID) { auto traceMonitor = ProcessorTraceMonitor::Create( GetID(), thread_id, m_pt_process_trace_config, true); if (traceMonitor) { m_pt_traced_thread_group.insert(thread_id); m_processor_trace_monitor.insert( std::make_pair(thread_id, std::move(*traceMonitor))); } else { LLDB_LOG(log, "failed to start trace on thread {0}", thread_id); Status error(traceMonitor.takeError()); LLDB_LOG(log, "error {0}", error); } } return thread_sp; } Status NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); Status error; // Find out the size of a breakpoint (might depend on where we are in the // code). NativeRegisterContextSP context_sp = thread.GetRegisterContext(); if (!context_sp) { error.SetErrorString("cannot get a NativeRegisterContext for the thread"); LLDB_LOG(log, "failed: {0}", error); return error; } uint32_t breakpoint_size = 0; error = GetSoftwareBreakpointPCOffset(breakpoint_size); if (error.Fail()) { LLDB_LOG(log, "GetBreakpointSize() failed: {0}", error); return error; } else LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); // First try probing for a breakpoint at a software breakpoint location: PC - // breakpoint size. const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation(); lldb::addr_t breakpoint_addr = initial_pc_addr; if (breakpoint_size > 0) { // Do not allow breakpoint probe to wrap around. if (breakpoint_addr >= breakpoint_size) breakpoint_addr -= breakpoint_size; } // Check if we stopped because of a breakpoint. NativeBreakpointSP breakpoint_sp; error = m_breakpoint_list.GetBreakpoint(breakpoint_addr, breakpoint_sp); if (!error.Success() || !breakpoint_sp) { // We didn't find one at a software probe location. Nothing to do. LLDB_LOG(log, "pid {0} no lldb breakpoint found at current pc with " "adjustment: {1}", GetID(), breakpoint_addr); return Status(); } // If the breakpoint is not a software breakpoint, nothing to do. if (!breakpoint_sp->IsSoftwareBreakpoint()) { LLDB_LOG( log, "pid {0} breakpoint found at {1:x}, not software, nothing to adjust", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // Change the program counter. LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); error = context_sp->SetPC(breakpoint_addr); if (error.Fail()) { LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(), thread.GetID(), error); return error; } return error; } Status NativeProcessLinux::GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) { Status error = PopulateMemoryRegionCache(); if (error.Fail()) return error; FileSpec module_file_spec(module_path, true); file_spec.Clear(); for (const auto &it : m_mem_region_cache) { if (it.second.GetFilename() == module_file_spec.GetFilename()) { file_spec = it.second; return Status(); } } return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", module_file_spec.GetFilename().AsCString(), GetID()); } Status NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) { load_addr = LLDB_INVALID_ADDRESS; Status error = PopulateMemoryRegionCache(); if (error.Fail()) return error; FileSpec file(file_name, false); for (const auto &it : m_mem_region_cache) { if (it.second == file) { load_addr = it.first.GetRange().GetRangeBase(); return Status(); } } return Status("No load address found for specified file."); } NativeThreadLinuxSP NativeProcessLinux::GetThreadByID(lldb::tid_t tid) { return std::static_pointer_cast( NativeProcessProtocol::GetThreadByID(tid)); } Status NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0}", thread.GetID()); // Before we do the resume below, first check if we have a pending // stop notification that is currently waiting for // all threads to stop. This is potentially a buggy situation since // we're ostensibly waiting for threads to stop before we send out the // pending notification, and here we are resuming one before we send // out the pending stop notification. if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { LLDB_LOG(log, "about to resume tid {0} per explicit request but we have a " "pending stop notification (tid {1}) that is actively " "waiting for this thread to stop. Valid sequence of events?", thread.GetID(), m_pending_notification_tid); } // Request a resume. We expect this to be synchronous and the system // to reflect it is running after this completes. switch (state) { case eStateRunning: { const auto resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { const auto step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; } default: LLDB_LOG(log, "Unhandled state {0}.", state); llvm_unreachable("Unhandled state for resume"); } } //===----------------------------------------------------------------------===// void NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "about to process event: (triggering_tid: {0})", triggering_tid); m_pending_notification_tid = triggering_tid; // Request a stop for all the thread stops that need to be stopped // and are not already known to be stopped. for (const auto &thread_sp : m_threads) { if (StateIsRunningState(thread_sp->GetState())) static_pointer_cast(thread_sp)->RequestStop(); } SignalIfAllThreadsStopped(); LLDB_LOG(log, "event processing done"); } void NativeProcessLinux::SignalIfAllThreadsStopped() { if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) return; // No pending notification. Nothing to do. for (const auto &thread_sp : m_threads) { if (StateIsRunningState(thread_sp->GetState())) return; // Some threads are still running. Don't signal yet. } // We have a pending notification and all threads have stopped. Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); // Clear any temporary breakpoints we used to implement software single // stepping. for (const auto &thread_info : m_threads_stepping_with_breakpoint) { Status error = RemoveBreakpoint(thread_info.second); if (error.Fail()) LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", thread_info.first, error); } m_threads_stepping_with_breakpoint.clear(); // Notify the delegate about the stop SetCurrentThreadID(m_pending_notification_tid); SetState(StateType::eStateStopped, true); m_pending_notification_tid = LLDB_INVALID_THREAD_ID; } void NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0}", thread.GetID()); if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && StateIsRunningState(thread.GetState())) { // We will need to wait for this new thread to stop as well before firing // the // notification. thread.RequestStop(); } } void NativeProcessLinux::SigchldHandler() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Process all pending waitpid notifications. while (true) { int status = -1; ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, -1, &status, __WALL | __WNOTHREAD | WNOHANG); if (wait_pid == 0) break; // We are done. if (wait_pid == -1) { Status error(errno, eErrorTypePOSIX); LLDB_LOG(log, "waitpid (-1, &status, _) failed: {0}", error); break; } WaitStatus wait_status = WaitStatus::Decode(status); bool exited = wait_status.type == WaitStatus::Exit || (wait_status.type == WaitStatus::Signal && wait_pid == static_cast<::pid_t>(GetID())); LLDB_LOG( log, "waitpid (-1, &status, _) => pid = {0}, status = {1}, exited = {2}", wait_pid, wait_status, exited); MonitorCallback(wait_pid, exited, wait_status); } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 can be a valid result (i.e. // for PTRACE_PEEK*) Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result) { Status error; long int ret; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); PtraceDisplayBytes(req, data, data_size); errno = 0; if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), *(unsigned int *)addr, data); else ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), addr, data); if (ret == -1) error.SetErrorToErrno(); if (result) *result = ret; LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, data_size, ret); PtraceDisplayBytes(req, data, data_size); if (error.Fail()) LLDB_LOG(log, "ptrace() failed: {0}", error); return error; } llvm::Expected NativeProcessLinux::LookupProcessorTraceInstance(lldb::user_id_t traceid, lldb::tid_t thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (thread == LLDB_INVALID_THREAD_ID && traceid == m_pt_proces_trace_id) { LLDB_LOG(log, "thread not specified: {0}", traceid); return Status("tracing not active thread not specified").ToError(); } for (auto& iter : m_processor_trace_monitor) { if (traceid == iter.second->GetTraceID() && (thread == iter.first || thread == LLDB_INVALID_THREAD_ID)) return *(iter.second); } LLDB_LOG(log, "traceid not being traced: {0}", traceid); return Status("tracing not active for this thread").ToError(); } Status NativeProcessLinux::GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset) { TraceOptions trace_options; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); Status error; LLDB_LOG(log, "traceid {0}", traceid); auto perf_monitor = LookupProcessorTraceInstance(traceid, thread); if (!perf_monitor) { LLDB_LOG(log, "traceid not being traced: {0}", traceid); buffer = buffer.slice(buffer.size()); error = perf_monitor.takeError(); return error; } return (*perf_monitor).ReadPerfTraceData(buffer, offset); } Status NativeProcessLinux::GetData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); Status error; LLDB_LOG(log, "traceid {0}", traceid); auto perf_monitor = LookupProcessorTraceInstance(traceid, thread); if (!perf_monitor) { LLDB_LOG(log, "traceid not being traced: {0}", traceid); buffer = buffer.slice(buffer.size()); error = perf_monitor.takeError(); return error; } return (*perf_monitor).ReadPerfTraceAux(buffer, offset); } Status NativeProcessLinux::GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) { Status error; if (config.getThreadID() == LLDB_INVALID_THREAD_ID && m_pt_proces_trace_id == traceid) { if (m_pt_proces_trace_id == LLDB_INVALID_UID) { error.SetErrorString("tracing not active for this process"); return error; } config = m_pt_process_trace_config; } else { auto perf_monitor = LookupProcessorTraceInstance(traceid, config.getThreadID()); if (!perf_monitor) { error = perf_monitor.takeError(); return error; } error = (*perf_monitor).GetTraceConfig(config); } return error; } lldb::user_id_t NativeProcessLinux::StartTraceGroup(const TraceOptions &config, Status &error) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (config.getType() != TraceType::eTraceTypeProcessorTrace) return LLDB_INVALID_UID; if (m_pt_proces_trace_id != LLDB_INVALID_UID) { error.SetErrorString("tracing already active on this process"); return m_pt_proces_trace_id; } for (const auto &thread_sp : m_threads) { if (auto traceInstance = ProcessorTraceMonitor::Create( GetID(), thread_sp->GetID(), config, true)) { m_pt_traced_thread_group.insert(thread_sp->GetID()); m_processor_trace_monitor.insert( std::make_pair(thread_sp->GetID(), std::move(*traceInstance))); } } m_pt_process_trace_config = config; error = ProcessorTraceMonitor::GetCPUType(m_pt_process_trace_config); // Trace on Complete process will have traceid of 0 m_pt_proces_trace_id = 0; LLDB_LOG(log, "Process Trace ID {0}", m_pt_proces_trace_id); return m_pt_proces_trace_id; } lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config, Status &error) { if (config.getType() != TraceType::eTraceTypeProcessorTrace) return NativeProcessProtocol::StartTrace(config, error); Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); lldb::tid_t threadid = config.getThreadID(); if (threadid == LLDB_INVALID_THREAD_ID) return StartTraceGroup(config, error); auto thread_sp = GetThreadByID(threadid); if (!thread_sp) { // Thread not tracked by lldb so don't trace. error.SetErrorString("invalid thread id"); return LLDB_INVALID_UID; } const auto &iter = m_processor_trace_monitor.find(threadid); if (iter != m_processor_trace_monitor.end()) { LLDB_LOG(log, "Thread already being traced"); error.SetErrorString("tracing already active on this thread"); return LLDB_INVALID_UID; } auto traceMonitor = ProcessorTraceMonitor::Create(GetID(), threadid, config, false); if (!traceMonitor) { error = traceMonitor.takeError(); LLDB_LOG(log, "error {0}", error); return LLDB_INVALID_UID; } lldb::user_id_t ret_trace_id = (*traceMonitor)->GetTraceID(); m_processor_trace_monitor.insert( std::make_pair(threadid, std::move(*traceMonitor))); return ret_trace_id; } Status NativeProcessLinux::StopTracingForThread(lldb::tid_t thread) { Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); LLDB_LOG(log, "Thread {0}", thread); const auto& iter = m_processor_trace_monitor.find(thread); if (iter == m_processor_trace_monitor.end()) { error.SetErrorString("tracing not active for this thread"); return error; } if (iter->second->GetTraceID() == m_pt_proces_trace_id) { // traceid maps to the whole process so we have to erase it from the // thread group. LLDB_LOG(log, "traceid maps to process"); m_pt_traced_thread_group.erase(thread); } m_processor_trace_monitor.erase(iter); return error; } Status NativeProcessLinux::StopTrace(lldb::user_id_t traceid, lldb::tid_t thread) { Status error; TraceOptions trace_options; trace_options.setThreadID(thread); error = NativeProcessLinux::GetTraceConfig(traceid, trace_options); if (error.Fail()) return error; switch (trace_options.getType()) { case lldb::TraceType::eTraceTypeProcessorTrace: if (traceid == m_pt_proces_trace_id && thread == LLDB_INVALID_THREAD_ID) StopProcessorTracingOnProcess(); else error = StopProcessorTracingOnThread(traceid, thread); break; default: error.SetErrorString("trace not supported"); break; } return error; } void NativeProcessLinux::StopProcessorTracingOnProcess() { for (auto thread_id_iter : m_pt_traced_thread_group) m_processor_trace_monitor.erase(thread_id_iter); m_pt_traced_thread_group.clear(); m_pt_proces_trace_id = LLDB_INVALID_UID; } Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid, lldb::tid_t thread) { Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (thread == LLDB_INVALID_THREAD_ID) { for (auto& iter : m_processor_trace_monitor) { if (iter.second->GetTraceID() == traceid) { // Stopping a trace instance for an individual thread // hence there will only be one traceid that can match. m_processor_trace_monitor.erase(iter.first); return error; } LLDB_LOG(log, "Trace ID {0}", iter.second->GetTraceID()); } LLDB_LOG(log, "Invalid TraceID"); error.SetErrorString("invalid trace id"); return error; } // thread is specified so we can use find function on the map. const auto& iter = m_processor_trace_monitor.find(thread); if (iter == m_processor_trace_monitor.end()) { // thread not found in our map. LLDB_LOG(log, "thread not being traced"); error.SetErrorString("tracing not active for this thread"); return error; } if (iter->second->GetTraceID() != traceid) { // traceid did not match so it has to be invalid. LLDB_LOG(log, "Invalid TraceID"); error.SetErrorString("invalid trace id"); return error; } LLDB_LOG(log, "UID - {0} , Thread -{1}", traceid, thread); if (traceid == m_pt_proces_trace_id) { // traceid maps to the whole process so we have to erase it from the // thread group. LLDB_LOG(log, "traceid maps to process"); m_pt_traced_thread_group.erase(thread); } m_processor_trace_monitor.erase(iter); return error; } Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.h (revision 321194) @@ -1,289 +1,288 @@ //===-- NativeProcessLinux.h ---------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeProcessLinux_H_ #define liblldb_NativeProcessLinux_H_ -// C++ Includes +#include #include // Other libraries and framework includes #include "lldb/Core/ArchSpec.h" #include "lldb/Host/Debug.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/linux/Support.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/FileSpec.h" #include "lldb/lldb-types.h" #include "NativeThreadLinux.h" #include "ProcessorTrace.h" #include "lldb/Host/common/NativeProcessProtocol.h" namespace lldb_private { class Status; class Scalar; namespace process_linux { /// @class NativeProcessLinux /// @brief Manages communication with the inferior (debugee) process. /// /// Upon construction, this class prepares and launches an inferior process for /// debugging. /// /// Changes in the inferior process state are broadcasted. class NativeProcessLinux : public NativeProcessProtocol { public: class Factory : public NativeProcessProtocol::Factory { public: - llvm::Expected + llvm::Expected> Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop) const override; - llvm::Expected + llvm::Expected> Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop) const override; }; // --------------------------------------------------------------------- // NativeProcessProtocol Interface // --------------------------------------------------------------------- Status Resume(const ResumeActionList &resume_actions) override; Status Halt() override; Status Detach() override; Status Signal(int signo) override; Status Interrupt() override; Status Kill() override; Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) override; Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override; Status DeallocateMemory(lldb::addr_t addr) override; lldb::addr_t GetSharedLibraryInfoAddress() override; size_t UpdateThreads() override; bool GetArchitecture(ArchSpec &arch) const override; Status SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) override; Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; void DoStopIDBumped(uint32_t newBumpId) override; Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) override; Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) override; NativeThreadLinuxSP GetThreadByID(lldb::tid_t id); llvm::ErrorOr> GetAuxvData() const override { return getProcFile(GetID(), "auxv"); } lldb::user_id_t StartTrace(const TraceOptions &config, Status &error) override; Status StopTrace(lldb::user_id_t traceid, lldb::tid_t thread) override; Status GetData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset = 0) override; Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread, llvm::MutableArrayRef &buffer, size_t offset = 0) override; Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) override; // --------------------------------------------------------------------- // Interface used by NativeRegisterContext-derived classes. // --------------------------------------------------------------------- static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, void *data = nullptr, size_t data_size = 0, long *result = nullptr); bool SupportHardwareSingleStepping() const; protected: // --------------------------------------------------------------------- // NativeProcessProtocol protected interface // --------------------------------------------------------------------- Status GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override; private: MainLoop::SignalHandleUP m_sigchld_handle; ArchSpec m_arch; LazyBool m_supports_mem_region = eLazyBoolCalculate; std::vector> m_mem_region_cache; lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID; // List of thread ids stepping with a breakpoint with the address of // the relevan breakpoint std::map m_threads_stepping_with_breakpoint; // --------------------------------------------------------------------- // Private Instance Methods // --------------------------------------------------------------------- NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate, - const ArchSpec &arch, MainLoop &mainloop); + const ArchSpec &arch, MainLoop &mainloop, + llvm::ArrayRef<::pid_t> tids); // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); static Status SetDefaultPtraceOpts(const lldb::pid_t); - - void InitializeThreads(llvm::ArrayRef<::pid_t> tids); void MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status); void WaitForNewThread(::pid_t tid); void MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread); void MonitorTrace(NativeThreadLinux &thread); void MonitorBreakpoint(NativeThreadLinux &thread); void MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index); void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); Status SetupSoftwareSingleStepping(NativeThreadLinux &thread); #if 0 static ::ProcessMessage::CrashReason GetCrashReasonForSIGSEGV(const siginfo_t *info); static ::ProcessMessage::CrashReason GetCrashReasonForSIGILL(const siginfo_t *info); static ::ProcessMessage::CrashReason GetCrashReasonForSIGFPE(const siginfo_t *info); static ::ProcessMessage::CrashReason GetCrashReasonForSIGBUS(const siginfo_t *info); #endif bool HasThreadNoLock(lldb::tid_t thread_id); bool StopTrackingThread(lldb::tid_t thread_id); NativeThreadLinuxSP AddThread(lldb::tid_t thread_id); Status GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size); Status FixupBreakpointPCAsNeeded(NativeThreadLinux &thread); /// Writes a siginfo_t structure corresponding to the given thread ID to the /// memory region pointed to by @p siginfo. Status GetSignalInfo(lldb::tid_t tid, void *siginfo); /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) /// corresponding to the given thread ID to the memory pointed to by @p /// message. Status GetEventMessage(lldb::tid_t tid, unsigned long *message); void NotifyThreadDeath(lldb::tid_t tid); Status Detach(lldb::tid_t tid); // This method is requests a stop on all threads which are still running. It // sets up a // deferred delegate notification, which will fire once threads report as // stopped. The // triggerring_tid will be set as the current thread (main stop reason). void StopRunningThreads(lldb::tid_t triggering_tid); // Notify the delegate if all threads have stopped. void SignalIfAllThreadsStopped(); // Resume the given thread, optionally passing it the given signal. The type // of resume // operation (continue, single-step) depends on the state parameter. Status ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo); void ThreadWasCreated(NativeThreadLinux &thread); void SigchldHandler(); Status PopulateMemoryRegionCache(); lldb::user_id_t StartTraceGroup(const TraceOptions &config, Status &error); // This function is intended to be used to stop tracing // on a thread that exited. Status StopTracingForThread(lldb::tid_t thread); // The below function as the name suggests, looks up a ProcessorTrace // instance from the m_processor_trace_monitor map. In the case of // process tracing where the traceid passed would map to the complete // process, it is mandatory to provide a threadid to obtain a trace // instance (since ProcessorTrace is tied to a thread). In the other // scenario that an individual thread is being traced, just the traceid // is sufficient to obtain the actual ProcessorTrace instance. llvm::Expected LookupProcessorTraceInstance(lldb::user_id_t traceid, lldb::tid_t thread); // Stops tracing on individual threads being traced. Not intended // to be used to stop tracing on complete process. Status StopProcessorTracingOnThread(lldb::user_id_t traceid, lldb::tid_t thread); // Intended to stop tracing on complete process. // Should not be used for stopping trace on // individual threads. void StopProcessorTracingOnProcess(); llvm::DenseMap m_processor_trace_monitor; // Set for tracking threads being traced under // same process user id. llvm::DenseSet m_pt_traced_thread_group; lldb::user_id_t m_pt_proces_trace_id = LLDB_INVALID_UID; TraceOptions m_pt_process_trace_config; }; } // namespace process_linux } // namespace lldb_private #endif // #ifndef liblldb_NativeProcessLinux_H_ Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp (revision 321194) @@ -1,208 +1,204 @@ //===-- NativeRegisterContextLinux.cpp --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeRegisterContextLinux.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" #include "lldb/Host/linux/Ptrace.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" using namespace lldb_private; using namespace lldb_private::process_linux; NativeRegisterContextLinux::NativeRegisterContextLinux( NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx, RegisterInfoInterface *reg_info_interface_p) : NativeRegisterContextRegisterInfo(native_thread, concrete_frame_idx, reg_info_interface_p) {} lldb::ByteOrder NativeRegisterContextLinux::GetByteOrder() const { // Get the target process whose privileged thread was used for the register // read. lldb::ByteOrder byte_order = lldb::eByteOrderInvalid; - NativeProcessProtocolSP process_sp(m_thread.GetProcess()); - if (!process_sp) - return byte_order; - - if (!process_sp->GetByteOrder(byte_order)) { + if (!m_thread.GetProcess().GetByteOrder(byte_order)) { // FIXME log here } return byte_order; } Status NativeRegisterContextLinux::ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value) { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) return Status("register %" PRIu32 " not found", reg_index); return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value); } Status NativeRegisterContextLinux::WriteRegisterRaw(uint32_t reg_index, const RegisterValue ®_value) { uint32_t reg_to_write = reg_index; RegisterValue value_to_write = reg_value; // Check if this is a subregister of a full register. const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) { Status error; RegisterValue full_value; uint32_t full_reg = reg_info->invalidate_regs[0]; const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); // Read the full register. error = ReadRegister(full_reg_info, full_value); if (error.Fail()) return error; lldb::ByteOrder byte_order = GetByteOrder(); uint8_t dst[RegisterValue::kMaxRegisterByteSize]; // Get the bytes for the full register. const uint32_t dest_size = full_value.GetAsMemoryData( full_reg_info, dst, sizeof(dst), byte_order, error); if (error.Success() && dest_size) { uint8_t src[RegisterValue::kMaxRegisterByteSize]; // Get the bytes for the source data. const uint32_t src_size = reg_value.GetAsMemoryData( reg_info, src, sizeof(src), byte_order, error); if (error.Success() && src_size && (src_size < dest_size)) { // Copy the src bytes to the destination. memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size); // Set this full register as the value to write. value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); value_to_write.SetType(full_reg_info); reg_to_write = full_reg; } } } const RegisterInfo *const register_to_write_info_p = GetRegisterInfoAtIndex(reg_to_write); assert(register_to_write_info_p && "register to write does not have valid RegisterInfo"); if (!register_to_write_info_p) return Status("NativeRegisterContextLinux::%s failed to get RegisterInfo " "for write register index %" PRIu32, __FUNCTION__, reg_to_write); return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value); } Status NativeRegisterContextLinux::ReadGPR() { void *buf = GetGPRBuffer(); if (!buf) return Status("GPR buffer is NULL"); size_t buf_size = GetGPRSize(); return DoReadGPR(buf, buf_size); } Status NativeRegisterContextLinux::WriteGPR() { void *buf = GetGPRBuffer(); if (!buf) return Status("GPR buffer is NULL"); size_t buf_size = GetGPRSize(); return DoWriteGPR(buf, buf_size); } Status NativeRegisterContextLinux::ReadFPR() { void *buf = GetFPRBuffer(); if (!buf) return Status("FPR buffer is NULL"); size_t buf_size = GetFPRSize(); return DoReadFPR(buf, buf_size); } Status NativeRegisterContextLinux::WriteFPR() { void *buf = GetFPRBuffer(); if (!buf) return Status("FPR buffer is NULL"); size_t buf_size = GetFPRSize(); return DoWriteFPR(buf, buf_size); } Status NativeRegisterContextLinux::ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset) { return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), static_cast(®set), buf, buf_size); } Status NativeRegisterContextLinux::WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset) { return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), static_cast(®set), buf, buf_size); } Status NativeRegisterContextLinux::DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_REGISTERS)); long data; Status error = NativeProcessLinux::PtraceWrapper( PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast(offset), nullptr, 0, &data); if (error.Success()) // First cast to an unsigned of the same size to avoid sign extension. value.SetUInt(static_cast(data), size); LLDB_LOG(log, "{0}: {1:x}", reg_name, data); return error; } Status NativeRegisterContextLinux::DoWriteRegisterValue( uint32_t offset, const char *reg_name, const RegisterValue &value) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_REGISTERS)); void *buf = reinterpret_cast(value.GetAsUInt64()); LLDB_LOG(log, "{0}: {1}", reg_name, buf); return NativeProcessLinux::PtraceWrapper( PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast(offset), buf); } Status NativeRegisterContextLinux::DoReadGPR(void *buf, size_t buf_size) { return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), nullptr, buf, buf_size); } Status NativeRegisterContextLinux::DoWriteGPR(void *buf, size_t buf_size) { return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), nullptr, buf, buf_size); } Status NativeRegisterContextLinux::DoReadFPR(void *buf, size_t buf_size) { return NativeProcessLinux::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); } Status NativeRegisterContextLinux::DoWriteFPR(void *buf, size_t buf_size) { return NativeProcessLinux::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); } Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp (revision 321194) @@ -1,999 +1,999 @@ //===-- NativeRegisterContextLinux_arm64.cpp --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(__arm64__) || defined(__aarch64__) #include "NativeRegisterContextLinux_arm.h" #include "NativeRegisterContextLinux_arm64.h" // C Includes // C++ Includes // Other libraries and framework includes #include "lldb/Core/RegisterValue.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" #include "Plugins/Process/Linux/Procfs.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" // System includes - They have to be included after framework includes because // they define some // macros which collide with variable names in other modules #include // NT_PRSTATUS and NT_FPREGSET definition #include // user_hwdebug_state definition #include #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; // ARM64 general purpose registers. static const uint32_t g_gpr_regnums_arm64[] = { gpr_x0_arm64, gpr_x1_arm64, gpr_x2_arm64, gpr_x3_arm64, gpr_x4_arm64, gpr_x5_arm64, gpr_x6_arm64, gpr_x7_arm64, gpr_x8_arm64, gpr_x9_arm64, gpr_x10_arm64, gpr_x11_arm64, gpr_x12_arm64, gpr_x13_arm64, gpr_x14_arm64, gpr_x15_arm64, gpr_x16_arm64, gpr_x17_arm64, gpr_x18_arm64, gpr_x19_arm64, gpr_x20_arm64, gpr_x21_arm64, gpr_x22_arm64, gpr_x23_arm64, gpr_x24_arm64, gpr_x25_arm64, gpr_x26_arm64, gpr_x27_arm64, gpr_x28_arm64, gpr_fp_arm64, gpr_lr_arm64, gpr_sp_arm64, gpr_pc_arm64, gpr_cpsr_arm64, gpr_w0_arm64, gpr_w1_arm64, gpr_w2_arm64, gpr_w3_arm64, gpr_w4_arm64, gpr_w5_arm64, gpr_w6_arm64, gpr_w7_arm64, gpr_w8_arm64, gpr_w9_arm64, gpr_w10_arm64, gpr_w11_arm64, gpr_w12_arm64, gpr_w13_arm64, gpr_w14_arm64, gpr_w15_arm64, gpr_w16_arm64, gpr_w17_arm64, gpr_w18_arm64, gpr_w19_arm64, gpr_w20_arm64, gpr_w21_arm64, gpr_w22_arm64, gpr_w23_arm64, gpr_w24_arm64, gpr_w25_arm64, gpr_w26_arm64, gpr_w27_arm64, gpr_w28_arm64, LLDB_INVALID_REGNUM // register sets need to end with this flag }; static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - 1) == k_num_gpr_registers_arm64, "g_gpr_regnums_arm64 has wrong number of register infos"); // ARM64 floating point registers. static const uint32_t g_fpu_regnums_arm64[] = { fpu_v0_arm64, fpu_v1_arm64, fpu_v2_arm64, fpu_v3_arm64, fpu_v4_arm64, fpu_v5_arm64, fpu_v6_arm64, fpu_v7_arm64, fpu_v8_arm64, fpu_v9_arm64, fpu_v10_arm64, fpu_v11_arm64, fpu_v12_arm64, fpu_v13_arm64, fpu_v14_arm64, fpu_v15_arm64, fpu_v16_arm64, fpu_v17_arm64, fpu_v18_arm64, fpu_v19_arm64, fpu_v20_arm64, fpu_v21_arm64, fpu_v22_arm64, fpu_v23_arm64, fpu_v24_arm64, fpu_v25_arm64, fpu_v26_arm64, fpu_v27_arm64, fpu_v28_arm64, fpu_v29_arm64, fpu_v30_arm64, fpu_v31_arm64, fpu_s0_arm64, fpu_s1_arm64, fpu_s2_arm64, fpu_s3_arm64, fpu_s4_arm64, fpu_s5_arm64, fpu_s6_arm64, fpu_s7_arm64, fpu_s8_arm64, fpu_s9_arm64, fpu_s10_arm64, fpu_s11_arm64, fpu_s12_arm64, fpu_s13_arm64, fpu_s14_arm64, fpu_s15_arm64, fpu_s16_arm64, fpu_s17_arm64, fpu_s18_arm64, fpu_s19_arm64, fpu_s20_arm64, fpu_s21_arm64, fpu_s22_arm64, fpu_s23_arm64, fpu_s24_arm64, fpu_s25_arm64, fpu_s26_arm64, fpu_s27_arm64, fpu_s28_arm64, fpu_s29_arm64, fpu_s30_arm64, fpu_s31_arm64, fpu_d0_arm64, fpu_d1_arm64, fpu_d2_arm64, fpu_d3_arm64, fpu_d4_arm64, fpu_d5_arm64, fpu_d6_arm64, fpu_d7_arm64, fpu_d8_arm64, fpu_d9_arm64, fpu_d10_arm64, fpu_d11_arm64, fpu_d12_arm64, fpu_d13_arm64, fpu_d14_arm64, fpu_d15_arm64, fpu_d16_arm64, fpu_d17_arm64, fpu_d18_arm64, fpu_d19_arm64, fpu_d20_arm64, fpu_d21_arm64, fpu_d22_arm64, fpu_d23_arm64, fpu_d24_arm64, fpu_d25_arm64, fpu_d26_arm64, fpu_d27_arm64, fpu_d28_arm64, fpu_d29_arm64, fpu_d30_arm64, fpu_d31_arm64, fpu_fpsr_arm64, fpu_fpcr_arm64, LLDB_INVALID_REGNUM // register sets need to end with this flag }; static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) - 1) == k_num_fpr_registers_arm64, "g_fpu_regnums_arm64 has wrong number of register infos"); namespace { // Number of register sets provided by this context. enum { k_num_register_sets = 2 }; } // Register sets for ARM64. static const RegisterSet g_reg_sets_arm64[k_num_register_sets] = { {"General Purpose Registers", "gpr", k_num_gpr_registers_arm64, g_gpr_regnums_arm64}, {"Floating Point Registers", "fpu", k_num_fpr_registers_arm64, g_fpu_regnums_arm64}}; NativeRegisterContextLinux * NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx) { switch (target_arch.GetMachine()) { case llvm::Triple::arm: return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx); case llvm::Triple::aarch64: return new NativeRegisterContextLinux_arm64(target_arch, native_thread, concrete_frame_idx); default: llvm_unreachable("have no register context for architecture"); } } NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx) : NativeRegisterContextLinux(native_thread, concrete_frame_idx, new RegisterInfoPOSIX_arm64(target_arch)) { switch (target_arch.GetMachine()) { case llvm::Triple::aarch64: m_reg_info.num_registers = k_num_registers_arm64; m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64; m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64; m_reg_info.last_gpr = k_last_gpr_arm64; m_reg_info.first_fpr = k_first_fpr_arm64; m_reg_info.last_fpr = k_last_fpr_arm64; m_reg_info.first_fpr_v = fpu_v0_arm64; m_reg_info.last_fpr_v = fpu_v31_arm64; m_reg_info.gpr_flags = gpr_cpsr_arm64; break; default: llvm_unreachable("Unhandled target architecture."); break; } ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; m_max_hbp_supported = 16; m_refresh_hwdebug_info = true; } uint32_t NativeRegisterContextLinux_arm64::GetRegisterSetCount() const { return k_num_register_sets; } const RegisterSet * NativeRegisterContextLinux_arm64::GetRegisterSet(uint32_t set_index) const { if (set_index < k_num_register_sets) return &g_reg_sets_arm64[set_index]; return nullptr; } uint32_t NativeRegisterContextLinux_arm64::GetUserRegisterCount() const { uint32_t count = 0; for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) count += g_reg_sets_arm64[set_index].num_registers; return count; } Status NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { Status error; if (!reg_info) { error.SetErrorString("reg_info NULL"); return error; } const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; if (IsFPR(reg)) { error = ReadFPR(); if (error.Fail()) return error; } else { uint32_t full_reg = reg; bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); if (is_subreg) { // Read the full aligned 64-bit register. full_reg = reg_info->invalidate_regs[0]; } error = ReadRegisterRaw(full_reg, reg_value); if (error.Success()) { // If our read was not aligned (for ah,bh,ch,dh), shift our returned value // one byte to the right. if (is_subreg && (reg_info->byte_offset & 0x1)) reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); // If our return byte size was greater than the return value reg size, // then // use the type specified by reg_info rather than the uint64_t default if (reg_value.GetByteSize() > reg_info->byte_size) reg_value.SetType(reg_info); } return error; } // Get pointer to m_fpr variable and set the data from it. uint32_t fpr_offset = CalculateFprOffset(reg_info); assert(fpr_offset < sizeof m_fpr); uint8_t *src = (uint8_t *)&m_fpr + fpr_offset; reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size, eByteOrderLittle, error); return error; } Status NativeRegisterContextLinux_arm64::WriteRegister( const RegisterInfo *reg_info, const RegisterValue ®_value) { if (!reg_info) return Status("reg_info NULL"); const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; if (reg_index == LLDB_INVALID_REGNUM) return Status("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); if (IsGPR(reg_index)) return WriteRegisterRaw(reg_index, reg_value); if (IsFPR(reg_index)) { // Get pointer to m_fpr variable and set the data to it. uint32_t fpr_offset = CalculateFprOffset(reg_info); assert(fpr_offset < sizeof m_fpr); uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset; switch (reg_info->byte_size) { case 2: *(uint16_t *)dst = reg_value.GetAsUInt16(); break; case 4: *(uint32_t *)dst = reg_value.GetAsUInt32(); break; case 8: *(uint64_t *)dst = reg_value.GetAsUInt64(); break; default: assert(false && "Unhandled data size."); return Status("unhandled register data size %" PRIu32, reg_info->byte_size); } Status error = WriteFPR(); if (error.Fail()) return error; return Status(); } return Status("failed - register wasn't recognized to be a GPR or an FPR, " "write strategy unknown"); } Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues( lldb::DataBufferSP &data_sp) { Status error; data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); if (!data_sp) return Status("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); error = ReadGPR(); if (error.Fail()) return error; error = ReadFPR(); if (error.Fail()) return error; uint8_t *dst = data_sp->GetBytes(); if (dst == nullptr) { error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); return error; } ::memcpy(dst, &m_gpr_arm64, GetGPRSize()); dst += GetGPRSize(); ::memcpy(dst, &m_fpr, sizeof(m_fpr)); return error; } Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { Status error; if (!data_sp) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); return error; } if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched " "data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); return error; } uint8_t *src = data_sp->GetBytes(); if (src == nullptr) { error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s " "DataBuffer::GetBytes() returned a null " "pointer", __FUNCTION__); return error; } ::memcpy(&m_gpr_arm64, src, GetRegisterInfoInterface().GetGPRSize()); error = WriteGPR(); if (error.Fail()) return error; src += GetRegisterInfoInterface().GetGPRSize(); ::memcpy(&m_fpr, src, sizeof(m_fpr)); error = WriteFPR(); if (error.Fail()) return error; return error; } bool NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const { return reg <= m_reg_info.last_gpr; // GPR's come first. } bool NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const { return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); } uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); if (log) log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); Status error; // Read hardware breakpoint and watchpoint information. error = ReadHardwareDebugInfo(); if (error.Fail()) return 0; return m_max_hbp_supported; } uint32_t NativeRegisterContextLinux_arm64::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return LLDB_INVALID_INDEX32; uint32_t control_value = 0, bp_index = 0; // Check if size has a valid hardware breakpoint length. if (size != 4) return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware // breakpoint // Check 4-byte alignment for hardware breakpoint target address. if (addr & 0x03) return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. // Setup control value control_value = 0; control_value |= ((1 << size) - 1) << 5; control_value |= (2 << 1) | 1; // Iterate over stored breakpoints and find a free bp_index bp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if ((m_hbr_regs[i].control & 1) == 0) { bp_index = i; // Mark last free slot } else if (m_hbr_regs[i].address == addr) { return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. } } if (bp_index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; // Update breakpoint in local cache m_hbr_regs[bp_index].real_addr = addr; m_hbr_regs[bp_index].address = addr; m_hbr_regs[bp_index].control = control_value; // PTRACE call to set corresponding hardware breakpoint register. error = WriteHardwareDebugRegs(eDREGTypeBREAK); if (error.Fail()) { m_hbr_regs[bp_index].address = 0; m_hbr_regs[bp_index].control &= ~1; return LLDB_INVALID_INDEX32; } return bp_index; } bool NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint( uint32_t hw_idx) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); LLDB_LOG(log, "hw_idx: {0}", hw_idx); // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return false; if (hw_idx >= m_max_hbp_supported) return false; // Create a backup we can revert to in case of failure. lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; uint32_t tempControl = m_hbr_regs[hw_idx].control; m_hbr_regs[hw_idx].control &= ~1; m_hbr_regs[hw_idx].address = 0; // PTRACE call to clear corresponding hardware breakpoint register. error = WriteHardwareDebugRegs(eDREGTypeBREAK); if (error.Fail()) { m_hbr_regs[hw_idx].control = tempControl; m_hbr_regs[hw_idx].address = tempAddr; return false; } return true; } Status NativeRegisterContextLinux_arm64::GetHardwareBreakHitIndex( uint32_t &bp_index, lldb::addr_t trap_addr) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); if (log) log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); lldb::addr_t break_addr; for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { break_addr = m_hbr_regs[bp_index].address; if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) { m_hbr_regs[bp_index].hit_addr = trap_addr; return Status(); } } bp_index = LLDB_INVALID_INDEX32; return Status(); } Status NativeRegisterContextLinux_arm64::ClearAllHardwareBreakpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); if (log) log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); Status error; // Read hardware breakpoint and watchpoint information. error = ReadHardwareDebugInfo(); if (error.Fail()) return error; lldb::addr_t tempAddr = 0; uint32_t tempControl = 0; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if (m_hbr_regs[i].control & 0x01) { // Create a backup we can revert to in case of failure. tempAddr = m_hbr_regs[i].address; tempControl = m_hbr_regs[i].control; // Clear watchpoints in local cache m_hbr_regs[i].control &= ~1; m_hbr_regs[i].address = 0; // Ptrace call to update hardware debug registers error = WriteHardwareDebugRegs(eDREGTypeBREAK); if (error.Fail()) { m_hbr_regs[i].control = tempControl; m_hbr_regs[i].address = tempAddr; return error; } } } return Status(); } uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return 0; LLDB_LOG(log, "{0}", m_max_hwp_supported); return m_max_hwp_supported; } uint32_t NativeRegisterContextLinux_arm64::SetHardwareWatchpoint( lldb::addr_t addr, size_t size, uint32_t watch_flags) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, watch_flags); // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return LLDB_INVALID_INDEX32; uint32_t control_value = 0, wp_index = 0; lldb::addr_t real_addr = addr; // Check if we are setting watchpoint other than read/write/access // Also update watchpoint flag to match AArch64 write-read bit configuration. switch (watch_flags) { case 1: watch_flags = 2; break; case 2: watch_flags = 1; break; case 3: break; default: return LLDB_INVALID_INDEX32; } // Check if size has a valid hardware watchpoint length. if (size != 1 && size != 2 && size != 4 && size != 8) return LLDB_INVALID_INDEX32; // Check 8-byte alignment for hardware watchpoint target address. // Below is a hack to recalculate address and size in order to // make sure we can watch non 8-byte alligned addresses as well. if (addr & 0x07) { uint8_t watch_mask = (addr & 0x07) + size; if (watch_mask > 0x08) return LLDB_INVALID_INDEX32; else if (watch_mask <= 0x02) size = 2; else if (watch_mask <= 0x04) size = 4; else size = 8; addr = addr & (~0x07); } // Setup control value control_value = watch_flags << 3; control_value |= ((1 << size) - 1) << 5; control_value |= (2 << 1) | 1; // Iterate over stored watchpoints and find a free wp_index wp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hwp_supported; i++) { if ((m_hwp_regs[i].control & 1) == 0) { wp_index = i; // Mark last free slot } else if (m_hwp_regs[i].address == addr) { return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. } } if (wp_index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; // Update watchpoint in local cache m_hwp_regs[wp_index].real_addr = real_addr; m_hwp_regs[wp_index].address = addr; m_hwp_regs[wp_index].control = control_value; // PTRACE call to set corresponding watchpoint register. error = WriteHardwareDebugRegs(eDREGTypeWATCH); if (error.Fail()) { m_hwp_regs[wp_index].address = 0; m_hwp_regs[wp_index].control &= ~1; return LLDB_INVALID_INDEX32; } return wp_index; } bool NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint( uint32_t wp_index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}", wp_index); // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return false; if (wp_index >= m_max_hwp_supported) return false; // Create a backup we can revert to in case of failure. lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; uint32_t tempControl = m_hwp_regs[wp_index].control; // Update watchpoint in local cache m_hwp_regs[wp_index].control &= ~1; m_hwp_regs[wp_index].address = 0; // Ptrace call to update hardware debug registers error = WriteHardwareDebugRegs(eDREGTypeWATCH); if (error.Fail()) { m_hwp_regs[wp_index].control = tempControl; m_hwp_regs[wp_index].address = tempAddr; return false; } return true; } Status NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints() { // Read hardware breakpoint and watchpoint information. Status error = ReadHardwareDebugInfo(); if (error.Fail()) return error; lldb::addr_t tempAddr = 0; uint32_t tempControl = 0; for (uint32_t i = 0; i < m_max_hwp_supported; i++) { if (m_hwp_regs[i].control & 0x01) { // Create a backup we can revert to in case of failure. tempAddr = m_hwp_regs[i].address; tempControl = m_hwp_regs[i].control; // Clear watchpoints in local cache m_hwp_regs[i].control &= ~1; m_hwp_regs[i].address = 0; // Ptrace call to update hardware debug registers error = WriteHardwareDebugRegs(eDREGTypeWATCH); if (error.Fail()) { m_hwp_regs[i].control = tempControl; m_hwp_regs[i].address = tempAddr; return error; } } } return Status(); } uint32_t NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}", wp_index); switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) { case 0x01: return 1; case 0x03: return 2; case 0x0f: return 4; case 0xff: return 8; default: return 0; } } bool NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}", wp_index); if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) return true; else return false; } Status NativeRegisterContextLinux_arm64::GetWatchpointHitIndex( uint32_t &wp_index, lldb::addr_t trap_addr) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); uint32_t watch_size; lldb::addr_t watch_addr; for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { watch_size = GetWatchpointSize(wp_index); watch_addr = m_hwp_regs[wp_index].address; if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) { m_hwp_regs[wp_index].hit_addr = trap_addr; return Status(); } } wp_index = LLDB_INVALID_INDEX32; return Status(); } lldb::addr_t NativeRegisterContextLinux_arm64::GetWatchpointAddress(uint32_t wp_index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}", wp_index); if (wp_index >= m_max_hwp_supported) return LLDB_INVALID_ADDRESS; if (WatchpointIsEnabled(wp_index)) return m_hwp_regs[wp_index].real_addr; else return LLDB_INVALID_ADDRESS; } lldb::addr_t NativeRegisterContextLinux_arm64::GetWatchpointHitAddress(uint32_t wp_index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); LLDB_LOG(log, "wp_index: {0}", wp_index); if (wp_index >= m_max_hwp_supported) return LLDB_INVALID_ADDRESS; if (WatchpointIsEnabled(wp_index)) return m_hwp_regs[wp_index].hit_addr; else return LLDB_INVALID_ADDRESS; } Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { if (!m_refresh_hwdebug_info) { return Status(); } ::pid_t tid = m_thread.GetID(); int regset = NT_ARM_HW_WATCH; struct iovec ioVec; struct user_hwdebug_state dreg_state; Status error; ioVec.iov_base = &dreg_state; ioVec.iov_len = sizeof(dreg_state); error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); if (error.Fail()) return error; m_max_hwp_supported = dreg_state.dbg_info & 0xff; regset = NT_ARM_HW_BREAK; error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); if (error.Fail()) return error; m_max_hbp_supported = dreg_state.dbg_info & 0xff; m_refresh_hwdebug_info = false; return error; } Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) { struct iovec ioVec; struct user_hwdebug_state dreg_state; Status error; memset(&dreg_state, 0, sizeof(dreg_state)); ioVec.iov_base = &dreg_state; if (hwbType == eDREGTypeWATCH) { hwbType = NT_ARM_HW_WATCH; ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); for (uint32_t i = 0; i < m_max_hwp_supported; i++) { dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; } } else { hwbType = NT_ARM_HW_BREAK; ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); for (uint32_t i = 0; i < m_max_hbp_supported; i++) { dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address; dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; } } return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &hwbType, &ioVec, ioVec.iov_len); } Status NativeRegisterContextLinux_arm64::DoReadRegisterValue( uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) { Status error; if (offset > sizeof(struct user_pt_regs)) { offset -= sizeof(struct user_pt_regs); if (offset > sizeof(struct user_fpsimd_state)) { error.SetErrorString("invalid offset value"); return error; } elf_fpregset_t regs; int regset = NT_FPREGSET; struct iovec ioVec; ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); if (error.Success()) { ArchSpec arch; - if (m_thread.GetProcess()->GetArchitecture(arch)) + if (m_thread.GetProcess().GetArchitecture(arch)) value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 16, arch.GetByteOrder()); else error.SetErrorString("failed to get architecture"); } } else { elf_gregset_t regs; int regset = NT_PRSTATUS; struct iovec ioVec; ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); if (error.Success()) { ArchSpec arch; - if (m_thread.GetProcess()->GetArchitecture(arch)) + if (m_thread.GetProcess().GetArchitecture(arch)) value.SetBytes((void *)(((unsigned char *)(regs)) + offset), 8, arch.GetByteOrder()); else error.SetErrorString("failed to get architecture"); } } return error; } Status NativeRegisterContextLinux_arm64::DoWriteRegisterValue( uint32_t offset, const char *reg_name, const RegisterValue &value) { Status error; ::pid_t tid = m_thread.GetID(); if (offset > sizeof(struct user_pt_regs)) { offset -= sizeof(struct user_pt_regs); if (offset > sizeof(struct user_fpsimd_state)) { error.SetErrorString("invalid offset value"); return error; } elf_fpregset_t regs; int regset = NT_FPREGSET; struct iovec ioVec; ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); if (error.Success()) { ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 16); error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); } } else { elf_gregset_t regs; int regset = NT_PRSTATUS; struct iovec ioVec; ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); if (error.Success()) { ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 8); error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); } } return error; } Status NativeRegisterContextLinux_arm64::DoReadGPR(void *buf, size_t buf_size) { int regset = NT_PRSTATUS; struct iovec ioVec; Status error; ioVec.iov_base = buf; ioVec.iov_len = buf_size; return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); } Status NativeRegisterContextLinux_arm64::DoWriteGPR(void *buf, size_t buf_size) { int regset = NT_PRSTATUS; struct iovec ioVec; Status error; ioVec.iov_base = buf; ioVec.iov_len = buf_size; return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); } Status NativeRegisterContextLinux_arm64::DoReadFPR(void *buf, size_t buf_size) { int regset = NT_FPREGSET; struct iovec ioVec; Status error; ioVec.iov_base = buf; ioVec.iov_len = buf_size; return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); } Status NativeRegisterContextLinux_arm64::DoWriteFPR(void *buf, size_t buf_size) { int regset = NT_FPREGSET; struct iovec ioVec; Status error; ioVec.iov_base = buf; ioVec.iov_len = buf_size; return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); } uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset( const RegisterInfo *reg_info) const { return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; } #endif // defined (__arm64__) || defined (__aarch64__) Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp (revision 321194) @@ -1,1059 +1,1059 @@ //===-- NativeRegisterContextLinux_mips64.cpp ---------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(__mips__) #include "NativeRegisterContextLinux_mips64.h" // C Includes // C++ Includes // Other libraries and framework includes #include "Plugins/Process/Linux/NativeProcessLinux.h" #include "Plugins/Process/Linux/Procfs.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Plugins/Process/Utility/RegisterContextLinux_mips.h" #include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-private-enumerations.h" #define NT_MIPS_MSA 0x600 #define CONFIG5_FRE (1 << 8) #define SR_FR (1 << 26) #define NUM_REGISTERS 32 #include #include #ifndef PTRACE_GET_WATCH_REGS enum pt_watch_style { pt_watch_style_mips32, pt_watch_style_mips64 }; struct mips32_watch_regs { uint32_t watchlo[8]; uint16_t watchhi[8]; uint16_t watch_masks[8]; uint32_t num_valid; } __attribute__((aligned(8))); struct mips64_watch_regs { uint64_t watchlo[8]; uint16_t watchhi[8]; uint16_t watch_masks[8]; uint32_t num_valid; } __attribute__((aligned(8))); struct pt_watch_regs { enum pt_watch_style style; union { struct mips32_watch_regs mips32; struct mips64_watch_regs mips64; }; }; #define PTRACE_GET_WATCH_REGS 0xd0 #define PTRACE_SET_WATCH_REGS 0xd1 #endif #define W (1 << 0) #define R (1 << 1) #define I (1 << 2) #define IRW (I | R | W) #ifndef PTRACE_GETREGSET #define PTRACE_GETREGSET 0x4204 #endif struct pt_watch_regs default_watch_regs; using namespace lldb_private; using namespace lldb_private::process_linux; NativeRegisterContextLinux * NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx) { return new NativeRegisterContextLinux_mips64(target_arch, native_thread, concrete_frame_idx); } #define REG_CONTEXT_SIZE \ (GetRegisterInfoInterface().GetGPRSize() + sizeof(FPR_linux_mips) + \ sizeof(MSA_linux_mips)) // ---------------------------------------------------------------------------- // NativeRegisterContextLinux_mips64 members. // ---------------------------------------------------------------------------- static RegisterInfoInterface * CreateRegisterInfoInterface(const ArchSpec &target_arch) { if ((target_arch.GetMachine() == llvm::Triple::mips) || (target_arch.GetMachine() == llvm::Triple::mipsel)) { // 32-bit hosts run with a RegisterContextLinux_mips context. return new RegisterContextLinux_mips( target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); } else { return new RegisterContextLinux_mips64( target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); } } NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx) : NativeRegisterContextLinux(native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) { switch (target_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: m_reg_info.num_registers = k_num_registers_mips; m_reg_info.num_gpr_registers = k_num_gpr_registers_mips; m_reg_info.num_fpr_registers = k_num_fpr_registers_mips; m_reg_info.last_gpr = k_last_gpr_mips; m_reg_info.first_fpr = k_first_fpr_mips; m_reg_info.last_fpr = k_last_fpr_mips; m_reg_info.first_msa = k_first_msa_mips; m_reg_info.last_msa = k_last_msa_mips; break; case llvm::Triple::mips64: case llvm::Triple::mips64el: m_reg_info.num_registers = k_num_registers_mips64; m_reg_info.num_gpr_registers = k_num_gpr_registers_mips64; m_reg_info.num_fpr_registers = k_num_fpr_registers_mips64; m_reg_info.last_gpr = k_last_gpr_mips64; m_reg_info.first_fpr = k_first_fpr_mips64; m_reg_info.last_fpr = k_last_fpr_mips64; m_reg_info.first_msa = k_first_msa_mips64; m_reg_info.last_msa = k_last_msa_mips64; break; default: assert(false && "Unhandled target architecture."); break; } // Initialize m_iovec to point to the buffer and buffer size // using the conventions of Berkeley style UIO structures, as required // by PTRACE extensions. m_iovec.iov_base = &m_msa; m_iovec.iov_len = sizeof(MSA_linux_mips); // init h/w watchpoint addr map for (int index = 0; index <= MAX_NUM_WP; index++) hw_addr_map[index] = LLDB_INVALID_ADDRESS; ::memset(&m_gpr, 0, sizeof(GPR_linux_mips)); ::memset(&m_fpr, 0, sizeof(FPR_linux_mips)); ::memset(&m_msa, 0, sizeof(MSA_linux_mips)); } uint32_t NativeRegisterContextLinux_mips64::GetRegisterSetCount() const { switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: { const auto context = static_cast (GetRegisterInfoInterface()); return context.GetRegisterSetCount(); } case llvm::Triple::mips: case llvm::Triple::mipsel: { const auto context = static_cast (GetRegisterInfoInterface()); return context.GetRegisterSetCount(); } default: llvm_unreachable("Unhandled target architecture."); } } lldb::addr_t NativeRegisterContextLinux_mips64::GetPCfromBreakpointLocation( lldb::addr_t fail_value) { Status error; RegisterValue pc_value; lldb::addr_t pc = fail_value; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); LLDB_LOG(log, "Reading PC from breakpoint location"); // PC register is at index 34 of the register array const RegisterInfo *const pc_info_p = GetRegisterInfoAtIndex(gpr_pc_mips64); error = ReadRegister(pc_info_p, pc_value); if (error.Success()) { pc = pc_value.GetAsUInt64(); // CAUSE register is at index 37 of the register array const RegisterInfo *const cause_info_p = GetRegisterInfoAtIndex(gpr_cause_mips64); RegisterValue cause_value; ReadRegister(cause_info_p, cause_value); uint64_t cause = cause_value.GetAsUInt64(); LLDB_LOG(log, "PC {0:x} cause {1:x}", pc, cause); /* * The breakpoint might be in a delay slot. In this case PC points * to the delayed branch instruction rather then the instruction * in the delay slot. If the CAUSE.BD flag is set then adjust the * PC based on the size of the branch instruction. */ if ((cause & (1 << 31)) != 0) { lldb::addr_t branch_delay = 0; branch_delay = 4; // FIXME - Adjust according to size of branch instruction at PC pc = pc + branch_delay; pc_value.SetUInt64(pc); WriteRegister(pc_info_p, pc_value); LLDB_LOG(log, "New PC {0:x}", pc); } } return pc; } const RegisterSet * NativeRegisterContextLinux_mips64::GetRegisterSet(uint32_t set_index) const { if (set_index >= GetRegisterSetCount()) return nullptr; switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: { const auto context = static_cast (GetRegisterInfoInterface()); return context.GetRegisterSet(set_index); } case llvm::Triple::mips: case llvm::Triple::mipsel: { const auto context = static_cast (GetRegisterInfoInterface()); return context.GetRegisterSet(set_index); } default: llvm_unreachable("Unhandled target architecture."); } } lldb_private::Status NativeRegisterContextLinux_mips64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { Status error; if (!reg_info) { error.SetErrorString("reg_info NULL"); return error; } const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; uint8_t byte_size = reg_info->byte_size; if (reg == LLDB_INVALID_REGNUM) { // This is likely an internal register for lldb use only and should not be // directly queried. error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " "register, cannot read directly", reg_info->name); return error; } if (IsMSA(reg) && !IsMSAAvailable()) { error.SetErrorString("MSA not available on this processor"); return error; } if (IsMSA(reg) || IsFPR(reg)) { uint8_t *src = nullptr; lldbassert(reg_info->byte_offset < sizeof(UserArea)); error = ReadCP1(); if (!error.Success()) { error.SetErrorString("failed to read co-processor 1 register"); return error; } if (IsFPR(reg)) { if (IsFR0() && (byte_size != 4)) { byte_size = 4; uint8_t ptrace_index; ptrace_index = reg_info->kinds[lldb::eRegisterKindProcessPlugin]; src = ReturnFPOffset(ptrace_index, reg_info->byte_offset); } else src = (uint8_t *)&m_fpr + reg_info->byte_offset - sizeof(m_gpr); } else src = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); switch (byte_size) { case 4: reg_value.SetUInt32(*(uint32_t *)src); break; case 8: reg_value.SetUInt64(*(uint64_t *)src); break; case 16: reg_value.SetBytes((const void *)src, 16, GetByteOrder()); break; default: assert(false && "Unhandled data size."); error.SetErrorStringWithFormat("unhandled byte size: %" PRIu32, reg_info->byte_size); break; } } else { error = ReadRegisterRaw(reg, reg_value); } return error; } lldb_private::Status NativeRegisterContextLinux_mips64::WriteRegister( const RegisterInfo *reg_info, const RegisterValue ®_value) { Status error; assert(reg_info && "reg_info is null"); const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; if (reg_index == LLDB_INVALID_REGNUM) return Status("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); if (IsMSA(reg_index) && !IsMSAAvailable()) { error.SetErrorString("MSA not available on this processor"); return error; } if (IsFPR(reg_index) || IsMSA(reg_index)) { uint8_t *dst = nullptr; uint64_t *src = nullptr; uint8_t byte_size = reg_info->byte_size; lldbassert(reg_info->byte_offset < sizeof(UserArea)); // Initialise the FP and MSA buffers by reading all co-processor 1 registers ReadCP1(); if (IsFPR(reg_index)) { if (IsFR0() && (byte_size != 4)) { byte_size = 4; uint8_t ptrace_index; ptrace_index = reg_info->kinds[lldb::eRegisterKindProcessPlugin]; dst = ReturnFPOffset(ptrace_index, reg_info->byte_offset); } else dst = (uint8_t *)&m_fpr + reg_info->byte_offset - sizeof(m_gpr); } else dst = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); switch (byte_size) { case 4: *(uint32_t *)dst = reg_value.GetAsUInt32(); break; case 8: *(uint64_t *)dst = reg_value.GetAsUInt64(); break; case 16: src = (uint64_t *)reg_value.GetBytes(); *(uint64_t *)dst = *src; *(uint64_t *)(dst + 8) = *(src + 1); break; default: assert(false && "Unhandled data size."); error.SetErrorStringWithFormat("unhandled byte size: %" PRIu32, reg_info->byte_size); break; } error = WriteCP1(); if (!error.Success()) { error.SetErrorString("failed to write co-processor 1 register"); return error; } } else { error = WriteRegisterRaw(reg_index, reg_value); } return error; } Status NativeRegisterContextLinux_mips64::ReadAllRegisterValues( lldb::DataBufferSP &data_sp) { Status error; data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); if (!data_sp) { error.SetErrorStringWithFormat( "failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); return error; } error = ReadGPR(); if (!error.Success()) { error.SetErrorString("ReadGPR() failed"); return error; } error = ReadCP1(); if (!error.Success()) { error.SetErrorString("ReadCP1() failed"); return error; } uint8_t *dst = data_sp->GetBytes(); if (dst == nullptr) { error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); return error; } ::memcpy(dst, &m_gpr, GetRegisterInfoInterface().GetGPRSize()); dst += GetRegisterInfoInterface().GetGPRSize(); ::memcpy(dst, &m_fpr, GetFPRSize()); dst += GetFPRSize(); ::memcpy(dst, &m_msa, sizeof(MSA_linux_mips)); return error; } Status NativeRegisterContextLinux_mips64::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { Status error; if (!data_sp) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_mips64::%s invalid data_sp provided", __FUNCTION__); return error; } if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_mips64::%s data_sp contained mismatched " "data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); return error; } uint8_t *src = data_sp->GetBytes(); if (src == nullptr) { error.SetErrorStringWithFormat("NativeRegisterContextLinux_mips64::%s " "DataBuffer::GetBytes() returned a null " "pointer", __FUNCTION__); return error; } ::memcpy(&m_gpr, src, GetRegisterInfoInterface().GetGPRSize()); src += GetRegisterInfoInterface().GetGPRSize(); ::memcpy(&m_fpr, src, GetFPRSize()); src += GetFPRSize(); ::memcpy(&m_msa, src, sizeof(MSA_linux_mips)); error = WriteGPR(); if (!error.Success()) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_mips64::%s WriteGPR() failed", __FUNCTION__); return error; } error = WriteCP1(); if (!error.Success()) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_mips64::%s WriteCP1() failed", __FUNCTION__); return error; } return error; } Status NativeRegisterContextLinux_mips64::ReadCP1() { Status error; uint8_t *src = nullptr; uint8_t *dst = nullptr; lldb::ByteOrder byte_order = GetByteOrder(); bool IsBigEndian = (byte_order == lldb::eByteOrderBig); if (IsMSAAvailable()) { error = NativeRegisterContextLinux::ReadRegisterSet( &m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); src = (uint8_t *)&m_msa + (IsBigEndian * 8); dst = (uint8_t *)&m_fpr; for (int i = 0; i < NUM_REGISTERS; i++) { // Copy fp values from msa buffer fetched via ptrace *(uint64_t *)dst = *(uint64_t *)src; src = src + 16; dst = dst + 8; } m_fpr.fir = m_msa.fir; m_fpr.fcsr = m_msa.fcsr; m_fpr.config5 = m_msa.config5; } else { error = NativeRegisterContextLinux::ReadFPR(); } return error; } uint8_t * NativeRegisterContextLinux_mips64::ReturnFPOffset(uint8_t reg_index, uint32_t byte_offset) { uint8_t *fp_buffer_ptr = nullptr; lldb::ByteOrder byte_order = GetByteOrder(); bool IsBigEndian = (byte_order == lldb::eByteOrderBig); if (reg_index % 2) { uint8_t offset_diff = (IsBigEndian) ? 8 : 4; fp_buffer_ptr = (uint8_t *)&m_fpr + byte_offset - offset_diff - sizeof(m_gpr); } else { fp_buffer_ptr = (uint8_t *)&m_fpr + byte_offset + 4 * (IsBigEndian) - sizeof(m_gpr); } return fp_buffer_ptr; } Status NativeRegisterContextLinux_mips64::WriteCP1() { Status error; uint8_t *src = nullptr; uint8_t *dst = nullptr; lldb::ByteOrder byte_order = GetByteOrder(); bool IsBigEndian = (byte_order == lldb::eByteOrderBig); if (IsMSAAvailable()) { dst = (uint8_t *)&m_msa + (IsBigEndian * 8); src = (uint8_t *)&m_fpr; for (int i = 0; i < NUM_REGISTERS; i++) { // Copy fp values to msa buffer for ptrace *(uint64_t *)dst = *(uint64_t *)src; dst = dst + 16; src = src + 8; } m_msa.fir = m_fpr.fir; m_msa.fcsr = m_fpr.fcsr; m_msa.config5 = m_fpr.config5; error = NativeRegisterContextLinux::WriteRegisterSet( &m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); } else { error = NativeRegisterContextLinux::WriteFPR(); } return error; } bool NativeRegisterContextLinux_mips64::IsFR0() { const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex(gpr_sr_mips64); RegisterValue reg_value; ReadRegister(reg_info_p, reg_value); uint64_t value = reg_value.GetAsUInt64(); return (!(value & SR_FR)); } bool NativeRegisterContextLinux_mips64::IsFRE() { const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex(gpr_config5_mips64); RegisterValue reg_value; ReadRegister(reg_info_p, reg_value); uint64_t config5 = reg_value.GetAsUInt64(); return (config5 & CONFIG5_FRE); } bool NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const { return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); } static uint32_t GetWatchHi(struct pt_watch_regs *regs, uint32_t index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) return regs->mips32.watchhi[index]; else if (regs->style == pt_watch_style_mips64) return regs->mips64.watchhi[index]; LLDB_LOG(log, "Invalid watch register style"); return 0; } static void SetWatchHi(struct pt_watch_regs *regs, uint32_t index, uint16_t value) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) regs->mips32.watchhi[index] = value; else if (regs->style == pt_watch_style_mips64) regs->mips64.watchhi[index] = value; LLDB_LOG(log, "Invalid watch register style"); return; } static lldb::addr_t GetWatchLo(struct pt_watch_regs *regs, uint32_t index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) return regs->mips32.watchlo[index]; else if (regs->style == pt_watch_style_mips64) return regs->mips64.watchlo[index]; LLDB_LOG(log, "Invalid watch register style"); return LLDB_INVALID_ADDRESS; } static void SetWatchLo(struct pt_watch_regs *regs, uint32_t index, uint64_t value) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) regs->mips32.watchlo[index] = (uint32_t)value; else if (regs->style == pt_watch_style_mips64) regs->mips64.watchlo[index] = value; else LLDB_LOG(log, "Invalid watch register style"); } static uint32_t GetIRWMask(struct pt_watch_regs *regs, uint32_t index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) return regs->mips32.watch_masks[index] & IRW; else if (regs->style == pt_watch_style_mips64) return regs->mips64.watch_masks[index] & IRW; LLDB_LOG(log, "Invalid watch register style"); return 0; } static uint32_t GetRegMask(struct pt_watch_regs *regs, uint32_t index) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); if (regs->style == pt_watch_style_mips32) return regs->mips32.watch_masks[index] & ~IRW; else if (regs->style == pt_watch_style_mips64) return regs->mips64.watch_masks[index] & ~IRW; LLDB_LOG(log, "Invalid watch register style"); return 0; } static lldb::addr_t GetRangeMask(lldb::addr_t mask) { lldb::addr_t mask_bit = 1; while (mask_bit < mask) { mask = mask | mask_bit; mask_bit <<= 1; } return mask; } static int GetVacantWatchIndex(struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid) { lldb::addr_t last_byte = addr + size - 1; lldb::addr_t mask = GetRangeMask(addr ^ last_byte) | IRW; lldb::addr_t base_addr = addr & ~mask; // Check if this address is already watched by previous watch points. lldb::addr_t lo; uint16_t hi; uint32_t vacant_watches = 0; for (uint32_t index = 0; index < num_valid; index++) { lo = GetWatchLo(regs, index); if (lo != 0 && irw == ((uint32_t)lo & irw)) { hi = GetWatchHi(regs, index) | IRW; lo &= ~(lldb::addr_t)hi; if (addr >= lo && last_byte <= (lo + hi)) return index; } else vacant_watches++; } // Now try to find a vacant index if (vacant_watches > 0) { vacant_watches = 0; for (uint32_t index = 0; index < num_valid; index++) { lo = GetWatchLo(regs, index); if (lo == 0 && irw == (GetIRWMask(regs, index) & irw)) { if (mask <= (GetRegMask(regs, index) | IRW)) { // It fits, we can use it. SetWatchLo(regs, index, base_addr | irw); SetWatchHi(regs, index, mask & ~IRW); return index; } else { // It doesn't fit, but has the proper IRW capabilities vacant_watches++; } } } if (vacant_watches > 1) { // Split this watchpoint accross several registers struct pt_watch_regs regs_copy; regs_copy = *regs; lldb::addr_t break_addr; uint32_t segment_size; for (uint32_t index = 0; index < num_valid; index++) { lo = GetWatchLo(®s_copy, index); hi = GetRegMask(®s_copy, index) | IRW; if (lo == 0 && irw == (hi & irw)) { lo = addr & ~(lldb::addr_t)hi; break_addr = lo + hi + 1; if (break_addr >= addr + size) segment_size = size; else segment_size = break_addr - addr; mask = GetRangeMask(addr ^ (addr + segment_size - 1)); SetWatchLo(®s_copy, index, (addr & ~mask) | irw); SetWatchHi(®s_copy, index, mask & ~IRW); if (break_addr >= addr + size) { *regs = regs_copy; return index; } size = addr + size - break_addr; addr = break_addr; } } } } return LLDB_INVALID_INDEX32; } bool NativeRegisterContextLinux_mips64::IsMSA(uint32_t reg_index) const { return (m_reg_info.first_msa <= reg_index && reg_index <= m_reg_info.last_msa); } bool NativeRegisterContextLinux_mips64::IsMSAAvailable() { MSA_linux_mips msa_buf; unsigned int regset = NT_MIPS_MSA; Status error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, Host::GetCurrentProcessID(), static_cast(®set), &msa_buf, sizeof(MSA_linux_mips)); if (error.Success() && msa_buf.mir) { return true; } return false; } Status NativeRegisterContextLinux_mips64::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { if (wp_index >= NumSupportedHardwareWatchpoints()) return Status("Watchpoint index out of range"); // reading the current state of watch regs struct pt_watch_regs watch_readback; Status error = DoReadWatchPointRegisterValue( m_thread.GetID(), static_cast(&watch_readback)); if (GetWatchHi(&watch_readback, wp_index) & (IRW)) { // clear hit flag in watchhi SetWatchHi(&watch_readback, wp_index, (GetWatchHi(&watch_readback, wp_index) & ~(IRW))); DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(&watch_readback)); is_hit = true; return error; } is_hit = false; return error; } Status NativeRegisterContextLinux_mips64::GetWatchpointHitIndex( uint32_t &wp_index, lldb::addr_t trap_addr) { uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { bool is_hit; Status error = IsWatchpointHit(wp_index, is_hit); if (error.Fail()) { wp_index = LLDB_INVALID_INDEX32; } else if (is_hit) { return error; } } wp_index = LLDB_INVALID_INDEX32; return Status(); } Status NativeRegisterContextLinux_mips64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) { is_vacant = false; return Status("MIPS TODO: " "NativeRegisterContextLinux_mips64::IsWatchpointVacant not " "implemented"); } bool NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint( uint32_t wp_index) { if (wp_index >= NumSupportedHardwareWatchpoints()) return false; struct pt_watch_regs regs; // First reading the current state of watch regs DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); if (regs.style == pt_watch_style_mips32) { regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index]; regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index]; regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index]; } else // pt_watch_style_mips64 { regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index]; regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index]; regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index]; } Status error = DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); if (!error.Fail()) { hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS; return true; } return false; } Status NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints() { return DoWriteWatchPointRegisterValue( m_thread.GetID(), static_cast(&default_watch_regs)); } Status NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex( lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { Status error; error.SetErrorString("MIPS TODO: " "NativeRegisterContextLinux_mips64::" "SetHardwareWatchpointWithIndex not implemented"); return error; } uint32_t NativeRegisterContextLinux_mips64::SetHardwareWatchpoint( lldb::addr_t addr, size_t size, uint32_t watch_flags) { struct pt_watch_regs regs; // First reading the current state of watch regs DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); // Try if a new watch point fits in this state int index = GetVacantWatchIndex(®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints()); // New watchpoint doesn't fit if (index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; // It fits, so we go ahead with updating the state of watch regs DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); // Storing exact address hw_addr_map[index] = addr; return index; } lldb::addr_t NativeRegisterContextLinux_mips64::GetWatchpointAddress(uint32_t wp_index) { if (wp_index >= NumSupportedHardwareWatchpoints()) return LLDB_INVALID_ADDRESS; return hw_addr_map[wp_index]; } struct EmulatorBaton { lldb::addr_t m_watch_hit_addr; NativeProcessLinux *m_process; NativeRegisterContext *m_reg_context; EmulatorBaton(NativeProcessLinux *process, NativeRegisterContext *reg_context) : m_watch_hit_addr(LLDB_INVALID_ADDRESS), m_process(process), m_reg_context(reg_context) {} }; static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, size_t length) { size_t bytes_read; EmulatorBaton *emulator_baton = static_cast(baton); emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); return bytes_read; } static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t length) { return length; } static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, const RegisterInfo *reg_info, RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); const RegisterInfo *full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]); Status error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); if (error.Success()) return true; return false; } static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { if (reg_info->kinds[lldb::eRegisterKindDWARF] == dwarf_bad_mips64) { EmulatorBaton *emulator_baton = static_cast(baton); emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64(); } return true; } /* * MIPS Linux kernel returns a masked address (last 3bits are masked) * when a HW watchpoint is hit. However user may not have set a watchpoint * on this address. Emulate instruction at PC and find the base address of * the load/store instruction. This will give the exact address used to * read/write the variable. Send this exact address to client so that * it can decide to stop or continue the thread. */ lldb::addr_t NativeRegisterContextLinux_mips64::GetWatchpointHitAddress(uint32_t wp_index) { if (wp_index >= NumSupportedHardwareWatchpoints()) return LLDB_INVALID_ADDRESS; lldb_private::ArchSpec arch; arch = GetRegisterInfoInterface().GetTargetArchitecture(); std::unique_ptr emulator_ap( EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr)); if (emulator_ap == nullptr) return LLDB_INVALID_ADDRESS; EmulatorBaton baton( - static_cast(m_thread.GetProcess().get()), this); + static_cast(&m_thread.GetProcess()), this); emulator_ap->SetBaton(&baton); emulator_ap->SetReadMemCallback(&ReadMemoryCallback); emulator_ap->SetReadRegCallback(&ReadRegisterCallback); emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); if (!emulator_ap->ReadInstruction()) return LLDB_INVALID_ADDRESS; if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone)) return baton.m_watch_hit_addr; return LLDB_INVALID_ADDRESS; } uint32_t NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); struct pt_watch_regs regs; static int num_valid = 0; if (!num_valid) { DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); default_watch_regs = regs; // Keeping default watch regs values for future use switch (regs.style) { case pt_watch_style_mips32: num_valid = regs.mips32.num_valid; // Using num_valid as cache return num_valid; case pt_watch_style_mips64: num_valid = regs.mips64.num_valid; return num_valid; } LLDB_LOG(log, "Invalid watch register style"); return 0; } return num_valid; } Status NativeRegisterContextLinux_mips64::ReadRegisterRaw(uint32_t reg_index, RegisterValue &value) { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) return Status("register %" PRIu32 " not found", reg_index); uint32_t offset = reg_info->kinds[lldb::eRegisterKindProcessPlugin]; if ((offset == ptrace_sr_mips) || (offset == ptrace_config5_mips)) return Read_SR_Config(reg_info->byte_offset, reg_info->name, reg_info->byte_size, value); return DoReadRegisterValue(offset, reg_info->name, reg_info->byte_size, value); } Status NativeRegisterContextLinux_mips64::WriteRegisterRaw( uint32_t reg_index, const RegisterValue &value) { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) return Status("register %" PRIu32 " not found", reg_index); if (reg_info->invalidate_regs) lldbassert(false && "reg_info->invalidate_regs is unhandled"); uint32_t offset = reg_info->kinds[lldb::eRegisterKindProcessPlugin]; return DoWriteRegisterValue(offset, reg_info->name, value); } Status NativeRegisterContextLinux_mips64::Read_SR_Config(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) { GPR_linux_mips regs; ::memset(®s, 0, sizeof(GPR_linux_mips)); Status error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); if (error.Success()) { lldb_private::ArchSpec arch; - if (m_thread.GetProcess()->GetArchitecture(arch)) { + if (m_thread.GetProcess().GetArchitecture(arch)) { void *target_address = ((uint8_t *)®s) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips); value.SetUInt(*(uint32_t *)target_address, size); } else error.SetErrorString("failed to get architecture"); } return error; } Status NativeRegisterContextLinux_mips64::DoReadWatchPointRegisterValue( lldb::tid_t tid, void *watch_readback) { return NativeProcessLinux::PtraceWrapper(PTRACE_GET_WATCH_REGS, m_thread.GetID(), watch_readback); } Status NativeRegisterContextLinux_mips64::DoWriteWatchPointRegisterValue( lldb::tid_t tid, void *watch_reg_value) { return NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_thread.GetID(), watch_reg_value); } #endif // defined (__mips__) Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.cpp (revision 321194) @@ -1,479 +1,465 @@ //===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeThreadLinux.h" #include #include #include "NativeProcessLinux.h" #include "NativeRegisterContextLinux.h" #include "SingleStepCheck.h" #include "lldb/Core/State.h" #include "lldb/Host/HostNativeThread.h" #include "lldb/Host/linux/Ptrace.h" #include "lldb/Host/linux/Support.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/SmallString.h" #include "Plugins/Process/POSIX/CrashReason.h" #include // Try to define a macro to encapsulate the tgkill syscall #define tgkill(pid, tid, sig) \ syscall(__NR_tgkill, static_cast<::pid_t>(pid), static_cast<::pid_t>(tid), \ sig) using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; namespace { void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info, const char *const header) { switch (stop_info.reason) { case eStopReasonNone: log.Printf("%s: %s no stop reason", __FUNCTION__, header); return; case eStopReasonTrace: log.Printf("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonBreakpoint: log.Printf("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonWatchpoint: log.Printf("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonSignal: log.Printf("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonException: log.Printf("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); return; case eStopReasonExec: log.Printf("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonPlanComplete: log.Printf("%s: %s plan complete", __FUNCTION__, header); return; case eStopReasonThreadExiting: log.Printf("%s: %s thread exiting", __FUNCTION__, header); return; case eStopReasonInstrumentation: log.Printf("%s: %s instrumentation", __FUNCTION__, header); return; default: log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, static_cast(stop_info.reason)); } } } -NativeThreadLinux::NativeThreadLinux(NativeProcessLinux *process, +NativeThreadLinux::NativeThreadLinux(NativeProcessLinux &process, lldb::tid_t tid) : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), m_stop_info(), m_reg_context_sp(), m_stop_description() {} std::string NativeThreadLinux::GetName() { NativeProcessLinux &process = GetProcess(); auto BufferOrError = getProcFile(process.GetID(), GetID(), "comm"); if (!BufferOrError) return ""; return BufferOrError.get()->getBuffer().rtrim('\n'); } lldb::StateType NativeThreadLinux::GetState() { return m_state; } bool NativeThreadLinux::GetStopReason(ThreadStopInfo &stop_info, std::string &description) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); description.clear(); switch (m_state) { case eStateStopped: case eStateCrashed: case eStateExited: case eStateSuspended: case eStateUnloaded: if (log) LogThreadStopInfo(*log, m_stop_info, "m_stop_info in thread:"); stop_info = m_stop_info; description = m_stop_description; if (log) LogThreadStopInfo(*log, stop_info, "returned stop_info:"); return true; case eStateInvalid: case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateRunning: case eStateStepping: case eStateDetached: if (log) { log->Printf("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason", __FUNCTION__, GetID(), StateAsCString(m_state)); } return false; } llvm_unreachable("unhandled StateType!"); } NativeRegisterContextSP NativeThreadLinux::GetRegisterContext() { // Return the register context if we already created it. if (m_reg_context_sp) return m_reg_context_sp; - NativeProcessProtocolSP m_process_sp = m_process_wp.lock(); - if (!m_process_sp) - return NativeRegisterContextSP(); - ArchSpec target_arch; - if (!m_process_sp->GetArchitecture(target_arch)) + if (!m_process.GetArchitecture(target_arch)) return NativeRegisterContextSP(); const uint32_t concrete_frame_idx = 0; m_reg_context_sp.reset( NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( target_arch, *this, concrete_frame_idx)); return m_reg_context_sp; } Status NativeThreadLinux::SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { if (!hardware) return Status("not implemented"); if (m_state == eStateLaunching) return Status(); Status error = RemoveWatchpoint(addr); if (error.Fail()) return error; NativeRegisterContextSP reg_ctx = GetRegisterContext(); uint32_t wp_index = reg_ctx->SetHardwareWatchpoint(addr, size, watch_flags); if (wp_index == LLDB_INVALID_INDEX32) return Status("Setting hardware watchpoint failed."); m_watchpoint_index_map.insert({addr, wp_index}); return Status(); } Status NativeThreadLinux::RemoveWatchpoint(lldb::addr_t addr) { auto wp = m_watchpoint_index_map.find(addr); if (wp == m_watchpoint_index_map.end()) return Status(); uint32_t wp_index = wp->second; m_watchpoint_index_map.erase(wp); if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) return Status(); return Status("Clearing hardware watchpoint failed."); } Status NativeThreadLinux::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { if (m_state == eStateLaunching) return Status(); Status error = RemoveHardwareBreakpoint(addr); if (error.Fail()) return error; NativeRegisterContextSP reg_ctx = GetRegisterContext(); uint32_t bp_index = reg_ctx->SetHardwareBreakpoint(addr, size); if (bp_index == LLDB_INVALID_INDEX32) return Status("Setting hardware breakpoint failed."); m_hw_break_index_map.insert({addr, bp_index}); return Status(); } Status NativeThreadLinux::RemoveHardwareBreakpoint(lldb::addr_t addr) { auto bp = m_hw_break_index_map.find(addr); if (bp == m_hw_break_index_map.end()) return Status(); uint32_t bp_index = bp->second; if (GetRegisterContext()->ClearHardwareBreakpoint(bp_index)) { m_hw_break_index_map.erase(bp); return Status(); } return Status("Clearing hardware breakpoint failed."); } Status NativeThreadLinux::Resume(uint32_t signo) { const StateType new_state = StateType::eStateRunning; MaybeLogStateChange(new_state); m_state = new_state; m_stop_info.reason = StopReason::eStopReasonNone; m_stop_description.clear(); // If watchpoints have been set, but none on this thread, // then this is a new thread. So set all existing watchpoints. if (m_watchpoint_index_map.empty()) { NativeProcessLinux &process = GetProcess(); const auto &watchpoint_map = process.GetWatchpointMap(); GetRegisterContext()->ClearAllHardwareWatchpoints(); for (const auto &pair : watchpoint_map) { const auto &wp = pair.second; SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); } } // Set all active hardware breakpoint on all threads. if (m_hw_break_index_map.empty()) { NativeProcessLinux &process = GetProcess(); const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); GetRegisterContext()->ClearAllHardwareBreakpoints(); for (const auto &pair : hw_breakpoint_map) { const auto &bp = pair.second; SetHardwareBreakpoint(bp.m_addr, bp.m_size); } } intptr_t data = 0; if (signo != LLDB_INVALID_SIGNAL_NUMBER) data = signo; return NativeProcessLinux::PtraceWrapper(PTRACE_CONT, GetID(), nullptr, reinterpret_cast(data)); } Status NativeThreadLinux::SingleStep(uint32_t signo) { const StateType new_state = StateType::eStateStepping; MaybeLogStateChange(new_state); m_state = new_state; m_stop_info.reason = StopReason::eStopReasonNone; if(!m_step_workaround) { // If we already hava a workaround inplace, don't reset it. Otherwise, the // destructor of the existing instance will run after the new instance has // fetched the cpu mask, and the thread will end up with the wrong mask. m_step_workaround = SingleStepWorkaround::Get(m_tid); } intptr_t data = 0; if (signo != LLDB_INVALID_SIGNAL_NUMBER) data = signo; // If hardware single-stepping is not supported, we just do a continue. The // breakpoint on the // next instruction has been setup in NativeProcessLinux::Resume. return NativeProcessLinux::PtraceWrapper( GetProcess().SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT, m_tid, nullptr, reinterpret_cast(data)); } void NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); SetStopped(); m_stop_info.reason = StopReason::eStopReasonSignal; m_stop_info.details.signal.signo = signo; m_stop_description.clear(); if (info) { switch (signo) { case SIGSEGV: case SIGBUS: case SIGFPE: case SIGILL: // In case of MIPS64 target, SI_KERNEL is generated for invalid 64bit // address. const auto reason = (info->si_signo == SIGBUS && info->si_code == SI_KERNEL) ? CrashReason::eInvalidAddress : GetCrashReason(*info); m_stop_description = GetCrashReasonString(reason, *info); break; } } } bool NativeThreadLinux::IsStopped(int *signo) { if (!StateIsStoppedState(m_state, false)) return false; // If we are stopped by a signal, return the signo. if (signo && m_state == StateType::eStateStopped && m_stop_info.reason == StopReason::eStopReasonSignal) { *signo = m_stop_info.details.signal.signo; } // Regardless, we are stopped. return true; } void NativeThreadLinux::SetStopped() { if (m_state == StateType::eStateStepping) m_step_workaround.reset(); const StateType new_state = StateType::eStateStopped; MaybeLogStateChange(new_state); m_state = new_state; m_stop_description.clear(); } void NativeThreadLinux::SetStoppedByExec() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("NativeThreadLinux::%s()", __FUNCTION__); SetStopped(); m_stop_info.reason = StopReason::eStopReasonExec; m_stop_info.details.signal.signo = SIGSTOP; } void NativeThreadLinux::SetStoppedByBreakpoint() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; m_stop_description.clear(); } void NativeThreadLinux::SetStoppedByWatchpoint(uint32_t wp_index) { SetStopped(); lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); std::ostringstream ostr; ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; ostr << wp_index; /* * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For * example: * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at * 'm', then * watch exception is generated even when 'n' is read/written. To handle this * case, * find the base address of the load/store instruction and append it in the * stop-info * packet. */ ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); m_stop_description = ostr.str(); m_stop_info.reason = StopReason::eStopReasonWatchpoint; m_stop_info.details.signal.signo = SIGTRAP; } bool NativeThreadLinux::IsStoppedAtBreakpoint() { return GetState() == StateType::eStateStopped && m_stop_info.reason == StopReason::eStopReasonBreakpoint; } bool NativeThreadLinux::IsStoppedAtWatchpoint() { return GetState() == StateType::eStateStopped && m_stop_info.reason == StopReason::eStopReasonWatchpoint; } void NativeThreadLinux::SetStoppedByTrace() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonTrace; m_stop_info.details.signal.signo = SIGTRAP; } void NativeThreadLinux::SetStoppedWithNoReason() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonNone; m_stop_info.details.signal.signo = 0; } void NativeThreadLinux::SetExited() { const StateType new_state = StateType::eStateExited; MaybeLogStateChange(new_state); m_state = new_state; m_stop_info.reason = StopReason::eStopReasonThreadExiting; } Status NativeThreadLinux::RequestStop() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); NativeProcessLinux &process = GetProcess(); lldb::pid_t pid = process.GetID(); lldb::tid_t tid = GetID(); if (log) log->Printf("NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid); Status err; errno = 0; if (::tgkill(pid, tid, SIGSTOP) != 0) { err.SetErrorToErrno(); if (log) log->Printf("NativeThreadLinux::%s tgkill(%" PRIu64 ", %" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, tid, err.AsCString()); } return err; } void NativeThreadLinux::MaybeLogStateChange(lldb::StateType new_state) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); // If we're not logging, we're done. if (!log) return; // If this is a state change to the same state, we're done. lldb::StateType old_state = m_state; if (new_state == old_state) return; - NativeProcessProtocolSP m_process_sp = m_process_wp.lock(); - lldb::pid_t pid = - m_process_sp ? m_process_sp->GetID() : LLDB_INVALID_PROCESS_ID; - - // Log it. - log->Printf("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 - ") changing from state %s to %s", - pid, GetID(), StateAsCString(old_state), - StateAsCString(new_state)); + LLDB_LOG(log, "pid={0}, tid={1}: changing from state {2} to {3}", + m_process.GetID(), GetID(), old_state, new_state); } NativeProcessLinux &NativeThreadLinux::GetProcess() { - auto process_sp = std::static_pointer_cast( - NativeThreadProtocol::GetProcess()); - assert(process_sp); - return *process_sp; + return static_cast(m_process); } Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeThreadLinux.h (revision 321194) @@ -1,117 +1,118 @@ //===-- NativeThreadLinux.h ----------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeThreadLinux_H_ #define liblldb_NativeThreadLinux_H_ #include "SingleStepCheck.h" #include "lldb/Host/common/NativeThreadProtocol.h" #include "lldb/lldb-private-forward.h" +#include #include #include #include namespace lldb_private { namespace process_linux { class NativeProcessLinux; class NativeThreadLinux : public NativeThreadProtocol { friend class NativeProcessLinux; public: - NativeThreadLinux(NativeProcessLinux *process, lldb::tid_t tid); + NativeThreadLinux(NativeProcessLinux &process, lldb::tid_t tid); // --------------------------------------------------------------------- // NativeThreadProtocol Interface // --------------------------------------------------------------------- std::string GetName() override; lldb::StateType GetState() override; bool GetStopReason(ThreadStopInfo &stop_info, std::string &description) override; NativeRegisterContextSP GetRegisterContext() override; Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; Status RemoveWatchpoint(lldb::addr_t addr) override; Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; private: // --------------------------------------------------------------------- // Interface for friend classes // --------------------------------------------------------------------- /// Resumes the thread. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. Status Resume(uint32_t signo); /// Single steps the thread. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. Status SingleStep(uint32_t signo); void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); /// Return true if the thread is stopped. /// If stopped by a signal, indicate the signo in the signo argument. /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. bool IsStopped(int *signo); void SetStoppedByExec(); void SetStoppedByBreakpoint(); void SetStoppedByWatchpoint(uint32_t wp_index); bool IsStoppedAtBreakpoint(); bool IsStoppedAtWatchpoint(); void SetStoppedByTrace(); void SetStoppedWithNoReason(); void SetExited(); Status RequestStop(); // --------------------------------------------------------------------- // Private interface // --------------------------------------------------------------------- void MaybeLogStateChange(lldb::StateType new_state); NativeProcessLinux &GetProcess(); void SetStopped(); // --------------------------------------------------------------------- // Member Variables // --------------------------------------------------------------------- lldb::StateType m_state; ThreadStopInfo m_stop_info; NativeRegisterContextSP m_reg_context_sp; std::string m_stop_description; using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; WatchpointIndexMap m_hw_break_index_map; std::unique_ptr m_step_workaround; }; typedef std::shared_ptr NativeThreadLinuxSP; } // namespace process_linux } // namespace lldb_private #endif // #ifndef liblldb_NativeThreadLinux_H_ Index: vendor/lldb/dist/source/Plugins/Process/Linux/ProcessorTrace.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/ProcessorTrace.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/Linux/ProcessorTrace.h (revision 321194) @@ -1,140 +1,141 @@ //===-- ProcessorTrace.h -------------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessorTrace_H_ #define liblldb_ProcessorTrace_H_ #include "lldb/Utility/Status.h" #include "lldb/Utility/TraceOptions.h" #include "lldb/lldb-types.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include #include +#include namespace lldb_private { namespace process_linux { // --------------------------------------------------------------------- // This class keeps track of one tracing instance of // Intel(R) Processor Trace on Linux OS. There is a map keeping track // of different tracing instances on each thread, which enables trace // gathering on a per thread level. // // The tracing instance is linked with a trace id. The trace id acts like // a key to the tracing instance and trace manipulations could be // performed using the trace id. // // The traace id could map to trace instances for a group of threads // (spanning to all the threads in the process) or a single thread. // The kernel interface for us is the perf_event_open. // --------------------------------------------------------------------- class ProcessorTraceMonitor; typedef std::unique_ptr ProcessorTraceMonitorUP; class ProcessorTraceMonitor { class munmap_delete { size_t m_length; public: munmap_delete(size_t length) : m_length(length) {} void operator()(void *ptr) { if (m_length) munmap(ptr, m_length); } }; class file_close { public: file_close() = default; void operator()(int *ptr) { if (ptr == nullptr) return; if (*ptr == -1) return; close(*ptr); std::default_delete()(ptr); } }; std::unique_ptr m_mmap_meta; std::unique_ptr m_mmap_aux; std::unique_ptr m_fd; // perf_event_mmap_page *m_mmap_base; lldb::user_id_t m_traceid; lldb::tid_t m_thread_id; // Counter to track trace instances. static lldb::user_id_t m_trace_num; void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; } Status StartTrace(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config); llvm::MutableArrayRef GetAuxBuffer(); llvm::MutableArrayRef GetDataBuffer(); ProcessorTraceMonitor() : m_mmap_meta(nullptr, munmap_delete(0)), m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()), m_traceid(LLDB_INVALID_UID), m_thread_id(LLDB_INVALID_THREAD_ID){}; void SetThreadID(lldb::tid_t tid) { m_thread_id = tid; } public: static Status GetCPUType(TraceOptions &config); static llvm::Expected Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config, bool useProcessSettings); Status ReadPerfTraceAux(llvm::MutableArrayRef &buffer, size_t offset = 0); Status ReadPerfTraceData(llvm::MutableArrayRef &buffer, size_t offset = 0); ~ProcessorTraceMonitor() = default; lldb::tid_t GetThreadID() const { return m_thread_id; } lldb::user_id_t GetTraceID() const { return m_traceid; } Status GetTraceConfig(TraceOptions &config) const; // --------------------------------------------------------------------- /// Read data from a cyclic buffer /// /// @param[in] [out] buf /// Destination buffer, the buffer will be truncated to written size. /// /// @param[in] src /// Source buffer which must be a cyclic buffer. /// /// @param[in] src_cyc_index /// The index pointer (start of the valid data in the cyclic /// buffer). /// /// @param[in] offset /// The offset to begin reading the data in the cyclic buffer. // --------------------------------------------------------------------- static void ReadCyclicBuffer(llvm::MutableArrayRef &dst, llvm::MutableArrayRef src, size_t src_cyc_index, size_t offset); }; } // namespace process_linux } // namespace lldb_private #endif Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp (revision 321194) @@ -1,938 +1,939 @@ //===-- NativeProcessNetBSD.cpp ------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeProcessNetBSD.h" // C Includes // C++ Includes // Other libraries and framework includes #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Core/State.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/common/NativeBreakpoint.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Target/Process.h" #include "llvm/Support/Errno.h" // System includes - They have to be included after framework includes because // they define some // macros which collide with variable names in other modules // clang-format off #include #include #include #include #include #include #include // clang-format on using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_netbsd; using namespace llvm; // Simple helper function to ensure flags are enabled on the given file // descriptor. static Status EnsureFDFlags(int fd, int flags) { Status error; int status = fcntl(fd, F_GETFL); if (status == -1) { error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return error; } return error; } // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- -llvm::Expected +llvm::Expected> NativeProcessNetBSD::Factory::Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop) const { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); Status status; ::pid_t pid = ProcessLauncherPosixFork() .LaunchProcess(launch_info, status) .GetProcessId(); LLDB_LOG(log, "pid = {0:x}", pid); if (status.Fail()) { LLDB_LOG(log, "failed to launch process: {0}", status); return status.ToError(); } // Wait for the child process to trap on its call to execve. int wstatus; ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); assert(wpid == pid); (void)wpid; if (!WIFSTOPPED(wstatus)) { LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", WaitStatus::Decode(wstatus)); return llvm::make_error("Could not sync with inferior process", llvm::inconvertibleErrorCode()); } LLDB_LOG(log, "inferior started, now in stopped state"); ArchSpec arch; if ((status = ResolveProcessArchitecture(pid, arch)).Fail()) return status.ToError(); // Set the architecture to the exe architecture. LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, arch.GetArchitectureName()); - std::shared_ptr process_sp(new NativeProcessNetBSD( + std::unique_ptr process_up(new NativeProcessNetBSD( pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate, arch, mainloop)); - status = process_sp->ReinitializeThreads(); + status = process_up->ReinitializeThreads(); if (status.Fail()) return status.ToError(); - for (const auto &thread_sp : process_sp->m_threads) { + for (const auto &thread_sp : process_up->m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP); } - process_sp->SetState(StateType::eStateStopped); + process_up->SetState(StateType::eStateStopped); - return process_sp; + return std::move(process_up); } -llvm::Expected NativeProcessNetBSD::Factory::Attach( +llvm::Expected> +NativeProcessNetBSD::Factory::Attach( lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop) const { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); // Retrieve the architecture for the running process. ArchSpec arch; Status status = ResolveProcessArchitecture(pid, arch); if (!status.Success()) return status.ToError(); - std::shared_ptr process_sp( + std::unique_ptr process_up( new NativeProcessNetBSD(pid, -1, native_delegate, arch, mainloop)); - status = process_sp->Attach(); + status = process_up->Attach(); if (!status.Success()) return status.ToError(); - return process_sp; + return std::move(process_up); } // ----------------------------------------------------------------------------- // Public Instance Methods // ----------------------------------------------------------------------------- NativeProcessNetBSD::NativeProcessNetBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate, const ArchSpec &arch, MainLoop &mainloop) : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) { if (m_terminal_fd != -1) { Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); assert(status.Success()); } Status status; m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); assert(m_sigchld_handle && status.Success()); } // Handles all waitpid events from the inferior process. void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) { switch (signal) { case SIGTRAP: return MonitorSIGTRAP(pid); case SIGSTOP: return MonitorSIGSTOP(pid); default: return MonitorSignal(pid, signal); } } void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid); /* Stop Tracking All Threads attached to Process */ m_threads.clear(); SetExitStatus(status, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); } void NativeProcessNetBSD::MonitorSIGSTOP(lldb::pid_t pid) { ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); // Get details on the signal raised. if (siginfo_err.Success()) { // Handle SIGSTOP from LLGS (LLDB GDB Server) if (info.psi_siginfo.si_code == SI_USER && info.psi_siginfo.si_pid == ::getpid()) { /* Stop Tracking All Threads attached to Process */ for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP, &info.psi_siginfo); } } } } void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); // Get details on the signal raised. if (siginfo_err.Fail()) { return; } switch (info.psi_siginfo.si_code) { case TRAP_BRKPT: for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByBreakpoint(); FixupBreakpointPCAsNeeded( *static_pointer_cast(thread_sp)); } SetState(StateType::eStateStopped, true); break; case TRAP_TRACE: for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedByTrace(); } SetState(StateType::eStateStopped, true); break; case TRAP_EXEC: { Status error = ReinitializeThreads(); if (error.Fail()) { SetState(StateType::eStateInvalid); return; } // Let our delegate know we have just exec'd. NotifyDidExec(); for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedByExec(); } SetState(StateType::eStateStopped, true); } break; case TRAP_DBREG: { // If a watchpoint was hit, report it uint32_t wp_index; Status error = static_pointer_cast(m_threads[info.psi_lwpid]) ->GetRegisterContext() ->GetWatchpointHitIndex(wp_index, (uintptr_t)info.psi_siginfo.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, LWP = {1}, error = {2}", GetID(), info.psi_lwpid, error); if (wp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByWatchpoint(wp_index); } SetState(StateType::eStateStopped, true); break; } // If a breakpoint was hit, report it uint32_t bp_index; error = static_pointer_cast(m_threads[info.psi_lwpid]) ->GetRegisterContext() ->GetHardwareBreakHitIndex(bp_index, (uintptr_t)info.psi_siginfo.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for hardware " "breakpoint hits, pid = {0}, LWP = {1}, error = {2}", GetID(), info.psi_lwpid, error); if (bp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByBreakpoint(); } SetState(StateType::eStateStopped, true); break; } } break; } } void NativeProcessNetBSD::MonitorSignal(lldb::pid_t pid, int signal) { ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( info.psi_siginfo.si_signo, &info.psi_siginfo); } SetState(StateType::eStateStopped, true); } Status NativeProcessNetBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, int *result) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); Status error; int ret; errno = 0; ret = ptrace(req, static_cast<::pid_t>(pid), addr, data); if (ret == -1) error.SetErrorToErrno(); if (result) *result = ret; LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret); if (error.Fail()) LLDB_LOG(log, "ptrace() failed: {0}", error); return error; } Status NativeProcessNetBSD::GetSoftwareBreakpointPCOffset( uint32_t &actual_opcode_size) { // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode[] = {0xCC}; switch (m_arch.GetMachine()) { case llvm::Triple::x86_64: actual_opcode_size = static_cast(sizeof(g_i386_opcode)); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessNetBSD::FixupBreakpointPCAsNeeded(NativeThreadNetBSD &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); Status error; // Find out the size of a breakpoint (might depend on where we are in the // code). NativeRegisterContextSP context_sp = thread.GetRegisterContext(); if (!context_sp) { error.SetErrorString("cannot get a NativeRegisterContext for the thread"); LLDB_LOG(log, "failed: {0}", error); return error; } uint32_t breakpoint_size = 0; error = GetSoftwareBreakpointPCOffset(breakpoint_size); if (error.Fail()) { LLDB_LOG(log, "GetBreakpointSize() failed: {0}", error); return error; } else LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); // First try probing for a breakpoint at a software breakpoint location: PC // - breakpoint size. const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation(); lldb::addr_t breakpoint_addr = initial_pc_addr; if (breakpoint_size > 0) { // Do not allow breakpoint probe to wrap around. if (breakpoint_addr >= breakpoint_size) breakpoint_addr -= breakpoint_size; } // Check if we stopped because of a breakpoint. NativeBreakpointSP breakpoint_sp; error = m_breakpoint_list.GetBreakpoint(breakpoint_addr, breakpoint_sp); if (!error.Success() || !breakpoint_sp) { // We didn't find one at a software probe location. Nothing to do. LLDB_LOG(log, "pid {0} no lldb breakpoint found at current pc with " "adjustment: {1}", GetID(), breakpoint_addr); return Status(); } // If the breakpoint is not a software breakpoint, nothing to do. if (!breakpoint_sp->IsSoftwareBreakpoint()) { LLDB_LOG( log, "pid {0} breakpoint found at {1:x}, not software, nothing to adjust", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // Change the program counter. LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); error = context_sp->SetPC(breakpoint_addr); if (error.Fail()) { LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(), thread.GetID(), error); return error; } return error; } Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); const auto &thread_sp = m_threads[0]; const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) { LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), thread_sp->GetID()); return Status(); } Status error; switch (action->state) { case eStateRunning: { // Run the thread, possibly feeding it the signal. error = NativeProcessNetBSD::PtraceWrapper(PT_CONTINUE, GetID(), (void *)1, action->signal); if (!error.Success()) return error; for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetRunning(); } SetState(eStateRunning, true); break; } case eStateStepping: // Run the thread, possibly feeding it the signal. error = NativeProcessNetBSD::PtraceWrapper(PT_STEP, GetID(), (void *)1, action->signal); if (!error.Success()) return error; for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStepping(); } SetState(eStateStepping, true); break; case eStateSuspended: case eStateStopped: llvm_unreachable("Unexpected state"); default: return Status("NativeProcessNetBSD::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread_sp->GetID()); } return Status(); } Status NativeProcessNetBSD::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) error.SetErrorToErrno(); return error; } Status NativeProcessNetBSD::Detach() { Status error; // Stop monitoring the inferior. m_sigchld_handle.reset(); // Tell ptrace to detach from the process. if (GetID() == LLDB_INVALID_PROCESS_ID) return error; return PtraceWrapper(PT_DETACH, GetID()); } Status NativeProcessNetBSD::Signal(int signo) { Status error; if (kill(GetID(), signo)) error.SetErrorToErrno(); return error; } Status NativeProcessNetBSD::Kill() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); Status error; switch (m_state) { case StateType::eStateInvalid: case StateType::eStateExited: case StateType::eStateCrashed: case StateType::eStateDetached: case StateType::eStateUnloaded: // Nothing to do - the process is already dead. LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), StateAsCString(m_state)); return error; case StateType::eStateConnected: case StateType::eStateAttaching: case StateType::eStateLaunching: case StateType::eStateStopped: case StateType::eStateRunning: case StateType::eStateStepping: case StateType::eStateSuspended: // We can try to kill a process in these states. break; } if (kill(GetID(), SIGKILL) != 0) { error.SetErrorToErrno(); return error; } return error; } Status NativeProcessNetBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { if (m_supports_mem_region == LazyBool::eLazyBoolNo) { // We're done. return Status("unsupported"); } Status error = PopulateMemoryRegionCache(); if (error.Fail()) { return error; } lldb::addr_t prev_base_address = 0; // FIXME start by finding the last region that is <= target address using // binary search. Data is sorted. // There can be a ton of regions on pthreads apps with lots of threads. for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); ++it) { MemoryRegionInfo &proc_entry_info = it->first; // Sanity check assumption that memory map entries are ascending. assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && "descending memory map entries detected, unexpected"); prev_base_address = proc_entry_info.GetRange().GetRangeBase(); UNUSED_IF_ASSERT_DISABLED(prev_base_address); // If the target address comes before this entry, indicate distance to // next region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetByteSize( proc_entry_info.GetRange().GetRangeBase() - load_addr); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } else if (proc_entry_info.GetRange().Contains(load_addr)) { // The target address is within the memory region we're processing here. range_info = proc_entry_info; return error; } // The target memory address comes somewhere after the region we just // parsed. } // If we made it here, we didn't find an entry that contained the given // address. Return the // load_addr as start and the amount of bytes betwwen load address and the // end of the memory as size. range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } Status NativeProcessNetBSD::PopulateMemoryRegionCache() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // If our cache is empty, pull the latest. There should always be at least // one memory region if memory region handling is supported. if (!m_mem_region_cache.empty()) { LLDB_LOG(log, "reusing {0} cached memory region entries", m_mem_region_cache.size()); return Status(); } struct kinfo_vmentry *vm; size_t count, i; vm = kinfo_getvmmap(GetID(), &count); if (vm == NULL) { m_supports_mem_region = LazyBool::eLazyBoolNo; Status error; error.SetErrorString("not supported"); return error; } for (i = 0; i < count; i++) { MemoryRegionInfo info; info.Clear(); info.GetRange().SetRangeBase(vm[i].kve_start); info.GetRange().SetRangeEnd(vm[i].kve_end); info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); if (vm[i].kve_protection & VM_PROT_READ) info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); else info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_protection & VM_PROT_WRITE) info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); else info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_protection & VM_PROT_EXECUTE) info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); else info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_path[0]) info.SetName(vm[i].kve_path); m_mem_region_cache.emplace_back( info, FileSpec(info.GetName().GetCString(), true)); } free(vm); if (m_mem_region_cache.empty()) { // No entries after attempting to read them. This shouldn't happen. // Assume we don't support map entries. LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " "for memory region metadata retrieval"); m_supports_mem_region = LazyBool::eLazyBoolNo; Status error; error.SetErrorString("not supported"); return error; } LLDB_LOG(log, "read {0} memory region entries from process {1}", m_mem_region_cache.size(), GetID()); // We support memory retrieval, remember that. m_supports_mem_region = LazyBool::eLazyBoolYes; return Status(); } Status NativeProcessNetBSD::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) { return Status("Unimplemented"); } Status NativeProcessNetBSD::DeallocateMemory(lldb::addr_t addr) { return Status("Unimplemented"); } lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; } size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); } bool NativeProcessNetBSD::GetArchitecture(ArchSpec &arch) const { arch = m_arch; return true; } Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { if (hardware) return Status("NativeProcessNetBSD does not support hardware breakpoints"); else return SetSoftwareBreakpoint(addr, size); } Status NativeProcessNetBSD::GetSoftwareBreakpointTrapOpcode( size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) { static const uint8_t g_i386_opcode[] = {0xCC}; switch (m_arch.GetMachine()) { case llvm::Triple::x86: case llvm::Triple::x86_64: trap_opcode_bytes = g_i386_opcode; actual_opcode_size = sizeof(g_i386_opcode); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) { return Status("Unimplemented"); } Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) { load_addr = LLDB_INVALID_ADDRESS; return Status(); } void NativeProcessNetBSD::SigchldHandler() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Process all pending waitpid notifications. int status; ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WALLSIG | WNOHANG); if (wait_pid == 0) return; // We are done. if (wait_pid == -1) { Status error(errno, eErrorTypePOSIX); LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); } WaitStatus wait_status = WaitStatus::Decode(status); bool exited = wait_status.type == WaitStatus::Exit || (wait_status.type == WaitStatus::Signal && wait_pid == static_cast<::pid_t>(GetID())); LLDB_LOG(log, "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", GetID(), wait_pid, status, exited); if (exited) MonitorExited(wait_pid, wait_status); else { assert(wait_status.type == WaitStatus::Stop); MonitorCallback(wait_pid, wait_status.status); } } bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) { for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); if (thread_sp->GetID() == thread_id) { // We have this thread. return true; } } // We don't have this thread. return false; } NativeThreadNetBSDSP NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); assert(!HasThreadNoLock(thread_id) && "attempted to add a thread by id that already exists"); // If this is the first thread, save it as the current thread if (m_threads.empty()) SetCurrentThreadID(thread_id); - auto thread_sp = std::make_shared(this, thread_id); + auto thread_sp = std::make_shared(*this, thread_id); m_threads.push_back(thread_sp); return thread_sp; } Status NativeProcessNetBSD::Attach() { // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. Status status = PtraceWrapper(PT_ATTACH, m_pid); if (status.Fail()) return status; int wstatus; // Need to use WALLSIG otherwise we receive an error with errno=ECHLD // At this point we should have a thread stopped if waitpid succeeds. if ((wstatus = waitpid(m_pid, NULL, WALLSIG)) < 0) return Status(errno, eErrorTypePOSIX); /* Initialize threads */ status = ReinitializeThreads(); if (status.Fail()) return status; for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP); } // Let our process instance know the thread has stopped. SetState(StateType::eStateStopped); return Status(); } Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { unsigned char *dst = static_cast(buf); struct ptrace_io_desc io; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); bytes_read = 0; io.piod_op = PIOD_READ_D; io.piod_len = size; do { io.piod_offs = (void *)(addr + bytes_read); io.piod_addr = dst + bytes_read; Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return error; bytes_read = io.piod_len; io.piod_len = size - bytes_read; } while (bytes_read < size); return Status(); } Status NativeProcessNetBSD::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { Status error = ReadMemory(addr, buf, size, bytes_read); if (error.Fail()) return error; return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); } Status NativeProcessNetBSD::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) { const unsigned char *src = static_cast(buf); Status error; struct ptrace_io_desc io; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); bytes_written = 0; io.piod_op = PIOD_WRITE_D; io.piod_len = size; do { io.piod_addr = const_cast(static_cast(src + bytes_written)); io.piod_offs = (void *)(addr + bytes_written); Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return error; bytes_written = io.piod_len; io.piod_len = size - bytes_written; } while (bytes_written < size); return error; } llvm::ErrorOr> NativeProcessNetBSD::GetAuxvData() const { /* * ELF_AUX_ENTRIES is currently restricted to kernel * ( r. 1.155 specifies 15) * * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this * information isn't needed. */ size_t auxv_size = 100 * sizeof(AuxInfo); ErrorOr> buf = llvm::MemoryBuffer::getNewMemBuffer(auxv_size); struct ptrace_io_desc io; io.piod_op = PIOD_READ_AUXV; io.piod_offs = 0; io.piod_addr = const_cast(static_cast(buf.get()->getBufferStart())); io.piod_len = auxv_size; Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return std::error_code(error.GetError(), std::generic_category()); if (io.piod_len < 1) return std::error_code(ECANCELED, std::generic_category()); return buf; } Status NativeProcessNetBSD::ReinitializeThreads() { // Clear old threads m_threads.clear(); // Initialize new thread struct ptrace_lwpinfo info = {}; Status error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info)); if (error.Fail()) { return error; } // Reinitialize from scratch threads and register them in process while (info.pl_lwpid != 0) { NativeThreadNetBSDSP thread_sp = AddThread(info.pl_lwpid); error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info)); if (error.Fail()) { return error; } } return error; } Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h (revision 321194) @@ -1,144 +1,144 @@ //===-- NativeProcessNetBSD.h --------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeProcessNetBSD_H_ #define liblldb_NativeProcessNetBSD_H_ // C++ Includes // Other libraries and framework includes #include "lldb/Core/ArchSpec.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/FileSpec.h" #include "NativeThreadNetBSD.h" #include "lldb/Host/common/NativeProcessProtocol.h" namespace lldb_private { namespace process_netbsd { /// @class NativeProcessNetBSD /// @brief Manages communication with the inferior (debugee) process. /// /// Upon construction, this class prepares and launches an inferior process for /// debugging. /// /// Changes in the inferior process state are broadcasted. class NativeProcessNetBSD : public NativeProcessProtocol { public: class Factory : public NativeProcessProtocol::Factory { public: - llvm::Expected + llvm::Expected> Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop) const override; - llvm::Expected + llvm::Expected> Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop) const override; }; // --------------------------------------------------------------------- // NativeProcessProtocol Interface // --------------------------------------------------------------------- Status Resume(const ResumeActionList &resume_actions) override; Status Halt() override; Status Detach() override; Status Signal(int signo) override; Status Kill() override; Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) override; Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override; Status DeallocateMemory(lldb::addr_t addr) override; lldb::addr_t GetSharedLibraryInfoAddress() override; size_t UpdateThreads() override; bool GetArchitecture(ArchSpec &arch) const override; Status SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) override; Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) override; Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) override; llvm::ErrorOr> GetAuxvData() const override; // --------------------------------------------------------------------- // Interface used by NativeRegisterContext-derived classes. // --------------------------------------------------------------------- static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, int data = 0, int *result = nullptr); protected: // --------------------------------------------------------------------- // NativeProcessProtocol protected interface // --------------------------------------------------------------------- Status GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override; private: MainLoop::SignalHandleUP m_sigchld_handle; ArchSpec m_arch; LazyBool m_supports_mem_region = eLazyBoolCalculate; std::vector> m_mem_region_cache; // --------------------------------------------------------------------- // Private Instance Methods // --------------------------------------------------------------------- NativeProcessNetBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate, const ArchSpec &arch, MainLoop &mainloop); bool HasThreadNoLock(lldb::tid_t thread_id); NativeThreadNetBSDSP AddThread(lldb::tid_t thread_id); void MonitorCallback(lldb::pid_t pid, int signal); void MonitorExited(lldb::pid_t pid, WaitStatus status); void MonitorSIGSTOP(lldb::pid_t pid); void MonitorSIGTRAP(lldb::pid_t pid); void MonitorSignal(lldb::pid_t pid, int signal); Status GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size); Status FixupBreakpointPCAsNeeded(NativeThreadNetBSD &thread); Status PopulateMemoryRegionCache(); void SigchldHandler(); Status Attach(); Status ReinitializeThreads(); }; } // namespace process_netbsd } // namespace lldb_private #endif // #ifndef liblldb_NativeProcessNetBSD_H_ Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp (revision 321194) @@ -1,118 +1,112 @@ //===-- NativeRegisterContextNetBSD.cpp -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeRegisterContextNetBSD.h" #include "lldb/Host/common/NativeProcessProtocol.h" using namespace lldb_private; using namespace lldb_private::process_netbsd; // clang-format off #include #include // clang-format on NativeRegisterContextNetBSD::NativeRegisterContextNetBSD( NativeThreadProtocol &native_thread, uint32_t concrete_frame_idx, RegisterInfoInterface *reg_info_interface_p) : NativeRegisterContextRegisterInfo(native_thread, concrete_frame_idx, reg_info_interface_p) {} Status NativeRegisterContextNetBSD::ReadGPR() { void *buf = GetGPRBuffer(); if (!buf) return Status("GPR buffer is NULL"); return DoReadGPR(buf); } Status NativeRegisterContextNetBSD::WriteGPR() { void *buf = GetGPRBuffer(); if (!buf) return Status("GPR buffer is NULL"); return DoWriteGPR(buf); } Status NativeRegisterContextNetBSD::ReadFPR() { void *buf = GetFPRBuffer(); if (!buf) return Status("FPR buffer is NULL"); return DoReadFPR(buf); } Status NativeRegisterContextNetBSD::WriteFPR() { void *buf = GetFPRBuffer(); if (!buf) return Status("FPR buffer is NULL"); return DoWriteFPR(buf); } Status NativeRegisterContextNetBSD::ReadDBR() { void *buf = GetDBRBuffer(); if (!buf) return Status("DBR buffer is NULL"); return DoReadDBR(buf); } Status NativeRegisterContextNetBSD::WriteDBR() { void *buf = GetDBRBuffer(); if (!buf) return Status("DBR buffer is NULL"); return DoWriteDBR(buf); } Status NativeRegisterContextNetBSD::DoReadGPR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_GETREGS, GetProcessPid(), buf, m_thread.GetID()); } Status NativeRegisterContextNetBSD::DoWriteGPR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_SETREGS, GetProcessPid(), buf, m_thread.GetID()); } Status NativeRegisterContextNetBSD::DoReadFPR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_GETFPREGS, GetProcessPid(), buf, m_thread.GetID()); } Status NativeRegisterContextNetBSD::DoWriteFPR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_SETFPREGS, GetProcessPid(), buf, m_thread.GetID()); } Status NativeRegisterContextNetBSD::DoReadDBR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_GETDBREGS, GetProcessPid(), buf, m_thread.GetID()); } Status NativeRegisterContextNetBSD::DoWriteDBR(void *buf) { return NativeProcessNetBSD::PtraceWrapper(PT_SETDBREGS, GetProcessPid(), buf, m_thread.GetID()); } NativeProcessNetBSD &NativeRegisterContextNetBSD::GetProcess() { - auto process_sp = - std::static_pointer_cast(m_thread.GetProcess()); - assert(process_sp); - return *process_sp; + return static_cast(m_thread.GetProcess()); } ::pid_t NativeRegisterContextNetBSD::GetProcessPid() { - NativeProcessNetBSD &process = GetProcess(); - lldb::pid_t pid = process.GetID(); - - return pid; + return GetProcess().GetID(); } Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp (revision 321194) @@ -1,222 +1,218 @@ //===-- NativeThreadNetBSD.cpp -------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeThreadNetBSD.h" #include "NativeRegisterContextNetBSD.h" #include "NativeProcessNetBSD.h" #include "Plugins/Process/POSIX/CrashReason.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Utility/LLDBAssert.h" #include using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_netbsd; -NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD *process, +NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD &process, lldb::tid_t tid) : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), m_stop_info(), m_reg_context_sp(), m_stop_description() {} void NativeThreadNetBSD::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo); SetStopped(); m_stop_info.reason = StopReason::eStopReasonSignal; m_stop_info.details.signal.signo = signo; m_stop_description.clear(); if (info) { switch (signo) { case SIGSEGV: case SIGBUS: case SIGFPE: case SIGILL: const auto reason = GetCrashReason(*info); m_stop_description = GetCrashReasonString(reason, *info); break; } } } void NativeThreadNetBSD::SetStoppedByBreakpoint() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; } void NativeThreadNetBSD::SetStoppedByTrace() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonTrace; m_stop_info.details.signal.signo = SIGTRAP; } void NativeThreadNetBSD::SetStoppedByExec() { SetStopped(); m_stop_info.reason = StopReason::eStopReasonExec; m_stop_info.details.signal.signo = SIGTRAP; } void NativeThreadNetBSD::SetStoppedByWatchpoint(uint32_t wp_index) { SetStopped(); lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); std::ostringstream ostr; ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; ostr << wp_index; ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); m_stop_description = ostr.str(); m_stop_info.reason = StopReason::eStopReasonWatchpoint; m_stop_info.details.signal.signo = SIGTRAP; } void NativeThreadNetBSD::SetStopped() { const StateType new_state = StateType::eStateStopped; m_state = new_state; m_stop_description.clear(); } void NativeThreadNetBSD::SetRunning() { m_state = StateType::eStateRunning; m_stop_info.reason = StopReason::eStopReasonNone; } void NativeThreadNetBSD::SetStepping() { m_state = StateType::eStateStepping; m_stop_info.reason = StopReason::eStopReasonNone; } std::string NativeThreadNetBSD::GetName() { return std::string(""); } lldb::StateType NativeThreadNetBSD::GetState() { return m_state; } bool NativeThreadNetBSD::GetStopReason(ThreadStopInfo &stop_info, std::string &description) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); description.clear(); switch (m_state) { case eStateStopped: case eStateCrashed: case eStateExited: case eStateSuspended: case eStateUnloaded: stop_info = m_stop_info; description = m_stop_description; return true; case eStateInvalid: case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateRunning: case eStateStepping: case eStateDetached: LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(), StateAsCString(m_state)); return false; } llvm_unreachable("unhandled StateType!"); } NativeRegisterContextSP NativeThreadNetBSD::GetRegisterContext() { // Return the register context if we already created it. if (m_reg_context_sp) return m_reg_context_sp; - NativeProcessProtocolSP m_process_sp = m_process_wp.lock(); - if (!m_process_sp) - return NativeRegisterContextSP(); - ArchSpec target_arch; - if (!m_process_sp->GetArchitecture(target_arch)) + if (!m_process.GetArchitecture(target_arch)) return NativeRegisterContextSP(); const uint32_t concrete_frame_idx = 0; m_reg_context_sp.reset( NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD( target_arch, *this, concrete_frame_idx)); return m_reg_context_sp; } Status NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { if (!hardware) return Status("not implemented"); if (m_state == eStateLaunching) return Status(); Status error = RemoveWatchpoint(addr); if (error.Fail()) return error; NativeRegisterContextSP reg_ctx = GetRegisterContext(); uint32_t wp_index = reg_ctx->SetHardwareWatchpoint(addr, size, watch_flags); if (wp_index == LLDB_INVALID_INDEX32) return Status("Setting hardware watchpoint failed."); m_watchpoint_index_map.insert({addr, wp_index}); return Status(); } Status NativeThreadNetBSD::RemoveWatchpoint(lldb::addr_t addr) { auto wp = m_watchpoint_index_map.find(addr); if (wp == m_watchpoint_index_map.end()) return Status(); uint32_t wp_index = wp->second; m_watchpoint_index_map.erase(wp); if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) return Status(); return Status("Clearing hardware watchpoint failed."); } Status NativeThreadNetBSD::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { if (m_state == eStateLaunching) return Status(); Status error = RemoveHardwareBreakpoint(addr); if (error.Fail()) return error; NativeRegisterContextSP reg_ctx = GetRegisterContext(); uint32_t bp_index = reg_ctx->SetHardwareBreakpoint(addr, size); if (bp_index == LLDB_INVALID_INDEX32) return Status("Setting hardware breakpoint failed."); m_hw_break_index_map.insert({addr, bp_index}); return Status(); } Status NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { auto bp = m_hw_break_index_map.find(addr); if (bp == m_hw_break_index_map.end()) return Status(); uint32_t bp_index = bp->second; if (GetRegisterContext()->ClearHardwareBreakpoint(bp_index)) { m_hw_break_index_map.erase(bp); return Status(); } return Status("Clearing hardware breakpoint failed."); } Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h (revision 321194) @@ -1,80 +1,81 @@ //===-- NativeThreadNetBSD.h ---------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeThreadNetBSD_H_ #define liblldb_NativeThreadNetBSD_H_ #include "lldb/Host/common/NativeThreadProtocol.h" +#include #include #include namespace lldb_private { namespace process_netbsd { class NativeProcessNetBSD; class NativeThreadNetBSD : public NativeThreadProtocol { friend class NativeProcessNetBSD; public: - NativeThreadNetBSD(NativeProcessNetBSD *process, lldb::tid_t tid); + NativeThreadNetBSD(NativeProcessNetBSD &process, lldb::tid_t tid); // --------------------------------------------------------------------- // NativeThreadProtocol Interface // --------------------------------------------------------------------- std::string GetName() override; lldb::StateType GetState() override; bool GetStopReason(ThreadStopInfo &stop_info, std::string &description) override; NativeRegisterContextSP GetRegisterContext() override; Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; Status RemoveWatchpoint(lldb::addr_t addr) override; Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; private: // --------------------------------------------------------------------- // Interface for friend classes // --------------------------------------------------------------------- void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); void SetStoppedByBreakpoint(); void SetStoppedByTrace(); void SetStoppedByExec(); void SetStoppedByWatchpoint(uint32_t wp_index); void SetStopped(); void SetRunning(); void SetStepping(); // --------------------------------------------------------------------- // Member Variables // --------------------------------------------------------------------- lldb::StateType m_state; ThreadStopInfo m_stop_info; NativeRegisterContextSP m_reg_context_sp; std::string m_stop_description; using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; WatchpointIndexMap m_hw_break_index_map; }; typedef std::shared_ptr NativeThreadNetBSDSP; } // namespace process_netbsd } // namespace lldb_private #endif // #ifndef liblldb_NativeThreadNetBSD_H_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (revision 321194) @@ -1,3434 +1,3316 @@ //===-- GDBRemoteCommunicationServerLLGS.cpp --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "lldb/Host/Config.h" #include "GDBRemoteCommunicationServerLLGS.h" #include "lldb/Utility/StreamGDBRemote.h" // C Includes // C++ Includes #include #include #include // Other libraries and framework includes #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Debug.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeThreadProtocol.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UriParser.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/ScopedPrinter.h" // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; using namespace llvm; //---------------------------------------------------------------------- // GDBRemote Errors //---------------------------------------------------------------------- namespace { enum GDBRemoteServerError { // Set to the first unused error number in literal form below eErrorFirst = 29, eErrorNoProcess = eErrorFirst, eErrorResume, eErrorExitStatus }; } //---------------------------------------------------------------------- // GDBRemoteCommunicationServerLLGS constructor //---------------------------------------------------------------------- GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( MainLoop &mainloop, const NativeProcessProtocol::Factory &process_factory) : GDBRemoteCommunicationServerCommon("gdb-remote.server", "gdb-remote.server.rx_packet"), m_mainloop(mainloop), m_process_factory(process_factory), m_stdio_communication("process.stdio") { RegisterPacketHandlers(); } void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C, &GDBRemoteCommunicationServerLLGS::Handle_C); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c, &GDBRemoteCommunicationServerLLGS::Handle_c); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D, &GDBRemoteCommunicationServerLLGS::Handle_D); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H, &GDBRemoteCommunicationServerLLGS::Handle_H); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I, &GDBRemoteCommunicationServerLLGS::Handle_I); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_interrupt, &GDBRemoteCommunicationServerLLGS::Handle_interrupt); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_m, &GDBRemoteCommunicationServerLLGS::Handle_memory_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, &GDBRemoteCommunicationServerLLGS::Handle_M); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, &GDBRemoteCommunicationServerLLGS::Handle_p); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, &GDBRemoteCommunicationServerLLGS::Handle_P); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, &GDBRemoteCommunicationServerLLGS::Handle_qC); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress, &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo, &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported, &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qProcessInfo, &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qRegisterInfo, &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState, &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState, &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR, &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qsThreadInfo, &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read, &GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s, &GDBRemoteCommunicationServerLLGS::Handle_s); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_stop_reason, &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ? RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vAttach, &GDBRemoteCommunicationServerLLGS::Handle_vAttach); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont, &GDBRemoteCommunicationServerLLGS::Handle_vCont); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont_actions, &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_x, &GDBRemoteCommunicationServerLLGS::Handle_memory_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z, &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceStart, &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead, &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead, &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceStop, &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead, &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead); RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt, bool &quit) { quit = true; return this->Handle_k(packet); }); } Status GDBRemoteCommunicationServerLLGS::SetLaunchArguments(const char *const args[], int argc) { if ((argc < 1) || !args || !args[0] || !args[0][0]) return Status("%s: no process command line specified to launch", __FUNCTION__); m_process_launch_info.SetArguments(const_cast(args), true); return Status(); } Status GDBRemoteCommunicationServerLLGS::SetLaunchFlags(unsigned int launch_flags) { m_process_launch_info.GetFlags().Set(launch_flags); return Status(); } Status GDBRemoteCommunicationServerLLGS::LaunchProcess() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!m_process_launch_info.GetArguments().GetArgumentCount()) return Status("%s: no process command line specified to launch", __FUNCTION__); const bool should_forward_stdio = m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr || m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr; m_process_launch_info.SetLaunchInSeparateProcessGroup(true); m_process_launch_info.GetFlags().Set(eLaunchFlagDebug); const bool default_to_use_pty = true; m_process_launch_info.FinalizeFileActions(nullptr, default_to_use_pty); { std::lock_guard guard(m_debugged_process_mutex); - assert(!m_debugged_process_sp && "lldb-server creating debugged " + assert(!m_debugged_process_up && "lldb-server creating debugged " "process but one already exists"); auto process_or = m_process_factory.Launch(m_process_launch_info, *this, m_mainloop); if (!process_or) { Status status(process_or.takeError()); llvm::errs() << llvm::formatv( "failed to launch executable `{0}`: {1}", m_process_launch_info.GetArguments().GetArgumentAtIndex(0), status); return status; } - m_debugged_process_sp = *process_or; + m_debugged_process_up = std::move(*process_or); } // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol // as needed. // llgs local-process debugging may specify PTY paths, which will make these // file actions non-null // process launch -i/e/o will also make these file actions non-null // nullptr means that the traffic is expected to flow over gdb-remote protocol if (should_forward_stdio) { // nullptr means it's not redirected to file or pty (in case of LLGS local) // at least one of stdio will be transferred pty<->gdb-remote // we need to give the pty master handle to this object to read and/or write - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " setting up stdout/stderr redirection via $O gdb-remote commands", - __FUNCTION__, m_debugged_process_sp->GetID()); + LLDB_LOG(log, + "pid = {0}: setting up stdout/stderr redirection via $O " + "gdb-remote commands", + m_debugged_process_up->GetID()); // Setup stdout/stderr mapping from inferior to $O - auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor(); + auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor(); if (terminal_fd >= 0) { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " "inferior STDIO fd to %d", __FUNCTION__, terminal_fd); Status status = SetSTDIOFileDescriptor(terminal_fd); if (status.Fail()) return status; } else { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " "inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); } } else { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " skipping stdout/stderr redirection via $O: inferior will " - "communicate over client-provided file descriptors", - __FUNCTION__, m_debugged_process_sp->GetID()); + LLDB_LOG(log, + "pid = {0} skipping stdout/stderr redirection via $O: inferior " + "will communicate over client-provided file descriptors", + m_debugged_process_up->GetID()); } printf("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments().GetArgumentAtIndex(0), - m_debugged_process_sp->GetID()); + m_debugged_process_up->GetID()); return Status(); } Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, __FUNCTION__, pid); // Before we try to attach, make sure we aren't already monitoring something // else. - if (m_debugged_process_sp && - m_debugged_process_sp->GetID() != LLDB_INVALID_PROCESS_ID) + if (m_debugged_process_up && + m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID) return Status("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", - pid, m_debugged_process_sp->GetID()); + pid, m_debugged_process_up->GetID()); // Try to attach. auto process_or = m_process_factory.Attach(pid, *this, m_mainloop); if (!process_or) { Status status(process_or.takeError()); llvm::errs() << llvm::formatv("failed to attach to process {0}: {1}", pid, status); return status; } - m_debugged_process_sp = *process_or; + m_debugged_process_up = std::move(*process_or); // Setup stdout/stderr mapping from inferior. - auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor(); + auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor(); if (terminal_fd >= 0) { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " "inferior STDIO fd to %d", __FUNCTION__, terminal_fd); Status status = SetSTDIOFileDescriptor(terminal_fd); if (status.Fail()) return status; } else { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " "inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); } printf("Attached to process %" PRIu64 "...\n", pid); return Status(); } void GDBRemoteCommunicationServerLLGS::InitializeDelegate( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " "NativeProcessProtocol pid %" PRIu64 ", current state: %s", __FUNCTION__, process->GetID(), StateAsCString(process->GetState())); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendWResponse( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // send W notification auto wait_status = process->GetExitStatus(); if (!wait_status) { LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status", process->GetID()); StreamGDBRemote response; response.PutChar('E'); response.PutHex8(GDBRemoteServerError::eErrorExitStatus); return SendPacketNoLock(response.GetString()); } LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(), *wait_status); StreamGDBRemote response; response.Format("{0:g}", *wait_status); return SendPacketNoLock(response.GetString()); } static void AppendHexValue(StreamString &response, const uint8_t *buf, uint32_t buf_size, bool swap) { int64_t i; if (swap) { for (i = buf_size - 1; i >= 0; i--) response.PutHex8(buf[i]); } else { for (i = 0; i < buf_size; i++) response.PutHex8(buf[i]); } } static void WriteRegisterValueInHexFixedWidth( StreamString &response, NativeRegisterContextSP ®_ctx_sp, const RegisterInfo ®_info, const RegisterValue *reg_value_p, lldb::ByteOrder byte_order) { RegisterValue reg_value; if (!reg_value_p) { Status error = reg_ctx_sp->ReadRegister(®_info, reg_value); if (error.Success()) reg_value_p = ®_value; // else log. } if (reg_value_p) { AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(), reg_value_p->GetByteSize(), byte_order == lldb::eByteOrderLittle); } else { // Zero-out any unreadable values. if (reg_info.byte_size > 0) { std::basic_string zeros(reg_info.byte_size, '\0'); AppendHexValue(response, zeros.data(), zeros.size(), false); } } } static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); if (!reg_ctx_sp) return nullptr; JSONObject::SP register_object_sp = std::make_shared(); #ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET // Expedite all registers in the first register set (i.e. should be GPRs) that // are not contained in other registers. const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0); if (!reg_set_p) return nullptr; for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { uint32_t reg_num = *reg_num_p; #else // Expedite only a couple of registers until we figure out why sending // registers is // expensive. static const uint32_t k_expedited_registers[] = { LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM}; for (const uint32_t *generic_reg_p = k_expedited_registers; *generic_reg_p != LLDB_INVALID_REGNUM; ++generic_reg_p) { uint32_t reg_num = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, *generic_reg_p); if (reg_num == LLDB_INVALID_REGNUM) continue; // Target does not support the given register. #endif const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(reg_num); if (reg_info_p == nullptr) { if (log) log->Printf( "%s failed to get register info for register index %" PRIu32, __FUNCTION__, reg_num); continue; } if (reg_info_p->value_regs != nullptr) continue; // Only expedite registers that are not contained in other // registers. RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Fail()) { if (log) log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", reg_num, error.AsCString()); continue; } StreamString stream; WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, ®_value, lldb::eByteOrderBig); register_object_sp->SetObject( llvm::to_string(reg_num), std::make_shared(stream.GetString())); } return register_object_sp; } static const char *GetStopReasonString(StopReason stop_reason) { switch (stop_reason) { case eStopReasonTrace: return "trace"; case eStopReasonBreakpoint: return "breakpoint"; case eStopReasonWatchpoint: return "watchpoint"; case eStopReasonSignal: return "signal"; case eStopReasonException: return "exception"; case eStopReasonExec: return "exec"; case eStopReasonInstrumentation: case eStopReasonInvalid: case eStopReasonPlanComplete: case eStopReasonThreadExiting: case eStopReasonNone: break; // ignored } return nullptr; } static JSONArray::SP GetJSONThreadsInfo(NativeProcessProtocol &process, bool abridged) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); JSONArray::SP threads_array_sp = std::make_shared(); // Ensure we can get info on the given thread. uint32_t thread_idx = 0; for (NativeThreadProtocolSP thread_sp; (thread_sp = process.GetThreadAtIndex(thread_idx)) != nullptr; ++thread_idx) { lldb::tid_t tid = thread_sp->GetID(); // Grab the reason this thread stopped. struct ThreadStopInfo tid_stop_info; std::string description; if (!thread_sp->GetStopReason(tid_stop_info, description)) return nullptr; const int signum = tid_stop_info.details.signal.signo; if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, __FUNCTION__, process.GetID(), tid, signum, tid_stop_info.reason, tid_stop_info.details.exception.type); } JSONObject::SP thread_obj_sp = std::make_shared(); threads_array_sp->AppendObject(thread_obj_sp); if (!abridged) { if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread_sp)) thread_obj_sp->SetObject("registers", registers_sp); } thread_obj_sp->SetObject("tid", std::make_shared(tid)); if (signum != 0) thread_obj_sp->SetObject("signal", std::make_shared(signum)); const std::string thread_name = thread_sp->GetName(); if (!thread_name.empty()) thread_obj_sp->SetObject("name", std::make_shared(thread_name)); if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason)) thread_obj_sp->SetObject("reason", std::make_shared(stop_reason_str)); if (!description.empty()) thread_obj_sp->SetObject("description", std::make_shared(description)); if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) { thread_obj_sp->SetObject( "metype", std::make_shared(tid_stop_info.details.exception.type)); JSONArray::SP medata_array_sp = std::make_shared(); for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { medata_array_sp->AppendObject(std::make_shared( tid_stop_info.details.exception.data[i])); } thread_obj_sp->SetObject("medata", medata_array_sp); } // TODO: Expedite interesting regions of inferior memory } return threads_array_sp; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a debugged process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(50); - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64 - " tid %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID(), tid); + LLDB_LOG(log, "preparing packet for pid {0} tid {1}", + m_debugged_process_up->GetID(), tid); // Ensure we can get info on the given thread. - NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadByID(tid)); + NativeThreadProtocolSP thread_sp(m_debugged_process_up->GetThreadByID(tid)); if (!thread_sp) return SendErrorResponse(51); // Grab the reason this thread stopped. struct ThreadStopInfo tid_stop_info; std::string description; if (!thread_sp->GetStopReason(tid_stop_info, description)) return SendErrorResponse(52); // FIXME implement register handling for exec'd inferiors. // if (tid_stop_info.reason == eStopReasonExec) // { // const bool force = true; // InitializeRegisters(force); // } StreamString response; // Output the T packet with the thread response.PutChar('T'); int signum = tid_stop_info.details.signal.signo; - if (log) { - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " tid %" PRIu64 - " got signal signo = %d, reason = %d, exc_type = %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID(), tid, signum, - tid_stop_info.reason, tid_stop_info.details.exception.type); - } + LLDB_LOG( + log, + "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}", + m_debugged_process_up->GetID(), tid, signum, int(tid_stop_info.reason), + tid_stop_info.details.exception.type); // Print the signal number. response.PutHex8(signum & 0xff); // Include the tid. response.Printf("thread:%" PRIx64 ";", tid); // Include the thread name if there is one. const std::string thread_name = thread_sp->GetName(); if (!thread_name.empty()) { size_t thread_name_len = thread_name.length(); if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) { response.PutCString("name:"); response.PutCString(thread_name); } else { // The thread name contains special chars, send as hex bytes. response.PutCString("hexname:"); response.PutCStringAsRawHex8(thread_name.c_str()); } response.PutChar(';'); } // 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) { response.PutCString("threads:"); uint32_t thread_index = 0; NativeThreadProtocolSP listed_thread_sp; for (listed_thread_sp = - m_debugged_process_sp->GetThreadAtIndex(thread_index); + m_debugged_process_up->GetThreadAtIndex(thread_index); listed_thread_sp; ++thread_index, - listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex( + listed_thread_sp = m_debugged_process_up->GetThreadAtIndex( thread_index)) { if (thread_index > 0) response.PutChar(','); response.Printf("%" PRIx64, listed_thread_sp->GetID()); } response.PutChar(';'); // 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 (thread_index > 0) { const bool threads_with_valid_stop_info_only = true; JSONArray::SP threads_info_sp = GetJSONThreadsInfo( - *m_debugged_process_sp, threads_with_valid_stop_info_only); + *m_debugged_process_up, threads_with_valid_stop_info_only); if (threads_info_sp) { response.PutCString("jstopinfo:"); StreamString unescaped_response; threads_info_sp->Write(unescaped_response); response.PutCStringAsRawHex8(unescaped_response.GetData()); response.PutChar(';'); - } else if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to prepare a " - "jstopinfo field for pid %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); + } else + LLDB_LOG(log, "failed to prepare a jstopinfo field for pid {0}", + m_debugged_process_up->GetID()); } uint32_t i = 0; response.PutCString("thread-pcs"); char delimiter = ':'; for (NativeThreadProtocolSP thread_sp; - (thread_sp = m_debugged_process_sp->GetThreadAtIndex(i)) != nullptr; + (thread_sp = m_debugged_process_up->GetThreadAtIndex(i)) != nullptr; ++i) { NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); if (!reg_ctx_sp) continue; uint32_t reg_to_read = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(reg_to_read); RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Fail()) { if (log) log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", reg_to_read, error.AsCString()); continue; } response.PutChar(delimiter); delimiter = ','; WriteRegisterValueInHexFixedWidth(response, reg_ctx_sp, *reg_info_p, ®_value, endian::InlHostByteOrder()); } response.PutChar(';'); } // // Expedite registers. // // Grab the register context. NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); if (reg_ctx_sp) { // Expedite all registers in the first register set (i.e. should be GPRs) // that are not contained in other registers. const RegisterSet *reg_set_p; if (reg_ctx_sp->GetRegisterSetCount() > 0 && ((reg_set_p = reg_ctx_sp->GetRegisterSet(0)) != nullptr)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s expediting registers " "from set '%s' (registers set count: %zu)", __FUNCTION__, reg_set_p->name ? reg_set_p->name : "", reg_set_p->num_registers); for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(*reg_num_p); if (reg_info_p == nullptr) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get " "register info for register set '%s', register index " "%" PRIu32, __FUNCTION__, reg_set_p->name ? reg_set_p->name : "", *reg_num_p); } else if (reg_info_p->value_regs == nullptr) { // Only expediate registers that are not contained in other registers. RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Success()) { response.Printf("%.02x:", *reg_num_p); WriteRegisterValueInHexFixedWidth(response, reg_ctx_sp, *reg_info_p, ®_value, lldb::eByteOrderBig); response.PutChar(';'); } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to read " "register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", *reg_num_p, error.AsCString()); } } } } } const char *reason_str = GetStopReasonString(tid_stop_info.reason); if (reason_str != nullptr) { response.Printf("reason:%s;", reason_str); } if (!description.empty()) { // Description may contains special chars, send as hex bytes. response.PutCString("description:"); response.PutCStringAsRawHex8(description.c_str()); response.PutChar(';'); } else if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) { response.PutCString("metype:"); response.PutHex64(tid_stop_info.details.exception.type); response.PutCString(";mecount:"); response.PutHex32(tid_stop_info.details.exception.data_count); response.PutChar(';'); for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { response.PutCString("medata:"); response.PutHex64(tid_stop_info.details.exception.data[i]); response.PutChar(';'); } } return SendPacketNoLock(response.GetString()); } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); PacketResult result = SendStopReasonForState(StateType::eStateExited); if (result != PacketResult::Success) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " "notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID()); } // Close the pipe to the inferior terminal i/o if we launched it // and set one up. MaybeCloseInferiorTerminalConnection(); // We are ready to exit the debug monitor. m_exit_now = true; } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); // Send the stop reason unless this is the stop after the // launch or attach. switch (m_inferior_prev_state) { case eStateLaunching: case eStateAttaching: // Don't send anything per debugserver behavior. break; default: // In all other cases, send the stop reason. PacketResult result = SendStopReasonForState(StateType::eStateStopped); if (result != PacketResult::Success) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " "notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID()); } break; } } void GDBRemoteCommunicationServerLLGS::ProcessStateChanged( NativeProcessProtocol *process, lldb::StateType state) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " "NativeProcessProtocol pid %" PRIu64 ", state: %s", __FUNCTION__, process->GetID(), StateAsCString(state)); } switch (state) { case StateType::eStateRunning: StartSTDIOForwarding(); break; case StateType::eStateStopped: // Make sure we get all of the pending stdout/stderr from the inferior // and send it to the lldb host before we send the state change // notification SendProcessOutput(); // Then stop the forwarding, so that any late output (see llvm.org/pr25652) // does not // interfere with our protocol. StopSTDIOForwarding(); HandleInferiorState_Stopped(process); break; case StateType::eStateExited: // Same as above SendProcessOutput(); StopSTDIOForwarding(); HandleInferiorState_Exited(process); break; default: if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s didn't handle state " "change for pid %" PRIu64 ", new state: %s", __FUNCTION__, process->GetID(), StateAsCString(state)); } break; } // Remember the previous state reported to us. m_inferior_prev_state = state; } void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) { ClearProcessSpecificData(); } void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() { Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_COMM)); if (!m_handshake_completed) { if (!HandshakeWithClient()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with " "client failed, exiting", __FUNCTION__); m_mainloop.RequestTermination(); return; } m_handshake_completed = true; } bool interrupt = false; bool done = false; Status error; while (true) { const PacketResult result = GetPacketAndSendResponse( std::chrono::microseconds(0), error, interrupt, done); if (result == PacketResult::ErrorReplyTimeout) break; // No more packets in the queue if ((result != PacketResult::Success)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet " "failed: %s", __FUNCTION__, error.AsCString()); m_mainloop.RequestTermination(); break; } } } Status GDBRemoteCommunicationServerLLGS::InitializeConnection( std::unique_ptr &&connection) { IOObjectSP read_object_sp = connection->GetReadObject(); GDBRemoteCommunicationServer::SetConnection(connection.release()); Status error; m_network_handle_up = m_mainloop.RegisterReadObject( read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); }, error); return error; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer, uint32_t len) { if ((buffer == nullptr) || (len == 0)) { // Nothing to send. return PacketResult::Success; } StreamString response; response.PutChar('O'); response.PutBytesAsRawHex8(buffer, len); return SendPacketNoLock(response.GetString()); } Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) { Status error; // Set up the reading/handling of process I/O std::unique_ptr conn_up( new ConnectionFileDescriptor(fd, true)); if (!conn_up) { error.SetErrorString("failed to create ConnectionFileDescriptor"); return error; } m_stdio_communication.SetCloseOnEOF(false); m_stdio_communication.SetConnection(conn_up.release()); if (!m_stdio_communication.IsConnected()) { error.SetErrorString( "failed to set connection for inferior I/O communication"); return error; } return Status(); } void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() { // Don't forward if not connected (e.g. when attaching). if (!m_stdio_communication.IsConnected()) return; Status error; lldbassert(!m_stdio_handle_up); m_stdio_handle_up = m_mainloop.RegisterReadObject( m_stdio_communication.GetConnection()->GetReadObject(), [this](MainLoopBase &) { SendProcessOutput(); }, error); if (!m_stdio_handle_up) { // Not much we can do about the failure. Log it and continue without // forwarding. if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio " "forwarding: %s", __FUNCTION__, error.AsCString()); } } void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() { m_stdio_handle_up.reset(); } void GDBRemoteCommunicationServerLLGS::SendProcessOutput() { char buffer[1024]; ConnectionStatus status; Status error; while (true) { size_t bytes_read = m_stdio_communication.Read( buffer, sizeof buffer, std::chrono::microseconds(0), status, &error); switch (status) { case eConnectionStatusSuccess: SendONotification(buffer, bytes_read); break; case eConnectionStatusLostConnection: case eConnectionStatusEndOfFile: case eConnectionStatusError: case eConnectionStatusNoConnection: if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio " "forwarding as communication returned status %d (error: " "%s)", __FUNCTION__, status, error.AsCString()); m_stdio_handle_up.reset(); return; case eConnectionStatusInterrupted: case eConnectionStatusTimedOut: return; } } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jTraceStart( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); if (!packet.ConsumeFront("jTraceStart:")) return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); TraceOptions options; uint64_t type = std::numeric_limits::max(); uint64_t buffersize = std::numeric_limits::max(); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint64_t metabuffersize = std::numeric_limits::max(); auto json_object = StructuredData::ParseJSON(packet.Peek()); if (!json_object || json_object->GetType() != lldb::eStructuredDataTypeDictionary) return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); auto json_dict = json_object->GetAsDictionary(); json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize); options.setMetaDataBufferSize(metabuffersize); json_dict->GetValueForKeyAsInteger("buffersize", buffersize); options.setTraceBufferSize(buffersize); json_dict->GetValueForKeyAsInteger("type", type); options.setType(static_cast(type)); json_dict->GetValueForKeyAsInteger("threadid", tid); options.setThreadID(tid); StructuredData::ObjectSP custom_params_sp = json_dict->GetValueForKey("params"); if (custom_params_sp && custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary) return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); options.setTraceParams( static_pointer_cast(custom_params_sp)); if (buffersize == std::numeric_limits::max() || type != lldb::TraceType::eTraceTypeProcessorTrace) { LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize, type); return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet "); } Status error; lldb::user_id_t uid = LLDB_INVALID_UID; - uid = m_debugged_process_sp->StartTrace(options, error); + uid = m_debugged_process_up->StartTrace(options, error); LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError()); if (error.Fail()) return SendErrorResponse(error); StreamGDBRemote response; response.Printf("%" PRIx64, uid); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jTraceStop( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); if (!packet.ConsumeFront("jTraceStop:")) return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); lldb::user_id_t uid = LLDB_INVALID_UID; lldb::tid_t tid = LLDB_INVALID_THREAD_ID; auto json_object = StructuredData::ParseJSON(packet.Peek()); if (!json_object || json_object->GetType() != lldb::eStructuredDataTypeDictionary) return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); auto json_dict = json_object->GetAsDictionary(); if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); json_dict->GetValueForKeyAsInteger("threadid", tid); - Status error = m_debugged_process_sp->StopTrace(uid, tid); + Status error = m_debugged_process_up->StopTrace(uid, tid); if (error.Fail()) return SendErrorResponse(error); return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); if (!packet.ConsumeFront("jTraceConfigRead:")) return SendIllFormedResponse(packet, "jTraceConfigRead: Ill formed packet "); lldb::user_id_t uid = LLDB_INVALID_UID; lldb::tid_t threadid = LLDB_INVALID_THREAD_ID; auto json_object = StructuredData::ParseJSON(packet.Peek()); if (!json_object || json_object->GetType() != lldb::eStructuredDataTypeDictionary) return SendIllFormedResponse(packet, "jTraceConfigRead: Ill formed packet "); auto json_dict = json_object->GetAsDictionary(); if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) return SendIllFormedResponse(packet, "jTraceConfigRead: Ill formed packet "); json_dict->GetValueForKeyAsInteger("threadid", threadid); TraceOptions options; StreamGDBRemote response; options.setThreadID(threadid); - Status error = m_debugged_process_sp->GetTraceConfig(uid, options); + Status error = m_debugged_process_up->GetTraceConfig(uid, options); if (error.Fail()) return SendErrorResponse(error); StreamGDBRemote escaped_response; StructuredData::Dictionary json_packet; json_packet.AddIntegerItem("type", options.getType()); json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize()); json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize()); StructuredData::DictionarySP custom_params = options.getTraceParams(); if (custom_params) json_packet.AddItem("params", custom_params); StreamString json_string; json_packet.Dump(json_string, false); escaped_response.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); return SendPacketNoLock(escaped_response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jTraceRead( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); enum PacketType { MetaData, BufferData }; PacketType tracetype = MetaData; if (packet.ConsumeFront("jTraceBufferRead:")) tracetype = BufferData; else if (packet.ConsumeFront("jTraceMetaRead:")) tracetype = MetaData; else { return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); } lldb::user_id_t uid = LLDB_INVALID_UID; uint64_t byte_count = std::numeric_limits::max(); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint64_t offset = std::numeric_limits::max(); auto json_object = StructuredData::ParseJSON(packet.Peek()); if (!json_object || json_object->GetType() != lldb::eStructuredDataTypeDictionary) return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); auto json_dict = json_object->GetAsDictionary(); if (!json_dict->GetValueForKeyAsInteger("traceid", uid) || !json_dict->GetValueForKeyAsInteger("offset", offset) || !json_dict->GetValueForKeyAsInteger("buffersize", byte_count)) return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); json_dict->GetValueForKeyAsInteger("threadid", tid); // Allocate the response buffer. std::unique_ptr buffer (new (std::nothrow) uint8_t[byte_count]); if (!buffer) return SendErrorResponse(0x78); StreamGDBRemote response; Status error; llvm::MutableArrayRef buf(buffer.get(), byte_count); if (tracetype == BufferData) - error = m_debugged_process_sp->GetData(uid, tid, buf, offset); + error = m_debugged_process_up->GetData(uid, tid, buf, offset); else if (tracetype == MetaData) - error = m_debugged_process_sp->GetMetaData(uid, tid, buf, offset); + error = m_debugged_process_up->GetMetaData(uid, tid, buf, offset); if (error.Fail()) return SendErrorResponse(error); for (auto i : buf) response.PutHex8(i); StreamGDBRemote escaped_response; escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); return SendPacketNoLock(escaped_response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); - lldb::pid_t pid = m_debugged_process_sp->GetID(); + lldb::pid_t pid = m_debugged_process_up->GetID(); if (pid == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(1); ProcessInstanceInfo proc_info; if (!Host::GetProcessInfo(pid, proc_info)) return SendErrorResponse(1); StreamString response; CreateProcessInfoResponse_DebugServerStyle(proc_info, response); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. - lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID(); + lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); SetCurrentThreadID(tid); - NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetCurrentThread(); + NativeThreadProtocolSP thread_sp = m_debugged_process_up->GetCurrentThread(); if (!thread_sp) return SendErrorResponse(69); StreamString response; response.Printf("QC%" PRIx64, thread_sp->GetID()); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); StopSTDIOForwarding(); - if (!m_debugged_process_sp) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s No debugged process found.", - __FUNCTION__); + if (!m_debugged_process_up) { + LLDB_LOG(log, "No debugged process found."); return PacketResult::Success; } - Status error = m_debugged_process_sp->Kill(); - if (error.Fail() && log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to kill debugged " - "process %" PRIu64 ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + Status error = m_debugged_process_up->Kill(); + if (error.Fail()) + LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", + m_debugged_process_up->GetID(), error); // No OK response for kill packet. // return SendOKResponse (); return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("QSetDisableASLR:")); if (packet.GetU32(0)) m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR); else m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("QSetWorkingDir:")); std::string path; packet.GetHexByteString(path); m_process_launch_info.SetWorkingDirectory(FileSpec{path, true}); return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir( StringExtractorGDBRemote &packet) { FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()}; if (working_dir) { StreamString response; response.PutCStringAsRawHex8(working_dir.GetCString()); return SendPacketNoLock(response.GetString()); } return SendErrorResponse(14); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); // Ensure we have a native process. - if (!m_debugged_process_sp) { + if (!m_debugged_process_up) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " "shared pointer", __FUNCTION__); return SendErrorResponse(0x36); } // Pull out the signal number. packet.SetFilePos(::strlen("C")); if (packet.GetBytesLeft() < 1) { // Shouldn't be using a C without a signal. return SendIllFormedResponse(packet, "C packet specified without signal."); } const uint32_t signo = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (signo == std::numeric_limits::max()) return SendIllFormedResponse(packet, "failed to parse signal number"); // Handle optional continue address. if (packet.GetBytesLeft() > 0) { // FIXME add continue at address support for $C{signo}[;{continue-address}]. if (*packet.Peek() == ';') return SendUnimplementedResponse(packet.GetStringRef().c_str()); else return SendIllFormedResponse( packet, "unexpected content after $C{signal-number}"); } ResumeActionList resume_actions(StateType::eStateRunning, 0); Status error; // We have two branches: what to do if a continue thread is specified (in // which case we target // sending the signal to that thread), or when we don't have a continue thread // set (in which // case we send a signal to the process). // TODO discuss with Greg Clayton, make sure this makes sense. lldb::tid_t signal_tid = GetContinueThreadID(); if (signal_tid != LLDB_INVALID_THREAD_ID) { // The resume action for the continue thread (or all threads if a continue // thread is not set). ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning, static_cast(signo)}; // Add the action for the continue thread (or all threads when the continue // thread isn't present). resume_actions.Append(action); } else { // Send the signal to the process since we weren't targeting a specific // continue thread with the signal. - error = m_debugged_process_sp->Signal(signo); + error = m_debugged_process_up->Signal(signo); if (error.Fail()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send " - "signal for process %" PRIu64 ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "failed to send signal for process {0}: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x52); } } // Resume the threads. - error = m_debugged_process_sp->Resume(resume_actions); + error = m_debugged_process_up->Resume(resume_actions); if (error.Fail()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to resume " - "threads for process %" PRIu64 ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "failed to resume threads for process {0}: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x38); } // Don't send an "OK" packet; response is the stopped/exited message. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); packet.SetFilePos(packet.GetFilePos() + ::strlen("c")); // For now just support all continue. const bool has_continue_address = (packet.GetBytesLeft() > 0); if (has_continue_address) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s not implemented for " - "c{address} variant [%s remains]", - __FUNCTION__, packet.Peek()); + LLDB_LOG(log, "not implemented for c[address] variant [{0} remains]", + packet.Peek()); return SendUnimplementedResponse(packet.GetStringRef().c_str()); } // Ensure we have a native process. - if (!m_debugged_process_sp) { + if (!m_debugged_process_up) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " "shared pointer", __FUNCTION__); return SendErrorResponse(0x36); } // Build the ResumeActionList ResumeActionList actions(StateType::eStateRunning, 0); - Status error = m_debugged_process_sp->Resume(actions); + Status error = m_debugged_process_up->Resume(actions); if (error.Fail()) { - if (log) { - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s c failed for process %" PRIu64 - ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); - } + LLDB_LOG(log, "c failed for process {0}: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(GDBRemoteServerError::eErrorResume); } - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); - + LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID()); // No response required from continue. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vCont_actions( StringExtractorGDBRemote &packet) { StreamString response; response.Printf("vCont;c;C;s;S"); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vCont( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s handling vCont packet", __FUNCTION__); packet.SetFilePos(::strlen("vCont")); if (packet.GetBytesLeft() == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s missing action from " "vCont package", __FUNCTION__); return SendIllFormedResponse(packet, "Missing action from vCont package"); } // Check if this is all continue (no options or ";c"). if (::strcmp(packet.Peek(), ";c") == 0) { // Move past the ';', then do a simple 'c'. packet.SetFilePos(packet.GetFilePos() + 1); return Handle_c(packet); } else if (::strcmp(packet.Peek(), ";s") == 0) { // Move past the ';', then do a simple 's'. packet.SetFilePos(packet.GetFilePos() + 1); return Handle_s(packet); } // Ensure we have a native process. - if (!m_debugged_process_sp) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " - "shared pointer", - __FUNCTION__); + if (!m_debugged_process_up) { + LLDB_LOG(log, "no debugged process"); return SendErrorResponse(0x36); } ResumeActionList thread_actions; while (packet.GetBytesLeft() && *packet.Peek() == ';') { // Skip the semi-colon. packet.GetChar(); // Build up the thread action. ResumeAction thread_action; thread_action.tid = LLDB_INVALID_THREAD_ID; thread_action.state = eStateInvalid; thread_action.signal = 0; const char action = packet.GetChar(); switch (action) { case 'C': thread_action.signal = packet.GetHexMaxU32(false, 0); if (thread_action.signal == 0) return SendIllFormedResponse( packet, "Could not parse signal in vCont packet C action"); LLVM_FALLTHROUGH; case 'c': // Continue thread_action.state = eStateRunning; break; case 'S': thread_action.signal = packet.GetHexMaxU32(false, 0); if (thread_action.signal == 0) return SendIllFormedResponse( packet, "Could not parse signal in vCont packet S action"); LLVM_FALLTHROUGH; case 's': // Step thread_action.state = eStateStepping; break; default: return SendIllFormedResponse(packet, "Unsupported vCont action"); break; } // Parse out optional :{thread-id} value. if (packet.GetBytesLeft() && (*packet.Peek() == ':')) { // Consume the separator. packet.GetChar(); thread_action.tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); if (thread_action.tid == LLDB_INVALID_THREAD_ID) return SendIllFormedResponse( packet, "Could not parse thread number in vCont packet"); } thread_actions.Append(thread_action); } - Status error = m_debugged_process_sp->Resume(thread_actions); + Status error = m_debugged_process_up->Resume(thread_actions); if (error.Fail()) { - if (log) { - log->Printf("GDBRemoteCommunicationServerLLGS::%s vCont failed for " - "process %" PRIu64 ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); - } + LLDB_LOG(log, "vCont failed for process {0}: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(GDBRemoteServerError::eErrorResume); } - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); - + LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID()); // No response required from vCont. return PacketResult::Success; } void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s setting current thread " - "id to %" PRIu64, - __FUNCTION__, tid); + LLDB_LOG(log, "setting current thread id to {0}", tid); m_current_tid = tid; - if (m_debugged_process_sp) - m_debugged_process_sp->SetCurrentThreadID(m_current_tid); + if (m_debugged_process_up) + m_debugged_process_up->SetCurrentThreadID(m_current_tid); } void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s setting continue thread " - "id to %" PRIu64, - __FUNCTION__, tid); + LLDB_LOG(log, "setting continue thread id to {0}", tid); m_continue_tid = tid; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_stop_reason( StringExtractorGDBRemote &packet) { // Handle the $? gdbremote command. // If no process, indicate error - if (!m_debugged_process_sp) + if (!m_debugged_process_up) return SendErrorResponse(02); - return SendStopReasonForState(m_debugged_process_sp->GetState()); + return SendStopReasonForState(m_debugged_process_up->GetState()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReasonForState( lldb::StateType process_state) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); switch (process_state) { case eStateAttaching: case eStateLaunching: case eStateRunning: case eStateStepping: case eStateDetached: // NOTE: gdb protocol doc looks like it should return $OK // when everything is running (i.e. no stopped result). return PacketResult::Success; // Ignore case eStateSuspended: case eStateStopped: case eStateCrashed: { - lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID(); + lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. SetCurrentThreadID(tid); return SendStopReplyPacketForThread(tid); } case eStateInvalid: case eStateUnloaded: case eStateExited: - return SendWResponse(m_debugged_process_sp.get()); + return SendWResponse(m_debugged_process_up.get()); default: - if (log) { - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - ", current state reporting not handled: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - StateAsCString(process_state)); - } + LLDB_LOG(log, "pid {0}, current state reporting not handled: {1}", + m_debugged_process_up->GetID(), process_state); break; } return SendErrorResponse(0); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); // Ensure we have a thread. - NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadAtIndex(0)); + NativeThreadProtocolSP thread_sp(m_debugged_process_up->GetThreadAtIndex(0)); if (!thread_sp) return SendErrorResponse(69); // Get the register context for the first thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) return SendErrorResponse(69); // Parse out the register number from the request. packet.SetFilePos(strlen("qRegisterInfo")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) return SendErrorResponse(69); // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) return SendErrorResponse(69); const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) return SendErrorResponse(69); // Build the reginfos response. StreamGDBRemote response; response.PutCString("name:"); response.PutCString(reg_info->name); response.PutChar(';'); if (reg_info->alt_name && reg_info->alt_name[0]) { response.PutCString("alt-name:"); response.PutCString(reg_info->alt_name); response.PutChar(';'); } response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";", reg_info->byte_size * 8, reg_info->byte_offset); switch (reg_info->encoding) { case eEncodingUint: response.PutCString("encoding:uint;"); break; case eEncodingSint: response.PutCString("encoding:sint;"); break; case eEncodingIEEE754: response.PutCString("encoding:ieee754;"); break; case eEncodingVector: response.PutCString("encoding:vector;"); break; default: break; } switch (reg_info->format) { case eFormatBinary: response.PutCString("format:binary;"); break; case eFormatDecimal: response.PutCString("format:decimal;"); break; case eFormatHex: response.PutCString("format:hex;"); break; case eFormatFloat: response.PutCString("format:float;"); break; case eFormatVectorOfSInt8: response.PutCString("format:vector-sint8;"); break; case eFormatVectorOfUInt8: response.PutCString("format:vector-uint8;"); break; case eFormatVectorOfSInt16: response.PutCString("format:vector-sint16;"); break; case eFormatVectorOfUInt16: response.PutCString("format:vector-uint16;"); break; case eFormatVectorOfSInt32: response.PutCString("format:vector-sint32;"); break; case eFormatVectorOfUInt32: response.PutCString("format:vector-uint32;"); break; case eFormatVectorOfFloat32: response.PutCString("format:vector-float32;"); break; case eFormatVectorOfUInt64: response.PutCString("format:vector-uint64;"); break; case eFormatVectorOfUInt128: response.PutCString("format:vector-uint128;"); break; default: break; }; const char *const register_set_name = reg_context_sp->GetRegisterSetNameForRegisterAtIndex(reg_index); if (register_set_name) { response.PutCString("set:"); response.PutCString(register_set_name); response.PutChar(';'); } if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) response.Printf("ehframe:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindEHFrame]); if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) response.Printf("dwarf:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindDWARF]); switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) { case LLDB_REGNUM_GENERIC_PC: response.PutCString("generic:pc;"); break; case LLDB_REGNUM_GENERIC_SP: response.PutCString("generic:sp;"); break; case LLDB_REGNUM_GENERIC_FP: response.PutCString("generic:fp;"); break; case LLDB_REGNUM_GENERIC_RA: response.PutCString("generic:ra;"); break; case LLDB_REGNUM_GENERIC_FLAGS: response.PutCString("generic:flags;"); break; case LLDB_REGNUM_GENERIC_ARG1: response.PutCString("generic:arg1;"); break; case LLDB_REGNUM_GENERIC_ARG2: response.PutCString("generic:arg2;"); break; case LLDB_REGNUM_GENERIC_ARG3: response.PutCString("generic:arg3;"); break; case LLDB_REGNUM_GENERIC_ARG4: response.PutCString("generic:arg4;"); break; case LLDB_REGNUM_GENERIC_ARG5: response.PutCString("generic:arg5;"); break; case LLDB_REGNUM_GENERIC_ARG6: response.PutCString("generic:arg6;"); break; case LLDB_REGNUM_GENERIC_ARG7: response.PutCString("generic:arg7;"); break; case LLDB_REGNUM_GENERIC_ARG8: response.PutCString("generic:arg8;"); break; default: break; } if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) { response.PutCString("container-regs:"); int i = 0; for (const uint32_t *reg_num = reg_info->value_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { if (i > 0) response.PutChar(','); response.Printf("%" PRIx32, *reg_num); } response.PutChar(';'); } if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) { response.PutCString("invalidate-regs:"); int i = 0; for (const uint32_t *reg_num = reg_info->invalidate_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { if (i > 0) response.PutChar(','); response.Printf("%" PRIx32, *reg_num); } response.PutChar(';'); } if (reg_info->dynamic_size_dwarf_expr_bytes) { const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len; response.PutCString("dynamic_size_dwarf_expr_bytes:"); for (uint32_t i = 0; i < dwarf_opcode_len; ++i) response.PutHex8(reg_info->dynamic_size_dwarf_expr_bytes[i]); response.PutChar(';'); } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s() no process (%s), " - "returning OK", - __FUNCTION__, - m_debugged_process_sp ? "invalid process id" - : "null m_debugged_process_sp"); + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOG(log, "no process ({0}), returning OK", + m_debugged_process_up ? "invalid process id" + : "null m_debugged_process_up"); return SendOKResponse(); } StreamGDBRemote response; response.PutChar('m'); - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s() starting thread iteration", - __FUNCTION__); - + LLDB_LOG(log, "starting thread iteration"); NativeThreadProtocolSP thread_sp; uint32_t thread_index; for (thread_index = 0, - thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_index); + thread_sp = m_debugged_process_up->GetThreadAtIndex(thread_index); thread_sp; ++thread_index, - thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_index)) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s() iterated thread %" PRIu32 - "(%s, tid=0x%" PRIx64 ")", - __FUNCTION__, thread_index, thread_sp ? "is not null" : "null", - thread_sp ? thread_sp->GetID() : LLDB_INVALID_THREAD_ID); + thread_sp = m_debugged_process_up->GetThreadAtIndex(thread_index)) { + LLDB_LOG(log, "iterated thread {0}({1}, tid={2})", thread_index, + thread_sp ? "is not null" : "null", + thread_sp ? thread_sp->GetID() : LLDB_INVALID_THREAD_ID); if (thread_index > 0) response.PutChar(','); response.Printf("%" PRIx64, thread_sp->GetID()); } - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s() finished thread iteration", - __FUNCTION__); - + LLDB_LOG(log, "finished thread iteration"); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo( StringExtractorGDBRemote &packet) { // FIXME for now we return the full thread list in the initial packet and // always do nothing here. return SendPacketNoLock("l"); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out the register number from the request. packet.SetFilePos(strlen("p")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x15); } // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no thread available", __FUNCTION__); return SendErrorResponse(0x15); } // Get the thread's register context. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 - " failed, no register context available for the thread", - __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); + LLDB_LOG( + log, + "pid {0} tid {1} failed, no register context available for the thread", + m_debugged_process_up->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount()); return SendErrorResponse(0x15); } const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); return SendErrorResponse(0x15); } // Build the reginfos response. StreamGDBRemote response; // Retrieve the value RegisterValue reg_value; Status error = reg_context_sp->ReadRegister(reg_info, reg_value); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, read of " "requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString()); return SendErrorResponse(0x15); } const uint8_t *const data = reinterpret_cast(reg_value.GetBytes()); if (!data) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get data " "bytes from requested register %" PRIu32, __FUNCTION__, reg_index); return SendErrorResponse(0x15); } // FIXME flip as needed to get data in big/little endian format for this host. for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i) response.PutHex8(data[i]); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Ensure there is more content. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Empty P packet"); // Parse out the register number from the request. packet.SetFilePos(strlen("P")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x29); } // Note debugserver would send an E30 here. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '=')) return SendIllFormedResponse( packet, "P packet missing '=' char after register number"); // Get process architecture. ArchSpec process_arch; - if (!m_debugged_process_sp || - !m_debugged_process_sp->GetArchitecture(process_arch)) { + if (!m_debugged_process_up || + !m_debugged_process_up->GetArchitecture(process_arch)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to retrieve " "inferior architecture", __FUNCTION__); return SendErrorResponse(0x49); } // Parse out the value. uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register size_t reg_size = packet.GetHexBytesAvail(reg_bytes); // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, no thread " "available (thread index 0)", __FUNCTION__); return SendErrorResponse(0x28); } // Get the thread's register context. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", - __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); + __FUNCTION__, m_debugged_process_up->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); return SendErrorResponse(0x48); } // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount()); return SendErrorResponse(0x47); } // The dwarf expression are evaluate on host site // which may cause register size to change // Hence the reg_size may not be same as reg_info->bytes_size if ((reg_size != reg_info->byte_size) && !(reg_info->dynamic_size_dwarf_expr_bytes)) { return SendIllFormedResponse(packet, "P packet register size is incorrect"); } // Build the reginfos response. StreamGDBRemote response; RegisterValue reg_value(reg_bytes, reg_size, process_arch.GetByteOrder()); Status error = reg_context_sp->WriteRegister(reg_info, reg_value); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, write of " "requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString()); return SendErrorResponse(0x32); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out which variant of $H is requested. packet.SetFilePos(strlen("H")); if (packet.GetBytesLeft() < 1) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, H command " "missing {g,c} variant", __FUNCTION__); return SendIllFormedResponse(packet, "H command missing {g,c} variant"); } const char h_variant = packet.GetChar(); switch (h_variant) { case 'g': break; case 'c': break; default: if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c", __FUNCTION__, h_variant); return SendIllFormedResponse(packet, "H variant unsupported, should be c or g"); } // Parse out the thread number. // FIXME return a parse success/fail value. All values are valid here. const lldb::tid_t tid = packet.GetHexMaxU64(false, std::numeric_limits::max()); // Ensure we have the given thread when not specifying -1 (all threads) or 0 // (any thread). if (tid != LLDB_INVALID_THREAD_ID && tid != 0) { - NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadByID(tid)); + NativeThreadProtocolSP thread_sp(m_debugged_process_up->GetThreadByID(tid)); if (!thread_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 " not found", __FUNCTION__, tid); return SendErrorResponse(0x15); } } // Now switch the given thread type. switch (h_variant) { case 'g': SetCurrentThreadID(tid); break; case 'c': SetContinueThreadID(tid); break; default: assert(false && "unsupported $H variant - shouldn't get here"); return SendIllFormedResponse(packet, "H variant unsupported, should be c or g"); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } packet.SetFilePos(::strlen("I")); uint8_t tmp[4096]; for (;;) { size_t read = packet.GetHexBytesAvail(tmp); if (read == 0) { break; } // write directly to stdin *this might block if stdin buffer is full* // TODO: enqueue this block in circular buffer and send window size to // remote host ConnectionStatus status; Status error; m_stdio_communication.Write(tmp, read, status, &error); if (error.Fail()) { return SendErrorResponse(0x15); } } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_interrupt( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s failed, no process available", - __FUNCTION__); + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOG(log, "failed, no process available"); return SendErrorResponse(0x15); } // Interrupt the process. - Status error = m_debugged_process_sp->Interrupt(); + Status error = m_debugged_process_up->Interrupt(); if (error.Fail()) { - if (log) { - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s failed for process %" PRIu64 - ": %s", - __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); - } + LLDB_LOG(log, "failed for process {0}: {1}", m_debugged_process_up->GetID(), + error); return SendErrorResponse(GDBRemoteServerError::eErrorResume); } - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s stopped process %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); + LLDB_LOG(log, "stopped process {0}", m_debugged_process_up->GetID()); // No response required from stop all. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_memory_read( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("m")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short m packet"); // Read the address. Punting on validation. // FIXME replace with Hex U64 read with no default value that fails on failed // read. const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); // Validate comma. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) return SendIllFormedResponse(packet, "Comma sep missing in m packet"); // Get # bytes to read. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Length missing in m packet"); const uint64_t byte_count = packet.GetHexMaxU64(false, 0); if (byte_count == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to read: " "zero-length packet", __FUNCTION__); return SendOKResponse(); } // Allocate the response buffer. std::string buf(byte_count, '\0'); if (buf.empty()) return SendErrorResponse(0x78); // Retrieve the process memory. size_t bytes_read = 0; - Status error = m_debugged_process_sp->ReadMemoryWithoutTrap( + Status error = m_debugged_process_up->ReadMemoryWithoutTrap( read_addr, &buf[0], byte_count, bytes_read); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to read. Error: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), read_addr, + __FUNCTION__, m_debugged_process_up->GetID(), read_addr, error.AsCString()); return SendErrorResponse(0x08); } if (bytes_read == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", - __FUNCTION__, m_debugged_process_sp->GetID(), read_addr, + __FUNCTION__, m_debugged_process_up->GetID(), read_addr, byte_count); return SendErrorResponse(0x08); } StreamGDBRemote response; packet.SetFilePos(0); char kind = packet.GetChar('?'); if (kind == 'x') response.PutEscapedBytes(buf.data(), byte_count); else { assert(kind == 'm'); for (size_t i = 0; i < bytes_read; ++i) response.PutHex8(buf[i]); } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("M")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short M packet"); // Read the address. Punting on validation. // FIXME replace with Hex U64 read with no default value that fails on failed // read. const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); // Validate comma. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) return SendIllFormedResponse(packet, "Comma sep missing in M packet"); // Get # bytes to read. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Length missing in M packet"); const uint64_t byte_count = packet.GetHexMaxU64(false, 0); if (byte_count == 0) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to write: " - "zero-length packet", - __FUNCTION__); + LLDB_LOG(log, "nothing to write: zero-length packet"); return PacketResult::Success; } // Validate colon. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) return SendIllFormedResponse( packet, "Comma sep missing in M packet after byte length"); // Allocate the conversion buffer. std::vector buf(byte_count, 0); if (buf.empty()) return SendErrorResponse(0x78); // Convert the hex memory write contents to bytes. StreamGDBRemote response; const uint64_t convert_count = packet.GetHexBytes(buf, 0); if (convert_count != byte_count) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " mem 0x%" PRIx64 ": asked to write %" PRIu64 - " bytes, but only found %" PRIu64 " to convert.", - __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, - byte_count, convert_count); + LLDB_LOG(log, + "pid {0} mem {1:x}: asked to write {2} bytes, but only found {3} " + "to convert.", + m_debugged_process_up->GetID(), write_addr, byte_count, + convert_count); return SendIllFormedResponse(packet, "M content byte length specified did " "not match hex-encoded content " "length"); } // Write the process memory. size_t bytes_written = 0; - Status error = m_debugged_process_sp->WriteMemory(write_addr, &buf[0], + Status error = m_debugged_process_up->WriteMemory(write_addr, &buf[0], byte_count, bytes_written); if (error.Fail()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " mem 0x%" PRIx64 ": failed to write. Error: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, - error.AsCString()); + LLDB_LOG(log, "pid {0} mem {1:x}: failed to write. Error: {2}", + m_debugged_process_up->GetID(), write_addr, error); return SendErrorResponse(0x09); } if (bytes_written == 0) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " mem 0x%" PRIx64 ": wrote 0 of %" PRIu64 " requested bytes", - __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, - byte_count); + LLDB_LOG(log, "pid {0} mem {1:x}: wrote 0 of {2} requested bytes", + m_debugged_process_up->GetID(), write_addr, byte_count); return SendErrorResponse(0x09); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Currently only the NativeProcessProtocol knows if it can handle a // qMemoryRegionInfoSupported // request, but we're not guaranteed to be attached to a process. For now // we'll assume the // client only asks this when a process is being debugged. // Ensure we have a process running; otherwise, we can't figure this out // since we won't have a NativeProcessProtocol. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Test if we can get any region back when asking for the region around NULL. MemoryRegionInfo region_info; const Status error = - m_debugged_process_sp->GetMemoryRegionInfo(0, region_info); + m_debugged_process_up->GetMemoryRegionInfo(0, region_info); if (error.Fail()) { // We don't support memory region info collection for this // NativeProcessProtocol. return SendUnimplementedResponse(""); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Ensure we have a process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("qMemoryRegionInfo:")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); // Read the address. Punting on validation. const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); StreamGDBRemote response; // Get the memory region info for the target address. MemoryRegionInfo region_info; const Status error = - m_debugged_process_sp->GetMemoryRegionInfo(read_addr, region_info); + m_debugged_process_up->GetMemoryRegionInfo(read_addr, region_info); if (error.Fail()) { // Return the error message. response.PutCString("error:"); response.PutCStringAsRawHex8(error.AsCString()); response.PutChar(';'); } else { // Range start and size. response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";", region_info.GetRange().GetRangeBase(), region_info.GetRange().GetByteSize()); // Permissions. if (region_info.GetReadable() || region_info.GetWritable() || region_info.GetExecutable()) { // Write permissions info. response.PutCString("permissions:"); if (region_info.GetReadable()) response.PutChar('r'); if (region_info.GetWritable()) response.PutChar('w'); if (region_info.GetExecutable()) response.PutChar('x'); response.PutChar(';'); } // Name ConstString name = region_info.GetName(); if (name) { response.PutCString("name:"); response.PutCStringAsRawHex8(name.AsCString()); response.PutChar(';'); } } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) { // Ensure we have a process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s failed, no process available", - __FUNCTION__); + LLDB_LOG(log, "failed, no process available"); return SendErrorResponse(0x15); } // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos(strlen("Z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "Too short Z packet, missing software/hardware specifier"); bool want_breakpoint = true; bool want_hardware = false; uint32_t watch_flags = 0; const GDBStoppointType stoppoint_type = GDBStoppointType(packet.GetS32(eStoppointInvalid)); switch (stoppoint_type) { case eBreakpointSoftware: want_hardware = false; want_breakpoint = true; break; case eBreakpointHardware: want_hardware = true; want_breakpoint = true; break; case eWatchpointWrite: watch_flags = 1; want_hardware = true; want_breakpoint = false; break; case eWatchpointRead: watch_flags = 2; want_hardware = true; want_breakpoint = false; break; case eWatchpointReadWrite: watch_flags = 3; want_hardware = true; want_breakpoint = false; break; case eStoppointInvalid: return SendIllFormedResponse( packet, "Z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed Z packet, expecting comma after stoppoint type"); // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing address"); const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed Z packet, expecting comma after address"); // Parse out the stoppoint size (i.e. size hint for opcode size). const uint32_t size = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (size == std::numeric_limits::max()) return SendIllFormedResponse( packet, "Malformed Z packet, failed to parse size argument"); if (want_breakpoint) { // Try to set the breakpoint. const Status error = - m_debugged_process_sp->SetBreakpoint(addr, size, want_hardware); + m_debugged_process_up->SetBreakpoint(addr, size, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to set breakpoint: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to set breakpoint: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x09); } else { // Try to set the watchpoint. - const Status error = m_debugged_process_sp->SetWatchpoint( + const Status error = m_debugged_process_up->SetWatchpoint( addr, size, watch_flags, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to set watchpoint: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to set watchpoint: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x09); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { // Ensure we have a process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s failed, no process available", - __FUNCTION__); + LLDB_LOG(log, "failed, no process available"); return SendErrorResponse(0x15); } // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos(strlen("z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "Too short z packet, missing software/hardware specifier"); bool want_breakpoint = true; bool want_hardware = false; const GDBStoppointType stoppoint_type = GDBStoppointType(packet.GetS32(eStoppointInvalid)); switch (stoppoint_type) { case eBreakpointHardware: want_breakpoint = true; want_hardware = true; break; case eBreakpointSoftware: want_breakpoint = true; break; case eWatchpointWrite: want_breakpoint = false; break; case eWatchpointRead: want_breakpoint = false; break; case eWatchpointReadWrite: want_breakpoint = false; break; default: return SendIllFormedResponse( packet, "z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed z packet, expecting comma after stoppoint type"); // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing address"); const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed z packet, expecting comma after address"); /* // Parse out the stoppoint size (i.e. size hint for opcode size). const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); if (size == std::numeric_limits::max ()) return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); */ if (want_breakpoint) { // Try to clear the breakpoint. const Status error = - m_debugged_process_sp->RemoveBreakpoint(addr, want_hardware); + m_debugged_process_up->RemoveBreakpoint(addr, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to remove breakpoint: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x09); } else { // Try to clear the watchpoint. - const Status error = m_debugged_process_sp->RemoveWatchpoint(addr); + const Status error = m_debugged_process_up->RemoveWatchpoint(addr); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to remove watchpoint: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x09); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x32); } // We first try to use a continue thread id. If any one or any all set, use // the current thread. // Bail out if we don't have a thread id. lldb::tid_t tid = GetContinueThreadID(); if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) tid = GetCurrentThreadID(); if (tid == LLDB_INVALID_THREAD_ID) return SendErrorResponse(0x33); // Double check that we have such a thread. // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. - NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetThreadByID(tid); + NativeThreadProtocolSP thread_sp = m_debugged_process_up->GetThreadByID(tid); if (!thread_sp || thread_sp->GetID() != tid) return SendErrorResponse(0x33); // Create the step action for the given thread. ResumeAction action = {tid, eStateStepping, 0}; // Setup the actions list. ResumeActionList actions; actions.Append(action); // All other threads stop while we're single stepping a thread. actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); - Status error = m_debugged_process_sp->Resume(actions); + Status error = m_debugged_process_up->Resume(actions); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " Resume() failed with error: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), tid, + __FUNCTION__, m_debugged_process_up->GetID(), tid, error.AsCString()); return SendErrorResponse(0x49); } // No response here - the stop or exit will come from the resulting action. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read( StringExtractorGDBRemote &packet) { // *BSD impls should be able to do this too. #if defined(__linux__) || defined(__NetBSD__) Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Parse out the offset. packet.SetFilePos(strlen("qXfer:auxv:read::")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing offset"); const uint64_t auxv_offset = packet.GetHexMaxU64(false, std::numeric_limits::max()); if (auxv_offset == std::numeric_limits::max()) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing offset"); // Parse out comma. if (packet.GetBytesLeft() < 1 || packet.GetChar() != ',') return SendIllFormedResponse( packet, "qXfer:auxv:read:: packet missing comma after offset"); // Parse out the length. const uint64_t auxv_length = packet.GetHexMaxU64(false, std::numeric_limits::max()); if (auxv_length == std::numeric_limits::max()) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing length"); // Grab the auxv data if we need it. if (!m_active_auxv_buffer_up) { // Make sure we have a valid process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x10); } // Grab the auxv data. - auto buffer_or_error = m_debugged_process_sp->GetAuxvData(); + auto buffer_or_error = m_debugged_process_up->GetAuxvData(); if (!buffer_or_error) { std::error_code ec = buffer_or_error.getError(); LLDB_LOG(log, "no auxv data retrieved: {0}", ec.message()); return SendErrorResponse(ec.value()); } m_active_auxv_buffer_up = std::move(*buffer_or_error); } StreamGDBRemote response; bool done_with_buffer = false; llvm::StringRef buffer = m_active_auxv_buffer_up->getBuffer(); if (auxv_offset >= buffer.size()) { // We have nothing left to send. Mark the buffer as complete. response.PutChar('l'); done_with_buffer = true; } else { // Figure out how many bytes are available starting at the given offset. buffer = buffer.drop_front(auxv_offset); // Mark the response type according to whether we're reading the remainder // of the auxv data. if (auxv_length >= buffer.size()) { // There will be nothing left to read after this response.PutChar('l'); done_with_buffer = true; } else { // There will still be bytes to read after this request. response.PutChar('m'); buffer = buffer.take_front(auxv_length); } // Now write the data in encoded binary form. response.PutEscapedBytes(buffer.data(), buffer.size()); } if (done_with_buffer) m_active_auxv_buffer_up.reset(); return SendPacketNoLock(response.GetString()); #else return SendUnimplementedResponse("not implemented on this platform"); #endif } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Move past packet name. packet.SetFilePos(strlen("QSaveRegisterState")); // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (m_thread_suffix_supported) return SendIllFormedResponse( packet, "No thread specified in QSaveRegisterState packet"); else return SendIllFormedResponse(packet, "No thread was is set with the Hg packet"); } // Grab the register context for the thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 - " failed, no register context available for the thread", - __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); + LLDB_LOG( + log, + "pid {0} tid {1} failed, no register context available for the thread", + m_debugged_process_up->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Save registers to a buffer. DataBufferSP register_data_sp; Status error = reg_context_sp->ReadAllRegisterValues(register_data_sp); if (error.Fail()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to save all register values: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to save all register values: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x75); } // Allocate a new save id. const uint32_t save_id = GetNextSavedRegistersID(); assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) && "GetNextRegisterSaveID() returned an existing register save id"); // Save the register data buffer under the save id. { std::lock_guard guard(m_saved_registers_mutex); m_saved_registers_map[save_id] = register_data_sp; } // Write the response. StreamGDBRemote response; response.Printf("%" PRIu32, save_id); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out save id. packet.SetFilePos(strlen("QRestoreRegisterState:")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "QRestoreRegisterState packet missing register save id"); const uint32_t save_id = packet.GetU32(0); if (save_id == 0) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s QRestoreRegisterState " - "packet has malformed save id, expecting decimal uint32_t", - __FUNCTION__); + LLDB_LOG(log, "QRestoreRegisterState packet has malformed save id, " + "expecting decimal uint32_t"); return SendErrorResponse(0x76); } // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (m_thread_suffix_supported) return SendIllFormedResponse( packet, "No thread specified in QRestoreRegisterState packet"); else return SendIllFormedResponse(packet, "No thread was is set with the Hg packet"); } // Grab the register context for the thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { - if (log) - log->Printf( - "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 - " failed, no register context available for the thread", - __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); + LLDB_LOG( + log, + "pid {0} tid {1} failed, no register context available for the thread", + m_debugged_process_up->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Retrieve register state buffer, then remove from the list. DataBufferSP register_data_sp; { std::lock_guard guard(m_saved_registers_mutex); // Find the register set buffer for the given save id. auto it = m_saved_registers_map.find(save_id); if (it == m_saved_registers_map.end()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " does not have a register set save buffer for id %" PRIu32, - __FUNCTION__, m_debugged_process_sp->GetID(), save_id); + LLDB_LOG(log, + "pid {0} does not have a register set save buffer for id {1}", + m_debugged_process_up->GetID(), save_id); return SendErrorResponse(0x77); } register_data_sp = it->second; // Remove it from the map. m_saved_registers_map.erase(it); } Status error = reg_context_sp->WriteAllRegisterValues(register_data_sp); if (error.Fail()) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - " failed to restore all register values: %s", - __FUNCTION__, m_debugged_process_sp->GetID(), - error.AsCString()); + LLDB_LOG(log, "pid {0} failed to restore all register values: {1}", + m_debugged_process_up->GetID(), error); return SendErrorResponse(0x77); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vAttach( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Consume the ';' after vAttach. packet.SetFilePos(strlen("vAttach")); if (!packet.GetBytesLeft() || packet.GetChar() != ';') return SendIllFormedResponse(packet, "vAttach missing expected ';'"); // Grab the PID to which we will attach (assume hex encoding). lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); if (pid == LLDB_INVALID_PROCESS_ID) return SendIllFormedResponse(packet, "vAttach failed to parse the process id"); // Attempt to attach. if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s attempting to attach to " "pid %" PRIu64, __FUNCTION__, pid); Status error = AttachToProcess(pid); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to " "pid %" PRIu64 ": %s\n", __FUNCTION__, pid, error.AsCString()); return SendErrorResponse(0x01); } // Notify we attached by sending a stop packet. - return SendStopReasonForState(m_debugged_process_sp->GetState()); + return SendStopReasonForState(m_debugged_process_up->GetState()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); StopSTDIOForwarding(); // Fail if we don't have a current process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; // Consume the ';' after D. packet.SetFilePos(1); if (packet.GetBytesLeft()) { if (packet.GetChar() != ';') return SendIllFormedResponse(packet, "D missing expected ';'"); // Grab the PID from which we will detach (assume hex encoding). pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); if (pid == LLDB_INVALID_PROCESS_ID) return SendIllFormedResponse(packet, "D failed to parse the process id"); } - if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_sp->GetID() != pid) { + if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid) { return SendIllFormedResponse(packet, "Invalid pid"); } - const Status error = m_debugged_process_sp->Detach(); + const Status error = m_debugged_process_up->Detach(); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to detach from " "pid %" PRIu64 ": %s\n", - __FUNCTION__, m_debugged_process_sp->GetID(), + __FUNCTION__, m_debugged_process_up->GetID(), error.AsCString()); return SendErrorResponse(0x01); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); packet.SetFilePos(strlen("qThreadStopInfo")); const lldb::tid_t tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); if (tid == LLDB_INVALID_THREAD_ID) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x15); } return SendStopReplyPacketForThread(tid); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo( StringExtractorGDBRemote &) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a debugged process. - if (!m_debugged_process_sp || - (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(50); + LLDB_LOG(log, "preparing packet for pid {0}", m_debugged_process_up->GetID()); - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid " - "%" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); - StreamString response; const bool threads_with_valid_stop_info_only = false; JSONArray::SP threads_array_sp = GetJSONThreadsInfo( - *m_debugged_process_sp, threads_with_valid_stop_info_only); + *m_debugged_process_up, threads_with_valid_stop_info_only); if (!threads_array_sp) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to prepare a " - "packet for pid %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID()); + LLDB_LOG(log, "failed to prepare a packet for pid {0}", + m_debugged_process_up->GetID()); return SendErrorResponse(52); } threads_array_sp->Write(response); StreamGDBRemote escaped_response; escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); return SendPacketNoLock(escaped_response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) + if (!m_debugged_process_up || + m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(68); packet.SetFilePos(strlen("qWatchpointSupportInfo")); if (packet.GetBytesLeft() == 0) return SendOKResponse(); if (packet.GetChar() != ':') return SendErrorResponse(67); - auto hw_debug_cap = m_debugged_process_sp->GetHardwareDebugSupportInfo(); + auto hw_debug_cap = m_debugged_process_up->GetHardwareDebugSupportInfo(); StreamGDBRemote response; if (hw_debug_cap == llvm::None) response.Printf("num:0;"); else response.Printf("num:%d;", hw_debug_cap->second); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. - if (!m_debugged_process_sp || - m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) + if (!m_debugged_process_up || + m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(67); packet.SetFilePos(strlen("qFileLoadAddress:")); if (packet.GetBytesLeft() == 0) return SendErrorResponse(68); std::string file_name; packet.GetHexByteString(file_name); lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS; Status error = - m_debugged_process_sp->GetFileLoadAddress(file_name, file_load_address); + m_debugged_process_up->GetFileLoadAddress(file_name, file_load_address); if (error.Fail()) return SendErrorResponse(69); if (file_load_address == LLDB_INVALID_ADDRESS) return SendErrorResponse(1); // File not loaded StreamGDBRemote response; response.PutHex64(file_load_address); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( StringExtractorGDBRemote &packet) { std::vector signals; packet.SetFilePos(strlen("QPassSignals:")); // Read sequence of hex signal numbers divided by a semicolon and // optionally spaces. while (packet.GetBytesLeft() > 0) { int signal = packet.GetS32(-1, 16); if (signal < 0) return SendIllFormedResponse(packet, "Failed to parse signal number."); signals.push_back(signal); packet.SkipSpaces(); char separator = packet.GetChar(); if (separator == '\0') break; // End of string if (separator != ';') return SendIllFormedResponse(packet, "Invalid separator," " expected semicolon."); } // Fail if we don't have a current process. - if (!m_debugged_process_sp) + if (!m_debugged_process_up) return SendErrorResponse(68); - Status error = m_debugged_process_sp->IgnoreSignals(signals); + Status error = m_debugged_process_up->IgnoreSignals(signals); if (error.Fail()) return SendErrorResponse(69); return SendOKResponse(); } void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Tell the stdio connection to shut down. if (m_stdio_communication.IsConnected()) { auto connection = m_stdio_communication.GetConnection(); if (connection) { Status error; connection->Disconnect(&error); if (error.Success()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " "terminal stdio - SUCCESS", __FUNCTION__); } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " "terminal stdio - FAIL: %s", __FUNCTION__, error.AsCString()); } } } } NativeThreadProtocolSP GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( StringExtractorGDBRemote &packet) { NativeThreadProtocolSP thread_sp; // We have no thread if we don't have a process. - if (!m_debugged_process_sp || - m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) + if (!m_debugged_process_up || + m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) return thread_sp; // If the client hasn't asked for thread suffix support, there will not be a // thread suffix. // Use the current thread in that case. if (!m_thread_suffix_supported) { const lldb::tid_t current_tid = GetCurrentThreadID(); if (current_tid == LLDB_INVALID_THREAD_ID) return thread_sp; else if (current_tid == 0) { // Pick a thread. - return m_debugged_process_sp->GetThreadAtIndex(0); + return m_debugged_process_up->GetThreadAtIndex(0); } else - return m_debugged_process_sp->GetThreadByID(current_tid); + return m_debugged_process_up->GetThreadByID(current_tid); } Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out the ';'. if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " "error: expected ';' prior to start of thread suffix: packet " "contents = '%s'", __FUNCTION__, packet.GetStringRef().c_str()); return thread_sp; } if (!packet.GetBytesLeft()) return thread_sp; // Parse out thread: portion. if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " "error: expected 'thread:' but not found, packet contents = " "'%s'", __FUNCTION__, packet.GetStringRef().c_str()); return thread_sp; } packet.SetFilePos(packet.GetFilePos() + strlen("thread:")); const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); if (tid != 0) - return m_debugged_process_sp->GetThreadByID(tid); + return m_debugged_process_up->GetThreadByID(tid); return thread_sp; } lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const { if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) { // Use whatever the debug process says is the current thread id // since the protocol either didn't specify or specified we want // any/all threads marked as the current thread. - if (!m_debugged_process_sp) + if (!m_debugged_process_up) return LLDB_INVALID_THREAD_ID; - return m_debugged_process_sp->GetCurrentThreadID(); + return m_debugged_process_up->GetCurrentThreadID(); } // Use the specific current thread id set by the gdb remote protocol. return m_current_tid; } uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() { std::lock_guard guard(m_saved_registers_mutex); return m_next_saved_registers_id++; } void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "clearing auxv buffer: {0}", m_active_auxv_buffer_up.get()); m_active_auxv_buffer_up.reset(); } FileSpec GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path, const ArchSpec &arch) { - if (m_debugged_process_sp) { + if (m_debugged_process_up) { FileSpec file_spec; - if (m_debugged_process_sp + if (m_debugged_process_up ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec) .Success()) { if (file_spec.Exists()) return file_spec; } } return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h (revision 321194) @@ -1,264 +1,264 @@ //===-- GDBRemoteCommunicationServerLLGS.h ----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_GDBRemoteCommunicationServerLLGS_h_ #define liblldb_GDBRemoteCommunicationServerLLGS_h_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Core/Communication.h" #include "lldb/Host/MainLoop.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/lldb-private-forward.h" // Project includes #include "GDBRemoteCommunicationServerCommon.h" class StringExtractorGDBRemote; namespace lldb_private { namespace process_gdb_remote { class ProcessGDBRemote; class GDBRemoteCommunicationServerLLGS : public GDBRemoteCommunicationServerCommon, public NativeProcessProtocol::NativeDelegate { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ GDBRemoteCommunicationServerLLGS( MainLoop &mainloop, const NativeProcessProtocol::Factory &process_factory); //------------------------------------------------------------------ /// Specify the program to launch and its arguments. /// /// @param[in] args /// The command line to launch. /// /// @param[in] argc /// The number of elements in the args array of cstring pointers. /// /// @return /// An Status object indicating the success or failure of making /// the setting. //------------------------------------------------------------------ Status SetLaunchArguments(const char *const args[], int argc); //------------------------------------------------------------------ /// Specify the launch flags for the process. /// /// @param[in] launch_flags /// The launch flags to use when launching this process. /// /// @return /// An Status object indicating the success or failure of making /// the setting. //------------------------------------------------------------------ Status SetLaunchFlags(unsigned int launch_flags); //------------------------------------------------------------------ /// Launch a process with the current launch settings. /// /// This method supports running an lldb-gdbserver or similar /// server in a situation where the startup code has been provided /// with all the information for a child process to be launched. /// /// @return /// An Status object indicating the success or failure of the /// launch. //------------------------------------------------------------------ Status LaunchProcess() override; //------------------------------------------------------------------ /// Attach to a process. /// /// This method supports attaching llgs to a process accessible via the /// configured Platform. /// /// @return /// An Status object indicating the success or failure of the /// attach operation. //------------------------------------------------------------------ Status AttachToProcess(lldb::pid_t pid); //------------------------------------------------------------------ // NativeProcessProtocol::NativeDelegate overrides //------------------------------------------------------------------ void InitializeDelegate(NativeProcessProtocol *process) override; void ProcessStateChanged(NativeProcessProtocol *process, lldb::StateType state) override; void DidExec(NativeProcessProtocol *process) override; Status InitializeConnection(std::unique_ptr &&connection); protected: MainLoop &m_mainloop; MainLoop::ReadHandleUP m_network_handle_up; const NativeProcessProtocol::Factory &m_process_factory; lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID; lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID; std::recursive_mutex m_debugged_process_mutex; - NativeProcessProtocolSP m_debugged_process_sp; + std::unique_ptr m_debugged_process_up; Communication m_stdio_communication; MainLoop::ReadHandleUP m_stdio_handle_up; lldb::StateType m_inferior_prev_state = lldb::StateType::eStateInvalid; std::unique_ptr m_active_auxv_buffer_up; std::mutex m_saved_registers_mutex; std::unordered_map m_saved_registers_map; uint32_t m_next_saved_registers_id = 1; bool m_handshake_completed = false; PacketResult SendONotification(const char *buffer, uint32_t len); PacketResult SendWResponse(NativeProcessProtocol *process); PacketResult SendStopReplyPacketForThread(lldb::tid_t tid); PacketResult SendStopReasonForState(lldb::StateType process_state); PacketResult Handle_k(StringExtractorGDBRemote &packet); PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qC(StringExtractorGDBRemote &packet); PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet); PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); PacketResult Handle_C(StringExtractorGDBRemote &packet); PacketResult Handle_c(StringExtractorGDBRemote &packet); PacketResult Handle_vCont(StringExtractorGDBRemote &packet); PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet); PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet); PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet); PacketResult Handle_p(StringExtractorGDBRemote &packet); PacketResult Handle_P(StringExtractorGDBRemote &packet); PacketResult Handle_H(StringExtractorGDBRemote &packet); PacketResult Handle_I(StringExtractorGDBRemote &packet); PacketResult Handle_interrupt(StringExtractorGDBRemote &packet); // Handles $m and $x packets. PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); PacketResult Handle_M(StringExtractorGDBRemote &packet); PacketResult Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet); PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet); PacketResult Handle_Z(StringExtractorGDBRemote &packet); PacketResult Handle_z(StringExtractorGDBRemote &packet); PacketResult Handle_s(StringExtractorGDBRemote &packet); PacketResult Handle_qXfer_auxv_read(StringExtractorGDBRemote &packet); PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet); PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet); PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet); PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet); PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet); PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); PacketResult Handle_D(StringExtractorGDBRemote &packet); PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet); PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); void SetCurrentThreadID(lldb::tid_t tid); lldb::tid_t GetCurrentThreadID() const; void SetContinueThreadID(lldb::tid_t tid); lldb::tid_t GetContinueThreadID() const { return m_continue_tid; } Status SetSTDIOFileDescriptor(int fd); FileSpec FindModuleFile(const std::string &module_path, const ArchSpec &arch) override; private: void HandleInferiorState_Exited(NativeProcessProtocol *process); void HandleInferiorState_Stopped(NativeProcessProtocol *process); NativeThreadProtocolSP GetThreadFromSuffix(StringExtractorGDBRemote &packet); uint32_t GetNextSavedRegistersID(); void MaybeCloseInferiorTerminalConnection(); void ClearProcessSpecificData(); void RegisterPacketHandlers(); void DataAvailableCallback(); void SendProcessOutput(); void StartSTDIOForwarding(); void StopSTDIOForwarding(); //------------------------------------------------------------------ // For GDBRemoteCommunicationServerLLGS only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerLLGS); }; } // namespace process_gdb_remote } // namespace lldb_private #endif // liblldb_GDBRemoteCommunicationServerLLGS_h_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 321193) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 321194) @@ -1,5225 +1,5227 @@ //===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" // C Includes #include #include #ifndef LLDB_DISABLE_POSIX #include #include // for mmap #include +#include #endif #include #include #include // C++ Includes #include #include #include #include #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostThread.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" // Project includes #include "GDBRemoteRegisterContext.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" #include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Host.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). // We need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this // function and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { StreamFile strm; Status error(strm.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm); } } namespace { static PropertyDefinition g_properties[] = { {"packet-timeout", OptionValue::eTypeUInt64, true, 1, NULL, NULL, "Specify the default packet timeout in seconds."}, {"target-definition-file", OptionValue::eTypeFileSpec, true, 0, NULL, NULL, "The file that provides the description for remote target registers."}, {NULL, OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL}}; enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return ProcessGDBRemote::GetPluginNameStatic(); } PluginProperties() : Properties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } virtual ~PluginProperties() {} uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64( NULL, idx, g_properties[idx].default_uint_value); } bool SetPacketTimeout(uint64_t timeout) { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, timeout); } FileSpec GetTargetDefinitionFile() const { const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec(NULL, idx); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset(new PluginProperties()); return g_settings_sp; } } // anonymous namespace end // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. #if defined(__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif #if defined(__APPLE__) && \ (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) static bool rand_initialized = false; static inline uint16_t get_random_port() { if (!rand_initialized) { time_t seed = time(NULL); rand_initialized = true; srand(seed); } return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; } #endif ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char *ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset(new ProcessGDBRemote(target_sp, listener_sp)); return process_sp; } bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeJIT: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return exe_module->GetFileSpec().Exists(); } // However, if there is no executable module, we return true since we might be // preparing to attach. return true; } //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), m_flags(0), m_gdb_comm(), m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(), m_register_info(), m_async_broadcaster(NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_listener_sp( Listener::MakeListener("lldb.process.gdb-remote.async-listener")), m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(), m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(), m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), m_max_memory_size(0), m_remote_stub_max_memory_size(0), m_addr_to_mmap_size(), m_thread_create_bp_sp(), m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_command_sp(), m_breakpoint_pc_offset(0), m_initial_tid(LLDB_INVALID_THREAD_ID) { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, "async thread did exit"); Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); const uint32_t async_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (m_async_listener_sp->StartListeningForEvents( &m_async_broadcaster, async_event_mask) != async_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for " "m_async_broadcaster events", __FUNCTION__); } const uint32_t gdb_event_mask = Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; if (m_async_listener_sp->StartListeningForEvents( &m_gdb_comm, gdb_event_mask) != gdb_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events", __FUNCTION__); } const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get // stranded and // its connection will go away so when it wakes up it will crash. So kill it // for sure here. StopAsyncThread(); KillDebugserverProcess(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); Status error; StructuredData::ObjectSP module_object_sp( interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { StructuredData::DictionarySP target_definition_sp( interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); if (target_definition_sp) { StructuredData::ObjectSP target_object( target_definition_sp->GetValueForKey("host-info")); if (target_object) { if (auto host_info_dict = target_object->GetAsDictionary()) { StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); if (auto triple_string_value = triple_value->GetAsString()) { std::string triple_string = triple_string_value->GetValue(); ArchSpec host_arch(triple_string.c_str()); if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { GetTarget().SetArchitecture(host_arch); } } } } m_breakpoint_pc_offset = 0; StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); if (breakpoint_pc_offset_value) { if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); } if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } return false; } // If the remote stub didn't give us eh_frame or DWARF register numbers for a // register, // see if the ABI can provide them. // DWARF and eh_frame register numbers are defined as a part of the ABI. static void AugmentRegisterInfoViaABI(RegisterInfo ®_info, ConstString reg_name, ABISP abi_sp) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM || reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) { if (abi_sp) { RegisterInfo abi_reg_info; if (abi_sp->GetRegisterInfoByName(reg_name, abi_reg_info)) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindEHFrame] = abi_reg_info.kinds[eRegisterKindEHFrame]; } if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindDWARF] = abi_reg_info.kinds[eRegisterKindDWARF]; } if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindGeneric] = abi_reg_info.kinds[eRegisterKindGeneric]; } } } } } static size_t SplitCommaSeparatedRegisterNumberString( const llvm::StringRef &comma_separated_regiter_numbers, std::vector ®nums, int base) { regnums.clear(); std::pair value_pair; value_pair.second = comma_separated_regiter_numbers; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); if (reg != LLDB_INVALID_REGNUM) regnums.push_back(reg); } } while (!value_pair.second.empty()); return regnums.size(); } void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; m_register_info.Clear(); // Check if qHostInfo specified a specific packet timeout for this connection. // If so then lets update our setting so the user knows what the timeout is // and can see it. const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout > std::chrono::seconds(0)) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); } // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the // target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile(); if (!target_definition_fspec.Exists()) { // If the filename doesn't exist, it may be a ~ not having been expanded - // try to resolve it. target_definition_fspec.ResolvePath(); } if (target_definition_fspec) { // See if we can get register definitions from a python file if (ParsePythonTargetDefinition(target_definition_fspec)) { return; } else { StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); stream_sp->Printf("ERROR: target description file %s failed to parse.\n", target_definition_fspec.GetPath().c_str()); } } const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec arch_to_use; if (remote_process_arch.IsValid()) arch_to_use = remote_process_arch; else arch_to_use = remote_host_arch; if (!arch_to_use.IsValid()) arch_to_use = target_arch; if (GetGDBServerRegisterInfo(arch_to_use)) return; char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) == GDBRemoteCommunication::PacketResult::Success) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { llvm::StringRef name; llvm::StringRef value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // process plugin reg num reg_num // native register number }, NULL, NULL, NULL, // Dwarf expression opcode bytes pointer 0 // Dwarf expression opcode bytes length }; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { reg_name.SetString(value); } else if (name.equals("alt-name")) { alt_name.SetString(value); } else if (name.equals("bitsize")) { value.getAsInteger(0, reg_info.byte_size); reg_info.byte_size /= CHAR_BIT; } else if (name.equals("offset")) { if (value.getAsInteger(0, reg_offset)) reg_offset = UINT32_MAX; } else if (name.equals("encoding")) { const Encoding encoding = Args::StringToEncoding(value); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.equals("format")) { Format format = eFormatInvalid; if (Args::StringToFormat(value.str().c_str(), format, NULL) .Success()) reg_info.format = format; else { reg_info.format = llvm::StringSwitch(value) .Case("binary", eFormatBinary) .Case("decimal", eFormatDecimal) .Case("hex", eFormatHex) .Case("float", eFormatFloat) .Case("vector-sint8", eFormatVectorOfSInt8) .Case("vector-uint8", eFormatVectorOfUInt8) .Case("vector-sint16", eFormatVectorOfSInt16) .Case("vector-uint16", eFormatVectorOfUInt16) .Case("vector-sint32", eFormatVectorOfSInt32) .Case("vector-uint32", eFormatVectorOfUInt32) .Case("vector-float32", eFormatVectorOfFloat32) .Case("vector-uint64", eFormatVectorOfUInt64) .Case("vector-uint128", eFormatVectorOfUInt128) .Default(eFormatInvalid); } } else if (name.equals("set")) { set_name.SetString(value); } else if (name.equals("gcc") || name.equals("ehframe")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame])) reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; } else if (name.equals("dwarf")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF])) reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; } else if (name.equals("generic")) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name.equals("container-regs")) { SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.equals("invalidate-regs")) { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } else if (name.equals("dynamic_size_dwarf_expr_bytes")) { size_t dwarf_opcode_len = value.size() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; StringExtractor opcode_extractor(value); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } } reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } // We have to make a temporary ABI here, and not use the GetABI because // this code // gets called in DidAttach, when the target architecture (and // consequently the ABI we'll get from // the process) may be wrong. ABISP abi_to_use = ABI::FindPlugin(shared_from_this(), arch_to_use); AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use); m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } else { break; // ensure exit before reg_num is incremented } } else { break; } } if (m_register_info.GetNumRegisters() > 0) { m_register_info.Finalize(GetTarget().GetArchitecture()); return; } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); if (!target_arch.IsValid()) { if (arch_to_use.IsValid() && (arch_to_use.GetMachine() == llvm::Triple::arm || arch_to_use.GetMachine() == llvm::Triple::thumb) && arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm || target_arch.GetMachine() == llvm::Triple::thumb) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize(GetTarget().GetArchitecture()); } Status ProcessGDBRemote::WillLaunch(Module *module) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error(WillLaunchOrAttach()); if (error.Fail()) return error; error = ConnectToDebugserver(remote_url); if (error.Fail()) return error; StartAsyncThread(); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected // and could now request to launch or attach, or get remote process // listings... SetPrivateState(eStateConnected); } else { // We have a valid process SetID(pid); GetThreadList(); StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); Target &target = GetTarget(); if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); } else { target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); } } const StateType state = SetThreadStopInfo(response); if (state != eStateInvalid) { SetPrivateState(state); } else error.SetErrorStringWithFormat( "Process %" PRIu64 " was reported after connecting to " "'%s', but state was not stopped: %s", pid, remote_url.str().c_str(), StateAsCString(state)); } else error.SetErrorStringWithFormat("Process %" PRIu64 " was reported after connecting to '%s', " "but no stop reply packet was received", pid, remote_url.str().c_str()); } if (log) log->Printf("ProcessGDBRemote::%s pid %" PRIu64 ": normalizing target architecture initial triple: %s " "(GetTarget().GetArchitecture().IsValid() %s, " "m_gdb_comm.GetHostArchitecture().IsValid(): %s)", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str(), GetTarget().GetArchitecture().IsValid() ? "true" : "false", m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false"); if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } if (log) log->Printf("ProcessGDBRemote::%s pid %" PRIu64 ": normalized target architecture triple: %s", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); if (error.Success()) { PlatformSP platform_sp = GetTarget().GetPlatform(); if (platform_sp && platform_sp->IsConnected()) SetUnixSignals(platform_sp->GetUnixSignals()); else SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); } return error; } Status ProcessGDBRemote::WillLaunchOrAttach() { Status error; m_stdio_communication.Clear(); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Status ProcessGDBRemote::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; if (log) log->Printf("ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stderr_file_spec = file_action->GetFileSpec(); } if (log) { if (stdin_file_spec || stdout_file_spec || stderr_file_spec) log->Printf("ProcessGDBRemote::%s provided with STDIO paths via " "launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); else log->Printf("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file // or stdio is completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | // LLDB_LOG_OPTION_PREPEND_TIMESTAMP | // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); ObjectFile *object_file = exe_module->GetObjectFile(); if (object_file) { error = EstablishConnectionIfNeeded(launch_info); if (error.Success()) { lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp(GetTarget().GetPlatform()); if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) stdin_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stdout_file_spec) stdout_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stderr_file_spec) stderr_file_spec.SetFile(FileSystem::DEV_NULL, false); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, NULL, 0)) { FileSpec slave_name{pty.GetSlaveName(NULL, 0), false}; if (!stdin_file_spec) stdin_file_spec = slave_name; if (!stdout_file_spec) stdout_file_spec = slave_name; if (!stderr_file_spec) stderr_file_spec = slave_name; } if (log) log->Printf( "ProcessGDBRemote::%s adjusted STDIO paths for local platform " "(IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); } if (log) log->Printf("ProcessGDBRemote::%s final STDIO paths after all " "adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); if (stdin_file_spec) m_gdb_comm.SetSTDIN(stdin_file_spec); if (stdout_file_spec) m_gdb_comm.SetSTDOUT(stdout_file_spec); if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket( GetTarget().GetArchitecture().GetArchitectureName()); const char *launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); if (working_dir) { m_gdb_comm.SetWorkingDir(working_dir); } // Send the environment and the program + arguments after we connect const Args &environment = launch_info.GetEnvironmentEntries(); if (environment.GetArgumentCount()) { size_t num_environment_entries = environment.GetArgumentCount(); for (size_t i = 0; i < num_environment_entries; ++i) { const char *env_entry = environment.GetArgumentAtIndex(i); if (env_entry == NULL || m_gdb_comm.SendEnvironmentPacket(env_entry) != 0) break; } } { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); if (arg_packet_err == 0) { std::string error_str; if (m_gdb_comm.GetLaunchSuccess(error_str)) { SetID(m_gdb_comm.GetCurrentProcessID()); } else { error.SetErrorString(error_str.c_str()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } } if (GetID() == LLDB_INVALID_PROCESS_ID) { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess(); return error; } StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); if (process_arch.IsValid()) { GetTarget().MergeArchitecture(process_arch); } else { const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); if (host_arch.IsValid()) GetTarget().MergeArchitecture(host_arch); } SetPrivateState(SetThreadStopInfo(response)); if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor(pty.ReleaseMasterFileDescriptor()); } } } else { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat( "failed to get object file from '%s' for arch %s", exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { Status error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!connect_url.empty()) { if (log) log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, connect_url.str().c_str()); std::unique_ptr conn_ap( new ConnectionFileDescriptor()); if (conn_ap.get()) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection(conn_ap.release()); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } retry_count++; if (retry_count >= max_retry_count) break; usleep(100000); } } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Start the communications read thread so all incoming data can be // parsed into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer(&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Send $QNonStop:1 packet on startup if required if (GetTarget().GetNonStopModeEnabled()) GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true)); m_gdb_comm.GetEchoSupported(); m_gdb_comm.GetThreadSuffixSupported(); m_gdb_comm.GetListThreadsInStopReplySupported(); m_gdb_comm.GetHostInfo(); m_gdb_comm.GetVContSupported('c'); m_gdb_comm.GetVAttachOrWaitSupported(); m_gdb_comm.EnableErrorStringInPacket(); // Ask the remote server for the default thread id if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.GetDefaultThreadId(m_initial_tid); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse( GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); if (GetID() != LLDB_INVALID_PROCESS_ID) { BuildDynamicRegisterInfo(false); // See if the GDB server supports the qHostInfo information // See if the GDB server supports the qProcessInfo packet, if so // prefer that over the Host information as it will be more specific // to our process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { process_arch = remote_process_arch; if (log) log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, " "using %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } else { process_arch = m_gdb_comm.GetHostArchitecture(); if (log) log->Printf("ProcessGDBRemote::%s gdb-remote did not have process " "architecture, using gdb-remote host architecture %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { if (log) log->Printf( "ProcessGDBRemote::%s analyzing target arch, currently %s %s", __FUNCTION__, target_arch.GetArchitectureName() ? target_arch.GetArchitectureName() : "", target_arch.GetTriple().getTriple().c_str() ? target_arch.GetTriple().getTriple().c_str() : ""); // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM // architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared // libraries // it has, so we really need to take the remote host architecture as our // defacto architecture in this case. if ((process_arch.GetMachine() == llvm::Triple::arm || process_arch.GetMachine() == llvm::Triple::thumb) && process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture(process_arch); if (log) log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, " "setting target arch to %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); llvm::Triple new_target_triple = target_arch.GetTriple(); if (new_target_triple.getVendorName().size() == 0) { new_target_triple.setVendor(remote_triple.getVendor()); if (new_target_triple.getOSName().size() == 0) { new_target_triple.setOS(remote_triple.getOS()); if (new_target_triple.getEnvironmentName().size() == 0) new_target_triple.setEnvironment( remote_triple.getEnvironment()); } ArchSpec new_target_arch = target_arch; new_target_arch.SetTriple(new_target_triple); GetTarget().SetArchitecture(new_target_arch); } } if (log) log->Printf("ProcessGDBRemote::%s final target arch after " "adjustments for remote architecture: %s %s", __FUNCTION__, target_arch.GetArchitectureName() ? target_arch.GetArchitectureName() : "", target_arch.GetTriple().getTriple().c_str() ? target_arch.GetTriple().getTriple().c_str() : ""); } else { // The target doesn't have a valid architecture yet, set it from // the architecture we got from the remote GDB server GetTarget().SetArchitecture(process_arch); } } // Find out which StructuredDataPlugins are supported by the // debug monitor. These plugins transmit data over async $J packets. auto supported_packets_array = m_gdb_comm.GetSupportedStructuredDataPlugins(); if (supported_packets_array) MapSupportedStructuredDataPlugins(*supported_packets_array); } } void ProcessGDBRemote::DidLaunch() { ArchSpec process_arch; DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::DoAttachToProcessWithID( lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; if (log) log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID(attach_pid); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len)); } else SetExitStatus(-1, error.AsCString()); } return error; } Status ProcessGDBRemote::DoAttachToProcessWithName( const char *process_name, const ProcessAttachInfo &attach_info) { Status error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { StreamString packet; m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet.GetString().data(), packet.GetSize())); } else SetExitStatus(-1, error.AsCString()); } return error; } lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options, Status &error) { return m_gdb_comm.SendStartTracePacket(options, error); } Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { return m_gdb_comm.SendStopTracePacket(uid, thread_id); } Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset) { return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset); } Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset) { return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset); } Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) { return m_gdb_comm.SendGetTraceConfigPacket(uid, options); } void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); } void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) { // If you can figure out what the architecture is, fill it in here. process_arch.Clear(); DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::WillResume() { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); m_jstopinfo_sp.reset(); m_jthreadsinfo_sp.reset(); return Status(); } Status ProcessGDBRemote::DoResume() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::Resume()"); ListenerSP listener_sp( Listener::MakeListener("gdb-remote.resume-packet-sent")); if (listener_sp->StartListeningForEvents( &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener_sp->StartListeningForEvents( &m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport()) { if (!GetTarget().GetNonStopModeEnabled() && (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString("c"); } else { continue_packet.PutCString("vCont"); if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (continue_packet_error) continue_packet.Clear(); } } else continue_packet_error = true; if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont // packet that wasn't supported by the remote GDB server. // We need to try and make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.PutChar('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front()); continue_packet.PutChar('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { if ((num_continue_C_tids + num_continue_c_tids) == num_threads && num_continue_C_tids > 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue // accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { if (m_continue_C_tids[i].second != continue_signo) continue_packet_error = true; } } if (!continue_packet_error) m_gdb_comm.SetCurrentThreadForRun(-1); } else { // Set the continue thread ID continue_packet_error = false; m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first); } if (!continue_packet_error) { // Add threads continuing with the same signo... continue_packet.Printf("C%2.2x", continue_signo); } } } if (continue_packet_error && num_continue_s_tids > 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); // If in Non-Stop-Mode use vCont when stepping if (GetTarget().GetNonStopModeEnabled()) { if (m_gdb_comm.GetVContSupported('s')) continue_packet.PutCString("vCont;s"); else continue_packet.PutChar('s'); } else continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 1 && num_continue_S_tids == 0) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front()); continue_packet.PutChar('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i = 1; i < num_threads; ++i) { if (m_continue_S_tids[i].second != step_signo) continue_packet_error = true; } } if (!continue_packet_error) { // Add threads stepping with the same signo... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.Printf("S%2.2x", step_signo); } } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 1) { // Only one thread is stepping with signal m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first); continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); continue_packet_error = false; } } } if (continue_packet_error) { error.SetErrorString("can't make continue packet for this resume"); } else { EventSP event_sp; if (!m_async_thread.IsJoinable()) { error.SetErrorString("Trying to resume but the async thread is dead."); if (log) log->Printf("ProcessGDBRemote::DoResume: Trying to resume but the " "async thread is dead."); return error; } m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(continue_packet.GetString().data(), continue_packet.GetSize())); if (listener_sp->GetEvent(event_sp, std::chrono::seconds(5)) == false) { error.SetErrorString("Resume timed out."); if (log) log->Printf("ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) { error.SetErrorString("Broadcast continue, but the async thread was " "killed before we got an ack back."); if (log) log->Printf("ProcessGDBRemote::DoResume: Broadcast continue, but the " "async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::HandleStopReplySequence() { while (true) { // Send vStopped StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); // OK represents end of signal list if (response.IsOKResponse()) break; // If not OK or a normal packet we have a problem if (!response.IsNormalResponse()) break; SetLastStopPacket(response); } } void ProcessGDBRemote::ClearThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); } size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) { m_thread_ids.clear(); m_thread_pcs.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); return m_thread_ids.size(); } size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) { m_thread_pcs.clear(); size_t comma_pos; lldb::addr_t pc; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back(pc); value.erase(0, comma_pos + 1); } pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_THREAD_ID) m_thread_pcs.push_back(pc); return m_thread_pcs.size(); } bool ProcessGDBRemote::UpdateThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); if (m_jthreadsinfo_sp) { // If we have the JSON threads info, we can get the thread list from that StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos && thread_infos->GetSize() > 0) { m_thread_ids.clear(); m_thread_pcs.clear(); thread_infos->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); if (thread_dict) { // Set the thread stop info from the JSON dictionary SetThreadStopInfo(thread_dict); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (thread_dict->GetValueForKeyAsInteger("tid", tid)) m_thread_ids.push_back(tid); } return true; // Keep iterating through all thread_info objects }); } if (!m_thread_ids.empty()) return true; } else { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair // Lock the thread stack while we access it // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); std::unique_lock stop_stack_lock( m_last_stop_packet_mutex, std::defer_lock); if (stop_stack_lock.try_lock()) { // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; const std::string &stop_info_str = stop_info.GetStringRef(); m_thread_pcs.clear(); const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); if (thread_pcs_pos != std::string::npos) { const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); UpdateThreadPCsFromStopReplyThreadsValue(value); } } const size_t threads_pos = stop_info_str.find(";threads:"); if (threads_pos != std::string::npos) { const size_t start = threads_pos + strlen(";threads:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); if (UpdateThreadIDsFromStopReplyThreadsValue(value)) return true; } } } } } bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD)); LLDB_LOGV(log, "pid = {0}", GetID()); size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i = 0; i < num_thread_ids; ++i) { tid_t tid = m_thread_ids[i]; ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { thread_sp.reset(new ThreadGDBRemote(*this, tid)); LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } else { LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } SetThreadPc(thread_sp, i); new_thread_list.AddThreadSortedByIndexID(thread_sp); } } // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id // table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i = 0; i < old_num_thread_ids; i++) { ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); if (old_thread_sp) { lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } return true; } void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) { if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && GetByteOrder() != eByteOrderInvalid) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); if (reg_ctx_sp) { uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]); } } } } bool ProcessGDBRemote::GetThreadStopInfoFromJSON( ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (thread_infos_sp) { StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); if (thread_infos) { lldb::tid_t tid; const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) { if (thread_dict->GetValueForKeyAsInteger( "tid", tid, LLDB_INVALID_THREAD_ID)) { if (tid == thread->GetID()) return (bool)SetThreadStopInfo(thread_dict); } } } } } return false; } bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp)) return true; // See if we got thread stop info for any threads valid stop info reasons // threads // via the "jstopinfo" packet stop reply packet key/value pair? if (m_jstopinfo_sp) { // If we have "jstopinfo" then we have stop descriptions for all threads // that have stop reasons, and if there is no entry for a thread, then // it has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { thread->SetStopInfo(StopInfoSP()); } return true; } // Fall back to using the qThreadStopInfo packet StringExtractorGDBRemote stop_packet; if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) return SetThreadStopInfo(stop_packet) == eStateStopped; return false; } ThreadSP ProcessGDBRemote::SetThreadStopInfo( lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, addr_t thread_dispatch_qaddr, bool queue_vars_valid, // Set to true if queue_name, queue_kind and // queue_serial are valid LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t, std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { ThreadSP thread_sp; if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to // m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us std::lock_guard guard( m_thread_list_real.GetMutex()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp.reset(new ThreadGDBRemote(*this, tid)); m_thread_list_real.AddThread(thread_sp); } } if (thread_sp) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid); if (iter != m_thread_ids.end()) { SetThreadPc(thread_sp, iter - m_thread_ids.begin()); } for (const auto &pair : expedited_register_map) { StringExtractor reg_value_extractor; reg_value_extractor.GetStringRef() = pair.second; DataBufferSP buffer_sp(new DataBufferHeap( reg_value_extractor.GetStringRef().size() / 2, 0)); reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc'); gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); } thread_sp->SetName(thread_name.empty() ? NULL : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr); // Check if the GDB server was able to provide the queue name, kind and // serial number if (queue_vars_valid) gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial, dispatch_queue_t, associated_with_dispatch_queue); else gdb_thread->ClearQueueInfo(); gdb_thread->SetAssociatedWithLibdispatchQueue( associated_with_dispatch_queue); if (dispatch_queue_t != LLDB_INVALID_ADDRESS) gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t); // Make sure we update our thread stop reason just once if (!thread_sp->StopInfoIsUpToDate()) { thread_sp->SetStopInfo(StopInfoSP()); // If there's a memory thread backed by this thread, we need to use it // to calcualte StopInfo. ThreadSP memory_thread_sp = m_thread_list.FindThreadByProtocolID(thread_sp->GetProtocolID()); if (memory_thread_sp) thread_sp = memory_thread_sp; if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo( StopInfoMachException::CreateStopReasonWithMachException( *thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); // If the current pc is a breakpoint site then the StopInfo should // be set to Breakpoint // Otherwise, it will be set to Trace. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, // we can just report no reason. We don't need to worry about // stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices // that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } } else if (reason.compare("trap") == 0) { // Let the trap just use the standard signal stop reason below... } else if (reason.compare("watchpoint") == 0) { StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { WatchpointSP wp_sp; ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); if ((core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) || (core >= ArchSpec::eCore_arm_generic && core <= ArchSpec::eCore_arm_aarch64)) wp_sp = GetTarget().GetWatchpointList().FindByAddress( wp_hit_addr); if (!wp_sp) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); if (wp_sp) { wp_sp->SetHardwareIndex(wp_index); watch_id = wp_sp->GetID(); } } if (watch_id == LLDB_INVALID_WATCH_ID) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet( GDBR_LOG_WATCHPOINTS)); if (log) log->Printf("failed to find watchpoint"); } thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID( *thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason.compare("exception") == 0) { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); handled = true; } else if (reason.compare("exec") == 0) { did_exec = true; thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } else if (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( pc); // If the current pc is a breakpoint site then the StopInfo should // be set to Breakpoint // even though the remote stub did not set it as such. This can // happen when // the thread is involuntarily interrupted (e.g. due to stops on // other // threads) just as it is about to execute the breakpoint // instruction. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); handled = true; } } if (!handled && signo && did_exec == false) { if (signo == SIGTRAP) { // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, // we can just report no reason. We don't need to worry about // stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices // that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread(thread_sp.get())) { if (m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of // the trace. If we were // not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step // over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); else thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } } if (!handled) thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo()); if (stop_info_sp) { const char *stop_info_desc = stop_info_sp->GetDescription(); if (!stop_info_desc || !stop_info_desc[0]) stop_info_sp->SetDescription(description.c_str()); } else { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); } } } } } } return thread_sp; } lldb::ThreadSP ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { static ConstString g_key_tid("tid"); static ConstString g_key_name("name"); static ConstString g_key_reason("reason"); static ConstString g_key_metype("metype"); static ConstString g_key_medata("medata"); static ConstString g_key_qaddr("qaddr"); static ConstString g_key_dispatch_queue_t("dispatch_queue_t"); static ConstString g_key_associated_with_dispatch_queue( "associated_with_dispatch_queue"); static ConstString g_key_queue_name("qname"); static ConstString g_key_queue_kind("qkind"); static ConstString g_key_queue_serial_number("qserialnum"); static ConstString g_key_registers("registers"); static ConstString g_key_memory("memory"); static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); static ConstString g_key_signal("signal"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint8_t signo = 0; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ExpeditedRegisterMap expedited_register_map; bool queue_vars_valid = false; addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; // Iterate through all of the thread dictionary key/value pairs from the // structured data dictionary thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, &associated_with_dispatch_queue, &dispatch_queue_t, &queue_name, &queue_kind, &queue_serial_number]( ConstString key, StructuredData::Object *object) -> bool { if (key == g_key_tid) { // thread in big endian hex tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); } else if (key == g_key_metype) { // exception type in big endian hex exc_type = object->GetIntegerValue(0); } else if (key == g_key_medata) { // exception data in big endian hex StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([&exc_data](StructuredData::Object *object) -> bool { exc_data.push_back(object->GetIntegerValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_name) { thread_name = object->GetStringValue(); } else if (key == g_key_qaddr) { thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); } else if (key == g_key_queue_name) { queue_vars_valid = true; queue_name = object->GetStringValue(); } else if (key == g_key_queue_kind) { std::string queue_kind_str = object->GetStringValue(); if (queue_kind_str == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (queue_kind_str == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key == g_key_queue_serial_number) { queue_serial_number = object->GetIntegerValue(0); if (queue_serial_number != 0) queue_vars_valid = true; } else if (key == g_key_dispatch_queue_t) { dispatch_queue_t = object->GetIntegerValue(0); if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS) queue_vars_valid = true; } else if (key == g_key_associated_with_dispatch_queue) { queue_vars_valid = true; bool associated = object->GetBooleanValue(); if (associated) associated_with_dispatch_queue = eLazyBoolYes; else associated_with_dispatch_queue = eLazyBoolNo; } else if (key == g_key_reason) { reason = object->GetStringValue(); } else if (key == g_key_description) { description = object->GetStringValue(); } else if (key == g_key_registers) { StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); if (registers_dict) { registers_dict->ForEach( [&expedited_register_map](ConstString key, StructuredData::Object *object) -> bool { const uint32_t reg = StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10); if (reg != UINT32_MAX) expedited_register_map[reg] = object->GetStringValue(); return true; // Keep iterating through all array items }); } } else if (key == g_key_memory) { StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); if (mem_cache_dict) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (mem_cache_dict->GetValueForKeyAsInteger( "address", mem_cache_addr)) { if (mem_cache_addr != LLDB_INVALID_ADDRESS) { llvm::StringRef str; if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) { StringExtractor bytes(str); bytes.SetFilePos(0); const size_t byte_size = bytes.GetStringRef().size() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } } return true; // Keep iterating through all array items }); } } else if (key == g_key_signal) signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); return true; // Keep iterating through all dictionary key/value pairs }); return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); } StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { stop_packet.SetFilePos(0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we // need to clear our thread lists and also know to rebuild our dynamic // register info before we lookup and threads and populate the expedited // register values so we need to know this right away so we can cleanup // and update our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); llvm::StringRef key; llvm::StringRef value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; ExpeditedRegisterMap expedited_register_map; while (stop_packet.GetNameColonValue(key, value)) { if (key.compare("metype") == 0) { // exception type in big endian hex value.getAsInteger(16, exc_type); } else if (key.compare("medata") == 0) { // exception data in big endian hex uint64_t x; value.getAsInteger(16, x); exc_data.push_back(x); } else if (key.compare("thread") == 0) { // thread in big endian hex if (value.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; } else if (key.compare("threads") == 0) { std::lock_guard guard( m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet lldb::tid_t tid; while (!value.empty()) { llvm::StringRef tid_str; std::tie(tid_str, value) = value.split(','); if (tid_str.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; m_thread_ids.push_back(tid); } } else if (key.compare("thread-pcs") == 0) { m_thread_pcs.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet lldb::addr_t pc; while (!value.empty()) { llvm::StringRef pc_str; std::tie(pc_str, value) = value.split(','); if (pc_str.getAsInteger(16, pc)) pc = LLDB_INVALID_ADDRESS; m_thread_pcs.push_back(pc); } } else if (key.compare("jstopinfo") == 0) { StringExtractor json_extractor(value); std::string json; // Now convert the HEX bytes into a string value json_extractor.GetHexByteString(json); // This JSON contains thread IDs and thread stop info for all threads. // It doesn't contain expedited registers, memory or queue info. m_jstopinfo_sp = StructuredData::ParseJSON(json); } else if (key.compare("hexname") == 0) { StringExtractor name_extractor(value); std::string name; // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(thread_name); } else if (key.compare("name") == 0) { thread_name = value; } else if (key.compare("qaddr") == 0) { value.getAsInteger(16, thread_dispatch_qaddr); } else if (key.compare("dispatch_queue_t") == 0) { queue_vars_valid = true; value.getAsInteger(16, dispatch_queue_t); } else if (key.compare("qname") == 0) { queue_vars_valid = true; StringExtractor name_extractor(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(queue_name); } else if (key.compare("qkind") == 0) { queue_kind = llvm::StringSwitch(value) .Case("serial", eQueueKindSerial) .Case("concurrent", eQueueKindConcurrent) .Default(eQueueKindUnknown); queue_vars_valid = queue_kind != eQueueKindUnknown; } else if (key.compare("qserialnum") == 0) { if (!value.getAsInteger(0, queue_serial_number)) queue_vars_valid = true; } else if (key.compare("reason") == 0) { reason = value; } else if (key.compare("description") == 0) { StringExtractor desc_extractor(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString(description); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited // memory // that can populate the L1 memory cache in the process so that things // like // the frame pointer backchain can be expedited. This will help stack // backtracing be more efficient by not having to send as many memory // read // requests down the remote GDB server. // Key/value pair format: memory:=; // is a number whose base will be interpreted by the prefix: // "0x[0-9a-fA-F]+" for hex // "0[0-7]+" for octal // "[1-9]+" for decimal // is native endian ASCII hex bytes just like the register // values llvm::StringRef addr_str, bytes_str; std::tie(addr_str, bytes_str) = value.split('='); if (!addr_str.empty() && !bytes_str.empty()) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (!addr_str.getAsInteger(0, mem_cache_addr)) { StringExtractor bytes(bytes_str); const size_t byte_size = bytes.GetBytesLeft() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || key.compare("awatch") == 0) { // Support standard GDB remote stop reply packet 'TAAwatch:addr' lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS; value.getAsInteger(16, wp_addr); WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); uint32_t wp_index = LLDB_INVALID_INDEX32; if (wp_sp) wp_index = wp_sp->GetHardwareIndex(); reason = "watchpoint"; StreamString ostr; ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); description = ostr.GetString(); } else if (key.compare("library") == 0) { LoadModules(); } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = UINT32_MAX; if (!key.getAsInteger(16, reg)) expedited_register_map[reg] = std::move(value); } } if (tid == LLDB_INVALID_THREAD_ID) { // A thread id may be invalid if the response is old style 'S' packet // which does not provide the // thread information. So update the thread list and choose the first one. UpdateThreadIDList(); if (!m_thread_ids.empty()) { tid = m_thread_ids.front(); } } ThreadSP thread_sp = SetThreadStopInfo( tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); return eStateStopped; } break; case 'W': case 'X': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. // Scope for the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; // Process thread stop info SetThreadStopInfo(stop_info); } // Clear the thread stop stack m_stop_packet_stack.clear(); } // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); m_initial_tid = LLDB_INVALID_THREAD_ID; } // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } Status ProcessGDBRemote::DoHalt(bool &caused_stop) { Status error; if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close // our file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else caused_stop = m_gdb_comm.Interrupt(); return error; } Status ProcessGDBRemote::DoDetach(bool keep_stopped) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); error = m_gdb_comm.Detach(keep_stopped); if (log) { if (error.Success()) log->PutCString( "ProcessGDBRemote::DoDetach() detach packet sent successfully"); else log->Printf("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread(); SetPrivateState(eStateDetached); ResumePrivateStateThread(); // KillDebugserverProcess (); return error; } Status ProcessGDBRemote::DoDestroy() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the // process // they are debugging properly. If the process is sitting at a breakpoint or // an exception, // this can cause problems with restarting. So we check to see if any of our // threads are stopped // at a breakpoint, and if so we remove all the breakpoints, resume the // process, and THEN // destroy it again. // // Note, we don't have a good way to test the version of debugserver, but I // happen to know that // the set of all the iOS debugservers which don't support // GetThreadSuffixSupported() and that of // the debugservers with this bug are equal. There really should be a better // way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if // we resume and then halt and // get called here to destroy again and we're still at a breakpoint or // exception, then we should // just do the straight-forward kill. // // And of course, if we weren't able to stop the process by the time we get // here, it isn't // necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to " "destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled // Process::Destroy, // but we really need it to happen here and it doesn't matter if we do // it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { if (log) log->Printf( "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } if (stop_looks_like_crash) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a " "breakpoint, continue and then kill."); m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to // suspend all the threads // before resuming so they won't get into more trouble. Sadly, for // the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if // it is suspended, so we do // have to run the risk of letting those threads proceed a bit. { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { if (log) log->Printf("ProcessGDBRemote::DoDestroy() - Suspending " "thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume(); return Destroy(false); } } } } // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; GDBRemoteCommunication::ScopedTimeout(m_gdb_comm, std::chrono::seconds(3)); if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) == GDBRemoteCommunication::PacketResult::Success) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host // Platform, then hand the process off // to debugserver, which becomes the parent process through // "PT_ATTACH". Then when we go to kill // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then we // call waitpid which returns // with no error and the correct status. But amusingly enough that // doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably // the kernel is in the process of // switching ownership back to lldb which was the original parent, and // gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { int status; ::pid_t reap_pid; reap_pid = waitpid(GetID(), &status, WNOHANG); if (log) log->Printf("Reaped pid: %d, status: %d.\n", reap_pid, status); } #endif SetLastStopPacket(response); ClearThreadIDList(); exit_status = response.GetHexU8(); } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - got unexpected response " "to k packet: %s", response.GetStringRef().c_str()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(response.GetStringRef()); } } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - killed or interrupted while " "attaching"); exit_string.assign("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the // status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread(); KillDebugserverProcess(); return error; } void ProcessGDBRemote::SetLastStopPacket( const StringExtractorGDBRemote &response) { const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo(true); m_gdb_comm.ResetDiscoverableSettings(did_exec); } // Scope the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // We are are not using non-stop mode, there can only be one last stop // reply packet, so clear the list. if (GetTarget().GetNonStopModeEnabled() == false) m_stop_packet_stack.clear(); // Add this stop packet to the stop packet stack // This stack will get popped and examined when we switch to the // Stopped state m_stop_packet_stack.push_back(response); } } void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { Process::SetUnixSignals(std::make_shared(signals_sp)); } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessGDBRemote::IsAlive() { return m_gdb_comm.IsConnected() && Process::IsAlive(); } addr_t ProcessGDBRemote::GetImageInfoAddress() { // request the link map address via the $qShlibInfoAddr packet lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); // the loaded module list can also provides a link map address if (addr == LLDB_INVALID_ADDRESS) { LoadedModuleInfoList list; if (GetLoadedModuleList(list).Success()) addr = list.m_link_map; } return addr; } void ProcessGDBRemote::WillPublicStop() { // See if the GDB remote client supports the JSON threads info. // If so, we gather stop info for all threads, expedited registers, // expedited memory, runtime queue information (iOS and MacOSX only), // and more. Expediting memory will help stack backtracing be much // faster. Expediting registers will make sure we don't have to read // the thread registers for GPRs. m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); if (m_jthreadsinfo_sp) { // Now set the stop info for each thread and also expedite any registers // and memory that was in the jThreadsInfo response. StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos) { const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) SetThreadStopInfo(thread_dict); } } } } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, Status &error) { GetMaxMemorySize(); bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } char packet[64]; int packet_len; packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, binary_memory_read ? 'x' : 'm', (uint64_t)addr, (uint64_t)size); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsNormalResponse()) { error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has // already de-quoted any // 0x7d character escaping that was present in the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us // too // much data for some reason. data_received_size = size; } memcpy(buf, response.GetStringRef().data(), data_received_size); return data_received_size; } else { return response.GetHexBytes( llvm::MutableArrayRef((uint8_t *)buf, size), '\xdd'); } } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support reading memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { GetMaxMemorySize(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } StreamString packet; packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support writing memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory write packet '%s': '%s'", packet.GetData(), response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetData()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) return allocated_addr; } if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else { allocated_addr = LLDB_INVALID_ADDRESS; if (log) log->Printf("ProcessGDBRemote::%s no direct stub support for memory " "allocation, and InferiorCallMmap also failed - is stub " "missing register context save/restore capability?", __FUNCTION__); } } if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat( "unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString(permissions)); else error.Clear(); return allocated_addr; } Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr, MemoryRegionInfo ®ion_info) { Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) { Status error(m_gdb_comm.GetWatchpointSupportInfo(num)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) { Status error(m_gdb_comm.GetWatchpointSupportInfo( num, after, GetTarget().GetArchitecture())); return error; } Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { Status error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString( "tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory(addr)) error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); break; case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase(pos); else error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len, Status &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } return 0; } Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != NULL); // Get logging info Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); // Get the breakpoint address const addr_t addr = bp_site->GetLoadAddress(); // Log that a breakpoint was requested if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); // Breakpoint already exists and is enabled if (bp_site->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } // Get the software breakpoint trap opcode size const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this // breakpoint type // is supported by the remote stub. These are set to true by default, and // later set to false // only after we receive an unimplemented response when sending a breakpoint // packet. This means // initially that unless we were specifically instructed to use a hardware // breakpoint, LLDB will // attempt to set a software breakpoint. HardwareRequired() also queries a // boolean variable which // indicates if the user specifically asked for hardware breakpoints. If true // then we will // skip over software breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointSoftware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); return error; } // SendGDBStoppointTypePacket() will return an error if it was unable to set // this // breakpoint. We need to differentiate between a error specific to placing // this breakpoint // or if we have learned that this breakpoint type is unsupported. To do // this, we // must test the support boolean for this breakpoint type to see if it now // indicates that // this breakpoint type is unsupported. If they are still supported then we // should return // with the error code. If they are now unsupported, then we would like to // fall through // and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the breakpoint request", errno); else error.SetErrorString("error sending the breakpoint request"); return error; } // We reach here when software breakpoints have been found to be // unsupported. For future // calls to set a breakpoint, we will not attempt to set a breakpoint with a // type that is // known not to be supported. if (log) log->Printf("Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } // The process of setting a hardware breakpoint is much the same as above. We // check the // supported boolean for this breakpoint type, and if it is thought to be // supported then we // will try to set this breakpoint with a hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointHardware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported breakpoint // type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the hardware breakpoint request " "(hardware breakpoint resources might be exhausted or unavailable)", error_no); else error.SetErrorString("error sending the hardware breakpoint request " "(hardware breakpoint resources " "might be exhausted or unavailable)"); return error; } // We will reach here when the stub gives an unsupported response to a // hardware breakpoint if (log) log->Printf("Hardware breakpoints are unsupported"); // Finally we will falling through to a #trap style breakpoint } // Don't fall through when hardware breakpoints were specifically requested if (bp_site->HardwareRequired()) { error.SetErrorString("hardware breakpoints are not supported"); return error; } // As a last resort we want to place a manual breakpoint. An instruction // is placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != NULL); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); if (log) log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint(bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: { GDBStoppointType stoppoint_type; if (bp_site->IsHardware()) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } break; } if (error.Success()) bp_site->SetEnabled(false); } else { if (log) log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket(type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); if (log) log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. // This disabling attempt might come from the user-supplied actions, we'll // route it in order for // the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_flags = 0; m_thread_list_real.Clear(); m_thread_list.Clear(); } Status ProcessGDBRemote::DoSignal(int signo) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal(signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Status ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) { // Make sure we aren't already connected? if (m_gdb_comm.IsConnected()) return Status(); PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && !platform_sp->IsHost()) return Status("Lost debug server connection"); auto error = LaunchAndConnectToDebugserver(process_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "unable to launch " DEBUGSERVER_BASENAME; } return error; } #if defined(__APPLE__) #define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1 #endif #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION static bool SetCloexecFlag(int fd) { #if defined(FD_CLOEXEC) int flags = ::fcntl(fd, F_GETFD); if (flags == -1) return false; return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); #else return false; #endif } #endif Status ProcessGDBRemote::LaunchAndConnectToDebugserver( const ProcessInfo &process_info) { using namespace std::placeholders; // For _1, _2, etc. Status error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; // Make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); const std::weak_ptr this_wp = std::static_pointer_cast(shared_from_this()); debugserver_launch_info.SetMonitorProcessCallback( std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false); debugserver_launch_info.SetUserID(process_info.GetUserID()); int communication_fd = -1; #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Auto close the sockets we might open up unless everything goes OK. This // helps us not leak file descriptors when things go wrong. lldb_utility::CleanUp our_socket(-1, -1, close); lldb_utility::CleanUp gdb_socket(-1, -1, close); // Use a socketpair on Apple for now until other platforms can verify it // works and is fast enough { int sockets[2]; /* the pair of socket descriptors */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { error.SetErrorToErrno(); return error; } our_socket.set(sockets[0]); gdb_socket.set(sockets[1]); } // Don't let any child processes inherit our communication socket SetCloexecFlag(our_socket.get()); communication_fd = gdb_socket.get(); #endif error = m_gdb_comm.StartDebugserverProcess( nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info, nullptr, nullptr, communication_fd); if (error.Success()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Our process spawned correctly, we can now set our connection to use our // end of the socket pair m_gdb_comm.SetConnection( new ConnectionFileDescriptor(our_socket.release(), true)); #endif StartAsyncThread(); } if (error.Fail()) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting // (send NULL URL) ConnectToDebugserver(""); } else { error.SetErrorString("connection failed"); } } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess( std::weak_ptr process_wp, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); const bool handled = true; if (log) log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", __FUNCTION__, debugserver_pid, signo, signo, exit_status); std::shared_ptr process_sp = process_wp.lock(); if (log) log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__, static_cast(process_sp.get())); if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) return handled; // Sleep for a half a second to make sure our inferior process has // time to set its exit status before we set it incorrectly when // both the debugserver and the inferior process shut down. usleep(500000); // If our process hasn't yet exited, debugserver might have died. // If the process did exit, then we are reaping it. const StateType state = process_sp->GetState(); if (state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); if (signal_cstr) ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process_sp->SetExitStatus(-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote // know that it no longer has a debugserver instance process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; return handled; } void ProcessGDBRemote::KillDebugserverProcess() { m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill(m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); }); } void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). m_async_thread = ThreadLauncher::LaunchThread("", ProcessGDBRemote::AsyncThread, this, NULL); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was " "already running.", __FUNCTION__); return m_async_thread.IsJoinable(); } void ProcessGDBRemote::StopAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread m_async_thread.Join(nullptr); m_async_thread.Reset(); } else if (log) log->Printf( "ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) { // get the packet at a string const std::string &pkt = packet.GetStringRef(); // skip %stop: StringExtractorGDBRemote stop_info(pkt.c_str() + 5); // pass as a thread stop info packet SetLastStopPacket(stop_info); // check for more stop reasons HandleStopReplySequence(); // if the process is stopped then we need to fake a resume // so that we can stop properly with the new break. This // is possible due to SetPrivateState() broadcasting the // state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } // since we have some stopped packets we can halt the process SetPrivateState(lldb::StateType::eStateStopped); return true; } thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote *)arg; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); EventSP event_sp; bool done = false; while (!done) { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes(); const size_t continue_cstr_len = continue_packet->GetByteSize(); if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr(continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; // If in Non-Stop-Mode if (process->GetTarget().GetNonStopModeEnabled()) { // send the vCont packet if (!process->GetGDBRemote().SendvContPacket( llvm::StringRef(continue_cstr, continue_cstr_len), response)) { // Something went wrong done = true; break; } } // If in All-Stop-Mode else { StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse( *process, *process->GetUnixSignals(), llvm::StringRef(continue_cstr, continue_cstr_len), response); // We need to immediately clear the thread ID list so we are sure // to get a valid list of threads. // The thread ID list might be contained within the "response", or // the stop reply packet that // caused the stop. So clear it now before we give the stop reply // packet to the process // using the process->SetLastStopPacket()... process->ClearThreadIDList(); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket(response); process->SetPrivateState(stop_state); break; case eStateExited: { process->SetLastStopPacket(response); process->ClearThreadIDList(); response.SetFilePos(1); int exit_status = response.GetHexU8(); std::string desc_string; if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { llvm::StringRef desc_str; llvm::StringRef desc_token; while (response.GetNameColonValue(desc_token, desc_str)) { if (desc_token != "description") continue; StringExtractor extractor(desc_str); extractor.GetHexByteString(desc_string); } } process->SetExitStatus(exit_status, desc_string.c_str()); done = true; break; } case eStateInvalid: { // Check to see if we were trying to attach and if we got back // the "E87" error code from debugserver -- this indicates that // the process is not debuggable. Return a slightly more // helpful // error message about why the attach failed. if (::strstr(continue_cstr, "vAttach") != NULL && response.GetError() == 0x87) { process->SetExitStatus(-1, "cannot attach to process due to " "System Integrity Protection"); } // E01 code from vAttach means that the attach failed if (::strstr(continue_cstr, "vAttach") != NULL && response.GetError() == 0x1) { process->SetExitStatus(-1, "unable to attach"); } else { process->SetExitStatus(-1, "lost connection"); } break; } default: process->SetPrivateState(stop_state); break; } // switch(stop_state) } // else // if in All-stop-mode } // if (continue_packet) } // case eBroadcastBitAysncContinue break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) { switch (event_type) { case Communication::eBroadcastBitReadThreadDidExit: process->SetExitStatus(-1, "lost connection"); done = true; break; case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { lldb_private::Event *event = event_sp.get(); const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); StringExtractorGDBRemote notify( (const char *)continue_packet->GetBytes()); // Hand this over to the process to handle process->HandleNotifyPacket(notify); break; } default: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } } else { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); return NULL; } // uint32_t // ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList // &matches, std::vector &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to // fire up a debugserver // // process and ask it for the list of processes. But if we are local, we // can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } // else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit( void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new // thread when it starts to // run so I can stop it if that's what I want to do. Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; } Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG(log, "Check if need to update ignored signals"); // QPassSignals package is not supported by the server, // there is no way we can ignore any signals on server side. if (!m_gdb_comm.GetQPassSignalsSupported()) return Status(); // No signals, nothing to send. if (m_unix_signals_sp == nullptr) return Status(); // Signals' version hasn't changed, no need to send anything. uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); if (new_signals_version == m_last_signals_version) { LLDB_LOG(log, "Signals' version hasn't changed. version={0}", m_last_signals_version); return Status(); } auto signals_to_ignore = m_unix_signals_sp->GetFilteredSignals(false, false, false); Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); LLDB_LOG(log, "Signals' version changed. old version={0}, new version={1}, " "signals ignored={2}, update result={3}", m_last_signals_version, new_signals_version, signals_to_ignore.size(), error); if (error.Success()) m_last_signals_version = new_signals_version; return error; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(GetTarget()); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf( "Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback( ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { if (log) log->Printf("Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != NULL; } bool ProcessGDBRemote::StopNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf("Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset(DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } Status ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; Status error; return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported); if (return_value != 0) { if (!was_supported) error.SetErrorString("Sending events is not supported for this process."); else error.SetErrorStringWithFormat("Error sending event data: %d.", return_value); } return error; } const DataBufferSP ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { std::string response_string; if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", response_string) == GDBRemoteCommunication::PacketResult::Success) buf.reset(new DataBufferHeap(response_string.c_str(), response_string.length())); } return buf; } StructuredData::ObjectSP ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetThreadExtendedInfoSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); SystemRuntime *runtime = GetSystemRuntime(); if (runtime) { runtime->AddThreadExtendedInfoPacketHints(args_dict); } args_dict->GetAsDictionary()->AddIntegerItem("thread", tid); StreamString packet; packet << "jThreadExtendedInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( lldb::addr_t image_list_address, lldb::addr_t image_count) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddIntegerItem("image_list_address", image_list_address); args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); StructuredData::ArraySP addresses(new StructuredData::Array); for (auto addr : load_addresses) { StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr)); addresses->AddItem(addr_sp); } args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( StructuredData::ObjectSP args_dict) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); StreamString packet; packet << "jGetLoadedDynamicLibrariesInfos:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { StructuredData::ObjectSP object_sp; StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); if (m_gdb_comm.GetSharedCacheInfoSupported()) { StreamString packet; packet << "jGetSharedCacheInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } Status ProcessGDBRemote::ConfigureStructuredData( const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); } // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. // // If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a // conservative default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; const uint64_t conservative_default = 512; if (m_max_memory_size == 0) { uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); if (stub_max_size != UINT64_MAX && stub_max_size != 0) { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; // Even if the stub says it can support ginormous packets, // don't exceed our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } // Memory packet have other overheads too like Maddr,size:#NN // Instead of calculating the bytes taken by size and addr every // time, we take a maximum guess here. if (stub_max_size > 70) stub_max_size -= 32 + 32 + 6; else { // In unlikely scenario that max packet size is less then 70, we will // hope that data being written is small enough to fit. Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_COMM | GDBR_LOG_MEMORY)); if (log) log->Warning("Packet size is too small. " "LLDB may face problems while writing memory"); } m_max_memory_size = stub_max_size; } else { m_max_memory_size = conservative_default; } } } void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize( uint64_t user_specified_max) { if (user_specified_max != 0) { GetMaxMemorySize(); if (m_remote_stub_max_memory_size != 0) { if (m_remote_stub_max_memory_size < user_specified_max) { m_max_memory_size = m_remote_stub_max_memory_size; // user specified a // packet size too // big, go as big // as the remote stub says we can go. } else { m_max_memory_size = user_specified_max; // user's packet size is good } } else { m_max_memory_size = user_specified_max; // user's packet size is probably fine } } } bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); const ModuleCacheKey key(module_file_spec.GetPath(), arch.GetTriple().getTriple()); auto cached = m_cached_module_specs.find(key); if (cached != m_cached_module_specs.end()) { module_spec = cached->second; return bool(module_spec); } if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) { if (log) log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str()); return false; } if (log) { StreamString stream; module_spec.Dump(stream); log->Printf("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str(), stream.GetData()); } m_cached_module_specs[key] = module_spec; return true; } void ProcessGDBRemote::PrefetchModuleSpecs( llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); if (module_specs) { for (const FileSpec &spec : module_file_specs) m_cached_module_specs[ModuleCacheKey(spec.GetPath(), triple.getTriple())] = ModuleSpec(); for (const ModuleSpec &spec : *module_specs) m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(), triple.getTriple())] = spec; } } bool ProcessGDBRemote::GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { if (m_gdb_comm.GetOSVersion(major, minor, update)) return true; // We failed to get the host OS version, defer to the base // implementation to correctly invalidate the arguments. return Process::GetHostOSVersion(major, minor, update); } namespace { typedef std::vector stringVec; typedef std::vector GDBServerRegisterVec; struct RegisterSetInfo { ConstString name; }; typedef std::map RegisterSetMap; struct GdbServerTargetInfo { std::string arch; std::string osabi; stringVec includes; RegisterSetMap reg_set_map; XMLNode feature_node; }; bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, uint32_t &cur_reg_num, uint32_t ®_offset) { if (!feature_node) return false; feature_node.ForEachChildElementWithName( "reg", [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, &abi_sp](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; bool encoding_set = false; bool format_set = false; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num cur_reg_num, // process plugin reg num cur_reg_num // native register number }, NULL, NULL, NULL, // Dwarf Expression opcode bytes pointer 0 // Dwarf Expression opcode bytes length }; reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, ®_offset, &dwarf_opcode_bytes]( const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { reg_name.SetString(value); } else if (name == "bitsize") { reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); if (regnum != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindProcessPlugin] = regnum; } } else if (name == "offset") { reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); } else if (name == "altname") { alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); } else if (name == "format") { format_set = true; Format format = eFormatInvalid; if (Args::StringToFormat(value.data(), format, NULL).Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; else if (value == "vector-uint8") reg_info.format = eFormatVectorOfUInt8; else if (value == "vector-sint16") reg_info.format = eFormatVectorOfSInt16; else if (value == "vector-uint16") reg_info.format = eFormatVectorOfUInt16; else if (value == "vector-sint32") reg_info.format = eFormatVectorOfSInt32; else if (value == "vector-uint32") reg_info.format = eFormatVectorOfUInt32; else if (value == "vector-float32") reg_info.format = eFormatVectorOfFloat32; else if (value == "vector-uint64") reg_info.format = eFormatVectorOfUInt64; else if (value == "vector-uint128") reg_info.format = eFormatVectorOfUInt128; } else if (name == "group_id") { const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) set_name = pos->second.name; } else if (name == "gcc_regnum" || name == "ehframe_regnum") { reg_info.kinds[eRegisterKindEHFrame] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "dwarf_regnum") { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "generic") { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name == "value_regnums") { SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); } else if (name == "invalidate_regnums") { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); } else if (name == "dynamic_size_dwarf_expr_bytes") { StringExtractor opcode_extractor; std::string opcode_string = value.str(); size_t dwarf_opcode_len = opcode_string.length() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; opcode_extractor.GetStringRef().swap(opcode_string); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } else { printf("unhandled attribute %s = %s\n", name.data(), value.data()); } return true; // Keep iterating through all attributes }); if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) { reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; } else if (gdb_type == "i387_ext" || gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } } // Only update the register set name if we didn't get a "reg_set" // attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } ++cur_reg_num; AugmentRegisterInfoViaABI(reg_info, reg_name, abi_sp); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); return true; // Keep iterating through all "reg" elements }); return true; } } // namespace {} // query the target of gdb-remote for extended target information // return: 'true' on success // 'false' on failure bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return false; // redirect libxml2's error handler since the default prints to stdout GDBRemoteCommunicationClient &comm = m_gdb_comm; // check that we have extended feature read support if (!comm.GetQXferFeaturesReadSupported()) return false; // request the target xml file std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), raw, lldberr)) { return false; } XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { XMLNode feature_node; target_node.ForEachChildElement([&target_info, &feature_node]( const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "architecture") { node.GetElementText(target_info.arch); } else if (name == "osabi") { node.GetElementText(target_info.osabi); } else if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } else if (name == "feature") { feature_node = node; } else if (name == "groups") { node.ForEachChildElementWithName( "group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; node.ForEachAttribute( [&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements }); } return true; // Keep iterating through all children of the target_node }); // Initialize these outside of ParseRegisters, since they should not be // reset inside each include feature uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; // Don't use Process::GetABI, this code gets called from DidAttach, and in // that context we haven't // set the Target's architecture yet, so the ABI is also potentially // incorrect. ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use); if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } for (const auto &include : target_info.includes) { // request register file std::string xml_data; if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), xml_data, lldberr)) continue; XMLDocument include_xml_document; include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), include.c_str()); XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); if (include_feature_node) { ParseRegisters(include_feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } } this->m_register_info.Finalize(arch_to_use); } } return m_register_info.GetNumRegisters() > 0; } Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return Status(0, ErrorType::eErrorTypeGeneric); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("ProcessGDBRemote::%s", __FUNCTION__); GDBRemoteCommunicationClient &comm = m_gdb_comm; // check that we have extended feature read support if (comm.GetQXferLibrariesSVR4ReadSupported()) { list.clear(); // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""), raw, lldberr)) return Status(0, ErrorType::eErrorTypeGeneric); // parse the xml file in memory if (log) log->Printf("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Status(0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list-svr4"); if (!root_element) return Status(); // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); if (!main_lm.empty()) { list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); } root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute( [&module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") module.set_name(value.str()); else if (name == "lm") { // the address of the link_map struct. module.set_link_map(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the // link_map struct. module.set_base(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); // base address is always a displacement, not an absolute // value. module.set_base_is_offset(true); } else if (name == "l_ld") { // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } return true; // Keep iterating over all properties of "library" }); if (log) { std::string name; lldb::addr_t lm = 0, base = 0, ld = 0; bool base_is_offset; module.get_name(name); module.get_link_map(lm); module.get_base(base); module.get_base_is_offset(base_is_offset); module.get_dynamic(ld); log->Printf("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 "[%s], ld:0x%08" PRIx64 ", name:'%s')", lm, base, (base_is_offset ? "offset" : "absolute"), ld, name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) log->Printf("found %" PRId32 " modules in total", (int)list.m_list.size()); } else if (comm.GetQXferLibrariesReadSupported()) { list.clear(); // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw, lldberr)) return Status(0, ErrorType::eErrorTypeGeneric); if (log) log->Printf("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Status(0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list"); if (!root_element) return Status(); root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; llvm::StringRef name = library.GetAttributeValue("name"); module.set_name(name.str()); // The base address of a given library will be the address of its // first section. Most remotes send only one section for Windows // targets for example. const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); module.set_base( StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); // These addresses are absolute values. module.set_base_is_offset(false); if (log) { std::string name; lldb::addr_t base = 0; bool base_is_offset; module.get_name(name); module.get_base(base); module.get_base_is_offset(base_is_offset); log->Printf("found (base:0x%08" PRIx64 "[%s], name:'%s')", base, (base_is_offset ? "offset" : "absolute"), name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) log->Printf("found %" PRId32 " modules in total", (int)list.m_list.size()); } else { return Status(0, ErrorType::eErrorTypeGeneric); } return Status(); } lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, bool value_is_offset) { DynamicLoader *loader = GetDynamicLoader(); if (!loader) return nullptr; return loader->LoadModuleAtAddress(file, link_map, base_addr, value_is_offset); } size_t ProcessGDBRemote::LoadModules(LoadedModuleInfoList &module_list) { using lldb_private::process_gdb_remote::ProcessGDBRemote; // request a list of loaded libraries from GDBServer if (GetLoadedModuleList(module_list).Fail()) return 0; // get a list of all the modules ModuleList new_modules; for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list.m_list) { std::string mod_name; lldb::addr_t mod_base; lldb::addr_t link_map; bool mod_base_is_offset; bool valid = true; valid &= modInfo.get_name(mod_name); valid &= modInfo.get_base(mod_base); valid &= modInfo.get_base_is_offset(mod_base_is_offset); if (!valid) continue; if (!modInfo.get_link_map(link_map)) link_map = LLDB_INVALID_ADDRESS; FileSpec file(mod_name, true); lldb::ModuleSP module_sp = LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset); if (module_sp.get()) new_modules.Append(module_sp); } if (new_modules.GetSize() > 0) { ModuleList removed_modules; Target &target = GetTarget(); ModuleList &loaded_modules = m_process->GetTarget().GetImages(); for (size_t i = 0; i < loaded_modules.GetSize(); ++i) { const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); bool found = false; for (size_t j = 0; j < new_modules.GetSize(); ++j) { if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) found = true; } // The main executable will never be included in libraries-svr4, don't // remove it if (!found && loaded_module.get() != target.GetExecutableModulePointer()) { removed_modules.Append(loaded_module); } } loaded_modules.Remove(removed_modules); m_process->GetTarget().ModulesDidUnload(removed_modules, false); new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool { lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); if (!obj) return true; if (obj->GetType() != ObjectFile::Type::eTypeExecutable) return true; lldb::ModuleSP module_copy_sp = module_sp; target.SetExecutableModule(module_copy_sp, false); return false; }); loaded_modules.AppendIfNeeded(new_modules); m_process->GetTarget().ModulesDidLoad(new_modules); } return new_modules.GetSize(); } size_t ProcessGDBRemote::LoadModules() { LoadedModuleInfoList module_list; return LoadModules(module_list); } Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; std::string file_path = file.GetPath(false); if (file_path.empty()) return Status("Empty file name specified"); StreamString packet; packet.PutCString("qFileLoadAddress:"); packet.PutCStringAsRawHex8(file_path.c_str()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) != GDBRemoteCommunication::PacketResult::Success) return Status("Sending qFileLoadAddress packet failed"); if (response.IsErrorResponse()) { if (response.GetError() == 1) { // The file is not loaded into the inferior is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; return Status(); } return Status( "Fetching file load address from remote server returned an error"); } if (response.IsNormalResponse()) { is_loaded = true; load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); return Status(); } return Status( "Unknown error happened during sending the load address packet"); } void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { // We must call the lldb_private::Process::ModulesDidLoad () first before we // do anything Process::ModulesDidLoad(module_list); // After loading shared libraries, we can ask our remote GDB server if // it needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) { AppendSTDOUT(out.data(), out.size()); } static const char *end_delimiter = "--end--;"; static const int end_delimiter_len = 8; void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) { std::string input = data.str(); // '1' to move beyond 'A' if (m_partial_profile_data.length() > 0) { m_partial_profile_data.append(input); input = m_partial_profile_data; m_partial_profile_data.clear(); } size_t found, pos = 0, len = input.length(); while ((found = input.find(end_delimiter, pos)) != std::string::npos) { StringExtractorGDBRemote profileDataExtractor( input.substr(pos, found).c_str()); std::string profile_data = HarmonizeThreadIdsForProfileData(profileDataExtractor); BroadcastAsyncProfileData(profile_data); pos = found + end_delimiter_len; } if (pos < len) { // Last incomplete chunk. m_partial_profile_data = input.substr(pos); } } std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &profileDataExtractor) { std::map new_thread_id_to_used_usec_map; std::string output; llvm::raw_string_ostream output_stream(output); llvm::StringRef name, value; // Going to assuming thread_used_usec comes first, else bail out. while (profileDataExtractor.GetNameColonValue(name, value)) { if (name.compare("thread_used_id") == 0) { StringExtractor threadIDHexExtractor(value); uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); bool has_used_usec = false; uint32_t curr_used_usec = 0; llvm::StringRef usec_name, usec_value; uint32_t input_file_pos = profileDataExtractor.GetFilePos(); if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { if (usec_name.equals("thread_used_usec")) { has_used_usec = true; usec_value.getAsInteger(0, curr_used_usec); } else { // We didn't find what we want, it is probably // an older version. Bail out. profileDataExtractor.SetFilePos(input_file_pos); } } if (has_used_usec) { uint32_t prev_used_usec = 0; std::map::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); if (iterator != m_thread_id_to_used_usec_map.end()) { prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; } uint32_t real_used_usec = curr_used_usec - prev_used_usec; // A good first time record is one that runs for at least 0.25 sec bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); bool good_subsequent_time = (prev_used_usec > 0) && ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id))); if (good_first_time || good_subsequent_time) { // We try to avoid doing too many index id reservation, // resulting in fast increase of index ids. output_stream << name << ":"; int32_t index_id = AssignIndexIDToThread(thread_id); output_stream << index_id << ";"; output_stream << usec_name << ":" << usec_value << ";"; } else { // Skip past 'thread_used_name'. llvm::StringRef local_name, local_value; profileDataExtractor.GetNameColonValue(local_name, local_value); } // Store current time as previous time so that they can be compared // later. new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; } else { // Bail out and use old string. output_stream << name << ":" << value << ";"; } } else { output_stream << name << ":" << value << ";"; } } output_stream << end_delimiter; m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; return output_stream.str(); } void ProcessGDBRemote::HandleStopReply() { if (GetStopID() != 0) return; if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } static const char *const s_async_json_packet_prefix = "JSON-async:"; static StructuredData::ObjectSP ParseStructuredDataPacket(llvm::StringRef packet) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!packet.consume_front(s_async_json_packet_prefix)) { if (log) { log->Printf( "GDBRemoteCommmunicationClientBase::%s() received $J packet " "but was not a StructuredData packet: packet starts with " "%s", __FUNCTION__, packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str()); } return StructuredData::ObjectSP(); } // This is an asynchronous JSON packet, destined for a // StructuredDataPlugin. StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet); if (log) { if (json_sp) { StreamString json_str; json_sp->Dump(json_str); json_str.Flush(); log->Printf("ProcessGDBRemote::%s() " "received Async StructuredData packet: %s", __FUNCTION__, json_str.GetData()); } else { log->Printf("ProcessGDBRemote::%s" "() received StructuredData packet:" " parse failure", __FUNCTION__); } } return json_sp; } void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) { auto structured_data_sp = ParseStructuredDataPacket(data); if (structured_data_sp) RouteAsyncStructuredData(structured_data_sp); } class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed { public: CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet speed-test", "Tests packet speeds of various sizes to determine " "the performance characteristics of the GDB remote " "connection. ", NULL), m_option_group(), m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size " "(default is 1000).", 1000), m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_json(LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) { m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessGDBRemoteSpeedTest() {} Options *GetOptions() override { return &m_option_group; } bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { StreamSP output_stream_sp( m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream(output_stream_sp); const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); const bool json = m_json.GetOptionValue().GetCurrentValue(); const uint64_t k_recv_amount = 4 * 1024 * 1024; // Receive amount in bytes process->GetGDBRemote().TestPacketSpeed( num_packets, max_send, max_recv, k_recv_amount, json, output_stream_sp ? *output_stream_sp : result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } protected: OptionGroupOptions m_option_group; OptionGroupUInt64 m_num_packets; OptionGroupUInt64 m_max_send; OptionGroupUInt64 m_max_recv; OptionGroupBoolean m_json; }; class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet history", "Dumps the packet history buffer. ", NULL) {} ~CommandObjectProcessGDBRemotePacketHistory() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", NULL) {} ~CommandObjectProcessGDBRemotePacketXferSize() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat("'%s' takes an argument to specify the max " "amount to be transferred when " "reading/writing", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const char *packet_size = command.GetArgumentAtIndex(0); errno = 0; uint64_t user_specified_max = strtoul(packet_size, NULL, 10); if (errno == 0 && user_specified_max != 0) { process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet send", "Send a custom packet through the GDB remote " "protocol and print the answer. " "The packet header and footer will automatically " "be added to the packet prior to sending and " "stripped from the result.", NULL) {} ~CommandObjectProcessGDBRemotePacketSend() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat( "'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i = 0; i < argc; ++i) { const char *packet_cstr = command.GetArgumentAtIndex(0); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse( packet_cstr, response, send_async); result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf(" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->HarmonizeThreadIdsForProfileData(response); } if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " "and print the response." "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} ~CommandObjectProcessGDBRemotePacketMonitor() {} bool DoExecute(const char *command, CommandReturnObject &result) override { if (command == NULL || command[0] == '\0') { result.AppendErrorWithFormat("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse( packet.GetString(), response, send_async); result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf(" packet: %s\n", packet.GetData()); const std::string &response_str = response.GetStringRef(); if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", NULL) { LoadSubCommand( "history", CommandObjectSP( new CommandObjectProcessGDBRemotePacketHistory(interpreter))); LoadSubCommand( "send", CommandObjectSP( new CommandObjectProcessGDBRemotePacketSend(interpreter))); LoadSubCommand( "monitor", CommandObjectSP( new CommandObjectProcessGDBRemotePacketMonitor(interpreter))); LoadSubCommand( "xfer-size", CommandObjectSP( new CommandObjectProcessGDBRemotePacketXferSize(interpreter))); LoadSubCommand("speed-test", CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest( interpreter))); } ~CommandObjectProcessGDBRemotePacket() {} }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process plugin", "Commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand( "packet", CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter))); } ~CommandObjectMultiwordProcessGDBRemote() {} }; CommandObject *ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset(new CommandObjectMultiwordProcessGDBRemote( GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); } Index: vendor/lldb/dist/source/Target/Target.cpp =================================================================== --- vendor/lldb/dist/source/Target/Target.cpp (revision 321193) +++ vendor/lldb/dist/source/Target/Target.cpp (revision 321194) @@ -1,4135 +1,4136 @@ //===-- Target.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "Plugins/ExpressionParser/Clang/ClangASTSource.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointResolver.h" #include "lldb/Breakpoint/BreakpointResolverAddress.h" #include "lldb/Breakpoint/BreakpointResolverFileLine.h" #include "lldb/Breakpoint/BreakpointResolverFileRegex.h" #include "lldb/Breakpoint/BreakpointResolverName.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Event.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/REPL.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Host/Host.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupWatchpoint.h" #include "lldb/Interpreter/OptionValues.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Language.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" using namespace lldb; using namespace lldb_private; constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout; ConstString &Target::GetStaticBroadcasterClass() { static ConstString class_name("lldb.target"); return class_name; } Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp, bool is_dummy_target) : TargetProperties(this), Broadcaster(debugger.GetBroadcasterManager(), Target::GetStaticBroadcasterClass().AsCString()), ExecutionContextScope(), m_debugger(debugger), m_platform_sp(platform_sp), m_mutex(), m_arch(target_arch), m_images(this), m_section_load_history(), m_breakpoint_list(false), m_internal_breakpoint_list(true), m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), m_image_search_paths(ImageSearchPathsChanged, this), m_ast_importer_sp(), m_source_manager_ap(), m_stop_hooks(), m_stop_hook_next_id(0), m_valid(true), m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target) { SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); SetEventName(eBroadcastBitModulesLoaded, "modules-loaded"); SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); CheckInWithManager(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Target::Target()", static_cast(this)); if (m_arch.IsValid()) { LogIfAnyCategoriesSet( LIBLLDB_LOG_TARGET, "Target::Target created with architecture %s (%s)", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); } } Target::~Target() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Target::~Target()", static_cast(this)); DeleteCurrentProcess(); } void Target::PrimeFromDummyTarget(Target *target) { if (!target) return; m_stop_hooks = target->m_stop_hooks; for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) { if (breakpoint_sp->IsInternal()) continue; BreakpointSP new_bp(new Breakpoint(*this, *breakpoint_sp.get())); AddBreakpoint(new_bp, false); } } void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) { // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); if (description_level != lldb::eDescriptionLevelBrief) { s->Indent(); s->PutCString("Target\n"); s->IndentMore(); m_images.Dump(s); m_breakpoint_list.Dump(s); m_internal_breakpoint_list.Dump(s); s->IndentLess(); } else { Module *exe_module = GetExecutableModulePointer(); if (exe_module) s->PutCString(exe_module->GetFileSpec().GetFilename().GetCString()); else s->PutCString("No executable module."); } } void Target::CleanupProcess() { // Do any cleanup of the target we need to do between process instances. // NB It is better to do this before destroying the process in case the // clean up needs some help from the process. m_breakpoint_list.ClearAllBreakpointSites(); m_internal_breakpoint_list.ClearAllBreakpointSites(); // Disable watchpoints just on the debugger side. std::unique_lock lock; this->GetWatchpointList().GetListMutex(lock); DisableAllWatchpoints(false); ClearAllWatchpointHitCounts(); ClearAllWatchpointHistoricValues(); } void Target::DeleteCurrentProcess() { if (m_process_sp) { m_section_load_history.Clear(); if (m_process_sp->IsAlive()) m_process_sp->Destroy(false); m_process_sp->Finalize(); CleanupProcess(); m_process_sp.reset(); } } const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp, llvm::StringRef plugin_name, const FileSpec *crash_file) { DeleteCurrentProcess(); m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, listener_sp, crash_file); return m_process_sp; } const lldb::ProcessSP &Target::GetProcessSP() const { return m_process_sp; } lldb::REPLSP Target::GetREPL(Status &err, lldb::LanguageType language, const char *repl_options, bool can_create) { if (language == eLanguageTypeUnknown) { std::set repl_languages; Language::GetLanguagesSupportingREPLs(repl_languages); if (repl_languages.size() == 1) { language = *repl_languages.begin(); } else if (repl_languages.size() == 0) { err.SetErrorStringWithFormat( "LLDB isn't configured with REPL support for any languages."); return REPLSP(); } else { err.SetErrorStringWithFormat( "Multiple possible REPL languages. Please specify a language."); return REPLSP(); } } REPLMap::iterator pos = m_repl_map.find(language); if (pos != m_repl_map.end()) { return pos->second; } if (!can_create) { err.SetErrorStringWithFormat( "Couldn't find an existing REPL for %s, and can't create a new one", Language::GetNameForLanguageType(language)); return lldb::REPLSP(); } Debugger *const debugger = nullptr; lldb::REPLSP ret = REPL::Create(err, language, debugger, this, repl_options); if (ret) { m_repl_map[language] = ret; return m_repl_map[language]; } if (err.Success()) { err.SetErrorStringWithFormat("Couldn't create a REPL for %s", Language::GetNameForLanguageType(language)); } return lldb::REPLSP(); } void Target::SetREPL(lldb::LanguageType language, lldb::REPLSP repl_sp) { lldbassert(!m_repl_map.count(language)); m_repl_map[language] = repl_sp; } void Target::Destroy() { std::lock_guard guard(m_mutex); m_valid = false; DeleteCurrentProcess(); m_platform_sp.reset(); m_arch.Clear(); ClearModules(true); m_section_load_history.Clear(); const bool notify = false; m_breakpoint_list.RemoveAll(notify); m_internal_breakpoint_list.RemoveAll(notify); m_last_created_breakpoint.reset(); m_last_created_watchpoint.reset(); m_search_filter_sp.reset(); m_image_search_paths.Clear(notify); m_stop_hooks.clear(); m_stop_hook_next_id = 0; m_suppress_stop_hooks = false; } BreakpointList &Target::GetBreakpointList(bool internal) { if (internal) return m_internal_breakpoint_list; else return m_breakpoint_list; } const BreakpointList &Target::GetBreakpointList(bool internal) const { if (internal) return m_internal_breakpoint_list; else return m_breakpoint_list; } BreakpointSP Target::GetBreakpointByID(break_id_t break_id) { BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); return bp_sp; } BreakpointSP Target::CreateSourceRegexBreakpoint( const FileSpecList *containingModules, const FileSpecList *source_file_spec_list, const std::unordered_set &function_names, RegularExpression &source_regex, bool internal, bool hardware, LazyBool move_to_nearest_code) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( containingModules, source_file_spec_list)); if (move_to_nearest_code == eLazyBoolCalculate) move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex( nullptr, source_regex, function_names, !static_cast(move_to_nearest_code))); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, const FileSpec &file, uint32_t line_no, lldb::addr_t offset, LazyBool check_inlines, LazyBool skip_prologue, bool internal, bool hardware, LazyBool move_to_nearest_code) { FileSpec remapped_file; ConstString remapped_path; if (GetSourcePathMap().ReverseRemapPath(ConstString(file.GetPath().c_str()), remapped_path)) remapped_file.SetFile(remapped_path.AsCString(), true); else remapped_file = file; if (check_inlines == eLazyBoolCalculate) { const InlineStrategy inline_strategy = GetInlineStrategy(); switch (inline_strategy) { case eInlineBreakpointsNever: check_inlines = eLazyBoolNo; break; case eInlineBreakpointsHeaders: if (remapped_file.IsSourceImplementationFile()) check_inlines = eLazyBoolNo; else check_inlines = eLazyBoolYes; break; case eInlineBreakpointsAlways: check_inlines = eLazyBoolYes; break; } } SearchFilterSP filter_sp; if (check_inlines == eLazyBoolNo) { // Not checking for inlines, we are looking only for matching compile units FileSpecList compile_unit_list; compile_unit_list.Append(remapped_file); filter_sp = GetSearchFilterForModuleAndCUList(containingModules, &compile_unit_list); } else { filter_sp = GetSearchFilterForModuleList(containingModules); } if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (move_to_nearest_code == eLazyBoolCalculate) move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( nullptr, remapped_file, line_no, offset, check_inlines, skip_prologue, !static_cast(move_to_nearest_code))); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal, bool hardware) { Address so_addr; // Check for any reason we want to move this breakpoint to other address. addr = GetBreakableLoadAddress(addr); // Attempt to resolve our load address if possible, though it is ok if // it doesn't resolve to section/offset. // Try and resolve as a load address if possible GetSectionLoadList().ResolveLoadAddress(addr, so_addr); if (!so_addr.IsValid()) { // The address didn't resolve, so just set this as an absolute address so_addr.SetOffset(addr); } BreakpointSP bp_sp(CreateBreakpoint(so_addr, internal, hardware)); return bp_sp; } BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal, bool hardware) { SearchFilterSP filter_sp( new SearchFilterForUnconstrainedSearches(shared_from_this())); BreakpointResolverSP resolver_sp( new BreakpointResolverAddress(nullptr, addr)); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false); } lldb::BreakpointSP Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal, const FileSpec *file_spec, bool request_hardware) { SearchFilterSP filter_sp( new SearchFilterForUnconstrainedSearches(shared_from_this())); BreakpointResolverSP resolver_sp( new BreakpointResolverAddress(nullptr, file_addr, file_spec)); return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware, false); } BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const char *func_name, uint32_t func_name_type_mask, LanguageType language, lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (func_name) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp(new BreakpointResolverName( nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, offset, skip_prologue)); bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } lldb::BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const std::vector &func_names, uint32_t func_name_type_mask, LanguageType language, lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; size_t num_names = func_names.size(); if (num_names > 0) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp( new BreakpointResolverName(nullptr, func_names, func_name_type_mask, language, offset, skip_prologue)); bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } BreakpointSP Target::CreateBreakpoint( const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const char *func_names[], size_t num_names, uint32_t func_name_type_mask, LanguageType language, lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (num_names > 0) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) { if (offset == 0) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; else skip_prologue = eLazyBoolNo; } if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp(new BreakpointResolverName( nullptr, func_names, num_names, func_name_type_mask, language, offset, skip_prologue)); resolver_sp->SetOffset(offset); bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } SearchFilterSP Target::GetSearchFilterForModule(const FileSpec *containingModule) { SearchFilterSP filter_sp; if (containingModule != nullptr) { // TODO: We should look into sharing module based search filters // across many breakpoints like we do for the simple target based one filter_sp.reset( new SearchFilterByModule(shared_from_this(), *containingModule)); } else { if (!m_search_filter_sp) m_search_filter_sp.reset( new SearchFilterForUnconstrainedSearches(shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; } SearchFilterSP Target::GetSearchFilterForModuleList(const FileSpecList *containingModules) { SearchFilterSP filter_sp; if (containingModules && containingModules->GetSize() != 0) { // TODO: We should look into sharing module based search filters // across many breakpoints like we do for the simple target based one filter_sp.reset( new SearchFilterByModuleList(shared_from_this(), *containingModules)); } else { if (!m_search_filter_sp) m_search_filter_sp.reset( new SearchFilterForUnconstrainedSearches(shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; } SearchFilterSP Target::GetSearchFilterForModuleAndCUList( const FileSpecList *containingModules, const FileSpecList *containingSourceFiles) { if (containingSourceFiles == nullptr || containingSourceFiles->GetSize() == 0) return GetSearchFilterForModuleList(containingModules); SearchFilterSP filter_sp; if (containingModules == nullptr) { // We could make a special "CU List only SearchFilter". Better yet was if // these could be composable, // but that will take a little reworking. filter_sp.reset(new SearchFilterByModuleListAndCU( shared_from_this(), FileSpecList(), *containingSourceFiles)); } else { filter_sp.reset(new SearchFilterByModuleListAndCU( shared_from_this(), *containingModules, *containingSourceFiles)); } return filter_sp; } BreakpointSP Target::CreateFuncRegexBreakpoint( const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, RegularExpression &func_regex, lldb::LanguageType requested_language, LazyBool skip_prologue, bool internal, bool hardware) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( containingModules, containingSourceFiles)); bool skip = (skip_prologue == eLazyBoolCalculate) ? GetSkipPrologue() : static_cast(skip_prologue); BreakpointResolverSP resolver_sp(new BreakpointResolverName( nullptr, func_regex, requested_language, 0, skip)); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } lldb::BreakpointSP Target::CreateExceptionBreakpoint(enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal, Args *additional_args, Status *error) { BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint( *this, language, catch_bp, throw_bp, internal); if (exc_bkpt_sp && additional_args) { Breakpoint::BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition(); if (precondition_sp && additional_args) { if (error) *error = precondition_sp->ConfigurePrecondition(*additional_args); else precondition_sp->ConfigurePrecondition(*additional_args); } } return exc_bkpt_sp; } BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal, bool request_hardware, bool resolve_indirect_symbols) { BreakpointSP bp_sp; if (filter_sp && resolver_sp) { bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, request_hardware, resolve_indirect_symbols)); resolver_sp->SetBreakpoint(bp_sp.get()); AddBreakpoint(bp_sp, internal); } return bp_sp; } void Target::AddBreakpoint(lldb::BreakpointSP bp_sp, bool internal) { if (!bp_sp) return; if (internal) m_internal_breakpoint_list.Add(bp_sp, false); else m_breakpoint_list.Add(bp_sp, true); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) { StreamString s; bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); log->Printf("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); } bp_sp->ResolveBreakpoint(); if (!internal) { m_last_created_breakpoint = bp_sp; } } bool Target::ProcessIsValid() { return (m_process_sp && m_process_sp->IsAlive()); } static bool CheckIfWatchpointsExhausted(Target *target, Status &error) { uint32_t num_supported_hardware_watchpoints; Status rc = target->GetProcessSP()->GetWatchpointSupportInfo( num_supported_hardware_watchpoints); if (num_supported_hardware_watchpoints == 0) { error.SetErrorStringWithFormat( "Target supports (%u) hardware watchpoint slots.\n", num_supported_hardware_watchpoints); return false; } return true; } // See also Watchpoint::SetWatchpointType(uint32_t type) and // the OptionGroupWatchpoint::WatchType enum type. WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, const CompilerType *type, uint32_t kind, Status &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64 " type = %u)\n", __FUNCTION__, addr, (uint64_t)size, kind); WatchpointSP wp_sp; if (!ProcessIsValid()) { error.SetErrorString("process is not alive"); return wp_sp; } if (addr == LLDB_INVALID_ADDRESS || size == 0) { if (size == 0) error.SetErrorString("cannot set a watchpoint with watch_size of 0"); else error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr); return wp_sp; } if (!LLDB_WATCH_TYPE_IS_VALID(kind)) { error.SetErrorStringWithFormat("invalid watchpoint type: %d", kind); } if (!CheckIfWatchpointsExhausted(this, error)) return wp_sp; // Currently we only support one watchpoint per address, with total number // of watchpoints limited by the hardware which the inferior is running on. // Grab the list mutex while doing operations. const bool notify = false; // Don't notify about all the state changes we do // on creating the watchpoint. std::unique_lock lock; this->GetWatchpointList().GetListMutex(lock); WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); if (matched_sp) { size_t old_size = matched_sp->GetByteSize(); uint32_t old_type = (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) | (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0); // Return the existing watchpoint if both size and type match. if (size == old_size && kind == old_type) { wp_sp = matched_sp; wp_sp->SetEnabled(false, notify); } else { // Nil the matched watchpoint; we will be creating a new one. m_process_sp->DisableWatchpoint(matched_sp.get(), notify); m_watchpoint_list.Remove(matched_sp->GetID(), true); } } if (!wp_sp) { wp_sp.reset(new Watchpoint(*this, addr, size, type)); wp_sp->SetWatchpointType(kind, notify); m_watchpoint_list.Add(wp_sp, true); } error = m_process_sp->EnableWatchpoint(wp_sp.get(), notify); if (log) log->Printf("Target::%s (creation of watchpoint %s with id = %u)\n", __FUNCTION__, error.Success() ? "succeeded" : "failed", wp_sp->GetID()); if (error.Fail()) { // Enabling the watchpoint on the device side failed. // Remove the said watchpoint from the list maintained by the target // instance. m_watchpoint_list.Remove(wp_sp->GetID(), true); // See if we could provide more helpful error message. if (!OptionGroupWatchpoint::IsWatchSizeSupported(size)) error.SetErrorStringWithFormat( "watch size of %" PRIu64 " is not supported", (uint64_t)size); wp_sp.reset(); } else m_last_created_watchpoint = wp_sp; return wp_sp; } void Target::RemoveAllBreakpoints(bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.RemoveAll(true); if (internal_also) m_internal_breakpoint_list.RemoveAll(false); m_last_created_breakpoint.reset(); } void Target::DisableAllBreakpoints(bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.SetEnabledAll(false); if (internal_also) m_internal_breakpoint_list.SetEnabledAll(false); } void Target::EnableAllBreakpoints(bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.SetEnabledAll(true); if (internal_also) m_internal_breakpoint_list.SetEnabledAll(true); } bool Target::RemoveBreakpointByID(break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); if (DisableBreakpointByID(break_id)) { if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) m_internal_breakpoint_list.Remove(break_id, false); else { if (m_last_created_breakpoint) { if (m_last_created_breakpoint->GetID() == break_id) m_last_created_breakpoint.reset(); } m_breakpoint_list.Remove(break_id, true); } return true; } return false; } bool Target::DisableBreakpointByID(break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); if (bp_sp) { bp_sp->SetEnabled(false); return true; } return false; } bool Target::EnableBreakpointByID(break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); if (bp_sp) { bp_sp->SetEnabled(true); return true; } return false; } Status Target::SerializeBreakpointsToFile(const FileSpec &file, const BreakpointIDList &bp_ids, bool append) { Status error; if (!file) { error.SetErrorString("Invalid FileSpec."); return error; } std::string path(file.GetPath()); StructuredData::ObjectSP input_data_sp; StructuredData::ArraySP break_store_sp; StructuredData::Array *break_store_ptr = nullptr; if (append) { input_data_sp = StructuredData::ParseJSONFromFile(file, error); if (error.Success()) { break_store_ptr = input_data_sp->GetAsArray(); if (!break_store_ptr) { error.SetErrorStringWithFormat( "Tried to append to invalid input file %s", path.c_str()); return error; } } } if (!break_store_ptr) { break_store_sp.reset(new StructuredData::Array()); break_store_ptr = break_store_sp.get(); } StreamFile out_file(path.c_str(), File::OpenOptions::eOpenOptionTruncate | File::OpenOptions::eOpenOptionWrite | File::OpenOptions::eOpenOptionCanCreate | File::OpenOptions::eOpenOptionCloseOnExec, lldb::eFilePermissionsFileDefault); if (!out_file.GetFile().IsValid()) { error.SetErrorStringWithFormat("Unable to open output file: %s.", path.c_str()); return error; } std::unique_lock lock; GetBreakpointList().GetListMutex(lock); if (bp_ids.GetSize() == 0) { const BreakpointList &breakpoints = GetBreakpointList(); size_t num_breakpoints = breakpoints.GetSize(); for (size_t i = 0; i < num_breakpoints; i++) { Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); // If a breakpoint can't serialize it, just ignore it for now: if (bkpt_save_sp) break_store_ptr->AddItem(bkpt_save_sp); } } else { std::unordered_set processed_bkpts; const size_t count = bp_ids.GetSize(); for (size_t i = 0; i < count; ++i) { BreakpointID cur_bp_id = bp_ids.GetBreakpointIDAtIndex(i); lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID(); if (bp_id != LLDB_INVALID_BREAK_ID) { // Only do each breakpoint once: std::pair::iterator, bool> insert_result = processed_bkpts.insert(bp_id); if (!insert_result.second) continue; Breakpoint *bp = GetBreakpointByID(bp_id).get(); StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); // If the user explicitly asked to serialize a breakpoint, and we // can't, then // raise an error: if (!bkpt_save_sp) { error.SetErrorStringWithFormat("Unable to serialize breakpoint %d", bp_id); return error; } break_store_ptr->AddItem(bkpt_save_sp); } } } break_store_ptr->Dump(out_file, false); out_file.PutChar('\n'); return error; } Status Target::CreateBreakpointsFromFile(const FileSpec &file, BreakpointIDList &new_bps) { std::vector no_names; return CreateBreakpointsFromFile(file, no_names, new_bps); } Status Target::CreateBreakpointsFromFile(const FileSpec &file, std::vector &names, BreakpointIDList &new_bps) { std::unique_lock lock; GetBreakpointList().GetListMutex(lock); Status error; StructuredData::ObjectSP input_data_sp = StructuredData::ParseJSONFromFile(file, error); if (!error.Success()) { return error; } else if (!input_data_sp || !input_data_sp->IsValid()) { error.SetErrorStringWithFormat("Invalid JSON from input file: %s.", file.GetPath().c_str()); return error; } StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); if (!bkpt_array) { error.SetErrorStringWithFormat( "Invalid breakpoint data from input file: %s.", file.GetPath().c_str()); return error; } size_t num_bkpts = bkpt_array->GetSize(); size_t num_names = names.size(); for (size_t i = 0; i < num_bkpts; i++) { StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i); // Peel off the breakpoint key, and feed the rest to the Breakpoint: StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary(); if (!bkpt_dict) { error.SetErrorStringWithFormat( "Invalid breakpoint data for element %zu from input file: %s.", i, file.GetPath().c_str()); return error; } StructuredData::ObjectSP bkpt_data_sp = bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey()); if (num_names && !Breakpoint::SerializedBreakpointMatchesNames(bkpt_data_sp, names)) continue; BreakpointSP bkpt_sp = Breakpoint::CreateFromStructuredData(*this, bkpt_data_sp, error); if (!error.Success()) { error.SetErrorStringWithFormat( "Error restoring breakpoint %zu from %s: %s.", i, file.GetPath().c_str(), error.AsCString()); return error; } new_bps.AddBreakpointID(BreakpointID(bkpt_sp->GetID())); } return error; } // The flag 'end_to_end', default to true, signifies that the operation is // performed end to end, for both the debugger and the debuggee. // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end // to end operations. bool Target::RemoveAllWatchpoints(bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.RemoveAll(true); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } m_watchpoint_list.RemoveAll(true); m_last_created_watchpoint.reset(); return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to // end operations. bool Target::DisableAllWatchpoints(bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.SetEnabledAll(false); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to // end operations. bool Target::EnableAllWatchpoints(bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.SetEnabledAll(true); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Status rc = m_process_sp->EnableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::ClearAllWatchpointHitCounts() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->ResetHitCount(); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::ClearAllWatchpointHistoricValues() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->ResetHistoricValues(); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list // during these operations. bool Target::IgnoreAllWatchpoints(uint32_t ignore_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s\n", __FUNCTION__); if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->SetIgnoreCount(ignore_count); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::DisableWatchpointByID(lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); if (wp_sp) { Status rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Success()) return true; // Else, fallthrough. } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::EnableWatchpointByID(lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); if (wp_sp) { Status rc = m_process_sp->EnableWatchpoint(wp_sp.get()); if (rc.Success()) return true; // Else, fallthrough. } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::RemoveWatchpointByID(lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id); if (watch_to_remove_sp == m_last_created_watchpoint) m_last_created_watchpoint.reset(); if (DisableWatchpointByID(watch_id)) { m_watchpoint_list.Remove(watch_id, true); return true; } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::IgnoreWatchpointByID(lldb::watch_id_t watch_id, uint32_t ignore_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); if (wp_sp) { wp_sp->SetIgnoreCount(ignore_count); return true; } return false; } ModuleSP Target::GetExecutableModule() { // search for the first executable in the module list for (size_t i = 0; i < m_images.GetSize(); ++i) { ModuleSP module_sp = m_images.GetModuleAtIndex(i); lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); if (obj == nullptr) continue; if (obj->GetType() == ObjectFile::Type::eTypeExecutable) return module_sp; } // as fall back return the first module loaded return m_images.GetModuleAtIndex(0); } Module *Target::GetExecutableModulePointer() { return GetExecutableModule().get(); } static void LoadScriptingResourceForModule(const ModuleSP &module_sp, Target *target) { Status error; StreamString feedback_stream; if (module_sp && !module_sp->LoadScriptingResourceInTarget(target, error, &feedback_stream)) { if (error.AsCString()) target->GetDebugger().GetErrorFile()->Printf( "unable to load scripting data for module %s - error reported was " "%s\n", module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), error.AsCString()); } if (feedback_stream.GetSize()) target->GetDebugger().GetErrorFile()->Printf("%s\n", feedback_stream.GetData()); } void Target::ClearModules(bool delete_locations) { ModulesDidUnload(m_images, delete_locations); m_section_load_history.Clear(); m_images.Clear(); m_scratch_type_system_map.Clear(); m_ast_importer_sp.reset(); } void Target::DidExec() { // When a process exec's we need to know about it so we can do some cleanup. m_breakpoint_list.RemoveInvalidLocations(m_arch); m_internal_breakpoint_list.RemoveInvalidLocations(m_arch); } void Target::SetExecutableModule(ModuleSP &executable_sp, bool get_dependent_files) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); ClearModules(false); if (executable_sp) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "Target::SetExecutableModule (executable = '%s')", executable_sp->GetFileSpec().GetPath().c_str()); m_images.Append(executable_sp); // The first image is our executable file // If we haven't set an architecture yet, reset our architecture based on // what we found in the executable module. if (!m_arch.IsValid()) { m_arch = executable_sp->GetArchitecture(); if (log) log->Printf("Target::SetExecutableModule setting architecture to %s " "(%s) based on executable file", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); } FileSpecList dependent_files; ObjectFile *executable_objfile = executable_sp->GetObjectFile(); if (executable_objfile && get_dependent_files) { executable_objfile->GetDependentModules(dependent_files); for (uint32_t i = 0; i < dependent_files.GetSize(); i++) { FileSpec dependent_file_spec( dependent_files.GetFileSpecPointerAtIndex(i)); FileSpec platform_dependent_file_spec; if (m_platform_sp) m_platform_sp->GetFileWithUUID(dependent_file_spec, nullptr, platform_dependent_file_spec); else platform_dependent_file_spec = dependent_file_spec; ModuleSpec module_spec(platform_dependent_file_spec, m_arch); ModuleSP image_module_sp(GetSharedModule(module_spec)); if (image_module_sp) { ObjectFile *objfile = image_module_sp->GetObjectFile(); if (objfile) objfile->GetDependentModules(dependent_files); } } } } } bool Target::SetArchitecture(const ArchSpec &arch_spec) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); bool missing_local_arch = !m_arch.IsValid(); bool replace_local_arch = true; bool compatible_local_arch = false; ArchSpec other(arch_spec); if (!missing_local_arch) { if (m_arch.IsCompatibleMatch(arch_spec)) { other.MergeFrom(m_arch); if (m_arch.IsCompatibleMatch(other)) { compatible_local_arch = true; bool arch_changed, vendor_changed, os_changed, os_ver_changed, env_changed; m_arch.PiecewiseTripleCompare(other, arch_changed, vendor_changed, os_changed, os_ver_changed, env_changed); if (!arch_changed && !vendor_changed && !os_changed && !env_changed) replace_local_arch = false; } } } if (compatible_local_arch || missing_local_arch) { // If we haven't got a valid arch spec, or the architectures are compatible // update the architecture, unless the one we already have is more specified if (replace_local_arch) m_arch = other; if (log) log->Printf("Target::SetArchitecture set architecture to %s (%s)", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); return true; } // If we have an executable file, try to reset the executable to the desired // architecture if (log) log->Printf("Target::SetArchitecture changing architecture to %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str()); m_arch = other; ModuleSP executable_sp = GetExecutableModule(); ClearModules(true); // Need to do something about unsetting breakpoints. if (executable_sp) { if (log) log->Printf("Target::SetArchitecture Trying to select executable file " "architecture %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str()); ModuleSpec module_spec(executable_sp->GetFileSpec(), other); Status error = ModuleList::GetSharedModule(module_spec, executable_sp, &GetExecutableSearchPaths(), nullptr, nullptr); if (!error.Fail() && executable_sp) { SetExecutableModule(executable_sp, true); return true; } } return false; } bool Target::MergeArchitecture(const ArchSpec &arch_spec) { if (arch_spec.IsValid()) { if (m_arch.IsCompatibleMatch(arch_spec)) { // The current target arch is compatible with "arch_spec", see if we // can improve our current architecture using bits from "arch_spec" // Merge bits from arch_spec into "merged_arch" and set our architecture ArchSpec merged_arch(m_arch); merged_arch.MergeFrom(arch_spec); return SetArchitecture(merged_arch); } else { // The new architecture is different, we just need to replace it return SetArchitecture(arch_spec); } } return false; } void Target::WillClearList(const ModuleList &module_list) {} void Target::ModuleAdded(const ModuleList &module_list, const ModuleSP &module_sp) { // A module is being added to this target for the first time if (m_valid) { ModuleList my_module_list; my_module_list.Append(module_sp); LoadScriptingResourceForModule(module_sp, this); ModulesDidLoad(my_module_list); } } void Target::ModuleRemoved(const ModuleList &module_list, const ModuleSP &module_sp) { // A module is being removed from this target. if (m_valid) { ModuleList my_module_list; my_module_list.Append(module_sp); ModulesDidUnload(my_module_list, false); } } void Target::ModuleUpdated(const ModuleList &module_list, const ModuleSP &old_module_sp, const ModuleSP &new_module_sp) { // A module is replacing an already added module if (m_valid) { m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, new_module_sp); m_internal_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced( old_module_sp, new_module_sp); } } void Target::ModulesDidLoad(ModuleList &module_list) { if (m_valid && module_list.GetSize()) { m_breakpoint_list.UpdateBreakpoints(module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); if (m_process_sp) { m_process_sp->ModulesDidLoad(module_list); } BroadcastEvent(eBroadcastBitModulesLoaded, new TargetEventData(this->shared_from_this(), module_list)); } } void Target::SymbolsDidLoad(ModuleList &module_list) { if (m_valid && module_list.GetSize()) { if (m_process_sp) { LanguageRuntime *runtime = m_process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (runtime) { ObjCLanguageRuntime *objc_runtime = (ObjCLanguageRuntime *)runtime; objc_runtime->SymbolsDidLoad(module_list); } } m_breakpoint_list.UpdateBreakpoints(module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); BroadcastEvent(eBroadcastBitSymbolsLoaded, new TargetEventData(this->shared_from_this(), module_list)); } } void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) { if (m_valid && module_list.GetSize()) { UnloadModuleSections(module_list); m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); m_internal_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); BroadcastEvent(eBroadcastBitModulesUnloaded, new TargetEventData(this->shared_from_this(), module_list)); } } bool Target::ModuleIsExcludedForUnconstrainedSearches( const FileSpec &module_file_spec) { if (GetBreakpointsConsultPlatformAvoidList()) { ModuleList matchingModules; ModuleSpec module_spec(module_file_spec); size_t num_modules = GetImages().FindModules(module_spec, matchingModules); // If there is more than one module for this file spec, only return true if // ALL the modules are on the // black list. if (num_modules > 0) { for (size_t i = 0; i < num_modules; i++) { if (!ModuleIsExcludedForUnconstrainedSearches( matchingModules.GetModuleAtIndex(i))) return false; } return true; } } return false; } bool Target::ModuleIsExcludedForUnconstrainedSearches( const lldb::ModuleSP &module_sp) { if (GetBreakpointsConsultPlatformAvoidList()) { if (m_platform_sp) return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches(*this, module_sp); } return false; } size_t Target::ReadMemoryFromFileCache(const Address &addr, void *dst, size_t dst_len, Status &error) { SectionSP section_sp(addr.GetSection()); if (section_sp) { // If the contents of this section are encrypted, the on-disk file is // unusable. Read only from live memory. if (section_sp->IsEncrypted()) { error.SetErrorString("section is encrypted"); return 0; } ModuleSP module_sp(section_sp->GetModule()); if (module_sp) { ObjectFile *objfile = section_sp->GetModule()->GetObjectFile(); if (objfile) { size_t bytes_read = objfile->ReadSectionData( section_sp.get(), addr.GetOffset(), dst, dst_len); if (bytes_read > 0) return bytes_read; else error.SetErrorStringWithFormat("error reading data from section %s", section_sp->GetName().GetCString()); } else error.SetErrorString("address isn't from a object file"); } else error.SetErrorString("address isn't in a module"); } else error.SetErrorString("address doesn't contain a section that points to a " "section in a object file"); return 0; } size_t Target::ReadMemory(const Address &addr, bool prefer_file_cache, void *dst, size_t dst_len, Status &error, lldb::addr_t *load_addr_ptr) { error.Clear(); // if we end up reading this from process memory, we will fill this // with the actual load address if (load_addr_ptr) *load_addr_ptr = LLDB_INVALID_ADDRESS; size_t bytes_read = 0; addr_t load_addr = LLDB_INVALID_ADDRESS; addr_t file_addr = LLDB_INVALID_ADDRESS; Address resolved_addr; if (!addr.IsSectionOffset()) { SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) { // No sections are loaded, so we must assume we are not running // yet and anything we are given is a file address. file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its // offset is the file address m_images.ResolveFileAddress(file_addr, resolved_addr); } else { // We have at least one section loaded. This can be because // we have manually loaded some sections with "target modules load ..." // or because we have have a live process that has sections loaded // through the dynamic loader load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its // offset is the load address section_load_list.ResolveLoadAddress(load_addr, resolved_addr); } } if (!resolved_addr.IsValid()) resolved_addr = addr; if (prefer_file_cache) { bytes_read = ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); if (bytes_read > 0) return bytes_read; } if (ProcessIsValid()) { if (load_addr == LLDB_INVALID_ADDRESS) load_addr = resolved_addr.GetLoadAddress(this); if (load_addr == LLDB_INVALID_ADDRESS) { ModuleSP addr_module_sp(resolved_addr.GetModule()); if (addr_module_sp && addr_module_sp->GetFileSpec()) error.SetErrorStringWithFormatv( "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded", addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress()); else error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress()); } else { bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); if (bytes_read != dst_len) { if (error.Success()) { if (bytes_read == 0) error.SetErrorStringWithFormat( "read memory from 0x%" PRIx64 " failed", load_addr); else error.SetErrorStringWithFormat( "only %" PRIu64 " of %" PRIu64 " bytes were read from memory at 0x%" PRIx64, (uint64_t)bytes_read, (uint64_t)dst_len, load_addr); } } if (bytes_read) { if (load_addr_ptr) *load_addr_ptr = load_addr; return bytes_read; } // If the address is not section offset we have an address that // doesn't resolve to any address in any currently loaded shared // libraries and we failed to read memory so there isn't anything // more we can do. If it is section offset, we might be able to // read cached memory from the object file. if (!resolved_addr.IsSectionOffset()) return 0; } } if (!prefer_file_cache && resolved_addr.IsSectionOffset()) { // If we didn't already try and read from the object file cache, then // try it after failing to read from the process. return ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); } return 0; } size_t Target::ReadCStringFromMemory(const Address &addr, std::string &out_str, Status &error) { char buf[256]; out_str.clear(); addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); while (1) { size_t length = ReadCStringFromMemory(address, buf, sizeof(buf), error); if (length == 0) break; out_str.append(buf, length); // If we got "length - 1" bytes, we didn't get the whole C string, we // need to read some more characters if (length == sizeof(buf) - 1) curr_addr += length; else break; address = Address(curr_addr); } return out_str.size(); } size_t Target::ReadCStringFromMemory(const Address &addr, char *dst, size_t dst_max_len, Status &result_error) { size_t total_cstr_len = 0; if (dst && dst_max_len) { result_error.Clear(); // NULL out everything just to be safe memset(dst, 0, dst_max_len); Status error; addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); // We could call m_process_sp->GetMemoryCacheLineSize() but I don't // think this really needs to be tied to the memory cache subsystem's // cache line size, so leave this as a fixed constant. const size_t cache_line_size = 512; size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; while (bytes_left > 0) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory(address, false, curr_dst, bytes_to_read, error); if (bytes_read == 0) { result_error = error; dst[total_cstr_len] = '\0'; break; } const size_t len = strlen(curr_dst); total_cstr_len += len; if (len < bytes_to_read) break; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; address = Address(curr_addr); } } else { if (dst == nullptr) result_error.SetErrorString("invalid arguments"); else result_error.Clear(); } return total_cstr_len; } size_t Target::ReadScalarIntegerFromMemory(const Address &addr, bool prefer_file_cache, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error) { uint64_t uval; if (byte_size <= sizeof(uval)) { size_t bytes_read = ReadMemory(addr, prefer_file_cache, &uval, byte_size, error); if (bytes_read == byte_size) { DataExtractor data(&uval, sizeof(uval), m_arch.GetByteOrder(), m_arch.GetAddressByteSize()); lldb::offset_t offset = 0; if (byte_size <= 4) scalar = data.GetMaxU32(&offset, byte_size); else scalar = data.GetMaxU64(&offset, byte_size); if (is_signed) scalar.SignExtend(byte_size * 8); return bytes_read; } } else { error.SetErrorStringWithFormat( "byte size of %u is too large for integer scalar type", byte_size); } return 0; } uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr, bool prefer_file_cache, size_t integer_byte_size, uint64_t fail_value, Status &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(addr, prefer_file_cache, integer_byte_size, false, scalar, error)) return scalar.ULongLong(fail_value); return fail_value; } bool Target::ReadPointerFromMemory(const Address &addr, bool prefer_file_cache, Status &error, Address &pointer_addr) { Scalar scalar; if (ReadScalarIntegerFromMemory(addr, prefer_file_cache, m_arch.GetAddressByteSize(), false, scalar, error)) { addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); if (pointer_vm_addr != LLDB_INVALID_ADDRESS) { SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) { // No sections are loaded, so we must assume we are not running // yet and anything we are given is a file address. m_images.ResolveFileAddress(pointer_vm_addr, pointer_addr); } else { // We have at least one section loaded. This can be because // we have manually loaded some sections with "target modules load ..." // or because we have have a live process that has sections loaded // through the dynamic loader section_load_list.ResolveLoadAddress(pointer_vm_addr, pointer_addr); } // We weren't able to resolve the pointer value, so just return // an address with no section if (!pointer_addr.IsValid()) pointer_addr.SetOffset(pointer_vm_addr); return true; } } return false; } ModuleSP Target::GetSharedModule(const ModuleSpec &module_spec, Status *error_ptr) { ModuleSP module_sp; Status error; // First see if we already have this module in our module list. If we do, // then we're done, we don't need // to consult the shared modules list. But only do this if we are passed a // UUID. if (module_spec.GetUUID().IsValid()) module_sp = m_images.FindFirstModule(module_spec); if (!module_sp) { ModuleSP old_module_sp; // This will get filled in if we have a new version // of the library bool did_create_module = false; // If there are image search path entries, try to use them first to acquire // a suitable image. if (m_image_search_paths.GetSize()) { ModuleSpec transformed_spec(module_spec); if (m_image_search_paths.RemapPath( module_spec.GetFileSpec().GetDirectory(), transformed_spec.GetFileSpec().GetDirectory())) { transformed_spec.GetFileSpec().GetFilename() = module_spec.GetFileSpec().GetFilename(); error = ModuleList::GetSharedModule(transformed_spec, module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } } if (!module_sp) { // If we have a UUID, we can check our global shared module list in case // we already have it. If we don't have a valid UUID, then we can't since // the path in "module_spec" will be a platform path, and we will need to // let the platform find that file. For example, we could be asking for // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick // the local copy of "/usr/lib/dyld" since our platform could be a remote // platform that has its own "/usr/lib/dyld" in an SDK or in a local file // cache. if (module_spec.GetUUID().IsValid()) { // We have a UUID, it is OK to check the global module list... error = ModuleList::GetSharedModule(module_spec, module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } if (!module_sp) { // The platform is responsible for finding and caching an appropriate // module in the shared module cache. if (m_platform_sp) { error = m_platform_sp->GetSharedModule( module_spec, m_process_sp.get(), module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } else { error.SetErrorString("no platform is currently set"); } } } // We found a module that wasn't in our target list. Let's make sure that // there wasn't an equivalent // module in the list already, and if there was, let's remove it. if (module_sp) { ObjectFile *objfile = module_sp->GetObjectFile(); if (objfile) { switch (objfile->GetType()) { case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of /// a program's execution state case ObjectFile::eTypeExecutable: /// A normal executable case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker /// executable case ObjectFile::eTypeObjectFile: /// An intermediate object file case ObjectFile::eTypeSharedLibrary: /// A shared library that can be /// used during execution break; case ObjectFile::eTypeDebugInfo: /// An object file that contains only /// debug information if (error_ptr) error_ptr->SetErrorString("debug info files aren't valid target " "modules, please specify an executable"); return ModuleSP(); case ObjectFile::eTypeStubLibrary: /// A library that can be linked /// against but not used for /// execution if (error_ptr) error_ptr->SetErrorString("stub libraries aren't valid target " "modules, please specify an executable"); return ModuleSP(); default: if (error_ptr) error_ptr->SetErrorString( "unsupported file type, please specify an executable"); return ModuleSP(); } // GetSharedModule is not guaranteed to find the old shared module, for // instance // in the common case where you pass in the UUID, it is only going to // find the one // module matching the UUID. In fact, it has no good way to know what // the "old module" // relevant to this target is, since there might be many copies of a // module with this file spec // in various running debug sessions, but only one of them will belong // to this target. // So let's remove the UUID from the module list, and look in the // target's module list. // Only do this if there is SOMETHING else in the module spec... if (!old_module_sp) { if (module_spec.GetUUID().IsValid() && !module_spec.GetFileSpec().GetFilename().IsEmpty() && !module_spec.GetFileSpec().GetDirectory().IsEmpty()) { ModuleSpec module_spec_copy(module_spec.GetFileSpec()); module_spec_copy.GetUUID().Clear(); ModuleList found_modules; size_t num_found = m_images.FindModules(module_spec_copy, found_modules); if (num_found == 1) { old_module_sp = found_modules.GetModuleAtIndex(0); } } } // Preload symbols outside of any lock, so hopefully we can do this for // each library in parallel. if (GetPreloadSymbols()) module_sp->PreloadSymbols(); if (old_module_sp && m_images.GetIndexForModule(old_module_sp.get()) != LLDB_INVALID_INDEX32) { m_images.ReplaceModule(old_module_sp, module_sp); Module *old_module_ptr = old_module_sp.get(); old_module_sp.reset(); ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr); } else m_images.Append(module_sp); } else module_sp.reset(); } } if (error_ptr) *error_ptr = error; return module_sp; } TargetSP Target::CalculateTarget() { return shared_from_this(); } ProcessSP Target::CalculateProcess() { return m_process_sp; } ThreadSP Target::CalculateThread() { return ThreadSP(); } StackFrameSP Target::CalculateStackFrame() { return StackFrameSP(); } void Target::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.Clear(); exe_ctx.SetTargetPtr(this); } PathMappingList &Target::GetImageSearchPathList() { return m_image_search_paths; } void Target::ImageSearchPathsChanged(const PathMappingList &path_list, void *baton) { Target *target = (Target *)baton; ModuleSP exe_module_sp(target->GetExecutableModule()); if (exe_module_sp) target->SetExecutableModule(exe_module_sp, true); } TypeSystem *Target::GetScratchTypeSystemForLanguage(Status *error, lldb::LanguageType language, bool create_on_demand) { if (!m_valid) return nullptr; if (error) { error->Clear(); } if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all // assembly code || language == eLanguageTypeUnknown) { std::set languages_for_types; std::set languages_for_expressions; Language::GetLanguagesSupportingTypeSystems(languages_for_types, languages_for_expressions); if (languages_for_expressions.count(eLanguageTypeC)) { language = eLanguageTypeC; // LLDB's default. Override by setting the // target language. } else { if (languages_for_expressions.empty()) { return nullptr; } else { language = *languages_for_expressions.begin(); } } } return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this, create_on_demand); } PersistentExpressionState * Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) { TypeSystem *type_system = GetScratchTypeSystemForLanguage(nullptr, language, true); if (type_system) { return type_system->GetPersistentExpressionState(); } else { return nullptr; } } UserExpression *Target::GetUserExpressionForLanguage( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options, Status &error) { Status type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage(&type_system_error, language); UserExpression *user_expr = nullptr; if (!type_system) { error.SetErrorStringWithFormat( "Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return nullptr; } user_expr = type_system->GetUserExpression(expr, prefix, language, desired_type, options); if (!user_expr) error.SetErrorStringWithFormat( "Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return user_expr; } FunctionCaller *Target::GetFunctionCallerForLanguage( lldb::LanguageType language, const CompilerType &return_type, const Address &function_address, const ValueList &arg_value_list, const char *name, Status &error) { Status type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage(&type_system_error, language); FunctionCaller *persistent_fn = nullptr; if (!type_system) { error.SetErrorStringWithFormat( "Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return persistent_fn; } persistent_fn = type_system->GetFunctionCaller(return_type, function_address, arg_value_list, name); if (!persistent_fn) error.SetErrorStringWithFormat( "Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return persistent_fn; } UtilityFunction * Target::GetUtilityFunctionForLanguage(const char *text, lldb::LanguageType language, const char *name, Status &error) { Status type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage(&type_system_error, language); UtilityFunction *utility_fn = nullptr; if (!type_system) { error.SetErrorStringWithFormat( "Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return utility_fn; } utility_fn = type_system->GetUtilityFunction(text, name); if (!utility_fn) error.SetErrorStringWithFormat( "Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return utility_fn; } ClangASTContext *Target::GetScratchClangASTContext(bool create_on_demand) { if (m_valid) { if (TypeSystem *type_system = GetScratchTypeSystemForLanguage( nullptr, eLanguageTypeC, create_on_demand)) return llvm::dyn_cast(type_system); } return nullptr; } ClangASTImporterSP Target::GetClangASTImporter() { if (m_valid) { if (!m_ast_importer_sp) { m_ast_importer_sp.reset(new ClangASTImporter()); } return m_ast_importer_sp; } return ClangASTImporterSP(); } void Target::SettingsInitialize() { Process::SettingsInitialize(); } void Target::SettingsTerminate() { Process::SettingsTerminate(); } FileSpecList Target::GetDefaultExecutableSearchPaths() { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetExecutableSearchPaths(); return FileSpecList(); } FileSpecList Target::GetDefaultDebugFileSearchPaths() { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetDebugFileSearchPaths(); return FileSpecList(); } FileSpecList Target::GetDefaultClangModuleSearchPaths() { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetClangModuleSearchPaths(); return FileSpecList(); } ArchSpec Target::GetDefaultArchitecture() { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetDefaultArchitecture(); return ArchSpec(); } void Target::SetDefaultArchitecture(const ArchSpec &arch) { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) { LogIfAnyCategoriesSet( LIBLLDB_LOG_TARGET, "Target::SetDefaultArchitecture setting target's " "default architecture to %s (%s)", arch.GetArchitectureName(), arch.GetTriple().getTriple().c_str()); return properties_sp->SetDefaultArchitecture(arch); } } Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) { // The target can either exist in the "process" of ExecutionContext, or in // the "target_sp" member of SymbolContext. This accessor helper function // will get the target from one of these locations. Target *target = nullptr; if (sc_ptr != nullptr) target = sc_ptr->target_sp.get(); if (target == nullptr && exe_ctx_ptr) target = exe_ctx_ptr->GetTargetPtr(); return target; } ExpressionResults Target::EvaluateExpression( llvm::StringRef expr, ExecutionContextScope *exe_scope, lldb::ValueObjectSP &result_valobj_sp, const EvaluateExpressionOptions &options, std::string *fixed_expression) { result_valobj_sp.reset(); ExpressionResults execution_results = eExpressionSetupError; if (expr.empty()) return execution_results; // We shouldn't run stop hooks in expressions. // Be sure to reset this if you return anywhere within this function. bool old_suppress_value = m_suppress_stop_hooks; m_suppress_stop_hooks = true; ExecutionContext exe_ctx; if (exe_scope) { exe_scope->CalculateExecutionContext(exe_ctx); } else if (m_process_sp) { m_process_sp->CalculateExecutionContext(exe_ctx); } else { CalculateExecutionContext(exe_ctx); } // Make sure we aren't just trying to see the value of a persistent // variable (something like "$0") lldb::ExpressionVariableSP persistent_var_sp; // Only check for persistent variables the expression starts with a '$' if (expr[0] == '$') persistent_var_sp = GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC) ->GetPersistentExpressionState() ->GetVariable(expr); if (persistent_var_sp) { result_valobj_sp = persistent_var_sp->GetValueObject(); execution_results = eExpressionCompleted; } else { const char *prefix = GetExpressionPrefixContentsAsCString(); Status error; execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, result_valobj_sp, error, 0, // Line Number fixed_expression); } m_suppress_stop_hooks = old_suppress_value; return execution_results; } lldb::ExpressionVariableSP Target::GetPersistentVariable(const ConstString &name) { lldb::ExpressionVariableSP variable_sp; m_scratch_type_system_map.ForEach( [name, &variable_sp](TypeSystem *type_system) -> bool { if (PersistentExpressionState *persistent_state = type_system->GetPersistentExpressionState()) { variable_sp = persistent_state->GetVariable(name); if (variable_sp) return false; // Stop iterating the ForEach } return true; // Keep iterating the ForEach }); return variable_sp; } lldb::addr_t Target::GetPersistentSymbol(const ConstString &name) { lldb::addr_t address = LLDB_INVALID_ADDRESS; m_scratch_type_system_map.ForEach( [name, &address](TypeSystem *type_system) -> bool { if (PersistentExpressionState *persistent_state = type_system->GetPersistentExpressionState()) { address = persistent_state->LookupSymbol(name); if (address != LLDB_INVALID_ADDRESS) return false; // Stop iterating the ForEach } return true; // Keep iterating the ForEach }); return address; } lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const { addr_t code_addr = load_addr; switch (m_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassUnknown: case eAddressClassInvalid: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: if ((code_addr & 2ull) || (addr_class == eAddressClassCodeAlternateISA)) code_addr |= 1ull; break; } break; case llvm::Triple::arm: case llvm::Triple::thumb: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassUnknown: case eAddressClassInvalid: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: // Check if bit zero it no set? if ((code_addr & 1ull) == 0) { // Bit zero isn't set, check if the address is a multiple of 2? if (code_addr & 2ull) { // The address is a multiple of 2 so it must be thumb, set bit zero code_addr |= 1ull; } else if (addr_class == eAddressClassCodeAlternateISA) { // We checked the address and the address claims to be the alternate // ISA // which means thumb, so set bit zero. code_addr |= 1ull; } } break; } break; default: break; } return code_addr; } lldb::addr_t Target::GetOpcodeLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const { addr_t opcode_addr = load_addr; switch (m_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::arm: case llvm::Triple::thumb: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassInvalid: case eAddressClassUnknown: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: opcode_addr &= ~(1ull); break; } break; default: break; } return opcode_addr; } lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) { addr_t breakable_addr = addr; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); switch (m_arch.GetMachine()) { default: break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: { addr_t function_start = 0; addr_t current_offset = 0; uint32_t loop_count = 0; Address resolved_addr; uint32_t arch_flags = m_arch.GetFlags(); bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16; bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips; SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) // No sections are loaded, so we must assume we are not running yet // and need to operate only on file address. m_images.ResolveFileAddress(addr, resolved_addr); else section_load_list.ResolveLoadAddress(addr, resolved_addr); // Get the function boundaries to make sure we don't scan back before the // beginning of the current function. ModuleSP temp_addr_module_sp(resolved_addr.GetModule()); if (temp_addr_module_sp) { SymbolContext sc; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, resolve_scope, sc); Address sym_addr; if (sc.function) sym_addr = sc.function->GetAddressRange().GetBaseAddress(); else if (sc.symbol) sym_addr = sc.symbol->GetAddress(); function_start = sym_addr.GetLoadAddress(this); if (function_start == LLDB_INVALID_ADDRESS) function_start = sym_addr.GetFileAddress(); if (function_start) current_offset = addr - function_start; } // If breakpoint address is start of function then we dont have to do // anything. if (current_offset == 0) return breakable_addr; else loop_count = current_offset / 2; if (loop_count > 3) { // Scan previous 6 bytes if (IsMips16 | IsMicromips) loop_count = 3; // For mips-only, instructions are always 4 bytes, so scan previous 4 // bytes only. else loop_count = 2; } // Create Disassembler Instance lldb::DisassemblerSP disasm_sp( Disassembler::FindPlugin(m_arch, nullptr, nullptr)); ExecutionContext exe_ctx; CalculateExecutionContext(exe_ctx); InstructionList instruction_list; InstructionSP prev_insn; bool prefer_file_cache = true; // Read from file uint32_t inst_to_choose = 0; for (uint32_t i = 1; i <= loop_count; i++) { // Adjust the address to read from. resolved_addr.Slide(-2); AddressRange range(resolved_addr, i * 2); uint32_t insn_size = 0; disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache); uint32_t num_insns = disasm_sp->GetInstructionList().GetSize(); if (num_insns) { prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0); insn_size = prev_insn->GetOpcode().GetByteSize(); if (i == 1 && insn_size == 2) { // This looks like a valid 2-byte instruction (but it could be a part // of upper 4 byte instruction). instruction_list.Append(prev_insn); inst_to_choose = 1; } else if (i == 2) { // Here we may get one 4-byte instruction or two 2-byte instructions. if (num_insns == 2) { // Looks like there are two 2-byte instructions above our breakpoint // target address. // Now the upper 2-byte instruction is either a valid 2-byte // instruction or could be a part of it's upper 4-byte instruction. // In both cases we don't care because in this case lower 2-byte // instruction is definitely a valid instruction // and whatever i=1 iteration has found out is true. inst_to_choose = 1; break; } else if (insn_size == 4) { // This instruction claims its a valid 4-byte instruction. But it // could be a part of it's upper 4-byte instruction. // Lets try scanning upper 2 bytes to verify this. instruction_list.Append(prev_insn); inst_to_choose = 2; } } else if (i == 3) { if (insn_size == 4) // FIXME: We reached here that means instruction at [target - 4] has // already claimed to be a 4-byte instruction, // and now instruction at [target - 6] is also claiming that it's a // 4-byte instruction. This can not be true. // In this case we can not decide the valid previous instruction so // we let lldb set the breakpoint at the address given by user. inst_to_choose = 0; else // This is straight-forward inst_to_choose = 2; break; } } else { // Decode failed, bytes do not form a valid instruction. So whatever // previous iteration has found out is true. if (i > 1) { inst_to_choose = i - 1; break; } } } // Check if we are able to find any valid instruction. if (inst_to_choose) { if (inst_to_choose > instruction_list.GetSize()) inst_to_choose--; prev_insn = instruction_list.GetInstructionAtIndex(inst_to_choose - 1); if (prev_insn->HasDelaySlot()) { uint32_t shift_size = prev_insn->GetOpcode().GetByteSize(); // Adjust the breakable address breakable_addr = addr - shift_size; if (log) log->Printf("Target::%s Breakpoint at 0x%8.8" PRIx64 " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n", __FUNCTION__, addr, breakable_addr); } } break; } } return breakable_addr; } SourceManager &Target::GetSourceManager() { if (!m_source_manager_ap) m_source_manager_ap.reset(new SourceManager(shared_from_this())); return *m_source_manager_ap; } ClangModulesDeclVendor *Target::GetClangModulesDeclVendor() { static std::mutex s_clang_modules_decl_vendor_mutex; // If this is contended // we can make it // per-target { std::lock_guard guard(s_clang_modules_decl_vendor_mutex); if (!m_clang_modules_decl_vendor_ap) { m_clang_modules_decl_vendor_ap.reset( ClangModulesDeclVendor::Create(*this)); } } return m_clang_modules_decl_vendor_ap.get(); } Target::StopHookSP Target::CreateStopHook() { lldb::user_id_t new_uid = ++m_stop_hook_next_id; Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), new_uid)); m_stop_hooks[new_uid] = stop_hook_sp; return stop_hook_sp; } bool Target::RemoveStopHookByID(lldb::user_id_t user_id) { size_t num_removed = m_stop_hooks.erase(user_id); return (num_removed != 0); } void Target::RemoveAllStopHooks() { m_stop_hooks.clear(); } Target::StopHookSP Target::GetStopHookByID(lldb::user_id_t user_id) { StopHookSP found_hook; StopHookCollection::iterator specified_hook_iter; specified_hook_iter = m_stop_hooks.find(user_id); if (specified_hook_iter != m_stop_hooks.end()) found_hook = (*specified_hook_iter).second; return found_hook; } bool Target::SetStopHookActiveStateByID(lldb::user_id_t user_id, bool active_state) { StopHookCollection::iterator specified_hook_iter; specified_hook_iter = m_stop_hooks.find(user_id); if (specified_hook_iter == m_stop_hooks.end()) return false; (*specified_hook_iter).second->SetIsActive(active_state); return true; } void Target::SetAllStopHooksActiveState(bool active_state) { StopHookCollection::iterator pos, end = m_stop_hooks.end(); for (pos = m_stop_hooks.begin(); pos != end; pos++) { (*pos).second->SetIsActive(active_state); } } void Target::RunStopHooks() { if (m_suppress_stop_hooks) return; if (!m_process_sp) return; // make sure we check that we are not stopped // because of us running a user expression // since in that case we do not want to run the stop-hooks if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression()) return; if (m_stop_hooks.empty()) return; StopHookCollection::iterator pos, end = m_stop_hooks.end(); // If there aren't any active stop hooks, don't bother either: bool any_active_hooks = false; for (pos = m_stop_hooks.begin(); pos != end; pos++) { if ((*pos).second->IsActive()) { any_active_hooks = true; break; } } if (!any_active_hooks) return; CommandReturnObject result; std::vector exc_ctx_with_reasons; std::vector sym_ctx_with_reasons; ThreadList &cur_threadlist = m_process_sp->GetThreadList(); size_t num_threads = cur_threadlist.GetSize(); for (size_t i = 0; i < num_threads; i++) { lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i); if (cur_thread_sp->ThreadStoppedForAReason()) { lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0); exc_ctx_with_reasons.push_back(ExecutionContext( m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get())); sym_ctx_with_reasons.push_back( cur_frame_sp->GetSymbolContext(eSymbolContextEverything)); } } // If no threads stopped for a reason, don't run the stop-hooks. size_t num_exe_ctx = exc_ctx_with_reasons.size(); if (num_exe_ctx == 0) return; result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream()); result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream()); bool keep_going = true; bool hooks_ran = false; bool print_hook_header = (m_stop_hooks.size() != 1); bool print_thread_header = (num_exe_ctx != 1); for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) { // result.Clear(); StopHookSP cur_hook_sp = (*pos).second; if (!cur_hook_sp->IsActive()) continue; bool any_thread_matched = false; for (size_t i = 0; keep_going && i < num_exe_ctx; i++) { if ((cur_hook_sp->GetSpecifier() == nullptr || cur_hook_sp->GetSpecifier()->SymbolContextMatches( sym_ctx_with_reasons[i])) && (cur_hook_sp->GetThreadSpecifier() == nullptr || cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests( exc_ctx_with_reasons[i].GetThreadRef()))) { if (!hooks_ran) { hooks_ran = true; } if (print_hook_header && !any_thread_matched) { const char *cmd = (cur_hook_sp->GetCommands().GetSize() == 1 ? cur_hook_sp->GetCommands().GetStringAtIndex(0) : nullptr); if (cmd) result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(), cmd); else result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID()); any_thread_matched = true; } if (print_thread_header) result.AppendMessageWithFormat( "-- Thread %d\n", exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); CommandInterpreterRunOptions options; options.SetStopOnContinue(true); options.SetStopOnError(true); options.SetEchoCommands(false); options.SetPrintResults(true); options.SetAddToHistory(false); GetDebugger().GetCommandInterpreter().HandleCommands( cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options, result); // If the command started the target going again, we should bag out of // running the stop hooks. if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || (result.GetStatus() == eReturnStatusSuccessContinuingResult)) { result.AppendMessageWithFormat("Aborting stop hooks, hook %" PRIu64 " set the program running.", cur_hook_sp->GetID()); keep_going = false; } } } } result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); } const TargetPropertiesSP &Target::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function static TargetPropertiesSP *g_settings_sp_ptr = new TargetPropertiesSP(new TargetProperties(nullptr)); return *g_settings_sp_ptr; } Status Target::Install(ProcessLaunchInfo *launch_info) { Status error; PlatformSP platform_sp(GetPlatform()); if (platform_sp) { if (platform_sp->IsRemote()) { if (platform_sp->IsConnected()) { // Install all files that have an install path, and always install the // main executable when connected to a remote platform const ModuleList &modules = GetImages(); const size_t num_images = modules.GetSize(); for (size_t idx = 0; idx < num_images; ++idx) { ModuleSP module_sp(modules.GetModuleAtIndex(idx)); if (module_sp) { const bool is_main_executable = module_sp == GetExecutableModule(); FileSpec local_file(module_sp->GetFileSpec()); if (local_file) { FileSpec remote_file(module_sp->GetRemoteInstallFileSpec()); if (!remote_file) { if (is_main_executable) // TODO: add setting for always // installing main executable??? { // Always install the main executable remote_file = platform_sp->GetRemoteWorkingDirectory(); remote_file.AppendPathComponent( module_sp->GetFileSpec().GetFilename().GetCString()); } } if (remote_file) { error = platform_sp->Install(local_file, remote_file); if (error.Success()) { module_sp->SetPlatformFileSpec(remote_file); if (is_main_executable) { platform_sp->SetFilePermissions(remote_file, 0700); if (launch_info) launch_info->SetExecutableFile(remote_file, false); } } else break; } } } } } } } return error; } bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr, uint32_t stop_id) { return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr); } bool Target::ResolveFileAddress(lldb::addr_t file_addr, Address &resolved_addr) { return m_images.ResolveFileAddress(file_addr, resolved_addr); } bool Target::SetSectionLoadAddress(const SectionSP §ion_sp, addr_t new_section_load_addr, bool warn_multiple) { const addr_t old_section_load_addr = m_section_load_history.GetSectionLoadAddress( SectionLoadHistory::eStopIDNow, section_sp); if (old_section_load_addr != new_section_load_addr) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); if (m_section_load_history.SetSectionLoadAddress( stop_id, section_sp, new_section_load_addr, warn_multiple)) return true; // Return true if the section load address was changed... } return false; // Return false to indicate nothing changed } size_t Target::UnloadModuleSections(const ModuleList &module_list) { size_t section_unload_count = 0; size_t num_modules = module_list.GetSize(); for (size_t i = 0; i < num_modules; ++i) { section_unload_count += UnloadModuleSections(module_list.GetModuleAtIndex(i)); } return section_unload_count; } size_t Target::UnloadModuleSections(const lldb::ModuleSP &module_sp) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); SectionList *sections = module_sp->GetSectionList(); size_t section_unload_count = 0; if (sections) { const uint32_t num_sections = sections->GetNumSections(0); for (uint32_t i = 0; i < num_sections; ++i) { section_unload_count += m_section_load_history.SetSectionUnloaded( stop_id, sections->GetSectionAtIndex(i)); } } return section_unload_count; } bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); return m_section_load_history.SetSectionUnloaded(stop_id, section_sp); } bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp, addr_t load_addr) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); return m_section_load_history.SetSectionUnloaded(stop_id, section_sp, load_addr); } void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { Status error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); if (log) log->Printf("Target::%s() called for %s", __FUNCTION__, launch_info.GetExecutableFile().GetPath().c_str()); StateType state = eStateInvalid; // Scope to temporarily get the process state in case someone has manually // remotely connected already to a process and we can skip the platform // launching. { ProcessSP process_sp(GetProcessSP()); if (process_sp) { state = process_sp->GetState(); if (log) log->Printf( "Target::%s the process exists, and its current state is %s", __FUNCTION__, StateAsCString(state)); } else { if (log) log->Printf("Target::%s the process instance doesn't currently exist.", __FUNCTION__); } } launch_info.GetFlags().Set(eLaunchFlagDebug); // Get the value of synchronous execution here. If you wait till after you // have started to // run, then you could have hit a breakpoint, whose command might switch the // value, and // then you'll pick up that incorrect value. Debugger &debugger = GetDebugger(); const bool synchronous_execution = debugger.GetCommandInterpreter().GetSynchronous(); PlatformSP platform_sp(GetPlatform()); // Finalize the file actions, and if none were given, default to opening // up a pseudo terminal const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; if (log) log->Printf("Target::%s have platform=%s, platform_sp->IsHost()=%s, " "default_to_use_pty=%s", __FUNCTION__, platform_sp ? "true" : "false", platform_sp ? (platform_sp->IsHost() ? "true" : "false") : "n/a", default_to_use_pty ? "true" : "false"); launch_info.FinalizeFileActions(this, default_to_use_pty); if (state == eStateConnected) { if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { error.SetErrorString( "can't launch in tty when launching through a remote connection"); return error; } } if (!launch_info.GetArchitecture().IsValid()) launch_info.GetArchitecture() = GetArchitecture(); // If we're not already connected to the process, and if we have a platform // that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && platform_sp->CanDebugProcess()) { if (log) log->Printf("Target::%s asking the platform to debug the process", __FUNCTION__); // Get a weak pointer to the previous process if we have one ProcessWP process_wp; if (m_process_sp) process_wp = m_process_sp; m_process_sp = GetPlatform()->DebugProcess(launch_info, debugger, this, error); // Cleanup the old process since someone might still have a strong // reference to this process and we would like to allow it to cleanup // as much as it can without the object being destroyed. We try to // lock the shared pointer and if that works, then someone else still // has a strong reference to the process. ProcessSP old_process_sp(process_wp.lock()); if (old_process_sp) old_process_sp->Finalize(); } else { if (log) log->Printf("Target::%s the platform doesn't know how to debug a " "process, getting a process plugin to do this for us.", __FUNCTION__); if (state == eStateConnected) { assert(m_process_sp); } else { // Use a Process plugin to construct the process. const char *plugin_name = launch_info.GetProcessPluginName(); CreateProcess(launch_info.GetListenerForProcess(debugger), plugin_name, nullptr); } // Since we didn't have a platform launch the process, launch it here. if (m_process_sp) error = m_process_sp->Launch(launch_info); } if (!m_process_sp) { if (error.Success()) error.SetErrorString("failed to launch or debug process"); return error; } if (error.Success()) { if (synchronous_execution || !launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { ListenerSP hijack_listener_sp(launch_info.GetHijackListener()); if (!hijack_listener_sp) { hijack_listener_sp = Listener::MakeListener("lldb.Target.Launch.hijack"); launch_info.SetHijackListener(hijack_listener_sp); m_process_sp->HijackProcessEvents(hijack_listener_sp); } StateType state = m_process_sp->WaitForProcessToStop( llvm::None, nullptr, false, hijack_listener_sp, nullptr); if (state == eStateStopped) { if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { if (synchronous_execution) { error = m_process_sp->PrivateResume(); if (error.Success()) { state = m_process_sp->WaitForProcessToStop( llvm::None, nullptr, true, hijack_listener_sp, stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) { error.SetErrorStringWithFormat("process isn't stopped: %s", StateAsCString(state)); } } } else { m_process_sp->RestoreProcessEvents(); error = m_process_sp->PrivateResume(); } if (!error.Success()) { Status error2; error2.SetErrorStringWithFormat( "process resume at entry point failed: %s", error.AsCString()); error = error2; } } } else if (state == eStateExited) { bool with_shell = !!launch_info.GetShell(); const int exit_status = m_process_sp->GetExitStatus(); const char *exit_desc = m_process_sp->GetExitDescription(); #define LAUNCH_SHELL_MESSAGE \ "\n'r' and 'run' are aliases that default to launching through a " \ "shell.\nTry launching without going through a shell by using 'process " \ "launch'." if (exit_desc && exit_desc[0]) { if (with_shell) error.SetErrorStringWithFormat( "process exited with status %i (%s)" LAUNCH_SHELL_MESSAGE, exit_status, exit_desc); else error.SetErrorStringWithFormat("process exited with status %i (%s)", exit_status, exit_desc); } else { if (with_shell) error.SetErrorStringWithFormat( "process exited with status %i" LAUNCH_SHELL_MESSAGE, exit_status); else error.SetErrorStringWithFormat("process exited with status %i", exit_status); } } else { error.SetErrorStringWithFormat( "initial process state wasn't stopped: %s", StateAsCString(state)); } } m_process_sp->RestoreProcessEvents(); } else { Status error2; error2.SetErrorStringWithFormat("process launch failed: %s", error.AsCString()); error = error2; } return error; } Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { auto state = eStateInvalid; auto process_sp = GetProcessSP(); if (process_sp) { state = process_sp->GetState(); if (process_sp->IsAlive() && state != eStateConnected) { if (state == eStateAttaching) return Status("process attach is in progress"); return Status("a process is already being debugged"); } } const ModuleSP old_exec_module_sp = GetExecutableModule(); // If no process info was specified, then use the target executable // name as the process to attach to by default if (!attach_info.ProcessInfoSpecified()) { if (old_exec_module_sp) attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetPlatformFileSpec().GetFilename(); if (!attach_info.ProcessInfoSpecified()) { return Status("no process specified, create a target with a file, or " "specify the --pid or --name"); } } const auto platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); ListenerSP hijack_listener_sp; const bool async = attach_info.GetAsync(); if (!async) { hijack_listener_sp = Listener::MakeListener("lldb.Target.Attach.attach.hijack"); attach_info.SetHijackListener(hijack_listener_sp); } Status error; if (state != eStateConnected && platform_sp != nullptr && platform_sp->CanDebugProcess()) { SetPlatform(platform_sp); process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error); } else { if (state != eStateConnected) { const char *plugin_name = attach_info.GetProcessPluginName(); process_sp = CreateProcess(attach_info.GetListenerForProcess(GetDebugger()), plugin_name, nullptr); if (process_sp == nullptr) { error.SetErrorStringWithFormat( "failed to create process using plugin %s", (plugin_name) ? plugin_name : "null"); return error; } } if (hijack_listener_sp) process_sp->HijackProcessEvents(hijack_listener_sp); error = process_sp->Attach(attach_info); } if (error.Success() && process_sp) { if (async) { process_sp->RestoreProcessEvents(); } else { state = process_sp->WaitForProcessToStop( llvm::None, nullptr, false, attach_info.GetHijackListener(), stream); process_sp->RestoreProcessEvents(); if (state != eStateStopped) { const char *exit_desc = process_sp->GetExitDescription(); if (exit_desc) error.SetErrorStringWithFormat("%s", exit_desc); else error.SetErrorString( "process did not stop (no such process or permission problem?)"); process_sp->Destroy(false); } } } return error; } //-------------------------------------------------------------- // Target::StopHook //-------------------------------------------------------------- Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid) : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(), m_thread_spec_ap(), m_active(true) {} Target::StopHook::StopHook(const StopHook &rhs) : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp), m_thread_spec_ap(), m_active(rhs.m_active) { if (rhs.m_thread_spec_ap) m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); } Target::StopHook::~StopHook() = default; void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) { m_specifier_sp.reset(specifier); } void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) { m_thread_spec_ap.reset(specifier); } void Target::StopHook::GetDescription(Stream *s, lldb::DescriptionLevel level) const { int indent_level = s->GetIndentLevel(); s->SetIndentLevel(indent_level + 2); s->Printf("Hook: %" PRIu64 "\n", GetID()); if (m_active) s->Indent("State: enabled\n"); else s->Indent("State: disabled\n"); if (m_specifier_sp) { s->Indent(); s->PutCString("Specifier:\n"); s->SetIndentLevel(indent_level + 4); m_specifier_sp->GetDescription(s, level); s->SetIndentLevel(indent_level + 2); } if (m_thread_spec_ap) { StreamString tmp; s->Indent("Thread:\n"); m_thread_spec_ap->GetDescription(&tmp, level); s->SetIndentLevel(indent_level + 4); s->Indent(tmp.GetString()); s->PutCString("\n"); s->SetIndentLevel(indent_level + 2); } s->Indent("Commands: \n"); s->SetIndentLevel(indent_level + 4); uint32_t num_commands = m_commands.GetSize(); for (uint32_t i = 0; i < num_commands; i++) { s->Indent(m_commands.GetStringAtIndex(i)); s->PutCString("\n"); } s->SetIndentLevel(indent_level); } //-------------------------------------------------------------- // class TargetProperties //-------------------------------------------------------------- OptionEnumValueElement lldb_private::g_dynamic_value_types[] = { {eNoDynamicValues, "no-dynamic-values", "Don't calculate the dynamic type of values"}, {eDynamicCanRunTarget, "run-target", "Calculate the dynamic type of values " "even if you have to run the target."}, {eDynamicDontRunTarget, "no-run-target", "Calculate the dynamic type of values, but don't run the target."}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_inline_breakpoint_enums[] = { {eInlineBreakpointsNever, "never", "Never look for inline breakpoint " "locations (fastest). This setting " "should only be used if you know that " "no inlining occurs in your programs."}, {eInlineBreakpointsHeaders, "headers", "Only check for inline breakpoint locations when setting breakpoints in " "header files, but not when setting breakpoint in implementation source " "files (default)."}, {eInlineBreakpointsAlways, "always", "Always look for inline breakpoint locations when setting file and line " "breakpoints (slower but most accurate)."}, {0, nullptr, nullptr}}; typedef enum x86DisassemblyFlavor { eX86DisFlavorDefault, eX86DisFlavorIntel, eX86DisFlavorATT } x86DisassemblyFlavor; static OptionEnumValueElement g_x86_dis_flavor_value_types[] = { {eX86DisFlavorDefault, "default", "Disassembler default (currently att)."}, {eX86DisFlavorIntel, "intel", "Intel disassembler flavor."}, {eX86DisFlavorATT, "att", "AT&T disassembler flavor."}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_hex_immediate_style_values[] = { {Disassembler::eHexStyleC, "c", "C-style (0xffff)."}, {Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_load_script_from_sym_file_values[] = { {eLoadScriptFromSymFileTrue, "true", "Load debug scripts inside symbol files"}, {eLoadScriptFromSymFileFalse, "false", "Do not load debug scripts inside symbol files."}, {eLoadScriptFromSymFileWarn, "warn", "Warn about debug scripts inside symbol files but do not load them."}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_load_current_working_dir_lldbinit_values[] = { {eLoadCWDlldbinitTrue, "true", "Load .lldbinit files from current directory"}, {eLoadCWDlldbinitFalse, "false", "Do not load .lldbinit files from current directory"}, {eLoadCWDlldbinitWarn, "warn", "Warn about loading .lldbinit files from current directory"}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_memory_module_load_level_values[] = { {eMemoryModuleLoadLevelMinimal, "minimal", "Load minimal information when loading modules from memory. Currently " "this setting loads sections only."}, {eMemoryModuleLoadLevelPartial, "partial", "Load partial information when loading modules from memory. Currently " "this setting loads sections and function bounds."}, {eMemoryModuleLoadLevelComplete, "complete", "Load complete information when loading modules from memory. Currently " "this setting loads sections and all symbols."}, {0, nullptr, nullptr}}; static PropertyDefinition g_properties[] = { {"default-arch", OptionValue::eTypeArch, true, 0, nullptr, nullptr, "Default architecture to choose, when there's a choice."}, {"move-to-nearest-code", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Move breakpoints to nearest code."}, {"language", OptionValue::eTypeLanguage, false, eLanguageTypeUnknown, nullptr, nullptr, "The language to use when interpreting expressions entered in commands."}, {"expr-prefix", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, "Path to a file containing expressions to be prepended to all " "expressions."}, {"prefer-dynamic-value", OptionValue::eTypeEnum, false, eDynamicDontRunTarget, nullptr, g_dynamic_value_types, "Should printed values be shown as their dynamic value."}, {"enable-synthetic-value", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Should synthetic values be used by default whenever available."}, {"skip-prologue", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Skip function prologues when setting breakpoints by name."}, {"source-map", OptionValue::eTypePathMap, false, 0, nullptr, nullptr, "Source path remappings are used to track the change of location between " "a source file when built, and " "where it exists on the current system. It consists of an array of " "duples, the first element of each duple is " "some part (starting at the root) of the path to the file when it was " "built, " "and the second is where the remainder of the original build hierarchy is " "rooted on the local system. " "Each element of the array is checked in order and the first one that " "results in a match wins."}, {"exec-search-paths", OptionValue::eTypeFileSpecList, false, 0, nullptr, nullptr, "Executable search paths to use when locating executable files " "whose paths don't match the local file system."}, {"debug-file-search-paths", OptionValue::eTypeFileSpecList, false, 0, nullptr, nullptr, "List of directories to be searched when locating debug symbol files."}, {"clang-module-search-paths", OptionValue::eTypeFileSpecList, false, 0, nullptr, nullptr, "List of directories to be searched when locating modules for Clang."}, {"auto-import-clang-modules", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Automatically load Clang modules referred to by the program."}, {"auto-apply-fixits", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Automatically apply fix-it hints to expressions."}, {"notify-about-fixits", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Print the fixed expression text."}, {"save-jit-objects", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "Save intermediate object files generated by the LLVM JIT"}, {"max-children-count", OptionValue::eTypeSInt64, false, 256, nullptr, nullptr, "Maximum number of children to expand in any level of depth."}, {"max-string-summary-length", OptionValue::eTypeSInt64, false, 1024, nullptr, nullptr, "Maximum number of characters to show when using %s in summary strings."}, {"max-memory-read-size", OptionValue::eTypeSInt64, false, 1024, nullptr, nullptr, "Maximum number of bytes that 'memory read' will fetch before " "--force must be specified."}, {"breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Consult the platform module avoid list when " "setting non-module specific breakpoints."}, {"arg0", OptionValue::eTypeString, false, 0, nullptr, nullptr, "The first argument passed to the program in the argument array which can " "be different from the executable itself."}, {"run-args", OptionValue::eTypeArgs, false, 0, nullptr, nullptr, "A list containing all the arguments to be passed to the executable when " "it is run. Note that this does NOT include the argv[0] which is in " "target.arg0."}, {"env-vars", OptionValue::eTypeDictionary, false, OptionValue::eTypeString, nullptr, nullptr, "A list of all the environment variables to be passed " "to the executable's environment, and their values."}, {"inherit-env", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Inherit the environment from the process that is running LLDB."}, {"input-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, "The file/path to be used by the executable program for reading its " "standard input."}, {"output-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, "The file/path to be used by the executable program for writing its " "standard output."}, {"error-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, "The file/path to be used by the executable program for writing its " "standard error."}, {"detach-on-error", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "debugserver will detach (rather than killing) a process if it " "loses connection with lldb."}, {"preload-symbols", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Enable loading of symbol tables before they are needed."}, {"disable-aslr", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Disable Address Space Layout Randomization (ASLR)"}, {"disable-stdio", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "Disable stdin/stdout for process (e.g. for a GUI application)"}, {"inline-breakpoint-strategy", OptionValue::eTypeEnum, false, eInlineBreakpointsAlways, nullptr, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " "Breakpoint locations can end up being inlined by the compiler, so that a " "compile unit 'a.c' might contain an inlined function from another source " "file. " "Usually this is limited to breakpoint locations from inlined functions " "from header or other include files, or more accurately " "non-implementation source files. " "Sometimes code might #include implementation files and cause inlined " "breakpoint locations in inlined implementation files. " "Always checking for inlined breakpoint locations can be expensive " "(memory and time), so if you have a project with many headers " "and find that setting breakpoints is slow, then you can change this " "setting to headers. " "This setting allows you to control exactly which strategy is used when " "setting " "file and line breakpoints."}, // FIXME: This is the wrong way to do per-architecture settings, but we // don't have a general per architecture settings system in place yet. {"x86-disassembly-flavor", OptionValue::eTypeEnum, false, eX86DisFlavorDefault, nullptr, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets."}, {"use-hex-immediates", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Show immediates in disassembly as hexadecimal."}, {"hex-immediate-style", OptionValue::eTypeEnum, false, Disassembler::eHexStyleC, nullptr, g_hex_immediate_style_values, "Which style to use for printing hexadecimal disassembly values."}, {"use-fast-stepping", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "Use a fast stepping algorithm based on running from branch to " "branch rather than instruction single-stepping."}, {"load-script-from-symbol-file", OptionValue::eTypeEnum, false, eLoadScriptFromSymFileWarn, nullptr, g_load_script_from_sym_file_values, "Allow LLDB to load scripting resources embedded in symbol files when " "available."}, {"load-cwd-lldbinit", OptionValue::eTypeEnum, false, eLoadCWDlldbinitWarn, nullptr, g_load_current_working_dir_lldbinit_values, "Allow LLDB to .lldbinit files from the current directory automatically."}, {"memory-module-load-level", OptionValue::eTypeEnum, false, eMemoryModuleLoadLevelComplete, nullptr, g_memory_module_load_level_values, "Loading modules from memory can be slow as reading the symbol tables and " "other data can take a long time depending on your connection to the " "debug target. " "This setting helps users control how much information gets loaded when " "loading modules from memory." "'complete' is the default value for this setting which will load all " "sections and symbols by reading them from memory (slowest, most " "accurate). " "'partial' will load sections and attempt to find function bounds without " "downloading the symbol table (faster, still accurate, missing symbol " "names). " "'minimal' is the fastest setting and will load section data with no " "symbols, but should rarely be used as stack frames in these memory " "regions will be inaccurate and not provide any context (fastest). "}, {"display-expression-in-crashlogs", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "Expressions that crash will show up in crash logs if " "the host system supports executable specific crash log " "strings and this setting is set to true."}, {"trap-handler-names", OptionValue::eTypeArray, true, OptionValue::eTypeString, nullptr, nullptr, "A list of trap handler function names, e.g. a common Unix user process " "one is _sigtramp."}, {"display-runtime-support-values", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "If true, LLDB will show variables that are meant to " "support the operation of a language's runtime " "support."}, {"non-stop-mode", OptionValue::eTypeBoolean, false, 0, nullptr, nullptr, "Disable lock-step debugging, instead control threads independently."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertyDefaultArch, ePropertyMoveToNearestCode, ePropertyLanguage, ePropertyExprPrefix, ePropertyPreferDynamic, ePropertyEnableSynthetic, ePropertySkipPrologue, ePropertySourceMap, ePropertyExecutableSearchPaths, ePropertyDebugFileSearchPaths, ePropertyClangModuleSearchPaths, ePropertyAutoImportClangModules, ePropertyAutoApplyFixIts, ePropertyNotifyAboutFixIts, ePropertySaveObjects, ePropertyMaxChildrenCount, ePropertyMaxSummaryLength, ePropertyMaxMemReadSize, ePropertyBreakpointUseAvoidList, ePropertyArg0, ePropertyRunArgs, ePropertyEnvVars, ePropertyInheritEnv, ePropertyInputPath, ePropertyOutputPath, ePropertyErrorPath, ePropertyDetachOnError, ePropertyPreloadSymbols, ePropertyDisableASLR, ePropertyDisableSTDIO, ePropertyInlineStrategy, ePropertyDisassemblyFlavor, ePropertyUseHexImmediates, ePropertyHexImmediateStyle, ePropertyUseFastStepping, ePropertyLoadScriptFromSymbolFile, ePropertyLoadCWDlldbinitFile, ePropertyMemoryModuleLoadLevel, ePropertyDisplayExpressionsInCrashlogs, ePropertyTrapHandlerNames, ePropertyDisplayRuntimeSupportValues, ePropertyNonStopModeEnabled, ePropertyExperimental }; class TargetOptionValueProperties : public OptionValueProperties { public: TargetOptionValueProperties(const ConstString &name) : OptionValueProperties(name), m_target(nullptr), m_got_host_env(false) {} // This constructor is used when creating TargetOptionValueProperties when it // is part of a new lldb_private::Target instance. It will copy all current // global property values as needed TargetOptionValueProperties(Target *target, const TargetPropertiesSP &target_properties_sp) : OptionValueProperties(*target_properties_sp->GetValueProperties()), m_target(target), m_got_host_env(false) {} const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the target options, we will always // try and grab the setting from the current target if there is one. Else we // just // use the one from this instance. if (idx == ePropertyEnvVars) GetHostEnvironmentIfNeeded(); if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { TargetOptionValueProperties *target_properties = static_cast( target->GetValueProperties().get()); if (this != target_properties) return target_properties->ProtectedGetPropertyAtIndex(idx); } } return ProtectedGetPropertyAtIndex(idx); } lldb::TargetSP GetTargetSP() { return m_target->shared_from_this(); } protected: void GetHostEnvironmentIfNeeded() const { if (!m_got_host_env) { if (m_target) { m_got_host_env = true; const uint32_t idx = ePropertyInheritEnv; if (GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0)) { PlatformSP platform_sp(m_target->GetPlatform()); if (platform_sp) { StringList env; if (platform_sp->GetEnvironment(env)) { OptionValueDictionary *env_dict = GetPropertyAtIndexAsOptionValueDictionary(nullptr, ePropertyEnvVars); if (env_dict) { const bool can_replace = false; const size_t envc = env.GetSize(); for (size_t idx = 0; idx < envc; idx++) { const char *env_entry = env.GetStringAtIndex(idx); if (env_entry) { const char *equal_pos = ::strchr(env_entry, '='); ConstString key; // It is ok to have environment variables with no values const char *value = nullptr; if (equal_pos) { key.SetCStringWithLength(env_entry, equal_pos - env_entry); if (equal_pos[1]) value = equal_pos + 1; } else { key.SetCString(env_entry); } // Don't allow existing keys to be replaced with ones we get // from the platform environment env_dict->SetValueForKey( key, OptionValueSP(new OptionValueString(value)), can_replace); } } } } } } } } } Target *m_target; mutable bool m_got_host_env; }; //---------------------------------------------------------------------- // TargetProperties //---------------------------------------------------------------------- static PropertyDefinition g_experimental_properties[]{ {"inject-local-vars", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, inject local variables explicitly into the expression text. " "This will fix symbol resolution when there are name collisions between " "ivars and local variables. " "But it can make expressions run much more slowly."}, {nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}}; enum { ePropertyInjectLocalVars = 0 }; class TargetExperimentalOptionValueProperties : public OptionValueProperties { public: TargetExperimentalOptionValueProperties() : OptionValueProperties( ConstString(Properties::GetExperimentalSettingsName())) {} }; TargetExperimentalProperties::TargetExperimentalProperties() : Properties(OptionValuePropertiesSP( new TargetExperimentalOptionValueProperties())) { m_collection_sp->Initialize(g_experimental_properties); } //---------------------------------------------------------------------- // TargetProperties //---------------------------------------------------------------------- TargetProperties::TargetProperties(Target *target) : Properties(), m_launch_info() { if (target) { m_collection_sp.reset( new TargetOptionValueProperties(target, Target::GetGlobalProperties())); // Set callbacks to update launch_info whenever "settins set" updated any of // these properties m_collection_sp->SetValueChangedCallback( ePropertyArg0, TargetProperties::Arg0ValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyRunArgs, TargetProperties::RunArgsValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyEnvVars, TargetProperties::EnvVarsValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyInputPath, TargetProperties::InputPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyOutputPath, TargetProperties::OutputPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyErrorPath, TargetProperties::ErrorPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyDetachOnError, TargetProperties::DetachOnErrorValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyDisableASLR, TargetProperties::DisableASLRValueChangedCallback, this); m_collection_sp->SetValueChangedCallback( ePropertyDisableSTDIO, TargetProperties::DisableSTDIOValueChangedCallback, this); m_experimental_properties_up.reset(new TargetExperimentalProperties()); m_collection_sp->AppendProperty( ConstString(Properties::GetExperimentalSettingsName()), ConstString("Experimental settings - setting these won't produce " "errors if the setting is not present."), true, m_experimental_properties_up->GetValueProperties()); // Update m_launch_info once it was created Arg0ValueChangedCallback(this, nullptr); RunArgsValueChangedCallback(this, nullptr); // EnvVarsValueChangedCallback(this, nullptr); // FIXME: cause segfault in // Target::GetPlatform() InputPathValueChangedCallback(this, nullptr); OutputPathValueChangedCallback(this, nullptr); ErrorPathValueChangedCallback(this, nullptr); DetachOnErrorValueChangedCallback(this, nullptr); DisableASLRValueChangedCallback(this, nullptr); DisableSTDIOValueChangedCallback(this, nullptr); } else { m_collection_sp.reset( new TargetOptionValueProperties(ConstString("target"))); m_collection_sp->Initialize(g_properties); m_experimental_properties_up.reset(new TargetExperimentalProperties()); m_collection_sp->AppendProperty( ConstString(Properties::GetExperimentalSettingsName()), ConstString("Experimental settings - setting these won't produce " "errors if the setting is not present."), true, m_experimental_properties_up->GetValueProperties()); m_collection_sp->AppendProperty( ConstString("process"), ConstString("Settings specific to processes."), true, Process::GetGlobalProperties()->GetValueProperties()); } } TargetProperties::~TargetProperties() = default; bool TargetProperties::GetInjectLocalVariables( ExecutionContext *exe_ctx) const { const Property *exp_property = m_collection_sp->GetPropertyAtIndex( exe_ctx, false, ePropertyExperimental); OptionValueProperties *exp_values = exp_property->GetValue()->GetAsProperties(); if (exp_values) return exp_values->GetPropertyAtIndexAsBoolean( exe_ctx, ePropertyInjectLocalVars, true); else return true; } void TargetProperties::SetInjectLocalVariables(ExecutionContext *exe_ctx, bool b) { const Property *exp_property = m_collection_sp->GetPropertyAtIndex(exe_ctx, true, ePropertyExperimental); OptionValueProperties *exp_values = exp_property->GetValue()->GetAsProperties(); if (exp_values) exp_values->SetPropertyAtIndexAsBoolean(exe_ctx, ePropertyInjectLocalVars, true); } ArchSpec TargetProperties::GetDefaultArchitecture() const { OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( nullptr, ePropertyDefaultArch); if (value) return value->GetCurrentValue(); return ArchSpec(); } void TargetProperties::SetDefaultArchitecture(const ArchSpec &arch) { OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( nullptr, ePropertyDefaultArch); if (value) return value->SetCurrentValue(arch, true); } bool TargetProperties::GetMoveToNearestCode() const { const uint32_t idx = ePropertyMoveToNearestCode; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const { const uint32_t idx = ePropertyPreferDynamic; return (lldb::DynamicValueType) m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } bool TargetProperties::SetPreferDynamicValue(lldb::DynamicValueType d) { const uint32_t idx = ePropertyPreferDynamic; return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, d); } bool TargetProperties::GetPreloadSymbols() const { const uint32_t idx = ePropertyPreloadSymbols; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetPreloadSymbols(bool b) { const uint32_t idx = ePropertyPreloadSymbols; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetDisableASLR() const { const uint32_t idx = ePropertyDisableASLR; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDisableASLR(bool b) { const uint32_t idx = ePropertyDisableASLR; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetDetachOnError() const { const uint32_t idx = ePropertyDetachOnError; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDetachOnError(bool b) { const uint32_t idx = ePropertyDetachOnError; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetDisableSTDIO() const { const uint32_t idx = ePropertyDisableSTDIO; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDisableSTDIO(bool b) { const uint32_t idx = ePropertyDisableSTDIO; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } const char *TargetProperties::GetDisassemblyFlavor() const { const uint32_t idx = ePropertyDisassemblyFlavor; const char *return_value; x86DisassemblyFlavor flavor_value = (x86DisassemblyFlavor)m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); return_value = g_x86_dis_flavor_value_types[flavor_value].string_value; return return_value; } InlineStrategy TargetProperties::GetInlineStrategy() const { const uint32_t idx = ePropertyInlineStrategy; return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } llvm::StringRef TargetProperties::GetArg0() const { const uint32_t idx = ePropertyArg0; return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, llvm::StringRef()); } void TargetProperties::SetArg0(llvm::StringRef arg) { const uint32_t idx = ePropertyArg0; m_collection_sp->SetPropertyAtIndexAsString( nullptr, idx, arg); m_launch_info.SetArg0(arg); } bool TargetProperties::GetRunArguments(Args &args) const { const uint32_t idx = ePropertyRunArgs; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); } void TargetProperties::SetRunArguments(const Args &args) { const uint32_t idx = ePropertyRunArgs; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); m_launch_info.GetArguments() = args; } size_t TargetProperties::GetEnvironmentAsArgs(Args &env) const { const uint32_t idx = ePropertyEnvVars; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env); } void TargetProperties::SetEnvironmentFromArgs(const Args &env) { const uint32_t idx = ePropertyEnvVars; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, env); m_launch_info.GetEnvironmentEntries() = env; } bool TargetProperties::GetSkipPrologue() const { const uint32_t idx = ePropertySkipPrologue; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } PathMappingList &TargetProperties::GetSourcePathMap() const { const uint32_t idx = ePropertySourceMap; OptionValuePathMappings *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList &TargetProperties::GetExecutableSearchPaths() { const uint32_t idx = ePropertyExecutableSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList &TargetProperties::GetDebugFileSearchPaths() { const uint32_t idx = ePropertyDebugFileSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList &TargetProperties::GetClangModuleSearchPaths() { const uint32_t idx = ePropertyClangModuleSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } bool TargetProperties::GetEnableAutoImportClangModules() const { const uint32_t idx = ePropertyAutoImportClangModules; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetEnableAutoApplyFixIts() const { const uint32_t idx = ePropertyAutoApplyFixIts; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetEnableNotifyAboutFixIts() const { const uint32_t idx = ePropertyNotifyAboutFixIts; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetEnableSaveObjects() const { const uint32_t idx = ePropertySaveObjects; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetEnableSyntheticValue() const { const uint32_t idx = ePropertyEnableSynthetic; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const { const uint32_t idx = ePropertyMaxChildrenCount; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { const uint32_t idx = ePropertyMaxSummaryLength; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } uint32_t TargetProperties::GetMaximumMemReadSize() const { const uint32_t idx = ePropertyMaxMemReadSize; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } FileSpec TargetProperties::GetStandardInputPath() const { const uint32_t idx = ePropertyInputPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void TargetProperties::SetStandardInputPath(llvm::StringRef path) { const uint32_t idx = ePropertyInputPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); } FileSpec TargetProperties::GetStandardOutputPath() const { const uint32_t idx = ePropertyOutputPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void TargetProperties::SetStandardOutputPath(llvm::StringRef path) { const uint32_t idx = ePropertyOutputPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); } FileSpec TargetProperties::GetStandardErrorPath() const { const uint32_t idx = ePropertyErrorPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void TargetProperties::SetStandardErrorPath(llvm::StringRef path) { const uint32_t idx = ePropertyErrorPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); } LanguageType TargetProperties::GetLanguage() const { OptionValueLanguage *value = m_collection_sp->GetPropertyAtIndexAsOptionValueLanguage( nullptr, ePropertyLanguage); if (value) return value->GetCurrentValue(); return LanguageType(); } const char *TargetProperties::GetExpressionPrefixContentsAsCString() { const uint32_t idx = ePropertyExprPrefix; OptionValueFileSpec *file = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false, idx); if (file) { const bool null_terminate = true; DataBufferSP data_sp(file->GetFileContents(null_terminate)); if (data_sp) return (const char *)data_sp->GetBytes(); } return nullptr; } bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() { const uint32_t idx = ePropertyBreakpointUseAvoidList; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetUseHexImmediates() const { const uint32_t idx = ePropertyUseHexImmediates; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetUseFastStepping() const { const uint32_t idx = ePropertyUseFastStepping; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetDisplayExpressionsInCrashlogs() const { const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile() const { const uint32_t idx = ePropertyLoadScriptFromSymbolFile; return (LoadScriptFromSymFile) m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } LoadCWDlldbinitFile TargetProperties::GetLoadCWDlldbinitFile() const { const uint32_t idx = ePropertyLoadCWDlldbinitFile; return (LoadCWDlldbinitFile)m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle() const { const uint32_t idx = ePropertyHexImmediateStyle; return (Disassembler::HexImmediateStyle) m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const { const uint32_t idx = ePropertyMemoryModuleLoadLevel; return (MemoryModuleLoadLevel) m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } bool TargetProperties::GetUserSpecifiedTrapHandlerNames(Args &args) const { const uint32_t idx = ePropertyTrapHandlerNames; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); } void TargetProperties::SetUserSpecifiedTrapHandlerNames(const Args &args) { const uint32_t idx = ePropertyTrapHandlerNames; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); } bool TargetProperties::GetDisplayRuntimeSupportValues() const { const uint32_t idx = ePropertyDisplayRuntimeSupportValues; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); } void TargetProperties::SetDisplayRuntimeSupportValues(bool b) { const uint32_t idx = ePropertyDisplayRuntimeSupportValues; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetNonStopModeEnabled() const { const uint32_t idx = ePropertyNonStopModeEnabled; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); } void TargetProperties::SetNonStopModeEnabled(bool b) { const uint32_t idx = ePropertyNonStopModeEnabled; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } const ProcessLaunchInfo &TargetProperties::GetProcessLaunchInfo() { m_launch_info.SetArg0(GetArg0()); // FIXME: Arg0 callback doesn't work return m_launch_info; } void TargetProperties::SetProcessLaunchInfo( const ProcessLaunchInfo &launch_info) { m_launch_info = launch_info; SetArg0(launch_info.GetArg0()); SetRunArguments(launch_info.GetArguments()); SetEnvironmentFromArgs(launch_info.GetEnvironmentEntries()); const FileAction *input_file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (input_file_action) { SetStandardInputPath(input_file_action->GetPath()); } const FileAction *output_file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (output_file_action) { SetStandardOutputPath(output_file_action->GetPath()); } const FileAction *error_file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (error_file_action) { SetStandardErrorPath(error_file_action->GetPath()); } SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError)); SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)); SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO)); } void TargetProperties::Arg0ValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.SetArg0(this_->GetArg0()); } void TargetProperties::RunArgsValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); Args args; if (this_->GetRunArguments(args)) this_->m_launch_info.GetArguments() = args; } void TargetProperties::EnvVarsValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); Args args; if (this_->GetEnvironmentAsArgs(args)) this_->m_launch_info.GetEnvironmentEntries() = args; } void TargetProperties::InputPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction( STDIN_FILENO, this_->GetStandardInputPath(), true, false); } void TargetProperties::OutputPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction( STDOUT_FILENO, this_->GetStandardOutputPath(), false, true); } void TargetProperties::ErrorPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction( STDERR_FILENO, this_->GetStandardErrorPath(), false, true); } void TargetProperties::DetachOnErrorValueChangedCallback( void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDetachOnError()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDetachOnError); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDetachOnError); } void TargetProperties::DisableASLRValueChangedCallback( void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDisableASLR()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableASLR); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR); } void TargetProperties::DisableSTDIOValueChangedCallback( void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDisableSTDIO()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO); } //---------------------------------------------------------------------- // Target::TargetEventData //---------------------------------------------------------------------- Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp) : EventData(), m_target_sp(target_sp), m_module_list() {} Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp, const ModuleList &module_list) : EventData(), m_target_sp(target_sp), m_module_list(module_list) {} Target::TargetEventData::~TargetEventData() = default; const ConstString &Target::TargetEventData::GetFlavorString() { static ConstString g_flavor("Target::TargetEventData"); return g_flavor; } void Target::TargetEventData::Dump(Stream *s) const { for (size_t i = 0; i < m_module_list.GetSize(); ++i) { if (i != 0) *s << ", "; m_module_list.GetModuleAtIndex(i)->GetDescription( s, lldb::eDescriptionLevelBrief); } } const Target::TargetEventData * Target::TargetEventData::GetEventDataFromEvent(const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == TargetEventData::GetFlavorString()) return static_cast(event_ptr->GetData()); } return nullptr; } TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) { TargetSP target_sp; const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) target_sp = event_data->m_target_sp; return target_sp; } ModuleList Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) { ModuleList module_list; const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) module_list = event_data->m_module_list; return module_list; } Index: vendor/lldb/dist/test/CMakeLists.txt =================================================================== --- vendor/lldb/dist/test/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/test/CMakeLists.txt (revision 321194) @@ -1,123 +1,123 @@ function(add_python_test_target name test_script args comment) set(PYTHON_TEST_COMMAND ${PYTHON_EXECUTABLE} ${test_script} ${args} ) add_custom_target(${name} COMMAND ${PYTHON_TEST_COMMAND} ${ARG_DEFAULT_ARGS} COMMENT "${comment}" DEPENDS ${LLDB_TEST_DEPS} USES_TERMINAL ) endfunction() set(LLDB_TEST_DEPS lldb) if(TARGET lldb-server) list(APPEND LLDB_TEST_DEPS lldb-server) endif() if(TARGET debugserver) list(APPEND LLDB_TEST_DEPS debugserver) endif() if(TARGET lldb-mi) list(APPEND LLDB_TEST_DEPS lldb-mi) endif() if ("${LLDB_TEST_COMPILER}" STREQUAL "") string(REGEX REPLACE ".*ccache\ +" "" LLDB_TEST_COMPILER ${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}) endif() # The default architecture with which to compile test executables is the default LLVM target # architecture, which itself defaults to the host architecture. string(TOLOWER "${LLVM_TARGET_ARCH}" LLDB_DEFAULT_TEST_ARCH) if( LLDB_DEFAULT_TEST_ARCH STREQUAL "host" ) string(REGEX MATCH "^[^-]*" LLDB_DEFAULT_TEST_ARCH ${LLVM_HOST_TRIPLE}) endif () # Allow the user to override the default by setting LLDB_TEST_ARCH set(LLDB_TEST_ARCH ${LLDB_DEFAULT_TEST_ARCH} CACHE STRING "Specify the architecture to run LLDB tests as (x86|x64). Determines whether tests are compiled with -m32 or -m64") if(LLDB_TEST_CLANG) set(LLDB_TEST_COMPILER $) endif() # Users can override LLDB_TEST_USER_ARGS to specify arbitrary arguments to pass to the script set(LLDB_TEST_USER_ARGS - "${LLDB_TEST_USER_ARGS_New}" - CACHE STRING "Specify additional arguments to pass to test runner. For example: '-C gcc -C clang -A i386 -A x86_64'" FORCE) + "" + CACHE STRING "Specify additional arguments to pass to test runner. For example: '-C gcc -C clang -A i386 -A x86_64'") set(LLDB_TEST_COMMON_ARGS --arch=${LLDB_TEST_ARCH} --executable $ -s ${CMAKE_BINARY_DIR}/lldb-test-traces -S nm -u CXXFLAGS -u CFLAGS -C ${LLDB_TEST_COMPILER} ) if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) # All tests are currently flaky on Windows, so rerun them all once when they fail. set(LLDB_TEST_COMMON_ARGS ${LLDB_TEST_COMMON_ARGS} --rerun-all-issues) set(LLDB_TEST_DEBUG_TEST_CRASHES 0 CACHE BOOL "(Windows only) Enables debugging of tests in the test suite by showing the crash dialog when lldb crashes") set(LLDB_TEST_HIDE_CONSOLE_WINDOWS 1 CACHE BOOL "(Windows only) Hides the console window for an inferior when it is launched through the test suite") if (LLDB_TEST_DEBUG_TEST_CRASHES) set(LLDB_TEST_COMMON_ARGS ${LLDB_TEST_COMMON_ARGS} --enable-crash-dialog) endif() if (NOT LLDB_TEST_HIDE_CONSOLE_WINDOWS) set(LLDB_TEST_COMMON_ARGS ${LLDB_TEST_COMMON_ARGS} --show-inferior-console) endif() endif() if(LLDB_CODESIGN_IDENTITY) list(APPEND LLDB_TEST_COMMON_ARGS --codesign-identity "${LLDB_CODESIGN_IDENTITY}") endif() if(LLDB_BUILD_FRAMEWORK) list(APPEND LLDB_TEST_COMMON_ARGS --framework $) endif() if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows|Darwin") list(APPEND LLDB_TEST_COMMON_ARGS --env ARCHIVER=${CMAKE_AR} --env OBJCOPY=${CMAKE_OBJCOPY}) endif() if(CMAKE_HOST_APPLE) list(APPEND LLDB_TEST_COMMON_ARGS --server $) endif() +set(LLDB_DOTEST_ARGS ${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}) + add_python_test_target(check-lldb-single ${LLDB_SOURCE_DIR}/test/dotest.py - "--no-multiprocess;${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}" - "Testing LLDB with args: ${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}" + "--no-multiprocess;${LLDB_DOTEST_ARGS}" + "Testing LLDB with args: ${LLDB_DOTEST_ARGS}" ) -set(LLDB_DOTEST_ARGS -q;${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}) - # If tests crash cause LLDB to crash, or things are otherwise unstable, or if machine-parsable # output is desired (i.e. in continuous integration contexts) check-lldb-single is a better target. add_python_test_target(check-lldb ${LLDB_SOURCE_DIR}/test/dotest.py - "${LLDB_DOTEST_ARGS}" + "-q;${LLDB_DOTEST_ARGS}" "Testing LLDB (parallel execution, with a separate subprocess per test)" ) add_custom_target(lldb-test-depends DEPENDS ${LLDB_TEST_DEPENDS}) # This will add LLDB's test dependencies to the depenednecies for check-all and # include them in the test-depends target. set_property(GLOBAL APPEND PROPERTY LLVM_LIT_DEPENDS ${ARG_DEPENDS}) Index: vendor/lldb/dist/tools/debugserver/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/CMakeLists.txt (revision 321194) @@ -1,2 +1,19 @@ -project(C CXX ASM-ATT) +cmake_minimum_required(VERSION 3.4.3) + +project(Debugserver LANGUAGES C CXX ASM-ATT) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_SOURCE_DIR}/../../cmake" + "${CMAKE_SOURCE_DIR}/../../cmake/modules" + ) + + include(LLDBStandalone) + include(AddLLDB) + + set(LLDB_SOURCE_DIR "${CMAKE_SOURCE_DIR}/../../") + include_directories(${LLDB_SOURCE_DIR}/include) +endif() + add_subdirectory(source) Index: vendor/lldb/dist/tools/debugserver/source/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/source/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/CMakeLists.txt (revision 321194) @@ -1,127 +1,139 @@ +include(CheckCXXCompilerFlag) include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) include_directories(${LLDB_SOURCE_DIR}/source) include_directories(MacOSX/DarwinLog) include_directories(MacOSX) #include_directories(${CMAKE_CURRENT_BINARY_DIR}/MacOSX) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/../resources/lldb-debugserver-Info.plist") check_cxx_compiler_flag("-Wno-gnu-zero-variadic-macro-arguments" CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) if (CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments") endif () check_cxx_compiler_flag("-Wno-zero-length-array" CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY) if (CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-length-array") endif () check_cxx_compiler_flag("-Wno-extended-offsetof" CXX_SUPPORTS_NO_EXTENDED_OFFSETOF) if (CXX_SUPPORTS_NO_EXTENDED_OFFSETOF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof") endif () -find_library(COCOA_LIBRARY Cocoa) add_subdirectory(MacOSX) set(generated_mach_interfaces ${CMAKE_CURRENT_BINARY_DIR}/mach_exc.h ${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c ${CMAKE_CURRENT_BINARY_DIR}/mach_excUser.c ) add_custom_command(OUTPUT ${generated_mach_interfaces} COMMAND mig ${CMAKE_CURRENT_SOURCE_DIR}/MacOSX/dbgnub-mig.defs DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/MacOSX/dbgnub-mig.defs ) set(DEBUGSERVER_VERS_GENERATED_FILE ${CMAKE_CURRENT_BINARY_DIR}/debugserver_vers.c) set_source_files_properties(${DEBUGSERVER_VERS_GENERATED_FILE} PROPERTIES GENERATED 1) add_custom_command(OUTPUT ${DEBUGSERVER_VERS_GENERATED_FILE} COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj debugserver > ${DEBUGSERVER_VERS_GENERATED_FILE} DEPENDS ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj ) set(lldbDebugserverCommonSources DNBArch.cpp DNBBreakpoint.cpp DNB.cpp DNBDataRef.cpp DNBError.cpp DNBLog.cpp DNBRegisterInfo.cpp DNBThreadResumeActions.cpp JSON.cpp StdStringExtractor.cpp # JSON reader depends on the following LLDB-common files ${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp ${LLDB_SOURCE_DIR}/source/Host/common/SocketAddress.cpp # end JSON reader dependencies libdebugserver.cpp PseudoTerminal.cpp PThreadEvent.cpp PThreadMutex.cpp RNBContext.cpp RNBRemote.cpp RNBServices.cpp RNBSocket.cpp SysSignal.cpp TTYState.cpp MacOSX/CFBundle.cpp MacOSX/CFString.cpp MacOSX/Genealogy.cpp MacOSX/MachException.cpp MacOSX/MachProcess.mm MacOSX/MachTask.mm MacOSX/MachThread.cpp MacOSX/MachThreadList.cpp MacOSX/MachVMMemory.cpp MacOSX/MachVMRegion.cpp MacOSX/OsLogger.cpp ${generated_mach_interfaces} ${DEBUGSERVER_VERS_GENERATED_FILE}) add_library(lldbDebugserverCommon ${lldbDebugserverCommonSources}) + +if (APPLE) + if(IOS) + find_library(COCOA_LIBRARY UIKit) + target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY} ${CORE_FOUNDATION_LIBRARY} ${FOUNDATION_LIBRARY}) + else() + find_library(COCOA_LIBRARY Cocoa) + target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY}) + endif() +endif() + target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY} - lldbDebugserverMacOSX_I386 - lldbDebugserverMacOSX_X86_64 - lldbDebugserverMacOSX_DarwinLog) + ${CORE_FOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} + lldbDebugserverArchSupport + lldbDebugserverDarwin_DarwinLog) set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources}) add_lldb_tool(debugserver INCLUDE_IN_FRAMEWORK debugserver.cpp LINK_LIBS lldbDebugserverCommon ) set(LLDB_CODESIGN_IDENTITY "lldb_codesign" CACHE STRING "Identity used for code signing. Set to empty string to skip the signing step.") if (NOT ("${LLDB_CODESIGN_IDENTITY}" STREQUAL "")) execute_process( COMMAND xcrun -f codesign_allocate OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE CODESIGN_ALLOCATE ) add_custom_command(TARGET debugserver POST_BUILD COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${LLDB_CODESIGN_IDENTITY} $ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) endif() Index: vendor/lldb/dist/tools/debugserver/source/MacOSX/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/source/MacOSX/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/MacOSX/CMakeLists.txt (revision 321194) @@ -1,8 +1,23 @@ -#add_subdirectory(arm64) -#add_subdirectory(arm) -add_subdirectory(i386) -#add_subdirectory(ppc) -add_subdirectory(x86_64) +if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*arm.*") + list(APPEND SOURCES arm/DNBArchImpl.cpp arm64/DNBArchImplARM64.cpp) + include_directories(${CURRENT_SOURCE_DIR}/arm ${CURRENT_SOURCE_DIR}/arm64) +endif() + +if(NOT CMAKE_OSX_ARCHITECTURES OR "${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*86.*") + list(APPEND SOURCES i386/DNBArchImplI386.cpp x86_64/DNBArchImplX86_64.cpp) + include_directories(${CURRENT_SOURCE_DIR}/i386 ${CURRENT_SOURCE_DIR}/x86_64) +endif() + +if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*ppc.*") + list(APPEND SOURCES ppc/DNBArchImpl.cpp) + include_directories(${CURRENT_SOURCE_DIR}/ppc) +endif() + add_subdirectory(DarwinLog) include_directories(..) + +include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) +add_library(lldbDebugserverArchSupport + ${SOURCES} + ) Index: vendor/lldb/dist/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt (revision 321194) @@ -1,15 +1,15 @@ # Due to sources including headers like: # #include "MacOSX/i386/DNBArchImplI386.h" # we must include the grandparent directory... include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) -add_library(lldbDebugserverMacOSX_DarwinLog +add_library(lldbDebugserverDarwin_DarwinLog ActivityStore.cpp DarwinLogCollector.cpp LogFilter.cpp LogFilterChain.cpp LogFilterExactMatch.cpp LogFilterRegex.cpp LogMessage.cpp LogMessageOsLog.cpp ) Index: vendor/lldb/dist/tools/debugserver/source/MacOSX/i386/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/source/MacOSX/i386/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/MacOSX/i386/CMakeLists.txt (nonexistent) @@ -1,4 +0,0 @@ -include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) -add_library(lldbDebugserverMacOSX_I386 - DNBArchImplI386.cpp - ) Index: vendor/lldb/dist/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt (nonexistent) @@ -1,8 +0,0 @@ -# Due to sources including headers like: -# #include "MacOSX/i386/DNBArchImplI386.h" -# we must include the grandparent directory... -include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) - -add_library(lldbDebugserverMacOSX_X86_64 - DNBArchImplX86_64.cpp - ) Index: vendor/lldb/dist/tools/debugserver/source/RNBSocket.cpp =================================================================== --- vendor/lldb/dist/tools/debugserver/source/RNBSocket.cpp (revision 321193) +++ vendor/lldb/dist/tools/debugserver/source/RNBSocket.cpp (revision 321194) @@ -1,376 +1,391 @@ //===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Created by Greg Clayton on 12/12/07. // //===----------------------------------------------------------------------===// #include "RNBSocket.h" #include "DNBError.h" #include "DNBLog.h" #include #include #include #include #include #include #include #include #include #include #include "lldb/Host/SocketAddress.h" #ifdef WITH_LOCKDOWN #include "lockdown.h" #endif /* Once we have a RNBSocket object with a port # specified, this function is called to wait for an incoming connection. This function blocks while waiting for that connection. */ bool ResolveIPV4HostName(const char *hostname, in_addr_t &addr) { if (hostname == NULL || hostname[0] == '\0' || strcmp(hostname, "localhost") == 0 || strcmp(hostname, "127.0.0.1") == 0) { addr = htonl(INADDR_LOOPBACK); return true; } else if (strcmp(hostname, "*") == 0) { addr = htonl(INADDR_ANY); return true; } else { // See if an IP address was specified as numbers int inet_pton_result = ::inet_pton(AF_INET, hostname, &addr); if (inet_pton_result == 1) return true; struct hostent *host_entry = gethostbyname(hostname); if (host_entry) { std::string ip_str( ::inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list)); inet_pton_result = ::inet_pton(AF_INET, ip_str.c_str(), &addr); if (inet_pton_result == 1) return true; } } return false; } rnb_err_t RNBSocket::Listen(const char *listen_host, uint16_t port, PortBoundCallback callback, const void *callback_baton) { // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); // Disconnect without saving errno Disconnect(false); DNBError err; int queue_id = kqueue(); if (queue_id < 0) { err.SetError(errno, DNBError::MachKernel); err.LogThreaded("error: failed to create kqueue."); return rnb_err; } + bool any_addr = (strcmp(listen_host, "*") == 0); + + // If the user wants to allow connections from any address we should create + // sockets on all families that can resolve localhost. This will allow us to + // listen for IPv6 and IPv4 connections from all addresses if those interfaces + // are available. + const char *local_addr = any_addr ? "localhost" : listen_host; + std::map sockets; auto addresses = lldb_private::SocketAddress::GetAddressInfo( - listen_host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); + local_addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); for (auto address : addresses) { int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); if (sock_fd == -1) continue; SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1); - address.SetPort(port); + lldb_private::SocketAddress bind_address = address; - int error = ::bind(sock_fd, &address.sockaddr(), address.GetLength()); + if(any_addr || !bind_address.IsLocalhost()) + bind_address.SetToAnyAddress(bind_address.GetFamily(), port); + else + bind_address.SetPort(port); + + int error = + ::bind(sock_fd, &bind_address.sockaddr(), bind_address.GetLength()); if (error == -1) { ClosePort(sock_fd, false); continue; } error = ::listen(sock_fd, 5); if (error == -1) { ClosePort(sock_fd, false); continue; } // We were asked to listen on port zero which means we must now read the // actual port that was given to us as port zero is a special code for "find // an open port for me". This will only execute on the first socket created, // subesquent sockets will reuse this port number. if (port == 0) { socklen_t sa_len = address.GetLength(); if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0) port = address.GetPort(); } sockets[sock_fd] = address; } if (sockets.size() == 0) { err.SetError(errno, DNBError::POSIX); err.LogThreaded("::listen or ::bind failed"); return rnb_err; } if (callback) callback(callback_baton, port); std::vector events; events.resize(sockets.size()); int i = 0; for (auto socket : sockets) { EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0); } bool accept_connection = false; // Loop until we are happy with our connection while (!accept_connection) { struct kevent event_list[4]; int num_events = kevent(queue_id, events.data(), events.size(), event_list, 4, NULL); if (num_events < 0) { err.SetError(errno, DNBError::MachKernel); err.LogThreaded("error: kevent() failed."); } for (int i = 0; i < num_events; ++i) { auto sock_fd = event_list[i].ident; auto socket_pair = sockets.find(sock_fd); if (socket_pair == sockets.end()) continue; lldb_private::SocketAddress &addr_in = socket_pair->second; lldb_private::SocketAddress accept_addr; socklen_t sa_len = accept_addr.GetMaxLength(); m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len); if (m_fd == -1) { err.SetError(errno, DNBError::POSIX); err.LogThreaded("error: Socket accept failed."); } if (addr_in.IsAnyAddr()) accept_connection = true; else { if (accept_addr == addr_in) accept_connection = true; else { ::close(m_fd); m_fd = -1; ::fprintf( stderr, "error: rejecting incoming connection from %s (expecting %s)\n", accept_addr.GetIPAddress().c_str(), addr_in.GetIPAddress().c_str()); DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n", accept_addr.GetIPAddress().c_str(), addr_in.GetIPAddress().c_str()); + err.Clear(); } } } if (err.Fail()) break; } for (auto socket : sockets) { int ListenFd = socket.first; ClosePort(ListenFd, false); } if (err.Fail()) return rnb_err; // Keep our TCP packets coming without any delays. SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1); return rnb_success; } rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) { auto result = rnb_err; Disconnect(false); auto addresses = lldb_private::SocketAddress::GetAddressInfo( host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); for (auto address : addresses) { m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); if (m_fd == -1) continue; // Enable local address reuse SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1); address.SetPort(port); if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) { Disconnect(false); continue; } SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1); result = rnb_success; break; } return result; } rnb_err_t RNBSocket::useFD(int fd) { if (fd < 0) { DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in."); return rnb_err; } m_fd = fd; return rnb_success; } #ifdef WITH_LOCKDOWN rnb_err_t RNBSocket::ConnectToService() { DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); // Disconnect from any previous connections Disconnect(false); if (::secure_lockdown_checkin(&m_ld_conn, NULL, NULL) != kLDESuccess) { DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed"); m_fd = -1; return rnb_not_connected; } m_fd = ::lockdown_get_socket(m_ld_conn); if (m_fd == -1) { DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed"); return rnb_not_connected; } m_fd_from_lockdown = true; return rnb_success; } #endif rnb_err_t RNBSocket::OpenFile(const char *path) { DNBError err; m_fd = open(path, O_RDWR); if (m_fd == -1) { err.SetError(errno, DNBError::POSIX); err.LogThreaded("can't open file '%s'", path); return rnb_not_connected; } else { struct termios stdin_termios; if (::tcgetattr(m_fd, &stdin_termios) == 0) { stdin_termios.c_lflag &= ~ECHO; // Turn off echoing stdin_termios.c_lflag &= ~ICANON; // Get one char at a time ::tcsetattr(m_fd, TCSANOW, &stdin_termios); } } return rnb_success; } int RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value) { return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); } rnb_err_t RNBSocket::Disconnect(bool save_errno) { #ifdef WITH_LOCKDOWN if (m_fd_from_lockdown) { m_fd_from_lockdown = false; m_fd = -1; lockdown_disconnect(m_ld_conn); return rnb_success; } #endif return ClosePort(m_fd, save_errno); } rnb_err_t RNBSocket::Read(std::string &p) { char buf[1024]; p.clear(); // Note that BUF is on the stack so we must be careful to keep any // writes to BUF from overflowing or we'll have security issues. if (m_fd == -1) return rnb_err; // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); DNBError err; ssize_t bytesread = read(m_fd, buf, sizeof(buf)); if (bytesread <= 0) err.SetError(errno, DNBError::POSIX); else p.append(buf, bytesread); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof(buf), (uint64_t)bytesread); // Our port went away - we have to mark this so IsConnected will return the // truth. if (bytesread == 0) { m_fd = -1; return rnb_not_connected; } else if (bytesread == -1) { m_fd = -1; return rnb_err; } // Strip spaces from the end of the buffer while (!p.empty() && isspace(p[p.size() - 1])) p.erase(p.size() - 1); // Most data in the debugserver packets valid printable characters... DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); return rnb_success; } rnb_err_t RNBSocket::Write(const void *buffer, size_t length) { if (m_fd == -1) return rnb_err; DNBError err; ssize_t bytessent = write(m_fd, buffer, length); if (bytessent < 0) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent); if (bytessent < 0) return rnb_err; if ((size_t)bytessent != length) return rnb_err; DNBLogThreadedIf( LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *) buffer); // All data is string based in debugserver, so this is safe DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer); return rnb_success; } rnb_err_t RNBSocket::ClosePort(int &fd, bool save_errno) { int close_err = 0; if (fd > 0) { errno = 0; close_err = close(fd); fd = -1; } return close_err != 0 ? rnb_err : rnb_success; } Index: vendor/lldb/dist/tools/driver/Driver.cpp =================================================================== --- vendor/lldb/dist/tools/driver/Driver.cpp (revision 321193) +++ vendor/lldb/dist/tools/driver/Driver.cpp (revision 321194) @@ -1,1258 +1,1259 @@ //===-- Driver.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Driver.h" +#include #include #include #include #include #include // Includes for pipe() #if defined(_WIN32) #include #include #else #include #endif #include #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "llvm/Support/ConvertUTF.h" #include #if !defined(__APPLE__) #include "llvm/Support/DataTypes.h" #endif using namespace lldb; static void reset_stdin_termios(); static bool g_old_stdin_termios_is_valid = false; static struct termios g_old_stdin_termios; static Driver *g_driver = NULL; // In the Driver::MainLoop, we change the terminal settings. This function is // added as an atexit handler to make sure we clean them up. static void reset_stdin_termios() { if (g_old_stdin_termios_is_valid) { g_old_stdin_termios_is_valid = false; ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios); } } typedef struct { uint32_t usage_mask; // Used to mark options that can be used together. If (1 // << n & usage_mask) != 0 // then this option belongs to option set n. bool required; // This option is required (in the current usage level) const char *long_option; // Full name for this option. int short_option; // Single character for this option. int option_has_arg; // no_argument, required_argument or optional_argument uint32_t completion_type; // Cookie the option class can use to do define the // argument completion. lldb::CommandArgumentType argument_type; // Type of argument this option takes const char *usage_text; // Full text explaining what this options does and // what (if any) argument to // pass it. } OptionDefinition; #define LLDB_3_TO_5 LLDB_OPT_SET_3 | LLDB_OPT_SET_4 | LLDB_OPT_SET_5 #define LLDB_4_TO_5 LLDB_OPT_SET_4 | LLDB_OPT_SET_5 static OptionDefinition g_options[] = { {LLDB_OPT_SET_1, true, "help", 'h', no_argument, 0, eArgTypeNone, "Prints out the usage information for the LLDB debugger."}, {LLDB_OPT_SET_2, true, "version", 'v', no_argument, 0, eArgTypeNone, "Prints out the current version number of the LLDB debugger."}, {LLDB_OPT_SET_3, true, "arch", 'a', required_argument, 0, eArgTypeArchitecture, "Tells the debugger to use the specified architecture when starting and " "running the program. must " "be one of the architectures for which the program was compiled."}, {LLDB_OPT_SET_3, true, "file", 'f', required_argument, 0, eArgTypeFilename, "Tells the debugger to use the file as the program to be " "debugged."}, {LLDB_OPT_SET_3, false, "core", 'c', required_argument, 0, eArgTypeFilename, "Tells the debugger to use the fullpath to as the core file."}, {LLDB_OPT_SET_5, true, "attach-pid", 'p', required_argument, 0, eArgTypePid, "Tells the debugger to attach to a process with the given pid."}, {LLDB_OPT_SET_4, true, "attach-name", 'n', required_argument, 0, eArgTypeProcessName, "Tells the debugger to attach to a process with the given name."}, {LLDB_OPT_SET_4, true, "wait-for", 'w', no_argument, 0, eArgTypeNone, "Tells the debugger to wait for a process with the given pid or name to " "launch before attaching."}, {LLDB_3_TO_5, false, "source", 's', required_argument, 0, eArgTypeFilename, "Tells the debugger to read in and execute the lldb commands in the given " "file, after any file provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "one-line", 'o', required_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command after any file " "provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "source-before-file", 'S', required_argument, 0, eArgTypeFilename, "Tells the debugger to read in and execute the lldb " "commands in the given file, before any file provided " "on the command line has been loaded."}, {LLDB_3_TO_5, false, "one-line-before-file", 'O', required_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command " "before any file provided on the command line has been " "loaded."}, {LLDB_3_TO_5, false, "one-line-on-crash", 'k', required_argument, 0, eArgTypeNone, "When in batch mode, tells the debugger to execute this " "one-line lldb command if the target crashes."}, {LLDB_3_TO_5, false, "source-on-crash", 'K', required_argument, 0, eArgTypeFilename, "When in batch mode, tells the debugger to source this " "file of lldb commands if the target crashes."}, {LLDB_3_TO_5, false, "source-quietly", 'Q', no_argument, 0, eArgTypeNone, "Tells the debugger to execute this one-line lldb command before any file " "provided on the command line has been loaded."}, {LLDB_3_TO_5, false, "batch", 'b', no_argument, 0, eArgTypeNone, "Tells the debugger to run the commands from -s, -S, -o & -O, and " "then quit. However if any run command stopped due to a signal or crash, " "the debugger will return to the interactive prompt at the place of the " "crash."}, {LLDB_3_TO_5, false, "editor", 'e', no_argument, 0, eArgTypeNone, "Tells the debugger to open source files using the host's \"external " "editor\" mechanism."}, {LLDB_3_TO_5, false, "no-lldbinit", 'x', no_argument, 0, eArgTypeNone, "Do not automatically parse any '.lldbinit' files."}, {LLDB_3_TO_5, false, "no-use-colors", 'X', no_argument, 0, eArgTypeNone, "Do not use colors."}, {LLDB_OPT_SET_6, true, "python-path", 'P', no_argument, 0, eArgTypeNone, "Prints out the path to the lldb.py file for this version of lldb."}, {LLDB_3_TO_5, false, "script-language", 'l', required_argument, 0, eArgTypeScriptLang, "Tells the debugger to use the specified scripting language for " "user-defined scripts, rather than the default. " "Valid scripting languages that can be specified include Python, Perl, " "Ruby and Tcl. Currently only the Python " "extensions have been implemented."}, {LLDB_3_TO_5, false, "debug", 'd', no_argument, 0, eArgTypeNone, "Tells the debugger to print out extra information for debugging itself."}, {LLDB_OPT_SET_7, true, "repl", 'r', optional_argument, 0, eArgTypeNone, "Runs lldb in REPL mode with a stub process."}, {LLDB_OPT_SET_7, true, "repl-language", 'R', required_argument, 0, eArgTypeNone, "Chooses the language for the REPL."}, {0, false, NULL, 0, 0, 0, eArgTypeNone, NULL}}; static const uint32_t last_option_set_with_args = 2; Driver::Driver() : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)), m_option_data() { // We want to be able to handle CTRL+D in the terminal to have it terminate // certain input m_debugger.SetCloseInputOnEOF(false); g_driver = this; } Driver::~Driver() { g_driver = NULL; } // This function takes INDENT, which tells how many spaces to output at the // front // of each line; TEXT, which is the text that is to be output. It outputs the // text, on multiple lines if necessary, to RESULT, with INDENT spaces at the // front of each line. It breaks lines on spaces, tabs or newlines, shortening // the line if necessary to not break in the middle of a word. It assumes that // each output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. void OutputFormattedUsageText(FILE *out, int indent, const char *text, int output_max_columns) { int len = strlen(text); std::string text_string(text); // Force indentation to be reasonable. if (indent >= output_max_columns) indent = 0; // Will it all fit on one line? if (len + indent < output_max_columns) // Output as a single line fprintf(out, "%*s%s\n", indent, "", text); else { // We need to break it up into multiple lines. int text_width = output_max_columns - indent - 1; int start = 0; int end = start; int final_end = len; int sub_len; while (end < final_end) { // Dont start the 'text' on a space, since we're already outputting the // indentation. while ((start < final_end) && (text[start] == ' ')) start++; end = start + text_width; if (end > final_end) end = final_end; else { // If we're not at the end of the text, make sure we break the line on // white space. while (end > start && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') end--; } sub_len = end - start; std::string substring = text_string.substr(start, sub_len); fprintf(out, "%*s%s\n", indent, "", substring.c_str()); start = end + 1; } } } void ShowUsage(FILE *out, OptionDefinition *option_table, Driver::OptionData data) { uint32_t screen_width = 80; uint32_t indent_level = 0; const char *name = "lldb"; fprintf(out, "\nUsage:\n\n"); indent_level += 2; // First, show each usage level set of options, e.g. // [options-for-level-0] // // [options-for-level-1] // etc. uint32_t num_options; uint32_t num_option_sets = 0; for (num_options = 0; option_table[num_options].long_option != NULL; ++num_options) { uint32_t this_usage_mask = option_table[num_options].usage_mask; if (this_usage_mask == LLDB_OPT_SET_ALL) { if (num_option_sets == 0) num_option_sets = 1; } else { for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) { if (this_usage_mask & 1 << j) { if (num_option_sets <= j) num_option_sets = j + 1; } } } } for (uint32_t opt_set = 0; opt_set < num_option_sets; opt_set++) { uint32_t opt_set_mask; opt_set_mask = 1 << opt_set; if (opt_set > 0) fprintf(out, "\n"); fprintf(out, "%*s%s", indent_level, "", name); bool is_help_line = false; for (uint32_t i = 0; i < num_options; ++i) { if (option_table[i].usage_mask & opt_set_mask) { CommandArgumentType arg_type = option_table[i].argument_type; const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString(arg_type); // This is a bit of a hack, but there's no way to say certain options // don't have arguments yet... // so we do it by hand here. if (option_table[i].short_option == 'h') is_help_line = true; if (option_table[i].required) { if (option_table[i].option_has_arg == required_argument) fprintf(out, " -%c <%s>", option_table[i].short_option, arg_name); else if (option_table[i].option_has_arg == optional_argument) fprintf(out, " -%c [<%s>]", option_table[i].short_option, arg_name); else fprintf(out, " -%c", option_table[i].short_option); } else { if (option_table[i].option_has_arg == required_argument) fprintf(out, " [-%c <%s>]", option_table[i].short_option, arg_name); else if (option_table[i].option_has_arg == optional_argument) fprintf(out, " [-%c [<%s>]]", option_table[i].short_option, arg_name); else fprintf(out, " [-%c]", option_table[i].short_option); } } } if (!is_help_line && (opt_set <= last_option_set_with_args)) fprintf(out, " [[--] [ ...]]"); } fprintf(out, "\n\n"); // Now print out all the detailed information about the various options: long // form, short form and help text: // -- long_name // - short // help text // This variable is used to keep track of which options' info we've printed // out, because some options can be in // more than one usage level, but we only want to print the long form of its // information once. Driver::OptionData::OptionSet options_seen; Driver::OptionData::OptionSet::iterator pos; indent_level += 5; for (uint32_t i = 0; i < num_options; ++i) { // Only print this option if we haven't already seen it. pos = options_seen.find(option_table[i].short_option); if (pos == options_seen.end()) { CommandArgumentType arg_type = option_table[i].argument_type; const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString(arg_type); options_seen.insert(option_table[i].short_option); fprintf(out, "%*s-%c ", indent_level, "", option_table[i].short_option); if (arg_type != eArgTypeNone) fprintf(out, "<%s>", arg_name); fprintf(out, "\n"); fprintf(out, "%*s--%s ", indent_level, "", option_table[i].long_option); if (arg_type != eArgTypeNone) fprintf(out, "<%s>", arg_name); fprintf(out, "\n"); indent_level += 5; OutputFormattedUsageText(out, indent_level, option_table[i].usage_text, screen_width); indent_level -= 5; fprintf(out, "\n"); } } indent_level -= 5; fprintf(out, "\n%*sNotes:\n", indent_level, ""); indent_level += 5; fprintf(out, "\n%*sMultiple \"-s\" and \"-o\" options can be provided. They will " "be processed" "\n%*sfrom left to right in order, with the source files and commands" "\n%*sinterleaved. The same is true of the \"-S\" and \"-O\" " "options. The before" "\n%*sfile and after file sets can intermixed freely, the command " "parser will" "\n%*ssort them out. The order of the file specifiers (\"-c\", " "\"-f\", etc.) is" "\n%*snot significant in this regard.\n\n", indent_level, "", indent_level, "", indent_level, "", indent_level, "", indent_level, "", indent_level, ""); fprintf( out, "\n%*sIf you don't provide -f then the first argument will be the file " "to be" "\n%*sdebugged which means that '%s -- [ []]' also" "\n%*sworks. But remember to end the options with \"--\" if any of your" "\n%*sarguments have a \"-\" in them.\n\n", indent_level, "", indent_level, "", name, indent_level, "", indent_level, ""); } void BuildGetOptTable(OptionDefinition *expanded_option_table, std::vector &getopt_table, uint32_t num_options) { if (num_options == 0) return; uint32_t i; uint32_t j; std::bitset<256> option_seen; getopt_table.resize(num_options + 1); for (i = 0, j = 0; i < num_options; ++i) { char short_opt = expanded_option_table[i].short_option; if (option_seen.test(short_opt) == false) { getopt_table[j].name = expanded_option_table[i].long_option; getopt_table[j].has_arg = expanded_option_table[i].option_has_arg; getopt_table[j].flag = NULL; getopt_table[j].val = expanded_option_table[i].short_option; option_seen.set(short_opt); ++j; } } getopt_table[j].name = NULL; getopt_table[j].has_arg = 0; getopt_table[j].flag = NULL; getopt_table[j].val = 0; } Driver::OptionData::OptionData() : m_args(), m_script_lang(lldb::eScriptLanguageDefault), m_core_file(), m_crash_log(), m_initial_commands(), m_after_file_commands(), m_after_crash_commands(), m_debug_mode(false), m_source_quietly(false), m_print_version(false), m_print_python_path(false), m_print_help(false), m_wait_for(false), m_repl(false), m_repl_lang(eLanguageTypeUnknown), m_repl_options(), m_process_name(), m_process_pid(LLDB_INVALID_PROCESS_ID), m_use_external_editor(false), m_batch(false), m_seen_options() {} Driver::OptionData::~OptionData() {} void Driver::OptionData::Clear() { m_args.clear(); m_script_lang = lldb::eScriptLanguageDefault; m_initial_commands.clear(); m_after_file_commands.clear(); // If there is a local .lldbinit, add that to the // list of things to be sourced, if the settings // permit it. SBFileSpec local_lldbinit(".lldbinit", true); SBFileSpec homedir_dot_lldb = SBHostOS::GetUserHomeDirectory(); homedir_dot_lldb.AppendPathComponent(".lldbinit"); // Only read .lldbinit in the current working directory // if it's not the same as the .lldbinit in the home // directory (which is already being read in). if (local_lldbinit.Exists() && strcmp(local_lldbinit.GetDirectory(), homedir_dot_lldb.GetDirectory()) != 0) { char path[2048]; local_lldbinit.GetPath(path, 2047); InitialCmdEntry entry(path, true, true, true); m_after_file_commands.push_back(entry); } m_debug_mode = false; m_source_quietly = false; m_print_help = false; m_print_version = false; m_print_python_path = false; m_use_external_editor = false; m_wait_for = false; m_process_name.erase(); m_batch = false; m_after_crash_commands.clear(); m_process_pid = LLDB_INVALID_PROCESS_ID; } void Driver::OptionData::AddInitialCommand(const char *command, CommandPlacement placement, bool is_file, SBError &error) { std::vector *command_set; switch (placement) { case eCommandPlacementBeforeFile: command_set = &(m_initial_commands); break; case eCommandPlacementAfterFile: command_set = &(m_after_file_commands); break; case eCommandPlacementAfterCrash: command_set = &(m_after_crash_commands); break; } if (is_file) { SBFileSpec file(command); if (file.Exists()) command_set->push_back(InitialCmdEntry(command, is_file, false)); else if (file.ResolveExecutableLocation()) { char final_path[PATH_MAX]; file.GetPath(final_path, sizeof(final_path)); command_set->push_back(InitialCmdEntry(final_path, is_file, false)); } else error.SetErrorStringWithFormat( "file specified in --source (-s) option doesn't exist: '%s'", optarg); } else command_set->push_back(InitialCmdEntry(command, is_file, false)); } void Driver::ResetOptionValues() { m_option_data.Clear(); } const char *Driver::GetFilename() const { if (m_option_data.m_args.empty()) return NULL; return m_option_data.m_args.front().c_str(); } const char *Driver::GetCrashLogFilename() const { if (m_option_data.m_crash_log.empty()) return NULL; return m_option_data.m_crash_log.c_str(); } lldb::ScriptLanguage Driver::GetScriptLanguage() const { return m_option_data.m_script_lang; } void Driver::WriteCommandsForSourcing(CommandPlacement placement, SBStream &strm) { std::vector *command_set; switch (placement) { case eCommandPlacementBeforeFile: command_set = &m_option_data.m_initial_commands; break; case eCommandPlacementAfterFile: command_set = &m_option_data.m_after_file_commands; break; case eCommandPlacementAfterCrash: command_set = &m_option_data.m_after_crash_commands; break; } for (const auto &command_entry : *command_set) { const char *command = command_entry.contents.c_str(); if (command_entry.is_file) { // If this command_entry is a file to be sourced, and it's the ./.lldbinit // file (the .lldbinit // file in the current working directory), only read it if // target.load-cwd-lldbinit is 'true'. if (command_entry.is_cwd_lldbinit_file_read) { SBStringList strlist = m_debugger.GetInternalVariableValue( "target.load-cwd-lldbinit", m_debugger.GetInstanceName()); if (strlist.GetSize() == 1 && strcmp(strlist.GetStringAtIndex(0), "warn") == 0) { FILE *output = m_debugger.GetOutputFileHandle(); ::fprintf( output, "There is a .lldbinit file in the current directory which is not " "being read.\n" "To silence this warning without sourcing in the local " ".lldbinit,\n" "add the following to the lldbinit file in your home directory:\n" " settings set target.load-cwd-lldbinit false\n" "To allow lldb to source .lldbinit files in the current working " "directory,\n" "set the value of this variable to true. Only do so if you " "understand and\n" "accept the security risk.\n"); return; } if (strlist.GetSize() == 1 && strcmp(strlist.GetStringAtIndex(0), "false") == 0) { return; } } bool source_quietly = m_option_data.m_source_quietly || command_entry.source_quietly; strm.Printf("command source -s %i '%s'\n", source_quietly, command); } else strm.Printf("%s\n", command); } } bool Driver::GetDebugMode() const { return m_option_data.m_debug_mode; } // Check the arguments that were passed to this program to make sure they are // valid and to get their // argument values (if any). Return a boolean value indicating whether or not // to start up the full // debugger (i.e. the Command Interpreter) or not. Return FALSE if the // arguments were invalid OR // if the user only wanted help or version information. SBError Driver::ParseArgs(int argc, const char *argv[], FILE *out_fh, bool &exiting) { ResetOptionValues(); SBCommandReturnObject result; SBError error; std::string option_string; struct option *long_options = NULL; std::vector long_options_vector; uint32_t num_options; for (num_options = 0; g_options[num_options].long_option != NULL; ++num_options) /* Do Nothing. */; if (num_options == 0) { if (argc > 1) error.SetErrorStringWithFormat("invalid number of options"); return error; } BuildGetOptTable(g_options, long_options_vector, num_options); if (long_options_vector.empty()) long_options = NULL; else long_options = &long_options_vector.front(); if (long_options == NULL) { error.SetErrorStringWithFormat("invalid long options"); return error; } // Build the option_string argument for call to getopt_long_only. for (int i = 0; long_options[i].name != NULL; ++i) { if (long_options[i].flag == NULL) { option_string.push_back((char)long_options[i].val); switch (long_options[i].has_arg) { default: case no_argument: break; case required_argument: option_string.push_back(':'); break; case optional_argument: option_string.append("::"); break; } } } // This is kind of a pain, but since we make the debugger in the Driver's // constructor, we can't // know at that point whether we should read in init files yet. So we don't // read them in in the // Driver constructor, then set the flags back to "read them in" here, and // then if we see the // "-n" flag, we'll turn it off again. Finally we have to read them in by // hand later in the // main loop. m_debugger.SkipLLDBInitFiles(false); m_debugger.SkipAppInitFiles(false); // Prepare for & make calls to getopt_long_only. #if __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif int val; while (1) { int long_options_index = -1; val = ::getopt_long_only(argc, const_cast(argv), option_string.c_str(), long_options, &long_options_index); if (val == -1) break; else if (val == '?') { m_option_data.m_print_help = true; error.SetErrorStringWithFormat("unknown or ambiguous option"); break; } else if (val == 0) continue; else { m_option_data.m_seen_options.insert((char)val); if (long_options_index == -1) { for (int i = 0; long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; ++i) { if (long_options[i].val == val) { long_options_index = i; break; } } } if (long_options_index >= 0) { const int short_option = g_options[long_options_index].short_option; switch (short_option) { case 'h': m_option_data.m_print_help = true; break; case 'v': m_option_data.m_print_version = true; break; case 'P': m_option_data.m_print_python_path = true; break; case 'b': m_option_data.m_batch = true; break; case 'c': { SBFileSpec file(optarg); if (file.Exists()) { m_option_data.m_core_file = optarg; } else error.SetErrorStringWithFormat( "file specified in --core (-c) option doesn't exist: '%s'", optarg); } break; case 'e': m_option_data.m_use_external_editor = true; break; case 'x': m_debugger.SkipLLDBInitFiles(true); m_debugger.SkipAppInitFiles(true); break; case 'X': m_debugger.SetUseColor(false); break; case 'f': { SBFileSpec file(optarg); if (file.Exists()) { m_option_data.m_args.push_back(optarg); } else if (file.ResolveExecutableLocation()) { char path[PATH_MAX]; file.GetPath(path, sizeof(path)); m_option_data.m_args.push_back(path); } else error.SetErrorStringWithFormat( "file specified in --file (-f) option doesn't exist: '%s'", optarg); } break; case 'a': if (!m_debugger.SetDefaultArchitecture(optarg)) error.SetErrorStringWithFormat( "invalid architecture in the -a or --arch option: '%s'", optarg); break; case 'l': m_option_data.m_script_lang = m_debugger.GetScriptingLanguage(optarg); break; case 'd': m_option_data.m_debug_mode = true; break; case 'Q': m_option_data.m_source_quietly = true; break; case 'K': m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, true, error); break; case 'k': m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, false, error); break; case 'n': m_option_data.m_process_name = optarg; break; case 'w': m_option_data.m_wait_for = true; break; case 'p': { char *remainder; m_option_data.m_process_pid = strtol(optarg, &remainder, 0); if (remainder == optarg || *remainder != '\0') error.SetErrorStringWithFormat( "Could not convert process PID: \"%s\" into a pid.", optarg); } break; case 'r': m_option_data.m_repl = true; if (optarg && optarg[0]) m_option_data.m_repl_options = optarg; else m_option_data.m_repl_options.clear(); break; case 'R': m_option_data.m_repl_lang = SBLanguageRuntime::GetLanguageTypeFromString(optarg); if (m_option_data.m_repl_lang == eLanguageTypeUnknown) { error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"", optarg); } break; case 's': m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, true, error); break; case 'o': m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, false, error); break; case 'S': m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, true, error); break; case 'O': m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, false, error); break; default: m_option_data.m_print_help = true; error.SetErrorStringWithFormat("unrecognized option %c", short_option); break; } } else { error.SetErrorStringWithFormat("invalid option with value %i", val); } if (error.Fail()) { return error; } } } if (error.Fail() || m_option_data.m_print_help) { ShowUsage(out_fh, g_options, m_option_data); exiting = true; } else if (m_option_data.m_print_version) { ::fprintf(out_fh, "%s\n", m_debugger.GetVersionString()); exiting = true; } else if (m_option_data.m_print_python_path) { SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath(); if (python_file_spec.IsValid()) { char python_path[PATH_MAX]; size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX); if (num_chars < PATH_MAX) { ::fprintf(out_fh, "%s\n", python_path); } else ::fprintf(out_fh, "\n"); } else ::fprintf(out_fh, "\n"); exiting = true; } else if (m_option_data.m_process_name.empty() && m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) { // Any arguments that are left over after option parsing are for // the program. If a file was specified with -f then the filename // is already in the m_option_data.m_args array, and any remaining args // are arguments for the inferior program. If no file was specified with // -f, then what is left is the program name followed by any arguments. // Skip any options we consumed with getopt_long_only argc -= optind; argv += optind; if (argc > 0) { for (int arg_idx = 0; arg_idx < argc; ++arg_idx) { const char *arg = argv[arg_idx]; if (arg) m_option_data.m_args.push_back(arg); } } } else { // Skip any options we consumed with getopt_long_only argc -= optind; // argv += optind; // Commented out to keep static analyzer happy if (argc > 0) ::fprintf(out_fh, "Warning: program arguments are ignored when attaching.\n"); } return error; } static ::FILE *PrepareCommandsForSourcing(const char *commands_data, size_t commands_size, int fds[2]) { enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE ::FILE *commands_file = NULL; fds[0] = -1; fds[1] = -1; int err = 0; #ifdef _WIN32 err = _pipe(fds, commands_size, O_BINARY); #else err = pipe(fds); #endif if (err == 0) { ssize_t nrwr = write(fds[WRITE], commands_data, commands_size); if (nrwr < 0) { fprintf(stderr, "error: write(%i, %p, %" PRIu64 ") failed (errno = %i) " "when trying to open LLDB commands pipe\n", fds[WRITE], static_cast(commands_data), static_cast(commands_size), errno); } else if (static_cast(nrwr) == commands_size) { // Close the write end of the pipe so when we give the read end to // the debugger/command interpreter it will exit when it consumes all // of the data #ifdef _WIN32 _close(fds[WRITE]); fds[WRITE] = -1; #else close(fds[WRITE]); fds[WRITE] = -1; #endif // Now open the read file descriptor in a FILE * that we can give to // the debugger as an input handle commands_file = fdopen(fds[READ], "r"); if (commands_file) { fds[READ] = -1; // The FILE * 'commands_file' now owns the read descriptor // Hand ownership if the FILE * over to the debugger for // "commands_file". } else { fprintf(stderr, "error: fdopen(%i, \"r\") failed (errno = %i) when " "trying to open LLDB commands pipe\n", fds[READ], errno); } } } else { fprintf(stderr, "error: can't create pipe file descriptors for LLDB commands\n"); } return commands_file; } void CleanupAfterCommandSourcing(int fds[2]) { enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE // Close any pipes that we still have ownership of if (fds[WRITE] != -1) { #ifdef _WIN32 _close(fds[WRITE]); fds[WRITE] = -1; #else close(fds[WRITE]); fds[WRITE] = -1; #endif } if (fds[READ] != -1) { #ifdef _WIN32 _close(fds[READ]); fds[READ] = -1; #else close(fds[READ]); fds[READ] = -1; #endif } } std::string EscapeString(std::string arg) { std::string::size_type pos = 0; while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) { arg.insert(pos, 1, '\\'); pos += 2; } return '"' + arg + '"'; } void Driver::MainLoop() { if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) { g_old_stdin_termios_is_valid = true; atexit(reset_stdin_termios); } #ifndef _MSC_VER // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets // which causes it to miss newlines depending on whether there have been an // odd or even number of characters. Bug has been reported to MS via Connect. ::setbuf(stdin, NULL); #endif ::setbuf(stdout, NULL); m_debugger.SetErrorFileHandle(stderr, false); m_debugger.SetOutputFileHandle(stdout, false); m_debugger.SetInputFileHandle(stdin, false); // Don't take ownership of STDIN yet... m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); struct winsize window_size; if (isatty(STDIN_FILENO) && ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { if (window_size.ws_col > 0) m_debugger.SetTerminalWidth(window_size.ws_col); } SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter(); // Before we handle any options from the command line, we parse the // .lldbinit file in the user's home directory. SBCommandReturnObject result; sb_interpreter.SourceInitFileInHomeDirectory(result); if (GetDebugMode()) { result.PutError(m_debugger.GetErrorFileHandle()); result.PutOutput(m_debugger.GetOutputFileHandle()); } // Now we handle options we got from the command line SBStream commands_stream; // First source in the commands specified to be run before the file arguments // are processed. WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream); const size_t num_args = m_option_data.m_args.size(); if (num_args > 0) { char arch_name[64]; if (m_debugger.GetDefaultArchitecture(arch_name, sizeof(arch_name))) commands_stream.Printf("target create --arch=%s %s", arch_name, EscapeString(m_option_data.m_args[0]).c_str()); else commands_stream.Printf("target create %s", EscapeString(m_option_data.m_args[0]).c_str()); if (!m_option_data.m_core_file.empty()) { commands_stream.Printf(" --core %s", EscapeString(m_option_data.m_core_file).c_str()); } commands_stream.Printf("\n"); if (num_args > 1) { commands_stream.Printf("settings set -- target.run-args "); for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx) commands_stream.Printf( " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str()); commands_stream.Printf("\n"); } } else if (!m_option_data.m_core_file.empty()) { commands_stream.Printf("target create --core %s\n", EscapeString(m_option_data.m_core_file).c_str()); } else if (!m_option_data.m_process_name.empty()) { commands_stream.Printf("process attach --name %s", EscapeString(m_option_data.m_process_name).c_str()); if (m_option_data.m_wait_for) commands_stream.Printf(" --waitfor"); commands_stream.Printf("\n"); } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) { commands_stream.Printf("process attach --pid %" PRIu64 "\n", m_option_data.m_process_pid); } WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream); if (GetDebugMode()) { result.PutError(m_debugger.GetErrorFileHandle()); result.PutOutput(m_debugger.GetOutputFileHandle()); } bool handle_events = true; bool spawn_thread = false; if (m_option_data.m_repl) { const char *repl_options = NULL; if (!m_option_data.m_repl_options.empty()) repl_options = m_option_data.m_repl_options.c_str(); SBError error(m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options)); if (error.Fail()) { const char *error_cstr = error.GetCString(); if (error_cstr && error_cstr[0]) fprintf(stderr, "error: %s\n", error_cstr); else fprintf(stderr, "error: %u\n", error.GetError()); } } else { // Check if we have any data in the commands stream, and if so, save it to a // temp file // so we can then run the command interpreter using the file contents. const char *commands_data = commands_stream.GetData(); const size_t commands_size = commands_stream.GetSize(); // The command file might have requested that we quit, this variable will // track that. bool quit_requested = false; bool stopped_for_crash = false; if (commands_data && commands_size) { int initial_commands_fds[2]; bool success = true; FILE *commands_file = PrepareCommandsForSourcing( commands_data, commands_size, initial_commands_fds); if (commands_file) { m_debugger.SetInputFileHandle(commands_file, true); // Set the debugger into Sync mode when running the command file. // Otherwise command files // that run the target won't run in a sensible way. bool old_async = m_debugger.GetAsync(); m_debugger.SetAsync(false); int num_errors; SBCommandInterpreterRunOptions options; options.SetStopOnError(true); if (m_option_data.m_batch) options.SetStopOnCrash(true); m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options, num_errors, quit_requested, stopped_for_crash); if (m_option_data.m_batch && stopped_for_crash && !m_option_data.m_after_crash_commands.empty()) { int crash_command_fds[2]; SBStream crash_commands_stream; WriteCommandsForSourcing(eCommandPlacementAfterCrash, crash_commands_stream); const char *crash_commands_data = crash_commands_stream.GetData(); const size_t crash_commands_size = crash_commands_stream.GetSize(); commands_file = PrepareCommandsForSourcing( crash_commands_data, crash_commands_size, crash_command_fds); if (commands_file) { bool local_quit_requested; bool local_stopped_for_crash; m_debugger.SetInputFileHandle(commands_file, true); m_debugger.RunCommandInterpreter( handle_events, spawn_thread, options, num_errors, local_quit_requested, local_stopped_for_crash); if (local_quit_requested) quit_requested = true; } } m_debugger.SetAsync(old_async); } else success = false; // Close any pipes that we still have ownership of CleanupAfterCommandSourcing(initial_commands_fds); // Something went wrong with command pipe if (!success) { exit(1); } } // Now set the input file handle to STDIN and run the command // interpreter again in interactive mode and let the debugger // take ownership of stdin bool go_interactive = true; if (quit_requested) go_interactive = false; else if (m_option_data.m_batch && !stopped_for_crash) go_interactive = false; if (go_interactive) { m_debugger.SetInputFileHandle(stdin, true); m_debugger.RunCommandInterpreter(handle_events, spawn_thread); } } reset_stdin_termios(); fclose(stdin); SBDebugger::Destroy(m_debugger); } void Driver::ResizeWindow(unsigned short col) { GetDebugger().SetTerminalWidth(col); } void sigwinch_handler(int signo) { struct winsize window_size; if (isatty(STDIN_FILENO) && ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { if ((window_size.ws_col > 0) && g_driver != NULL) { g_driver->ResizeWindow(window_size.ws_col); } } } void sigint_handler(int signo) { static bool g_interrupt_sent = false; if (g_driver) { if (!g_interrupt_sent) { g_interrupt_sent = true; g_driver->GetDebugger().DispatchInputInterrupt(); g_interrupt_sent = false; return; } } exit(signo); } void sigtstp_handler(int signo) { if (g_driver) g_driver->GetDebugger().SaveInputTerminalState(); signal(signo, SIG_DFL); kill(getpid(), signo); signal(signo, sigtstp_handler); } void sigcont_handler(int signo) { if (g_driver) g_driver->GetDebugger().RestoreInputTerminalState(); signal(signo, SIG_DFL); kill(getpid(), signo); signal(signo, sigcont_handler); } int #ifdef _MSC_VER wmain(int argc, wchar_t const *wargv[]) #else main(int argc, char const *argv[]) #endif { #ifdef _MSC_VER // Convert wide arguments to UTF-8 std::vector argvStrings(argc); std::vector argvPointers(argc); for (int i = 0; i != argc; ++i) { llvm::convertWideToUTF8(wargv[i], argvStrings[i]); argvPointers[i] = argvStrings[i].c_str(); } const char **argv = argvPointers.data(); #endif SBDebugger::Initialize(); SBHostOS::ThreadCreated(""); signal(SIGINT, sigint_handler); #if !defined(_MSC_VER) signal(SIGPIPE, SIG_IGN); signal(SIGWINCH, sigwinch_handler); signal(SIGTSTP, sigtstp_handler); signal(SIGCONT, sigcont_handler); #endif // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called. { Driver driver; bool exiting = false; SBError error(driver.ParseArgs(argc, argv, stdout, exiting)); if (error.Fail()) { const char *error_cstr = error.GetCString(); if (error_cstr) ::fprintf(stderr, "error: %s\n", error_cstr); } else if (!exiting) { driver.MainLoop(); } } SBDebugger::Terminate(); return 0; } Index: vendor/lldb/dist/tools/lldb-mi/MICmnLLDBDebugger.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmnLLDBDebugger.cpp (revision 321193) +++ vendor/lldb/dist/tools/lldb-mi/MICmnLLDBDebugger.cpp (revision 321194) @@ -1,936 +1,937 @@ //===-- MICmnLLDBDebugger.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Third party headers: #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBType.h" #include "lldb/API/SBTypeCategory.h" #include "lldb/API/SBTypeNameSpecifier.h" #include "lldb/API/SBTypeSummary.h" +#include // In-house headers: #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBDebugger.h" #include "MICmnLLDBDebuggerHandleEvents.h" #include "MICmnLog.h" #include "MICmnResources.h" #include "MICmnThreadMgrStd.h" #include "MIDriverBase.h" #include "MIUtilSingletonHelper.h" //++ //------------------------------------------------------------------------------------ // MI private summary providers static inline bool MI_char_summary_provider(lldb::SBValue value, lldb::SBTypeSummaryOptions options, lldb::SBStream &stream) { if (!value.IsValid()) return false; lldb::SBType value_type = value.GetType(); if (!value_type.IsValid()) return false; lldb::BasicType type_code = value_type.GetBasicType(); if (type_code == lldb::eBasicTypeSignedChar) stream.Printf("%d %s", (int)value.GetValueAsSigned(), value.GetValue()); else if (type_code == lldb::eBasicTypeUnsignedChar) stream.Printf("%u %s", (unsigned)value.GetValueAsUnsigned(), value.GetValue()); else return false; return true; } //++ //------------------------------------------------------------------------------------ // MI summary helper routines static inline bool MI_add_summary(lldb::SBTypeCategory category, const char *typeName, lldb::SBTypeSummary::FormatCallback cb, uint32_t options, bool regex = false) { #if defined(LLDB_DISABLE_PYTHON) return false; #else lldb::SBTypeSummary summary = lldb::SBTypeSummary::CreateWithCallback(cb, options); return summary.IsValid() ? category.AddTypeSummary( lldb::SBTypeNameSpecifier(typeName, regex), summary) : false; #endif } //++ //------------------------------------------------------------------------------------ // Details: CMICmnLLDBDebugger constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmnLLDBDebugger::CMICmnLLDBDebugger() : m_constStrThisThreadId("MI debugger event") {} //++ //------------------------------------------------------------------------------------ // Details: CMICmnLLDBDebugger destructor. // Type: Overridable. // Args: None. // Return: None. // Throws: None. //-- CMICmnLLDBDebugger::~CMICmnLLDBDebugger() { Shutdown(); } //++ //------------------------------------------------------------------------------------ // Details: Initialize resources for *this debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::Initialize() { m_clientUsageRefCnt++; if (m_bInitialized) return MIstatus::success; bool bOk = MIstatus::success; CMIUtilString errMsg; ClrErrorDescription(); if (m_pClientDriver == nullptr) { bOk = false; errMsg = MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER); } // Note initialization order is important here as some resources depend on // previous MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); MI::ModuleInit( IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); // Note order is important here! if (bOk) lldb::SBDebugger::Initialize(); if (bOk && !InitSBDebugger()) { bOk = false; if (!errMsg.empty()) errMsg += ", "; errMsg += GetErrorDescription().c_str(); } if (bOk && !InitSBListener()) { bOk = false; if (!errMsg.empty()) errMsg += ", "; errMsg += GetErrorDescription().c_str(); } bOk = bOk && InitStdStreams(); bOk = bOk && RegisterMISummaryProviders(); m_bInitialized = bOk; if (!bOk && !HaveErrorDescription()) { CMIUtilString strInitError(CMIUtilString::Format( MIRSRC(IDS_MI_INIT_ERR_LLDBDEBUGGER), errMsg.c_str())); SetErrorDescription(strInitError); } return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Release resources for *this debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::Shutdown() { if (--m_clientUsageRefCnt > 0) return MIstatus::success; if (!m_bInitialized) return MIstatus::success; m_bInitialized = false; ClrErrorDescription(); bool bOk = MIstatus::success; CMIUtilString errMsg; // Explicitly delete the remote target in case MI needs to exit prematurely // otherwise // LLDB debugger may hang in its Destroy() fn waiting on events lldb::SBTarget sbTarget = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); m_lldbDebugger.DeleteTarget(sbTarget); // Debug: May need this but does seem to work without it so commented out the // fudge 19/06/2014 // It appears we need to wait as hang does not occur when hitting a debug // breakpoint here // const std::chrono::milliseconds time( 1000 ); // std::this_thread::sleep_for( time ); lldb::SBDebugger::Destroy(m_lldbDebugger); lldb::SBDebugger::Terminate(); m_pClientDriver = nullptr; m_mapBroadcastClassNameToEventMask.clear(); m_mapIdToEventMask.clear(); // Note shutdown order is important here MI::ModuleShutdown( IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); MI::ModuleShutdown( IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); if (!bOk) { SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_LLDBDEBUGGER), errMsg.c_str()); } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Return the LLDB debugger instance created for this debug session. // Type: Method. // Args: None. // Return: lldb::SBDebugger & - LLDB debugger object reference. // Throws: None. //-- lldb::SBDebugger &CMICmnLLDBDebugger::GetTheDebugger() { return m_lldbDebugger; } //++ //------------------------------------------------------------------------------------ // Details: Return the LLDB listener instance created for this debug session. // Type: Method. // Args: None. // Return: lldb::SBListener & - LLDB listener object reference. // Throws: None. //-- lldb::SBListener &CMICmnLLDBDebugger::GetTheListener() { return m_lldbListener; } //++ //------------------------------------------------------------------------------------ // Details: Set the client driver that wants to use *this LLDB debugger. Call // this function // prior to Initialize(). // Type: Method. // Args: vClientDriver - (R) A driver. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::SetDriver(const CMIDriverBase &vClientDriver) { m_pClientDriver = const_cast(&vClientDriver); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Get the client driver that is use *this LLDB debugger. // Type: Method. // Args: vClientDriver - (R) A driver. // Return: CMIDriverBase & - A driver instance. // Throws: None. //-- CMIDriverBase &CMICmnLLDBDebugger::GetDriver() const { return *m_pClientDriver; } //++ //------------------------------------------------------------------------------------ // Details: Wait until all events have been handled. // This function works in pair with // CMICmnLLDBDebugger::MonitorSBListenerEvents // that handles events from queue. When all events were handled and // queue is // empty the MonitorSBListenerEvents notifies this function that it's // ready to // go on. To synchronize them the m_mutexEventQueue and // m_conditionEventQueueEmpty are used. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMICmnLLDBDebugger::WaitForHandleEvent() { std::unique_lock lock(m_mutexEventQueue); lldb::SBEvent event; if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event)) m_conditionEventQueueEmpty.wait(lock); } //++ //------------------------------------------------------------------------------------ // Details: Check if need to rebroadcast stop event. This function will return // true if // debugger is in synchronouse mode. In such case the // CMICmnLLDBDebugger::RebroadcastStopEvent should be called to // rebroadcast // a new stop event (if any). // Type: Method. // Args: None. // Return: bool - True = Need to rebroadcast stop event, false = otherwise. // Throws: None. //-- bool CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() { CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); if (!rSessionInfo.GetDebugger().GetAsync()) { const bool include_expression_stops = false; m_nLastStopId = CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetStopID( include_expression_stops); return true; } return false; } //++ //------------------------------------------------------------------------------------ // Details: Rebroadcast stop event if needed. This function should be called // only if the // CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() returned // true. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMICmnLLDBDebugger::RebroadcastStopEvent() { lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); const bool include_expression_stops = false; const uint32_t nStopId = process.GetStopID(include_expression_stops); if (m_nLastStopId != nStopId) { lldb::SBEvent event = process.GetStopEventForStopID(nStopId); process.GetBroadcaster().BroadcastEvent(event); } } //++ //------------------------------------------------------------------------------------ // Details: Initialize the LLDB Debugger object. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitSBDebugger() { m_lldbDebugger = lldb::SBDebugger::Create(false); if (!m_lldbDebugger.IsValid()) { SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER)); return MIstatus::failure; } m_lldbDebugger.GetCommandInterpreter().SetPromptOnQuit(false); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Set the LLDB Debugger's std in, err and out streams. (Not // implemented left // here for reference. Was called in the // CMICmnLLDBDebugger::Initialize() ) // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitStdStreams() { // This is not required when operating the MI driver's code as it has its own // streams. Setting the Stdin for the lldbDebugger especially on LINUX will // cause // another thread to run and partially consume stdin data meant for MI stdin // handler // m_lldbDebugger.SetErrorFileHandle( m_pClientDriver->GetStderr(), false ); // m_lldbDebugger.SetOutputFileHandle( m_pClientDriver->GetStdout(), false ); // m_lldbDebugger.SetInputFileHandle( m_pClientDriver->GetStdin(), false ); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Set up the events from the SBDebugger's we would like to listen to. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::InitSBListener() { m_lldbListener = m_lldbDebugger.GetListener(); if (!m_lldbListener.IsValid()) { SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER)); return MIstatus::failure; } const CMIUtilString strDbgId("CMICmnLLDBDebugger1"); MIuint eventMask = lldb::SBTarget::eBroadcastBitBreakpointChanged | lldb::SBTarget::eBroadcastBitModulesLoaded | lldb::SBTarget::eBroadcastBitModulesUnloaded | lldb::SBTarget::eBroadcastBitWatchpointChanged | lldb::SBTarget::eBroadcastBitSymbolsLoaded; bool bOk = RegisterForEvent( strDbgId, CMIUtilString(lldb::SBTarget::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBThread::eBroadcastBitStackChanged; bOk = bOk && RegisterForEvent( strDbgId, CMIUtilString(lldb::SBThread::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBProcess::eBroadcastBitStateChanged | lldb::SBProcess::eBroadcastBitInterrupt | lldb::SBProcess::eBroadcastBitSTDOUT | lldb::SBProcess::eBroadcastBitSTDERR | lldb::SBProcess::eBroadcastBitProfileData | lldb::SBProcess::eBroadcastBitStructuredData; bOk = bOk && RegisterForEvent( strDbgId, CMIUtilString(lldb::SBProcess::GetBroadcasterClassName()), eventMask); eventMask = lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived | lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit | lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData | lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData; bOk = bOk && RegisterForEvent( strDbgId, m_lldbDebugger.GetCommandInterpreter().GetBroadcaster(), eventMask); return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Register with the debugger, the SBListener, the type of events you // are interested // in. Others, like commands, may have already set the mask. // Type: Method. // Args: vClientName - (R) ID of the client who wants these events // set. // vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::RegisterForEvent( const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { MIuint existingMask = 0; if (!BroadcasterGetMask(vBroadcasterClass, existingMask)) return MIstatus::failure; if (!ClientSaveMask(vClientName, vBroadcasterClass, vEventMask)) return MIstatus::failure; const char *pBroadCasterName = vBroadcasterClass.c_str(); MIuint eventMask = vEventMask; eventMask += existingMask; const MIuint result = m_lldbListener.StartListeningForEventClass( m_lldbDebugger, pBroadCasterName, eventMask); if (result == 0) { SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadCasterName)); return MIstatus::failure; } return BroadcasterSaveMask(vBroadcasterClass, eventMask); } //++ //------------------------------------------------------------------------------------ // Details: Register with the debugger, the SBListener, the type of events you // are interested // in. Others, like commands, may have already set the mask. // Type: Method. // Args: vClientName - (R) ID of the client who wants these events set. // vBroadcaster - (R) An SBBroadcaster's derived class. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::RegisterForEvent( const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster, const MIuint vEventMask) { const char *pBroadcasterName = vBroadcaster.GetName(); if (pBroadcasterName == nullptr) { SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), MIRSRC(IDS_WORD_INVALIDNULLPTR))); return MIstatus::failure; } CMIUtilString broadcasterName(pBroadcasterName); if (broadcasterName.length() == 0) { SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), MIRSRC(IDS_WORD_INVALIDEMPTY))); return MIstatus::failure; } MIuint existingMask = 0; if (!BroadcasterGetMask(broadcasterName, existingMask)) return MIstatus::failure; if (!ClientSaveMask(vClientName, broadcasterName, vEventMask)) return MIstatus::failure; MIuint eventMask = vEventMask; eventMask += existingMask; const MIuint result = m_lldbListener.StartListeningForEvents(vBroadcaster, eventMask); if (result == 0) { SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadcasterName)); return MIstatus::failure; } return BroadcasterSaveMask(broadcasterName, eventMask); } //++ //------------------------------------------------------------------------------------ // Details: Unregister with the debugger, the SBListener, the type of events you // are no // longer interested in. Others, like commands, may still remain // interested so // an event may not necessarily be stopped. // Type: Method. // Args: vClientName - (R) ID of the client who no longer requires // these events. // vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::UnregisterForEvent( const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { MIuint clientsEventMask = 0; if (!ClientGetTheirMask(vClientName, vBroadcasterClass, clientsEventMask)) return MIstatus::failure; if (!ClientRemoveTheirMask(vClientName, vBroadcasterClass)) return MIstatus::failure; const MIuint otherClientsEventMask = ClientGetMaskForAllClients(vBroadcasterClass); MIuint newEventMask = 0; for (MIuint i = 0; i < 32; i++) { const MIuint bit = 1 << i; const MIuint clientBit = bit & clientsEventMask; const MIuint othersBit = bit & otherClientsEventMask; if ((clientBit != 0) && (othersBit == 0)) { newEventMask += clientBit; } } const char *pBroadCasterName = vBroadcasterClass.c_str(); if (!m_lldbListener.StopListeningForEventClass( m_lldbDebugger, pBroadCasterName, newEventMask)) { SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STOPLISTENER), vClientName.c_str(), pBroadCasterName)); return MIstatus::failure; } return BroadcasterSaveMask(vBroadcasterClass, otherClientsEventMask); } //++ //------------------------------------------------------------------------------------ // Details: Given the SBBroadcaster class name retrieve it's current event mask. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (W) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterGetMask( const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) const { vwEventMask = 0; if (vBroadcasterClass.empty()) { SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), vBroadcasterClass.c_str())); return MIstatus::failure; } const MapBroadcastClassNameToEventMask_t::const_iterator it = m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); if (it != m_mapBroadcastClassNameToEventMask.end()) { vwEventMask = (*it).second; } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Remove the event mask for the specified SBBroadcaster class name. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterRemoveMask( const CMIUtilString &vBroadcasterClass) { MapBroadcastClassNameToEventMask_t::const_iterator it = m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); if (it != m_mapBroadcastClassNameToEventMask.end()) { m_mapBroadcastClassNameToEventMask.erase(it); } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Given the SBBroadcaster class name save it's current event mask. // Type: Method. // Args: vBroadcasterClass - (R) The SBBroadcaster's class name. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::BroadcasterSaveMask( const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { if (vBroadcasterClass.empty()) { SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), vBroadcasterClass.c_str())); return MIstatus::failure; } BroadcasterRemoveMask(vBroadcasterClass); MapPairBroadcastClassNameToEventMask_t pr(vBroadcasterClass, vEventMask); m_mapBroadcastClassNameToEventMask.insert(pr); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Iterate all the clients who have registered event masks against // particular // SBBroadcasters and build up the mask that is for all of them. // Type: Method. // Args: vBroadcasterClass - (R) The broadcaster to retrieve the mask for. // Return: MIuint - Event mask. // Throws: None. //-- MIuint CMICmnLLDBDebugger::ClientGetMaskForAllClients( const CMIUtilString &vBroadcasterClass) const { MIuint mask = 0; MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.begin(); while (it != m_mapIdToEventMask.end()) { const CMIUtilString &rId((*it).first); if (rId.find(vBroadcasterClass) != std::string::npos) { const MIuint clientsMask = (*it).second; mask |= clientsMask; } // Next ++it; } return mask; } //++ //------------------------------------------------------------------------------------ // Details: Given the client save its particular event requirements. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name targeted for // the events. // vEventMask - (R) The mask of events to listen for. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientSaveMask(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } CMIUtilString strId(vBroadcasterClass); strId += vClientName; ClientRemoveTheirMask(vClientName, vBroadcasterClass); MapPairIdToEventMask_t pr(strId, vEventMask); m_mapIdToEventMask.insert(pr); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Given the client remove it's particular event requirements. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientRemoveTheirMask( const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } CMIUtilString strId(vBroadcasterClass); strId += vClientName; const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); if (it != m_mapIdToEventMask.end()) { m_mapIdToEventMask.erase(it); } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the client's event mask used for on a particular // SBBroadcaster. // Type: Method. // Args: vClientName - (R) The Client's unique ID. // vBroadcasterClass - (R) The SBBroadcaster's class name. // vwEventMask - (W) The client's mask. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ClientGetTheirMask( const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) { vwEventMask = 0; if (vClientName.empty()) { SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); return MIstatus::failure; } const CMIUtilString strId(vBroadcasterClass + vClientName); const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); if (it != m_mapIdToEventMask.end()) { vwEventMask = (*it).second; } SetErrorDescription(CMIUtilString::Format( MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED), vClientName.c_str())); return MIstatus::failure; } //++ //------------------------------------------------------------------------------------ // Details: Momentarily wait for an events being broadcast and inspect those // that do // come this way. Check if the target should exit event if so start // shutting // down this thread and the application. Any other events pass on to // the // Out-of-band handler to further determine what kind of event arrived. // This function runs in the thread "MI debugger event". // Type: Method. // Args: vrbIsAlive - (W) False = yes exit event monitoring thread, true = // continue. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmnLLDBDebugger::MonitorSBListenerEvents(bool &vrbIsAlive) { vrbIsAlive = true; // Lock the mutex of event queue // Note that it should be locked while we are in // CMICmnLLDBDebugger::MonitorSBListenerEvents to // avoid a race condition with CMICmnLLDBDebugger::WaitForHandleEvent std::unique_lock lock(m_mutexEventQueue); lldb::SBEvent event; const bool bGotEvent = m_lldbListener.GetNextEvent(event); if (!bGotEvent) { // Notify that we are finished and unlock the mutex of event queue before // sleeping m_conditionEventQueueEmpty.notify_one(); lock.unlock(); // Wait a bit to reduce CPU load const std::chrono::milliseconds time(1); std::this_thread::sleep_for(time); return MIstatus::success; } assert(event.IsValid()); assert(event.GetBroadcaster().IsValid()); // Debugging m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s", event.GetBroadcasterClass())); bool bHandledEvent = false; bool bOk = false; { // Lock Mutex before handling events so that we don't disturb a running cmd CMIUtilThreadLock lock( CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); bOk = CMICmnLLDBDebuggerHandleEvents::Instance().HandleEvent(event, bHandledEvent); } if (!bHandledEvent) { const CMIUtilString msg( CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT), event.GetBroadcasterClass())); m_pLog->WriteLog(msg); } if (!bOk) m_pLog->WriteLog( CMICmnLLDBDebuggerHandleEvents::Instance().GetErrorDescription()); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The main worker method for this thread. // Type: Method. // Args: vrbIsAlive - (W) True = *this thread is working, false = thread has // exited. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ThreadRun(bool &vrbIsAlive) { return MonitorSBListenerEvents(vrbIsAlive); } //++ //------------------------------------------------------------------------------------ // Details: Let this thread clean up after itself. // Type: Method. // Args: // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::ThreadFinish() { return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve *this thread object's name. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Text. // Throws: None. //-- const CMIUtilString &CMICmnLLDBDebugger::ThreadGetName() const { return m_constStrThisThreadId; } //++ //------------------------------------------------------------------------------------ // Details: Loads lldb-mi formatters // Type: Method. // Args: None. // Return: true - Functionality succeeded. // false - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::LoadMIFormatters(lldb::SBTypeCategory miCategory) { if (!MI_add_summary(miCategory, "char", MI_char_summary_provider, lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) return false; if (!MI_add_summary(miCategory, "unsigned char", MI_char_summary_provider, lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) return false; if (!MI_add_summary(miCategory, "signed char", MI_char_summary_provider, lldb::eTypeOptionHideValue | lldb::eTypeOptionSkipPointers)) return false; return true; } //++ //------------------------------------------------------------------------------------ // Details: Registers lldb-mi custom summary providers // Type: Method. // Args: None. // Return: true - Functionality succeeded. // false - Functionality failed. // Throws: None. //-- bool CMICmnLLDBDebugger::RegisterMISummaryProviders() { static const char *miCategoryName = "lldb-mi"; lldb::SBTypeCategory miCategory = m_lldbDebugger.CreateCategory(miCategoryName); if (!miCategory.IsValid()) return false; if (!LoadMIFormatters(miCategory)) { m_lldbDebugger.DeleteCategory(miCategoryName); return false; } miCategory.SetEnabled(true); return true; } Index: vendor/lldb/dist/tools/lldb-mi/MIDriver.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MIDriver.cpp (revision 321193) +++ vendor/lldb/dist/tools/lldb-mi/MIDriver.cpp (revision 321194) @@ -1,1364 +1,1366 @@ //===-- MIDriver.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Third party headers: #include "lldb/API/SBError.h" +#include +#include #include // In-house headers: #include "MICmdArgValFile.h" #include "MICmdArgValString.h" #include "MICmdMgr.h" #include "MICmnConfig.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBDebugger.h" #include "MICmnLog.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" #include "MICmnResources.h" #include "MICmnStreamStderr.h" #include "MICmnStreamStdout.h" #include "MICmnThreadMgrStd.h" #include "MIDriver.h" #include "MIUtilDebug.h" #include "MIUtilSingletonHelper.h" // Instantiations: #if _DEBUG const CMIUtilString CMIDriver::ms_constMIVersion = MIRSRC(IDS_MI_VERSION_DESCRIPTION_DEBUG); #else const CMIUtilString CMIDriver::ms_constMIVersion = MIRSRC(IDS_MI_VERSION_DESCRIPTION); // Matches version in resources file #endif // _DEBUG const CMIUtilString CMIDriver::ms_constAppNameShort(MIRSRC(IDS_MI_APPNAME_SHORT)); const CMIUtilString CMIDriver::ms_constAppNameLong(MIRSRC(IDS_MI_APPNAME_LONG)); //++ //------------------------------------------------------------------------------------ // Details: CMIDriver constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMIDriver::CMIDriver() : m_bFallThruToOtherDriverEnabled(false), m_bDriverIsExiting(false), m_handleMainThread(0), m_rStdin(CMICmnStreamStdin::Instance()), m_rLldbDebugger(CMICmnLLDBDebugger::Instance()), m_rStdOut(CMICmnStreamStdout::Instance()), m_eCurrentDriverState(eDriverState_NotRunning), m_bHaveExecutableFileNamePathOnCmdLine(false), m_bDriverDebuggingArgExecutable(false), m_bHaveCommandFileNamePathOnCmdLine(false) {} //++ //------------------------------------------------------------------------------------ // Details: CMIDriver destructor. // Type: Overridden. // Args: None. // Return: None. // Throws: None. //-- CMIDriver::~CMIDriver() {} //++ //------------------------------------------------------------------------------------ // Details: Set whether *this driver (the parent) is enabled to pass a command // to its // fall through (child) driver to interpret the command and do work // instead // (if *this driver decides it can't handle the command). // Type: Method. // Args: vbYes - (R) True = yes fall through, false = do not pass on // command. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::SetEnableFallThru(const bool vbYes) { m_bFallThruToOtherDriverEnabled = vbYes; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Get whether *this driver (the parent) is enabled to pass a command // to its // fall through (child) driver to interpret the command and do work // instead // (if *this driver decides it can't handle the command). // Type: Method. // Args: None. // Return: bool - True = yes fall through, false = do not pass on command. // Throws: None. //-- bool CMIDriver::GetEnableFallThru() const { return m_bFallThruToOtherDriverEnabled; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve MI's application name of itself. // Type: Method. // Args: None. // Return: CMIUtilString & - Text description. // Throws: None. //-- const CMIUtilString &CMIDriver::GetAppNameShort() const { return ms_constAppNameShort; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve MI's application name of itself. // Type: Method. // Args: None. // Return: CMIUtilString & - Text description. // Throws: None. //-- const CMIUtilString &CMIDriver::GetAppNameLong() const { return ms_constAppNameLong; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve MI's version description of itself. // Type: Method. // Args: None. // Return: CMIUtilString & - Text description. // Throws: None. //-- const CMIUtilString &CMIDriver::GetVersionDescription() const { return ms_constMIVersion; } //++ //------------------------------------------------------------------------------------ // Details: Initialize setup *this driver ready for use. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::Initialize() { m_eCurrentDriverState = eDriverState_Initialising; m_clientUsageRefCnt++; ClrErrorDescription(); if (m_bInitialized) return MIstatus::success; bool bOk = MIstatus::success; CMIUtilString errMsg; // Initialize all of the modules we depend on MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_THREADMANAGER, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg); MI::ModuleInit(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg); bOk &= m_rLldbDebugger.SetDriver(*this); MI::ModuleInit(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg); m_bExitApp = false; m_bInitialized = bOk; if (!bOk) { const CMIUtilString msg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_DRIVER), errMsg.c_str()); SetErrorDescription(msg); return MIstatus::failure; } m_eCurrentDriverState = eDriverState_RunningNotDebugging; return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Unbind detach or release resources used by *this driver. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::Shutdown() { if (--m_clientUsageRefCnt > 0) return MIstatus::success; if (!m_bInitialized) return MIstatus::success; m_eCurrentDriverState = eDriverState_ShuttingDown; ClrErrorDescription(); bool bOk = MIstatus::success; CMIUtilString errMsg; // Shutdown all of the modules we depend on MI::ModuleShutdown(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_THREADMANAGER, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg); MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); if (!bOk) { SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str()); } m_eCurrentDriverState = eDriverState_NotRunning; return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Work function. Client (the driver's user) is able to append their // own message // in to the MI's Log trace file. // Type: Method. // Args: vMessage - (R) Client's text message. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::WriteMessageToLog(const CMIUtilString &vMessage) { CMIUtilString msg; msg = CMIUtilString::Format(MIRSRC(IDS_MI_CLIENT_MSG), vMessage.c_str()); return m_pLog->Write(msg, CMICmnLog::eLogVerbosity_ClientMsg); } //++ //------------------------------------------------------------------------------------ // Details: CDriverMgr calls *this driver initialize setup ready for use. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::DoInitialize() { return CMIDriver::Instance().Initialize(); } //++ //------------------------------------------------------------------------------------ // Details: CDriverMgr calls *this driver to unbind detach or release resources // used by // *this driver. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::DoShutdown() { return CMIDriver::Instance().Shutdown(); } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the name for *this driver. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Driver name. // Throws: None. //-- const CMIUtilString &CMIDriver::GetName() const { const CMIUtilString &rName = GetAppNameLong(); const CMIUtilString &rVsn = GetVersionDescription(); static CMIUtilString strName = CMIUtilString::Format("%s %s", rName.c_str(), rVsn.c_str()); return strName; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve *this driver's last error condition. // Type: Overridden. // Args: None. // Return: CMIUtilString - Text description. // Throws: None. //-- CMIUtilString CMIDriver::GetError() const { return GetErrorDescription(); } //++ //------------------------------------------------------------------------------------ // Details: Call *this driver to return it's debugger. // Type: Overridden. // Args: None. // Return: lldb::SBDebugger & - LLDB debugger object reference. // Throws: None. //-- lldb::SBDebugger &CMIDriver::GetTheDebugger() { return m_rLldbDebugger.GetTheDebugger(); } //++ //------------------------------------------------------------------------------------ // Details: Specify another driver *this driver can call should this driver not // be able // to handle the client data input. DoFallThruToAnotherDriver() makes // the call. // Type: Overridden. // Args: vrOtherDriver - (R) Reference to another driver object. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver) { m_pDriverFallThru = const_cast(&vrOtherDriver); return m_pDriverFallThru->SetDriverParent(*this); } //++ //------------------------------------------------------------------------------------ // Details: Proxy function CMIDriverMgr IDriver interface implementation. *this // driver's // implementation called from here to match the existing function name // of the // original LLDB driver class (the extra indirection is not necessarily // required). // Check the arguments that were passed to this program to make sure // they are // valid and to get their argument values (if any). // Type: Overridden. // Args: argc - (R) An integer that contains the count of arguments // that follow in // argv. The argc parameter is always greater than // or equal to 1. // argv - (R) An array of null-terminated strings representing // command-line // arguments entered by the user of the program. By // convention, // argv[0] is the command with which the program is // invoked. // vpStdOut - (R) Pointer to a standard output stream. // vwbExiting - (W) True = *this want to exit, Reasons: help, // invalid arg(s), // version information only. // False = Continue to work, start debugger i.e. // Command // interpreter. // Return: lldb::SBError - LLDB current error status. // Throws: None. //-- lldb::SBError CMIDriver::DoParseArgs(const int argc, const char *argv[], FILE *vpStdOut, bool &vwbExiting) { return ParseArgs(argc, argv, vpStdOut, vwbExiting); } //++ //------------------------------------------------------------------------------------ // Details: Check the arguments that were passed to this program to make sure // they are // valid and to get their argument values (if any). The following are // options // that are only handled by *this driver: // --executable // --source or -s // The application's options --interpreter and --executable in code act // very similar. // The --executable is necessary to differentiate whether the MI Driver // is being // used by a client (e.g. Eclipse) or from the command line. Eclipse // issues the option // --interpreter and also passes additional arguments which can be // interpreted as an // executable if called from the command line. Using --executable tells // the MI Driver // it is being called from the command line and to prepare to launch // the executable // argument for a debug session. Using --interpreter on the command // line does not // issue additional commands to initialise a debug session. // Type: Overridden. // Args: argc - (R) An integer that contains the count of arguments // that follow in // argv. The argc parameter is always greater than // or equal to 1. // argv - (R) An array of null-terminated strings representing // command-line // arguments entered by the user of the program. By // convention, // argv[0] is the command with which the program is // invoked. // vpStdOut - (R) Pointer to a standard output stream. // vwbExiting - (W) True = *this want to exit, Reasons: help, // invalid arg(s), // version information only. // False = Continue to work, start debugger i.e. // Command // interpreter. // Return: lldb::SBError - LLDB current error status. // Throws: None. //-- lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[], FILE *vpStdOut, bool &vwbExiting) { lldb::SBError errStatus; const bool bHaveArgs(argc >= 2); // *** Add any args handled here to GetHelpOnCmdLineArgOptions() *** // CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING // Look for the command line options bool bHaveExecutableFileNamePath = false; bool bHaveExecutableLongOption = false; if (bHaveArgs) { // Search right to left to look for filenames for (MIint i = argc - 1; i > 0; i--) { const CMIUtilString strArg(argv[i]); const CMICmdArgValFile argFile; // Check for a filename if (argFile.IsFilePath(strArg) || CMICmdArgValString(true, false, true).IsStringArg(strArg)) { // Is this the command file for the '-s' or '--source' options? const CMIUtilString strPrevArg(argv[i - 1]); if (strPrevArg.compare("-s") == 0 || strPrevArg.compare("--source") == 0) { m_strCmdLineArgCommandFileNamePath = strArg; m_bHaveCommandFileNamePathOnCmdLine = true; i--; // skip '-s' on the next loop continue; } // Else, must be the executable bHaveExecutableFileNamePath = true; m_strCmdLineArgExecuteableFileNamePath = strArg; m_bHaveExecutableFileNamePathOnCmdLine = true; } // Report error if no command file was specified for the '-s' or // '--source' options else if (strArg.compare("-s") == 0 || strArg.compare("--source") == 0) { vwbExiting = true; const CMIUtilString errMsg = CMIUtilString::Format( MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str()); errStatus.SetErrorString(errMsg.c_str()); break; } // This argument is also checked for in CMIDriverMgr::ParseArgs() else if (strArg.compare("--executable") == 0) // Used to specify that // there is executable // argument also on the // command line { // See fn description. bHaveExecutableLongOption = true; } } } if (bHaveExecutableFileNamePath && bHaveExecutableLongOption) { SetDriverDebuggingArgExecutable(); } return errStatus; } //++ //------------------------------------------------------------------------------------ // Details: A client can ask if *this driver is GDB/MI compatible. // Type: Overridden. // Args: None. // Return: True - GBD/MI compatible LLDB front end. // False - Not GBD/MI compatible LLDB front end. // Throws: None. //-- bool CMIDriver::GetDriverIsGDBMICompatibleDriver() const { return true; } //++ //------------------------------------------------------------------------------------ // Details: Start worker threads for the driver. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::StartWorkerThreads() { bool bOk = MIstatus::success; // Grab the thread manager CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance(); // Start the event polling thread if (bOk && !rThreadMgr.ThreadStart(m_rLldbDebugger)) { const CMIUtilString errMsg = CMIUtilString::Format( MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE), CMICmnThreadMgrStd::Instance().GetErrorDescription().c_str()); SetErrorDescription(errMsg); return MIstatus::failure; } return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Stop worker threads for the driver. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::StopWorkerThreads() { CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance(); return rThreadMgr.ThreadAllTerminate(); } //++ //------------------------------------------------------------------------------------ // Details: Call this function puts *this driver to work. // This function is used by the application's main thread. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::DoMainLoop() { if (!InitClientIDEToMIDriver()) // Init Eclipse IDE { SetErrorDescriptionn(MIRSRC(IDS_MI_INIT_ERR_CLIENT_USING_DRIVER)); return MIstatus::failure; } if (!StartWorkerThreads()) return MIstatus::failure; bool bOk = MIstatus::success; if (HaveExecutableFileNamePathOnCmdLine()) { if (!LocalDebugSessionStartupExecuteCommands()) { SetErrorDescription(MIRSRC(IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION)); bOk = MIstatus::failure; } } // App is not quitting currently m_bExitApp = false; // Handle source file if (m_bHaveCommandFileNamePathOnCmdLine) { const bool bAsyncMode = false; ExecuteCommandFile(bAsyncMode); } // While the app is active while (bOk && !m_bExitApp) { CMIUtilString errorText; const char *pCmd = m_rStdin.ReadLine(errorText); if (pCmd != nullptr) { CMIUtilString lineText(pCmd); if (!lineText.empty()) { // Check that the handler thread is alive (otherwise we stuck here) assert(CMICmnLLDBDebugger::Instance().ThreadIsActive()); { // Lock Mutex before processing commands so that we don't disturb an // event // being processed CMIUtilThreadLock lock( CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); bOk = InterpretCommand(lineText); } // Draw prompt if desired bOk = bOk && CMICmnStreamStdout::WritePrompt(); // Wait while the handler thread handles incoming events CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); } } } // Signal that the application is shutting down DoAppQuit(); // Close and wait for the workers to stop StopWorkerThreads(); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Set things in motion, set state etc that brings *this driver (and // the // application) to a tidy shutdown. // This function is used by the application's main thread. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::DoAppQuit() { bool bYesQuit = true; // Shutdown stuff, ready app for exit { CMIUtilThreadLock lock(m_threadMutex); m_bDriverIsExiting = true; } return bYesQuit; } //++ //------------------------------------------------------------------------------------ // Details: *this driver passes text commands to a fall through driver is it // does not // understand them (the LLDB driver). // This function is used by the application's main thread. // Type: Method. // Args: vTextLine - (R) Text data representing a possible command. // vwbCmdYesValid - (W) True = Command valid, false = command not // handled. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::InterpretCommandFallThruDriver(const CMIUtilString &vTextLine, bool &vwbCmdYesValid) { MIunused(vTextLine); MIunused(vwbCmdYesValid); // ToDo: Implement when less urgent work to be done or decide remove as not // required // bool bOk = MIstatus::success; // bool bCmdNotUnderstood = true; // if( bCmdNotUnderstood && GetEnableFallThru() ) //{ // CMIUtilString errMsg; // bOk = DoFallThruToAnotherDriver( vStdInBuffer, errMsg ); // if( !bOk ) // { // errMsg = errMsg.StripCREndOfLine(); // errMsg = errMsg.StripCRAll(); // const CMIDriverBase * pOtherDriver = GetDriverToFallThruTo(); // const char * pName = pOtherDriver->GetDriverName().c_str(); // const char * pId = pOtherDriver->GetDriverId().c_str(); // const CMIUtilString msg( CMIUtilString::Format( MIRSRC( // IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR ), pName, pId, errMsg.c_str() ) //); // m_pLog->WriteMsg( msg ); // } //} // // vwbCmdYesValid = bOk; // CMIUtilString strNot; // if( vwbCmdYesValid) // strNot = CMIUtilString::Format( "%s ", MIRSRC( IDS_WORD_NOT ) ); // const CMIUtilString msg( CMIUtilString::Format( MIRSRC( // IDS_FALLTHRU_DRIVER_CMD_RECEIVED ), vTextLine.c_str(), strNot.c_str() ) ); // m_pLog->WriteLog( msg ); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the name for *this driver. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Driver name. // Throws: None. //-- const CMIUtilString &CMIDriver::GetDriverName() const { return GetName(); } //++ //------------------------------------------------------------------------------------ // Details: Get the unique ID for *this driver. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Text description. // Throws: None. //-- const CMIUtilString &CMIDriver::GetDriverId() const { return GetId(); } //++ //------------------------------------------------------------------------------------ // Details: This function allows *this driver to call on another driver to // perform work // should this driver not be able to handle the client data input. // SetDriverToFallThruTo() specifies the fall through to driver. // Check the error message if the function returns a failure. // Type: Overridden. // Args: vCmd - (R) Command instruction to interpret. // vwErrMsg - (W) Status description on command failing. // Return: MIstatus::success - Command succeeded. // MIstatus::failure - Command failed. // Throws: None. //-- bool CMIDriver::DoFallThruToAnotherDriver(const CMIUtilString &vCmd, CMIUtilString &vwErrMsg) { bool bOk = MIstatus::success; CMIDriverBase *pOtherDriver = GetDriverToFallThruTo(); if (pOtherDriver == nullptr) return bOk; return pOtherDriver->DoFallThruToAnotherDriver(vCmd, vwErrMsg); } //++ //------------------------------------------------------------------------------------ // Details: *this driver provides a file stream to other drivers on which *this // driver // write's out to and they read as expected input. *this driver is // passing // through commands to the (child) pass through assigned driver. // Type: Overrdidden. // Args: None. // Return: FILE * - Pointer to stream. // Throws: None. //-- FILE *CMIDriver::GetStdin() const { // Note this fn is called on CMIDriverMgr register driver so stream has to be // available before *this driver has been initialized! Flaw? // This very likely to change later to a stream that the pass thru driver // will read and we write to give it 'input' return stdin; } //++ //------------------------------------------------------------------------------------ // Details: *this driver provides a file stream to other pass through assigned // drivers // so they know what to write to. // Type: Overidden. // Args: None. // Return: FILE * - Pointer to stream. // Throws: None. //-- FILE *CMIDriver::GetStdout() const { // Note this fn is called on CMIDriverMgr register driver so stream has to be // available before *this driver has been initialized! Flaw? // Do not want to pass through driver to write to stdout return NULL; } //++ //------------------------------------------------------------------------------------ // Details: *this driver provides a error file stream to other pass through // assigned drivers // so they know what to write to. // Type: Overidden. // Args: None. // Return: FILE * - Pointer to stream. // Throws: None. //-- FILE *CMIDriver::GetStderr() const { // Note this fn is called on CMIDriverMgr register driver so stream has to be // available before *this driver has been initialized! Flaw? // This very likely to change later to a stream that the pass thru driver // will write to and *this driver reads from to pass on the CMICmnLog object return stderr; } //++ //------------------------------------------------------------------------------------ // Details: Set a unique ID for *this driver. It cannot be empty. // Type: Overridden. // Args: vId - (R) Text description. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::SetId(const CMIUtilString &vId) { if (vId.empty()) { SetErrorDescriptionn(MIRSRC(IDS_DRIVER_ERR_ID_INVALID), GetName().c_str(), vId.c_str()); return MIstatus::failure; } m_strDriverId = vId; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Get the unique ID for *this driver. // Type: Overridden. // Args: None. // Return: CMIUtilString & - Text description. // Throws: None. //-- const CMIUtilString &CMIDriver::GetId() const { return m_strDriverId; } //++ //------------------------------------------------------------------------------------ // Details: Interpret the text data and match against current commands to see if // there // is a match. If a match then the command is issued and actioned on. // The // text data if not understood by *this driver is past on to the Fall // Thru // driver. // This function is used by the application's main thread. // Type: Method. // Args: vTextLine - (R) Text data representing a possible command. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::InterpretCommand(const CMIUtilString &vTextLine) { const bool bNeedToRebroadcastStopEvent = m_rLldbDebugger.CheckIfNeedToRebroadcastStopEvent(); bool bCmdYesValid = false; bool bOk = InterpretCommandThisDriver(vTextLine, bCmdYesValid); if (bOk && !bCmdYesValid) bOk = InterpretCommandFallThruDriver(vTextLine, bCmdYesValid); if (bNeedToRebroadcastStopEvent) m_rLldbDebugger.RebroadcastStopEvent(); return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Helper function for CMIDriver::InterpretCommandThisDriver. // Convert a CLI command to MI command (just wrap any CLI command // into "-interpreter-exec command \"\""). // Type: Method. // Args: vTextLine - (R) Text data representing a possible command. // Return: CMIUtilString - The original MI command or converted CLI command. // MIstatus::failure - Functional failed. // Throws: None. //-- CMIUtilString CMIDriver::WrapCLICommandIntoMICommand(const CMIUtilString &vTextLine) const { // Tokens contain following digits static const CMIUtilString digits("0123456789"); // Consider an algorithm on the following example: // 001-file-exec-and-symbols "/path/to/file" // // 1. Skip a command token // For example: // 001-file-exec-and-symbols "/path/to/file" // 001target create "/path/to/file" // ^ -- command starts here (in both cases) // Also possible case when command not found: // 001 // ^ -- i.e. only tokens are present (or empty string at all) const size_t nCommandOffset = vTextLine.find_first_not_of(digits); // 2. Check if command is empty // For example: // 001-file-exec-and-symbols "/path/to/file" // 001target create "/path/to/file" // ^ -- command not empty (in both cases) // or: // 001 // ^ -- command wasn't found const bool bIsEmptyCommand = (nCommandOffset == CMIUtilString::npos); // 3. Check and exit if it isn't a CLI command // For example: // 001-file-exec-and-symbols "/path/to/file" // 001 // ^ -- it isn't CLI command (in both cases) // or: // 001target create "/path/to/file" // ^ -- it's CLI command const bool bIsCliCommand = !bIsEmptyCommand && (vTextLine.at(nCommandOffset) != '-'); if (!bIsCliCommand) return vTextLine; // 4. Wrap CLI command to make it MI-compatible // // 001target create "/path/to/file" // ^^^ -- token const std::string vToken(vTextLine.begin(), vTextLine.begin() + nCommandOffset); // 001target create "/path/to/file" // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- CLI command const CMIUtilString vCliCommand(std::string(vTextLine, nCommandOffset)); // 5. Escape special characters and embed the command in a string // Result: it looks like -- target create \"/path/to/file\". const std::string vShieldedCliCommand(vCliCommand.AddSlashes()); // 6. Turn the CLI command into an MI command, as in: // 001-interpreter-exec command "target create \"/path/to/file\"" // ^^^ -- token // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ -- wrapper // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- shielded // CLI command return CMIUtilString::Format("%s-interpreter-exec command \"%s\"", vToken.c_str(), vShieldedCliCommand.c_str()); } //++ //------------------------------------------------------------------------------------ // Details: Interpret the text data and match against current commands to see if // there // is a match. If a match then the command is issued and actioned on. // If a // command cannot be found to match then vwbCmdYesValid is set to false // and // nothing else is done here. // This function is used by the application's main thread. // Type: Method. // Args: vTextLine - (R) Text data representing a possible command. // vwbCmdYesValid - (W) True = Command valid, false = command not // handled. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::InterpretCommandThisDriver(const CMIUtilString &vTextLine, bool &vwbCmdYesValid) { // Convert any CLI commands into MI commands const CMIUtilString vMITextLine(WrapCLICommandIntoMICommand(vTextLine)); vwbCmdYesValid = false; bool bCmdNotInCmdFactor = false; SMICmdData cmdData; CMICmdMgr &rCmdMgr = CMICmdMgr::Instance(); if (!rCmdMgr.CmdInterpret(vMITextLine, vwbCmdYesValid, bCmdNotInCmdFactor, cmdData)) return MIstatus::failure; if (vwbCmdYesValid) { // For debugging only // m_pLog->WriteLog( cmdData.strMiCmdAll.c_str() ); return ExecuteCommand(cmdData); } // Check for escape character, may be cursor control characters // This code is not necessary for application operation, just want to keep // tabs on what // has been given to the driver to try and interpret. if (vMITextLine.at(0) == 27) { CMIUtilString logInput(MIRSRC(IDS_STDIN_INPUT_CTRL_CHARS)); for (MIuint i = 0; i < vMITextLine.length(); i++) { logInput += CMIUtilString::Format("%d ", vMITextLine.at(i)); } m_pLog->WriteLog(logInput); return MIstatus::success; } // Write to the Log that a 'command' was not valid. // Report back to the MI client via MI result record. CMIUtilString strNotInCmdFactory; if (bCmdNotInCmdFactor) strNotInCmdFactory = CMIUtilString::Format( MIRSRC(IDS_DRIVER_CMD_NOT_IN_FACTORY), cmdData.strMiCmd.c_str()); const CMIUtilString strNot( CMIUtilString::Format("%s ", MIRSRC(IDS_WORD_NOT))); const CMIUtilString msg(CMIUtilString::Format( MIRSRC(IDS_DRIVER_CMD_RECEIVED), vMITextLine.c_str(), strNot.c_str(), strNotInCmdFactory.c_str())); const CMICmnMIValueConst vconst = CMICmnMIValueConst(msg); const CMICmnMIValueResult valueResult("msg", vconst); const CMICmnMIResultRecord miResultRecord( cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, valueResult); const bool bOk = m_rStdOut.WriteMIResponse(miResultRecord.GetString()); // Proceed to wait for or execute next command return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Having previously had the potential command validated and found // valid now // get the command executed. // This function is used by the application's main thread. // Type: Method. // Args: vCmdData - (RW) Command meta data. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMIDriver::ExecuteCommand(const SMICmdData &vCmdData) { CMICmdMgr &rCmdMgr = CMICmdMgr::Instance(); return rCmdMgr.CmdExecute(vCmdData); } //++ //------------------------------------------------------------------------------------ // Details: Set the MI Driver's exit application flag. The application checks // this flag // after every stdin line is read so the exit may not be instantaneous. // If vbForceExit is false the MI Driver queries its state and // determines if is // should exit or continue operating depending on that running state. // This is related to the running state of the MI driver. // Type: Overridden. // Args: None. // Return: None. // Throws: None. //-- void CMIDriver::SetExitApplicationFlag(const bool vbForceExit) { if (vbForceExit) { CMIUtilThreadLock lock(m_threadMutex); m_bExitApp = true; return; } // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM // Did we receive a SIGINT from the client during a running debug program, if // so then SIGINT is not to be taken as meaning kill the MI driver application // but halt the inferior program being debugged instead if (m_eCurrentDriverState == eDriverState_RunningDebugging) { InterpretCommand("-exec-interrupt"); return; } m_bExitApp = true; } //++ //------------------------------------------------------------------------------------ // Details: Get the MI Driver's exit exit application flag. // This is related to the running state of the MI driver. // Type: Method. // Args: None. // Return: bool - True = MI Driver is shutting down, false = MI driver is // running. // Throws: None. //-- bool CMIDriver::GetExitApplicationFlag() const { return m_bExitApp; } //++ //------------------------------------------------------------------------------------ // Details: Get the current running state of the MI Driver. // Type: Method. // Args: None. // Return: DriverState_e - The current running state of the application. // Throws: None. //-- CMIDriver::DriverState_e CMIDriver::GetCurrentDriverState() const { return m_eCurrentDriverState; } //++ //------------------------------------------------------------------------------------ // Details: Set the current running state of the MI Driver to running and // currently not in // a debug session. // Type: Method. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Return: DriverState_e - The current running state of the application. // Throws: None. //-- bool CMIDriver::SetDriverStateRunningNotDebugging() { // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM if (m_eCurrentDriverState == eDriverState_RunningNotDebugging) return MIstatus::success; // Driver cannot be in the following states to set // eDriverState_RunningNotDebugging switch (m_eCurrentDriverState) { case eDriverState_NotRunning: case eDriverState_Initialising: case eDriverState_ShuttingDown: { SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); return MIstatus::failure; } case eDriverState_RunningDebugging: case eDriverState_RunningNotDebugging: break; case eDriverState_count: SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), "SetDriverStateRunningNotDebugging()")); return MIstatus::failure; } // Driver must be in this state to set eDriverState_RunningNotDebugging if (m_eCurrentDriverState != eDriverState_RunningDebugging) { SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); return MIstatus::failure; } m_eCurrentDriverState = eDriverState_RunningNotDebugging; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Set the current running state of the MI Driver to running and // currently not in // a debug session. The driver's state must in the state running and in // a // debug session to set this new state. // Type: Method. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Return: DriverState_e - The current running state of the application. // Throws: None. //-- bool CMIDriver::SetDriverStateRunningDebugging() { // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM if (m_eCurrentDriverState == eDriverState_RunningDebugging) return MIstatus::success; // Driver cannot be in the following states to set // eDriverState_RunningDebugging switch (m_eCurrentDriverState) { case eDriverState_NotRunning: case eDriverState_Initialising: case eDriverState_ShuttingDown: { SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); return MIstatus::failure; } case eDriverState_RunningDebugging: case eDriverState_RunningNotDebugging: break; case eDriverState_count: SetErrorDescription( CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), "SetDriverStateRunningDebugging()")); return MIstatus::failure; } // Driver must be in this state to set eDriverState_RunningDebugging if (m_eCurrentDriverState != eDriverState_RunningNotDebugging) { SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); return MIstatus::failure; } m_eCurrentDriverState = eDriverState_RunningDebugging; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Prepare the client IDE so it will start working/communicating with // *this MI // driver. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMIDriver::InitClientIDEToMIDriver() const { // Put other IDE init functions here return InitClientIDEEclipse(); } //++ //------------------------------------------------------------------------------------ // Details: The IDE Eclipse when debugging locally expects "(gdb)\n" character // sequence otherwise it refuses to communicate and times out. This // should be // sent to Eclipse before anything else. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMIDriver::InitClientIDEEclipse() const { return CMICmnStreamStdout::WritePrompt(); } //++ //------------------------------------------------------------------------------------ // Details: Ask *this driver whether it found an executable in the MI Driver's // list of // arguments which to open and debug. If so instigate commands to set // up a debug // session for that executable. // Type: Method. // Args: None. // Return: bool - True = True = Yes executable given as one of the parameters // to the MI // Driver. // False = not found. // Throws: None. //-- bool CMIDriver::HaveExecutableFileNamePathOnCmdLine() const { return m_bHaveExecutableFileNamePathOnCmdLine; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve from *this driver executable file name path to start a // debug session // with (if present see HaveExecutableFileNamePathOnCmdLine()). // Type: Method. // Args: None. // Return: CMIUtilString & - Executeable file name path or empty string. // Throws: None. //-- const CMIUtilString &CMIDriver::GetExecutableFileNamePathOnCmdLine() const { return m_strCmdLineArgExecuteableFileNamePath; } //++ //------------------------------------------------------------------------------------ // Details: Execute commands (by injecting them into the stdin line queue // container) and // other code to set up the MI Driver such that is can take the // executable // argument passed on the command and create a debug session for it. // Type: Method. // Args: None. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMIDriver::LocalDebugSessionStartupExecuteCommands() { const CMIUtilString strCmd(CMIUtilString::Format( "-file-exec-and-symbols \"%s\"", m_strCmdLineArgExecuteableFileNamePath.AddSlashes().c_str())); bool bOk = CMICmnStreamStdout::TextToStdout(strCmd); bOk = bOk && InterpretCommand(strCmd); bOk = bOk && CMICmnStreamStdout::WritePrompt(); return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Set the MI Driver into "its debugging an executable passed as an // argument" // mode as against running via a client like Eclipse. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMIDriver::SetDriverDebuggingArgExecutable() { m_bDriverDebuggingArgExecutable = true; } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the MI Driver state indicating if it is operating in "its // debugging // an executable passed as an argument" mode as against running via a // client // like Eclipse. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- bool CMIDriver::IsDriverDebuggingArgExecutable() const { return m_bDriverDebuggingArgExecutable; } //++ //------------------------------------------------------------------------------------ // Details: Execute commands from command source file in specified mode, and // set exit-flag if needed. // Type: Method. // Args: vbAsyncMode - (R) True = execute commands in asynchronous // mode, false = otherwise. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMIDriver::ExecuteCommandFile(const bool vbAsyncMode) { std::ifstream ifsStartScript(m_strCmdLineArgCommandFileNamePath.c_str()); if (!ifsStartScript.is_open()) { const CMIUtilString errMsg( CMIUtilString::Format(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN), m_strCmdLineArgCommandFileNamePath.c_str())); SetErrorDescription(errMsg.c_str()); const bool bForceExit = true; SetExitApplicationFlag(bForceExit); return MIstatus::failure; } // Switch lldb to synchronous mode CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); const bool bAsyncSetting = rSessionInfo.GetDebugger().GetAsync(); rSessionInfo.GetDebugger().SetAsync(vbAsyncMode); // Execute commands from file bool bOk = MIstatus::success; CMIUtilString strCommand; while (!m_bExitApp && std::getline(ifsStartScript, strCommand)) { // Print command bOk = CMICmnStreamStdout::TextToStdout(strCommand); // Skip if it's a comment or empty line if (strCommand.empty() || strCommand[0] == '#') continue; // Execute if no error if (bOk) { CMIUtilThreadLock lock(rSessionInfo.GetSessionMutex()); bOk = InterpretCommand(strCommand); } // Draw the prompt after command will be executed (if enabled) bOk = bOk && CMICmnStreamStdout::WritePrompt(); // Exit if there is an error if (!bOk) { const bool bForceExit = true; SetExitApplicationFlag(bForceExit); break; } // Wait while the handler thread handles incoming events CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); } // Switch lldb back to initial mode rSessionInfo.GetDebugger().SetAsync(bAsyncSetting); return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Gets called when lldb-mi gets a signal. Stops the process if it was // SIGINT. // // Type: Method. // Args: signal that was delivered // Return: None. // Throws: None. //-- void CMIDriver::DeliverSignal(int signal) { if (signal == SIGINT && (m_eCurrentDriverState == eDriverState_RunningDebugging)) InterpretCommand("-exec-interrupt"); } Index: vendor/lldb/dist/tools/lldb-mi/MIDriverMain.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MIDriverMain.cpp (revision 321193) +++ vendor/lldb/dist/tools/lldb-mi/MIDriverMain.cpp (revision 321194) @@ -1,200 +1,201 @@ //===-- MIDriverMain.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: Defines the entry point for the console application. // The MI application (project name MI) runs in two modes: // An LLDB native driver mode where it acts no different from the // LLDB driver. // The other mode is the MI when it finds on the command line // the --interpreter option. Command line argument --help on its // own will give // help for the LLDB driver. If entered with --interpreter then MI // help will // provided. // To implement new MI commands derive a new command class from the // command base // class. To enable the new command for interpretation add the new // command class // to the command factory. The files of relevance are: // MICmdCommands.cpp // MICmdBase.h / .cpp // MICmdCmd.h / .cpp #if defined(_MSC_VER) #define _INC_SIGNAL // Stop window's signal.h being included - // CODETAG_IOR_SIGNALS #endif // _MSC_VER // Third party headers: #include "lldb/API/SBHostOS.h" +#include #include // In house headers: #include "MICmnConfig.h" #include "MICmnResources.h" #include "MICmnStreamStdin.h" #include "MIDriver.h" #include "MIDriverMgr.h" #include "MIUtilDebug.h" #include "Platform.h" // Define signals - CODETAG_IOR_SIGNALS #if defined(_MSC_VER) #pragma warning( \ once : 4530) // Warning C4530: C++ exception handler used, but unwind // semantics are not enabled. Specify /EHsc #endif // _MSC_VER // CODETAG_IOR_SIGNALS //++ //------------------------------------------------------------------------------------ // Details: The SIGINT signal is sent to a process by its controlling terminal // when a // user wishes to interrupt the process. This is typically initiated by // pressing // Control-C, but on some systems, the "delete" character or "break" // key can be // used. // Be aware this function may be called on another thread besides the // main thread. // Type: Function. // Args: vSigno - (R) Signal number. // Return: None. // Throws: None. //-- void sigint_handler(int vSigno) { #ifdef _WIN32 // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif static bool g_interrupt_sent = false; CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); lldb::SBDebugger *pDebugger = rDriverMgr.DriverGetTheDebugger(); if (pDebugger != nullptr) { if (!g_interrupt_sent) { g_interrupt_sent = true; pDebugger->DispatchInputInterrupt(); g_interrupt_sent = false; } } // Send signal to driver so that it can take suitable action rDriverMgr.DeliverSignal(vSigno); } //++ //------------------------------------------------------------------------------------ // Details: Init the MI driver system. Initialize the whole driver system which // includes // both the original LLDB driver and the MI driver. // Type: Function. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool DriverSystemInit() { bool bOk = MIstatus::success; CMIDriver &rMIDriver = CMIDriver::Instance(); CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); bOk = rDriverMgr.Initialize(); // Register MIDriver first as it needs to initialize and be ready // for the Driver to get information from MIDriver when it initializes // (LLDB Driver is registered with the Driver Manager in MI's Initialize()) bOk = bOk && rDriverMgr.RegisterDriver(rMIDriver, "MIDriver"); // Will be main driver return bOk; } //++ //------------------------------------------------------------------------------------ // Details: Shutdown the debugger system. Release / terminate resources external // to // specifically the MI driver. // Type: Function. // Args: vbAppExitOk - (R) True = No problems, false = App exiting with // problems (investigate!). // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool DriverSystemShutdown(const bool vbAppExitOk) { bool bOk = MIstatus::success; // *** Order is important here *** CMIDriverMgr::Instance().Shutdown(); return bOk; } //++ //------------------------------------------------------------------------------------ // Details: MI's application start point of execution. The application runs in // two modes. // An LLDB native driver mode where it acts no different from the LLDB // driver. // The other mode is the MI when it finds on the command line // the --interpreter option. Command line argument --help on its own // will give // help for the LLDB driver. If entered with --interpreter then // application // help will provided. // Type: Method. // Args: argc - (R) An integer that contains the count of arguments that // follow in // argv. The argc parameter is always greater than or // equal to 1. // argv - (R) An array of null-terminated strings representing // command-line // arguments entered by the user of the program. By // convention, // argv[0] is the command with which the program is // invoked. // Return: int - 0 = Normal exit, program success. // >0 = Program success with status i.e. Control-C signal // status // <0 = Program failed. // -1 = Program failed reason not specified here, see MI log // file. // -1000 = Program failed did not initialize successfully. // Throws: None. //-- int main(int argc, char const *argv[]) { #if MICONFIG_DEBUG_SHOW_ATTACH_DBG_DLG #ifdef _WIN32 CMIUtilDebug::ShowDlgWaitForDbgAttach(); #else CMIUtilDebug::WaitForDbgAttachInfinteLoop(); #endif // _WIN32 #endif // MICONFIG_DEBUG_SHOW_ATTACH_DBG_DLG // *** Order is important here *** bool bOk = DriverSystemInit(); if (!bOk) { DriverSystemShutdown(bOk); return -1000; } // CODETAG_IOR_SIGNALS signal(SIGINT, sigint_handler); bool bExiting = false; CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); bOk = bOk && rDriverMgr.ParseArgs(argc, argv, bExiting); if (bOk && !bExiting) bOk = rDriverMgr.DriverParseArgs(argc, argv, stdout, bExiting); if (bOk && !bExiting) bOk = rDriverMgr.DriverMainLoop(); // Logger and other resources shutdown now DriverSystemShutdown(bOk); const int appResult = bOk ? 0 : -1; return appResult; } Index: vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp (revision 321193) +++ vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp (revision 321194) @@ -1,516 +1,516 @@ //===-- lldb-gdbserver.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 #ifndef _WIN32 #include #include #endif // C++ Includes #include "Acceptor.h" #include "LLDBServerUtilities.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/HostGetOpt.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/Socket.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Utility/Status.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Errno.h" #if defined(__linux__) #include "Plugins/Process/Linux/NativeProcessLinux.h" #elif defined(__NetBSD__) #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #endif #ifndef LLGS_PROGRAM_NAME #define LLGS_PROGRAM_NAME "lldb-server" #endif #ifndef LLGS_VERSION_STR #define LLGS_VERSION_STR "local_build" #endif using namespace llvm; using namespace lldb; using namespace lldb_private; using namespace lldb_private::lldb_server; using namespace lldb_private::process_gdb_remote; namespace { #if defined(__linux__) typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory; #elif defined(__NetBSD__) typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory; #else // Dummy implementation to make sure the code compiles class NativeProcessFactory : public NativeProcessProtocol::Factory { public: - llvm::Expected + llvm::Expected> Launch(ProcessLaunchInfo &launch_info, NativeProcessProtocol::NativeDelegate &delegate, MainLoop &mainloop) const override { llvm_unreachable("Not implemented"); } - llvm::Expected + llvm::Expected> Attach(lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &delegate, MainLoop &mainloop) const override { llvm_unreachable("Not implemented"); } }; #endif } //---------------------------------------------------------------------- // option descriptors for getopt_long_only() //---------------------------------------------------------------------- static int g_debug = 0; static int g_verbose = 0; static struct option g_long_options[] = { {"debug", no_argument, &g_debug, 1}, {"verbose", no_argument, &g_verbose, 1}, {"log-file", required_argument, NULL, 'l'}, {"log-channels", required_argument, NULL, 'c'}, {"attach", required_argument, NULL, 'a'}, {"named-pipe", required_argument, NULL, 'N'}, {"pipe", required_argument, NULL, 'U'}, {"native-regs", no_argument, NULL, 'r'}, // Specify to use the native registers instead of the gdb defaults // for the architecture. NOTE: this is a do-nothing arg as it's // behavior is default now. FIXME remove call from lldb-platform. {"reverse-connect", no_argument, NULL, 'R'}, // Specifies that llgs attaches to the client address:port rather // than llgs listening for a connection from address on port. {"setsid", no_argument, NULL, 'S'}, // Call setsid() to make llgs run in its own session. {NULL, 0, NULL, 0}}; //---------------------------------------------------------------------- // Watch for signals //---------------------------------------------------------------------- static int g_sighup_received_count = 0; #ifndef _WIN32 static void sighup_handler(MainLoopBase &mainloop) { ++g_sighup_received_count; Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("lldb-server:%s swallowing SIGHUP (receive count=%d)", __FUNCTION__, g_sighup_received_count); if (g_sighup_received_count >= 2) mainloop.RequestTermination(); } #endif // #ifndef _WIN32 static void display_usage(const char *progname, const char *subcommand) { fprintf(stderr, "Usage:\n %s %s " "[--log-file log-file-name] " "[--log-channels log-channel-list] " "[--setsid] " "[--named-pipe named-pipe-path] " "[--native-regs] " "[--attach pid] " "[[HOST]:PORT] " "[-- PROGRAM ARG1 ARG2 ...]\n", progname, subcommand); exit(0); } void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server, lldb::pid_t pid) { Status error = gdb_server.AttachToProcess(pid); if (error.Fail()) { fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid, error.AsCString()); exit(1); } } void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server, const std::string &process_name) { // FIXME implement. } void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server, const std::string &attach_target) { assert(!attach_target.empty() && "attach_target cannot be empty"); // First check if the attach_target is convertible to a long. If so, we'll use // it as a pid. char *end_p = nullptr; const long int pid = strtol(attach_target.c_str(), &end_p, 10); // We'll call it a match if the entire argument is consumed. if (end_p && static_cast(end_p - attach_target.c_str()) == attach_target.size()) handle_attach_to_pid(gdb_server, static_cast(pid)); else handle_attach_to_process_name(gdb_server, attach_target); } void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc, const char *const argv[]) { Status error; error = gdb_server.SetLaunchArguments(argv, argc); if (error.Fail()) { fprintf(stderr, "error: failed to set launch args for '%s': %s\n", argv[0], error.AsCString()); exit(1); } unsigned int launch_flags = eLaunchFlagStopAtEntry | eLaunchFlagDebug; error = gdb_server.SetLaunchFlags(launch_flags); if (error.Fail()) { fprintf(stderr, "error: failed to set launch flags for '%s': %s\n", argv[0], error.AsCString()); exit(1); } error = gdb_server.LaunchProcess(); if (error.Fail()) { fprintf(stderr, "error: failed to launch '%s': %s\n", argv[0], error.AsCString()); exit(1); } } Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &socket_id) { size_t bytes_written = 0; // Write the port number as a C string with the NULL terminator. return port_pipe.Write(socket_id.c_str(), socket_id.size() + 1, bytes_written); } Status writeSocketIdToPipe(const char *const named_pipe_path, const std::string &socket_id) { Pipe port_name_pipe; // Wait for 10 seconds for pipe to be opened. auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false, std::chrono::seconds{10}); if (error.Fail()) return error; return writeSocketIdToPipe(port_name_pipe, socket_id); } Status writeSocketIdToPipe(int unnamed_pipe_fd, const std::string &socket_id) { #if defined(_WIN32) return Status("Unnamed pipes are not supported on Windows."); #else Pipe port_pipe{Pipe::kInvalidDescriptor, unnamed_pipe_fd}; return writeSocketIdToPipe(port_pipe, socket_id); #endif } void ConnectToRemote(MainLoop &mainloop, GDBRemoteCommunicationServerLLGS &gdb_server, bool reverse_connect, const char *const host_and_port, const char *const progname, const char *const subcommand, const char *const named_pipe_path, int unnamed_pipe_fd) { Status error; if (host_and_port && host_and_port[0]) { // Parse out host and port. std::string final_host_and_port; std::string connection_host; std::string connection_port; uint32_t connection_portno = 0; // If host_and_port starts with ':', default the host to be "localhost" and // expect the remainder to be the port. if (host_and_port[0] == ':') final_host_and_port.append("localhost"); final_host_and_port.append(host_and_port); const std::string::size_type colon_pos = final_host_and_port.find(':'); if (colon_pos != std::string::npos) { connection_host = final_host_and_port.substr(0, colon_pos); connection_port = final_host_and_port.substr(colon_pos + 1); connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0); } std::unique_ptr connection_up; if (reverse_connect) { // llgs will connect to the gdb-remote client. // Ensure we have a port number for the connection. if (connection_portno == 0) { fprintf(stderr, "error: port number must be specified on when using " "reverse connect"); exit(1); } // Build the connection string. char connection_url[512]; snprintf(connection_url, sizeof(connection_url), "connect://%s", final_host_and_port.c_str()); // Create the connection. connection_up.reset(new ConnectionFileDescriptor); auto connection_result = connection_up->Connect(connection_url, &error); if (connection_result != eConnectionStatusSuccess) { fprintf(stderr, "error: failed to connect to client at '%s' " "(connection status: %d)", connection_url, static_cast(connection_result)); exit(-1); } if (error.Fail()) { fprintf(stderr, "error: failed to connect to client at '%s': %s", connection_url, error.AsCString()); exit(-1); } } else { std::unique_ptr acceptor_up( Acceptor::Create(final_host_and_port, false, error)); if (error.Fail()) { fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); exit(1); } error = acceptor_up->Listen(1); if (error.Fail()) { fprintf(stderr, "failed to listen: %s\n", error.AsCString()); exit(1); } const std::string socket_id = acceptor_up->GetLocalSocketId(); if (!socket_id.empty()) { // If we have a named pipe to write the socket id back to, do that now. if (named_pipe_path && named_pipe_path[0]) { error = writeSocketIdToPipe(named_pipe_path, socket_id); if (error.Fail()) fprintf(stderr, "failed to write to the named pipe \'%s\': %s", named_pipe_path, error.AsCString()); } // If we have an unnamed pipe to write the socket id back to, do that // now. else if (unnamed_pipe_fd >= 0) { error = writeSocketIdToPipe(unnamed_pipe_fd, socket_id); if (error.Fail()) fprintf(stderr, "failed to write to the unnamed pipe: %s", error.AsCString()); } } else { fprintf(stderr, "unable to get the socket id for the listening connection\n"); } Connection *conn = nullptr; error = acceptor_up->Accept(false, conn); if (error.Fail()) { printf("failed to accept new connection: %s\n", error.AsCString()); exit(1); } connection_up.reset(conn); } error = gdb_server.InitializeConnection(std::move(connection_up)); if (error.Fail()) { fprintf(stderr, "Failed to initialize connection: %s\n", error.AsCString()); exit(-1); } printf("Connection established.\n"); } } //---------------------------------------------------------------------- // main //---------------------------------------------------------------------- int main_gdbserver(int argc, char *argv[]) { Status error; MainLoop mainloop; #ifndef _WIN32 // Setup signal handlers first thing. signal(SIGPIPE, SIG_IGN); MainLoop::SignalHandleUP sighup_handle = mainloop.RegisterSignal(SIGHUP, sighup_handler, error); #endif const char *progname = argv[0]; const char *subcommand = argv[1]; argc--; argv++; int long_option_index = 0; int ch; std::string attach_target; std::string named_pipe_path; std::string log_file; StringRef log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" int unnamed_pipe_fd = -1; bool reverse_connect = false; // ProcessLaunchInfo launch_info; ProcessAttachInfo attach_info; bool show_usage = false; int option_error = 0; #if __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif std::string short_options(OptionParser::GetShortOptionString(g_long_options)); while ((ch = getopt_long_only(argc, argv, short_options.c_str(), g_long_options, &long_option_index)) != -1) { switch (ch) { case 0: // Any optional that auto set themselves will return 0 break; case 'l': // Set Log File if (optarg && optarg[0]) log_file.assign(optarg); break; case 'c': // Log Channels if (optarg && optarg[0]) log_channels = StringRef(optarg); break; case 'N': // named pipe if (optarg && optarg[0]) named_pipe_path = optarg; break; case 'U': // unnamed pipe if (optarg && optarg[0]) unnamed_pipe_fd = StringConvert::ToUInt32(optarg, -1); break; case 'r': // Do nothing, native regs is the default these days break; case 'R': reverse_connect = true; break; #ifndef _WIN32 case 'S': // Put llgs into a new session. Terminals group processes // into sessions and when a special terminal key sequences // (like control+c) are typed they can cause signals to go out to // all processes in a session. Using this --setsid (-S) option // will cause debugserver to run in its own sessions and be free // from such issues. // // This is useful when llgs is spawned from a command // line application that uses llgs to do the debugging, // yet that application doesn't want llgs receiving the // signals sent to the session (i.e. dying when anyone hits ^C). { const ::pid_t new_sid = setsid(); if (new_sid == -1) { llvm::errs() << llvm::formatv( "failed to set new session id for {0} ({1})\n", LLGS_PROGRAM_NAME, llvm::sys::StrError()); } } break; #endif case 'a': // attach {pid|process_name} if (optarg && optarg[0]) attach_target = optarg; break; case 'h': /* fall-through is intentional */ case '?': show_usage = true; break; } } if (show_usage || option_error) { display_usage(progname, subcommand); exit(option_error); } if (!LLDBServerUtilities::SetupLogging( log_file, log_channels, LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) return -1; Log *log(lldb_private::GetLogIfAnyCategoriesSet(GDBR_LOG_PROCESS)); if (log) { log->Printf("lldb-server launch"); for (int i = 0; i < argc; i++) { log->Printf("argv[%i] = '%s'", i, argv[i]); } } // Skip any options we consumed with getopt_long_only. argc -= optind; argv += optind; if (argc == 0) { display_usage(progname, subcommand); exit(255); } NativeProcessFactory factory; GDBRemoteCommunicationServerLLGS gdb_server(mainloop, factory); const char *const host_and_port = argv[0]; argc -= 1; argv += 1; // Any arguments left over are for the program that we need to launch. If // there // are no arguments, then the GDB server will start up and wait for an 'A' // packet // to launch a program, or a vAttach packet to attach to an existing process, // unless // explicitly asked to attach with the --attach={pid|program_name} form. if (!attach_target.empty()) handle_attach(gdb_server, attach_target); else if (argc > 0) handle_launch(gdb_server, argc, argv); // Print version info. printf("%s-%s", LLGS_PROGRAM_NAME, LLGS_VERSION_STR); ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, progname, subcommand, named_pipe_path.c_str(), unnamed_pipe_fd); if (!gdb_server.IsConnected()) { fprintf(stderr, "no connection information provided, unable to run\n"); display_usage(progname, subcommand); return 1; } mainloop.Run(); fprintf(stderr, "lldb-server exiting...\n"); return 0; } Index: vendor/lldb/dist/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp (revision 321193) +++ vendor/lldb/dist/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp (revision 321194) @@ -1,158 +1,163 @@ //===-- CPlusPlusLanguageTest.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" using namespace lldb_private; TEST(CPlusPlusLanguage, MethodNameParsing) { struct TestCase { std::string input; std::string context, basename, arguments, qualifiers, scope_qualified_name; }; TestCase test_cases[] = { {"main(int, char *[]) ", "", "main", "(int, char *[])", "", "main"}, {"foo::bar(baz) const", "foo", "bar", "(baz)", "const", "foo::bar"}, {"foo::~bar(baz)", "foo", "~bar", "(baz)", "", "foo::~bar"}, {"a::b::c::d(e,f)", "a::b::c", "d", "(e,f)", "", "a::b::c::d"}, {"void f(int)", "", "f", "(int)", "", "f"}, // Operators {"std::basic_ostream >& " "std::operator<< >" "(std::basic_ostream >&, char const*)", "std", "operator<< >", "(std::basic_ostream >&, char const*)", "", "std::operator<< >"}, {"operator delete[](void*, clang::ASTContext const&, unsigned long)", "", "operator delete[]", "(void*, clang::ASTContext const&, unsigned long)", "", "operator delete[]"}, {"llvm::Optional::operator bool() const", "llvm::Optional", "operator bool", "()", "const", "llvm::Optional::operator bool"}, {"(anonymous namespace)::FactManager::operator[](unsigned short)", "(anonymous namespace)::FactManager", "operator[]", "(unsigned short)", "", "(anonymous namespace)::FactManager::operator[]"}, {"const int& std::map>::operator[](short) const", "std::map>", "operator[]", "(short)", "const", "std::map>::operator[]"}, {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", "CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)", "", "CompareInsn::operator()"}, {"llvm::Optional::operator*() const &", "llvm::Optional", "operator*", "()", "const &", "llvm::Optional::operator*"}, // Internal classes {"operator<<(Cls, Cls)::Subclass::function()", "operator<<(Cls, Cls)::Subclass", "function", "()", "", "operator<<(Cls, Cls)::Subclass::function"}, {"SAEC::checkFunction(context&) const::CallBack::CallBack(int)", "SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "", "SAEC::checkFunction(context&) const::CallBack::CallBack"}, // Anonymous namespace {"XX::(anonymous namespace)::anon_class::anon_func() const", "XX::(anonymous namespace)::anon_class", "anon_func", "()", "const", "XX::(anonymous namespace)::anon_class::anon_func"}, + // Lambda + {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const", + "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const", + "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"}, + // Function pointers {"string (*f(vector&&))(float)", "", "f", "(vector&&)", "", "f"}, {"void (*&std::_Any_data::_M_access())()", "std::_Any_data", "_M_access", "()", "", "std::_Any_data::_M_access"}, {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "", "func1", "(int)", "", "func1"}, // Decltype {"decltype(nullptr)&& std::forward" "(std::remove_reference::type&)", "std", "forward", "(std::remove_reference::type&)", "", "std::forward"}, // Templates {"void llvm::PM>::" "addPass(llvm::VP)", "llvm::PM>", "addPass", "(llvm::VP)", "", "llvm::PM>::" "addPass"}, {"void std::vector >" "::_M_emplace_back_aux(Class const&)", "std::vector >", "_M_emplace_back_aux", "(Class const&)", "", "std::vector >::" "_M_emplace_back_aux"}, {"unsigned long llvm::countTrailingOnes" "(unsigned int, llvm::ZeroBehavior)", "llvm", "countTrailingOnes", "(unsigned int, llvm::ZeroBehavior)", "", "llvm::countTrailingOnes"}, {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned " "long)", "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"}, {"f, sizeof(B)()", "", "f, sizeof(B)", "()", "", "f, sizeof(B)"}}; for (const auto &test : test_cases) { CPlusPlusLanguage::MethodName method(ConstString(test.input)); EXPECT_TRUE(method.IsValid()) << test.input; if (method.IsValid()) { EXPECT_EQ(test.context, method.GetContext().str()); EXPECT_EQ(test.basename, method.GetBasename().str()); EXPECT_EQ(test.arguments, method.GetArguments().str()); EXPECT_EQ(test.qualifiers, method.GetQualifiers().str()); EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName()); } } } TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) { struct TestCase { std::string input; std::string context, basename; }; TestCase test_cases[] = { {"main", "", "main"}, {"main ", "", "main"}, {"foo01::bar", "foo01", "bar"}, {"foo::~bar", "foo", "~bar"}, {"std::vector::push_back", "std::vector", "push_back"}, {"operator<<(Cls, Cls)::Subclass::function", "operator<<(Cls, Cls)::Subclass", "function"}, {"std::vector>" "::_M_emplace_back_aux", "std::vector>", "_M_emplace_back_aux"}}; llvm::StringRef context, basename; for (const auto &test : test_cases) { EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier( test.input.c_str(), context, basename)); EXPECT_EQ(test.context, context.str()); EXPECT_EQ(test.basename, basename.str()); } EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context, basename)); EXPECT_FALSE( CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename)); EXPECT_FALSE( CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename)); EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( "selector:", context, basename)); EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( "selector:otherField:", context, basename)); EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( "abc::", context, basename)); } Index: vendor/lldb/dist/unittests/debugserver/RNBSocketTest.cpp =================================================================== --- vendor/lldb/dist/unittests/debugserver/RNBSocketTest.cpp (revision 321193) +++ vendor/lldb/dist/unittests/debugserver/RNBSocketTest.cpp (revision 321194) @@ -1,158 +1,160 @@ //===-- RNBSocketTest.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 #include #include #include "RNBDefs.h" #include "RNBSocket.h" #include "lldb/Host/Socket.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/common/TCPSocket.h" using namespace lldb_private; std::string hello = "Hello, world!"; std::string goodbye = "Goodbye!"; static void ServerCallbackv4(const void *baton, in_port_t port) { auto child_pid = fork(); if (child_pid == 0) { Socket *client_socket; char addr_buffer[256]; sprintf(addr_buffer, "%s:%d", baton, port); Status err = Socket::TcpConnect(addr_buffer, false, client_socket); if (err.Fail()) abort(); char buffer[32]; size_t read_size = 32; err = client_socket->Read((void *)&buffer[0], read_size); if (err.Fail()) abort(); std::string Recv(&buffer[0], read_size); if (Recv != hello) abort(); size_t write_size = goodbye.length(); err = client_socket->Write(goodbye.c_str(), write_size); if (err.Fail()) abort(); if (write_size != goodbye.length()) abort(); delete client_socket; exit(0); } } void TestSocketListen(const char *addr) { // Skip IPv6 tests if there isn't a valid interafce auto addresses = lldb_private::SocketAddress::GetAddressInfo( addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); if (addresses.size() == 0) return; char addr_wrap[256]; if (addresses.front().GetFamily() == AF_INET6) sprintf(addr_wrap, "[%s]", addr); else sprintf(addr_wrap, "%s", addr); RNBSocket server_socket; auto result = server_socket.Listen(addr, 0, ServerCallbackv4, (const void *)addr_wrap); ASSERT_TRUE(result == rnb_success); result = server_socket.Write(hello.c_str(), hello.length()); ASSERT_TRUE(result == rnb_success); std::string bye; result = server_socket.Read(bye); ASSERT_TRUE(result == rnb_success); ASSERT_EQ(bye, goodbye); int exit_status; wait(&exit_status); ASSERT_EQ(exit_status, 0); } TEST(RNBSocket, LoopBackListenIPv4) { TestSocketListen("127.0.0.1"); } TEST(RNBSocket, LoopBackListenIPv6) { TestSocketListen("::1"); } +TEST(RNBSocket, AnyListen) { TestSocketListen("*"); } + void TestSocketConnect(const char *addr) { // Skip IPv6 tests if there isn't a valid interafce auto addresses = lldb_private::SocketAddress::GetAddressInfo( addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); if (addresses.size() == 0) return; char addr_wrap[256]; if (addresses.front().GetFamily() == AF_INET6) sprintf(addr_wrap, "[%s]:0", addr); else sprintf(addr_wrap, "%s:0", addr); Socket *server_socket; Predicate port_predicate; port_predicate.SetValue(0, eBroadcastNever); Status err = Socket::TcpListen(addr_wrap, false, server_socket, &port_predicate); ASSERT_FALSE(err.Fail()); auto port = ((TCPSocket *)server_socket)->GetLocalPortNumber(); auto child_pid = fork(); if (child_pid != 0) { RNBSocket client_socket; auto result = client_socket.Connect(addr, port); ASSERT_TRUE(result == rnb_success); result = client_socket.Write(hello.c_str(), hello.length()); ASSERT_TRUE(result == rnb_success); std::string bye; result = client_socket.Read(bye); ASSERT_TRUE(result == rnb_success); ASSERT_EQ(bye, goodbye); } else { Socket *connected_socket; err = server_socket->Accept(connected_socket); if (err.Fail()) { llvm::errs() << err.AsCString(); abort(); } char buffer[32]; size_t read_size = 32; err = connected_socket->Read((void *)&buffer[0], read_size); if (err.Fail()) { llvm::errs() << err.AsCString(); abort(); } std::string Recv(&buffer[0], read_size); if (Recv != hello) { llvm::errs() << err.AsCString(); abort(); } size_t write_size = goodbye.length(); err = connected_socket->Write(goodbye.c_str(), write_size); if (err.Fail()) { llvm::errs() << err.AsCString(); abort(); } if (write_size != goodbye.length()) { llvm::errs() << err.AsCString(); abort(); } exit(0); } int exit_status; wait(&exit_status); ASSERT_EQ(exit_status, 0); } TEST(RNBSocket, LoopBackConnectIPv4) { TestSocketConnect("127.0.0.1"); } TEST(RNBSocket, LoopBackConnectIPv6) { TestSocketConnect("::1"); }