diff options
Diffstat (limited to 'tools')
103 files changed, 7072 insertions, 1471 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 75c9c15c487d..8be67393bef0 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,12 +1,15 @@ +add_subdirectory(argdumper) +add_subdirectory(driver) +add_subdirectory(intel-features) +add_subdirectory(lldb-mi) +add_subdirectory(lldb-test) +add_subdirectory(lldb-vscode) + if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(darwin-debug) add_subdirectory(debugserver) endif() -add_subdirectory(argdumper) -add_subdirectory(driver) -add_subdirectory(lldb-mi) -if (LLDB_CAN_USE_LLDB_SERVER) + +if (LLDB_CAN_USE_LLDB_SERVER AND NOT SKIP_LLDB_SERVER_BUILD) add_subdirectory(lldb-server) endif() -add_subdirectory(intel-features) -add_subdirectory(lldb-test) diff --git a/tools/argdumper/CMakeLists.txt b/tools/argdumper/CMakeLists.txt index a59a4e7a8514..71c73cc195c2 100644 --- a/tools/argdumper/CMakeLists.txt +++ b/tools/argdumper/CMakeLists.txt @@ -1,4 +1,4 @@ -add_lldb_tool(lldb-argdumper INCLUDE_IN_SUITE +add_lldb_tool(lldb-argdumper argdumper.cpp LINK_LIBS diff --git a/tools/darwin-debug/CMakeLists.txt b/tools/darwin-debug/CMakeLists.txt index c2b5cb486483..5be6e4ee4598 100644 --- a/tools/darwin-debug/CMakeLists.txt +++ b/tools/darwin-debug/CMakeLists.txt @@ -1,3 +1,3 @@ -add_lldb_tool(darwin-debug INCLUDE_IN_SUITE +add_lldb_tool(darwin-debug darwin-debug.cpp ) diff --git a/tools/darwin-debug/darwin-debug.cpp b/tools/darwin-debug/darwin-debug.cpp index e754ded474c4..31b04bcde9be 100644 --- a/tools/darwin-debug/darwin-debug.cpp +++ b/tools/darwin-debug/darwin-debug.cpp @@ -24,7 +24,7 @@ //---------------------------------------------------------------------- #if defined(__APPLE__) -#include <crt_externs.h> // for _NSGetEnviron() +#include <crt_externs.h> #include <getopt.h> #include <limits.h> #include <mach/machine.h> diff --git a/tools/debugserver/CMakeLists.txt b/tools/debugserver/CMakeLists.txt index ae436b8f07c3..1dc32434ba40 100644 --- a/tools/debugserver/CMakeLists.txt +++ b/tools/debugserver/CMakeLists.txt @@ -8,8 +8,9 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) "${CMAKE_SOURCE_DIR}/../../cmake" "${CMAKE_SOURCE_DIR}/../../cmake/modules" ) - + include(LLDBStandalone) + include(debugserverConfig) include(AddLLDB) set(LLDB_SOURCE_DIR "${CMAKE_SOURCE_DIR}/../../") diff --git a/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/tools/debugserver/debugserver.xcodeproj/project.pbxproj index 92b41b1873ab..f4267b7633a2 100644 --- a/tools/debugserver/debugserver.xcodeproj/project.pbxproj +++ b/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -710,12 +710,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; STRIP_INSTALLED_PRODUCT = NO; @@ -749,13 +746,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; STRIPFLAGS = "-x"; @@ -790,12 +784,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 1; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ""; STRIPFLAGS = "-x"; STRIP_STYLE = debugging; @@ -828,20 +819,15 @@ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; INSTALL_PATH = /usr/bin; "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "$(LLDB_ENERGY_CFLAGS)", "-DDT_VARIANT_$(DT_VARIANT)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*]" = ( @@ -851,8 +837,6 @@ "-DWITH_BKS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -871,7 +855,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -880,7 +863,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; @@ -916,20 +898,15 @@ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -939,8 +916,6 @@ "-DWITH_FBS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -959,7 +934,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -967,7 +941,6 @@ __info_plist, "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; @@ -1003,20 +976,15 @@ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1026,8 +994,6 @@ "-DWITH_BKS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -1046,7 +1012,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1055,7 +1020,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; @@ -1107,21 +1071,16 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = /usr/local/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1130,7 +1089,6 @@ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", "$(LLDB_ENERGY_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = ""; @@ -1191,8 +1149,6 @@ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; OTHER_CFLAGS = ( "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wparentheses", @@ -1215,7 +1171,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = "debugserver-nonui"; @@ -1251,7 +1206,6 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; INSTALL_PATH = /usr/local/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; @@ -1259,8 +1213,6 @@ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; OTHER_CFLAGS = ( "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", "$(LLDB_OS_LOG_CFLAGS)", ); @@ -1269,7 +1221,6 @@ "-DOS_OBJECT_USE_OBJC=0", "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; @@ -1279,7 +1230,6 @@ Foundation, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1288,7 +1238,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = "debugserver-nonui"; @@ -1391,7 +1340,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; @@ -1426,20 +1374,15 @@ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1449,8 +1392,6 @@ "-DWITH_BKS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -1468,7 +1409,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1477,7 +1417,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; @@ -1516,7 +1455,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; @@ -1551,19 +1489,14 @@ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1573,8 +1506,6 @@ "-DWITH_BKS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -1592,7 +1523,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1601,7 +1531,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; @@ -1716,7 +1645,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; @@ -1751,20 +1679,15 @@ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = ""; - LLDB_COMPRESSION_LDFLAGS = ""; + LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1774,8 +1697,6 @@ "-DWITH_FBS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -1794,7 +1715,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1803,7 +1723,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; @@ -1841,7 +1760,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - LLDB_COMPRESSION_CFLAGS = "-DHAVE_LIBCOMPRESSION=1"; LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)"; LLDB_USE_OS_LOG = 0; @@ -1877,20 +1795,15 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; INSTALL_PATH = /usr/bin; - LLDB_COMPRESSION_CFLAGS = ""; - LLDB_COMPRESSION_LDFLAGS = ""; + LLDB_COMPRESSION_LDFLAGS = "-lcompression"; LLDB_DEBUGSERVER = 1; LLDB_ENERGY_CFLAGS = ""; "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY"; LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample"; - LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; - LLDB_ZLIB_LDFLAGS = "-lz"; OTHER_CFLAGS = ( "-Wparentheses", "-DDT_VARIANT_$(DT_VARIANT)", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", ); "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( @@ -1900,8 +1813,6 @@ "-DWITH_BKS", "-DOS_OBJECT_USE_OBJC=0", "$(LLDB_ENERGY_CFLAGS)", - "$(LLDB_COMPRESSION_CFLAGS)", - "$(LLDB_ZLIB_CFLAGS)", "$(LLDB_OS_LOG_CFLAGS)", "-isystem", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", @@ -1919,7 +1830,6 @@ MobileCoreServices, "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( "-sectcreate", @@ -1928,7 +1838,6 @@ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", "$(LLDB_ENERGY_LDFLAGS)", "$(LLDB_COMPRESSION_LDFLAGS)", - "$(LLDB_ZLIB_LDFLAGS)", ); OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; PRODUCT_NAME = debugserver; diff --git a/tools/debugserver/source/CMakeLists.txt b/tools/debugserver/source/CMakeLists.txt index ec136039d349..860a0289d22c 100644 --- a/tools/debugserver/source/CMakeLists.txt +++ b/tools/debugserver/source/CMakeLists.txt @@ -5,7 +5,6 @@ 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") @@ -94,32 +93,121 @@ set(lldbDebugserverCommonSources add_library(lldbDebugserverCommon ${lldbDebugserverCommonSources}) +# LLDB-specific identity, currently used for code signing debugserver. +set(LLDB_CODESIGN_IDENTITY "" CACHE STRING + "Override code sign identity for debugserver and for use in tests; falls back to LLVM_CODESIGNING_IDENTITY if set or lldb_codesign otherwise (Darwin only)") -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 "") - set(DEBUGSERVER_PATH ${LLVM_RUNTIME_OUTPUT_INTDIR}/debugserver${CMAKE_EXECUTABLE_SUFFIX} CACHE PATH "Path to debugserver.") - set(SKIP_DEBUGSERVER OFF CACHE BOOL "Skip building the in-tree debug server") +# Determine which identity to use and store it in the separate cache entry. +# We will query it later for LLDB_TEST_COMMON_ARGS. +if(LLDB_CODESIGN_IDENTITY) + set(LLDB_CODESIGN_IDENTITY_USED ${LLDB_CODESIGN_IDENTITY} CACHE INTERNAL "" FORCE) +elseif(LLVM_CODESIGNING_IDENTITY) + set(LLDB_CODESIGN_IDENTITY_USED ${LLVM_CODESIGNING_IDENTITY} CACHE INTERNAL "" FORCE) else() + set(LLDB_CODESIGN_IDENTITY_USED lldb_codesign CACHE INTERNAL "" FORCE) +endif() + +# Override locally, so the identity is used for targets created in this scope. +set(LLVM_CODESIGNING_IDENTITY ${LLDB_CODESIGN_IDENTITY_USED}) + +option(LLDB_NO_DEBUGSERVER "Disable the debugserver target" OFF) +option(LLDB_USE_SYSTEM_DEBUGSERVER "Use the system's debugserver instead of building it from source (Darwin only)." OFF) + +# Incompatible options +if(LLDB_NO_DEBUGSERVER AND LLDB_USE_SYSTEM_DEBUGSERVER) + message(FATAL_ERROR "Inconsistent options: LLDB_NO_DEBUGSERVER and LLDB_USE_SYSTEM_DEBUGSERVER") +endif() + +# Try to locate the system debugserver. +# Subsequent feasibility checks depend on it. +if(APPLE AND CMAKE_HOST_APPLE) execute_process( COMMAND xcode-select -p - OUTPUT_VARIABLE XCODE_DEV_DIR) - string(STRIP ${XCODE_DEV_DIR} XCODE_DEV_DIR) - if(EXISTS "${XCODE_DEV_DIR}/../SharedFrameworks/LLDB.framework/") - set(DEBUGSERVER_PATH - "${XCODE_DEV_DIR}/../SharedFrameworks/LLDB.framework/Resources/debugserver" CACHE PATH "Path to debugserver.") - elseif(EXISTS "${XCODE_DEV_DIR}/Library/PrivateFrameworks/LLDB.framework/") - set(DEBUGSERVER_PATH - "${XCODE_DEV_DIR}/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver" CACHE PATH "Path to debugserver.") + OUTPUT_VARIABLE xcode_dev_dir) + string(STRIP ${xcode_dev_dir} xcode_dev_dir) + + set(debugserver_rel_path "LLDB.framework/Resources/debugserver") + set(debugserver_shared "${xcode_dev_dir}/../SharedFrameworks/${debugserver_rel_path}") + set(debugserver_private "${xcode_dev_dir}/Library/PrivateFrameworks/${debugserver_rel_path}") + + if(EXISTS ${debugserver_shared}) + set(system_debugserver ${debugserver_shared}) + elseif(EXISTS ${debugserver_private}) + set(system_debugserver ${debugserver_private}) + endif() +endif() + +# Handle unavailability +if(LLDB_USE_SYSTEM_DEBUGSERVER) + if(system_debugserver) + set(use_system_debugserver ON) + elseif(APPLE AND CMAKE_HOST_APPLE) + # Binary not found on system. Keep cached variable, to try again on reconfigure. + message(SEND_ERROR + "LLDB_USE_SYSTEM_DEBUGSERVER option set, but no debugserver found in:\ + ${debugserver_shared}\ + ${debugserver_private}") else() - message(SEND_ERROR "Cannot find debugserver on system.") + # Non-Apple target platform or non-Darwin host. Reset invalid cached variable. + message(WARNING "Reverting invalid option LLDB_USE_SYSTEM_DEBUGSERVER (Darwin only)") + set(LLDB_USE_SYSTEM_DEBUGSERVER OFF CACHE BOOL "" FORCE) endif() - set(SKIP_DEBUGSERVER ON CACHE BOOL "Skip building the in-tree debug server") +elseif(NOT LLDB_NO_DEBUGSERVER) + # Default case: on Darwin we need the right code signing ID. + # See lldb/docs/code-signing.txt for details. + if(CMAKE_HOST_APPLE AND NOT LLVM_CODESIGNING_IDENTITY STREQUAL "lldb_codesign") + set(problem "Cannot code sign debugserver with LLVM_CODESIGNING_IDENTITY '${LLVM_CODESIGNING_IDENTITY}'.") + set(advice "Pass -DLLDB_CODESIGN_IDENTITY=lldb_codesign to override the LLVM value for debugserver.") + if(system_debugserver) + set(effect "Will fall back to system's debugserver.") + set(use_system_debugserver ON) + else() + set(effect "debugserver will not be available.") + endif() + message(WARNING "${problem} ${effect} ${advice}") + else() + set(build_and_sign_debugserver ON) + endif() +endif() + +# TODO: We don't use the $<TARGET_FILE:debugserver> generator expression here, +# because the value of DEBUGSERVER_PATH is used to build LLDB_DOTEST_ARGS, +# which is used for configuring lldb-dotest.in, which does not have a generator +# step at the moment. +set(default_debugserver_path "${LLVM_RUNTIME_OUTPUT_INTDIR}/debugserver${CMAKE_EXECUTABLE_SUFFIX}") + +# Remember where debugserver binary goes and whether or not we have to test it. +set(DEBUGSERVER_PATH "" CACHE FILEPATH "Path to debugserver") +set(SKIP_TEST_DEBUGSERVER OFF CACHE BOOL "Building the in-tree debugserver was skipped") + +# Reset values in all cases in order to correctly support reconfigurations. +if(use_system_debugserver) + add_custom_target(debugserver + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${system_debugserver} ${LLVM_RUNTIME_OUTPUT_INTDIR} + COMMENT "Copying the system debugserver to LLDB's binaries directory.") + + # Don't test debugserver itself. + # Tests that require debugserver will use the copy. + set(DEBUGSERVER_PATH ${default_debugserver_path} CACHE FILEPATH "" FORCE) + set(SKIP_TEST_DEBUGSERVER ON CACHE BOOL "" FORCE) + + message(STATUS "Copy system debugserver from: ${system_debugserver}") +elseif(build_and_sign_debugserver) + # Build, sign and test debugserver (below) + set(DEBUGSERVER_PATH ${default_debugserver_path} CACHE FILEPATH "" FORCE) + set(SKIP_TEST_DEBUGSERVER OFF CACHE BOOL "" FORCE) + + message(STATUS "lldb debugserver: ${DEBUGSERVER_PATH}") +else() + # No tests for debugserver, no tests that require it. + set(DEBUGSERVER_PATH "" CACHE FILEPATH "" FORCE) + set(SKIP_TEST_DEBUGSERVER ON CACHE BOOL "" FORCE) + + message(STATUS "lldb debugserver will not be available.") endif() -message(STATUS "Path to the lldb debugserver: ${DEBUGSERVER_PATH}") -if (APPLE) +if(APPLE) if(IOS) find_library(BACKBOARD_LIBRARY BackBoardServices PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks) @@ -132,7 +220,7 @@ if (APPLE) find_library(LOCKDOWN_LIBRARY lockdown) if(NOT BACKBOARD_LIBRARY) - set(SKIP_DEBUGSERVER ON CACHE BOOL "Skip building the in-tree debug server" FORCE) + set(SKIP_TEST_DEBUGSERVER ON CACHE BOOL "" FORCE) endif() else() find_library(COCOA_LIBRARY Cocoa) @@ -143,7 +231,16 @@ if(HAVE_LIBCOMPRESSION) set(LIBCOMPRESSION compression) endif() -if(NOT SKIP_DEBUGSERVER) +if(LLDB_USE_ENTITLEMENTS) + if(IOS) + set(entitlements ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-entitlements.plist) + else() + # Same entitlements file as used for lldb-server + set(entitlements ${LLDB_SOURCE_DIR}/resources/debugserver-macosx-entitlements.plist) + endif() +endif() + +if(build_and_sign_debugserver) target_link_libraries(lldbDebugserverCommon INTERFACE ${COCOA_LIBRARY} ${CORE_FOUNDATION_LIBRARY} @@ -161,11 +258,14 @@ if(NOT SKIP_DEBUGSERVER) COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION) endif() set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources}) - add_lldb_tool(debugserver INCLUDE_IN_SUITE + add_lldb_tool(debugserver debugserver.cpp LINK_LIBS lldbDebugserverCommon + + ENTITLEMENTS + ${entitlements} ) if(IOS) set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_DEFINITIONS @@ -203,56 +303,8 @@ if(IOS) LINK_LIBS lldbDebugserverCommon_NonUI - ) -endif() - -set(entitlements_xml ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-macosx-entitlements.plist) -if(IOS) - set(entitlements_xml ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-entitlements.plist) -else() - set(entitlements_xml ${CMAKE_CURRENT_SOURCE_DIR}/../../../resources/debugserver-macosx-entitlements.plist) -endif() - -set(LLDB_USE_ENTITLEMENTS_Default On) -option(LLDB_USE_ENTITLEMENTS "Use entitlements when codesigning (Defaults Off when using lldb_codesign identity, otherwise On)" ${LLDB_USE_ENTITLEMENTS_Default}) -if (SKIP_DEBUGSERVER) - if (CMAKE_HOST_APPLE) - # If we haven't built a signed debugserver, copy the one from the system. - add_custom_target(debugserver - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DEBUGSERVER_PATH} ${CMAKE_BINARY_DIR}/bin - VERBATIM - COMMENT "Copying the system debugserver to LLDB's binaries directory.") - endif() -else() - if(LLDB_USE_ENTITLEMENTS) - set(entitlements_flags --entitlements ${entitlements_xml}) - endif() - 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} - ${entitlements_flags} - $<TARGET_FILE:debugserver> - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin - ) - if(IOS) - add_custom_command(TARGET debugserver-nonui - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} - codesign --force --sign ${LLDB_CODESIGN_IDENTITY} - ${entitlements_flags} - $<TARGET_FILE:debugserver> - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ENTITLEMENTS + ${entitlements} ) - endif() endif() - - - - diff --git a/tools/debugserver/source/DNBRegisterInfo.cpp b/tools/debugserver/source/DNBRegisterInfo.cpp index b85b39378183..d0f6063fbce0 100644 --- a/tools/debugserver/source/DNBRegisterInfo.cpp +++ b/tools/debugserver/source/DNBRegisterInfo.cpp @@ -156,6 +156,7 @@ void DNBRegisterValueClass::Dump(const char *pre, const char *post) const { DNBLogError( "unsupported vector format %d, defaulting to hex bytes.", info.format); + [[clang::fallthrough]]; case VectorOfUInt8: snprintf(str, sizeof(str), "%s", "uint8 { "); pos = str + strlen(str); diff --git a/tools/debugserver/source/JSONGenerator.h b/tools/debugserver/source/JSONGenerator.h index 7af6ae60c5f7..0ac5e0bb768c 100644 --- a/tools/debugserver/source/JSONGenerator.h +++ b/tools/debugserver/source/JSONGenerator.h @@ -10,8 +10,6 @@ #ifndef __JSONGenerator_h_ #define __JSONGenerator_h_ -// C Includes -// C++ Includes #include <iomanip> #include <sstream> @@ -184,7 +182,7 @@ public: void SetValue(bool value) { m_value = value; } void Dump(std::ostream &s) const override { - if (m_value == true) + if (m_value) s << "true"; else s << "false"; @@ -264,7 +262,7 @@ public: s << "{"; for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter) { - if (have_printed_one_elem == false) { + if (!have_printed_one_elem) { have_printed_one_elem = true; } else { s << ","; diff --git a/tools/debugserver/source/MacOSX/Genealogy.cpp b/tools/debugserver/source/MacOSX/Genealogy.cpp index 22ff52abaa47..1473a53fcbea 100644 --- a/tools/debugserver/source/MacOSX/Genealogy.cpp +++ b/tools/debugserver/source/MacOSX/Genealogy.cpp @@ -74,8 +74,7 @@ Genealogy::GetGenealogyInfoForThread(pid_t pid, nub_thread_t tid, // (else we'll need to hit the timeout for every thread we're asked about.) // We'll try again at the next public stop. - if (m_thread_activities.size() == 0 && - m_diagnosticd_call_timed_out == false) { + if (m_thread_activities.size() == 0 && !m_diagnosticd_call_timed_out) { GetActivities(pid, thread_list, task); } std::map<nub_thread_t, ThreadActivitySP>::const_iterator search; diff --git a/tools/debugserver/source/MacOSX/MachException.cpp b/tools/debugserver/source/MacOSX/MachException.cpp index da2b2fe92980..01e5892d487d 100644 --- a/tools/debugserver/source/MacOSX/MachException.cpp +++ b/tools/debugserver/source/MacOSX/MachException.cpp @@ -386,24 +386,29 @@ void MachException::Data::Dump() const { } } -#define PREV_EXC_MASK_ALL \ - (EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | \ - EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT | \ - EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT | \ - EXC_MASK_MACHINE) - -// Don't listen for EXC_RESOURCE, it should really get handled by the system -// handler. - -#ifndef EXC_RESOURCE -#define EXC_RESOURCE 11 -#endif - -#ifndef EXC_MASK_RESOURCE -#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) -#endif - -#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE) +// The EXC_MASK_ALL value hard-coded here so that lldb can be built +// on a new OS with an older deployment target . The new OS may have +// an addition to its EXC_MASK_ALL that the old OS will not recognize - +// <mach/exception_types.h> doesn't vary the value based on the deployment +// target. So we need a known set of masks that can be assumed to be +// valid when running on an older OS. We'll fall back to trying +// PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is +// not recognized. + +#define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \ + EXC_MASK_BAD_INSTRUCTION | \ + EXC_MASK_ARITHMETIC | \ + EXC_MASK_EMULATION | \ + EXC_MASK_SOFTWARE | \ + EXC_MASK_BREAKPOINT | \ + EXC_MASK_SYSCALL | \ + EXC_MASK_MACH_SYSCALL | \ + EXC_MASK_RPC_ALERT | \ + EXC_MASK_RESOURCE | \ + EXC_MASK_GUARD | \ + EXC_MASK_MACHINE) + +#define LLDB_EXC_MASK EXC_MASK_ALL kern_return_t MachException::PortInfo::Save(task_t task) { DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, @@ -485,9 +490,21 @@ const char *MachException::Name(exception_type_t exc_type) { return "EXC_MACH_SYSCALL"; case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; -#ifdef EXC_CRASH case EXC_CRASH: return "EXC_CRASH"; + case EXC_RESOURCE: + return "EXC_RESOURCE"; +#ifdef EXC_GUARD + case EXC_GUARD: + return "EXC_GUARD"; +#endif +#ifdef EXC_CORPSE_NOTIFY + case EXC_CORPSE_NOTIFY: + return "EXC_CORPSE_NOTIFY"; +#endif +#ifdef EXC_CORPSE_VARIANT_BIT + case EXC_CORPSE_VARIANT_BIT: + return "EXC_CORPSE_VARIANT_BIT"; #endif default: break; diff --git a/tools/debugserver/source/MacOSX/MachProcess.mm b/tools/debugserver/source/MacOSX/MachProcess.mm index 4ddc5f8b10dc..a3b905d05150 100644 --- a/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/tools/debugserver/source/MacOSX/MachProcess.mm @@ -783,8 +783,8 @@ JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON( uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr); image_info_dict_sp->AddStringItem("uuid", uuidstr); - if (image_infos[i].macho_info.min_version_os_name.empty() == false && - image_infos[i].macho_info.min_version_os_version.empty() == false) { + if (!image_infos[i].macho_info.min_version_os_name.empty() && + !image_infos[i].macho_info.min_version_os_version.empty()) { image_info_dict_sp->AddStringItem( "min_version_os_name", image_infos[i].macho_info.min_version_os_name); image_info_dict_sp->AddStringItem( @@ -803,6 +803,8 @@ JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON( (uint32_t)image_infos[i].macho_info.mach_header.cpusubtype); mach_header_dict_sp->AddIntegerItem( "filetype", image_infos[i].macho_info.mach_header.filetype); + mach_header_dict_sp->AddIntegerItem ("flags", + image_infos[i].macho_info.mach_header.flags); // DynamicLoaderMacOSX doesn't currently need these fields, so // don't send them. @@ -810,8 +812,6 @@ JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON( // image_infos[i].macho_info.mach_header.ncmds); // mach_header_dict_sp->AddIntegerItem ("sizeofcmds", // image_infos[i].macho_info.mach_header.sizeofcmds); - // mach_header_dict_sp->AddIntegerItem ("flags", - // image_infos[i].macho_info.mach_header.flags); image_info_dict_sp->AddItem("mach_header", mach_header_dict_sp); JSONGenerator::ArraySP segments_sp(new JSONGenerator::Array()); @@ -1602,7 +1602,7 @@ nub_size_t MachProcess::WriteMemory(nub_addr_t addr, nub_size_t size, void MachProcess::ReplyToAllExceptions() { PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); - if (m_exception_messages.empty() == false) { + if (!m_exception_messages.empty()) { MachException::Message::iterator pos; MachException::Message::iterator begin = m_exception_messages.begin(); MachException::Message::iterator end = m_exception_messages.end(); @@ -1774,7 +1774,7 @@ bool MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) { if (bp->IsHardware()) { bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint(bp); - if (hw_disable_result == true) { + if (hw_disable_result) { bp->SetEnabled(false); // Let the thread list know that a breakpoint has been modified if (remove) { @@ -1909,7 +1909,7 @@ bool MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) { if (wp->IsHardware()) { bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint(wp); - if (hw_disable_result == true) { + if (hw_disable_result) { wp->SetEnabled(false); if (remove) m_watchpoints.Remove(addr); @@ -2179,7 +2179,7 @@ task_t MachProcess::ExceptionMessageBundleComplete() { m_thread_list.Dump(); bool step_more = false; - if (m_thread_list.ShouldStop(step_more) && auto_resume == false) { + if (m_thread_list.ShouldStop(step_more) && !auto_resume) { // Wait for the eEventProcessRunningStateChanged event to be reset // before changing state to stopped to avoid race condition with // very fast start/stops diff --git a/tools/debugserver/source/MacOSX/MachTask.h b/tools/debugserver/source/MacOSX/MachTask.h index 1e0e2af9a92b..1fe74ddec56c 100644 --- a/tools/debugserver/source/MacOSX/MachTask.h +++ b/tools/debugserver/source/MacOSX/MachTask.h @@ -18,14 +18,10 @@ #ifndef __MachTask_h__ #define __MachTask_h__ -// C Includes #include <mach/mach.h> #include <sys/socket.h> -// C++ Includes #include <map> #include <string> -// Other libraries and framework includes -// Project includes #include "DNBDefs.h" #include "MachException.h" #include "MachVMMemory.h" diff --git a/tools/debugserver/source/MacOSX/MachThread.cpp b/tools/debugserver/source/MacOSX/MachThread.cpp index fc97825786a0..062e1c3d9edf 100644 --- a/tools/debugserver/source/MacOSX/MachThread.cpp +++ b/tools/debugserver/source/MacOSX/MachThread.cpp @@ -83,7 +83,7 @@ bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; - if (MachPortNumberIsValid(m_mach_port_number) == false) + if (!MachPortNumberIsValid(m_mach_port_number)) return false; integer_t times_to_resume; @@ -121,7 +121,7 @@ bool MachThread::RestoreSuspendCountAfterStop() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; - if (MachPortNumberIsValid(m_mach_port_number) == false) + if (!MachPortNumberIsValid(m_mach_port_number)) return false; if (m_suspend_count > 0) { diff --git a/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/tools/debugserver/source/MacOSX/MachVMRegion.cpp index c011c133ac38..172fc7867b57 100644 --- a/tools/debugserver/source/MacOSX/MachVMRegion.cpp +++ b/tools/debugserver/source/MacOSX/MachVMRegion.cpp @@ -166,10 +166,7 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) { // doesn't mean that "addr" is in the range. The data in this object will // be valid though, so you could see where the next region begins. So we // return false, yet leave "m_err" with a successfull return code. - if ((addr < m_start) || (addr >= (m_start + m_size))) - return false; - - return true; + return !((addr < m_start) || (addr >= (m_start + m_size))); } uint32_t MachVMRegion::GetDNBPermissions() const { diff --git a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp index adcd65002191..ba37a328ecf3 100644 --- a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp +++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -889,10 +889,7 @@ bool DNBArchImplI386::RollbackTransForHWP() { LOG_WATCHPOINTS, "DNBArchImplI386::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret); - if (kret == KERN_SUCCESS) - return true; - else - return false; + return kret == KERN_SUCCESS; } bool DNBArchImplI386::FinishTransForHWP() { m_2pc_trans_state = Trans_Done; @@ -918,7 +915,7 @@ uint32_t DNBArchImplI386::EnableHardwareWatchpoint(nub_addr_t addr, return INVALID_NUB_HW_INDEX; // We must watch for either read or write - if (read == false && write == false) + if (!read && !write) return INVALID_NUB_HW_INDEX; // Read the debug state diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp index f0a3d2b001b2..2f8ed32fa5d0 100644 --- a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp +++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -844,10 +844,7 @@ bool DNBArchImplX86_64::RollbackTransForHWP() { "DNBArchImplX86_64::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret); - if (kret == KERN_SUCCESS) - return true; - else - return false; + return kret == KERN_SUCCESS; } bool DNBArchImplX86_64::FinishTransForHWP() { m_2pc_trans_state = Trans_Done; @@ -873,7 +870,7 @@ uint32_t DNBArchImplX86_64::EnableHardwareWatchpoint(nub_addr_t addr, return INVALID_NUB_HW_INDEX; // We must watch for either read or write - if (read == false && write == false) + if (!read && !write) return INVALID_NUB_HW_INDEX; // Read the debug state diff --git a/tools/debugserver/source/PThreadMutex.cpp b/tools/debugserver/source/PThreadMutex.cpp index 32db862f6b42..0e9132e380bf 100644 --- a/tools/debugserver/source/PThreadMutex.cpp +++ b/tools/debugserver/source/PThreadMutex.cpp @@ -13,10 +13,6 @@ #include "PThreadMutex.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "DNBTimer.h" #if defined(DEBUG_PTHREAD_MUTEX_DEADLOCKS) diff --git a/tools/debugserver/source/RNBRemote.cpp b/tools/debugserver/source/RNBRemote.cpp index 8ba06be1d6ad..f611ec0d2199 100644 --- a/tools/debugserver/source/RNBRemote.cpp +++ b/tools/debugserver/source/RNBRemote.cpp @@ -42,15 +42,9 @@ #include "RNBSocket.h" #include "StdStringExtractor.h" -#if defined(HAVE_LIBCOMPRESSION) #include <compression.h> -#endif - -#if defined(HAVE_LIBZ) -#include <zlib.h> -#endif -#include <TargetConditionals.h> // for endianness predefines +#include <TargetConditionals.h> #include <iomanip> #include <sstream> #include <unordered_set> @@ -709,53 +703,73 @@ std::string RNBRemote::CompressString(const std::string &orig) { std::vector<uint8_t> encoded_data(encoded_data_buf_size); size_t compressed_size = 0; -#if defined(HAVE_LIBCOMPRESSION) + // Allocate a scratch buffer for libcompression the first + // time we see a different compression type; reuse it in + // all compression_encode_buffer calls so it doesn't need + // to allocate / free its own scratch buffer each time. + // This buffer will only be freed when compression type + // changes; otherwise it will persist until debugserver + // exit. + + static compression_types g_libcompress_scratchbuf_type = compression_types::none; + static void *g_libcompress_scratchbuf = nullptr; + + if (g_libcompress_scratchbuf_type != compression_type) { + if (g_libcompress_scratchbuf) { + free (g_libcompress_scratchbuf); + g_libcompress_scratchbuf = nullptr; + } + size_t scratchbuf_size = 0; + switch (compression_type) { + case compression_types::lz4: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZ4_RAW); + break; + case compression_types::zlib_deflate: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_ZLIB); + break; + case compression_types::lzma: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZMA); + break; + case compression_types::lzfse: + scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZFSE); + break; + default: + break; + } + if (scratchbuf_size > 0) { + g_libcompress_scratchbuf = (void*) malloc (scratchbuf_size); + g_libcompress_scratchbuf_type = compression_type; + } + } + if (compression_type == compression_types::lz4) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, - (const uint8_t *)orig.c_str(), orig.size(), nullptr, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, COMPRESSION_LZ4_RAW); } if (compression_type == compression_types::zlib_deflate) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, - (const uint8_t *)orig.c_str(), orig.size(), nullptr, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, COMPRESSION_ZLIB); } if (compression_type == compression_types::lzma) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, - (const uint8_t *)orig.c_str(), orig.size(), nullptr, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, COMPRESSION_LZMA); } if (compression_type == compression_types::lzfse) { compressed_size = compression_encode_buffer( encoded_data.data(), encoded_data_buf_size, - (const uint8_t *)orig.c_str(), orig.size(), nullptr, + (const uint8_t *)orig.c_str(), orig.size(), + g_libcompress_scratchbuf, COMPRESSION_LZFSE); } -#endif - -#if defined(HAVE_LIBZ) - if (compressed_size == 0 && - compression_type == compression_types::zlib_deflate) { - z_stream stream; - memset(&stream, 0, sizeof(z_stream)); - stream.next_in = (Bytef *)orig.c_str(); - stream.avail_in = (uInt)orig.size(); - stream.next_out = (Bytef *)encoded_data.data(); - stream.avail_out = (uInt)encoded_data_buf_size; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - deflateInit2(&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - int compress_status = deflate(&stream, Z_FINISH); - deflateEnd(&stream); - if (compress_status == Z_STREAM_END && stream.total_out > 0) { - compressed_size = stream.total_out; - } - } -#endif if (compressed_size > 0) { compressed.clear(); @@ -1811,18 +1825,18 @@ rnb_err_t RNBRemote::HandlePacket_qRcmd(const char *p) { } if (*c == '\0') { std::string command = get_identifier(line); - if (command.compare("set") == 0) { + if (command == "set") { std::string variable = get_identifier(line); std::string op = get_operator(line); std::string value = get_value(line); - if (variable.compare("logfile") == 0) { + if (variable == "logfile") { FILE *log_file = fopen(value.c_str(), "w"); if (log_file) { DNBLogSetLogCallback(FileLogCallback, log_file); return SendPacket("OK"); } return SendPacket("E71"); - } else if (variable.compare("logmask") == 0) { + } else if (variable == "logmask") { char *end; errno = 0; uint32_t logmask = @@ -3413,10 +3427,7 @@ static bool RNBRemoteShouldCancelCallback(void *not_used) { RNBRemoteSP remoteSP(g_remoteSP); if (remoteSP.get() != NULL) { RNBRemote *remote = remoteSP.get(); - if (remote->Comm().IsConnected()) - return false; - else - return true; + return !remote->Comm().IsConnected(); } return true; } @@ -3614,13 +3625,13 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { bool enable_compression = false; (void)enable_compression; -#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) +#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) \ + || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) \ + || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) \ + || (defined (TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1) enable_compression = true; #endif -#if defined(HAVE_LIBCOMPRESSION) - // libcompression is weak linked so test if compression_decode_buffer() is - // available if (enable_compression) { strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;" "DefaultCompressionMinSize="); @@ -3628,17 +3639,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize); numbuf[sizeof(numbuf) - 1] = '\0'; strcat(buf, numbuf); - } -#elif defined(HAVE_LIBZ) - if (enable_compression) { - strcat(buf, - ";SupportedCompressions=zlib-deflate;DefaultCompressionMinSize="); - char numbuf[16]; - snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize); - numbuf[sizeof(numbuf) - 1] = '\0'; - strcat(buf, numbuf); - } -#endif + } return SendPacket(buf); } @@ -3690,7 +3691,7 @@ rnb_err_t RNBRemote::HandlePacket_v(const char *p) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse signal in vCont packet"); // Fall through to next case... - + [[clang::fallthrough]]; case 'c': // Continue thread_action.state = eStateRunning; @@ -3703,7 +3704,7 @@ rnb_err_t RNBRemote::HandlePacket_v(const char *p) { return HandlePacket_ILLFORMED( __FILE__, __LINE__, p, "Could not parse signal in vCont packet"); // Fall through to next case... - + [[clang::fallthrough]]; case 's': // Step thread_action.state = eStateStepping; @@ -3817,7 +3818,7 @@ rnb_err_t RNBRemote::HandlePacket_v(const char *p) { attach_failed_due_to_sip = true; } - if (attach_failed_due_to_sip == false) { + if (!attach_failed_due_to_sip) { int csops_flags = 0; int retval = ::csops(pid_attaching_to, CS_OPS_STATUS, &csops_flags, sizeof(csops_flags)); @@ -4230,7 +4231,7 @@ rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) { std::string name; std::string value; while (packet.GetNameColonValue(name, value)) { - if (name.compare("scan_type") == 0) { + if (name == "scan_type") { std::istringstream iss(value); uint32_t int_value = 0; if (iss >> std::hex >> int_value) { @@ -4260,11 +4261,11 @@ rnb_err_t RNBRemote::HandlePacket_SetEnableAsyncProfiling(const char *p) { std::string name; std::string value; while (packet.GetNameColonValue(name, value)) { - if (name.compare("enable") == 0) { + if (name == "enable") { enable = strtoul(value.c_str(), NULL, 10) > 0; - } else if (name.compare("interval_usec") == 0) { + } else if (name == "interval_usec") { interval_usec = strtoul(value.c_str(), NULL, 10); - } else if (name.compare("scan_type") == 0) { + } else if (name == "scan_type") { std::istringstream iss(value); uint32_t int_value = 0; if (iss >> std::hex >> int_value) { @@ -4306,7 +4307,6 @@ rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) { } } -#if defined(HAVE_LIBCOMPRESSION) if (strstr(p, "type:zlib-deflate;") != nullptr) { EnableCompressionNextSendPacket(compression_types::zlib_deflate); m_compression_minsize = new_compression_minsize; @@ -4324,15 +4324,6 @@ rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) { m_compression_minsize = new_compression_minsize; return SendPacket("OK"); } -#endif - -#if defined(HAVE_LIBZ) - if (strstr(p, "type:zlib-deflate;") != nullptr) { - EnableCompressionNextSendPacket(compression_types::zlib_deflate); - m_compression_minsize = new_compression_minsize; - return SendPacket("OK"); - } -#endif return SendPacket("E88"); } @@ -5300,7 +5291,7 @@ RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) { thread_dict_sp->AddStringItem("reason", reason_value); - if (threads_with_valid_stop_info_only == false) { + if (!threads_with_valid_stop_info_only) { const char *thread_name = DNBThreadGetName(pid, tid); if (thread_name && thread_name[0]) thread_dict_sp->AddStringItem("name", thread_name); @@ -5488,7 +5479,7 @@ rnb_err_t RNBRemote::HandlePacket_jThreadExtendedInfo(const char *p) { bool need_to_print_comma = false; - if (thread_activity_sp && timed_out == false) { + if (thread_activity_sp && !timed_out) { const Genealogy::Activity *activity = &thread_activity_sp->current_activity; bool need_vouchers_comma_sep = false; diff --git a/tools/debugserver/source/RNBServices.cpp b/tools/debugserver/source/RNBServices.cpp index b2f4910f8855..89c4c27da1dd 100644 --- a/tools/debugserver/source/RNBServices.cpp +++ b/tools/debugserver/source/RNBServices.cpp @@ -57,9 +57,9 @@ int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) { const pid_t pid = proc_info.kp_proc.p_pid; // Skip zombie processes and processes with unset status - if (kinfo_user_matches == false || // User is acceptable - pid == our_pid || // Skip this process - pid == 0 || // Skip kernel (kernel pid is zero) + if (!kinfo_user_matches || // User is acceptable + pid == our_pid || // Skip this process + pid == 0 || // Skip kernel (kernel pid is zero) proc_info.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... proc_info.kp_proc.p_flag & P_TRACED || // Being debugged? diff --git a/tools/debugserver/source/StdStringExtractor.cpp b/tools/debugserver/source/StdStringExtractor.cpp index d23f9319fa7b..3b400b2a89d2 100644 --- a/tools/debugserver/source/StdStringExtractor.cpp +++ b/tools/debugserver/source/StdStringExtractor.cpp @@ -9,12 +9,8 @@ #include "StdStringExtractor.h" -// C Includes #include <stdlib.h> -// C++ Includes -// Other libraries and framework includes -// Project includes static inline int xdigit_to_sint(char ch) { if (ch >= 'a' && ch <= 'f') diff --git a/tools/debugserver/source/StdStringExtractor.h b/tools/debugserver/source/StdStringExtractor.h index c46978b61f4f..40b57cb08fb5 100644 --- a/tools/debugserver/source/StdStringExtractor.h +++ b/tools/debugserver/source/StdStringExtractor.h @@ -10,13 +10,9 @@ #ifndef utility_StdStringExtractor_h_ #define utility_StdStringExtractor_h_ -// C Includes -// C++ Includes #include <stdint.h> #include <string> -// Other libraries and framework includes -// Project includes // Based on StringExtractor, with the added limitation that this file should not // take a dependency on LLVM, as it is used from debugserver. diff --git a/tools/debugserver/source/TTYState.h b/tools/debugserver/source/TTYState.h index ab34015e1a42..88b6d3c2462e 100644 --- a/tools/debugserver/source/TTYState.h +++ b/tools/debugserver/source/TTYState.h @@ -56,4 +56,4 @@ protected: TTYState m_ttystates[2]; }; -#endif
\ No newline at end of file +#endif diff --git a/tools/debugserver/source/debugserver-entitlements.plist b/tools/debugserver/source/debugserver-entitlements.plist index 7f1ae4615019..6d9f44536f94 100644 --- a/tools/debugserver/source/debugserver-entitlements.plist +++ b/tools/debugserver/source/debugserver-entitlements.plist @@ -12,8 +12,6 @@ <true/> <key>com.apple.frontboard.debugapplications</key> <true/> - <key>run-unsigned-code</key> - <true/> <key>seatbelt-profiles</key> <array> <string>debugserver</string> diff --git a/tools/debugserver/source/debugserver.cpp b/tools/debugserver/source/debugserver.cpp index 7ae321ba431b..8afe17bce37d 100644 --- a/tools/debugserver/source/debugserver.cpp +++ b/tools/debugserver/source/debugserver.cpp @@ -9,19 +9,17 @@ #include <arpa/inet.h> #include <asl.h> -#include <crt_externs.h> // for _NSGetEnviron() +#include <crt_externs.h> #include <errno.h> #include <getopt.h> #include <netdb.h> #include <netinet/in.h> -#include <netinet/in.h> #include <netinet/tcp.h> #include <string> #include <sys/select.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <sys/types.h> -#include <sys/types.h> #include <sys/un.h> #include <vector> diff --git a/tools/debugserver/source/libdebugserver.cpp b/tools/debugserver/source/libdebugserver.cpp index 0d27cfd89a3d..34df67521a7c 100644 --- a/tools/debugserver/source/libdebugserver.cpp +++ b/tools/debugserver/source/libdebugserver.cpp @@ -176,7 +176,7 @@ RNBRunLoopMode HandleProcessStateChange(RNBRemoteSP &remote, bool initialize) { case eStateSuspended: case eStateCrashed: case eStateStopped: - if (initialize == false) { + if (!initialize) { // Compare the last stop count to our current notion of a stop count // to make sure we don't notify more than once for a given stop. nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount(); diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 9e01a6b67ea2..9636e1e2c790 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -1,12 +1,6 @@ -if ((CMAKE_SYSTEM_NAME MATCHES "Windows") OR - (CMAKE_SYSTEM_NAME MATCHES "NetBSD" )) - # These targets do not have getopt support, so they rely on the one provided by - # liblldb. However, getopt is not a part of the liblldb interface, so we have - # to link against the constituent libraries manually. Note that this is - # extremely scary as it introduces ODR violations, and it should go away as - # soon as possible. - set(host_lib lldbHost) -endif() +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(LLDBOptionsTableGen) add_lldb_tool(lldb Driver.cpp @@ -14,9 +8,9 @@ add_lldb_tool(lldb LINK_LIBS liblldb - ${host_lib} LINK_COMPONENTS + Option Support ) @@ -24,4 +18,11 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) add_definitions( -DIMPORT_LIBLLDB ) endif() -add_dependencies(lldb ${LLDB_SUITE_TARGET}) +add_dependencies(lldb + LLDBOptionsTableGen + ${tablegen_deps} +) + +if(LLDB_BUILD_FRAMEWORK) + lldb_setup_framework_rpaths_in_tool(lldb) +endif() diff --git a/tools/driver/Driver.cpp b/tools/driver/Driver.cpp index f3391a55e0d1..71a613afd9bf 100644 --- a/tools/driver/Driver.cpp +++ b/tools/driver/Driver.cpp @@ -9,8 +9,31 @@ #include "Driver.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> #include <atomic> +#include <bitset> #include <csignal> +#include <string> +#include <thread> +#include <utility> + #include <fcntl.h> #include <limits.h> #include <stdio.h> @@ -25,39 +48,50 @@ #include <unistd.h> #endif -#include <string> - -#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/ADT/StringRef.h" -#include "llvm/Support/ConvertUTF.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" -#include <thread> - #if !defined(__APPLE__) #include "llvm/Support/DataTypes.h" #endif using namespace lldb; +using namespace llvm; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class LLDBOptTable : public opt::OptTable { +public: + LLDBOptTable() : OptTable(InfoTable) {} +}; +} // namespace 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; +static Driver *g_driver = nullptr; // In the Driver::MainLoop, we change the terminal settings. This function is // added as an atexit handler to make sure we clean them up. @@ -68,402 +102,35 @@ static void reset_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. <architecture> 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 <filename> 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 <path> 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() { + : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) { // 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. <cmd> - // [options-for-level-0] - // <cmd> - // [options-for-level-1] - // etc. - - uint32_t num_options; - uint32_t num_option_sets = 0; +Driver::~Driver() { g_driver = nullptr; } - 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, " [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]"); - } - - fprintf(out, "\n\n"); - - // Now print out all the detailed information about the various options: long - // form, short form and help text: - // -- long_name <argument> - // - short <argument> - // 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 -- <filename> [<ARG1> [<ARG2>]]' 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<struct option> &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. +void Driver::OptionData::AddLocalLLDBInit() { + // 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); + // 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[PATH_MAX]; + local_lldbinit.GetPath(path, sizeof(path)); 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, +void Driver::OptionData::AddInitialCommand(std::string command, CommandPlacement placement, bool is_file, SBError &error) { std::vector<InitialCmdEntry> *command_set; @@ -480,7 +147,7 @@ void Driver::OptionData::AddInitialCommand(const char *command, } if (is_file) { - SBFileSpec file(command); + SBFileSpec file(command.c_str()); if (file.Exists()) command_set->push_back(InitialCmdEntry(command, is_file, false)); else if (file.ResolveExecutableLocation()) { @@ -489,22 +156,21 @@ void Driver::OptionData::AddInitialCommand(const char *command, command_set->push_back(InitialCmdEntry(final_path, is_file, false)); } else error.SetErrorStringWithFormat( - "file specified in --source (-s) option doesn't exist: '%s'", optarg); + "file specified in --source (-s) option doesn't exist: '%s'", + command.c_str()); } 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 nullptr; 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 nullptr; return m_option_data.m_crash_log.c_str(); } @@ -535,7 +201,7 @@ void Driver::WriteCommandsForSourcing(CommandPlacement placement, // 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( + SBStringList strlist = lldb::SBDebugger::GetInternalVariableValue( "target.load-cwd-lldbinit", m_debugger.GetInstanceName()); if (strlist.GetSize() == 1 && strcmp(strlist.GetStringAtIndex(0), "warn") == 0) { @@ -562,7 +228,8 @@ void Driver::WriteCommandsForSourcing(CommandPlacement placement, } bool source_quietly = m_option_data.m_source_quietly || command_entry.source_quietly; - strm.Printf("command source -s %i '%s'\n", source_quietly, command); + strm.Printf("command source -s %i '%s'\n", + static_cast<int>(source_quietly), command); } else strm.Printf("%s\n", command); } @@ -571,308 +238,232 @@ void Driver::WriteCommandsForSourcing(CommandPlacement placement, 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. +// 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::ProcessArgs(const opt::InputArgList &args, bool &exiting) { + SBError error; + m_option_data.AddLocalLLDBInit(); -SBError Driver::ParseArgs(int argc, const char *argv[], FILE *out_fh, - bool &exiting) { - ResetOptionValues(); + // 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); - SBCommandReturnObject result; + if (args.hasArg(OPT_version)) { + m_option_data.m_print_version = true; + } - SBError error; - std::string option_string; - struct option *long_options = NULL; - std::vector<struct option> 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; + if (args.hasArg(OPT_python_path)) { + m_option_data.m_print_python_path = true; + } + + if (args.hasArg(OPT_batch)) { + m_option_data.m_batch = true; + } + + if (auto *arg = args.getLastArg(OPT_core)) { + auto arg_value = arg->getValue(); + SBFileSpec file(arg_value); + if (!file.Exists()) { + error.SetErrorStringWithFormat( + "file specified in --core (-c) option doesn't exist: '%s'", + arg_value); + return error; + } + m_option_data.m_core_file = arg_value; } - BuildGetOptTable(g_options, long_options_vector, num_options); + if (args.hasArg(OPT_editor)) { + m_option_data.m_use_external_editor = true; + } - if (long_options_vector.empty()) - long_options = NULL; - else - long_options = &long_options_vector.front(); + if (args.hasArg(OPT_no_lldbinit)) { + m_debugger.SkipLLDBInitFiles(true); + m_debugger.SkipAppInitFiles(true); + } - if (long_options == NULL) { - error.SetErrorStringWithFormat("invalid long options"); - return error; + if (args.hasArg(OPT_no_use_colors)) { + m_debugger.SetUseColor(false); } - // 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; - } + if (auto *arg = args.getLastArg(OPT_file)) { + auto arg_value = arg->getValue(); + SBFileSpec file(arg_value); + if (file.Exists()) { + m_option_data.m_args.emplace_back(arg_value); + } else if (file.ResolveExecutableLocation()) { + char path[PATH_MAX]; + file.GetPath(path, sizeof(path)); + m_option_data.m_args.emplace_back(path); + } else { + error.SetErrorStringWithFormat( + "file specified in --file (-f) option doesn't exist: '%s'", + arg_value); + return error; } } - // 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. + if (auto *arg = args.getLastArg(OPT_arch)) { + auto arg_value = arg->getValue(); + if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) { + error.SetErrorStringWithFormat( + "invalid architecture in the -a or --arch option: '%s'", arg_value); + return error; + } + } - m_debugger.SkipLLDBInitFiles(false); - m_debugger.SkipAppInitFiles(false); + if (auto *arg = args.getLastArg(OPT_script_language)) { + auto arg_value = arg->getValue(); + m_option_data.m_script_lang = m_debugger.GetScriptingLanguage(arg_value); + } -// 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<char **>(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 (args.hasArg(OPT_no_use_colors)) { + m_option_data.m_debug_mode = true; + } - 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()) { + if (args.hasArg(OPT_no_use_colors)) { + m_debugger.SetUseColor(false); + } + + if (args.hasArg(OPT_source_quietly)) { + m_option_data.m_source_quietly = true; + } + + if (auto *arg = args.getLastArg(OPT_attach_name)) { + auto arg_value = arg->getValue(); + m_option_data.m_process_name = arg_value; + } + + if (args.hasArg(OPT_wait_for)) { + m_option_data.m_wait_for = true; + } + + if (auto *arg = args.getLastArg(OPT_attach_pid)) { + auto arg_value = arg->getValue(); + char *remainder; + m_option_data.m_process_pid = strtol(arg_value, &remainder, 0); + if (remainder == arg_value || *remainder != '\0') { + error.SetErrorStringWithFormat( + "Could not convert process PID: \"%s\" into a pid.", arg_value); + return error; + } + } + + if (auto *arg = args.getLastArg(OPT_repl_language)) { + auto arg_value = arg->getValue(); + m_option_data.m_repl_lang = + SBLanguageRuntime::GetLanguageTypeFromString(arg_value); + if (m_option_data.m_repl_lang == eLanguageTypeUnknown) { + error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"", + arg_value); + return error; + } + } + + if (args.hasArg(OPT_repl)) { + m_option_data.m_repl = true; + } + + if (auto *arg = args.getLastArg(OPT_repl_)) { + m_option_data.m_repl = true; + if (auto arg_value = arg->getValue()) + m_option_data.m_repl_options = arg_value; + } + + // We need to process the options below together as their relative order + // matters. + for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash, + OPT_source, OPT_source_before_file, + OPT_one_line, OPT_one_line_before_file)) { + auto arg_value = arg->getValue(); + if (arg->getOption().matches(OPT_source_on_crash)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line_on_crash)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash, + false, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_source)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_source_before_file)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, + true, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile, + false, error); + if (error.Fail()) + return error; + } + + if (arg->getOption().matches(OPT_one_line_before_file)) { + m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile, + false, error); + if (error.Fail()) return error; + } + } + + if (m_option_data.m_process_name.empty() && + m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) { + + // If the option data args array is empty that means the file was not + // specified with -f and we need to get it from the input args. + if (m_option_data.m_args.empty()) { + if (auto *arg = args.getLastArgNoClaim(OPT_INPUT)) { + m_option_data.m_args.push_back(arg->getAsString((args))); } } + + // Any argument following -- is an argument for the inferior. + if (auto *arg = args.getLastArgNoClaim(OPT_REM)) { + for (auto value : arg->getValues()) + m_option_data.m_args.emplace_back(value); + } + } else if (args.getLastArgNoClaim() != nullptr) { + WithColor::warning() << "program arguments are ignored when attaching.\n"; } - 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()); + if (m_option_data.m_print_version) { + llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n'; exiting = true; - } else if (m_option_data.m_print_python_path) { + return error; + } + + 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); + llvm::outs() << python_path << '\n'; } else - ::fprintf(out_fh, "<PATH TOO LONG>\n"); + llvm::outs() << "<PATH TOO LONG>\n"; } else - ::fprintf(out_fh, "<COULD NOT FIND PATH>\n"); + llvm::outs() << "<COULD NOT FIND PATH>\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; - - if (argc > 0) - ::fprintf(out_fh, - "Warning: program arguments are ignored when attaching.\n"); + return error; } return error; @@ -882,7 +473,7 @@ 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; + ::FILE *commands_file = nullptr; fds[0] = -1; fds[1] = -1; int err = 0; @@ -894,10 +485,13 @@ static ::FILE *PrepareCommandsForSourcing(const char *commands_data, 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<const void *>(commands_data), - static_cast<uint64_t>(commands_size), errno); + WithColor::error() + << format( + "write(%i, %p, %" PRIu64 + ") failed (errno = %i) when trying to open LLDB commands pipe", + fds[WRITE], static_cast<const void *>(commands_data), + static_cast<uint64_t>(commands_size), errno) + << '\n'; } else if (static_cast<size_t>(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 @@ -912,20 +506,20 @@ static ::FILE *PrepareCommandsForSourcing(const char *commands_data, // 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". + if (commands_file != nullptr) { + 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); + WithColor::error() << format("fdopen(%i, \"r\") failed (errno = %i) " + "when trying to open LLDB commands pipe", + fds[READ], errno) + << '\n'; } } } else { - fprintf(stderr, - "error: can't create pipe file descriptors for LLDB commands\n"); + WithColor::error() + << "can't create pipe file descriptors for LLDB commands\n"; } return commands_file; @@ -975,9 +569,9 @@ int Driver::MainLoop() { // 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); + ::setbuf(stdin, nullptr); #endif - ::setbuf(stdout, NULL); + ::setbuf(stdout, nullptr); m_debugger.SetErrorFileHandle(stderr, false); m_debugger.SetOutputFileHandle(stdout, false); @@ -987,7 +581,7 @@ int Driver::MainLoop() { m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); struct winsize window_size; - if (isatty(STDIN_FILENO) && + if ((isatty(STDIN_FILENO) != 0) && ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { if (window_size.ws_col > 0) m_debugger.SetTerminalWidth(window_size.ws_col); @@ -1018,7 +612,7 @@ int Driver::MainLoop() { 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))) + if (lldb::SBDebugger::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 @@ -1066,16 +660,16 @@ int Driver::MainLoop() { bool spawn_thread = false; if (m_option_data.m_repl) { - const char *repl_options = NULL; + const char *repl_options = nullptr; 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); + if ((error_cstr != nullptr) && (error_cstr[0] != 0)) + WithColor::error() << error_cstr << '\n'; else - fprintf(stderr, "error: %u\n", error.GetError()); + WithColor::error() << error.GetError() << '\n'; } } else { // Check if we have any data in the commands stream, and if so, save it to a @@ -1088,12 +682,12 @@ int Driver::MainLoop() { // track that. bool quit_requested = false; bool stopped_for_crash = false; - if (commands_data && commands_size) { + if ((commands_data != nullptr) && (commands_size != 0u)) { int initial_commands_fds[2]; bool success = true; FILE *commands_file = PrepareCommandsForSourcing( commands_data, commands_size, initial_commands_fds); - if (commands_file) { + if (commands_file != nullptr) { m_debugger.SetInputFileHandle(commands_file, true); // Set the debugger into Sync mode when running the command file. @@ -1122,7 +716,7 @@ int Driver::MainLoop() { 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) { + if (commands_file != nullptr) { bool local_quit_requested; bool local_stopped_for_crash; m_debugger.SetInputFileHandle(commands_file, true); @@ -1177,9 +771,9 @@ void Driver::ResizeWindow(unsigned short col) { void sigwinch_handler(int signo) { struct winsize window_size; - if (isatty(STDIN_FILENO) && + if ((isatty(STDIN_FILENO) != 0) && ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) { - if ((window_size.ws_col > 0) && g_driver != NULL) { + if ((window_size.ws_col > 0) && g_driver != nullptr) { g_driver->ResizeWindow(window_size.ws_col); } } @@ -1187,7 +781,7 @@ void sigwinch_handler(int signo) { void sigint_handler(int signo) { static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; - if (g_driver) { + if (g_driver != nullptr) { if (!g_interrupt_sent.test_and_set()) { g_driver->GetDebugger().DispatchInputInterrupt(); g_interrupt_sent.clear(); @@ -1199,7 +793,7 @@ void sigint_handler(int signo) { } void sigtstp_handler(int signo) { - if (g_driver) + if (g_driver != nullptr) g_driver->GetDebugger().SaveInputTerminalState(); signal(signo, SIG_DFL); @@ -1208,7 +802,7 @@ void sigtstp_handler(int signo) { } void sigcont_handler(int signo) { - if (g_driver) + if (g_driver != nullptr) g_driver->GetDebugger().RestoreInputTerminalState(); signal(signo, SIG_DFL); @@ -1216,6 +810,45 @@ void sigcont_handler(int signo) { signal(signo, sigcont_handler); } +static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { + std::string usage_str = tool_name.str() + "options"; + table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false); + + std::string examples = R"___( +EXAMPLES: + The debugger can be started in several modes. + + Passing an executable as a positional argument prepares lldb to debug the + given executable. Arguments passed after -- are considered arguments to the + debugged executable. + + lldb --arch x86_64 /path/to/program -- --arch arvm7 + + Passing one of the attach options causes lldb to immediately attach to the + given process. + + lldb -p <pid> + lldb -n <process-name> + + Passing --repl starts lldb in REPL mode. + + lldb -r + + Passing --core causes lldb to debug the core file. + + lldb -c /path/to/core + + Command options can be combined with either mode and cause lldb to run the + specified commands before or after events, like loading the file or crashing, + in the order provided on the command line. + + lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt' + lldb -S /source/before/file -s /source/after/file + lldb -K /source/before/crash -k /source/after/crash + )___"; + llvm::outs() << examples; +} + int #ifdef _MSC_VER wmain(int argc, wchar_t const *wargv[]) @@ -1234,11 +867,48 @@ main(int argc, char const *argv[]) const char **argv = argvPointers.data(); #endif - llvm::StringRef ToolName = argv[0]; + // Print stack trace on crash. + llvm::StringRef ToolName = llvm::sys::path::filename(argv[0]); llvm::sys::PrintStackTraceOnErrorSignal(ToolName); llvm::PrettyStackTraceProgram X(argc, argv); - SBDebugger::Initialize(); + // Parse arguments. + LLDBOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1); + opt::InputArgList input_args = T.ParseArgs(arg_arr, MAI, MAC); + + if (input_args.hasArg(OPT_help)) { + printHelp(T, ToolName); + return 0; + } + + for (auto *arg : input_args.filtered(OPT_UNKNOWN)) { + WithColor::warning() << "ignoring unknown option: " << arg->getSpelling() + << '\n'; + } + + SBInitializerOptions options; + + if (auto *arg = input_args.getLastArg(OPT_capture)) { + auto arg_value = arg->getValue(); + options.SetReproducerPath(arg_value); + options.SetCaptureReproducer(true); + } + + if (auto *arg = input_args.getLastArg(OPT_replay)) { + auto arg_value = arg->getValue(); + options.SetReplayReproducer(true); + options.SetReproducerPath(arg_value); + } + + SBError error = SBDebugger::Initialize(options); + if (error.Fail()) { + WithColor::error() << "initialization failed: " << error.GetCString() + << '\n'; + return 1; + } SBHostOS::ThreadCreated("<lldb.driver.main-thread>"); @@ -1257,12 +927,11 @@ main(int argc, char const *argv[]) Driver driver; bool exiting = false; - SBError error(driver.ParseArgs(argc, argv, stdout, exiting)); + SBError error(driver.ProcessArgs(input_args, exiting)); if (error.Fail()) { exit_code = 1; - const char *error_cstr = error.GetCString(); - if (error_cstr) - ::fprintf(stderr, "error: %s\n", error_cstr); + if (const char *error_cstr = error.GetCString()) + WithColor::error() << error_cstr << '\n'; } else if (!exiting) { exit_code = driver.MainLoop(); } diff --git a/tools/driver/Driver.h b/tools/driver/Driver.h index 34746a239999..3f2b0759255a 100644 --- a/tools/driver/Driver.h +++ b/tools/driver/Driver.h @@ -11,19 +11,19 @@ #define lldb_Driver_h_ #include "Platform.h" -#include "lldb/Host/PseudoTerminal.h" - -#include <bitset> -#include <set> -#include <string> -#include <vector> #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" -class IOChannel; +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" + +#include <set> +#include <string> +#include <vector> class Driver : public lldb::SBBroadcaster { public: @@ -42,8 +42,7 @@ public: /// @return The exit code that the process should return. int MainLoop(); - lldb::SBError ParseArgs(int argc, const char *argv[], FILE *out_fh, - bool &do_exit); + lldb::SBError ProcessArgs(const llvm::opt::InputArgList &args, bool &exiting); const char *GetFilename() const; @@ -58,60 +57,57 @@ public: bool GetDebugMode() const; - class OptionData { - public: - OptionData(); - ~OptionData(); - - void Clear(); - - void AddInitialCommand(const char *command, CommandPlacement placement, + struct OptionData { + void AddLocalLLDBInit(); + void AddInitialCommand(std::string command, CommandPlacement placement, bool is_file, lldb::SBError &error); - // static OptionDefinition m_cmd_option_table[]; - struct InitialCmdEntry { - InitialCmdEntry(const char *in_contents, bool in_is_file, + InitialCmdEntry(std::string contents, bool in_is_file, bool is_cwd_lldbinit_file_read, bool in_quiet = false) - : contents(in_contents), is_file(in_is_file), - is_cwd_lldbinit_file_read(is_cwd_lldbinit_file_read), - source_quietly(in_quiet) {} + : contents(std::move(contents)), is_file(in_is_file), + source_quietly(in_quiet), + is_cwd_lldbinit_file_read(is_cwd_lldbinit_file_read) {} std::string contents; bool is_file; - bool is_cwd_lldbinit_file_read; // if this is reading ./.lldbinit - so we - // may skip if not permitted bool source_quietly; + + /// Remember if this is reading the local lldbinit file so we can skip it + /// if not permitted. + bool is_cwd_lldbinit_file_read; }; std::vector<std::string> m_args; - lldb::ScriptLanguage m_script_lang; + + lldb::ScriptLanguage m_script_lang = lldb::eScriptLanguageDefault; + lldb::LanguageType m_repl_lang = lldb::eLanguageTypeUnknown; + lldb::pid_t m_process_pid = LLDB_INVALID_PROCESS_ID; + std::string m_core_file; std::string m_crash_log; + std::string m_repl_options; + std::string m_process_name; + std::vector<InitialCmdEntry> m_initial_commands; std::vector<InitialCmdEntry> m_after_file_commands; std::vector<InitialCmdEntry> m_after_crash_commands; - bool m_debug_mode; - bool m_source_quietly; - bool m_print_version; - bool m_print_python_path; - bool m_print_help; - bool m_wait_for; - bool m_repl; - lldb::LanguageType m_repl_lang; - std::string m_repl_options; - std::string m_process_name; - lldb::pid_t m_process_pid; - bool m_use_external_editor; // FIXME: When we have set/show variables we can - // remove this from here. - bool m_batch; - typedef std::set<char> OptionSet; + + bool m_debug_mode = false; + bool m_source_quietly = false; + bool m_print_version = false; + bool m_print_python_path = false; + bool m_wait_for = false; + bool m_repl = false; + bool m_batch = false; + + // FIXME: When we have set/show variables we can remove this from here. + bool m_use_external_editor = false; + + using OptionSet = std::set<char>; OptionSet m_seen_options; }; - static lldb::SBError SetOptionValue(int option_idx, const char *option_arg, - Driver::OptionData &data); - lldb::SBDebugger &GetDebugger() { return m_debugger; } void ResizeWindow(unsigned short col); @@ -119,10 +115,6 @@ public: private: lldb::SBDebugger m_debugger; OptionData m_option_data; - - void ResetOptionValues(); - - void ReadyForCommand(); }; #endif // lldb_Driver_h_ diff --git a/tools/driver/Options.td b/tools/driver/Options.td new file mode 100644 index 000000000000..563c0b54fb98 --- /dev/null +++ b/tools/driver/Options.td @@ -0,0 +1,229 @@ +include "llvm/Option/OptParser.td" + +class F<string name>: Flag<["--", "-"], name>; +class S<string name>: Separate<["--", "-"], name>; +class R<list<string> prefixes, string name> + : Option<prefixes, name, KIND_REMAINING_ARGS>; + +// Attaching options. +def grp_attach : OptionGroup<"attaching">, HelpText<"ATTACHING">; + +def attach_name: Separate<["--", "-"], "attach-name">, + MetaVarName<"<name>">, + HelpText<"Tells the debugger to attach to a process with the given name.">, + Group<grp_attach>; +def: Separate<["-"], "n">, + Alias<attach_name>, + HelpText<"Alias for --attach-name">, + Group<grp_attach>; + +def wait_for: F<"wait-for">, + HelpText<"Tells the debugger to wait for a process with the given pid or name to launch before attaching.">, + Group<grp_attach>; +def: Flag<["-"], "w">, + Alias<wait_for>, + HelpText<"Alias for --wait-for">, + Group<grp_attach>; + +def attach_pid: Separate<["--", "-"], "attach-pid">, + MetaVarName<"<pid>">, + HelpText<"Tells the debugger to attach to a process with the given pid.">, + Group<grp_attach>; +def: Separate<["-"], "p">, + Alias<attach_pid>, + HelpText<"Alias for --attach-pid">, + Group<grp_attach>; + + +// Scripting options. +def grp_scripting : OptionGroup<"scripting">, HelpText<"SCRIPTING">; + +def python_path: F<"python-path">, + HelpText<"Prints out the path to the lldb.py file for this version of lldb.">, + Group<grp_scripting>; +def: Flag<["-"], "P">, + Alias<python_path>, + HelpText<"Alias for --python-path">, + Group<grp_scripting>; + +def script_language: Separate<["--", "-"], "script-language">, + MetaVarName<"<language>">, + HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">, + Group<grp_scripting>; +def: Separate<["-"], "l">, + Alias<script_language>, + HelpText<"Alias for --script-language">, + Group<grp_scripting>; + +// Repl options. +def grp_repl : OptionGroup<"repl">, HelpText<"REPL">; + +def repl: Flag<["--", "-"], "repl">, + HelpText<"Runs lldb in REPL mode with a stub process.">, + Group<grp_repl>; +def: Flag<["-"], "r">, + Alias<repl>, + HelpText<"Alias for --repl">, + Group<grp_repl>; +def repl_: Joined<["--", "-"], "repl=">, + MetaVarName<"<flags>">, + HelpText<"Runs lldb in REPL mode with a stub process with the given flags.">, + Group<grp_repl>; +def: Joined<["-"], "r=">, + MetaVarName<"<flags>">, + Alias<repl_>, + HelpText<"Alias for --repl=<flags>">, + Group<grp_repl>; + +def repl_language: Separate<["--", "-"], "repl-language">, + MetaVarName<"<language>">, + HelpText<"Chooses the language for the REPL.">, + Group<grp_repl>; +def: Separate<["-"], "R">, + Alias<repl_language>, + HelpText<"Alias for --repl-language">, + Group<grp_repl>; + + +// Command options. +def grp_command : OptionGroup<"command">, HelpText<"COMMANDS">; + +def no_lldbinit: F<"no-lldbinit">, + HelpText<"Do not automatically parse any '.lldbinit' files.">, + Group<grp_command>; +def: Flag<["-"], "x">, + Alias<no_lldbinit>, + HelpText<"Alias for --no-lldbinit">, + Group<grp_command>; + +def batch: F<"batch">, + HelpText<"Tells the debugger to run the commands from -s, -S, -o & -O, and then quit.">, + Group<grp_command>; +def: Flag<["-"], "b">, + Alias<batch>, + HelpText<"Alias for --batch">, + Group<grp_command>; + +def source_quietly: F<"source-quietly">, + HelpText<"Tells the debugger to execute this one-line lldb command before any file has been loaded.">, + Group<grp_command>; +def: Flag<["-"], "Q">, + Alias<source_quietly>, + HelpText<"Alias for --source-quietly">, + Group<grp_command>; + +def one_line_on_crash: Separate<["--", "-"], "one-line-on-crash">, + MetaVarName<"<command>">, + HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">, + Group<grp_command>; +def: Separate<["-"], "k">, + Alias<one_line_on_crash>, + HelpText<"Alias for --one-line-on-crash">, + Group<grp_command>; + +def source_on_crash: Separate<["--", "-"], "source-on-crash">, + MetaVarName<"<file>">, + HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">, + Group<grp_command>; +def: Separate<["-"], "K">, + Alias<source_on_crash>, + HelpText<"Alias for --source-on-crash">, + Group<grp_command>; + +def source: Separate<["--", "-"], "source">, + MetaVarName<"<file>">, + HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, after any file has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "s">, + Alias<source>, + HelpText<"Alias for --source">, + Group<grp_command>; + +def source_before_file: Separate<["--", "-"], "source-before-file">, + MetaVarName<"<file>">, + HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, before any file has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "S">, + Alias<source_before_file>, + HelpText<"Alias for --source-before-file">, + Group<grp_command>; + +def one_line: Separate<["--", "-"], "one-line">, + MetaVarName<"<command>">, + HelpText<"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "o">, + Alias<one_line>, + HelpText<"Alias for --one-line">, + Group<grp_command>; + +def one_line_before_file: Separate<["--", "-"], "one-line-before-file">, + MetaVarName<"<command>">, + HelpText<"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded.">, + Group<grp_command>; +def: Separate<["-"], "O">, + Alias<one_line_before_file>, + HelpText<"Alias for --one-line-before-file">, + Group<grp_command>; + + +// General options. +def version: F<"version">, + HelpText<"Prints out the current version number of the LLDB debugger.">; +def: Flag<["-"], "v">, + Alias<version>, + HelpText<"Alias for --version">; + +def help: F<"help">, + HelpText<"Prints out the usage information for the LLDB debugger.">; +def: Flag<["-"], "h">, + Alias<help>, + HelpText<"Alias for --help">; + +def core: Separate<["--", "-"], "core">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to use the full path to <filename> as the core file.">; +def: Separate<["-"], "c">, + Alias<core>, + HelpText<"Alias for --core">; + +def editor: F<"editor">, + HelpText<"Tells the debugger to open source files using the host's \"external editor\" mechanism.">; +def: Flag<["-"], "e">, + Alias<editor>, + HelpText<"Alias for --editor">; + +def no_use_colors: F<"no-use-colors">, + HelpText<"Do not use colors.">; +def: Flag<["-"], "X">, + Alias<no_use_colors>, + HelpText<"Alias for --no-use-color">; + +def file: Separate<["--", "-"], "file">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to use the file <filename> as the program to be debugged.">; +def: Separate<["-"], "f">, + Alias<file>, + HelpText<"Alias for --file">; + +def arch: Separate<["--", "-"], "arch">, + MetaVarName<"<architecture>">, + HelpText<"Tells the debugger to use the specified architecture when starting and running the program.">; +def: Separate<["-"], "a">, + Alias<arch>, + HelpText<"Alias for --arch">; + +def debug: F<"debug">, + HelpText<"Tells the debugger to print out extra information for debugging itself.">; +def: Flag<["-"], "d">, + Alias<debug>, + HelpText<"Alias for --debug">; + +def capture: Separate<["--", "-"], "capture">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to capture a reproducer to <filename>.">; +def replay: Separate<["--", "-"], "replay">, + MetaVarName<"<filename>">, + HelpText<"Tells the debugger to replay a reproducer from <filename>.">; + +def REM : R<["--"], "">; diff --git a/tools/driver/Platform.h b/tools/driver/Platform.h index 521c5a1ccbb5..1bb42f9cfc16 100644 --- a/tools/driver/Platform.h +++ b/tools/driver/Platform.h @@ -12,7 +12,6 @@ #if defined(_WIN32) -#include "lldb/Host/HostGetOpt.h" #include <io.h> #if defined(_MSC_VER) #include <signal.h> @@ -61,7 +60,6 @@ struct timeval { long tv_usec; }; typedef long pid_t; -#define snprintf _snprintf #define PATH_MAX MAX_PATH #endif @@ -74,7 +72,6 @@ extern int tcsetattr(int fd, int optional_actions, extern int tcgetattr(int fildes, struct termios *termios_p); #else -#include "lldb/Host/HostGetOpt.h" #include <inttypes.h> #include <libgen.h> diff --git a/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp index e654f076fb3a..df1221f3ccff 100644 --- a/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp +++ b/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp @@ -12,7 +12,6 @@ #include <cerrno> #include <string> -// Project includes #include "cli-wrapper-mpxtable.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" diff --git a/tools/intel-features/intel-pt/Decoder.cpp b/tools/intel-features/intel-pt/Decoder.cpp index 0c385adc811c..f838ff4c1e6e 100644 --- a/tools/intel-features/intel-pt/Decoder.cpp +++ b/tools/intel-features/intel-pt/Decoder.cpp @@ -7,14 +7,12 @@ // //===----------------------------------------------------------------------===// -// Project includes #include "Decoder.h" // C/C++ Includes #include <cinttypes> #include <cstring> -// Other libraries and framework includes #include "lldb/API/SBModule.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBThread.h" diff --git a/tools/intel-features/intel-pt/Decoder.h b/tools/intel-features/intel-pt/Decoder.h index 3762196058fd..58ebb7e734cb 100644 --- a/tools/intel-features/intel-pt/Decoder.h +++ b/tools/intel-features/intel-pt/Decoder.h @@ -16,7 +16,6 @@ #include <string> #include <vector> -// Project includes, Other libraries and framework includes #include "lldb/API/SBDebugger.h" #include "lldb/API/SBError.h" #include "lldb/API/SBProcess.h" diff --git a/tools/intel-features/intel-pt/PTDecoder.cpp b/tools/intel-features/intel-pt/PTDecoder.cpp index 4f3554e84d3c..72effbe3da79 100644 --- a/tools/intel-features/intel-pt/PTDecoder.cpp +++ b/tools/intel-features/intel-pt/PTDecoder.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -// Project includes #include "PTDecoder.h" #include "Decoder.h" diff --git a/tools/intel-features/intel-pt/PTDecoder.h b/tools/intel-features/intel-pt/PTDecoder.h index 8af8c8672b18..c38057ac715a 100644 --- a/tools/intel-features/intel-pt/PTDecoder.h +++ b/tools/intel-features/intel-pt/PTDecoder.h @@ -13,7 +13,6 @@ // C/C++ Includes #include <vector> -// Project includes, Other libraries and framework includes #include "lldb/API/SBDebugger.h" #include "lldb/API/SBError.h" #include "lldb/API/SBProcess.h" diff --git a/tools/lldb-mi/CMakeLists.txt b/tools/lldb-mi/CMakeLists.txt index f3f4c0097efd..20c031a12004 100644 --- a/tools/lldb-mi/CMakeLists.txt +++ b/tools/lldb-mi/CMakeLists.txt @@ -93,3 +93,7 @@ add_lldb_tool(lldb-mi LINK_COMPONENTS Support ) + +if(LLDB_BUILD_FRAMEWORK) + lldb_setup_framework_rpaths_in_tool(lldb-mi) +endif() diff --git a/tools/lldb-mi/MICmdArgSet.h b/tools/lldb-mi/MICmdArgSet.h index cb321f0204d2..88716a91f0ad 100644 --- a/tools/lldb-mi/MICmdArgSet.h +++ b/tools/lldb-mi/MICmdArgSet.h @@ -9,12 +9,8 @@ #pragma once -// C Includes -// C++ Includes #include <vector> -// Other libraries and framework includes -// Project includes #include "MICmdArgContext.h" #include "MICmnBase.h" diff --git a/tools/lldb-mi/MICmdArgValBase.cpp b/tools/lldb-mi/MICmdArgValBase.cpp index bedd983a06f6..8b1706db93ef 100644 --- a/tools/lldb-mi/MICmdArgValBase.cpp +++ b/tools/lldb-mi/MICmdArgValBase.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "MICmdArgValBase.h" #include "MICmdArgContext.h" #include "MIUtilString.h" diff --git a/tools/lldb-mi/MICmdArgValBase.h b/tools/lldb-mi/MICmdArgValBase.h index 288476082806..1b4582963e57 100644 --- a/tools/lldb-mi/MICmdArgValBase.h +++ b/tools/lldb-mi/MICmdArgValBase.h @@ -9,10 +9,6 @@ #pragma once -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "MICmdArgSet.h" #include "MIUtilString.h" diff --git a/tools/lldb-mi/MICmdArgValConsume.cpp b/tools/lldb-mi/MICmdArgValConsume.cpp index 5a76ab4b56f5..86c4f0912565 100644 --- a/tools/lldb-mi/MICmdArgValConsume.cpp +++ b/tools/lldb-mi/MICmdArgValConsume.cpp @@ -67,7 +67,7 @@ bool CMICmdArgValConsume::Validate(CMICmdArgContext &vwArgContext) { while (it != vecOptions.end()) { const CMIUtilString &rTxt(*it); - if (rTxt.compare("--") == 0) { + if (rTxt == "--") { m_bFound = true; m_bValid = true; if (!vwArgContext.RemoveArg(rTxt)) diff --git a/tools/lldb-mi/MICmdArgValFile.cpp b/tools/lldb-mi/MICmdArgValFile.cpp index 77e9a6b2af8e..cb2ba0125903 100644 --- a/tools/lldb-mi/MICmdArgValFile.cpp +++ b/tools/lldb-mi/MICmdArgValFile.cpp @@ -158,10 +158,7 @@ bool CMICmdArgValFile::IsFilePath(const CMIUtilString &vrFileNamePath) const { return false; const bool bValidChars = IsValidChars(vrFileNamePath); - if (bValidChars || bHavePosSlash || bHaveBckSlash) - return true; - - return false; + return bValidChars || bHavePosSlash || bHaveBckSlash; } //++ diff --git a/tools/lldb-mi/MICmdArgValOptionLong.cpp b/tools/lldb-mi/MICmdArgValOptionLong.cpp index 70faab900f63..3824fe19188a 100644 --- a/tools/lldb-mi/MICmdArgValOptionLong.cpp +++ b/tools/lldb-mi/MICmdArgValOptionLong.cpp @@ -252,10 +252,7 @@ bool CMICmdArgValOptionLong::IsArgLongOption(const CMIUtilString &vrTxt) const { return false; const CMIUtilString strArg = vrTxt.substr(2); - if (strArg.IsNumber()) - return false; - - return true; + return !strArg.IsNumber(); } //++ diff --git a/tools/lldb-mi/MICmdArgValString.cpp b/tools/lldb-mi/MICmdArgValString.cpp index e872f3630587..28d4d18c361b 100644 --- a/tools/lldb-mi/MICmdArgValString.cpp +++ b/tools/lldb-mi/MICmdArgValString.cpp @@ -33,7 +33,7 @@ CMICmdArgValString::CMICmdArgValString() // Throws: None. //-- CMICmdArgValString::CMICmdArgValString(const bool vbAnything) - : m_bHandleQuotedString(vbAnything ? true : false), m_bAcceptNumbers(false), + : m_bHandleQuotedString(vbAnything), m_bAcceptNumbers(false), m_bHandleDirPaths(false), m_bHandleAnything(vbAnything) {} //++ @@ -391,8 +391,5 @@ bool CMICmdArgValString::IsStringArgQuotedQuotedTextEmbedded( return false; const size_t nLen = vrTxt.length(); - if ((nLen > 5) && ((nPos + 2) == (nPos2 - 2))) - return false; - - return true; + return !((nLen > 5) && ((nPos + 2) == (nPos2 - 2))); } diff --git a/tools/lldb-mi/MICmdArgValThreadGrp.cpp b/tools/lldb-mi/MICmdArgValThreadGrp.cpp index 613ad0dcc348..4ed83e6191e1 100644 --- a/tools/lldb-mi/MICmdArgValThreadGrp.cpp +++ b/tools/lldb-mi/MICmdArgValThreadGrp.cpp @@ -115,10 +115,7 @@ bool CMICmdArgValThreadGrp::IsArgThreadGrp(const CMIUtilString &vrTxt) const { return false; const CMIUtilString strNum = vrTxt.substr(1); - if (!strNum.IsNumber()) - return false; - - return true; + return strNum.IsNumber(); } //++ diff --git a/tools/lldb-mi/MICmdBase.h b/tools/lldb-mi/MICmdBase.h index cb44504a1613..fe6aadd66999 100644 --- a/tools/lldb-mi/MICmdBase.h +++ b/tools/lldb-mi/MICmdBase.h @@ -9,14 +9,10 @@ #pragma once -// C Includes -// C++ Includes -#include <functional> // for function +#include <functional> -// Other libraries and framework includes #include "lldb/API/SBError.h" -// Project includes #include "MICmdArgSet.h" #include "MICmdData.h" #include "MICmdFactory.h" diff --git a/tools/lldb-mi/MICmdCmdBreak.cpp b/tools/lldb-mi/MICmdCmdBreak.cpp index caad9192366f..0e42f8562ab4 100644 --- a/tools/lldb-mi/MICmdCmdBreak.cpp +++ b/tools/lldb-mi/MICmdCmdBreak.cpp @@ -165,8 +165,15 @@ bool CMICmdCmdBreakInsert::Execute() { if (sbTarget == rSessionInfo.GetDebugger().GetDummyTarget()) m_bBrkPtIsPending = true; - else + else { m_bBrkPtIsPending = pArgPendingBrkPt->GetFound(); + if (!m_bBrkPtIsPending) { + CMIUtilString pending; + if (m_rLLDBDebugSessionInfo.SharedDataRetrieve("breakpoint.pending", pending)) { + m_bBrkPtIsPending = pending == "on"; + } + } + } if (pArgLocation->GetFound()) m_brkName = pArgLocation->GetValue(); diff --git a/tools/lldb-mi/MICmdCmdData.cpp b/tools/lldb-mi/MICmdCmdData.cpp index 53c2d7e621a7..7a4845bba430 100644 --- a/tools/lldb-mi/MICmdCmdData.cpp +++ b/tools/lldb-mi/MICmdCmdData.cpp @@ -23,9 +23,9 @@ #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBThread.h" -#include "llvm/ADT/Twine.h" #include "llvm/ADT/StringRef.h" -#include <inttypes.h> // For PRIx64 +#include "llvm/ADT/Twine.h" +#include <inttypes.h> #include <string> // In-house headers: diff --git a/tools/lldb-mi/MICmdCmdGdbInfo.cpp b/tools/lldb-mi/MICmdCmdGdbInfo.cpp index ba96319469a1..5988066883d1 100644 --- a/tools/lldb-mi/MICmdCmdGdbInfo.cpp +++ b/tools/lldb-mi/MICmdCmdGdbInfo.cpp @@ -11,7 +11,7 @@ // Third party headers: #include "lldb/API/SBCommandReturnObject.h" -#include <inttypes.h> // For PRIx64 +#include <inttypes.h> // In-house headers: #include "MICmdArgValString.h" diff --git a/tools/lldb-mi/MICmdCmdGdbSet.cpp b/tools/lldb-mi/MICmdCmdGdbSet.cpp index 9f7325c931fc..b433f7678449 100644 --- a/tools/lldb-mi/MICmdCmdGdbSet.cpp +++ b/tools/lldb-mi/MICmdCmdGdbSet.cpp @@ -28,7 +28,8 @@ const CMICmdCmdGdbSet::MapGdbOptionNameToFnGdbOptionPtr_t {"output-radix", &CMICmdCmdGdbSet::OptionFnOutputRadix}, {"solib-search-path", &CMICmdCmdGdbSet::OptionFnSolibSearchPath}, {"disassembly-flavor", &CMICmdCmdGdbSet::OptionFnDisassemblyFlavor}, - {"fallback", &CMICmdCmdGdbSet::OptionFnFallback}}; + {"fallback", &CMICmdCmdGdbSet::OptionFnFallback}, + {"breakpoint", &CMICmdCmdGdbSet::OptionFnBreakpoint}}; //++ //------------------------------------------------------------------------------------ @@ -433,6 +434,56 @@ bool CMICmdCmdGdbSet::OptionFnDisassemblyFlavor( //++ //------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB set option 'breakpoint' to +// prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnBreakpoint( + const CMIUtilString::VecString_t &vrWords) { + bool bPending = false; + bool bOk = true; + + if (vrWords.size() != 2) + // Wrong number of arguments. + bOk = false; + else if (CMIUtilString::Compare(vrWords[0], "pending") && + (CMIUtilString::Compare(vrWords[1], "on") || + CMIUtilString::Compare(vrWords[1], "1"))) + bPending = true; + else if (CMIUtilString::Compare(vrWords[0], "pending") && + (CMIUtilString::Compare(vrWords[1], "off") || + CMIUtilString::Compare(vrWords[1], "0"))) + bPending = false; + else + // Unrecognized argument(s). + bOk = false; + + if (!bOk) { + // Report error. + m_bGbbOptionFnHasError = false; + SetError(MIRSRC(IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT)); + return MIstatus::failure; + } + + CMIUtilString sPendingVal = bPending ? "on" : "off"; + CMIUtilString sKey = "breakpoint.pending"; + if (!m_rLLDBDebugSessionInfo.SharedDataAdd(sKey, sPendingVal)) { + m_bGbbOptionFnHasError = false; + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), sKey.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +//------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option to prepare and send // back the // requested information. diff --git a/tools/lldb-mi/MICmdCmdGdbSet.h b/tools/lldb-mi/MICmdCmdGdbSet.h index 730754f7f82a..7cca20c3348d 100644 --- a/tools/lldb-mi/MICmdCmdGdbSet.h +++ b/tools/lldb-mi/MICmdCmdGdbSet.h @@ -80,6 +80,7 @@ private: bool OptionFnSolibSearchPath(const CMIUtilString::VecString_t &vrWords); bool OptionFnOutputRadix(const CMIUtilString::VecString_t &vrWords); bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); + bool OptionFnBreakpoint(const CMIUtilString::VecString_t &vrWords); bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); // Attributes: diff --git a/tools/lldb-mi/MICmdCmdGdbShow.cpp b/tools/lldb-mi/MICmdCmdGdbShow.cpp index 4ae45f8579e2..196b8271064f 100644 --- a/tools/lldb-mi/MICmdCmdGdbShow.cpp +++ b/tools/lldb-mi/MICmdCmdGdbShow.cpp @@ -32,7 +32,8 @@ const CMICmdCmdGdbShow::MapGdbOptionNameToFnGdbOptionPtr_t {"print", &CMICmdCmdGdbShow::OptionFnPrint}, {"language", &CMICmdCmdGdbShow::OptionFnLanguage}, {"disassembly-flavor", &CMICmdCmdGdbShow::OptionFnDisassemblyFlavor}, - {"fallback", &CMICmdCmdGdbShow::OptionFnFallback}}; + {"fallback", &CMICmdCmdGdbShow::OptionFnFallback}, + {"breakpoint", &CMICmdCmdGdbShow::OptionFnBreakpoint}}; //++ //------------------------------------------------------------------------------------ @@ -349,6 +350,43 @@ bool CMICmdCmdGdbShow::OptionFnDisassemblyFlavor(const CMIUtilString::VecString_ //++ //------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB show option 'breakpoint' to +// prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnBreakpoint( + const CMIUtilString::VecString_t &vrWords) { + if (vrWords.size() != 1) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS); + return MIstatus::failure; + } + + const CMIUtilString strOption(vrWords[0]); + if (!CMIUtilString::Compare(strOption, "pending")) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION), + strOption.c_str()); + return MIstatus::failure; + } + + if (!m_rLLDBDebugSessionInfo.SharedDataRetrieve("breakpoint.pending", + m_strValue)) { + if (m_strValue.empty()) + m_strValue = "off"; + } + + return MIstatus::success; +} + +//++ +//------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB show option to prepare and send // back the // requested information. diff --git a/tools/lldb-mi/MICmdCmdGdbShow.h b/tools/lldb-mi/MICmdCmdGdbShow.h index 452db827032c..51f07092843f 100644 --- a/tools/lldb-mi/MICmdCmdGdbShow.h +++ b/tools/lldb-mi/MICmdCmdGdbShow.h @@ -80,6 +80,7 @@ private: bool OptionFnLanguage(const CMIUtilString::VecString_t &vrWords); bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); + bool OptionFnBreakpoint(const CMIUtilString::VecString_t &vrWords); // Attributes: private: diff --git a/tools/lldb-mi/MICmdCmdTarget.cpp b/tools/lldb-mi/MICmdCmdTarget.cpp index a82bd682de81..0666fc4c60ec 100644 --- a/tools/lldb-mi/MICmdCmdTarget.cpp +++ b/tools/lldb-mi/MICmdCmdTarget.cpp @@ -10,9 +10,8 @@ // Overview: CMICmdCmdTargetSelect implementation. // Third Party Headers: -#include "lldb/API/SBCommandInterpreter.h" -#include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBError.h" // In-house headers: #include "MICmdArgValNumber.h" @@ -52,7 +51,7 @@ CMICmdCmdTargetSelect::CMICmdCmdTargetSelect() // Return: None. // Throws: None. //-- -CMICmdCmdTargetSelect::~CMICmdCmdTargetSelect() {} +CMICmdCmdTargetSelect::~CMICmdCmdTargetSelect() = default; //++ //------------------------------------------------------------------------------------ @@ -93,16 +92,17 @@ bool CMICmdCmdTargetSelect::Execute() { CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget target = rSessionInfo.GetTarget(); - // Check we have a valid target - // Note: target created via 'file-exec-and-symbols' command - if (!rSessionInfo.GetTarget().IsValid()) { + // Check we have a valid target. + // Note: target created via 'file-exec-and-symbols' command. + if (!target.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } - // Verify that we are executing remotely + // Verify that we are executing remotely. const CMIUtilString &rRemoteType(pArgType->GetValue()); if (rRemoteType != "remote") { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_TYPE), @@ -111,33 +111,25 @@ bool CMICmdCmdTargetSelect::Execute() { return MIstatus::failure; } - // Create a URL pointing to the remote gdb stub + // Create a URL pointing to the remote gdb stub. const CMIUtilString strUrl = CMIUtilString::Format("connect://%s", pArgParameters->GetValue().c_str()); - // Ask LLDB to connect to the target port - const char *pPlugin("gdb-remote"); lldb::SBError error; - lldb::SBProcess process = rSessionInfo.GetTarget().ConnectRemote( + // Ask LLDB to connect to the target port. + const char *pPlugin("gdb-remote"); + lldb::SBProcess process = target.ConnectRemote( rSessionInfo.GetListener(), strUrl.c_str(), pPlugin, error); - // Verify that we have managed to connect successfully - lldb::SBStream errMsg; - error.GetDescription(errMsg); + // Verify that we have managed to connect successfully. if (!process.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_PLUGIN), m_cmdData.strMiCmd.c_str(), - errMsg.GetData())); - return MIstatus::failure; - } - if (error.Fail()) { - SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_CONNECT_TO_TARGET), - m_cmdData.strMiCmd.c_str(), - errMsg.GetData())); + error.GetCString())); return MIstatus::failure; } - // Set the environment path if we were given one + // Set the environment path if we were given one. CMIUtilString strWkDir; if (rSessionInfo.SharedDataRetrieve<CMIUtilString>( rSessionInfo.m_constStrSharedDataKeyWkDir, strWkDir)) { @@ -150,28 +142,13 @@ bool CMICmdCmdTargetSelect::Execute() { } } - // Set the shared object path if we were given one + // Set the shared object path if we were given one. CMIUtilString strSolibPath; if (rSessionInfo.SharedDataRetrieve<CMIUtilString>( - rSessionInfo.m_constStrSharedDataSolibPath, strSolibPath)) { - lldb::SBDebugger &rDbgr = rSessionInfo.GetDebugger(); - lldb::SBCommandInterpreter cmdIterpreter = rDbgr.GetCommandInterpreter(); - - CMIUtilString strCmdString = CMIUtilString::Format( - "target modules search-paths add . %s", strSolibPath.c_str()); - - lldb::SBCommandReturnObject retObj; - cmdIterpreter.HandleCommand(strCmdString.c_str(), retObj, false); + rSessionInfo.m_constStrSharedDataSolibPath, strSolibPath)) + target.AppendImageSearchPath(".", strSolibPath.c_str(), error); - if (!retObj.Succeeded()) { - SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), - m_cmdData.strMiCmd.c_str(), - "target-select")); - return MIstatus::failure; - } - } - - return MIstatus::success; + return HandleSBError(error); } //++ diff --git a/tools/lldb-mi/MICmdFactory.cpp b/tools/lldb-mi/MICmdFactory.cpp index 8f6215463627..ad4283e50215 100644 --- a/tools/lldb-mi/MICmdFactory.cpp +++ b/tools/lldb-mi/MICmdFactory.cpp @@ -128,10 +128,7 @@ bool CMICmdFactory::CmdRegister(const CMIUtilString &vMiCmd, bool CMICmdFactory::HaveAlready(const CMIUtilString &vMiCmd) const { const MapMiCmdToCmdCreatorFn_t::const_iterator it = m_mapMiCmdToCmdCreatorFn.find(vMiCmd); - if (it != m_mapMiCmdToCmdCreatorFn.end()) - return true; - - return false; + return it != m_mapMiCmdToCmdCreatorFn.end(); } //++ diff --git a/tools/lldb-mi/MICmnBase.cpp b/tools/lldb-mi/MICmnBase.cpp index 63544dc5b9c7..7f82c6120e1a 100644 --- a/tools/lldb-mi/MICmnBase.cpp +++ b/tools/lldb-mi/MICmnBase.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // Third party headers -#include <stdarg.h> // va_list, va_start, var_end +#include <stdarg.h> // In-house headers: #include "MICmnBase.h" diff --git a/tools/lldb-mi/MICmnLLDBDebugSessionInfo.cpp b/tools/lldb-mi/MICmnLLDBDebugSessionInfo.cpp index 9a173d1007e2..f7f43f001b7b 100644 --- a/tools/lldb-mi/MICmnLLDBDebugSessionInfo.cpp +++ b/tools/lldb-mi/MICmnLLDBDebugSessionInfo.cpp @@ -9,11 +9,11 @@ // Third party headers: #include "lldb/API/SBThread.h" -#include <inttypes.h> // For PRIx64 +#include <inttypes.h> #ifdef _WIN32 -#include <io.h> // For the ::_access() +#include <io.h> #else -#include <unistd.h> // For the ::access() +#include <unistd.h> #endif // _WIN32 #include "lldb/API/SBBreakpointLocation.h" @@ -532,7 +532,7 @@ bool CMICmnLLDBDebugSessionInfo::GetVariableInfo(const lldb::SBValue &vrValue, const bool vbInSimpleForm, CMIUtilString &vwrStrValue) { const CMICmnLLDBUtilSBValue utilValue(vrValue, true, false); - const bool bExpandAggregates = vbInSimpleForm ? false : true; + const bool bExpandAggregates = !vbInSimpleForm; vwrStrValue = utilValue.GetValue(bExpandAggregates); return MIstatus::success; } diff --git a/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp b/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp index dceb29f5908f..cd54c2bb5bc4 100644 --- a/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp +++ b/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp @@ -20,9 +20,9 @@ #include "lldb/API/SBUnixSignals.h" #include "llvm/Support/Compiler.h" #ifdef _WIN32 -#include <io.h> // For the ::_access() +#include <io.h> #else -#include <unistd.h> // For the ::access() +#include <unistd.h> #endif // _WIN32 // In-house headers: @@ -39,7 +39,7 @@ #include "MICmnStreamStdout.h" #include "MIDriver.h" #include "MIUtilDebug.h" -#include "Platform.h" // for PATH_MAX +#include "Platform.h" #include <algorithm> @@ -437,10 +437,10 @@ bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointAdded( sBrkPtInfo.m_nIgnore = brkPt.GetIgnoreCount(); sBrkPtInfo.m_bPending = false; const char *pStrCondition = brkPt.GetCondition(); - sBrkPtInfo.m_bCondition = (pStrCondition != nullptr) ? true : false; + sBrkPtInfo.m_bCondition = pStrCondition != nullptr; sBrkPtInfo.m_strCondition = (pStrCondition != nullptr) ? pStrCondition : "??"; - sBrkPtInfo.m_bBrkPtThreadId = (brkPt.GetThreadID() != 0) ? true : false; + sBrkPtInfo.m_bBrkPtThreadId = brkPt.GetThreadID() != 0; sBrkPtInfo.m_nBrkPtThreadId = brkPt.GetThreadID(); } @@ -950,6 +950,7 @@ bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventBroadcastBitStateChanged( bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateSuspended( const lldb::SBEvent &vEvent) { bool bOk = MIstatus::success; + lldb::SBStream streamOut; lldb::SBDebugger &rDebugger = CMICmnLLDBDebugSessionInfo::Instance().GetDebugger(); lldb::SBProcess sbProcess = @@ -958,16 +959,17 @@ bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateSuspended( if (rDebugger.GetSelectedTarget() == target) { if (!UpdateSelectedThread()) return MIstatus::failure; - - lldb::SBCommandReturnObject result; - const lldb::ReturnStatus status = - rDebugger.GetCommandInterpreter().HandleCommand("process status", - result, false); - MIunused(status); - bOk = TextToStderr(result.GetError()); - bOk = bOk && TextToStdout(result.GetOutput()); + sbProcess.GetDescription(streamOut); + // Add a delimiter between process' and threads' info. + streamOut.Printf("\n"); + for (uint32_t i = 0, e = sbProcess.GetNumThreads(); i < e; ++i) { + const lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); + if (!thread.IsValid()) + continue; + thread.GetDescription(streamOut); + } + bOk = TextToStdout(streamOut.GetData()); } else { - lldb::SBStream streamOut; const MIuint nTargetIndex = rDebugger.GetIndexOfTarget(target); if (nTargetIndex != UINT_MAX) streamOut.Printf("Target %d: (", nTargetIndex); diff --git a/tools/lldb-mi/MICmnLog.cpp b/tools/lldb-mi/MICmnLog.cpp index 8643a8784e35..d30c03f5063d 100644 --- a/tools/lldb-mi/MICmnLog.cpp +++ b/tools/lldb-mi/MICmnLog.cpp @@ -209,10 +209,7 @@ bool CMICmnLog::RegisterMedium(const IMedium &vrMedium) { bool CMICmnLog::HaveMediumAlready(const IMedium &vrMedium) const { IMedium *pMedium = const_cast<IMedium *>(&vrMedium); const MapMediumToName_t::const_iterator it = m_mapMediumToName.find(pMedium); - if (it != m_mapMediumToName.end()) - return true; - - return false; + return it != m_mapMediumToName.end(); } //++ diff --git a/tools/lldb-mi/MICmnResources.cpp b/tools/lldb-mi/MICmnResources.cpp index e32a816fc9f1..d8fa0a829245 100644 --- a/tools/lldb-mi/MICmnResources.cpp +++ b/tools/lldb-mi/MICmnResources.cpp @@ -9,7 +9,7 @@ // Third party headers #include "assert.h" -#include <inttypes.h> // For PRIx64 +#include <inttypes.h> // In-house headers: #include "MICmnResources.h" @@ -78,7 +78,7 @@ const CMICmnResources::SRsrcTextData "and a custom plugin.\nThe custom plugin is not necessary to operate " "the MI Driver."}, {IDE_MI_APP_ARG_USAGE, "\nMI driver usage:\n\n\tlldb-mi [--longOption] " - "[-s hortOption] [executeable]\n\n[] = optional " + "[-s hortOption] [executable]\n\n[] = optional " "argument."}, {IDE_MI_APP_ARG_HELP, "-h\n--help\n\tPrints out usage information for " "the MI debugger. Exit the MI\n\tDriver " @@ -439,6 +439,8 @@ const CMICmnResources::SRsrcTextData {IDS_CMD_ERR_INFO_PRINTFN_FAILED, "The request '%s' failed."}, {IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC, "'target-async' expects \"on\" or \"off\""}, + {IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT, + "'breakpoint' expects \"pending on\" or \"pending off\""}, {IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH, "'solib-search-path' requires at least one argument"}, {IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS, @@ -449,6 +451,10 @@ const CMICmnResources::SRsrcTextData "'print' expects option-name and \"on\" or \"off\""}, {IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, "'print' error. The option '%s' not found"}, + {IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS, + "'breakpoint' expects option-name"}, + {IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION, + "'breakpoint' error. The option '%s' not found"}, {IDS_CMD_ERR_EXPR_INVALID, "Failed to evaluate expression: %s"}, {IDS_CMD_ERR_ATTACH_FAILED, "Command '%s'. Attach to process failed: %s"}, diff --git a/tools/lldb-mi/MICmnResources.h b/tools/lldb-mi/MICmnResources.h index 8912a2b8463e..4177a95a5488 100644 --- a/tools/lldb-mi/MICmnResources.h +++ b/tools/lldb-mi/MICmnResources.h @@ -264,11 +264,14 @@ enum { IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND, IDS_CMD_ERR_INFO_PRINTFN_FAILED, IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC, + IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT, IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH, IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS, IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION, IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS, IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, + IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS, + IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION, IDS_CMD_ERR_EXPR_INVALID, IDS_CMD_ERR_ATTACH_FAILED, IDS_CMD_ERR_ATTACH_BAD_ARGS diff --git a/tools/lldb-mi/MICmnStreamStdin.cpp b/tools/lldb-mi/MICmnStreamStdin.cpp index 154519dbec4c..4d3fa2aabd96 100644 --- a/tools/lldb-mi/MICmnStreamStdin.cpp +++ b/tools/lldb-mi/MICmnStreamStdin.cpp @@ -11,7 +11,7 @@ #ifdef _MSC_VER #include <windows.h> #endif -#include <string.h> // For std::strerror() +#include <string.h> // In-house headers: #include "MICmnLog.h" diff --git a/tools/lldb-mi/MIDataTypes.h b/tools/lldb-mi/MIDataTypes.h index 34fe4575160e..59ff099389cb 100644 --- a/tools/lldb-mi/MIDataTypes.h +++ b/tools/lldb-mi/MIDataTypes.h @@ -21,7 +21,7 @@ // Debugging: #ifdef _DEBUG -#include <crtdbg.h> // C-runtime debugging library (defines _ASSERT). +#include <crtdbg.h> #endif // _DEBUG #endif // _WIN32 diff --git a/tools/lldb-mi/MIDriver.cpp b/tools/lldb-mi/MIDriver.cpp index 23039110b428..cb8fb1c2c25f 100644 --- a/tools/lldb-mi/MIDriver.cpp +++ b/tools/lldb-mi/MIDriver.cpp @@ -443,8 +443,7 @@ lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[], 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) { + if (strPrevArg == "-s" || strPrevArg == "--source") { m_strCmdLineArgCommandFileNamePath = strArg; m_bHaveCommandFileNamePathOnCmdLine = true; i--; // skip '-s' on the next loop @@ -457,7 +456,7 @@ lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[], } // Report error if no command file was specified for the '-s' or // '--source' options - else if (strArg.compare("-s") == 0 || strArg.compare("--source") == 0) { + else if (strArg == "-s" || strArg == "--source") { vwbExiting = true; const CMIUtilString errMsg = CMIUtilString::Format( MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str()); @@ -465,13 +464,13 @@ lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[], 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. + else if (strArg == "--executable") // Used to specify that + // there is executable + // argument also on the + // command line + { // See fn description. bHaveExecutableLongOption = true; - } else if (strArg.compare("--synchronous") == 0) { + } else if (strArg == "--synchronous") { CMICmnLLDBDebugSessionInfo::Instance().GetDebugger().SetAsync(false); } } diff --git a/tools/lldb-mi/MIDriverMain.cpp b/tools/lldb-mi/MIDriverMain.cpp index 1b3521e8f03f..d347ecbbebeb 100644 --- a/tools/lldb-mi/MIDriverMain.cpp +++ b/tools/lldb-mi/MIDriverMain.cpp @@ -42,7 +42,7 @@ #include "MIDriver.h" #include "MIDriverMgr.h" #include "MIUtilDebug.h" -#include "Platform.h" // Define signals - CODETAG_IOR_SIGNALS +#include "Platform.h" #if defined(_MSC_VER) #pragma warning( \ @@ -165,11 +165,7 @@ bool DriverSystemShutdown(const bool vbAppExitOk) { //-- 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 llvm::StringRef ToolName = argv[0]; diff --git a/tools/lldb-mi/MIDriverMgr.cpp b/tools/lldb-mi/MIDriverMgr.cpp index 6b39832d736e..93070a5912d6 100644 --- a/tools/lldb-mi/MIDriverMgr.cpp +++ b/tools/lldb-mi/MIDriverMgr.cpp @@ -516,29 +516,27 @@ bool CMIDriverMgr::ParseArgs(const int argc, const char *argv[], const CMIUtilString strArg(argv[i]); // Argument "--executable" is also check for in CMIDriver::ParseArgs() - if ((0 == - strArg.compare( - "--interpreter")) || // Given by the client such as Eclipse - (0 == strArg.compare("--executable"))) // Used to specify that there - // is executable argument also - // on the command line - { // See fn description. + if (("--interpreter" == strArg) || // Given by the client such as Eclipse + ("--executable" == strArg)) // Used to specify that there + // is executable argument also + // on the command line + { // See fn description. bHaveArgInterpret = true; } - if (0 == strArg.compare("--version")) { + if ("--version" == strArg) { bHaveArgVersion = true; } - if (0 == strArg.compare("--versionLong")) { + if ("--versionLong" == strArg) { bHaveArgVersionLong = true; } - if (0 == strArg.compare("--log")) { + if ("--log" == strArg) { bHaveArgLog = true; } if (0 == strArg.compare(0, 10, "--log-dir=")) { strLogDir = strArg.substr(10, CMIUtilString::npos); bHaveArgLogDir = true; } - if ((0 == strArg.compare("--help")) || (0 == strArg.compare("-h"))) { + if (("--help" == strArg) || ("-h" == strArg)) { bHaveArgHelp = true; } } diff --git a/tools/lldb-mi/MIUtilDebug.cpp b/tools/lldb-mi/MIUtilDebug.cpp index 519fd950feb2..598a1a71046b 100644 --- a/tools/lldb-mi/MIUtilDebug.cpp +++ b/tools/lldb-mi/MIUtilDebug.cpp @@ -39,25 +39,6 @@ CMIUtilDebug::~CMIUtilDebug() {} //++ //------------------------------------------------------------------------------------ -// Details: Show a dialog to the process/application halts. It gives the -// opportunity to -// attach a debugger. -// Type: Static method. -// Args: None. -// Return: None. -// Throws: None. -//-- -void CMIUtilDebug::ShowDlgWaitForDbgAttach() { - const CMIUtilString strCaption(CMIDriver::Instance().GetAppNameShort()); -#ifdef _WIN32 - ::MessageBoxA(NULL, "Attach your debugger now", strCaption.c_str(), MB_OK); -#else -// ToDo: Implement other platform version of an Ok to continue dialog box -#endif // _WIN32 -} - -//++ -//------------------------------------------------------------------------------------ // Details: Temporarily stall the process/application to give the programmer the // opportunity to attach a debugger. How to use: Put a break in the // programmer diff --git a/tools/lldb-mi/MIUtilDebug.h b/tools/lldb-mi/MIUtilDebug.h index a49fd9a868ec..16b080ee7b62 100644 --- a/tools/lldb-mi/MIUtilDebug.h +++ b/tools/lldb-mi/MIUtilDebug.h @@ -24,7 +24,6 @@ class CMICmnLog; class CMIUtilDebug { // Statics: public: - static void ShowDlgWaitForDbgAttach(); static void WaitForDbgAttachInfinteLoop(); // Methods: diff --git a/tools/lldb-mi/MIUtilFileStd.cpp b/tools/lldb-mi/MIUtilFileStd.cpp index 1e76c1c58fe2..abf0ef407d3b 100644 --- a/tools/lldb-mi/MIUtilFileStd.cpp +++ b/tools/lldb-mi/MIUtilFileStd.cpp @@ -11,7 +11,7 @@ #include <assert.h> #include <cerrno> #include <stdio.h> -#include <string.h> // For strerror() +#include <string.h> // In-house headers: #include "MICmnResources.h" diff --git a/tools/lldb-mi/MIUtilMapIdToVariant.cpp b/tools/lldb-mi/MIUtilMapIdToVariant.cpp index 52bc413a471f..0a4e7299e926 100644 --- a/tools/lldb-mi/MIUtilMapIdToVariant.cpp +++ b/tools/lldb-mi/MIUtilMapIdToVariant.cpp @@ -52,10 +52,7 @@ void CMIUtilMapIdToVariant::Clear() { m_mapKeyToVariantValue.clear(); } bool CMIUtilMapIdToVariant::HaveAlready(const CMIUtilString &vId) const { const MapKeyToVariantValue_t::const_iterator it = m_mapKeyToVariantValue.find(vId); - if (it != m_mapKeyToVariantValue.end()) - return true; - - return false; + return it != m_mapKeyToVariantValue.end(); } //++ diff --git a/tools/lldb-mi/MIUtilString.cpp b/tools/lldb-mi/MIUtilString.cpp index 3797d1001798..627c2f3d4d3b 100644 --- a/tools/lldb-mi/MIUtilString.cpp +++ b/tools/lldb-mi/MIUtilString.cpp @@ -10,12 +10,12 @@ // Third party headers #include "llvm/Support/Compiler.h" #include <cstdlib> -#include <inttypes.h> // for PRIx8 -#include <limits.h> // for ULONG_MAX -#include <memory> // std::unique_ptr -#include <sstream> // std::stringstream -#include <stdarg.h> // va_list, va_start, var_end -#include <string.h> // for strncmp +#include <inttypes.h> +#include <limits.h> +#include <memory> +#include <sstream> +#include <stdarg.h> +#include <string.h> // In-house headers: #include "MIUtilString.h" @@ -378,10 +378,7 @@ bool CMIUtilString::IsNumber() const { return false; const size_t nPos = find_first_not_of("-.0123456789"); - if (nPos != std::string::npos) - return false; - - return true; + return nPos == std::string::npos; } //++ @@ -399,10 +396,7 @@ bool CMIUtilString::IsHexadecimalNumber() const { // Skip '0x..' prefix const size_t nPos = find_first_not_of("01234567890ABCDEFabcedf", 2); - if (nPos != std::string::npos) - return false; - - return true; + return nPos == std::string::npos; } //++ @@ -419,10 +413,7 @@ bool CMIUtilString::ExtractNumber(MIint64 &vwrNumber) const { vwrNumber = 0; if (!IsNumber()) { - if (ExtractNumberFromHexadecimal(vwrNumber)) - return true; - - return false; + return ExtractNumberFromHexadecimal(vwrNumber); } std::stringstream ss(const_cast<CMIUtilString &>(*this)); @@ -639,10 +630,7 @@ bool CMIUtilString::IsQuoted() const { return false; const size_t nLen = length(); - if ((nLen > 0) && (at(nLen - 1) != cQuote)) - return false; - - return true; + return !((nLen > 0) && (at(nLen - 1) != cQuote)); } //++ diff --git a/tools/lldb-server/CMakeLists.txt b/tools/lldb-server/CMakeLists.txt index c74f553e9659..5b138534404f 100644 --- a/tools/lldb-server/CMakeLists.txt +++ b/tools/lldb-server/CMakeLists.txt @@ -42,7 +42,7 @@ else() list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF) endif() -add_lldb_tool(lldb-server INCLUDE_IN_SUITE +add_lldb_tool(lldb-server Acceptor.cpp lldb-gdbserver.cpp lldb-platform.cpp diff --git a/tools/lldb-server/SystemInitializerLLGS.cpp b/tools/lldb-server/SystemInitializerLLGS.cpp index aeaf382a1dd8..93ef4d9d0761 100644 --- a/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/tools/lldb-server/SystemInitializerLLGS.cpp @@ -22,9 +22,14 @@ using HostObjectFile = ObjectFileELF; using namespace lldb_private; -void SystemInitializerLLGS::Initialize() { - SystemInitializerCommon::Initialize(); +llvm::Error +SystemInitializerLLGS::Initialize(const InitializerOptions &options) { + if (auto e = SystemInitializerCommon::Initialize(options)) + return e; + HostObjectFile::Initialize(); + + return llvm::Error::success(); } void SystemInitializerLLGS::Terminate() { diff --git a/tools/lldb-server/SystemInitializerLLGS.h b/tools/lldb-server/SystemInitializerLLGS.h index e6460a2cdd39..7feba3fe07bd 100644 --- a/tools/lldb-server/SystemInitializerLLGS.h +++ b/tools/lldb-server/SystemInitializerLLGS.h @@ -10,11 +10,13 @@ #ifndef LLDB_SYSTEMINITIALIZERLLGS_H #define LLDB_SYSTEMINITIALIZERLLGS_H +#include "lldb/Initialization/SystemInitializer.h" #include "lldb/Initialization/SystemInitializerCommon.h" class SystemInitializerLLGS : public lldb_private::SystemInitializerCommon { public: - void Initialize() override; + llvm::Error + Initialize(const lldb_private::InitializerOptions &options) override; void Terminate() override; }; diff --git a/tools/lldb-server/lldb-gdbserver.cpp b/tools/lldb-server/lldb-gdbserver.cpp index c91a8a89e46a..062bbd0c3b6e 100644 --- a/tools/lldb-server/lldb-gdbserver.cpp +++ b/tools/lldb-server/lldb-gdbserver.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes #include <errno.h> #include <stdint.h> #include <stdio.h> @@ -19,8 +18,6 @@ #include <unistd.h> #endif -// C++ Includes - #include "Acceptor.h" #include "LLDBServerUtilities.h" @@ -28,6 +25,7 @@ #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostGetOpt.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/Pipe.h" @@ -187,7 +185,9 @@ void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc, llvm::errs() << "Error getting current directory: " << ec.message() << "\n"; exit(1); } - info.SetWorkingDirectory(FileSpec(cwd, true)); + FileSpec cwd_spec(cwd); + FileSystem::Instance().Resolve(cwd_spec); + info.SetWorkingDirectory(cwd_spec); info.GetEnvironment() = Host::GetEnvironment(); gdb_server.SetLaunchInfo(info); @@ -218,20 +218,17 @@ Status writeSocketIdToPipe(const char *const named_pipe_path, 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}; +Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe, + const std::string &socket_id) { + Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe}; 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, + const char *const named_pipe_path, pipe_t unnamed_pipe, int connection_fd) { Status error; @@ -331,8 +328,8 @@ void ConnectToRemote(MainLoop &mainloop, } // 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); + else if (unnamed_pipe != LLDB_INVALID_PIPE) { + error = writeSocketIdToPipe(unnamed_pipe, socket_id); if (error.Fail()) fprintf(stderr, "failed to write to the unnamed pipe: %s\n", error.AsCString()); @@ -384,7 +381,7 @@ int main_gdbserver(int argc, char *argv[]) { std::string log_file; StringRef log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" - int unnamed_pipe_fd = -1; + lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE; bool reverse_connect = false; int connection_fd = -1; @@ -425,7 +422,7 @@ int main_gdbserver(int argc, char *argv[]) { case 'U': // unnamed pipe if (optarg && optarg[0]) - unnamed_pipe_fd = StringConvert::ToUInt32(optarg, -1); + unnamed_pipe = (pipe_t)StringConvert::ToUInt64(optarg, -1); break; case 'r': @@ -528,8 +525,8 @@ int main_gdbserver(int argc, char *argv[]) { 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, connection_fd); + progname, subcommand, named_pipe_path.c_str(), + unnamed_pipe, connection_fd); if (!gdb_server.IsConnected()) { fprintf(stderr, "no connection information provided, unable to run\n"); diff --git a/tools/lldb-server/lldb-platform.cpp b/tools/lldb-server/lldb-platform.cpp index cfaf5550d096..b7f7950d4ccf 100644 --- a/tools/lldb-server/lldb-platform.cpp +++ b/tools/lldb-server/lldb-platform.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes #include <errno.h> #if defined(__APPLE__) #include <netinet/in.h> @@ -19,10 +18,8 @@ #include <string.h> #include <sys/wait.h> -// C++ Includes #include <fstream> -// Other libraries and framework includes #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" @@ -100,7 +97,7 @@ static void display_usage(const char *progname, const char *subcommand) { static Status save_socket_id_to_file(const std::string &socket_id, const FileSpec &file_spec) { - FileSpec temp_file_spec(file_spec.GetDirectory().AsCString(), false); + FileSpec temp_file_spec(file_spec.GetDirectory().AsCString()); Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); if (error.Fail()) return Status("Failed to create directory %s: %s", @@ -193,7 +190,7 @@ int main_platform(int argc, char *argv[]) { case 'f': // Socket file if (optarg && optarg[0]) - socket_file.SetFile(optarg, false, FileSpec::Style::native); + socket_file.SetFile(optarg, FileSpec::Style::native); break; case 'p': { diff --git a/tools/lldb-server/lldb-server.cpp b/tools/lldb-server/lldb-server.cpp index f05c96cfaa95..c924fa22f310 100644 --- a/tools/lldb-server/lldb-server.cpp +++ b/tools/lldb-server/lldb-server.cpp @@ -38,8 +38,9 @@ int main_gdbserver(int argc, char *argv[]); int main_platform(int argc, char *argv[]); static void initialize() { - g_debugger_lifetime->Initialize(llvm::make_unique<SystemInitializerLLGS>(), - nullptr); + if (auto e = g_debugger_lifetime->Initialize( + llvm::make_unique<SystemInitializerLLGS>(), {}, nullptr)) + llvm::consumeError(std::move(e)); } static void terminate() { g_debugger_lifetime->Terminate(); } diff --git a/tools/lldb-test/FormatUtil.cpp b/tools/lldb-test/FormatUtil.cpp index 381cbd6e25b8..970f25a6b42f 100644 --- a/tools/lldb-test/FormatUtil.cpp +++ b/tools/lldb-test/FormatUtil.cpp @@ -14,6 +14,11 @@ using namespace lldb_private; using namespace llvm; +LinePrinter::Line::~Line() { + if (P) + P->NewLine(); +} + LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream) : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {} @@ -31,39 +36,31 @@ void LinePrinter::Unindent(uint32_t Amount) { void LinePrinter::NewLine() { OS << "\n"; - OS.indent(CurrentIndent); -} - -void LinePrinter::print(const Twine &T) { OS << T; } - -void LinePrinter::printLine(const Twine &T) { - NewLine(); - OS << T; } void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, uint32_t StartOffset) { - NewLine(); - OS << Label << " ("; - if (!Data.empty()) { - OS << "\n"; - OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, - CurrentIndent + IndentSpaces, true); - NewLine(); + if (Data.empty()) { + line() << Label << " ()"; + return; } - OS << ")"; + line() << Label << " ("; + OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + line() << ")"; } void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, uint64_t Base, uint32_t StartOffset) { - NewLine(); - OS << Label << " ("; - if (!Data.empty()) { - OS << "\n"; - Base += StartOffset; - OS << format_bytes_with_ascii(Data, Base, 32, 4, - CurrentIndent + IndentSpaces, true); - NewLine(); + if (Data.empty()) { + line() << Label << " ()"; + return; } - OS << ")"; + line() << Label << " ("; + Base += StartOffset; + OS << format_bytes_with_ascii(Data, Base, 32, 4, CurrentIndent + IndentSpaces, + true); + NewLine(); + line() << ")"; } diff --git a/tools/lldb-test/FormatUtil.h b/tools/lldb-test/FormatUtil.h index f22ee41662ee..598d4c5440f5 100644 --- a/tools/lldb-test/FormatUtil.h +++ b/tools/lldb-test/FormatUtil.h @@ -26,27 +26,36 @@ class LinePrinter { int CurrentIndent; public: + class Line { + LinePrinter *P; + + public: + Line(LinePrinter &P) : P(&P) { P.OS.indent(P.CurrentIndent); } + ~Line(); + + Line(Line &&RHS) : P(RHS.P) { RHS.P = nullptr; } + void operator=(Line &&) = delete; + + operator llvm::raw_ostream &() { return P->OS; } + }; + LinePrinter(int Indent, llvm::raw_ostream &Stream); void Indent(uint32_t Amount = 0); void Unindent(uint32_t Amount = 0); void NewLine(); - void printLine(const llvm::Twine &T); - void print(const llvm::Twine &T); + void printLine(const llvm::Twine &T) { line() << T; } template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) { printLine(llvm::formatv(Fmt, std::forward<Ts>(Items)...)); } - template <typename... Ts> void format(const char *Fmt, Ts &&... Items) { - print(llvm::formatv(Fmt, std::forward<Ts>(Items)...)); - } void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, uint32_t StartOffset); void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data, uint64_t BaseAddr, uint32_t StartOffset); - llvm::raw_ostream &getStream() { return OS; } + Line line() { return Line(*this); } int getIndentLevel() const { return CurrentIndent; } }; @@ -64,12 +73,6 @@ struct AutoIndent { uint32_t Amount = 0; }; -template <class T> -inline llvm::raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { - Printer.getStream() << Item; - return Printer.getStream(); -} - } // namespace lldb_private #endif diff --git a/tools/lldb-test/SystemInitializerTest.cpp b/tools/lldb-test/SystemInitializerTest.cpp index 2c190ecfc745..1220312def84 100644 --- a/tools/lldb-test/SystemInitializerTest.cpp +++ b/tools/lldb-test/SystemInitializerTest.cpp @@ -14,9 +14,6 @@ #include "lldb/Initialization/SystemInitializerCommon.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTContext.h" -#include "lldb/Symbol/GoASTContext.h" -#include "lldb/Symbol/JavaASTContext.h" -#include "lldb/Symbol/OCamlASTContext.h" #include "lldb/Utility/Timer.h" #include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" @@ -48,22 +45,17 @@ #include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h" #include "Plugins/JITLoader/GDB/JITLoaderGDB.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" -#include "Plugins/Language/Go/GoLanguage.h" -#include "Plugins/Language/Java/JavaLanguage.h" -#include "Plugins/Language/OCaml/OCamlLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" -#include "Plugins/LanguageRuntime/Go/GoLanguageRuntime.h" -#include "Plugins/LanguageRuntime/Java/JavaLanguageRuntime.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" -#include "Plugins/OperatingSystem/Go/OperatingSystemGo.h" #include "Plugins/Platform/Android/PlatformAndroid.h" #include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" #include "Plugins/Platform/Kalimba/PlatformKalimba.h" @@ -78,6 +70,7 @@ #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/Process/minidump/ProcessMinidump.h" #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" +#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" @@ -120,16 +113,18 @@ SystemInitializerTest::SystemInitializerTest() {} SystemInitializerTest::~SystemInitializerTest() {} -void SystemInitializerTest::Initialize() { - SystemInitializerCommon::Initialize(); +llvm::Error +SystemInitializerTest::Initialize(const InitializerOptions &options) { + if (auto e = SystemInitializerCommon::Initialize(options)) + return e; + breakpad::ObjectFileBreakpad::Initialize(); ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); ScriptInterpreterNone::Initialize(); - OperatingSystemGo::Initialize(); platform_freebsd::PlatformFreeBSD::Initialize(); platform_linux::PlatformLinux::Initialize(); @@ -152,9 +147,6 @@ void SystemInitializerTest::Initialize() { llvm::InitializeAllDisassemblers(); ClangASTContext::Initialize(); - GoASTContext::Initialize(); - JavaASTContext::Initialize(); - OCamlASTContext::Initialize(); ABIMacOSX_i386::Initialize(); ABIMacOSX_arm::Initialize(); @@ -185,6 +177,7 @@ void SystemInitializerTest::Initialize() { MainThreadCheckerRuntime::Initialize(); SymbolVendorELF::Initialize(); + breakpad::SymbolFileBreakpad::Initialize(); SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); @@ -198,15 +191,10 @@ void SystemInitializerTest::Initialize() { AppleObjCRuntimeV1::Initialize(); SystemRuntimeMacOSX::Initialize(); RenderScriptRuntime::Initialize(); - GoLanguageRuntime::Initialize(); - JavaLanguageRuntime::Initialize(); CPlusPlusLanguage::Initialize(); - GoLanguage::Initialize(); - JavaLanguage::Initialize(); ObjCLanguage::Initialize(); ObjCPlusPlusLanguage::Initialize(); - OCamlLanguage::Initialize(); #if defined(_WIN32) ProcessWindows::Initialize(); @@ -249,6 +237,8 @@ void SystemInitializerTest::Initialize() { // AFTER PluginManager::Initialize is called. Debugger::SettingsInitialize(); + + return llvm::Error::success(); } void SystemInitializerTest::Terminate() { @@ -261,9 +251,6 @@ void SystemInitializerTest::Terminate() { PluginManager::Terminate(); ClangASTContext::Terminate(); - GoASTContext::Terminate(); - JavaASTContext::Terminate(); - OCamlASTContext::Terminate(); ABIMacOSX_i386::Terminate(); ABIMacOSX_arm::Terminate(); @@ -289,6 +276,7 @@ void SystemInitializerTest::Terminate() { UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); SymbolVendorELF::Terminate(); + breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); SymbolFilePDB::Terminate(); SymbolFileSymtab::Terminate(); @@ -302,14 +290,10 @@ void SystemInitializerTest::Terminate() { AppleObjCRuntimeV1::Terminate(); SystemRuntimeMacOSX::Terminate(); RenderScriptRuntime::Terminate(); - JavaLanguageRuntime::Terminate(); CPlusPlusLanguage::Terminate(); - GoLanguage::Terminate(); - JavaLanguage::Terminate(); ObjCLanguage::Terminate(); ObjCPlusPlusLanguage::Terminate(); - OCamlLanguage::Terminate(); #if defined(__APPLE__) DynamicLoaderDarwinKernel::Terminate(); @@ -337,7 +321,6 @@ void SystemInitializerTest::Terminate() { DynamicLoaderStatic::Terminate(); DynamicLoaderWindowsDYLD::Terminate(); - OperatingSystemGo::Terminate(); platform_freebsd::PlatformFreeBSD::Terminate(); platform_linux::PlatformLinux::Terminate(); @@ -353,6 +336,7 @@ void SystemInitializerTest::Terminate() { PlatformDarwinKernel::Terminate(); #endif + breakpad::ObjectFileBreakpad::Terminate(); ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); diff --git a/tools/lldb-test/SystemInitializerTest.h b/tools/lldb-test/SystemInitializerTest.h index 887d6243765d..5950ff725ff0 100644 --- a/tools/lldb-test/SystemInitializerTest.h +++ b/tools/lldb-test/SystemInitializerTest.h @@ -26,7 +26,7 @@ public: SystemInitializerTest(); ~SystemInitializerTest() override; - void Initialize() override; + llvm::Error Initialize(const InitializerOptions &options) override; void Terminate() override; }; diff --git a/tools/lldb-test/lldb-test.cpp b/tools/lldb-test/lldb-test.cpp index c9225f63d303..f7bfc487c0ce 100644 --- a/tools/lldb-test/lldb-test.cpp +++ b/tools/lldb-test/lldb-test.cpp @@ -30,6 +30,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/IntervalMap.h" @@ -90,19 +91,27 @@ namespace object { cl::opt<bool> SectionContents("contents", cl::desc("Dump each section's contents"), cl::sub(ObjectFileSubcommand)); +cl::opt<bool> SectionDependentModules("dep-modules", + cl::desc("Dump each dependent module"), + cl::sub(ObjectFileSubcommand)); cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), cl::OneOrMore, cl::sub(ObjectFileSubcommand)); } // namespace object namespace symbols { -static cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input files>"), - cl::OneOrMore, - cl::sub(SymbolsSubcommand)); +static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"), + cl::Required, cl::sub(SymbolsSubcommand)); + +static cl::opt<std::string> + SymbolPath("symbol-file", + cl::desc("The file from which to fetch symbol information."), + cl::value_desc("file"), cl::sub(SymbolsSubcommand)); + enum class FindType { None, Function, + Block, Namespace, Type, Variable, @@ -112,6 +121,7 @@ static cl::opt<FindType> Find( cl::values( clEnumValN(FindType::None, "none", "No search, just dump the module."), clEnumValN(FindType::Function, "function", "Find functions."), + clEnumValN(FindType::Block, "block", "Find blocks."), clEnumValN(FindType::Namespace, "namespace", "Find namespaces."), clEnumValN(FindType::Type, "type", "Find types."), clEnumValN(FindType::Variable, "variable", "Find global variables.")), @@ -146,6 +156,10 @@ static FunctionNameType getFunctionNameFlags() { return Result; } +static cl::opt<bool> DumpAST("dump-ast", + cl::desc("Dump AST restored from symbols."), + cl::sub(SymbolsSubcommand)); + static cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."), cl::sub(SymbolsSubcommand)); @@ -158,10 +172,12 @@ static cl::opt<int> Line("line", cl::desc("Line to search."), static Expected<CompilerDeclContext> getDeclContext(SymbolVendor &Vendor); static Error findFunctions(lldb_private::Module &Module); +static Error findBlocks(lldb_private::Module &Module); static Error findNamespaces(lldb_private::Module &Module); static Error findTypes(lldb_private::Module &Module); static Error findVariables(lldb_private::Module &Module); static Error dumpModule(lldb_private::Module &Module); +static Error dumpAST(lldb_private::Module &Module); static Error verify(lldb_private::Module &Module); static Expected<Error (*)(lldb_private::Module &)> getAction(); @@ -197,7 +213,6 @@ struct IRMemoryMapTestState { : Target(Target), Map(Target), Allocations(IntervalMapAllocator) {} }; -bool areAllocationsOverlapping(const AllocationT &L, const AllocationT &R); bool evalMalloc(StringRef Line, IRMemoryMapTestState &State); bool evalFree(StringRef Line, IRMemoryMapTestState &State); int evaluateMemoryMapCommands(Debugger &Dbg); @@ -214,10 +229,9 @@ static Error make_string_error(const char *Format, Args &&... args) { TargetSP opts::createTarget(Debugger &Dbg, const std::string &Filename) { TargetSP Target; - Status ST = - Dbg.GetTargetList().CreateTarget(Dbg, Filename, /*triple*/ "", - /*get_dependent_modules*/ false, - /*platform_options*/ nullptr, Target); + Status ST = Dbg.GetTargetList().CreateTarget( + Dbg, Filename, /*triple*/ "", eLoadDependentsNo, + /*platform_options*/ nullptr, Target); if (ST.Fail()) { errs() << formatv("Failed to create target '{0}: {1}\n", Filename, ST); exit(1); @@ -278,7 +292,7 @@ std::string opts::breakpoint::substitute(StringRef Cmd) { OS << sys::path::parent_path(breakpoint::CommandFile); break; } - // fall through + LLVM_FALLTHROUGH; default: size_t pos = Cmd.find('%'); OS << Cmd.substr(0, pos); @@ -340,7 +354,7 @@ Error opts::symbols::findFunctions(lldb_private::Module &Module) { if (!File.empty()) { assert(Line != 0); - FileSpec src_file(File, false); + FileSpec src_file(File); size_t cu_count = Module.GetNumCompileUnits(); for (size_t i = 0; i < cu_count; i++) { lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); @@ -383,6 +397,42 @@ Error opts::symbols::findFunctions(lldb_private::Module &Module) { return Error::success(); } +Error opts::symbols::findBlocks(lldb_private::Module &Module) { + assert(!Regex); + assert(!File.empty()); + assert(Line != 0); + + SymbolContextList List; + + FileSpec src_file(File); + size_t cu_count = Module.GetNumCompileUnits(); + for (size_t i = 0; i < cu_count; i++) { + lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i); + if (!cu_sp) + continue; + + LineEntry le; + cu_sp->FindLineEntry(0, Line, &src_file, false, &le); + if (!le.IsValid()) + continue; + + auto addr = le.GetSameLineContiguousAddressRange().GetBaseAddress(); + if (!addr.IsValid()) + continue; + + SymbolContext sc; + uint32_t resolved = addr.CalculateSymbolContext(&sc, eSymbolContextBlock); + if (resolved & eSymbolContextBlock) + List.Append(sc); + } + + outs() << formatv("Found {0} blocks:\n", List.GetSize()); + StreamString Stream; + List.Dump(&Stream, nullptr); + outs() << Stream.GetData() << "\n"; + return Error::success(); +} + Error opts::symbols::findNamespaces(lldb_private::Module &Module) { SymbolVendor &Vendor = *Module.GetSymbolVendor(); Expected<CompilerDeclContext> ContextOr = getDeclContext(Vendor); @@ -391,9 +441,8 @@ Error opts::symbols::findNamespaces(lldb_private::Module &Module) { CompilerDeclContext *ContextPtr = ContextOr->IsValid() ? &*ContextOr : nullptr; - SymbolContext SC; CompilerDeclContext Result = - Vendor.FindNamespace(SC, ConstString(Name), ContextPtr); + Vendor.FindNamespace(ConstString(Name), ContextPtr); if (Result) outs() << "Found namespace: " << Result.GetScopeQualifiedName().GetStringRef() << "\n"; @@ -410,10 +459,9 @@ Error opts::symbols::findTypes(lldb_private::Module &Module) { CompilerDeclContext *ContextPtr = ContextOr->IsValid() ? &*ContextOr : nullptr; - SymbolContext SC; DenseSet<SymbolFile *> SearchedFiles; TypeMap Map; - Vendor.FindTypes(SC, ConstString(Name), ContextPtr, true, UINT32_MAX, + Vendor.FindTypes(ConstString(Name), ContextPtr, true, UINT32_MAX, SearchedFiles, Map); outs() << formatv("Found {0} types:\n", Map.GetSize()); @@ -470,6 +518,32 @@ Error opts::symbols::dumpModule(lldb_private::Module &Module) { return Error::success(); } +Error opts::symbols::dumpAST(lldb_private::Module &Module) { + SymbolVendor &plugin = *Module.GetSymbolVendor(); + Module.ParseAllDebugSymbols(); + + auto symfile = plugin.GetSymbolFile(); + if (!symfile) + return make_string_error("Module has no symbol file."); + + auto clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>( + symfile->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus)); + if (!clang_ast_ctx) + return make_string_error("Can't retrieve Clang AST context."); + + auto ast_ctx = clang_ast_ctx->getASTContext(); + if (!ast_ctx) + return make_string_error("Can't retrieve AST context."); + + auto tu = ast_ctx->getTranslationUnitDecl(); + if (!tu) + return make_string_error("Can't retrieve translation unit declaration."); + + tu->print(outs()); + + return Error::success(); +} + Error opts::symbols::verify(lldb_private::Module &Module) { SymbolVendor &plugin = *Module.GetSymbolVendor(); @@ -523,6 +597,10 @@ Error opts::symbols::verify(lldb_private::Module &Module) { } Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { + if (Verify && DumpAST) + return make_string_error( + "Cannot both verify symbol information and dump AST."); + if (Verify) { if (Find != FindType::None) return make_string_error( @@ -535,6 +613,17 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { return verify; } + if (DumpAST) { + if (Find != FindType::None) + return make_string_error("Cannot both search and dump AST."); + if (Regex || !Context.empty() || !Name.empty() || !File.empty() || + Line != 0) + return make_string_error( + "-regex, -context, -name, -file and -line options are not " + "applicable for dumping AST."); + return dumpAST; + } + if (Regex && !Context.empty()) return make_string_error( "Cannot search using both regular expressions and context."); @@ -566,6 +655,15 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { "when searching a function."); return findFunctions; + case FindType::Block: + if (File.empty() || Line == 0) + return make_string_error("Both file name and line number must be " + "specified when searching a block."); + if (Regex || getFunctionNameFlags() != 0) + return make_string_error("Cannot use regular expression or " + "function-flags for searching a block."); + return findBlocks; + case FindType::Namespace: if (Regex || !File.empty() || Line != 0) return make_string_error("Cannot search for namespaces using regular " @@ -584,6 +682,8 @@ Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() { "using line numbers."); return findVariables; } + + llvm_unreachable("Unsupported symbol action."); } int opts::symbols::dumpSymbols(Debugger &Dbg) { @@ -594,28 +694,59 @@ int opts::symbols::dumpSymbols(Debugger &Dbg) { } auto Action = *ActionOr; - int HadErrors = 0; - for (const auto &File : InputFilenames) { - outs() << "Module: " << File << "\n"; - ModuleSpec Spec{FileSpec(File, false)}; - Spec.GetSymbolFileSpec().SetFile(File, false, FileSpec::Style::native); + outs() << "Module: " << InputFile << "\n"; + ModuleSpec Spec{FileSpec(InputFile)}; + StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath; + Spec.GetSymbolFileSpec().SetFile(Symbols, FileSpec::Style::native); - auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); - SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); - if (!Vendor) { - WithColor::error() << "Module has no symbol vendor.\n"; - HadErrors = 1; - continue; - } + auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); + if (!Vendor) { + WithColor::error() << "Module has no symbol vendor.\n"; + return 1; + } - if (Error E = Action(*ModulePtr)) { - WithColor::error() << toString(std::move(E)) << "\n"; - HadErrors = 1; + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + return 1; + } + + return 0; +} + +static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) { + size_t Count = List.GetNumSections(0); + if (Count == 0) { + Printer.formatLine("There are no {0}sections", is_subsection ? "sub" : ""); + return; + } + Printer.formatLine("Showing {0} {1}sections", Count, + is_subsection ? "sub" : ""); + for (size_t I = 0; I < Count; ++I) { + auto S = List.GetSectionAtIndex(I); + assert(S); + AutoIndent Indent(Printer, 2); + Printer.formatLine("Index: {0}", I); + Printer.formatLine("ID: {0:x}", S->GetID()); + Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); + Printer.formatLine("Type: {0}", S->GetTypeAsCString()); + Printer.formatLine("Permissions: {0}", GetPermissionsAsCString(S->GetPermissions())); + Printer.formatLine("Thread specific: {0:y}", S->IsThreadSpecific()); + Printer.formatLine("VM address: {0:x}", S->GetFileAddress()); + Printer.formatLine("VM size: {0}", S->GetByteSize()); + Printer.formatLine("File size: {0}", S->GetFileSize()); + + if (opts::object::SectionContents) { + DataExtractor Data; + S->GetSectionData(Data); + ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; + Printer.formatBinary("Data: ", Bytes, 0); } - outs().flush(); + if (S->GetType() == eSectionTypeContainer) + dumpSectionList(Printer, S->GetChildren(), true); + Printer.NewLine(); } - return HadErrors; } static int dumpObjectFiles(Debugger &Dbg) { @@ -623,9 +754,17 @@ static int dumpObjectFiles(Debugger &Dbg) { int HadErrors = 0; for (const auto &File : opts::object::InputFilenames) { - ModuleSpec Spec{FileSpec(File, false)}; + ModuleSpec Spec{FileSpec(File)}; auto ModulePtr = std::make_shared<lldb_private::Module>(Spec); + + ObjectFile *ObjectPtr = ModulePtr->GetObjectFile(); + if (!ObjectPtr) { + WithColor::error() << File << " not recognised as an object file\n"; + HadErrors = 1; + continue; + } + // Fetch symbol vendor before we get the section list to give the symbol // vendor a chance to populate it. ModulePtr->GetSymbolVendor(); @@ -636,27 +775,29 @@ static int dumpObjectFiles(Debugger &Dbg) { continue; } + Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName()); Printer.formatLine("Architecture: {0}", ModulePtr->GetArchitecture().GetTriple().getTriple()); Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString()); - - size_t Count = Sections->GetNumSections(0); - Printer.formatLine("Showing {0} sections", Count); - for (size_t I = 0; I < Count; ++I) { - AutoIndent Indent(Printer, 2); - auto S = Sections->GetSectionAtIndex(I); - assert(S); - Printer.formatLine("Index: {0}", I); - Printer.formatLine("Name: {0}", S->GetName().GetStringRef()); - Printer.formatLine("Type: {0}", S->GetTypeAsCString()); - Printer.formatLine("VM size: {0}", S->GetByteSize()); - Printer.formatLine("File size: {0}", S->GetFileSize()); - - if (opts::object::SectionContents) { - DataExtractor Data; - S->GetSectionData(Data); - ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()}; - Printer.formatBinary("Data: ", Bytes, 0); + Printer.formatLine("Executable: {0}", ObjectPtr->IsExecutable()); + Printer.formatLine("Stripped: {0}", ObjectPtr->IsStripped()); + Printer.formatLine("Type: {0}", ObjectPtr->GetType()); + Printer.formatLine("Strata: {0}", ObjectPtr->GetStrata()); + Printer.formatLine("Base VM address: {0:x}", + ObjectPtr->GetBaseAddress().GetFileAddress()); + + dumpSectionList(Printer, *Sections, /*is_subsection*/ false); + + if (opts::object::SectionDependentModules) { + // A non-empty section list ensures a valid object file. + auto Obj = ModulePtr->GetObjectFile(); + FileSpecList Files; + auto Count = Obj->GetDependentModules(Files); + Printer.formatLine("Showing {0} dependent module(s)", Count); + for (size_t I = 0; I < Files.GetSize(); ++I) { + AutoIndent Indent(Printer, 2); + Printer.formatLine("Name: {0}", + Files.GetFileSpecAtIndex(I).GetCString()); } Printer.NewLine(); } @@ -664,13 +805,6 @@ static int dumpObjectFiles(Debugger &Dbg) { return HadErrors; } -/// Check if two half-open intervals intersect: -/// http://world.std.com/~swmcd/steven/tech/interval.html -bool opts::irmemorymap::areAllocationsOverlapping(const AllocationT &L, - const AllocationT &R) { - return R.first < L.second && L.first < R.second; -} - bool opts::irmemorymap::evalMalloc(StringRef Line, IRMemoryMapTestState &State) { // ::= <label> = malloc <size> <alignment> @@ -717,28 +851,21 @@ bool opts::irmemorymap::evalMalloc(StringRef Line, exit(1); } - // Check that the allocation does not overlap another allocation. Do so by - // testing each allocation which may cover the interval [Addr, EndOfRegion). - addr_t EndOfRegion = Addr + Size; - auto Probe = State.Allocations.begin(); - Probe.advanceTo(Addr); //< First interval s.t stop >= Addr. - AllocationT NewAllocation = {Addr, EndOfRegion}; - while (Probe != State.Allocations.end() && Probe.start() < EndOfRegion) { - AllocationT ProbeAllocation = {Probe.start(), Probe.stop()}; - if (areAllocationsOverlapping(ProbeAllocation, NewAllocation)) { - outs() << "Malloc error: overlapping allocation detected" - << formatv(", previous allocation at [{0:x}, {1:x})\n", - Probe.start(), Probe.stop()); - exit(1); - } - ++Probe; + // In case of Size == 0, we still expect the returned address to be unique and + // non-overlapping. + addr_t EndOfRegion = Addr + std::max<size_t>(Size, 1); + if (State.Allocations.overlaps(Addr, EndOfRegion)) { + auto I = State.Allocations.find(Addr); + outs() << "Malloc error: overlapping allocation detected" + << formatv(", previous allocation at [{0:x}, {1:x})\n", I.start(), + I.stop()); + exit(1); } - // Insert the new allocation into the interval map. Use unique allocation IDs - // to inhibit interval coalescing. + // Insert the new allocation into the interval map. Use unique allocation + // IDs to inhibit interval coalescing. static unsigned AllocationID = 0; - if (Size) - State.Allocations.insert(Addr, EndOfRegion, AllocationID++); + State.Allocations.insert(Addr, EndOfRegion, AllocationID++); // Store the label -> address mapping. State.Label2AddrMap[Label] = Addr; @@ -836,8 +963,13 @@ int main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n"); SystemLifetimeManager DebuggerLifetime; - DebuggerLifetime.Initialize(llvm::make_unique<SystemInitializerTest>(), - nullptr); + if (auto e = DebuggerLifetime.Initialize( + llvm::make_unique<SystemInitializerTest>(), {}, nullptr)) { + WithColor::error() << "initialization failed: " << toString(std::move(e)) + << '\n'; + return 1; + } + CleanUp TerminateDebugger([&] { DebuggerLifetime.Terminate(); }); auto Dbg = lldb_private::Debugger::CreateInstance(); diff --git a/tools/lldb-vscode/BreakpointBase.cpp b/tools/lldb-vscode/BreakpointBase.cpp new file mode 100644 index 000000000000..adb7abd1a3a1 --- /dev/null +++ b/tools/lldb-vscode/BreakpointBase.cpp @@ -0,0 +1,37 @@ +//===-- BreakpointBase.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BreakpointBase.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb_vscode; + +BreakpointBase::BreakpointBase(const llvm::json::Object &obj) + : condition(GetString(obj, "condition")), + hitCondition(GetString(obj, "hitCondition")), + logMessage(GetString(obj, "logMessage")) {} + +void BreakpointBase::SetCondition() { bp.SetCondition(condition.c_str()); } + +void BreakpointBase::SetHitCondition() { + uint64_t hitCount = 0; + if (llvm::to_integer(hitCondition, hitCount)) + bp.SetIgnoreCount(hitCount - 1); +} + +void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) { + if (condition != request_bp.condition) { + condition = request_bp.condition; + SetCondition(); + } + if (hitCondition != request_bp.hitCondition) { + hitCondition = request_bp.hitCondition; + SetHitCondition(); + } +} diff --git a/tools/lldb-vscode/BreakpointBase.h b/tools/lldb-vscode/BreakpointBase.h new file mode 100644 index 000000000000..e27ffe3bd390 --- /dev/null +++ b/tools/lldb-vscode/BreakpointBase.h @@ -0,0 +1,44 @@ +//===-- BreakpointBase.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_BREAKPOINTBASE_H_ +#define LLDBVSCODE_BREAKPOINTBASE_H_ + +#include "JSONUtils.h" +#include "lldb/API/SBBreakpoint.h" +#include "llvm/Support/JSON.h" +#include <string> + +namespace lldb_vscode { + +struct BreakpointBase { + + // An optional expression for conditional breakpoints. + std::string condition; + // An optional expression that controls how many hits of the breakpoint are + // ignored. The backend is expected to interpret the expression as needed + std::string hitCondition; + // If this attribute exists and is non-empty, the backend must not 'break' + // (stop) but log the message instead. Expressions within {} are + // interpolated. + std::string logMessage; + // The LLDB breakpoint associated wit this source breakpoint + lldb::SBBreakpoint bp; + + BreakpointBase() = default; + BreakpointBase(const llvm::json::Object &obj); + + void SetCondition(); + void SetHitCondition(); + void UpdateBreakpoint(const BreakpointBase &request_bp); +}; + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/CMakeLists.txt b/tools/lldb-vscode/CMakeLists.txt new file mode 100644 index 000000000000..08511248d335 --- /dev/null +++ b/tools/lldb-vscode/CMakeLists.txt @@ -0,0 +1,34 @@ +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) + add_definitions( -DIMPORT_LIBLLDB ) + list(APPEND extra_libs lldbHost) +endif () + +if (HAVE_LIBPTHREAD) + list(APPEND extra_libs pthread) +endif () + +# We need to include the llvm components we depend on manually, as liblldb does +# not re-export those. +set(LLVM_LINK_COMPONENTS Support) +add_lldb_tool(lldb-vscode + lldb-vscode.cpp + BreakpointBase.cpp + ExceptionBreakpoint.cpp + FunctionBreakpoint.cpp + JSONUtils.cpp + LLDBUtils.cpp + SourceBreakpoint.cpp + VSCode.cpp + + LINK_LIBS + liblldb + ${host_lib} + ${extra_libs} + + LINK_COMPONENTS + Support + ) + +if(LLDB_BUILD_FRAMEWORK) + lldb_setup_framework_rpaths_in_tool(lldb-vscode) +endif() diff --git a/tools/lldb-vscode/ExceptionBreakpoint.cpp b/tools/lldb-vscode/ExceptionBreakpoint.cpp new file mode 100644 index 000000000000..96bc0930e429 --- /dev/null +++ b/tools/lldb-vscode/ExceptionBreakpoint.cpp @@ -0,0 +1,32 @@ +//===-- ExceptionBreakpoint.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExceptionBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void ExceptionBreakpoint::SetBreakpoint() { + if (bp.IsValid()) + return; + bool catch_value = filter.find("_catch") != std::string::npos; + bool throw_value = filter.find("_throw") != std::string::npos; + bp = g_vsc.target.BreakpointCreateForException(language, catch_value, + throw_value); +} + +void ExceptionBreakpoint::ClearBreakpoint() { + if (!bp.IsValid()) + return; + g_vsc.target.BreakpointDelete(bp.GetID()); + bp = lldb::SBBreakpoint(); +} + +} // namespace lldb_vscode + diff --git a/tools/lldb-vscode/ExceptionBreakpoint.h b/tools/lldb-vscode/ExceptionBreakpoint.h new file mode 100644 index 000000000000..f3e1e7068095 --- /dev/null +++ b/tools/lldb-vscode/ExceptionBreakpoint.h @@ -0,0 +1,38 @@ +//===-- ExceptionBreakpoint.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_EXCEPTIONBREAKPOINT_H_ +#define LLDBVSCODE_EXCEPTIONBREAKPOINT_H_ + +#include <string> + +#include "lldb/API/SBBreakpoint.h" + +namespace lldb_vscode { + +struct ExceptionBreakpoint { + std::string filter; + std::string label; + lldb::LanguageType language; + bool default_value; + lldb::SBBreakpoint bp; + ExceptionBreakpoint(std::string f, std::string l, lldb::LanguageType lang) : + filter(std::move(f)), + label(std::move(l)), + language(lang), + default_value(false), + bp() {} + + void SetBreakpoint(); + void ClearBreakpoint(); +}; + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/FunctionBreakpoint.cpp b/tools/lldb-vscode/FunctionBreakpoint.cpp new file mode 100644 index 000000000000..f83333dc9895 --- /dev/null +++ b/tools/lldb-vscode/FunctionBreakpoint.cpp @@ -0,0 +1,28 @@ +//===-- FunctionBreakpoint.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FunctionBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +FunctionBreakpoint::FunctionBreakpoint(const llvm::json::Object &obj) + : BreakpointBase(obj), functionName(GetString(obj, "name")) {} + +void FunctionBreakpoint::SetBreakpoint() { + if (functionName.empty()) + return; + bp = g_vsc.target.BreakpointCreateByName(functionName.c_str()); + if (!condition.empty()) + SetCondition(); + if (!hitCondition.empty()) + SetHitCondition(); +} + +} diff --git a/tools/lldb-vscode/FunctionBreakpoint.h b/tools/lldb-vscode/FunctionBreakpoint.h new file mode 100644 index 000000000000..ff4f34dba077 --- /dev/null +++ b/tools/lldb-vscode/FunctionBreakpoint.h @@ -0,0 +1,29 @@ +//===-- FunctionBreakpoint.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_FUNCTIONBREAKPOINT_H_ +#define LLDBVSCODE_FUNCTIONBREAKPOINT_H_ + +#include "BreakpointBase.h" + +namespace lldb_vscode { + +struct FunctionBreakpoint : public BreakpointBase { + std::string functionName; + + FunctionBreakpoint() = default; + FunctionBreakpoint(const llvm::json::Object &obj); + + // Set this breakpoint in LLDB as a new breakpoint + void SetBreakpoint(); +}; + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/JSONUtils.cpp b/tools/lldb-vscode/JSONUtils.cpp new file mode 100644 index 000000000000..76cd44cc72d1 --- /dev/null +++ b/tools/lldb-vscode/JSONUtils.cpp @@ -0,0 +1,892 @@ +//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <algorithm> + +#include "llvm/Support/FormatAdapters.h" + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBValue.h" +#include "lldb/Host/PosixApi.h" + +#include "ExceptionBreakpoint.h" +#include "JSONUtils.h" +#include "LLDBUtils.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + llvm::StringRef str) { + if (LLVM_LIKELY(llvm::json::isUTF8(str))) + obj.try_emplace(key, str.str()); + else + obj.try_emplace(key, llvm::json::fixUTF8(str)); +} + +llvm::StringRef GetAsString(const llvm::json::Value &value) { + if (auto s = value.getAsString()) + return *s; + return llvm::StringRef(); +} + +// Gets a string from a JSON object using the key, or returns an empty string. +llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) { + if (auto value = obj.getString(key)) + return GetAsString(*value); + return llvm::StringRef(); +} + +llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) { + if (obj == nullptr) + return llvm::StringRef(); + return GetString(*obj, key); +} + +// Gets an unsigned integer from a JSON object using the key, or returns the +// specified fail value. +uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, + uint64_t fail_value) { + if (auto value = obj.getInteger(key)) + return (uint64_t)*value; + return fail_value; +} + +uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, + uint64_t fail_value) { + if (obj == nullptr) + return fail_value; + return GetUnsigned(*obj, key, fail_value); +} + +bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, + bool fail_value) { + if (auto value = obj.getBoolean(key)) + return *value; + if (auto value = obj.getInteger(key)) + return *value != 0; + return fail_value; +} + +bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, + bool fail_value) { + if (obj == nullptr) + return fail_value; + return GetBoolean(*obj, key, fail_value); +} + +int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, + int64_t fail_value) { + if (auto value = obj.getInteger(key)) + return *value; + return fail_value; +} + +int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, + int64_t fail_value) { + if (obj == nullptr) + return fail_value; + return GetSigned(*obj, key, fail_value); +} + +bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) { + return obj.find(key) != obj.end(); +} + +std::vector<std::string> GetStrings(const llvm::json::Object *obj, + llvm::StringRef key) { + std::vector<std::string> strs; + auto json_array = obj->getArray(key); + if (!json_array) + return strs; + for (const auto &value : *json_array) { + switch (value.kind()) { + case llvm::json::Value::String: + strs.push_back(value.getAsString()->str()); + break; + case llvm::json::Value::Number: + case llvm::json::Value::Boolean: { + std::string s; + llvm::raw_string_ostream strm(s); + strm << value; + strs.push_back(strm.str()); + break; + } + case llvm::json::Value::Null: + case llvm::json::Value::Object: + case llvm::json::Value::Array: + break; + } + } + return strs; +} + +void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, + llvm::StringRef key) { + + llvm::StringRef value = v.GetValue(); + llvm::StringRef summary = v.GetSummary(); + llvm::StringRef type_name = v.GetType().GetDisplayTypeName(); + + std::string result; + llvm::raw_string_ostream strm(result); + if (!value.empty()) { + strm << value; + if (!summary.empty()) + strm << ' ' << summary; + } else if (!summary.empty()) { + strm << ' ' << summary; + } else if (!type_name.empty()) { + strm << type_name; + lldb::addr_t address = v.GetLoadAddress(); + if (address != LLDB_INVALID_ADDRESS) + strm << " @ " << llvm::format_hex(address, 0); + } + strm.flush(); + EmplaceSafeString(object, key, result); +} + +void FillResponse(const llvm::json::Object &request, + llvm::json::Object &response) { + // Fill in all of the needed response fields to a "request" and set "success" + // to true by default. + response.try_emplace("type", "response"); + response.try_emplace("seq", (int64_t)0); + EmplaceSafeString(response, "command", GetString(request, "command")); + const int64_t seq = GetSigned(request, "seq", 0); + response.try_emplace("request_seq", seq); + response.try_emplace("success", true); +} + +//---------------------------------------------------------------------- +// "Scope": { +// "type": "object", +// "description": "A Scope is a named container for variables. Optionally +// a scope can map to a source or a range within a source.", +// "properties": { +// "name": { +// "type": "string", +// "description": "Name of the scope such as 'Arguments', 'Locals'." +// }, +// "variablesReference": { +// "type": "integer", +// "description": "The variables of this scope can be retrieved by +// passing the value of variablesReference to the +// VariablesRequest." +// }, +// "namedVariables": { +// "type": "integer", +// "description": "The number of named variables in this scope. The +// client can use this optional information to present +// the variables in a paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "integer", +// "description": "The number of indexed variables in this scope. The +// client can use this optional information to present +// the variables in a paged UI and fetch them in chunks." +// }, +// "expensive": { +// "type": "boolean", +// "description": "If true, the number of variables in this scope is +// large or expensive to retrieve." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "Optional source for this scope." +// }, +// "line": { +// "type": "integer", +// "description": "Optional start line of the range covered by this +// scope." +// }, +// "column": { +// "type": "integer", +// "description": "Optional start column of the range covered by this +// scope." +// }, +// "endLine": { +// "type": "integer", +// "description": "Optional end line of the range covered by this scope." +// }, +// "endColumn": { +// "type": "integer", +// "description": "Optional end column of the range covered by this +// scope." +// } +// }, +// "required": [ "name", "variablesReference", "expensive" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateScope(const llvm::StringRef name, + int64_t variablesReference, + int64_t namedVariables, bool expensive) { + llvm::json::Object object; + EmplaceSafeString(object, "name", name.str()); + object.try_emplace("variablesReference", variablesReference); + object.try_emplace("expensive", expensive); + object.try_emplace("namedVariables", namedVariables); + return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Breakpoint": { +// "type": "object", +// "description": "Information about a Breakpoint created in setBreakpoints +// or setFunctionBreakpoints.", +// "properties": { +// "id": { +// "type": "integer", +// "description": "An optional unique identifier for the breakpoint." +// }, +// "verified": { +// "type": "boolean", +// "description": "If true breakpoint could be set (but not necessarily +// at the desired location)." +// }, +// "message": { +// "type": "string", +// "description": "An optional message about the state of the breakpoint. +// This is shown to the user and can be used to explain +// why a breakpoint could not be verified." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The source where the breakpoint is located." +// }, +// "line": { +// "type": "integer", +// "description": "The start line of the actual range covered by the +// breakpoint." +// }, +// "column": { +// "type": "integer", +// "description": "An optional start column of the actual range covered +// by the breakpoint." +// }, +// "endLine": { +// "type": "integer", +// "description": "An optional end line of the actual range covered by +// the breakpoint." +// }, +// "endColumn": { +// "type": "integer", +// "description": "An optional end column of the actual range covered by +// the breakpoint. If no end line is given, then the end +// column is assumed to be in the start line." +// } +// }, +// "required": [ "verified" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc) { + // Each breakpoint location is treated as a separate breakpoint for VS code. + // They don't have the notion of a single breakpoint with multiple locations. + llvm::json::Object object; + if (!bp_loc.IsValid()) + return llvm::json::Value(std::move(object)); + + object.try_emplace("verified", true); + const auto vs_id = MakeVSCodeBreakpointID(bp_loc); + object.try_emplace("id", vs_id); + auto bp_addr = bp_loc.GetAddress(); + if (bp_addr.IsValid()) { + auto line_entry = bp_addr.GetLineEntry(); + const auto line = line_entry.GetLine(); + if (line != UINT32_MAX) + object.try_emplace("line", line); + object.try_emplace("source", CreateSource(line_entry)); + } + return llvm::json::Value(std::move(object)); +} + +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints) { + if (!bp.IsValid()) + return; + const auto num_locations = bp.GetNumLocations(); + if (num_locations == 0) + return; + for (size_t i = 0; i < num_locations; ++i) { + auto bp_loc = bp.GetLocationAtIndex(i); + breakpoints.emplace_back(CreateBreakpoint(bp_loc)); + } +} + +//---------------------------------------------------------------------- +// "Event": { +// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { +// "type": "object", +// "description": "Server-initiated event.", +// "properties": { +// "type": { +// "type": "string", +// "enum": [ "event" ] +// }, +// "event": { +// "type": "string", +// "description": "Type of event." +// }, +// "body": { +// "type": [ "array", "boolean", "integer", "null", "number" , +// "object", "string" ], +// "description": "Event-specific information." +// } +// }, +// "required": [ "type", "event" ] +// }] +// }, +// "ProtocolMessage": { +// "type": "object", +// "description": "Base class of requests, responses, and events.", +// "properties": { +// "seq": { +// "type": "integer", +// "description": "Sequence number." +// }, +// "type": { +// "type": "string", +// "description": "Message type.", +// "_enum": [ "request", "response", "event" ] +// } +// }, +// "required": [ "seq", "type" ] +// } +//---------------------------------------------------------------------- +llvm::json::Object CreateEventObject(const llvm::StringRef event_name) { + llvm::json::Object event; + event.try_emplace("seq", 0); + event.try_emplace("type", "event"); + EmplaceSafeString(event, "event", event_name); + return event; +} + +//---------------------------------------------------------------------- +// "ExceptionBreakpointsFilter": { +// "type": "object", +// "description": "An ExceptionBreakpointsFilter is shown in the UI as an +// option for configuring how exceptions are dealt with.", +// "properties": { +// "filter": { +// "type": "string", +// "description": "The internal ID of the filter. This value is passed +// to the setExceptionBreakpoints request." +// }, +// "label": { +// "type": "string", +// "description": "The name of the filter. This will be shown in the UI." +// }, +// "default": { +// "type": "boolean", +// "description": "Initial value of the filter. If not specified a value +// 'false' is assumed." +// } +// }, +// "required": [ "filter", "label" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value +CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { + llvm::json::Object object; + EmplaceSafeString(object, "filter", bp.filter); + EmplaceSafeString(object, "label", bp.label); + object.try_emplace("default", bp.default_value); + return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Source": { +// "type": "object", +// "description": "A Source is a descriptor for source code. It is returned +// from the debug adapter as part of a StackFrame and it is +// used by clients when specifying breakpoints.", +// "properties": { +// "name": { +// "type": "string", +// "description": "The short name of the source. Every source returned +// from the debug adapter has a name. When sending a +// source to the debug adapter this name is optional." +// }, +// "path": { +// "type": "string", +// "description": "The path of the source to be shown in the UI. It is +// only used to locate and load the content of the +// source if no sourceReference is specified (or its +// value is 0)." +// }, +// "sourceReference": { +// "type": "number", +// "description": "If sourceReference > 0 the contents of the source must +// be retrieved through the SourceRequest (even if a path +// is specified). A sourceReference is only valid for a +// session, so it must not be used to persist a source." +// }, +// "presentationHint": { +// "type": "string", +// "description": "An optional hint for how to present the source in the +// UI. A value of 'deemphasize' can be used to indicate +// that the source is not available or that it is +// skipped on stepping.", +// "enum": [ "normal", "emphasize", "deemphasize" ] +// }, +// "origin": { +// "type": "string", +// "description": "The (optional) origin of this source: possible values +// 'internal module', 'inlined content from source map', +// etc." +// }, +// "sources": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Source" +// }, +// "description": "An optional list of sources that are related to this +// source. These may be the source that generated this +// source." +// }, +// "adapterData": { +// "type":["array","boolean","integer","null","number","object","string"], +// "description": "Optional data that a debug adapter might want to loop +// through the client. The client should leave the data +// intact and persist it across sessions. The client +// should not interpret the data." +// }, +// "checksums": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Checksum" +// }, +// "description": "The checksums associated with this file." +// } +// } +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) { + llvm::json::Object object; + lldb::SBFileSpec file = line_entry.GetFileSpec(); + if (file.IsValid()) { + const char *name = file.GetFilename(); + if (name) + EmplaceSafeString(object, "name", name); + char path[PATH_MAX] = ""; + file.GetPath(path, sizeof(path)); + if (path[0]) { + EmplaceSafeString(object, "path", std::string(path)); + } + } + return llvm::json::Value(std::move(object)); +} + +llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { + disasm_line = 0; + auto line_entry = frame.GetLineEntry(); + if (line_entry.GetFileSpec().IsValid()) + return CreateSource(line_entry); + + llvm::json::Object object; + const auto pc = frame.GetPC(); + + lldb::SBInstructionList insts; + lldb::SBFunction function = frame.GetFunction(); + lldb::addr_t low_pc = LLDB_INVALID_ADDRESS; + if (function.IsValid()) { + low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target); + auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); + if (addr_srcref != g_vsc.addr_to_source_ref.end()) { + // We have this disassembly cached already, return the existing + // sourceReference + object.try_emplace("sourceReference", addr_srcref->second); + disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); + } else { + insts = function.GetInstructions(g_vsc.target); + } + } else { + lldb::SBSymbol symbol = frame.GetSymbol(); + if (symbol.IsValid()) { + low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target); + auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); + if (addr_srcref != g_vsc.addr_to_source_ref.end()) { + // We have this disassembly cached already, return the existing + // sourceReference + object.try_emplace("sourceReference", addr_srcref->second); + disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); + } else { + insts = symbol.GetInstructions(g_vsc.target); + } + } + } + const auto num_insts = insts.GetSize(); + if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) { + EmplaceSafeString(object, "name", frame.GetFunctionName()); + SourceReference source; + llvm::raw_string_ostream src_strm(source.content); + std::string line; + for (size_t i = 0; i < num_insts; ++i) { + lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); + const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target); + const char *m = inst.GetMnemonic(g_vsc.target); + const char *o = inst.GetOperands(g_vsc.target); + const char *c = inst.GetComment(g_vsc.target); + if (pc == inst_addr) + disasm_line = i + 1; + const auto inst_offset = inst_addr - low_pc; + int spaces = 0; + if (inst_offset < 10) + spaces = 3; + else if (inst_offset < 100) + spaces = 2; + else if (inst_offset < 1000) + spaces = 1; + line.clear(); + llvm::raw_string_ostream line_strm(line); + line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr, + inst_offset, llvm::fmt_repeat(' ', spaces), m, + o); + + // If there is a comment append it starting at column 60 or after one + // space past the last char + const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60); + if (c && c[0]) { + if (line.size() < comment_row) + line_strm.indent(comment_row - line_strm.str().size()); + line_strm << " # " << c; + } + src_strm << line_strm.str() << "\n"; + source.addr_to_line[inst_addr] = i + 1; + } + // Flush the source stream + src_strm.str(); + auto sourceReference = VSCode::GetNextSourceReference(); + g_vsc.source_map[sourceReference] = std::move(source); + g_vsc.addr_to_source_ref[low_pc] = sourceReference; + object.try_emplace("sourceReference", sourceReference); + } + return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "StackFrame": { +// "type": "object", +// "description": "A Stackframe contains the source location.", +// "properties": { +// "id": { +// "type": "integer", +// "description": "An identifier for the stack frame. It must be unique +// across all threads. This id can be used to retrieve +// the scopes of the frame with the 'scopesRequest' or +// to restart the execution of a stackframe." +// }, +// "name": { +// "type": "string", +// "description": "The name of the stack frame, typically a method name." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The optional source of the frame." +// }, +// "line": { +// "type": "integer", +// "description": "The line within the file of the frame. If source is +// null or doesn't exist, line is 0 and must be ignored." +// }, +// "column": { +// "type": "integer", +// "description": "The column within the line. If source is null or +// doesn't exist, column is 0 and must be ignored." +// }, +// "endLine": { +// "type": "integer", +// "description": "An optional end line of the range covered by the +// stack frame." +// }, +// "endColumn": { +// "type": "integer", +// "description": "An optional end column of the range covered by the +// stack frame." +// }, +// "moduleId": { +// "type": ["integer", "string"], +// "description": "The module associated with this frame, if any." +// }, +// "presentationHint": { +// "type": "string", +// "enum": [ "normal", "label", "subtle" ], +// "description": "An optional hint for how to present this frame in +// the UI. A value of 'label' can be used to indicate +// that the frame is an artificial frame that is used +// as a visual label or separator. A value of 'subtle' +// can be used to change the appearance of a frame in +// a 'subtle' way." +// } +// }, +// "required": [ "id", "name", "line", "column" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) { + llvm::json::Object object; + int64_t frame_id = MakeVSCodeFrameID(frame); + object.try_emplace("id", frame_id); + EmplaceSafeString(object, "name", frame.GetFunctionName()); + int64_t disasm_line = 0; + object.try_emplace("source", CreateSource(frame, disasm_line)); + + auto line_entry = frame.GetLineEntry(); + if (disasm_line > 0) { + object.try_emplace("line", disasm_line); + } else { + auto line = line_entry.GetLine(); + if (line == UINT32_MAX) + line = 0; + object.try_emplace("line", line); + } + object.try_emplace("column", line_entry.GetColumn()); + return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Thread": { +// "type": "object", +// "description": "A Thread", +// "properties": { +// "id": { +// "type": "integer", +// "description": "Unique identifier for the thread." +// }, +// "name": { +// "type": "string", +// "description": "A name of the thread." +// } +// }, +// "required": [ "id", "name" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateThread(lldb::SBThread &thread) { + llvm::json::Object object; + object.try_emplace("id", (int64_t)thread.GetThreadID()); + char thread_str[64]; + snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID()); + const char *name = thread.GetName(); + if (name) { + std::string thread_with_name(thread_str); + thread_with_name += ' '; + thread_with_name += name; + EmplaceSafeString(object, "name", thread_with_name); + } else { + EmplaceSafeString(object, "name", std::string(thread_str)); + } + return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "StoppedEvent": { +// "allOf": [ { "$ref": "#/definitions/Event" }, { +// "type": "object", +// "description": "Event message for 'stopped' event type. The event +// indicates that the execution of the debuggee has stopped +// due to some condition. This can be caused by a break +// point previously set, a stepping action has completed, +// by executing a debugger statement etc.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "stopped" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "reason": { +// "type": "string", +// "description": "The reason for the event. For backward +// compatibility this string is shown in the UI if +// the 'description' attribute is missing (but it +// must not be translated).", +// "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ] +// }, +// "description": { +// "type": "string", +// "description": "The full reason for the event, e.g. 'Paused +// on exception'. This string is shown in the UI +// as is." +// }, +// "threadId": { +// "type": "integer", +// "description": "The thread which was stopped." +// }, +// "text": { +// "type": "string", +// "description": "Additional information. E.g. if reason is +// 'exception', text contains the exception name. +// This string is shown in the UI." +// }, +// "allThreadsStopped": { +// "type": "boolean", +// "description": "If allThreadsStopped is true, a debug adapter +// can announce that all threads have stopped. +// The client should use this information to +// enable that all threads can be expanded to +// access their stacktraces. If the attribute +// is missing or false, only the thread with the +// given threadId can be expanded." +// } +// }, +// "required": [ "reason" ] +// } +// }, +// "required": [ "event", "body" ] +// }] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, + uint32_t stop_id) { + llvm::json::Object event(CreateEventObject("stopped")); + llvm::json::Object body; + switch (thread.GetStopReason()) { + case lldb::eStopReasonTrace: + case lldb::eStopReasonPlanComplete: + body.try_emplace("reason", "step"); + break; + case lldb::eStopReasonBreakpoint: { + ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); + if (exc_bp) { + body.try_emplace("reason", "exception"); + EmplaceSafeString(body, "description", exc_bp->label); + } else { + body.try_emplace("reason", "breakpoint"); + } + } break; + case lldb::eStopReasonWatchpoint: + case lldb::eStopReasonInstrumentation: + body.try_emplace("reason", "breakpoint"); + break; + case lldb::eStopReasonSignal: + body.try_emplace("reason", "exception"); + break; + case lldb::eStopReasonException: + body.try_emplace("reason", "exception"); + break; + case lldb::eStopReasonExec: + body.try_emplace("reason", "entry"); + break; + case lldb::eStopReasonThreadExiting: + case lldb::eStopReasonInvalid: + case lldb::eStopReasonNone: + break; + } + if (stop_id == 0) + body.try_emplace("reason", "entry"); + const lldb::tid_t tid = thread.GetThreadID(); + body.try_emplace("threadId", (int64_t)tid); + // If no description has been set, then set it to the default thread stopped + // description. If we have breakpoints that get hit and shouldn't be reported + // as breakpoints, then they will set the description above. + if (ObjectContainsKey(body, "description")) { + char description[1024]; + if (thread.GetStopDescription(description, sizeof(description))) { + EmplaceSafeString(body, "description", std::string(description)); + } + } + if (tid == g_vsc.focus_tid) { + body.try_emplace("threadCausedFocus", true); + } + body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid); + body.try_emplace("allThreadsStopped", true); + event.try_emplace("body", std::move(body)); + return llvm::json::Value(std::move(event)); +} + +//---------------------------------------------------------------------- +// "Variable": { +// "type": "object", +// "description": "A Variable is a name/value pair. Optionally a variable +// can have a 'type' that is shown if space permits or when +// hovering over the variable's name. An optional 'kind' is +// used to render additional properties of the variable, +// e.g. different icons can be used to indicate that a +// variable is public or private. If the value is +// structured (has children), a handle is provided to +// retrieve the children with the VariablesRequest. If +// the number of named or indexed children is large, the +// numbers should be returned via the optional +// 'namedVariables' and 'indexedVariables' attributes. The +// client can use this optional information to present the +// children in a paged UI and fetch them in chunks.", +// "properties": { +// "name": { +// "type": "string", +// "description": "The variable's name." +// }, +// "value": { +// "type": "string", +// "description": "The variable's value. This can be a multi-line text, +// e.g. for a function the body of a function." +// }, +// "type": { +// "type": "string", +// "description": "The type of the variable's value. Typically shown in +// the UI when hovering over the value." +// }, +// "presentationHint": { +// "$ref": "#/definitions/VariablePresentationHint", +// "description": "Properties of a variable that can be used to determine +// how to render the variable in the UI." +// }, +// "evaluateName": { +// "type": "string", +// "description": "Optional evaluatable name of this variable which can +// be passed to the 'EvaluateRequest' to fetch the +// variable's value." +// }, +// "variablesReference": { +// "type": "integer", +// "description": "If variablesReference is > 0, the variable is +// structured and its children can be retrieved by +// passing variablesReference to the VariablesRequest." +// }, +// "namedVariables": { +// "type": "integer", +// "description": "The number of named child variables. The client can +// use this optional information to present the children +// in a paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "integer", +// "description": "The number of indexed child variables. The client +// can use this optional information to present the +// children in a paged UI and fetch them in chunks." +// } +// }, +// "required": [ "name", "value", "variablesReference" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, + int64_t varID, bool format_hex) { + llvm::json::Object object; + auto name = v.GetName(); + EmplaceSafeString(object, "name", name ? name : "<null>"); + if (format_hex) + v.SetFormat(lldb::eFormatHex); + SetValueForKey(v, object, "value"); + auto type_cstr = v.GetType().GetDisplayTypeName(); + EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME); + if (varID != INT64_MAX) + object.try_emplace("id", varID); + if (v.MightHaveChildren()) + object.try_emplace("variablesReference", variablesReference); + else + object.try_emplace("variablesReference", (int64_t)0); + lldb::SBStream evaluateStream; + v.GetExpressionPath(evaluateStream); + const char *evaluateName = evaluateStream.GetData(); + if (evaluateName && evaluateName[0]) + EmplaceSafeString(object, "evaluateName", std::string(evaluateName)); + return llvm::json::Value(std::move(object)); +} + +} // namespace lldb_vscode + diff --git a/tools/lldb-vscode/JSONUtils.h b/tools/lldb-vscode/JSONUtils.h new file mode 100644 index 000000000000..0ca000ce9385 --- /dev/null +++ b/tools/lldb-vscode/JSONUtils.h @@ -0,0 +1,438 @@ +//===-- JSONUtils.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_JSONUTILS_H_ +#define LLDBVSCODE_JSONUTILS_H_ + +#include <stdint.h> +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include "VSCodeForward.h" + +namespace lldb_vscode { + +//------------------------------------------------------------------ +/// Emplace a StringRef in a json::Object after enusring that the +/// string is valid UTF8. If not, first call llvm::json::fixUTF8 +/// before emplacing. +/// +/// @param[in] obj +/// A JSON object that we will attempt to emplace the value in +/// +/// @param[in] key +/// The key to use when emplacing the value +/// +/// @param[in] str +/// The string to emplace +//------------------------------------------------------------------ +void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + llvm::StringRef str); + +//------------------------------------------------------------------ +/// Extract simple values as a string. +/// +/// @param[in] value +/// A JSON value to extract the string from. +/// +/// @return +/// A llvm::StringRef that contains the string value, or an empty +/// string if \a value isn't a string. +//------------------------------------------------------------------ +llvm::StringRef GetAsString(const llvm::json::Value &value); + +//------------------------------------------------------------------ +/// Extract the string value for the specified key from the +/// specified object. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// @param[in] key +/// The key to use when extracting the value +/// +/// @return +/// A llvm::StringRef that contains the string value for the +/// specified \a key, or an empty string if there is no key that +/// matches or if the value is not a string. +//------------------------------------------------------------------ +llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key); +llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key); + +//------------------------------------------------------------------ +/// Extract the unsigned integer value for the specified key from +/// the specified object. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// @param[in] key +/// The key to use when extracting the value +/// +/// @return +/// The unsigned integer value for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an integer. +//------------------------------------------------------------------ +uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, + uint64_t fail_value); +uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, + uint64_t fail_value); + +//------------------------------------------------------------------ +/// Extract the boolean value for the specified key from the +/// specified object. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// @param[in] key +/// The key to use when extracting the value +/// +/// @return +/// The boolean value for the specified \a key, or \a fail_value +/// if there is no key that matches or if the value is not a +/// boolean value of an integer. +//------------------------------------------------------------------ +bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, + bool fail_value); +bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, + bool fail_value); + +//------------------------------------------------------------------ +/// Extract the signed integer for the specified key from the +/// specified object. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// @param[in] key +/// The key to use when extracting the value +/// +/// @return +/// The signed integer value for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an integer. +//------------------------------------------------------------------ +int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, + int64_t fail_value); +int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, + int64_t fail_value); + +//------------------------------------------------------------------ +/// Check if the specified key exists in the specified object. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the value from +/// +/// @param[in] key +/// The key to check for +/// +/// @return +/// \b True if the key exists in the \a obj, \b False otherwise. +//------------------------------------------------------------------ +bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key); + +//------------------------------------------------------------------ +/// Extract an array of strings for the specified key from an object. +/// +/// String values in the array will be extracted without any quotes +/// around them. Numbers and Booleans will be converted into +/// strings. Any NULL, array or objects values in the array will be +/// ignored. +/// +/// @param[in] obj +/// A JSON object that we will attempt to extract the array from +/// +/// @param[in] key +/// The key to use when extracting the value +/// +/// @return +/// An array of string values for the specified \a key, or +/// \a fail_value if there is no key that matches or if the +/// value is not an array or all items in the array are not +/// strings, numbers or booleans. +//------------------------------------------------------------------ +std::vector<std::string> GetStrings(const llvm::json::Object *obj, + llvm::StringRef key); + +//------------------------------------------------------------------ +/// Fill a response object given the request object. +/// +/// The \a response object will get its "type" set to "response", +/// the "seq" set to zero, "response_seq" set to the "seq" value from +/// \a request, "command" set to the "command" from \a request, +/// and "success" set to true. +/// +/// @param[in] request +/// The request object received from a call to VSCode::ReadJSON(). +/// +/// @param[in,out] response +/// An empty llvm::json::Object object that will be filled +/// in as noted in description. +//------------------------------------------------------------------ +void FillResponse(const llvm::json::Object &request, + llvm::json::Object &response); + +//---------------------------------------------------------------------- +/// Emplace the string value from an SBValue into the supplied object +/// using \a key as the key that will contain the value. +/// +/// The value is what we will display in VS Code. Some SBValue objects +/// can have a value and/or a summary. If a value has both, we +/// combine the value and the summary into one string. If we only have a +/// value or summary, then that is considered the value. If there is +/// no value and no summary then the value is the type name followed by +/// the address of the type if it has an address. +/// +/// +/// @param[in] v +/// A lldb::SBValue object to extract the string value from +/// +/// +/// @param[in] object +/// The object to place the value object into +/// +/// +/// @param[in] key +/// The key name to use when inserting the value object we create +//---------------------------------------------------------------------- +void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, + llvm::StringRef key); + +//---------------------------------------------------------------------- +/// Converts \a bp to a JSON value and appends all locations to the +/// \a breakpoints array. +/// +/// @param[in] bp +/// A LLDB breakpoint object which will get all locations extracted +/// and converted into a JSON objects in the \a breakpoints array +/// +/// @param[in] breakpoints +/// A JSON array that will get a llvm::json::Value for \a bp +/// appended to it. +//---------------------------------------------------------------------- +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints); + +//---------------------------------------------------------------------- +/// Converts breakpoint location to a Visual Studio Code "Breakpoint" +/// JSON object and appends it to the \a breakpoints array. +/// +/// @param[in] bp_loc +/// A LLDB breakpoint location object to convert into a JSON value +/// +/// @return +/// A "Breakpoint" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc); + +//---------------------------------------------------------------------- +/// Create a "Event" JSON object using \a event_name as the event name +/// +/// @param[in] event_name +/// The string value to use for the "event" key in the JSON object. +/// +/// @return +/// A "Event" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Object CreateEventObject(const llvm::StringRef event_name); + +//---------------------------------------------------------------------- +/// Create a "ExceptionBreakpointsFilter" JSON object as described in +/// the Visual Studio Code debug adaptor definition. +/// +/// @param[in] bp +/// The exception breakppoint object to use +/// +/// @return +/// A "ExceptionBreakpointsFilter" JSON object with that follows +/// the formal JSON definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value +CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp); + +//---------------------------------------------------------------------- +/// Create a "Scope" JSON object as described in the Visual Studio Code +/// debug adaptor definition. +/// +/// @param[in] name +/// The value to place into the "name" key +// +/// @param[in] variablesReference +/// The value to place into the "variablesReference" key +// +/// @param[in] namedVariables +/// The value to place into the "namedVariables" key +// +/// @param[in] expensive +/// The value to place into the "expensive" key +/// +/// @return +/// A "Scope" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateScope(const llvm::StringRef name, + int64_t variablesReference, + int64_t namedVariables, bool expensive); + +//---------------------------------------------------------------------- +/// Create a "Source" JSON object as described in the Visual Studio Code +/// debug adaptor definition. +/// +/// @param[in] line_entry +/// The LLDB line table to use when populating out the "Source" +/// object +/// +/// @return +/// A "Source" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry); + +//---------------------------------------------------------------------- +/// Create a "Source" object for a given frame. +/// +/// When there is no source file information for a stack frame, we will +/// create disassembly for a function and store a permanent +/// "sourceReference" that contains the textual disassembly for a +/// function along with address to line information. The "Source" object +/// that is created will contain a "sourceReference" that the VSCode +/// protocol can later fetch as text in order to display disassembly. +/// The PC will be extracted from the frame and the disassembly line +/// within the source referred to by "sourceReference" will be filled +/// in. +/// +/// @param[in] frame +/// The LLDB stack frame to use when populating out the "Source" +/// object. +/// +/// @param[out] disasm_line +/// The line within the "sourceReference" file that the PC from +/// \a frame matches. +/// +/// @return +/// A "Source" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line); + +//---------------------------------------------------------------------- +/// Create a "StackFrame" object for a LLDB frame object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "id" - the stack frame ID as an integer +/// "name" - the function name as a string +/// "source" - source file information as a "Source" VSCode object +/// "line" - the source file line number as an integer +/// "column" - the source file column number as an integer +/// +/// @param[in] frame +/// The LLDB stack frame to use when populating out the "StackFrame" +/// object. +/// +/// @return +/// A "StackFrame" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateStackFrame(lldb::SBFrame &frame); + +//---------------------------------------------------------------------- +/// Create a "Thread" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "id" - the thread ID as an integer +/// "name" - the thread name as a string which combines the LLDB +/// thread index ID along with the string name of the thread +/// from the OS if it has a name. +/// +/// @param[in] thread +/// The LLDB thread to use when populating out the "Thread" +/// object. +/// +/// @return +/// A "Thread" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateThread(lldb::SBThread &thread); + +//---------------------------------------------------------------------- +/// Create a "StoppedEvent" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object's "body" object: +/// "reason" - With a valid stop reason enumeration string value +/// that Microsoft specifies +/// "threadId" - The thread ID as an integer +/// "description" - a stop description (like "breakpoint 12.3") as a +/// string +/// "preserveFocusHint" - a boolean value that states if this thread +/// should keep the focus in the GUI. +/// "allThreadsStopped" - set to True to indicate that all threads +/// stop when any thread stops. +/// +/// @param[in] thread +/// The LLDB thread to use when populating out the "StoppedEvent" +/// object. +/// +/// @return +/// A "StoppedEvent" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, uint32_t stop_id); + +//---------------------------------------------------------------------- +/// Create a "Variable" object for a LLDB thread object. +/// +/// This function will fill in the following keys in the returned +/// object: +/// "name" - the name of the variable +/// "value" - the value of the variable as a string +/// "type" - the typename of the variable as a string +/// "id" - a unique identifier for a value in case there are multiple +/// variables with the same name. Other parts of the VSCode +/// protocol refer to values by name so this can help +/// disambiguate such cases if a IDE passes this "id" value +/// back down. +/// "variablesReference" - Zero if the variable has no children, +/// non-zero integer otherwise which can be used to expand +/// the variable. +/// "evaluateName" - The name of the variable to use in expressions +/// as a string. +/// +/// @param[in] v +/// The LLDB value to use when populating out the "Variable" +/// object. +/// +/// @param[in] variablesReference +/// The variable reference. Zero if this value isn't structured +/// and has no children, non-zero if it does have children and +/// might be asked to expand itself. +/// +/// @param[in] varID +/// A unique variable identifier to help in properly identifying +/// variables with the same name. This is an extension to the +/// VS protocol. +/// +/// @param[in] format_hex +/// It set to true the variable will be formatted as hex in +/// the "value" key value pair for the value of the variable. +/// +/// @return +/// A "Variable" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +//---------------------------------------------------------------------- +llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, + int64_t varID, bool format_hex); + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/LLDBUtils.cpp b/tools/lldb-vscode/LLDBUtils.cpp new file mode 100644 index 000000000000..9653522a2d3a --- /dev/null +++ b/tools/lldb-vscode/LLDBUtils.cpp @@ -0,0 +1,98 @@ +//===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LLDBUtils.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands, + llvm::raw_ostream &strm) { + if (commands.empty()) + return; + lldb::SBCommandInterpreter interp = g_vsc.debugger.GetCommandInterpreter(); + if (!prefix.empty()) + strm << prefix << "\n"; + for (const auto &command : commands) { + lldb::SBCommandReturnObject result; + strm << "(lldb) " << command << "\n"; + interp.HandleCommand(command.c_str(), result); + auto output_len = result.GetOutputSize(); + if (output_len) { + const char *output = result.GetOutput(); + strm << output; + } + auto error_len = result.GetErrorSize(); + if (error_len) { + const char *error = result.GetError(); + strm << error; + } + } +} + +std::string RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands) { + std::string s; + llvm::raw_string_ostream strm(s); + RunLLDBCommands(prefix, commands, strm); + strm.flush(); + return s; +} + +bool ThreadHasStopReason(lldb::SBThread &thread) { + switch (thread.GetStopReason()) { + case lldb::eStopReasonTrace: + case lldb::eStopReasonPlanComplete: + case lldb::eStopReasonBreakpoint: + case lldb::eStopReasonWatchpoint: + case lldb::eStopReasonInstrumentation: + case lldb::eStopReasonSignal: + case lldb::eStopReasonException: + case lldb::eStopReasonExec: + return true; + case lldb::eStopReasonThreadExiting: + case lldb::eStopReasonInvalid: + case lldb::eStopReasonNone: + break; + } + return false; +} + +static uint32_t constexpr THREAD_INDEX_SHIFT = 19; + +uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) { + return dap_frame_id >> THREAD_INDEX_SHIFT; +} + +uint32_t GetLLDBFrameID(uint64_t dap_frame_id) { + return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1); +} + +int64_t MakeVSCodeFrameID(lldb::SBFrame &frame) { + return (int64_t)(frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT | + frame.GetFrameID()); +} + +static uint32_t constexpr BREAKPOINT_ID_SHIFT = 22; + +uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id) { + return dap_breakpoint_id >> BREAKPOINT_ID_SHIFT; +} + +uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id) { + return dap_breakpoint_id & ((1u << BREAKPOINT_ID_SHIFT) - 1); +} + +int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc) { + return (int64_t)(bp_loc.GetBreakpoint().GetID() << BREAKPOINT_ID_SHIFT | + bp_loc.GetID()); +} + +} // namespace lldb_vscode diff --git a/tools/lldb-vscode/LLDBUtils.h b/tools/lldb-vscode/LLDBUtils.h new file mode 100644 index 000000000000..96ebb85f14b1 --- /dev/null +++ b/tools/lldb-vscode/LLDBUtils.h @@ -0,0 +1,170 @@ +//===-- LLDBUtils.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_LLDBUTILS_H_ +#define LLDBVSCODE_LLDBUTILS_H_ + +#include "VSCodeForward.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <string> +#include <vector> + +namespace lldb_vscode { + +///---------------------------------------------------------------------- +/// Run a list of LLDB commands in the LLDB command interpreter. +/// +/// All output from every command, including the prompt + the command +/// is placed into the "strm" argument. +/// +/// @param[in] prefix +/// A string that will be printed into \a strm prior to emitting +/// the prompt + command and command output. Can be NULL. +/// +/// @param[in] commands +/// An array of LLDB commands to execute. +/// +/// @param[in] strm +/// The stream that will receive the prefix, prompt + command and +/// all command output. +//---------------------------------------------------------------------- +void RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands, + llvm::raw_ostream &strm); + +///---------------------------------------------------------------------- +/// Run a list of LLDB commands in the LLDB command interpreter. +/// +/// All output from every command, including the prompt + the command +/// is returned in the std::string return value. +/// +/// @param[in] prefix +/// A string that will be printed into \a strm prior to emitting +/// the prompt + command and command output. Can be NULL. +/// +/// @param[in] commands +/// An array of LLDB commands to execute. +/// +/// @return +/// A std::string that contains the prefix and all commands and +/// command output +//---------------------------------------------------------------------- +std::string RunLLDBCommands(llvm::StringRef prefix, + const llvm::ArrayRef<std::string> &commands); + +///---------------------------------------------------------------------- +/// Check if a thread has a stop reason. +/// +/// @param[in] thread +/// The LLDB thread object to check +/// +/// @return +/// \b True if the thread has a valid stop reason, \b false +/// otherwise. +//---------------------------------------------------------------------- +bool ThreadHasStopReason(lldb::SBThread &thread); + +///---------------------------------------------------------------------- +/// Given a LLDB frame, make a frame ID that is unique to a specific +/// thread and frame. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower 32 bits and the thread index ID in the upper 32 +/// bits. +/// +/// @param[in] frame +/// The LLDB stack frame object generate the ID for +/// +/// @return +/// A unique integer that allows us to easily find the right +/// stack frame within a thread on subsequent VS code requests. +//---------------------------------------------------------------------- +int64_t MakeVSCodeFrameID(lldb::SBFrame &frame); + +///---------------------------------------------------------------------- +/// Given a VSCode frame ID, convert to a LLDB thread index id. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in +/// the upper 32 - THREAD_INDEX_SHIFT bits. +/// +/// @param[in] dap_frame_id +/// The VSCode frame ID to convert to a thread index ID. +/// +/// @return +/// The LLDB thread index ID. +//---------------------------------------------------------------------- +uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id); + +///---------------------------------------------------------------------- +/// Given a VSCode frame ID, convert to a LLDB frame ID. +/// +/// VSCode requires a Stackframe "id" to be unique, so we use the frame +/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in +/// the upper 32 - THREAD_INDEX_SHIFT bits. +/// +/// @param[in] dap_frame_id +/// The VSCode frame ID to convert to a frame ID. +/// +/// @return +/// The LLDB frame index ID. +//---------------------------------------------------------------------- +uint32_t GetLLDBFrameID(uint64_t dap_frame_id); + +///---------------------------------------------------------------------- +/// Given a LLDB breakpoint, make a breakpoint ID that is unique to a +/// specific breakpoint and breakpoint location. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// @param[in] frame +/// The LLDB stack frame object generate the ID for +/// +/// @return +/// A unique integer that allows us to easily find the right +/// stack frame within a thread on subsequent VS code requests. +//---------------------------------------------------------------------- +int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc); + +///---------------------------------------------------------------------- +/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint ID. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// @param[in] dap_breakpoint_id +/// The VSCode breakpoint ID to convert to an LLDB breakpoint ID. +/// +/// @return +/// The LLDB breakpoint ID. +//---------------------------------------------------------------------- +uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id); + +///---------------------------------------------------------------------- +/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint location ID. +/// +/// VSCode requires a Breakpoint "id" to be unique, so we use the +/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the +/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits. +/// +/// @param[in] dap_breakpoint_id +/// The VSCode frame ID to convert to a breakpoint location ID. +/// +/// @return +/// The LLDB breakpoint location ID. +//---------------------------------------------------------------------- +uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id); +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/README.md b/tools/lldb-vscode/README.md new file mode 100644 index 000000000000..2294659fc294 --- /dev/null +++ b/tools/lldb-vscode/README.md @@ -0,0 +1,195 @@ + +# Table of Contents + +- [Introduction](#Introduction) +- [Installation](#Installation-Visual-Studio-Code) +- [Configurations](#configurations) + - [Launch Configuration Settings](#launch-configuration-settings) + - [Attach Configuration Settings](#attach-configuration-settings) + - [Example configurations](#example-configurations) + - [Launching](#launching) + - [Attach to process using process ID](#attach-using-pid) + - [Attach to process by name](#attach-by-name) + - [Loading a core file](#loading-a-core-file) + +# Introduction + +The `lldb-vscode` tool creates a command line tool that implements the [Visual +Studio Code Debug API](https://code.visualstudio.com/docs/extensionAPI/api-debugging). +It can be installed as an extension for the Visual Studio Code and Nuclide IDE. +The protocol is easy to run remotely and also can allow other tools and IDEs to +get a full featured debugger with a well defined protocol. + +# Installation for Visual Studio Code + +Installing the plug-in involves creating a directory in the `~/.vscode/extensions` folder and copying the package.json file that is in the same directory as this +documentation into it, and copying to symlinking a lldb-vscode binary into +the `bin` directory inside the plug-in directory. + +If you want to make a stand alone plug-in that you can send to others on unix systems: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp /path/to/a/built/lldb-vscode . +$ cp /path/to/a/built/liblldb.so . +``` + + +If you want to make a stand alone plug-in that you can send to others on macOS systems: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp /path/to/a/built/lldb-vscode . +$ rsync -av /path/to/a/built/LLDB.framework LLDB.framework +``` + +You might need to create additional directories for the `liblldb.so` or `LLDB.framework` inside or next to the `bin` folder depending on how the [rpath](https://en.wikipedia.org/wiki/Rpath) is set in your `lldb-vscode` binary. By default the `Debug` builds of LLDB usually includes +the current executable directory in the rpath, so these steps should work for most people. + +To create a plug-in that symlinks into your `lldb-vscode` in your build directory: + +``` +$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0 +$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin +$ ln -s /path/to/a/built/lldb-vscode +``` + +This is handy if you want to debug and develope the `lldb-vscode` executable when adding features or fixing bugs. + +# Configurations + +Launching to attaching require you to create a [launch configuration](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations). This file +defines arguments that get passed to `lldb-vscode` and the configuration settings +control how the launch or attach happens. + +## Launch Configuration Settings + +When you launch a program with Visual Studio Code you will need to create a [launch.json](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations) +file that defines how your program will be run. The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs: + +|parameter |type|req | | +|-------------------|----|:--:|---------| +|**name** |string|Y| A configuration name that will be displayed in the IDE. +|**type** |string|Y| Must be "lldb-vscode". +|**request** |string|Y| Must be "launch". +|**program** |string|Y| Path to the executable to launch. +|**args** |[string]|| An array of command line argument strings to be passed to the program being launched. +|**cwd** |string| | The program working directory. +|**env** |dictionary| | Environment variables to set when launching the program. The format of each environment variable string is "VAR=VALUE" for environment variables with values or just "VAR" for environment variables with no values. +|**stopOnEntry** |boolean| | Whether to stop program immediately after launching. +|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed. +|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed. +|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed. +|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed. +|**sourceMap** |[string[2]]| | Specify an array of path re-mappings. Each element in the array must be a two element array containing a source and destination pathname. +|**debuggerRoot** | string| |Specify a working directory to use when launching lldb-vscode. If the debug information in your executable contains relative paths, this option can be used so that `lldb-vscode` can find source files and object files that have relative paths. + +## Attaching Settings + +When attaching to a process using LLDB you can attach in a few ways + +1. Attach to an existing process using the process ID +2. Attach to an existing process by name +3. Attach by name by waiting for the next instance of a process to launch + +The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs: + +|parameter |type |req | | +|-------------------|--------|:--:|---------| +|**name** |string |Y| A configuration name that will be displayed in the IDE. +|**type** |string |Y| Must be "lldb-vscode". +|**request** |string |Y| Must be "attach". +|**program** |string | | Path to the executable to attach to. This value is optional but can help to resolve breakpoints prior the attaching to the program. +|**pid** |number | | The process id of the process you wish to attach to. If **pid** is omitted, the debugger will attempt to attach to the program by finding a process whose file name matches the file name from **porgram**. Setting this value to `${command:pickMyProcess}` will allow interactive process selection in the IDE. +|**stopOnEntry** |boolean| | Whether to stop program immediately after launching. +|**waitFor** |boolean | | Wait for the process to launch. +|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed. +|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed. +|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed. +|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed. +|**attachCommands** |[string]| | LLDB commands that will be executed after **preRunCommands** which take place of the code that normally does the attach. The commands can create a new target and attach or launch it however desired. This allows custom launch and attach configurations. Core files can use `target create --core /path/to/core` to attach to core files. + + +## Example configurations + +### Launching + +This will launch `/tmp/a.out` with arguments `one`, `two`, and `three` and +adds `FOO=1` and `bar` to the environment: + +```javascript +{ + "type": "lldb-vscode", + "request": "launch", + "name": "Debug", + "program": "/tmp/a.out", + "args": [ "one", "two", "three" ], + "env": [ "FOO=1", "BAR" ], +} +``` + +### Attach using PID + +This will attach to a process `a.out` whose process ID is 123: + +```javascript +{ + "type": "lldb-vscode", + "request": "attach", + "name": "Attach to PID", + "program": "/tmp/a.out", + "pid": 123 +} +``` + +### Attach by Name + +This will attach to an existing process whose base +name matches `a.out`. All we have to do is leave the `pid` value out of the +above configuration: + +```javascript +{ + "name": "Attach to Name", + "type": "lldb-vscode", + "request": "attach", + "program": "/tmp/a.out", +} +``` + +If you want to ignore any existing a.out processes and wait for the next instance +to be launched you can add the "waitFor" key value pair: + +```javascript +{ + "name": "Attach to Name (wait)", + "type": "lldb-vscode", + "request": "attach", + "program": "/tmp/a.out", + "waitFor": true +} +``` + +This will work as long as the architecture, vendor and OS supports waiting +for processes. Currently MacOS is the only platform that supports this. + + +### Loading a Core File + +Loading a core file can use the `"attach"` request along with the +`"attachCommands"` to implement a custom attach: + +```javascript +{ + "name": "Attach to Name (wait)", + "type": "lldb-vscode", + "request": "attach", + "attachCommands": ["target create -c /path/to/123.core /path/to/executable"], + "stopOnEntry": false +} +``` diff --git a/tools/lldb-vscode/SourceBreakpoint.cpp b/tools/lldb-vscode/SourceBreakpoint.cpp new file mode 100644 index 000000000000..f2a286c9eca2 --- /dev/null +++ b/tools/lldb-vscode/SourceBreakpoint.cpp @@ -0,0 +1,27 @@ +//===-- SourceBreakpoint.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SourceBreakpoint.h" +#include "VSCode.h" + +namespace lldb_vscode { + +SourceBreakpoint::SourceBreakpoint(const llvm::json::Object &obj) + : BreakpointBase(obj), line(GetUnsigned(obj, "line", 0)), + column(GetUnsigned(obj, "column", 0)) {} + +void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { + bp = g_vsc.target.BreakpointCreateByLocation(source_path.str().c_str(), line); + if (!condition.empty()) + SetCondition(); + if (!hitCondition.empty()) + SetHitCondition(); +} + +} // namespace lldb_vscode diff --git a/tools/lldb-vscode/SourceBreakpoint.h b/tools/lldb-vscode/SourceBreakpoint.h new file mode 100644 index 000000000000..8af647ca8fe5 --- /dev/null +++ b/tools/lldb-vscode/SourceBreakpoint.h @@ -0,0 +1,39 @@ +//===-- SourceBreakpoint.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_SOURCEBREAKPOINT_H_ +#define LLDBVSCODE_SOURCEBREAKPOINT_H_ + +#include "llvm/ADT/StringRef.h" +#include "BreakpointBase.h" + +namespace lldb_vscode { + +struct SourceBreakpoint : public BreakpointBase { + + uint32_t line; ///< The source line of the breakpoint or logpoint + uint32_t column; ///< An optional source column of the breakpoint + + SourceBreakpoint() : BreakpointBase(), line(0), column(0) {} + SourceBreakpoint(const llvm::json::Object &obj); + + // Set this breakpoint in LLDB as a new breakpoint + void SetBreakpoint(const llvm::StringRef source_path); +}; + +inline bool operator<(const SourceBreakpoint &lhs, + const SourceBreakpoint &rhs) { + if (lhs.line == rhs.line) + return lhs.column < rhs.column; + return lhs.line < rhs.line; +} + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/SourceReference.h b/tools/lldb-vscode/SourceReference.h new file mode 100644 index 000000000000..f047143e6c84 --- /dev/null +++ b/tools/lldb-vscode/SourceReference.h @@ -0,0 +1,33 @@ +//===-- SourceReference.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_SOURCEREFERENCE_H_ +#define LLDBVSCODE_SOURCEREFERENCE_H_ + +#include "lldb/lldb-types.h" +#include "llvm/ADT/DenseMap.h" +#include <string> + +namespace lldb_vscode { + +struct SourceReference { + std::string content; + llvm::DenseMap<lldb::addr_t, int64_t> addr_to_line; + + int64_t GetLineForPC(lldb::addr_t pc) const { + auto addr_line = addr_to_line.find(pc); + if (addr_line != addr_to_line.end()) + return addr_line->second; + return 0; + } +}; + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/VSCode.cpp b/tools/lldb-vscode/VSCode.cpp new file mode 100644 index 000000000000..0f138ec77707 --- /dev/null +++ b/tools/lldb-vscode/VSCode.cpp @@ -0,0 +1,349 @@ +//===-- VSCode.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdarg.h> +#include <fstream> +#include <mutex> + +#include "VSCode.h" +#include "LLDBUtils.h" + +#if defined(_WIN32) +#include <io.h> +#include <fcntl.h> +#endif + +using namespace lldb_vscode; + +namespace { + inline bool IsEmptyLine(llvm::StringRef S) { + return S.ltrim().empty(); + } +} // namespace + +namespace lldb_vscode { + +VSCode g_vsc; + +VSCode::VSCode() + : in(stdin), out(stdout), launch_info(nullptr), variables(), + broadcaster("lldb-vscode"), num_regs(0), num_locals(0), num_globals(0), + log(), exception_breakpoints( + {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus}, + {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus}, + {"objc_catch", "Objective C Catch", lldb::eLanguageTypeObjC}, + {"objc_throw", "Objective C Throw", lldb::eLanguageTypeObjC}, + {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift}, + {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}), + focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false), + stop_at_entry(false) { + const char *log_file_path = getenv("LLDBVSCODE_LOG"); +#if defined(_WIN32) +// Windows opens stdout and stdin in text mode which converts \n to 13,10 +// while the value is just 10 on Darwin/Linux. Setting the file mode to binary +// fixes this. + assert(_setmode(fileno(stdout), _O_BINARY)); + assert(_setmode(fileno(stdin), _O_BINARY)); +#endif + if (log_file_path) + log.reset(new std::ofstream(log_file_path)); +} + +VSCode::~VSCode() { + CloseInputStream(); + CloseOutputStream(); +} + +void VSCode::CloseInputStream() { + if (in != stdin) { + fclose(in); + in = nullptr; + } +} + +void VSCode::CloseOutputStream() { + if (out != stdout) { + fclose(out); + out = nullptr; + } +} + +int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const { + auto pos = source_map.find(sourceReference); + if (pos != source_map.end()) + return pos->second.GetLineForPC(pc); + return 0; +} + +ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) { + for (auto &bp : exception_breakpoints) { + if (bp.filter == filter) + return &bp; + } + return nullptr; +} + +ExceptionBreakpoint * +VSCode::GetExceptionBreakpoint(const lldb::break_id_t bp_id) { + for (auto &bp : exception_breakpoints) { + if (bp.bp.GetID() == bp_id) + return &bp; + } + return nullptr; +} + +//---------------------------------------------------------------------- +// Send the JSON in "json_str" to the "out" stream. Correctly send the +// "Content-Length:" field followed by the length, followed by the raw +// JSON bytes. +//---------------------------------------------------------------------- +void VSCode::SendJSON(const std::string &json_str) { + fprintf(out, "Content-Length: %u\r\n\r\n%s", (uint32_t)json_str.size(), + json_str.c_str()); + fflush(out); + if (log) { + *log << "<-- " << std::endl + << "Content-Length: " << json_str.size() << "\r\n\r\n" + << json_str << std::endl; + } +} + +//---------------------------------------------------------------------- +// Serialize the JSON value into a string and send the JSON packet to +// the "out" stream. +//---------------------------------------------------------------------- +void VSCode::SendJSON(const llvm::json::Value &json) { + std::string s; + llvm::raw_string_ostream strm(s); + strm << json; + static std::mutex mutex; + std::lock_guard<std::mutex> locker(mutex); + SendJSON(strm.str()); +} + +//---------------------------------------------------------------------- +// Read a JSON packet from the "in" stream. +//---------------------------------------------------------------------- +std::string VSCode::ReadJSON() { + static std::string header("Content-Length: "); + + uint32_t packet_len = 0; + std::string json_str; + char line[1024]; + + while (fgets(line, sizeof(line), in)) { + if (strncmp(line, header.data(), header.size()) == 0) { + packet_len = atoi(line + header.size()); + if (fgets(line, sizeof(line), in)) { + if (!IsEmptyLine(line)) + if (log) + *log << "warning: expected empty line but got: \"" << line << "\"" + << std::endl; + break; + } + } else { + if (log) + *log << "warning: expected \"" << header << "\" but got: \"" << line + << "\"" << std::endl; + } + } + // This is followed by two windows newline sequences ("\r\n\r\n") so eat + // two the newline sequences + if (packet_len > 0) { + json_str.resize(packet_len); + auto bytes_read = fread(&json_str[0], 1, packet_len, in); + if (bytes_read < packet_len) { + if (log) + *log << "error: read fewer bytes (" << bytes_read + << ") than requested (" << packet_len << ")" << std::endl; + json_str.erase(bytes_read); + } + if (log) { + *log << "--> " << std::endl; + *log << header << packet_len << "\r\n\r\n" << json_str << std::endl; + } + } + return json_str; +} + +//---------------------------------------------------------------------- +// "OutputEvent": { +// "allOf": [ { "$ref": "#/definitions/Event" }, { +// "type": "object", +// "description": "Event message for 'output' event type. The event +// indicates that the target has produced some output.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "output" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "category": { +// "type": "string", +// "description": "The output category. If not specified, +// 'console' is assumed.", +// "_enum": [ "console", "stdout", "stderr", "telemetry" ] +// }, +// "output": { +// "type": "string", +// "description": "The output to report." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If an attribute 'variablesReference' exists +// and its value is > 0, the output contains +// objects which can be retrieved by passing +// variablesReference to the VariablesRequest." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "An optional source location where the output +// was produced." +// }, +// "line": { +// "type": "integer", +// "description": "An optional source location line where the +// output was produced." +// }, +// "column": { +// "type": "integer", +// "description": "An optional source location column where the +// output was produced." +// }, +// "data": { +// "type":["array","boolean","integer","null","number","object", +// "string"], +// "description": "Optional data to report. For the 'telemetry' +// category the data will be sent to telemetry, for +// the other categories the data is shown in JSON +// format." +// } +// }, +// "required": ["output"] +// } +// }, +// "required": [ "event", "body" ] +// }] +// } +//---------------------------------------------------------------------- +void VSCode::SendOutput(OutputType o, const llvm::StringRef output) { + if (output.empty()) + return; + + llvm::json::Object event(CreateEventObject("output")); + llvm::json::Object body; + const char *category = nullptr; + switch (o) { + case OutputType::Console: + category = "console"; + break; + case OutputType::Stdout: + category = "stdout"; + break; + case OutputType::Stderr: + category = "stderr"; + break; + case OutputType::Telemetry: + category = "telemetry"; + break; + } + body.try_emplace("category", category); + EmplaceSafeString(body, "output", output.str()); + event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(event))); +} + +void __attribute__((format(printf, 3, 4))) +VSCode::SendFormattedOutput(OutputType o, const char *format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + int actual_length = vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + SendOutput(o, llvm::StringRef(buffer, + std::min<int>(actual_length, sizeof(buffer)))); +} + +int64_t VSCode::GetNextSourceReference() { + static int64_t ref = 0; + return ++ref; +} + +ExceptionBreakpoint * +VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) { + const auto num = thread.GetStopReasonDataCount(); + // Check to see if have hit an exception breakpoint and change the + // reason to "exception", but only do so if all breakpoints that were + // hit are exception breakpoints. + ExceptionBreakpoint *exc_bp = nullptr; + for (size_t i = 0; i < num; i += 2) { + // thread.GetStopReasonDataAtIndex(i) will return the bp ID and + // thread.GetStopReasonDataAtIndex(i+1) will return the location + // within that breakpoint. We only care about the bp ID so we can + // see if this is an exception breakpoint that is getting hit. + lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i); + exc_bp = GetExceptionBreakpoint(bp_id); + // If any breakpoint is not an exception breakpoint, then stop and + // report this as a normal breakpoint + if (exc_bp == nullptr) + return nullptr; + } + return exc_bp; +} + +lldb::SBThread VSCode::GetLLDBThread(const llvm::json::Object &arguments) { + auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID); + return target.GetProcess().GetThreadByID(tid); +} + +lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) { + const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX); + lldb::SBProcess process = target.GetProcess(); + // Upper 32 bits is the thread index ID + lldb::SBThread thread = + process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id)); + // Lower 32 bits is the frame index + return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id)); +} + +llvm::json::Value VSCode::CreateTopLevelScopes() { + llvm::json::Array scopes; + scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, num_locals, false)); + scopes.emplace_back( + CreateScope("Globals", VARREF_GLOBALS, num_globals, false)); + scopes.emplace_back(CreateScope("Registers", VARREF_REGS, num_regs, false)); + return llvm::json::Value(std::move(scopes)); +} + +void VSCode::RunLLDBCommands(llvm::StringRef prefix, + const std::vector<std::string> &commands) { + SendOutput(OutputType::Console, + llvm::StringRef(::RunLLDBCommands(prefix, commands))); +} + +void VSCode::RunInitCommands() { + RunLLDBCommands("Running initCommands:", init_commands); +} + +void VSCode::RunPreRunCommands() { + RunLLDBCommands("Running preRunCommands:", pre_run_commands); +} + +void VSCode::RunStopCommands() { + RunLLDBCommands("Running stopCommands:", stop_commands); +} + +void VSCode::RunExitCommands() { + RunLLDBCommands("Running exitCommands:", exit_commands); +} + +} // namespace lldb_vscode + diff --git a/tools/lldb-vscode/VSCode.h b/tools/lldb-vscode/VSCode.h new file mode 100644 index 000000000000..142be3318367 --- /dev/null +++ b/tools/lldb-vscode/VSCode.h @@ -0,0 +1,146 @@ +//===-- VSCode.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_VSCODE_H_ +#define LLDBVSCODE_VSCODE_H_ + +#include <iosfwd> +#include <map> +#include <set> +#include <stdio.h> +#include <thread> + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +#include "lldb/API/SBAttachInfo.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.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/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBLaunchInfo.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +#include "ExceptionBreakpoint.h" +#include "FunctionBreakpoint.h" +#include "SourceBreakpoint.h" +#include "SourceReference.h" + +#define VARREF_LOCALS (int64_t)1 +#define VARREF_GLOBALS (int64_t)2 +#define VARREF_REGS (int64_t)3 +#define VARREF_FIRST_VAR_IDX (int64_t)4 +#define VARREF_IS_SCOPE(v) (VARREF_LOCALS <= 1 && v < VARREF_FIRST_VAR_IDX) +#define VARIDX_TO_VARREF(i) ((i) + VARREF_FIRST_VAR_IDX) +#define VARREF_TO_VARIDX(v) ((v)-VARREF_FIRST_VAR_IDX) +#define NO_TYPENAME "<no-type>" + +namespace lldb_vscode { + +typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; +typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; +enum class OutputType { Console, Stdout, Stderr, Telemetry }; + +struct VSCode { + FILE *in; + FILE *out; + lldb::SBDebugger debugger; + lldb::SBTarget target; + lldb::SBAttachInfo attach_info; + lldb::SBLaunchInfo launch_info; + lldb::SBValueList variables; + lldb::SBBroadcaster broadcaster; + int64_t num_regs; + int64_t num_locals; + int64_t num_globals; + std::thread event_thread; + std::unique_ptr<std::ofstream> log; + llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref; + llvm::DenseMap<int64_t, SourceReference> source_map; + llvm::StringMap<SourceBreakpointMap> source_breakpoints; + FunctionBreakpointMap function_breakpoints; + std::vector<ExceptionBreakpoint> exception_breakpoints; + std::vector<std::string> init_commands; + std::vector<std::string> pre_run_commands; + std::vector<std::string> exit_commands; + std::vector<std::string> stop_commands; + lldb::tid_t focus_tid; + bool sent_terminated_event; + bool stop_at_entry; + // Keep track of the last stop thread index IDs as threads won't go away + // unless we send a "thread" event to indicate the thread exited. + llvm::DenseSet<lldb::tid_t> thread_ids; + VSCode(); + ~VSCode(); + VSCode(const VSCode &rhs) = delete; + void operator=(const VSCode &rhs) = delete; + void CloseInputStream(); + void CloseOutputStream(); + int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const; + ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); + ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); + //---------------------------------------------------------------------- + // Send the JSON in "json_str" to the "out" stream. Correctly send the + // "Content-Length:" field followed by the length, followed by the raw + // JSON bytes. + //---------------------------------------------------------------------- + void SendJSON(const std::string &json_str); + + //---------------------------------------------------------------------- + // Serialize the JSON value into a string and send the JSON packet to + // the "out" stream. + //---------------------------------------------------------------------- + void SendJSON(const llvm::json::Value &json); + + std::string ReadJSON(); + + void SendOutput(OutputType o, const llvm::StringRef output); + + void __attribute__((format(printf, 3, 4))) + SendFormattedOutput(OutputType o, const char *format, ...); + + static int64_t GetNextSourceReference(); + + ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); + + lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); + + lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); + + llvm::json::Value CreateTopLevelScopes(); + + void RunLLDBCommands(llvm::StringRef prefix, + const std::vector<std::string> &commands); + + void RunInitCommands(); + void RunPreRunCommands(); + void RunStopCommands(); + void RunExitCommands(); +}; + +extern VSCode g_vsc; + +} // namespace lldb_vscode + +#endif diff --git a/tools/lldb-vscode/VSCodeForward.h b/tools/lldb-vscode/VSCodeForward.h new file mode 100644 index 000000000000..d5b7f8d53f77 --- /dev/null +++ b/tools/lldb-vscode/VSCodeForward.h @@ -0,0 +1,47 @@ +//===-- VSCodeForward.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDBVSCODE_VSCODEFORWARD_H_ +#define LLDBVSCODE_VSCODEFORWARD_H_ + + +namespace lldb_vscode { +struct BreakpointBase; +struct ExceptionBreakpoint; +struct FunctionBreakpoint; +struct SourceBreakpoint; +struct SourceReference; +} // namespace lldb_vscode + +namespace lldb { +class SBAttachInfo; +class SBBreakpoint; +class SBBreakpointLocation; +class SBCommandInterpreter; +class SBCommandReturnObject; +class SBCommunication; +class SBDebugger; +class SBEvent; +class SBFrame; +class SBHostOS; +class SBInstruction; +class SBInstructionList; +class SBLanguageRuntime; +class SBLaunchInfo; +class SBLineEntry; +class SBListener; +class SBProcess; +class SBStream; +class SBStringList; +class SBTarget; +class SBThread; +class SBValue; +} // namespace lldb + +#endif diff --git a/tools/lldb-vscode/lldb-vscode-Info.plist b/tools/lldb-vscode/lldb-vscode-Info.plist new file mode 100644 index 000000000000..a6b824372546 --- /dev/null +++ b/tools/lldb-vscode/lldb-vscode-Info.plist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleIdentifier</key> + <string>com.apple.lldb-vscode</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>lldb-vscode</string> + <key>CFBundleVersion</key> + <string>360.99.0</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + <string>debug</string> + </array> +</dict> +</plist> diff --git a/tools/lldb-vscode/lldb-vscode.cpp b/tools/lldb-vscode/lldb-vscode.cpp new file mode 100644 index 000000000000..2aad7977efa0 --- /dev/null +++ b/tools/lldb-vscode/lldb-vscode.cpp @@ -0,0 +1,2706 @@ +//===-- lldb-vscode.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#if defined(_WIN32) +// We need to #define NOMINMAX in order to skip `min()` and `max()` macro +// definitions that conflict with other system headers. +// We also need to #undef GetObject (which is defined to GetObjectW) because +// the JSON code we use also has methods named `GetObject()` and we conflict +// against these. +#define NOMINMAX +#include <windows.h> +#undef GetObject +#else +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include <algorithm> +#include <chrono> +#include <fstream> +#include <map> +#include <memory> +#include <mutex> +#include <set> +#include <sstream> +#include <thread> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +#include "JSONUtils.h" +#include "LLDBUtils.h" +#include "VSCode.h" + +#if defined(_WIN32) +#define PATH_MAX MAX_PATH +typedef int socklen_t; +constexpr const char *dev_null_path = "nul"; + +#else +typedef int SOCKET; +constexpr const char *dev_null_path = "/dev/null"; + +#endif + +using namespace lldb_vscode; + +namespace { + +typedef void (*RequestCallback)(const llvm::json::Object &command); + +enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch }; + +enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 }; + +int AcceptConnection(int portno) { + // Accept a socket connection from any host on "portno". + int newsockfd = -1; + struct sockaddr_in serv_addr, cli_addr; + SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + if (g_vsc.log) + *g_vsc.log << "error: opening socket (" << strerror(errno) << ")" + << std::endl; + } else { + memset((char *)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + serv_addr.sin_port = htons(portno); + if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + if (g_vsc.log) + *g_vsc.log << "error: binding socket (" << strerror(errno) << ")" + << std::endl; + } else { + listen(sockfd, 5); + socklen_t clilen = sizeof(cli_addr); + newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); + if (newsockfd < 0) + if (g_vsc.log) + *g_vsc.log << "error: accept (" << strerror(errno) << ")" + << std::endl; + } +#if defined(_WIN32) + closesocket(sockfd); +#else + close(sockfd); +#endif + } + return newsockfd; +} + +std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) { + // Create and return an array of "const char *", one for each C string in + // "strs" and terminate the list with a NULL. This can be used for argument + // vectors (argv) or environment vectors (envp) like those passed to the + // "main" function in C programs. + std::vector<const char *> argv; + for (const auto &s : strs) + argv.push_back(s.c_str()); + argv.push_back(nullptr); + return argv; +} + +//---------------------------------------------------------------------- +// Send a "exited" event to indicate the process has exited. +//---------------------------------------------------------------------- +void SendProcessExitedEvent(lldb::SBProcess &process) { + llvm::json::Object event(CreateEventObject("exited")); + llvm::json::Object body; + body.try_emplace("exitCode", (int64_t)process.GetExitStatus()); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +void SendThreadExitedEvent(lldb::tid_t tid) { + llvm::json::Object event(CreateEventObject("thread")); + llvm::json::Object body; + body.try_emplace("reason", "exited"); + body.try_emplace("threadId", (int64_t)tid); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +//---------------------------------------------------------------------- +// Send a "terminated" event to indicate the process is done being +// debugged. +//---------------------------------------------------------------------- +void SendTerminatedEvent() { + if (!g_vsc.sent_terminated_event) { + g_vsc.sent_terminated_event = true; + // Send a "terminated" event + llvm::json::Object event(CreateEventObject("terminated")); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); + } +} + +//---------------------------------------------------------------------- +// Send a thread stopped event for all threads as long as the process +// is stopped. +//---------------------------------------------------------------------- +void SendThreadStoppedEvent() { + lldb::SBProcess process = g_vsc.target.GetProcess(); + if (process.IsValid()) { + auto state = process.GetState(); + if (state == lldb::eStateStopped) { + llvm::DenseSet<lldb::tid_t> old_thread_ids; + old_thread_ids.swap(g_vsc.thread_ids); + uint32_t stop_id = process.GetStopID(); + const uint32_t num_threads = process.GetNumThreads(); + + // First make a pass through the threads to see if the focused thread + // has a stop reason. In case the focus thread doesn't have a stop + // reason, remember the first thread that has a stop reason so we can + // set it as the focus thread if below if needed. + lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID; + uint32_t num_threads_with_reason = 0; + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + const lldb::tid_t tid = thread.GetThreadID(); + const bool has_reason = ThreadHasStopReason(thread); + // If the focus thread doesn't have a stop reason, clear the thread ID + if (tid == g_vsc.focus_tid && !has_reason) + g_vsc.focus_tid = LLDB_INVALID_THREAD_ID; + if (has_reason) { + ++num_threads_with_reason; + if (first_tid_with_reason == LLDB_INVALID_THREAD_ID) + first_tid_with_reason = tid; + } + } + + // We will have cleared g_vsc.focus_tid if he focus thread doesn't + // have a stop reason, so if it was cleared, or wasn't set, then set the + // focus thread to the first thread with a stop reason. + if (g_vsc.focus_tid == LLDB_INVALID_THREAD_ID) + g_vsc.focus_tid = first_tid_with_reason; + + // If no threads stopped with a reason, then report the first one so + // we at least let the UI know we stopped. + if (num_threads_with_reason == 0) { + lldb::SBThread thread = process.GetThreadAtIndex(0); + g_vsc.SendJSON(CreateThreadStopped(thread, stop_id)); + } else { + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + g_vsc.thread_ids.insert(thread.GetThreadID()); + if (ThreadHasStopReason(thread)) { + g_vsc.SendJSON(CreateThreadStopped(thread, stop_id)); + } + } + } + + for (auto tid : old_thread_ids) { + auto end = g_vsc.thread_ids.end(); + auto pos = g_vsc.thread_ids.find(tid); + if (pos == end) + SendThreadExitedEvent(tid); + } + } else { + if (g_vsc.log) + *g_vsc.log << "error: SendThreadStoppedEvent() when process" + " isn't stopped (" + << lldb::SBDebugger::StateAsCString(state) << ')' + << std::endl; + } + } else { + if (g_vsc.log) + *g_vsc.log << "error: SendThreadStoppedEvent() invalid process" + << std::endl; + } + g_vsc.RunStopCommands(); +} + +//---------------------------------------------------------------------- +// "ProcessEvent": { +// "allOf": [ +// { "$ref": "#/definitions/Event" }, +// { +// "type": "object", +// "description": "Event message for 'process' event type. The event +// indicates that the debugger has begun debugging a +// new process. Either one that it has launched, or one +// that it has attached to.", +// "properties": { +// "event": { +// "type": "string", +// "enum": [ "process" ] +// }, +// "body": { +// "type": "object", +// "properties": { +// "name": { +// "type": "string", +// "description": "The logical name of the process. This is +// usually the full path to process's executable +// file. Example: /home/myproj/program.js." +// }, +// "systemProcessId": { +// "type": "integer", +// "description": "The system process id of the debugged process. +// This property will be missing for non-system +// processes." +// }, +// "isLocalProcess": { +// "type": "boolean", +// "description": "If true, the process is running on the same +// computer as the debug adapter." +// }, +// "startMethod": { +// "type": "string", +// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ], +// "description": "Describes how the debug engine started +// debugging this process.", +// "enumDescriptions": [ +// "Process was launched under the debugger.", +// "Debugger attached to an existing process.", +// "A project launcher component has launched a new process in +// a suspended state and then asked the debugger to attach." +// ] +// } +// }, +// "required": [ "name" ] +// } +// }, +// "required": [ "event", "body" ] +// } +// ] +// } +//---------------------------------------------------------------------- +void SendProcessEvent(LaunchMethod launch_method) { + lldb::SBFileSpec exe_fspec = g_vsc.target.GetExecutable(); + char exe_path[PATH_MAX]; + exe_fspec.GetPath(exe_path, sizeof(exe_path)); + llvm::json::Object event(CreateEventObject("process")); + llvm::json::Object body; + EmplaceSafeString(body, "name", std::string(exe_path)); + const auto pid = g_vsc.target.GetProcess().GetProcessID(); + body.try_emplace("systemProcessId", (int64_t)pid); + body.try_emplace("isLocalProcess", true); + const char *startMethod = nullptr; + switch (launch_method) { + case Launch: + startMethod = "launch"; + break; + case Attach: + startMethod = "attach"; + break; + case AttachForSuspendedLaunch: + startMethod = "attachForSuspendedLaunch"; + break; + } + body.try_emplace("startMethod", startMethod); + event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(event))); +} + +//---------------------------------------------------------------------- +// Grab any STDOUT and STDERR from the process and send it up to VS Code +// via an "output" event to the "stdout" and "stderr" categories. +//---------------------------------------------------------------------- +void SendStdOutStdErr(lldb::SBProcess &process) { + char buffer[1024]; + size_t count; + while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0) + g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count)); + while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0) + g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count)); +} + +//---------------------------------------------------------------------- +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +//---------------------------------------------------------------------- +void EventThreadFunction() { + lldb::SBEvent event; + lldb::SBListener listener = g_vsc.debugger.GetListener(); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateInvalid: + // Not a state event + break; + case lldb::eStateUnloaded: + break; + case lldb::eStateConnected: + break; + case lldb::eStateAttaching: + break; + case lldb::eStateLaunching: + break; + case lldb::eStateStepping: + break; + case lldb::eStateCrashed: + break; + case lldb::eStateDetached: + break; + case lldb::eStateSuspended: + break; + case lldb::eStateStopped: + // Only report a stopped event if the process was not restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(process); + SendThreadStoppedEvent(); + } + break; + case lldb::eStateRunning: + break; + case lldb::eStateExited: { + // Run any exit LLDB commands the user specified in the + // launch.json + g_vsc.RunExitCommands(); + SendProcessExitedEvent(process); + SendTerminatedEvent(); + done = true; + } break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(process); + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + const auto num_locs = + lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent(event); + auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event); + bool added = event_type & lldb::eBreakpointEventTypeLocationsAdded; + bool removed = + event_type & lldb::eBreakpointEventTypeLocationsRemoved; + if (added || removed) { + for (size_t i = 0; i < num_locs; ++i) { + auto bp_loc = + lldb::SBBreakpoint::GetBreakpointLocationAtIndexFromEvent( + event, i); + auto bp_event = CreateEventObject("breakpoint"); + llvm::json::Object body; + body.try_emplace("breakpoint", CreateBreakpoint(bp_loc)); + if (added) + body.try_emplace("reason", "new"); + else + body.try_emplace("reason", "removed"); + bp_event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } + } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + +//---------------------------------------------------------------------- +// Both attach and launch take a either a sourcePath or sourceMap +// argument (or neither), from which we need to set the target.source-map. +//---------------------------------------------------------------------- +void SetSourceMapFromArguments(const llvm::json::Object &arguments) { + const char *sourceMapHelp = + "source must be be an array of two-element arrays, " + "each containing a source and replacement path string.\n"; + + std::string sourceMapCommand; + llvm::raw_string_ostream strm(sourceMapCommand); + strm << "settings set target.source-map "; + auto sourcePath = GetString(arguments, "sourcePath"); + + // sourceMap is the new, more general form of sourcePath and overrides it. + auto sourceMap = arguments.getArray("sourceMap"); + if (sourceMap) { + for (const auto &value : *sourceMap) { + auto mapping = value.getAsArray(); + if (mapping == nullptr || mapping->size() != 2 || + (*mapping)[0].kind() != llvm::json::Value::String || + (*mapping)[1].kind() != llvm::json::Value::String) { + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + return; + } + auto mapFrom = GetAsString((*mapping)[0]); + auto mapTo = GetAsString((*mapping)[1]); + strm << "\"" << mapFrom << "\" \"" << mapTo << "\""; + } + } else { + if (ObjectContainsKey(arguments, "sourceMap")) { + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + return; + } + if (sourcePath.empty()) + return; + // Do any source remapping needed before we create our targets + strm << "\".\" \"" << sourcePath << "\""; + } + strm.flush(); + if (!sourceMapCommand.empty()) { + g_vsc.RunLLDBCommands("Setting source map:", {sourceMapCommand}); + } +} + +//---------------------------------------------------------------------- +// "AttachRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Attach request; value of command field is 'attach'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "attach" ] +// }, +// "arguments": { +// "$ref": "#/definitions/AttachRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "AttachRequestArguments": { +// "type": "object", +// "description": "Arguments for 'attach' request.\nThe attach request has no +// standardized attributes." +// }, +// "AttachResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'attach' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_attach(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + const lldb::pid_t pid = + GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID); + if (pid != LLDB_INVALID_PROCESS_ID) + g_vsc.attach_info.SetProcessID(pid); + const auto wait_for = GetBoolean(arguments, "waitFor", false); + g_vsc.attach_info.SetWaitForLaunch(wait_for, false /*async*/); + g_vsc.init_commands = GetStrings(arguments, "initCommands"); + g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands"); + g_vsc.stop_commands = GetStrings(arguments, "stopCommands"); + g_vsc.exit_commands = GetStrings(arguments, "exitCommands"); + auto attachCommands = GetStrings(arguments, "attachCommands"); + g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); + const auto debuggerRoot = GetString(arguments, "debuggerRoot"); + + // This is a hack for loading DWARF in .o files on Mac where the .o files + // in the debug map of the main executable have relative paths which require + // the lldb-vscode binary to have its working directory set to that relative + // root for the .o files in order to be able to load debug info. + if (!debuggerRoot.empty()) { + llvm::sys::fs::set_current_path(debuggerRoot.data()); + } + + // Run any initialize LLDB commands the user specified in the launch.json + g_vsc.RunInitCommands(); + + // Grab the name of the program we need to debug and set it as the first + // argument that will be passed to the program we will debug. + const auto program = GetString(arguments, "program"); + if (!program.empty()) { + lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/); + + g_vsc.launch_info.SetExecutableFile(program_fspec, + false /*add_as_first_arg*/); + const char *target_triple = nullptr; + const char *uuid_cstr = nullptr; + // Stand alone debug info file if different from executable + const char *symfile = nullptr; + g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile); + if (error.Fail()) { + response.try_emplace("success", false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + } + + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); + g_vsc.launch_info.SetDetachOnError(detatchOnError); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + + if (pid == LLDB_INVALID_PROCESS_ID && wait_for) { + char attach_info[256]; + auto attach_info_len = + snprintf(attach_info, sizeof(attach_info), + "Waiting to attach to \"%s\"...", program.data()); + g_vsc.SendOutput(OutputType::Console, llvm::StringRef(attach_info, + attach_info_len)); + } + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + // Disable async events so the attach will be successful when we return from + // the launch call and the launch will happen synchronously + g_vsc.debugger.SetAsync(false); + g_vsc.target.Attach(g_vsc.attach_info, error); + // Reenable async events + g_vsc.debugger.SetAsync(true); + } else { + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If there + // is no valid process after running these commands, we have failed. + g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands); + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + g_vsc.target = g_vsc.debugger.GetSelectedTarget(); + } + + SetSourceMapFromArguments(*arguments); + + if (error.Success()) { + auto attached_pid = g_vsc.target.GetProcess().GetProcessID(); + if (attached_pid == LLDB_INVALID_PROCESS_ID) { + if (attachCommands.empty()) + error.SetErrorString("failed to attach to a process"); + else + error.SetErrorString("attachCommands failed to attach to a process"); + } + } + + if (error.Fail()) { + response.try_emplace("success", false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + if (error.Success()) { + SendProcessEvent(Attach); + g_vsc.SendJSON(CreateEventObject("initialized")); + // SendThreadStoppedEvent(); + } +} + +//---------------------------------------------------------------------- +// "ContinueRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Continue request; value of command field is 'continue'. +// The request starts the debuggee to run again.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "continue" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ContinueArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "ContinueArguments": { +// "type": "object", +// "description": "Arguments for 'continue' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Continue execution for the specified thread (if +// possible). If the backend cannot continue on a single +// thread but will continue on all threads, it should +// set the allThreadsContinued attribute in the response +// to true." +// } +// }, +// "required": [ "threadId" ] +// }, +// "ContinueResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'continue' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "allThreadsContinued": { +// "type": "boolean", +// "description": "If true, the continue request has ignored the +// specified thread and continued all threads +// instead. If this attribute is missing a value +// of 'true' is assumed for backward +// compatibility." +// } +// } +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_continue(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBProcess process = g_vsc.target.GetProcess(); + auto arguments = request.getObject("arguments"); + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID); + lldb::SBError error = process.Continue(); + llvm::json::Object body; + body.try_emplace("allThreadsContinued", true); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "ConfigurationDoneRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "ConfigurationDone request; value of command field +// is 'configurationDone'.\nThe client of the debug protocol must +// send this request at the end of the sequence of configuration +// requests (which was started by the InitializedEvent).", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "configurationDone" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ConfigurationDoneArguments" +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "ConfigurationDoneArguments": { +// "type": "object", +// "description": "Arguments for 'configurationDone' request.\nThe +// configurationDone request has no standardized attributes." +// }, +// "ConfigurationDoneResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'configurationDone' request. This is +// just an acknowledgement, so no body field is required." +// }] +// }, +//---------------------------------------------------------------------- +void request_configurationDone(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + if (g_vsc.stop_at_entry) + SendThreadStoppedEvent(); + else + g_vsc.target.GetProcess().Continue(); +} + +//---------------------------------------------------------------------- +// "DisconnectRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Disconnect request; value of command field is +// 'disconnect'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "disconnect" ] +// }, +// "arguments": { +// "$ref": "#/definitions/DisconnectArguments" +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "DisconnectArguments": { +// "type": "object", +// "description": "Arguments for 'disconnect' request.", +// "properties": { +// "terminateDebuggee": { +// "type": "boolean", +// "description": "Indicates whether the debuggee should be terminated +// when the debugger is disconnected. If unspecified, +// the debug adapter is free to do whatever it thinks +// is best. A client can only rely on this attribute +// being properly honored if a debug adapter returns +// true for the 'supportTerminateDebuggee' capability." +// }, +// "restart": { +// "type": "boolean", +// "description": "Indicates whether the debuggee should be restart +// the process." +// } +// } +// }, +// "DisconnectResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'disconnect' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_disconnect(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + + bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee", false); + lldb::SBProcess process = g_vsc.target.GetProcess(); + auto state = process.GetState(); + + switch (state) { + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + case lldb::eStateDetached: + case lldb::eStateExited: + break; + case lldb::eStateConnected: + case lldb::eStateAttaching: + case lldb::eStateLaunching: + case lldb::eStateStepping: + case lldb::eStateCrashed: + case lldb::eStateSuspended: + case lldb::eStateStopped: + case lldb::eStateRunning: + g_vsc.debugger.SetAsync(false); + if (terminateDebuggee) + process.Kill(); + else + process.Detach(); + g_vsc.debugger.SetAsync(true); + break; + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + SendTerminatedEvent(); + if (g_vsc.event_thread.joinable()) { + g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread); + g_vsc.event_thread.join(); + } +} + +void request_exceptionInfo(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + llvm::json::Object body; + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + auto stopReason = thread.GetStopReason(); + if (stopReason == lldb::eStopReasonSignal) + body.try_emplace("exceptionId", "signal"); + else if (stopReason == lldb::eStopReasonBreakpoint) { + ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); + if (exc_bp) { + EmplaceSafeString(body, "exceptionId", exc_bp->filter); + EmplaceSafeString(body, "description", exc_bp->label); + } else { + body.try_emplace("exceptionId", "exception"); + } + } else { + body.try_emplace("exceptionId", "exception"); + } + if (!ObjectContainsKey(body, "description")) { + char description[1024]; + if (thread.GetStopDescription(description, sizeof(description))) { + EmplaceSafeString(body, "description", std::string(description)); + } + } + body.try_emplace("breakMode", "always"); + // auto excInfoCount = thread.GetStopReasonDataCount(); + // for (auto i=0; i<excInfoCount; ++i) { + // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i); + // } + } else { + response.try_emplace("success", false); + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "EvaluateRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Evaluate request; value of command field is 'evaluate'. +// Evaluates the given expression in the context of the +// top most stack frame. The expression has access to any +// variables and arguments that are in scope.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "evaluate" ] +// }, +// "arguments": { +// "$ref": "#/definitions/EvaluateArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "EvaluateArguments": { +// "type": "object", +// "description": "Arguments for 'evaluate' request.", +// "properties": { +// "expression": { +// "type": "string", +// "description": "The expression to evaluate." +// }, +// "frameId": { +// "type": "integer", +// "description": "Evaluate the expression in the scope of this stack +// frame. If not specified, the expression is evaluated +// in the global scope." +// }, +// "context": { +// "type": "string", +// "_enum": [ "watch", "repl", "hover" ], +// "enumDescriptions": [ +// "evaluate is run in a watch.", +// "evaluate is run from REPL console.", +// "evaluate is run from a data hover." +// ], +// "description": "The context in which the evaluate request is run." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the Evaluate +// result." +// } +// }, +// "required": [ "expression" ] +// }, +// "EvaluateResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'evaluate' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "result": { +// "type": "string", +// "description": "The result of the evaluate request." +// }, +// "type": { +// "type": "string", +// "description": "The optional type of the evaluate result." +// }, +// "presentationHint": { +// "$ref": "#/definitions/VariablePresentationHint", +// "description": "Properties of a evaluate result that can be +// used to determine how to render the result in +// the UI." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If variablesReference is > 0, the evaluate +// result is structured and its children can be +// retrieved by passing variablesReference to the +// VariablesRequest." +// }, +// "namedVariables": { +// "type": "number", +// "description": "The number of named child variables. The +// client can use this optional information to +// present the variables in a paged UI and fetch +// them in chunks." +// }, +// "indexedVariables": { +// "type": "number", +// "description": "The number of indexed child variables. The +// client can use this optional information to +// present the variables in a paged UI and fetch +// them in chunks." +// } +// }, +// "required": [ "result", "variablesReference" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_evaluate(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments); + const auto expression = GetString(arguments, "expression"); + + if (!expression.empty() && expression[0] == '`') { + auto result = RunLLDBCommands(llvm::StringRef(), + {expression.substr(1)}); + EmplaceSafeString(body, "result", result); + body.try_emplace("variablesReference", (int64_t)0); + } else { + // Always try to get the answer from the local variables if possible. If + // this fails, then actually evaluate an expression using the expression + // parser. "frame variable" is more reliable than the expression parser in + // many cases and it is faster. + lldb::SBValue value = frame.GetValueForVariablePath( + expression.data(), lldb::eDynamicDontRunTarget); + if (value.GetError().Fail()) + value = frame.EvaluateExpression(expression.data()); + if (value.GetError().Fail()) { + response.try_emplace("success", false); + const char *error_cstr = value.GetError().GetCString(); + if (error_cstr && error_cstr[0]) + EmplaceSafeString(response, "message", std::string(error_cstr)); + else + EmplaceSafeString(response, "message", "evaluate failed"); + } else { + SetValueForKey(value, body, "result"); + auto value_typename = value.GetType().GetDisplayTypeName(); + EmplaceSafeString(body, "type", value_typename ? value_typename : NO_TYPENAME); + if (value.MightHaveChildren()) { + auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize()); + g_vsc.variables.Append(value); + body.try_emplace("variablesReference", variablesReference); + } else { + body.try_emplace("variablesReference", (int64_t)0); + } + } + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "InitializeRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Initialize request; value of command field is +// 'initialize'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "initialize" ] +// }, +// "arguments": { +// "$ref": "#/definitions/InitializeRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "InitializeRequestArguments": { +// "type": "object", +// "description": "Arguments for 'initialize' request.", +// "properties": { +// "clientID": { +// "type": "string", +// "description": "The ID of the (frontend) client using this adapter." +// }, +// "adapterID": { +// "type": "string", +// "description": "The ID of the debug adapter." +// }, +// "locale": { +// "type": "string", +// "description": "The ISO-639 locale of the (frontend) client using +// this adapter, e.g. en-US or de-CH." +// }, +// "linesStartAt1": { +// "type": "boolean", +// "description": "If true all line numbers are 1-based (default)." +// }, +// "columnsStartAt1": { +// "type": "boolean", +// "description": "If true all column numbers are 1-based (default)." +// }, +// "pathFormat": { +// "type": "string", +// "_enum": [ "path", "uri" ], +// "description": "Determines in what format paths are specified. The +// default is 'path', which is the native format." +// }, +// "supportsVariableType": { +// "type": "boolean", +// "description": "Client supports the optional type attribute for +// variables." +// }, +// "supportsVariablePaging": { +// "type": "boolean", +// "description": "Client supports the paging of variables." +// }, +// "supportsRunInTerminalRequest": { +// "type": "boolean", +// "description": "Client supports the runInTerminal request." +// } +// }, +// "required": [ "adapterID" ] +// }, +// "InitializeResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'initialize' request.", +// "properties": { +// "body": { +// "$ref": "#/definitions/Capabilities", +// "description": "The capabilities of this debug adapter." +// } +// } +// }] +// } +//---------------------------------------------------------------------- +void request_initialize(const llvm::json::Object &request) { + g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/); + // Create an empty target right away since we might get breakpoint requests + // before we are given an executable to launch in a "launch" request, or a + // executable when attaching to a process by process ID in a "attach" + // request. + FILE *out = fopen(dev_null_path, "w"); + if (out) { + // Set the output and error file handles to redirect into nothing otherwise + // if any code in LLDB prints to the debugger file handles, the output and + // error file handles are initialized to STDOUT and STDERR and any output + // will kill our debug session. + g_vsc.debugger.SetOutputFileHandle(out, true); + g_vsc.debugger.SetErrorFileHandle(out, false); + } + + g_vsc.target = g_vsc.debugger.CreateTarget(nullptr); + lldb::SBListener listener = g_vsc.debugger.GetListener(); + listener.StartListeningForEvents( + g_vsc.target.GetBroadcaster(), + lldb::SBTarget::eBroadcastBitBreakpointChanged); + listener.StartListeningForEvents(g_vsc.broadcaster, + eBroadcastBitStopEventThread); + // Start our event thread so we can receive events from the debugger, target, + // process and more. + g_vsc.event_thread = std::thread(EventThreadFunction); + + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + // The debug adapter supports the configurationDoneRequest. + body.try_emplace("supportsConfigurationDoneRequest", true); + // The debug adapter supports function breakpoints. + body.try_emplace("supportsFunctionBreakpoints", true); + // The debug adapter supports conditional breakpoints. + body.try_emplace("supportsConditionalBreakpoints", true); + // The debug adapter supports breakpoints that break execution after a + // specified number of hits. + body.try_emplace("supportsHitConditionalBreakpoints", true); + // The debug adapter supports a (side effect free) evaluate request for + // data hovers. + body.try_emplace("supportsEvaluateForHovers", true); + // Available filters or options for the setExceptionBreakpoints request. + llvm::json::Array filters; + for (const auto &exc_bp : g_vsc.exception_breakpoints) { + filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp)); + } + body.try_emplace("exceptionBreakpointFilters", std::move(filters)); + // The debug adapter supports stepping back via the stepBack and + // reverseContinue requests. + body.try_emplace("supportsStepBack", false); + // The debug adapter supports setting a variable to a value. + body.try_emplace("supportsSetVariable", true); + // The debug adapter supports restarting a frame. + body.try_emplace("supportsRestartFrame", false); + // The debug adapter supports the gotoTargetsRequest. + body.try_emplace("supportsGotoTargetsRequest", false); + // The debug adapter supports the stepInTargetsRequest. + body.try_emplace("supportsStepInTargetsRequest", false); + // The debug adapter supports the completionsRequest. + body.try_emplace("supportsCompletionsRequest", false); + // The debug adapter supports the modules request. + body.try_emplace("supportsModulesRequest", false); + // The set of additional module information exposed by the debug adapter. + // body.try_emplace("additionalModuleColumns"] = ColumnDescriptor + // Checksum algorithms supported by the debug adapter. + // body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm + // The debug adapter supports the RestartRequest. In this case a client + // should not implement 'restart' by terminating and relaunching the adapter + // but by calling the RestartRequest. + body.try_emplace("supportsRestartRequest", false); + // The debug adapter supports 'exceptionOptions' on the + // setExceptionBreakpoints request. + body.try_emplace("supportsExceptionOptions", true); + // The debug adapter supports a 'format' attribute on the stackTraceRequest, + // variablesRequest, and evaluateRequest. + body.try_emplace("supportsValueFormattingOptions", true); + // The debug adapter supports the exceptionInfo request. + body.try_emplace("supportsExceptionInfoRequest", true); + // The debug adapter supports the 'terminateDebuggee' attribute on the + // 'disconnect' request. + body.try_emplace("supportTerminateDebuggee", true); + // The debug adapter supports the delayed loading of parts of the stack, + // which requires that both the 'startFrame' and 'levels' arguments and the + // 'totalFrames' result of the 'StackTrace' request are supported. + body.try_emplace("supportsDelayedStackTraceLoading", true); + // The debug adapter supports the 'loadedSources' request. + body.try_emplace("supportsLoadedSourcesRequest", false); + + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "LaunchRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Launch request; value of command field is 'launch'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "launch" ] +// }, +// "arguments": { +// "$ref": "#/definitions/LaunchRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "LaunchRequestArguments": { +// "type": "object", +// "description": "Arguments for 'launch' request.", +// "properties": { +// "noDebug": { +// "type": "boolean", +// "description": "If noDebug is true the launch request should launch +// the program without enabling debugging." +// } +// } +// }, +// "LaunchResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'launch' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_launch(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + g_vsc.init_commands = GetStrings(arguments, "initCommands"); + g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands"); + g_vsc.stop_commands = GetStrings(arguments, "stopCommands"); + g_vsc.exit_commands = GetStrings(arguments, "exitCommands"); + g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); + const auto debuggerRoot = GetString(arguments, "debuggerRoot"); + + // This is a hack for loading DWARF in .o files on Mac where the .o files + // in the debug map of the main executable have relative paths which require + // the lldb-vscode binary to have its working directory set to that relative + // root for the .o files in order to be able to load debug info. + if (!debuggerRoot.empty()) { + llvm::sys::fs::set_current_path(debuggerRoot.data()); + } + + SetSourceMapFromArguments(*arguments); + + // Run any initialize LLDB commands the user specified in the launch.json + g_vsc.RunInitCommands(); + + // Grab the current working directory if there is one and set it in the + // launch info. + const auto cwd = GetString(arguments, "cwd"); + if (!cwd.empty()) + g_vsc.launch_info.SetWorkingDirectory(cwd.data()); + + // Grab the name of the program we need to debug and set it as the first + // argument that will be passed to the program we will debug. + const auto program = GetString(arguments, "program"); + if (!program.empty()) { + lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/); + + g_vsc.launch_info.SetExecutableFile(program_fspec, + true /*add_as_first_arg*/); + const char *target_triple = nullptr; + const char *uuid_cstr = nullptr; + // Stand alone debug info file if different from executable + const char *symfile = nullptr; + g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile); + if (error.Fail()) { + response.try_emplace("success", false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + } + } + + // Extract any extra arguments and append them to our program arguments for + // when we launch + auto args = GetStrings(arguments, "args"); + if (!args.empty()) + g_vsc.launch_info.SetArguments(MakeArgv(args).data(), true); + + // Pass any environment variables along that the user specified. + auto envs = GetStrings(arguments, "env"); + if (!envs.empty()) + g_vsc.launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true); + + auto flags = g_vsc.launch_info.GetLaunchFlags(); + + if (GetBoolean(arguments, "disableASLR", true)) + flags |= lldb::eLaunchFlagDisableASLR; + if (GetBoolean(arguments, "disableSTDIO", false)) + flags |= lldb::eLaunchFlagDisableSTDIO; + if (GetBoolean(arguments, "shellExpandArguments", false)) + flags |= lldb::eLaunchFlagShellExpandArguments; + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); + g_vsc.launch_info.SetDetachOnError(detatchOnError); + g_vsc.launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | + lldb::eLaunchFlagStopAtEntry); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + + // Disable async events so the launch will be successful when we return from + // the launch call and the launch will happen synchronously + g_vsc.debugger.SetAsync(false); + g_vsc.target.Launch(g_vsc.launch_info, error); + if (error.Fail()) { + response.try_emplace("success", false); + EmplaceSafeString(response, "message", std::string(error.GetCString())); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + + SendProcessEvent(Launch); + g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized"))); + // Reenable async events and start the event thread to catch async events. + g_vsc.debugger.SetAsync(true); +} + +//---------------------------------------------------------------------- +// "NextRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Next request; value of command field is 'next'. The +// request starts the debuggee to run again for one step. +// The debug adapter first sends the NextResponse and then +// a StoppedEvent (event type 'step') after the step has +// completed.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "next" ] +// }, +// "arguments": { +// "$ref": "#/definitions/NextArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "NextArguments": { +// "type": "object", +// "description": "Arguments for 'next' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'next' for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "NextResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'next' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_next(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepOver(); + } else { + response.try_emplace("success", false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "PauseRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Pause request; value of command field is 'pause'. The +// request suspenses the debuggee. The debug adapter first sends the +// PauseResponse and then a StoppedEvent (event type 'pause') after the +// thread has been paused successfully.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "pause" ] +// }, +// "arguments": { +// "$ref": "#/definitions/PauseArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "PauseArguments": { +// "type": "object", +// "description": "Arguments for 'pause' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Pause execution for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "PauseResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'pause' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_pause(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBProcess process = g_vsc.target.GetProcess(); + lldb::SBError error = process.Stop(); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "ScopesRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Scopes request; value of command field is 'scopes'. The +// request returns the variable scopes for a given stackframe ID.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "scopes" ] +// }, +// "arguments": { +// "$ref": "#/definitions/ScopesArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "ScopesArguments": { +// "type": "object", +// "description": "Arguments for 'scopes' request.", +// "properties": { +// "frameId": { +// "type": "integer", +// "description": "Retrieve the scopes for this stackframe." +// } +// }, +// "required": [ "frameId" ] +// }, +// "ScopesResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'scopes' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "scopes": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Scope" +// }, +// "description": "The scopes of the stackframe. If the array has +// length zero, there are no scopes available." +// } +// }, +// "required": [ "scopes" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_scopes(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments); + g_vsc.variables.Clear(); + g_vsc.variables.Append(frame.GetVariables(true, // arguments + true, // locals + false, // statics + true)); // in_scope_only + g_vsc.num_locals = g_vsc.variables.GetSize(); + g_vsc.variables.Append(frame.GetVariables(false, // arguments + false, // locals + true, // statics + true)); // in_scope_only + g_vsc.num_globals = g_vsc.variables.GetSize() - (g_vsc.num_locals); + g_vsc.variables.Append(frame.GetRegisters()); + g_vsc.num_regs = + g_vsc.variables.GetSize() - (g_vsc.num_locals + g_vsc.num_globals); + body.try_emplace("scopes", g_vsc.CreateTopLevelScopes()); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "SetBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetBreakpoints request; value of command field is +// 'setBreakpoints'. Sets multiple breakpoints for a single source and +// clears all previous breakpoints in that source. To clear all breakpoint +// for a source, specify an empty array. When a breakpoint is hit, a +// StoppedEvent (event type 'breakpoint') is generated.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setBreakpoints' request.", +// "properties": { +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The source location of the breakpoints; either +// source.path or source.reference must be specified." +// }, +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/SourceBreakpoint" +// }, +// "description": "The code locations of the breakpoints." +// }, +// "lines": { +// "type": "array", +// "items": { +// "type": "integer" +// }, +// "description": "Deprecated: The code locations of the breakpoints." +// }, +// "sourceModified": { +// "type": "boolean", +// "description": "A value of true indicates that the underlying source +// has been modified which results in new breakpoint locations." +// } +// }, +// "required": [ "source" ] +// }, +// "SetBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setBreakpoints' request. Returned is +// information about each breakpoint created by this request. This includes +// the actual code location and whether the breakpoint could be verified. +// The breakpoints returned are in the same order as the elements of the +// 'breakpoints' (or the deprecated 'lines') in the +// SetBreakpointsArguments.", "properties": { +// "body": { +// "type": "object", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Breakpoint" +// }, +// "description": "Information about the breakpoints. The array +// elements are in the same order as the elements of the +// 'breakpoints' (or the deprecated 'lines') in the +// SetBreakpointsArguments." +// } +// }, +// "required": [ "breakpoints" ] +// } +// }, +// "required": [ "body" ] +// }] +// }, +// "SourceBreakpoint": { +// "type": "object", +// "description": "Properties of a breakpoint or logpoint passed to the +// setBreakpoints request.", "properties": { +// "line": { +// "type": "integer", +// "description": "The source line of the breakpoint or logpoint." +// }, +// "column": { +// "type": "integer", +// "description": "An optional source column of the breakpoint." +// }, +// "condition": { +// "type": "string", +// "description": "An optional expression for conditional breakpoints." +// }, +// "hitCondition": { +// "type": "string", +// "description": "An optional expression that controls how many hits of +// the breakpoint are ignored. The backend is expected to interpret the +// expression as needed." +// }, +// "logMessage": { +// "type": "string", +// "description": "If this attribute exists and is non-empty, the backend +// must not 'break' (stop) but log the message instead. Expressions within +// {} are interpolated." +// } +// }, +// "required": [ "line" ] +// } +//---------------------------------------------------------------------- +void request_setBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto source = arguments->getObject("source"); + const auto path = GetString(source, "path"); + auto breakpoints = arguments->getArray("breakpoints"); + llvm::json::Array response_breakpoints; + // Decode the source breakpoint infos for this "setBreakpoints" request + SourceBreakpointMap request_bps; + for (const auto &bp : *breakpoints) { + auto bp_obj = bp.getAsObject(); + if (bp_obj) { + SourceBreakpoint src_bp(*bp_obj); + request_bps[src_bp.line] = std::move(src_bp); + } + } + + // See if we already have breakpoints set for this source file from a + // previous "setBreakpoints" request + auto old_src_bp_pos = g_vsc.source_breakpoints.find(path); + if (old_src_bp_pos != g_vsc.source_breakpoints.end()) { + + // We have already set breakpoints in this source file and they are giving + // use a new list of lines to set breakpoints on. Some breakpoints might + // already be set, and some might not. We need to remove any breakpoints + // whose lines are not contained in the any breakpoints lines in in the + // "breakpoints" array. + + // Delete any breakpoints in this source file that aren't in the + // request_bps set. There is no call to remove breakpoints other than + // calling this function with a smaller or empty "breakpoints" list. + std::vector<uint32_t> remove_lines; + for (auto &pair: old_src_bp_pos->second) { + auto request_pos = request_bps.find(pair.first); + if (request_pos == request_bps.end()) { + // This breakpoint no longer exists in this source file, delete it + g_vsc.target.BreakpointDelete(pair.second.bp.GetID()); + remove_lines.push_back(pair.first); + } else { + pair.second.UpdateBreakpoint(request_pos->second); + // Remove this breakpoint from the request breakpoints since we have + // handled it here and we don't need to set a new breakpoint below. + request_bps.erase(request_pos); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + } + // Remove any lines from this existing source breakpoint map + for (auto line: remove_lines) + old_src_bp_pos->second.erase(line); + + // Now add any breakpoint infos left over in request_bps are the + // breakpoints that weren't set in this source file yet. We need to update + // thread source breakpoint info for the source file in the variable + // "old_src_bp_pos->second" so the info for this source file is up to date. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(path.data()); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + old_src_bp_pos->second[pair.first] = std::move(pair.second); + } + } else { + // No breakpoints were set for this source file yet. Set all breakpoints + // for each line and add them to the response and create an entry in + // g_vsc.source_breakpoints for this source file. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(path.data()); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + g_vsc.source_breakpoints[path] = std::move(request_bps); + } + + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "SetExceptionBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetExceptionBreakpoints request; value of command field +// is 'setExceptionBreakpoints'. The request configures the debuggers +// response to thrown exceptions. If an exception is configured to break, a +// StoppedEvent is fired (event type 'exception').", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setExceptionBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetExceptionBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetExceptionBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setExceptionBreakpoints' request.", +// "properties": { +// "filters": { +// "type": "array", +// "items": { +// "type": "string" +// }, +// "description": "IDs of checked exception options. The set of IDs is +// returned via the 'exceptionBreakpointFilters' capability." +// }, +// "exceptionOptions": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/ExceptionOptions" +// }, +// "description": "Configuration options for selected exceptions." +// } +// }, +// "required": [ "filters" ] +// }, +// "SetExceptionBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setExceptionBreakpoints' request. This is +// just an acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_setExceptionBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto filters = arguments->getArray("filters"); + // Keep a list of any exception breakpoint filter names that weren't set + // so we can clear any exception breakpoints if needed. + std::set<std::string> unset_filters; + for (const auto &bp : g_vsc.exception_breakpoints) + unset_filters.insert(bp.filter); + + for (const auto &value : *filters) { + const auto filter = GetAsString(value); + auto exc_bp = g_vsc.GetExceptionBreakpoint(filter); + if (exc_bp) { + exc_bp->SetBreakpoint(); + unset_filters.erase(filter); + } + } + for (const auto &filter : unset_filters) { + auto exc_bp = g_vsc.GetExceptionBreakpoint(filter); + if (exc_bp) + exc_bp->ClearBreakpoint(); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "SetFunctionBreakpointsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "SetFunctionBreakpoints request; value of command field is +// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears +// all previous function breakpoints. To clear all function breakpoint, +// specify an empty array. When a function breakpoint is hit, a StoppedEvent +// (event type 'function breakpoint') is generated.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setFunctionBreakpoints" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetFunctionBreakpointsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetFunctionBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for 'setFunctionBreakpoints' request.", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/FunctionBreakpoint" +// }, +// "description": "The function names of the breakpoints." +// } +// }, +// "required": [ "breakpoints" ] +// }, +// "FunctionBreakpoint": { +// "type": "object", +// "description": "Properties of a breakpoint passed to the +// setFunctionBreakpoints request.", "properties": { +// "name": { +// "type": "string", +// "description": "The name of the function." +// }, +// "condition": { +// "type": "string", +// "description": "An optional expression for conditional breakpoints." +// }, +// "hitCondition": { +// "type": "string", +// "description": "An optional expression that controls how many hits of +// the breakpoint are ignored. The backend is expected to interpret the +// expression as needed." +// } +// }, +// "required": [ "name" ] +// }, +// "SetFunctionBreakpointsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setFunctionBreakpoints' request. Returned is +// information about each breakpoint created by this request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Breakpoint" +// }, +// "description": "Information about the breakpoints. The array +// elements correspond to the elements of the 'breakpoints' array." +// } +// }, +// "required": [ "breakpoints" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_setFunctionBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + lldb::SBError error; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + auto breakpoints = arguments->getArray("breakpoints"); + FunctionBreakpointMap request_bps; + llvm::json::Array response_breakpoints; + for (const auto &value : *breakpoints) { + auto bp_obj = value.getAsObject(); + if (bp_obj == nullptr) + continue; + FunctionBreakpoint func_bp(*bp_obj); + request_bps[func_bp.functionName] = std::move(func_bp); + } + + std::vector<llvm::StringRef> remove_names; + // Disable any function breakpoints that aren't in the request_bps. + // There is no call to remove function breakpoints other than calling this + // function with a smaller or empty "breakpoints" list. + for (auto &pair: g_vsc.function_breakpoints) { + auto request_pos = request_bps.find(pair.first()); + if (request_pos == request_bps.end()) { + // This function breakpoint no longer exists delete it from LLDB + g_vsc.target.BreakpointDelete(pair.second.bp.GetID()); + remove_names.push_back(pair.first()); + } else { + // Update the existing breakpoint as any setting withing the function + // breakpoint might have changed. + pair.second.UpdateBreakpoint(request_pos->second); + // Remove this breakpoint from the request breakpoints since we have + // handled it here and we don't need to set a new breakpoint below. + request_bps.erase(request_pos); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + } + } + // Remove any breakpoints that are no longer in our list + for (const auto &name: remove_names) + g_vsc.function_breakpoints.erase(name); + + // Any breakpoints that are left in "request_bps" are breakpoints that + // need to be set. + for (auto &pair : request_bps) { + pair.second.SetBreakpoint(); + // Add this breakpoint info to the response + AppendBreakpoint(pair.second.bp, response_breakpoints); + g_vsc.function_breakpoints[pair.first()] = std::move(pair.second); + } + + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "SourceRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Source request; value of command field is 'source'. The +// request retrieves the source code for a given source reference.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "source" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SourceArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SourceArguments": { +// "type": "object", +// "description": "Arguments for 'source' request.", +// "properties": { +// "source": { +// "$ref": "#/definitions/Source", +// "description": "Specifies the source content to load. Either +// source.path or source.sourceReference must be specified." +// }, +// "sourceReference": { +// "type": "integer", +// "description": "The reference to the source. This is the same as +// source.sourceReference. This is provided for backward compatibility +// since old backends do not understand the 'source' attribute." +// } +// }, +// "required": [ "sourceReference" ] +// }, +// "SourceResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'source' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "content": { +// "type": "string", +// "description": "Content of the source reference." +// }, +// "mimeType": { +// "type": "string", +// "description": "Optional content type (mime type) of the source." +// } +// }, +// "required": [ "content" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_source(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + + auto arguments = request.getObject("arguments"); + auto source = arguments->getObject("source"); + auto sourceReference = GetSigned(source, "sourceReference", -1); + auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference); + if (pos != g_vsc.source_map.end()) { + EmplaceSafeString(body, "content", pos->second.content); + } else { + response.try_emplace("success", false); + } + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "StackTraceRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StackTrace request; value of command field is +// 'stackTrace'. The request returns a stacktrace from the current execution +// state.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stackTrace" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StackTraceArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StackTraceArguments": { +// "type": "object", +// "description": "Arguments for 'stackTrace' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Retrieve the stacktrace for this thread." +// }, +// "startFrame": { +// "type": "integer", +// "description": "The index of the first frame to return; if omitted +// frames start at 0." +// }, +// "levels": { +// "type": "integer", +// "description": "The maximum number of frames to return. If levels is +// not specified or 0, all frames are returned." +// }, +// "format": { +// "$ref": "#/definitions/StackFrameFormat", +// "description": "Specifies details on how to format the stack frames." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StackTraceResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stackTrace' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "stackFrames": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/StackFrame" +// }, +// "description": "The frames of the stackframe. If the array has +// length zero, there are no stackframes available. This means that +// there is no location information available." +// }, +// "totalFrames": { +// "type": "integer", +// "description": "The total number of frames available." +// } +// }, +// "required": [ "stackFrames" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_stackTrace(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + lldb::SBError error; + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + llvm::json::Array stackFrames; + llvm::json::Object body; + + if (thread.IsValid()) { + const auto startFrame = GetUnsigned(arguments, "startFrame", 0); + const auto levels = GetUnsigned(arguments, "levels", 0); + const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels); + for (uint32_t i = startFrame; i < endFrame; ++i) { + auto frame = thread.GetFrameAtIndex(i); + if (!frame.IsValid()) + break; + stackFrames.emplace_back(CreateStackFrame(frame)); + } + } + body.try_emplace("stackFrames", std::move(stackFrames)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "StepInRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepIn request; value of command field is 'stepIn'. The +// request starts the debuggee to step into a function/method if possible. +// If it cannot step into a target, 'stepIn' behaves like 'next'. The debug +// adapter first sends the StepInResponse and then a StoppedEvent (event +// type 'step') after the step has completed. If there are multiple +// function/method calls (or other targets) on the source line, the optional +// argument 'targetId' can be used to control into which target the 'stepIn' +// should occur. The list of possible targets for a given source line can be +// retrieved via the 'stepInTargets' request.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepIn" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepInArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepInArguments": { +// "type": "object", +// "description": "Arguments for 'stepIn' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepIn' for this thread." +// }, +// "targetId": { +// "type": "integer", +// "description": "Optional id of the target to step into." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepInResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepIn' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_stepIn(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepInto(); + } else { + response.try_emplace("success", false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "StepOutRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepOut request; value of command field is 'stepOut'. The +// request starts the debuggee to run again for one step. The debug adapter +// first sends the StepOutResponse and then a StoppedEvent (event type +// 'step') after the step has completed.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepOut" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepOutArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepOutArguments": { +// "type": "object", +// "description": "Arguments for 'stepOut' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepOut' for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepOutResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepOut' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +//---------------------------------------------------------------------- +void request_stepOut(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + g_vsc.focus_tid = thread.GetThreadID(); + thread.StepOut(); + } else { + response.try_emplace("success", false); + } + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "ThreadsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Thread request; value of command field is 'threads'. The +// request retrieves a list of all threads.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "threads" ] +// } +// }, +// "required": [ "command" ] +// }] +// }, +// "ThreadsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'threads' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "threads": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Thread" +// }, +// "description": "All threads." +// } +// }, +// "required": [ "threads" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_threads(const llvm::json::Object &request) { + + lldb::SBProcess process = g_vsc.target.GetProcess(); + llvm::json::Object response; + FillResponse(request, response); + + const uint32_t num_threads = process.GetNumThreads(); + llvm::json::Array threads; + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); + threads.emplace_back(CreateThread(thread)); + } + if (threads.size() == 0) { + response.try_emplace("success", false); + } + llvm::json::Object body; + body.try_emplace("threads", std::move(threads)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "SetVariableRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "setVariable request; value of command field is +// 'setVariable'. Set the variable with the given name in the variable +// container to a new value.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "setVariable" ] +// }, +// "arguments": { +// "$ref": "#/definitions/SetVariableArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "SetVariableArguments": { +// "type": "object", +// "description": "Arguments for 'setVariable' request.", +// "properties": { +// "variablesReference": { +// "type": "integer", +// "description": "The reference of the variable container." +// }, +// "name": { +// "type": "string", +// "description": "The name of the variable." +// }, +// "value": { +// "type": "string", +// "description": "The value of the variable." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the response value." +// } +// }, +// "required": [ "variablesReference", "name", "value" ] +// }, +// "SetVariableResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'setVariable' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "value": { +// "type": "string", +// "description": "The new value of the variable." +// }, +// "type": { +// "type": "string", +// "description": "The type of the new value. Typically shown in the +// UI when hovering over the value." +// }, +// "variablesReference": { +// "type": "number", +// "description": "If variablesReference is > 0, the new value is +// structured and its children can be retrieved by passing +// variablesReference to the VariablesRequest." +// }, +// "namedVariables": { +// "type": "number", +// "description": "The number of named child variables. The client +// can use this optional information to present the variables in a +// paged UI and fetch them in chunks." +// }, +// "indexedVariables": { +// "type": "number", +// "description": "The number of indexed child variables. The client +// can use this optional information to present the variables in a +// paged UI and fetch them in chunks." +// } +// }, +// "required": [ "value" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_setVariable(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array variables; + llvm::json::Object body; + auto arguments = request.getObject("arguments"); + // This is a reference to the containing variable/scope + const auto variablesReference = + GetUnsigned(arguments, "variablesReference", 0); + const auto name = GetString(arguments, "name"); + const auto value = GetString(arguments, "value"); + // Set success to false just in case we don't find the variable by name + response.try_emplace("success", false); + + lldb::SBValue variable; + int64_t newVariablesReference = 0; + + // The "id" is the unique integer ID that is unique within the enclosing + // variablesReference. It is optionally added to any "interface Variable" + // objects to uniquely identify a variable within an enclosing + // variablesReference. It helps to disambiguate between two variables that + // have the same name within the same scope since the "setVariables" request + // only specifies the variable reference of the enclosing scope/variable, and + // the name of the variable. We could have two shadowed variables with the + // same name in "Locals" or "Globals". In our case the "id" absolute index + // of the variable within the g_vsc.variables list. + const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX); + if (id_value != UINT64_MAX) { + variable = g_vsc.variables.GetValueAtIndex(id_value); + } else if (VARREF_IS_SCOPE(variablesReference)) { + // variablesReference is one of our scopes, not an actual variable it is + // asking for a variable in locals or globals or registers + int64_t start_idx = 0; + int64_t end_idx = 0; + switch (variablesReference) { + case VARREF_LOCALS: + start_idx = 0; + end_idx = start_idx + g_vsc.num_locals; + break; + case VARREF_GLOBALS: + start_idx = g_vsc.num_locals; + end_idx = start_idx + g_vsc.num_globals; + break; + case VARREF_REGS: + start_idx = g_vsc.num_locals + g_vsc.num_globals; + end_idx = start_idx + g_vsc.num_regs; + break; + default: + break; + } + + // Find the variable by name in the correct scope and hope we don't have + // multiple variables with the same name. We search backwards because + // the list of variables has the top most variables first and variables + // in deeper scopes are last. This means we will catch the deepest + // variable whose name matches which is probably what the user wants. + for (int64_t i = end_idx - 1; i >= start_idx; --i) { + auto curr_variable = g_vsc.variables.GetValueAtIndex(i); + llvm::StringRef variable_name(curr_variable.GetName()); + if (variable_name == name) { + variable = curr_variable; + if (curr_variable.MightHaveChildren()) + newVariablesReference = i; + break; + } + } + } else { + // We have a named item within an actual variable so we need to find it + // withing the container variable by name. + const int64_t var_idx = VARREF_TO_VARIDX(variablesReference); + lldb::SBValue container = g_vsc.variables.GetValueAtIndex(var_idx); + variable = container.GetChildMemberWithName(name.data()); + if (!variable.IsValid()) { + if (name.startswith("[")) { + llvm::StringRef index_str(name.drop_front(1)); + uint64_t index = 0; + if (!index_str.consumeInteger(0, index)) { + if (index_str == "]") + variable = container.GetChildAtIndex(index); + } + } + } + + // We don't know the index of the variable in our g_vsc.variables + if (variable.IsValid()) { + if (variable.MightHaveChildren()) { + newVariablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize()); + g_vsc.variables.Append(variable); + } + } + } + + if (variable.IsValid()) { + lldb::SBError error; + bool success = variable.SetValueFromCString(value.data(), error); + if (success) { + SetValueForKey(variable, body, "value"); + EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName()); + body.try_emplace("variablesReference", newVariablesReference); + } else { + EmplaceSafeString(body, "message", std::string(error.GetCString())); + } + response.try_emplace("success", success); + } + + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +//---------------------------------------------------------------------- +// "VariablesRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Variables request; value of command field is 'variables'. +// Retrieves all child variables for the given variable reference. An +// optional filter can be used to limit the fetched children to either named +// or indexed children.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "variables" ] +// }, +// "arguments": { +// "$ref": "#/definitions/VariablesArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "VariablesArguments": { +// "type": "object", +// "description": "Arguments for 'variables' request.", +// "properties": { +// "variablesReference": { +// "type": "integer", +// "description": "The Variable reference." +// }, +// "filter": { +// "type": "string", +// "enum": [ "indexed", "named" ], +// "description": "Optional filter to limit the child variables to either +// named or indexed. If ommited, both types are fetched." +// }, +// "start": { +// "type": "integer", +// "description": "The index of the first variable to return; if omitted +// children start at 0." +// }, +// "count": { +// "type": "integer", +// "description": "The number of variables to return. If count is missing +// or 0, all variables are returned." +// }, +// "format": { +// "$ref": "#/definitions/ValueFormat", +// "description": "Specifies details on how to format the Variable +// values." +// } +// }, +// "required": [ "variablesReference" ] +// }, +// "VariablesResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'variables' request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "variables": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/Variable" +// }, +// "description": "All (or a range) of variables for the given +// variable reference." +// } +// }, +// "required": [ "variables" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +//---------------------------------------------------------------------- +void request_variables(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array variables; + auto arguments = request.getObject("arguments"); + const auto variablesReference = + GetUnsigned(arguments, "variablesReference", 0); + const int64_t start = GetSigned(arguments, "start", 0); + const int64_t count = GetSigned(arguments, "count", 0); + bool hex = false; + auto format = arguments->getObject("format"); + if (format) + hex = GetBoolean(format, "hex", false); + + if (VARREF_IS_SCOPE(variablesReference)) { + // variablesReference is one of our scopes, not an actual variable it is + // asking for the list of args, locals or globals. + int64_t start_idx = 0; + int64_t num_children = 0; + switch (variablesReference) { + case VARREF_LOCALS: + start_idx = start; + num_children = g_vsc.num_locals; + break; + case VARREF_GLOBALS: + start_idx = start + g_vsc.num_locals + start; + num_children = g_vsc.num_globals; + break; + case VARREF_REGS: + start_idx = start + g_vsc.num_locals + g_vsc.num_globals; + num_children = g_vsc.num_regs; + break; + default: + break; + } + const int64_t end_idx = start_idx + ((count == 0) ? num_children : count); + for (auto i = start_idx; i < end_idx; ++i) { + lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i); + if (!variable.IsValid()) + break; + variables.emplace_back( + CreateVariable(variable, VARIDX_TO_VARREF(i), i, hex)); + } + } else { + // We are expanding a variable that has children, so we will return its + // children. + const int64_t var_idx = VARREF_TO_VARIDX(variablesReference); + lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(var_idx); + if (variable.IsValid()) { + const auto num_children = variable.GetNumChildren(); + const int64_t end_idx = start + ((count == 0) ? num_children : count); + for (auto i = start; i < end_idx; ++i) { + lldb::SBValue child = variable.GetChildAtIndex(i); + if (!child.IsValid()) + break; + if (child.MightHaveChildren()) { + const int64_t var_idx = g_vsc.variables.GetSize(); + auto childVariablesReferences = VARIDX_TO_VARREF(var_idx); + variables.emplace_back( + CreateVariable(child, childVariablesReferences, var_idx, hex)); + g_vsc.variables.Append(child); + } else { + variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex)); + } + } + } + } + llvm::json::Object body; + body.try_emplace("variables", std::move(variables)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +// A request used in testing to get the details on all breakpoints that are +// currently set in the target. This helps us to test "setBreakpoints" and +// "setFunctionBreakpoints" requests to verify we have the correct set of +// breakpoints currently set in LLDB. +void request__testGetTargetBreakpoints(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Array response_breakpoints; + for (uint32_t i = 0; g_vsc.target.GetBreakpointAtIndex(i).IsValid(); ++i) { + auto bp = g_vsc.target.GetBreakpointAtIndex(i); + AppendBreakpoint(bp, response_breakpoints); + } + llvm::json::Object body; + body.try_emplace("breakpoints", std::move(response_breakpoints)); + response.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); +} + +const std::map<std::string, RequestCallback> &GetRequestHandlers() { +#define REQUEST_CALLBACK(name) \ + { #name, request_##name } + static std::map<std::string, RequestCallback> g_request_handlers = { + // VSCode Debug Adaptor requests + REQUEST_CALLBACK(attach), + REQUEST_CALLBACK(continue), + REQUEST_CALLBACK(configurationDone), + REQUEST_CALLBACK(disconnect), + REQUEST_CALLBACK(evaluate), + REQUEST_CALLBACK(exceptionInfo), + REQUEST_CALLBACK(initialize), + REQUEST_CALLBACK(launch), + REQUEST_CALLBACK(next), + REQUEST_CALLBACK(pause), + REQUEST_CALLBACK(scopes), + REQUEST_CALLBACK(setBreakpoints), + REQUEST_CALLBACK(setExceptionBreakpoints), + REQUEST_CALLBACK(setFunctionBreakpoints), + REQUEST_CALLBACK(setVariable), + REQUEST_CALLBACK(source), + REQUEST_CALLBACK(stackTrace), + REQUEST_CALLBACK(stepIn), + REQUEST_CALLBACK(stepOut), + REQUEST_CALLBACK(threads), + REQUEST_CALLBACK(variables), + // Testing requests + REQUEST_CALLBACK(_testGetTargetBreakpoints), + }; +#undef REQUEST_CALLBACK + return g_request_handlers; +} + +} // anonymous namespace + +int main(int argc, char *argv[]) { + + // Initialize LLDB first before we do anything. + lldb::SBDebugger::Initialize(); + + if (argc == 2) { + const char *arg = argv[1]; +#if !defined(_WIN32) + if (strcmp(arg, "-g") == 0) { + printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid()); + pause(); + } else { +#else + { +#endif + int portno = atoi(arg); + printf("Listening on port %i...\n", portno); + int socket_fd = AcceptConnection(portno); + if (socket_fd >= 0) { + // We must open two FILE objects, one for reading and one for writing + // the FILE objects have a mutex in them that won't allow reading and + // writing to the socket stream. + g_vsc.in = fdopen(socket_fd, "r"); + g_vsc.out = fdopen(socket_fd, "w"); + if (g_vsc.in == nullptr || g_vsc.out == nullptr) { + if (g_vsc.log) + *g_vsc.log << "fdopen failed (" << strerror(errno) << ")" + << std::endl; + exit(1); + } + } else { + exit(1); + } + } + } + auto request_handlers = GetRequestHandlers(); + uint32_t packet_idx = 0; + while (true) { + std::string json = g_vsc.ReadJSON(); + if (json.empty()) + break; + + llvm::StringRef json_sref(json); + llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref); + if (!json_value) { + auto error = json_value.takeError(); + if (g_vsc.log) { + std::string error_str; + llvm::raw_string_ostream strm(error_str); + strm << error; + strm.flush(); + + *g_vsc.log << "error: failed to parse JSON: " << error_str << std::endl + << json << std::endl; + } + return 1; + } + + auto object = json_value->getAsObject(); + if (!object) { + if (g_vsc.log) + *g_vsc.log << "error: json packet isn't a object" << std::endl; + return 1; + } + + const auto packet_type = GetString(object, "type"); + if (packet_type == "request") { + const auto command = GetString(object, "command"); + auto handler_pos = request_handlers.find(command); + if (handler_pos != request_handlers.end()) { + handler_pos->second(*object); + } else { + if (g_vsc.log) + *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl; + return 1; + } + } + ++packet_idx; + } + + // We must terminate the debugger in a thread before the C++ destructor + // chain messes everything up. + lldb::SBDebugger::Terminate(); + return 0; +} diff --git a/tools/lldb-vscode/package.json b/tools/lldb-vscode/package.json new file mode 100644 index 000000000000..aa3de65c172f --- /dev/null +++ b/tools/lldb-vscode/package.json @@ -0,0 +1,242 @@ +{ + "name": "lldb-vscode", + "displayName": "LLDB native Debug stub", + "version": "0.1.0", + "publisher": "llvm.org", + "description": "Debug adapter for LLDB which uses a C++ tool to interface directly with LLDB.", + "author": { + "name": "Greg Clayton", + "email": "clayborg@gmail.com" + }, + "license": "LLVM", + "keywords": [ + "multi-root ready" + ], + "engines": { + "vscode": "^1.18.0", + "node": "^7.9.0" + }, + "icon": "images/lldb.png", + "categories": [ + "Debuggers" + ], + "private": true, + "devDependencies": { + "@types/node": "7.0.43", + "@types/mocha": "2.2.45", + "typescript": "2.6.2", + "mocha": "4.0.1", + "vscode": "1.1.10", + "vscode-debugadapter-testsupport": "1.25.0", + "tslint": "5.8.0", + "vsce": "1.35.0" + }, + "contributes": { + "debuggers": [ + { + "type": "lldb-vscode", + "label": "Native LLDB Debugger", + "enableBreakpointsFor": { + "languageIds": [ + "ada", + "arm", + "asm", + "c", + "cpp", + "crystal", + "d", + "fortan", + "fortran-modern", + "nim", + "objective-c", + "objectpascal", + "pascal", + "rust", + "swift" + ] + }, + "program": "./bin/lldb-vscode", + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "program": { + "type": "string", + "description": "Path to the program to debug." + }, + "args": { + "type": [ "array", "string" ], + "description": "Program arguments.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Program working directory.", + "default": "${workspaceRoot}" + }, + "env": { + "type": "array", + "description": "Additional environment variables.", + "default": [] + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": false + }, + "disableASLR": { + "type": "boolean", + "description": "Enable or disable Address space layout randomization if the debugger supports it.", + "default": true + }, + "disableSTDIO": { + "type": "boolean", + "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", + "default": false + }, + "shellExpandArguments": { + "type": "boolean", + "description": "Expand program arguments as a shell would without actually launching the program in a shell.", + "default": false + }, + "detachOnError": { + "type": "boolean", + "description": "Detach from the program.", + "default": false + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.", + "default": [] + }, + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adaptor to so relative object files can be located." + }, + "initCommands": { + "type": "array", + "description": "Initialization commands executed upon debugger startup.", + "default": [] + }, + "preRunCommands": { + "type": "array", + "description": "Commands executed just before the program is launched.", + "default": [] + }, + "stopCommands": { + "type": "array", + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "description": "Commands executed at the end of debugging session.", + "default": [] + } + } + }, + "attach": { + "properties": { + "program": { + "type": "string", + "description": "Path to the program to attach to." + }, + "pid": { + "type": [ + "number", + "string" + ], + "description": "System process ID to attach to." + }, + "waitFor": { + "type": "boolean", + "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", + "default": true + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.", + "default": [] + }, + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adaptor to so relative object files can be located." + }, + "attachCommands": { + "type": "array", + "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", + "default": [] + }, + "initCommands": { + "type": "array", + "description": "Initialization commands executed upon debugger startup.", + "default": [] + }, + "preRunCommands": { + "type": "array", + "description": "Commands executed just before the program is attached to.", + "default": [] + }, + "stopCommands": { + "type": "array", + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "description": "Commands executed at the end of debugging session.", + "default": [] + } + } + } + }, + "initialConfigurations": [ + { + "type": "lldb-vscode", + "request": "launch", + "name": "Debug", + "program": "${workspaceRoot}/<your program>", + "args": [], + "env": [], + "cwd": "${workspaceRoot}" + } + ], + "configurationSnippets": [ + { + "label": "LLDB: Launch", + "description": "", + "body": { + "type": "lldb-vscode", + "request": "launch", + "name": "${2:Launch}", + "program": "^\"\\${workspaceRoot}/${1:<your program>}\"", + "args": [], + "env": [], + "cwd": "^\"\\${workspaceRoot}\"" + } + } + ] + } + ] + } +} |