Index: .gitignore =================================================================== --- .gitignore +++ .gitignore @@ -23,10 +23,15 @@ .sw? # OS X specific files. .DS_store +DerivedData/ + +# Remote build configuration files. +.remote-build.conf build/ llvm-build/ *xcuserdata +xcshareddata/ test/20* # We should ignore Xcode-style embedding of llvm/ at lldb root dir. Index: cmake/platforms/Android.cmake =================================================================== --- cmake/platforms/Android.cmake +++ cmake/platforms/Android.cmake @@ -45,6 +45,18 @@ elseif( ANDROID_ABI STREQUAL "x86_64" ) set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) set( ANDROID_TOOLCHAIN_NAME "x86_64-linux-android" ) +elseif( ANDROID_ABI STREQUAL "armeabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) + set( ANDROID_TOOLCHAIN_NAME "arm-linux-androideabi" ) +elseif( ANDROID_ABI STREQUAL "aarch64" ) + set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) + set( ANDROID_TOOLCHAIN_NAME "aarch64-linux-android" ) +elseif( ANDROID_ABI STREQUAL "mips" ) + set( CMAKE_SYSTEM_PROCESSOR "mips" ) + set( ANDROID_TOOLCHAIN_NAME "mipsel-linux-android" ) +elseif( ANDROID_ABI STREQUAL "mips64" ) + set( CMAKE_SYSTEM_PROCESSOR "mips64" ) + set( ANDROID_TOOLCHAIN_NAME "mips64el-linux-android" ) else() message( SEND_ERROR "Unknown ANDROID_ABI = \"${ANDROID_ABI}\"." ) endif() @@ -101,4 +113,4 @@ # only search for libraries and includes in the ndk toolchain set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) -set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) \ No newline at end of file +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) Index: gtest/unittest/Plugins/Process/Linux/Makefile =================================================================== --- gtest/unittest/Plugins/Process/Linux/Makefile +++ gtest/unittest/Plugins/Process/Linux/Makefile @@ -5,10 +5,12 @@ CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS ENABLE_THREADS := YES CXX_SOURCES := $(wildcard *.cpp) \ - $(realpath $(LEVEL)/../../source/Plugins/Process/Linux/ThreadStateCoordinator.cpp) + $(realpath $(LEVEL)/../../source/Plugins/Process/Linux/ThreadStateCoordinator.cpp) \ + $(realpath $(LEVEL)/../../source/Core/Error.cpp) MAKE_DSYM := NO OS := $(shell uname -s) + # $(info OS $(OS)) ifeq ($(OS),Linux) LD_EXTRAS := -lncurses -ldl Index: gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp =================================================================== --- gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp @@ -1,6 +1,7 @@ #include #include "gtest/gtest.h" +#include "lldb/Core/Error.h" #include "Plugins/Process/Linux/ThreadStateCoordinator.h" using namespace lldb_private; @@ -67,19 +68,15 @@ // Member functions. // Error handling. - void - SetErrorString (const std::string &error_string) - { - m_error_called = true; - m_error_string = error_string; - printf ("received error: %s (test might be expecting)\n", error_string.c_str ()); - } - ThreadStateCoordinator::ErrorFunction GetErrorFunction () { - using namespace std::placeholders; - return std::bind(&ThreadStateCoordinatorTest::SetErrorString, this, _1); + return [this] (const std::string &error_string) + { + m_error_called = true; + m_error_string = error_string; + printf ("received error: %s (test might be expecting)\n", error_string.c_str ()); + }; } bool @@ -88,19 +85,15 @@ return m_error_called; } - // Deferred notificaton reception. - void - DeferredStopNotificationHandler (lldb::tid_t triggered_tid) - { - m_deferred_notification_called = true; - m_deferred_notification_tid = triggered_tid; - } - + // Deferred notification reception. ThreadStateCoordinator::ThreadIDFunction GetDeferredStopNotificationFunction () { - using namespace std::placeholders; - return std::bind(&ThreadStateCoordinatorTest::DeferredStopNotificationHandler, this, _1); + return [this] (lldb::tid_t triggered_tid) + { + m_deferred_notification_called = true; + m_deferred_notification_tid = triggered_tid; + }; } bool @@ -116,17 +109,14 @@ } // Stop request call reception. - void - ThreadStopRequested (lldb::tid_t stop_tid) - { - m_requested_stop_tids.insert (stop_tid); - } - - ThreadStateCoordinator::ThreadIDFunction + ThreadStateCoordinator::StopThreadFunction GetStopRequestFunction () { - using namespace std::placeholders; - return std::bind(&ThreadStateCoordinatorTest::ThreadStopRequested, this, _1); + return [this] (lldb::tid_t stop_tid) + { + m_requested_stop_tids.insert (stop_tid); + return Error(); + }; } ThreadStateCoordinator::ThreadIDSet::size_type @@ -135,6 +125,17 @@ return m_requested_stop_tids.size(); } + ThreadStateCoordinator::ResumeThreadFunction + GetResumeThreadFunction (lldb::tid_t& resumed_tid, int& resume_call_count) + { + return [this, &resumed_tid, &resume_call_count] (lldb::tid_t tid, bool) + { + resumed_tid = tid; + ++resume_call_count; + return Error(); + }; + } + bool DidRequestStopForTid (lldb::tid_t tid) { @@ -187,7 +188,7 @@ void NotifyThreadStop (lldb::tid_t stopped_tid) { - m_coordinator.NotifyThreadStop (stopped_tid, GetErrorFunction ()); + m_coordinator.NotifyThreadStop (stopped_tid, false, GetErrorFunction ()); } void @@ -195,7 +196,7 @@ { m_coordinator.NotifyThreadDeath (tid, GetErrorFunction ()); } -}; + }; } TEST_F (ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents) @@ -572,11 +573,7 @@ int resume_call_count = 0; m_coordinator.RequestThreadResume (UNKNOWN_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }, + GetResumeThreadFunction(resumed_tid, resume_call_count), GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); @@ -596,12 +593,8 @@ int resume_call_count = 0; m_coordinator.RequestThreadResume (NEW_THREAD_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }, - GetErrorFunction ()); + GetResumeThreadFunction(resumed_tid, resume_call_count), + GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); @@ -621,11 +614,7 @@ int resume_call_count = 0; m_coordinator.RequestThreadResume (NEW_THREAD_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }, + GetResumeThreadFunction(resumed_tid, resume_call_count), GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); @@ -638,11 +627,7 @@ // Make a second resume request. const int initial_resume_call_count = resume_call_count; m_coordinator.RequestThreadResume (NEW_THREAD_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }, + GetResumeThreadFunction(resumed_tid, resume_call_count), GetErrorFunction ()); // Process next event. This should fail since the thread should already be running. @@ -662,12 +647,8 @@ int resume_call_count = 0; m_coordinator.RequestThreadResume (TEST_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }, - GetErrorFunction ()); + GetResumeThreadFunction(resumed_tid, resume_call_count), + GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); @@ -726,13 +707,15 @@ ASSERT_EQ (false, DidFireDeferredNotification ()); // Now report thread A is resuming. Ensure the resume is called. - bool resume_called = false; + lldb::tid_t resumed_tid = 0; + int resume_call_count = 0; m_coordinator.RequestThreadResume (PENDING_TID_A, - [&](lldb::tid_t tid) { resume_called = true; }, - GetErrorFunction ()); - ASSERT_EQ (false, resume_called); + GetResumeThreadFunction(resumed_tid, resume_call_count), + GetErrorFunction ()); + ASSERT_EQ (0, resume_call_count); ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); - ASSERT_EQ (true, resume_called); + ASSERT_EQ (1, resume_call_count); + ASSERT_EQ (PENDING_TID_A, resumed_tid); // Report thread B stopped. NotifyThreadStop (PENDING_TID_B); Index: gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp =================================================================== --- /dev/null +++ gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp @@ -0,0 +1,32 @@ +//===-- ThreadStateCoordinatorTestMock.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file provides a few necessary functions to link +// ThreadStateCoordinatorTest.cpp Bringing in the real implementations results +// in a cascade of dependencies that pull in all of lldb. + +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +void +lldb_private::Log::Error (char const*, ...) +{ +} + +void +lldb_private::Log::Printf (char const*, ...) +{ +} + +Log* +lldb_private::GetLogIfAnyCategoriesSet (unsigned int) +{ + return nullptr; +} Index: include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- include/lldb/Host/common/NativeProcessProtocol.h +++ include/lldb/Host/common/NativeProcessProtocol.h @@ -18,6 +18,7 @@ #include "lldb/Host/Mutex.h" #include "NativeBreakpointList.h" +#include "NativeWatchpointList.h" namespace lldb_private { @@ -130,6 +131,9 @@ //---------------------------------------------------------------------- // Watchpoint functions //---------------------------------------------------------------------- + virtual const NativeWatchpointList::WatchpointMap& + GetWatchpointMap () const; + virtual uint32_t GetMaxWatchpoints () const; @@ -295,6 +299,7 @@ Mutex m_delegates_mutex; std::vector m_delegates; NativeBreakpointList m_breakpoint_list; + NativeWatchpointList m_watchpoint_list; int m_terminal_fd; uint32_t m_stop_id; Index: include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- include/lldb/Host/common/NativeRegisterContext.h +++ include/lldb/Host/common/NativeRegisterContext.h @@ -15,6 +15,7 @@ // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" +#include "lldb/Host/common/NativeWatchpointList.h" namespace lldb_private { @@ -95,6 +96,9 @@ virtual bool ClearHardwareWatchpoint (uint32_t hw_index); + virtual Error + ClearAllHardwareWatchpoints (); + virtual bool HardwareSingleStep (bool enable); Index: include/lldb/Host/common/NativeThreadProtocol.h =================================================================== --- include/lldb/Host/common/NativeThreadProtocol.h +++ include/lldb/Host/common/NativeThreadProtocol.h @@ -53,10 +53,7 @@ RestoreAllRegisters (lldb::DataBufferSP &data_sp); virtual bool - GetStopReason (ThreadStopInfo &stop_info) = 0; - - virtual uint32_t - TranslateStopInfoToGdbSignal (const ThreadStopInfo &stop_info) const; + GetStopReason (ThreadStopInfo &stop_info, std::string& description) = 0; lldb::tid_t GetID() const Index: include/lldb/Host/common/NativeWatchpointList.h =================================================================== --- /dev/null +++ include/lldb/Host/common/NativeWatchpointList.h @@ -0,0 +1,47 @@ +//===-- NativeWatchpointList.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeWatchpointList_h_ +#define liblldb_NativeWatchpointList_h_ + +#include "lldb/lldb-private-forward.h" +#include "lldb/Core/Error.h" + +#include + +namespace lldb_private +{ + struct NativeWatchpoint + { + lldb::addr_t m_addr; + size_t m_size; + uint32_t m_watch_flags; + bool m_hardware; + }; + + class NativeWatchpointList + { + public: + Error + Add (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware); + + Error + Remove (lldb::addr_t addr); + + using WatchpointMap = std::map; + + const WatchpointMap& + GetWatchpointMap () const; + + private: + WatchpointMap m_watchpoints; + }; +} + +#endif // ifndef liblldb_NativeWatchpointList_h_ Index: include/lldb/Target/NativeRegisterContextRegisterInfo.h =================================================================== --- /dev/null +++ include/lldb/Target/NativeRegisterContextRegisterInfo.h @@ -1,47 +0,0 @@ -//===-- NativeRegisterContextRegisterInfo.h ----------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef lldb_NativeRegisterContextRegisterInfo_h -#define lldb_NativeRegisterContextRegisterInfo_h - -#include - -#include "NativeRegisterContext.h" -#include "Plugins/Process/Utility/RegisterInfoInterface.h" - -namespace lldb_private -{ - class NativeRegisterContextRegisterInfo: public NativeRegisterContext - { - public: - /// - /// Construct a NativeRegisterContextRegisterInfo, taking ownership - /// of the register_info_interface pointer. - /// - NativeRegisterContextRegisterInfo (NativeThreadProtocol &thread, - uint32_t concrete_frame_idx, - RegisterInfoInterface *register_info_interface); - - uint32_t - GetRegisterCount () const override; - - uint32_t - GetUserRegisterCount () const override; - - const RegisterInfo * - GetRegisterInfoAtIndex (uint32_t reg_index) const override; - - const RegisterInfoInterface& - GetRegisterInfoInterface () const; - - private: - std::unique_ptr m_register_info_interface_up; - }; -} -#endif Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -28,7 +28,7 @@ // TODO pull NativeDelegate class out of NativeProcessProtocol so we // can just forward ref the NativeDelegate rather than include it here. -#include "../../../source/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" namespace lldb_private { Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -7,6 +7,18 @@ objects = { /* Begin PBXAggregateTarget section */ + 235AFBBB199BC6FD00897A4B /* MacOSX and Linux */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 235AFBBC199BC6FD00897A4B /* Build configuration list for PBXAggregateTarget "MacOSX and Linux" */; + buildPhases = ( + ); + dependencies = ( + 235AFBC4199BC70B00897A4B /* PBXTargetDependency */, + 235AFBC2199BC70700897A4B /* PBXTargetDependency */, + ); + name = "MacOSX and Linux"; + productName = "MacOSX and Linux"; + }; 26CEF3A914FD58BF007286B2 /* desktop_no_xpc */ = { isa = PBXAggregateTarget; buildConfigurationList = 26CEF3AD14FD58BF007286B2 /* Build configuration list for PBXAggregateTarget "desktop_no_xpc" */; @@ -890,6 +902,20 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 235AFBC1199BC70700897A4B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26F5C26910F3D9A4009D5894; + remoteInfo = "lldb-tool"; + }; + 235AFBC3199BC70B00897A4B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 235AFBB5199BC6AD00897A4B; + remoteInfo = Linux; + }; 262CFC7111A450CB00946C6C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; @@ -1129,13 +1155,13 @@ 23B6FF4119D38D48004CC8C3 /* ThreadStateCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadStateCoordinator.h; sourceTree = ""; }; 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadStateCoordinator.cpp; sourceTree = ""; }; 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandOptionValidators.cpp; path = source/Interpreter/CommandOptionValidators.cpp; sourceTree = ""; }; - 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Target/NativeRegisterContext.cpp; sourceTree = ""; }; - 23EDE3311926843600F6A132 /* NativeRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Target/NativeRegisterContext.h; sourceTree = ""; }; + 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Host/common/NativeRegisterContext.cpp; sourceTree = ""; }; + 23EDE3311926843600F6A132 /* NativeRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Host/common/NativeRegisterContext.h; sourceTree = ""; }; 23EDE3371926AAD500F6A132 /* RegisterInfoInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RegisterInfoInterface.h; path = Utility/RegisterInfoInterface.h; sourceTree = ""; }; 23EFE388193D1ABC00E54E54 /* SBTypeEnumMember.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeEnumMember.h; path = include/lldb/API/SBTypeEnumMember.h; sourceTree = ""; }; 23EFE38A193D1AEC00E54E54 /* SBTypeEnumMember.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeEnumMember.cpp; path = source/API/SBTypeEnumMember.cpp; sourceTree = ""; }; - 23F403471926C8D50046DC9B /* NativeRegisterContextRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContextRegisterInfo.h; path = include/lldb/Target/NativeRegisterContextRegisterInfo.h; sourceTree = ""; }; - 23F403481926CC250046DC9B /* NativeRegisterContextRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContextRegisterInfo.cpp; path = source/Target/NativeRegisterContextRegisterInfo.cpp; sourceTree = ""; }; + 23F403471926C8D50046DC9B /* NativeRegisterContextRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContextRegisterInfo.h; path = include/lldb/Host/common/NativeRegisterContextRegisterInfo.h; sourceTree = ""; }; + 23F403481926CC250046DC9B /* NativeRegisterContextRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContextRegisterInfo.cpp; path = source/Host/common/NativeRegisterContextRegisterInfo.cpp; sourceTree = ""; }; 25420ECC1A6490B8009ADBCB /* OptionValueChar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueChar.cpp; path = source/Interpreter/OptionValueChar.cpp; sourceTree = ""; }; 25420ECE1A64911B009ADBCB /* OptionValueChar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionValueChar.h; path = include/lldb/Interpreter/OptionValueChar.h; sourceTree = ""; }; 25420ED11A649D88009ADBCB /* PipeBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PipeBase.cpp; sourceTree = ""; }; @@ -5145,6 +5171,20 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXLegacyTarget section */ + 235AFBB5199BC6AD00897A4B /* Linux */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-u scripts/Python/remote-build.py -x $(ACTION)"; + buildConfigurationList = 235AFBBA199BC6AD00897A4B /* Build configuration list for PBXLegacyTarget "Linux" */; + buildPhases = ( + ); + buildToolPath = python; + buildWorkingDirectory = .; + dependencies = ( + ); + name = Linux; + passBuildSettingsInEnvironment = 1; + productName = Linux; + }; 2687EAC51508110B00DD8C2E /* install-headers */ = { isa = PBXLegacyTarget; buildArgumentsString = "$(ACTION)"; @@ -5370,6 +5410,8 @@ EDC6D49814E5C19B001B75F8 /* launcherXPCService */, EDE274E214EDCE1F005B0F75 /* launcherRootXPCService */, 2687EAC51508110B00DD8C2E /* install-headers */, + 235AFBB5199BC6AD00897A4B /* Linux */, + 235AFBBB199BC6FD00897A4B /* MacOSX and Linux */, 2690CD161A6DC0D000E717C8 /* lldb-mi */, ); }; @@ -6221,6 +6263,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 235AFBC2199BC70700897A4B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 26F5C26910F3D9A4009D5894 /* lldb-tool */; + targetProxy = 235AFBC1199BC70700897A4B /* PBXContainerItemProxy */; + }; + 235AFBC4199BC70B00897A4B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 235AFBB5199BC6AD00897A4B /* Linux */; + targetProxy = 235AFBC3199BC70B00897A4B /* PBXContainerItemProxy */; + }; 262CFC7211A450CB00946C6C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = debugserver; @@ -6459,6 +6511,162 @@ }; name = Release; }; + 235AFBB6199BC6AD00897A4B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + DEBUGGING_SYMBOLS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 235AFBB7199BC6AD00897A4B /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + DEBUGGING_SYMBOLS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = DebugClang; + }; + 235AFBB8199BC6AD00897A4B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + 235AFBB9199BC6AD00897A4B /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = BuildAndIntegration; + }; + 235AFBBD199BC6FD00897A4B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 235AFBBE199BC6FD00897A4B /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = DebugClang; + }; + 235AFBBF199BC6FD00897A4B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 235AFBC0199BC6FD00897A4B /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = BuildAndIntegration; + }; 26579F6A126A25920007C5CB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -8358,6 +8566,28 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = BuildAndIntegration; }; + 235AFBBA199BC6AD00897A4B /* Build configuration list for PBXLegacyTarget "Linux" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 235AFBB6199BC6AD00897A4B /* Debug */, + 235AFBB7199BC6AD00897A4B /* DebugClang */, + 235AFBB8199BC6AD00897A4B /* Release */, + 235AFBB9199BC6AD00897A4B /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 235AFBBC199BC6FD00897A4B /* Build configuration list for PBXAggregateTarget "MacOSX and Linux" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 235AFBBD199BC6FD00897A4B /* Debug */, + 235AFBBE199BC6FD00897A4B /* DebugClang */, + 235AFBBF199BC6FD00897A4B /* Release */, + 235AFBC0199BC6FD00897A4B /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; 26579F6D126A25BF0007C5CB /* Build configuration list for PBXNativeTarget "darwin-debug" */ = { isa = XCConfigurationList; buildConfigurations = ( Index: scripts/Python/remote-build.py =================================================================== --- /dev/null +++ scripts/Python/remote-build.py @@ -0,0 +1,300 @@ +#!/usr/bin/python + +from __future__ import print_function + +import argparse +import getpass +import os +import os.path +import re +import select +import sys +import subprocess + +_COMMON_SYNC_OPTS = "-avzh --delete" +_COMMON_EXCLUDE_OPTS = "--exclude=DerivedData --exclude=.svn --exclude=.git --exclude=llvm-build/Release+Asserts" + +def normalize_configuration(config_text): + if not config_text: + return "debug" + + config_lower = config_text.lower() + if config_lower in ["debug", "release"]: + return config_lower + else: + raise Exception("unknown configuration specified: %s" % config_text) + +def parse_args(): + DEFAULT_REMOTE_ROOT_DIR = "/mnt/ssd/work/macosx.sync" + DEFAULT_REMOTE_HOSTNAME = "tfiala2.mtv.corp.google.com" + OPTIONS_FILENAME = ".remote-build.conf" + DEFAULT_SSH_PORT = "22" + + parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + + parser.add_argument( + "--configuration", "-c", + help="specify configuration (Debug, Release)", + default=normalize_configuration(os.environ.get('CONFIGURATION', 'Debug'))) + parser.add_argument( + "--debug", "-d", + action="store_true", + help="help debug the remote-build script by adding extra logging") + parser.add_argument( + "--local-lldb-dir", "-l", metavar="DIR", + help="specify local lldb directory (Xcode layout assumed for llvm/clang)", + default=os.getcwd()) + parser.add_argument( + "--port", "-p", + help="specify the port ssh should use to connect to the remote side", + default=DEFAULT_SSH_PORT) + parser.add_argument( + "--remote-address", "-r", metavar="REMOTE-ADDR", + help="specify the dns name or ip address of the remote linux system", + default=DEFAULT_REMOTE_HOSTNAME) + parser.add_argument( + "--remote-dir", metavar="DIR", + help="specify the root of the linux source/build dir", + default=DEFAULT_REMOTE_ROOT_DIR) + parser.add_argument( + "--user", "-u", help="specify the user name for the remote system", + default=getpass.getuser()) + parser.add_argument( + "--xcode-action", "-x", help="$(ACTION) from Xcode", nargs='?', default=None) + + command_line_args = sys.argv[1:] + if os.path.exists(OPTIONS_FILENAME): + # Prepend the file so that command line args override the file contents. + command_line_args.insert(0, "@%s" % OPTIONS_FILENAME) + + return parser.parse_args(command_line_args) + + +def maybe_create_remote_root_dir(args): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "mkdir", + "-p", + args.remote_dir] + print("create remote root dir command:\n{}".format(commandline)) + return subprocess.call(commandline) + + +def init_with_args(args): + # Expand any user directory specs in local-side source dir (on MacOSX). + args.local_lldb_dir = os.path.expanduser(args.local_lldb_dir) + + # Append the configuration type to the remote build dir. + args.configuration = normalize_configuration(args.configuration) + args.remote_build_dir = os.path.join( + args.remote_dir, + "build-%s" % args.configuration) + + # We assume the local lldb directory is really named 'lldb'. + # This is because on the remote end, the local lldb root dir + # is copied over underneath llvm/tools and will be named there + # whatever it is named locally. The remote build will assume + # is is called lldb. + if os.path.basename(args.local_lldb_dir) != 'lldb': + raise Exception( + "local lldb root needs to be called 'lldb' but was {} instead" + .format(os.path.basename(args.local_lldb_dir))) + + args.lldb_dir_relative_regex = re.compile("%s/llvm/tools/lldb/" % args.remote_dir) + args.llvm_dir_relative_regex = re.compile("%s/" % args.remote_dir) + + print("Xcode action:", args.xcode_action) + + # Ensure the remote directory exists. + result = maybe_create_remote_root_dir(args) + if result == 0: + print("using remote root dir: %s" % args.remote_dir) + else: + print("remote root dir doesn't exist and could not be created, " + + "error code:", result) + return False + + return True + +def sync_llvm(args): + commandline = ["rsync"] + commandline.extend(_COMMON_SYNC_OPTS.split()) + commandline.extend(_COMMON_EXCLUDE_OPTS.split()) + commandline.append("--exclude=/llvm/tools/lldb") + commandline.extend(["-e", "ssh -p {}".format(args.port)]) + commandline.extend([ + "%s/llvm" % args.local_lldb_dir, + "%s@%s:%s" % (args.user, args.remote_address, args.remote_dir)]) + if args.debug: + print("going to execute llvm sync: {}".format(commandline)) + return subprocess.call(commandline) + + +def sync_lldb(args): + commandline = ["rsync"] + commandline.extend(_COMMON_SYNC_OPTS.split()) + commandline.extend(_COMMON_EXCLUDE_OPTS.split()) + commandline.append("--exclude=/lldb/llvm") + commandline.extend(["-e", "ssh -p {}".format(args.port)]) + commandline.extend([ + args.local_lldb_dir, + "%s@%s:%s/llvm/tools" % (args.user, args.remote_address, args.remote_dir)]) + if args.debug: + print("going to execute lldb sync: {}".format(commandline)) + return subprocess.call(commandline) + + +def build_cmake_command(args): + # args.remote_build_dir + # args.configuration in ('release', 'debug') + + if args.configuration == 'debug-optimized': + build_type_name = "RelWithDebInfo" + elif args.configuration == 'release': + build_type_name = "Release" + else: + build_type_name = "Debug" + + ld_flags = "\"-lstdc++ -lm\"" + + install_dir = os.path.join( + args.remote_build_dir, "..", "install-{}".format(args.configuration)) + + command_line = [ + "cmake", + "-GNinja", + "-DCMAKE_CXX_COMPILER=clang", + "-DCMAKE_C_COMPILER=clang", + # "-DCMAKE_CXX_FLAGS=%s" % cxx_flags, + "-DCMAKE_SHARED_LINKER_FLAGS=%s" % ld_flags, + "-DCMAKE_EXE_LINKER_FLAGS=%s" % ld_flags, + "-DCMAKE_INSTALL_PREFIX:PATH=%s" % install_dir, + "-DCMAKE_BUILD_TYPE=%s" % build_type_name, + "-Wno-dev", + os.path.join("..", "llvm") + ] + + return command_line + + +def maybe_configure(args): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "cd", args.remote_dir, "&&", + "mkdir", "-p", args.remote_build_dir, "&&", + "cd", args.remote_build_dir, "&&" + ] + commandline.extend(build_cmake_command(args)) + + if args.debug: + print("configure command: {}".format(commandline)) + + return subprocess.call(commandline) + + +def filter_build_line(args, line): + lldb_relative_line = args.lldb_dir_relative_regex.sub('', line) + if len(lldb_relative_line) != len(line): + # We substituted - return the modified line + return lldb_relative_line + + # No match on lldb path (longer on linux than llvm path). Try + # the llvm path match. + return args.llvm_dir_relative_regex.sub('', line) + + +def run_remote_build_command(args, build_command_list): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "cd", args.remote_build_dir, "&&"] + commandline.extend(build_command_list) + + if args.debug: + print("running remote build command: {}".format(commandline)) + + proc = subprocess.Popen( + commandline, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + # Filter stdout/stderr output for file path mapping. + # We do this to enable Xcode to see filenames relative to the + # MacOSX-side directory structure. + while True: + reads = [proc.stdout.fileno(), proc.stderr.fileno()] + select_result = select.select(reads, [], []) + + for fd in select_result[0]: + if fd == proc.stdout.fileno(): + line = proc.stdout.readline() + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line) + elif fd == proc.stderr.fileno(): + line = proc.stderr.readline() + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line, file=sys.stderr) + + proc_retval = proc.poll() + if proc_retval != None: + # Process stopped. Drain output before finishing up. + + # Drain stdout. + while True: + line = proc.stdout.readline() + if line: + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line) + else: + break + + # Drain stderr. + while True: + line = proc.stderr.readline() + if line: + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line, file=sys.stderr) + else: + break + + return proc_retval + + +def build(args): + return run_remote_build_command(args, ["time", "ninja"]) + + +def clean(args): + return run_remote_build_command(args, ["ninja", "clean"]) + + +if __name__ == "__main__": + # Handle arg parsing. + args = parse_args() + + # Initialize the system. + if not init_with_args(args): + exit(1) + + # Sync over llvm and clang source. + sync_llvm(args) + + # Sync over lldb source. + sync_lldb(args) + + # Configure the remote build if it's not already. + maybe_configure(args) + + if args.xcode_action == 'clean': + exit(clean(args)) + else: + exit(build(args)) Index: source/Host/CMakeLists.txt =================================================================== --- source/Host/CMakeLists.txt +++ source/Host/CMakeLists.txt @@ -18,7 +18,10 @@ common/MonitoringProcessLauncher.cpp common/NativeBreakpoint.cpp common/NativeBreakpointList.cpp + common/NativeWatchpointList.cpp common/NativeProcessProtocol.cpp + common/NativeRegisterContext.cpp + common/NativeRegisterContextRegisterInfo.cpp common/NativeThreadProtocol.cpp common/OptionParser.cpp common/PipeBase.cpp Index: source/Host/common/NativeBreakpoint.h =================================================================== --- /dev/null +++ source/Host/common/NativeBreakpoint.h @@ -1,66 +0,0 @@ -//===-- NativeBreakpoint.h --------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_NativeBreakpoint_h_ -#define liblldb_NativeBreakpoint_h_ - -#include "lldb/lldb-types.h" - -namespace lldb_private -{ - class NativeBreakpointList; - - class NativeBreakpoint - { - friend class NativeBreakpointList; - - public: - // The assumption is that derived breakpoints are enabled when created. - NativeBreakpoint (lldb::addr_t addr); - - virtual - ~NativeBreakpoint (); - - Error - Enable (); - - Error - Disable (); - - lldb::addr_t - GetAddress () const { return m_addr; } - - bool - IsEnabled () const { return m_enabled; } - - virtual bool - IsSoftwareBreakpoint () const = 0; - - protected: - const lldb::addr_t m_addr; - int32_t m_ref_count; - - virtual Error - DoEnable () = 0; - - virtual Error - DoDisable () = 0; - - private: - bool m_enabled; - - // ----------------------------------------------------------- - // interface for NativeBreakpointList - // ----------------------------------------------------------- - void AddRef (); - int32_t DecRef (); - }; -} - -#endif // ifndef liblldb_NativeBreakpoint_h_ Index: source/Host/common/NativeBreakpoint.cpp =================================================================== --- source/Host/common/NativeBreakpoint.cpp +++ source/Host/common/NativeBreakpoint.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "NativeBreakpoint.h" +#include "lldb/Host/common/NativeBreakpoint.h" #include "lldb/lldb-defines.h" #include "lldb/Core/Error.h" Index: source/Host/common/NativeBreakpointList.h =================================================================== --- /dev/null +++ source/Host/common/NativeBreakpointList.h @@ -1,53 +0,0 @@ -//===-- NativeBreakpointList.h ----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_NativeBreakpointList_h_ -#define liblldb_NativeBreakpointList_h_ - -#include "lldb/lldb-private-forward.h" -#include "lldb/Core/Error.h" -#include "lldb/Host/Mutex.h" -// #include "lldb/Host/NativeBreakpoint.h" - -#include -#include - -namespace lldb_private -{ - class NativeBreakpointList - { - public: - typedef std::function CreateBreakpointFunc; - - NativeBreakpointList (); - - Error - AddRef (lldb::addr_t addr, size_t size_hint, bool hardware, CreateBreakpointFunc create_func); - - Error - DecRef (lldb::addr_t addr); - - Error - EnableBreakpoint (lldb::addr_t addr); - - Error - DisableBreakpoint (lldb::addr_t addr); - - Error - GetBreakpoint (lldb::addr_t addr, NativeBreakpointSP &breakpoint_sp); - - private: - typedef std::map BreakpointMap; - - Mutex m_mutex; - BreakpointMap m_breakpoints; - }; -} - -#endif // ifndef liblldb_NativeBreakpointList_h_ Index: source/Host/common/NativeBreakpointList.cpp =================================================================== --- source/Host/common/NativeBreakpointList.cpp +++ source/Host/common/NativeBreakpointList.cpp @@ -7,11 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "NativeBreakpointList.h" +#include "lldb/Host/common/NativeBreakpointList.h" #include "lldb/Core/Log.h" -#include "NativeBreakpoint.h" +#include "lldb/Host/common/NativeBreakpoint.h" using namespace lldb; using namespace lldb_private; Index: source/Host/common/NativeProcessProtocol.cpp =================================================================== --- source/Host/common/NativeProcessProtocol.cpp +++ source/Host/common/NativeProcessProtocol.cpp @@ -7,17 +7,17 @@ // //===----------------------------------------------------------------------===// -#include "NativeProcessProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/lldb-enumerations.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" -#include "lldb/Target/NativeRegisterContext.h" +#include "lldb/Host/common/NativeRegisterContext.h" -#include "NativeThreadProtocol.h" -#include "SoftwareBreakpoint.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" using namespace lldb; using namespace lldb_private; @@ -39,6 +39,7 @@ m_delegates_mutex (Mutex::eMutexTypeRecursive), m_delegates (), m_breakpoint_list (), + m_watchpoint_list (), m_terminal_fd (-1), m_stop_id (0) { @@ -159,6 +160,12 @@ return true; } +const NativeWatchpointList::WatchpointMap& +NativeProcessProtocol::GetWatchpointMap () const +{ + return m_watchpoint_list.GetWatchpointMap(); +} + uint32_t NativeProcessProtocol::GetMaxWatchpoints () const { @@ -199,9 +206,6 @@ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - // FIXME save the watchpoint on the set of process watchpoint vars - // so we can add them to a thread each time a new thread is registered. - // Update the thread list UpdateThreads (); @@ -261,15 +265,12 @@ return thread_error; } } - return Error (); + return m_watchpoint_list.Add (addr, size, watch_flags, hardware); } Error NativeProcessProtocol::RemoveWatchpoint (lldb::addr_t addr) { - // FIXME remove the watchpoint on the set of process watchpoint vars - // so we can add them to a thread each time a new thread is registered. - // Update the thread list UpdateThreads (); @@ -292,7 +293,8 @@ overall_error = thread_error; } } - return overall_error; + const Error error = m_watchpoint_list.Remove(addr); + return overall_error.Fail() ? overall_error : error; } bool Index: source/Host/common/NativeRegisterContext.cpp =================================================================== --- source/Host/common/NativeRegisterContext.cpp +++ source/Host/common/NativeRegisterContext.cpp @@ -7,15 +7,15 @@ // //===----------------------------------------------------------------------===// -#include "lldb/Target/NativeRegisterContext.h" +#include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Core/Log.h" #include "lldb/Core/RegisterValue.h" #include "lldb/lldb-private-log.h" -#include "Host/common/NativeProcessProtocol.h" -#include "Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" using namespace lldb; using namespace lldb_private; @@ -297,6 +297,12 @@ return false; } +Error +NativeRegisterContext::ClearAllHardwareWatchpoints () +{ + return Error ("not implemented"); +} + bool NativeRegisterContext::HardwareSingleStep (bool enable) { Index: source/Host/common/NativeRegisterContextRegisterInfo.cpp =================================================================== --- source/Host/common/NativeRegisterContextRegisterInfo.cpp +++ source/Host/common/NativeRegisterContextRegisterInfo.cpp @@ -9,7 +9,7 @@ #include "lldb/lldb-types.h" #include "lldb/lldb-private-forward.h" -#include "lldb/Target/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h" using namespace lldb_private; Index: source/Host/common/NativeThreadProtocol.cpp =================================================================== --- source/Host/common/NativeThreadProtocol.cpp +++ source/Host/common/NativeThreadProtocol.cpp @@ -7,11 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "NativeThreadProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" -#include "NativeProcessProtocol.h" -#include "lldb/Target/NativeRegisterContext.h" -#include "SoftwareBreakpoint.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" using namespace lldb; using namespace lldb_private; @@ -73,25 +73,3 @@ { return m_process_wp.lock (); } - -uint32_t -NativeThreadProtocol::TranslateStopInfoToGdbSignal (const ThreadStopInfo &stop_info) const -{ - // Default: no translation. Do the real translation where there - // is access to the host signal numbers. - switch (stop_info.reason) - { - case eStopReasonSignal: - return stop_info.details.signal.signo; - break; - - case eStopReasonException: - // FIXME verify the way to specify pass-thru here. - return static_cast (stop_info.details.exception.type); - break; - - default: - assert (0 && "unexpected stop_info.reason found"); - return 0; - } -} Index: source/Host/common/NativeWatchpointList.cpp =================================================================== --- /dev/null +++ source/Host/common/NativeWatchpointList.cpp @@ -0,0 +1,35 @@ +//===-- NativeWatchpointList.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/common/NativeWatchpointList.h" + +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +Error +NativeWatchpointList::Add (addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + m_watchpoints[addr] = {addr, size, watch_flags, hardware}; + return Error (); +} + +Error +NativeWatchpointList::Remove (addr_t addr) +{ + m_watchpoints.erase(addr); + return Error (); +} + +const NativeWatchpointList::WatchpointMap& +NativeWatchpointList::GetWatchpointMap () const +{ + return m_watchpoints; +} Index: source/Host/common/SoftwareBreakpoint.h =================================================================== --- /dev/null +++ source/Host/common/SoftwareBreakpoint.h @@ -1,51 +0,0 @@ -//===-- SoftwareBreakpoint.h ------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_SoftwareBreakpoint_h_ -#define liblldb_SoftwareBreakpoint_h_ - -#include "lldb/lldb-private-forward.h" -#include "NativeBreakpoint.h" - -namespace lldb_private -{ - class SoftwareBreakpoint : public NativeBreakpoint - { - public: - static Error - CreateSoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, size_t size_hint, NativeBreakpointSP &breakpoint_spn); - - SoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, const uint8_t *saved_opcodes, const uint8_t *trap_opcodes, size_t opcode_size); - - protected: - Error - DoEnable () override; - - Error - DoDisable () override; - - bool - IsSoftwareBreakpoint () const override; - - private: - /// Max number of bytes that a software trap opcode sequence can occupy. - static const size_t MAX_TRAP_OPCODE_SIZE = 8; - - NativeProcessProtocol &m_process; - uint8_t m_saved_opcodes [MAX_TRAP_OPCODE_SIZE]; - uint8_t m_trap_opcodes [MAX_TRAP_OPCODE_SIZE]; - const size_t m_opcode_size; - - static Error - EnableSoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, size_t bp_opcode_size, const uint8_t *bp_opcode_bytes, uint8_t *saved_opcode_bytes); - - }; -} - -#endif // #ifndef liblldb_SoftwareBreakpoint_h_ Index: source/Host/common/SoftwareBreakpoint.cpp =================================================================== --- source/Host/common/SoftwareBreakpoint.cpp +++ source/Host/common/SoftwareBreakpoint.cpp @@ -7,14 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "SoftwareBreakpoint.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Host/Debug.h" #include "lldb/Host/Mutex.h" -#include "NativeProcessProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" using namespace lldb_private; Index: source/Plugins/Platform/Linux/PlatformLinux.cpp =================================================================== --- source/Plugins/Platform/Linux/PlatformLinux.cpp +++ source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -761,6 +761,7 @@ // Adjust launch for a hijacker. ListenerSP listener_sp; +#if 0 if (!launch_info.GetHijackListener ()) { if (log) @@ -770,6 +771,7 @@ launch_info.SetHijackListener (listener_sp); process_sp->HijackProcessEvents (listener_sp.get ()); } +#endif // Log file actions. if (log) Index: source/Plugins/Process/FreeBSD/ProcessMonitor.h =================================================================== --- source/Plugins/Process/FreeBSD/ProcessMonitor.h +++ source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -311,18 +311,6 @@ MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::pid_t pid); - static ProcessMessage::CrashReason - GetCrashReasonForSIGSEGV(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGILL(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGFPE(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGBUS(const siginfo_t *info); - void DoOperation(Operation *op); Index: source/Plugins/Process/FreeBSD/ProcessMonitor.cpp =================================================================== --- source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -30,7 +30,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Utility/PseudoTerminal.h" - +#include "Plugins/Process/POSIX/CrashReason.h" #include "POSIXThread.h" #include "ProcessFreeBSD.h" #include "ProcessPOSIXLog.h" @@ -1306,27 +1306,14 @@ if (log) log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo)); - if (signo == SIGSEGV) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); - return ProcessMessage::Crash(tid, reason, signo, fault_addr); - } - - if (signo == SIGILL) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); - return ProcessMessage::Crash(tid, reason, signo, fault_addr); - } - - if (signo == SIGFPE) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); - return ProcessMessage::Crash(tid, reason, signo, fault_addr); - } - - if (signo == SIGBUS) { + switch (signo) + { + case SIGSEGV: + case SIGILL: + case SIGFPE: + case SIGBUS: lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); + const auto reason = GetCrashReason(*info); return ProcessMessage::Crash(tid, reason, signo, fault_addr); } @@ -1335,141 +1322,6 @@ return ProcessMessage::Signal(tid, signo); } -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGSEGV); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGSEGV"); - break; - case SEGV_MAPERR: - reason = ProcessMessage::eInvalidAddress; - break; - case SEGV_ACCERR: - reason = ProcessMessage::ePrivilegedAddress; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGILL); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGILL"); - break; - case ILL_ILLOPC: - reason = ProcessMessage::eIllegalOpcode; - break; - case ILL_ILLOPN: - reason = ProcessMessage::eIllegalOperand; - break; - case ILL_ILLADR: - reason = ProcessMessage::eIllegalAddressingMode; - break; - case ILL_ILLTRP: - reason = ProcessMessage::eIllegalTrap; - break; - case ILL_PRVOPC: - reason = ProcessMessage::ePrivilegedOpcode; - break; - case ILL_PRVREG: - reason = ProcessMessage::ePrivilegedRegister; - break; - case ILL_COPROC: - reason = ProcessMessage::eCoprocessorError; - break; - case ILL_BADSTK: - reason = ProcessMessage::eInternalStackError; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGFPE); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGFPE"); - break; - case FPE_INTDIV: - reason = ProcessMessage::eIntegerDivideByZero; - break; - case FPE_INTOVF: - reason = ProcessMessage::eIntegerOverflow; - break; - case FPE_FLTDIV: - reason = ProcessMessage::eFloatDivideByZero; - break; - case FPE_FLTOVF: - reason = ProcessMessage::eFloatOverflow; - break; - case FPE_FLTUND: - reason = ProcessMessage::eFloatUnderflow; - break; - case FPE_FLTRES: - reason = ProcessMessage::eFloatInexactResult; - break; - case FPE_FLTINV: - reason = ProcessMessage::eFloatInvalidOperation; - break; - case FPE_FLTSUB: - reason = ProcessMessage::eFloatSubscriptRange; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGBUS); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGBUS"); - break; - case BUS_ADRALN: - reason = ProcessMessage::eIllegalAlignment; - break; - case BUS_ADRERR: - reason = ProcessMessage::eIllegalAddress; - break; - case BUS_OBJERR: - reason = ProcessMessage::eHardwareError; - break; - } - - return reason; -} - void ProcessMonitor::ServeOperation(OperationArgs *args) { Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -25,12 +25,13 @@ #include "lldb/Host/Mutex.h" #include "lldb/Target/MemoryRegionInfo.h" -#include "Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" namespace lldb_private { class Error; class Module; + class ThreadStateCoordinator; class Scalar; /// @class NativeProcessLinux @@ -82,6 +83,9 @@ Signal (int signo) override; Error + Interrupt () override; + + Error Kill () override; Error @@ -122,7 +126,7 @@ /// dependent) offset. /// /// This method is provided for use by RegisterContextLinux derivatives. - bool + Error ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, lldb_private::RegisterValue &value); @@ -130,34 +134,34 @@ /// (architecture dependent) offset. /// /// This method is provided for use by RegisterContextLinux derivatives. - bool + Error WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, const lldb_private::RegisterValue &value); /// Reads all general purpose registers into the specified buffer. - bool + Error ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Reads generic floating point registers into the specified buffer. - bool + Error ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Reads the specified register set into the specified buffer. /// For instance, the extended floating-point register set. - bool + Error ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); /// Writes all general purpose registers into the specified buffer. - bool + Error WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Writes generic floating point registers into the specified buffer. - bool + Error WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size); /// Writes the specified register set into the specified buffer. /// For instance, the extended floating-point register set. - bool + Error WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); protected: @@ -183,21 +187,12 @@ sem_t m_operation_pending; sem_t m_operation_done; - // Set of tids we're waiting to stop before we notify the delegate of - // the stopped state. We only notify the delegate after all threads - // ordered to stop have signaled their stop. - std::unordered_set m_wait_for_stop_tids; - lldb_private::Mutex m_wait_for_stop_tids_mutex; - - std::unordered_set m_wait_for_group_stop_tids; - lldb::tid_t m_group_stop_signal_tid; - int m_group_stop_signal; - lldb_private::Mutex m_wait_for_group_stop_tids_mutex; - lldb_private::LazyBool m_supports_mem_region; std::vector m_mem_region_cache; lldb_private::Mutex m_mem_region_cache_mutex; + std::unique_ptr m_coordinator_up; + HostThread m_coordinator_thread; struct OperationArgs { @@ -290,7 +285,7 @@ static bool Attach(AttachArgs *args); - static bool + static Error SetDefaultPtraceOpts(const lldb::pid_t); static void @@ -334,6 +329,15 @@ void StopOpThread(); + Error + StartCoordinatorThread (); + + static void* + CoordinatorThread (void *arg); + + void + StopCoordinatorThread (); + /// Stops monitoring the child process thread. void StopMonitor(); @@ -361,38 +365,52 @@ /// Writes a siginfo_t structure corresponding to the given thread ID to the /// memory region pointed to by @p siginfo. - bool - GetSignalInfo(lldb::tid_t tid, void *siginfo, int &ptrace_err); + Error + GetSignalInfo(lldb::tid_t tid, void *siginfo); /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) /// corresponding to the given thread ID to the memory pointed to by @p /// message. - bool + Error GetEventMessage(lldb::tid_t tid, unsigned long *message); /// Resumes the given thread. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - bool + Error Resume(lldb::tid_t tid, uint32_t signo); /// Single steps the given thread. If @p signo is anything but /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - bool + Error SingleStep(lldb::tid_t tid, uint32_t signo); - /// Safely mark all existing threads as waiting for group stop. - /// When the final group stop comes in from the set of group stop threads, - /// we'll mark the current thread as signaled_thread_tid and set its stop - /// reason as the given signo. All other threads from group stop notification - /// will have thread stop reason marked as signaled with no signo. + // ThreadStateCoordinator helper methods. + void + NotifyThreadCreateStopped (lldb::tid_t tid); + void - SetGroupStopTids (lldb::tid_t signaled_thread_tid, int signo); + NotifyThreadCreateRunning (lldb::tid_t tid); void - OnGroupStop (lldb::tid_t tid); + NotifyThreadDeath (lldb::tid_t tid); + + void + NotifyThreadStop (lldb::tid_t tid); + + void + CallAfterRunningThreadsStop (lldb::tid_t tid, + const std::function &call_after_function); + + void + CallAfterRunningThreadsStopWithSkipTID (lldb::tid_t deferred_signal_tid, + lldb::tid_t skip_stop_request_tid, + const std::function &call_after_function); lldb_private::Error Detach(lldb::tid_t tid); + + lldb_private::Error + RequestThreadStop (const lldb::pid_t pid, const lldb::tid_t tid); }; } // End lldb_private namespace. Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -56,16 +56,17 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Symbol/ObjectFile.h" -#include "lldb/Target/NativeRegisterContext.h" +#include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Utility/PseudoTerminal.h" -#include "Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/NativeBreakpoint.h" #include "Utility/StringExtractor.h" #include "Plugins/Process/Utility/LinuxSignals.h" #include "NativeThreadLinux.h" #include "ProcFileReader.h" +#include "ThreadStateCoordinator.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #ifdef __ANDROID__ @@ -129,11 +130,11 @@ // We disable the tracing of ptrace calls for integration builds to // avoid the additional indirection and checks. #ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION -#define PTRACE(req, pid, addr, data, data_size) \ - PtraceWrapper((req), (pid), (addr), (data), (data_size), #req, __FILE__, __LINE__) +#define PTRACE(req, pid, addr, data, data_size, error) \ + PtraceWrapper((req), (pid), (addr), (data), (data_size), (error), #req, __FILE__, __LINE__) #else -#define PTRACE(req, pid, addr, data, data_size) \ - PtraceWrapper((req), (pid), (addr), (data), (data_size)) +#define PTRACE(req, pid, addr, data, data_size, error) \ + PtraceWrapper((req), (pid), (addr), (data), (data_size), (error)) #endif // Private bits we only need internally. @@ -149,6 +150,26 @@ return signals; } + ThreadStateCoordinator::LogFunction + GetThreadLoggerFunction () + { + return [](const char *format, va_list args) + { + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + if (log) + log->VAPrintf (format, args); + }; + } + + void + CoordinatorErrorHandler (const std::string &error_message) + { + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + if (log) + log->Printf ("NativeProcessLinux::%s %s", __FUNCTION__, error_message.c_str ()); + assert (false && "ThreadStateCoordinator error reported"); + } + Error ResolveProcessArchitecture (lldb::pid_t pid, Platform &platform, ArchSpec &arch) { @@ -253,8 +274,8 @@ // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) long - PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, - const char* reqName, const char* file, int line) + PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, Error& error, + const char* reqName, const char* file, int line) { long int result; @@ -262,30 +283,34 @@ PtraceDisplayBytes(req, data, data_size); + error.Clear(); errno = 0; if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) result = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), *(unsigned int *)addr, data); else result = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), addr, data); + if (result == -1) + error.SetErrorToErrno(); + if (log) log->Printf("ptrace(%s, %" PRIu64 ", %p, %p, %zu)=%lX called from file %s line %d", reqName, pid, addr, data, data_size, result, file, line); PtraceDisplayBytes(req, data, data_size); - if (log && errno != 0) + if (log && error.GetError() != 0) { const char* str; - switch (errno) + switch (error.GetError()) { case ESRCH: str = "ESRCH"; break; case EINVAL: str = "EINVAL"; break; case EBUSY: str = "EBUSY"; break; case EPERM: str = "EPERM"; break; - default: str = ""; + default: str = error.AsCString(); } - log->Printf("ptrace() failed; errno=%d (%s)", errno, str); + log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str); } return result; @@ -295,14 +320,19 @@ // Wrapper for ptrace when logging is not required. // Sets errno to 0 prior to calling ptrace. long - PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size) + PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, Error& error) { long result = 0; + + error.Clear(); errno = 0; if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) result = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), *(unsigned int *)addr, data); else result = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), addr, data); + + if (result == -1) + error.SetErrorToErrno(); return result; } #endif @@ -312,7 +342,7 @@ // NativeProcessLinux::WriteMemory. This enables mutual recursion between these // functions without needed to go thru the thread funnel. - static lldb::addr_t + lldb::addr_t DoReadMemory ( lldb::pid_t pid, lldb::addr_t vm_addr, @@ -337,11 +367,9 @@ assert(sizeof(data) >= word_size); for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { - errno = 0; - data = PTRACE(PTRACE_PEEKDATA, pid, (void*)vm_addr, NULL, 0); - if (errno) + data = PTRACE(PTRACE_PEEKDATA, pid, (void*)vm_addr, nullptr, 0, error); + if (error.Fail()) { - error.SetErrorToErrno(); if (log) ProcessPOSIXLog::DecNestLevel(); return bytes_read; @@ -376,7 +404,7 @@ return bytes_read; } - static lldb::addr_t + lldb::addr_t DoWriteMemory( lldb::pid_t pid, lldb::addr_t vm_addr, @@ -416,9 +444,8 @@ log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__, (void*)vm_addr, *(unsigned long*)src, data); - if (PTRACE(PTRACE_POKEDATA, pid, (void*)vm_addr, (void*)data, 0)) + if (PTRACE(PTRACE_POKEDATA, pid, (void*)vm_addr, (void*)data, 0, error)) { - error.SetErrorToErrno(); if (log) ProcessPOSIXLog::DecNestLevel(); return bytes_written; @@ -565,9 +592,11 @@ { public: ReadRegOperation(lldb::tid_t tid, uint32_t offset, const char *reg_name, - RegisterValue &value, bool &result) - : m_tid(tid), m_offset(static_cast (offset)), m_reg_name(reg_name), - m_value(value), m_result(result) + RegisterValue &value) + : m_tid(tid), + m_offset(static_cast (offset)), + m_reg_name(reg_name), + m_value(value) { } void Execute(NativeProcessLinux *monitor); @@ -577,7 +606,6 @@ uintptr_t m_offset; const char *m_reg_name; RegisterValue &m_value; - bool &m_result; }; void @@ -589,29 +617,23 @@ uintptr_t offset = m_offset - sizeof(struct user_pt_regs); if (offset > sizeof(struct user_fpsimd_state)) { - m_result = false; + m_error.SetErrorString("invalid offset value"); + return; } - else + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); + if (m_error.Success()) { - elf_fpregset_t regs; - int regset = NT_FPREGSET; - struct iovec ioVec; - - ioVec.iov_base = ®s; - ioVec.iov_len = sizeof regs; - if (PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; + lldb_private::ArchSpec arch; + if (monitor->GetArchitecture(arch)) + m_value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 16, arch.GetByteOrder()); else - { - lldb_private::ArchSpec arch; - if (monitor->GetArchitecture(arch)) - { - m_result = true; - m_value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 16, arch.GetByteOrder()); - } - else - m_result = false; - } + m_error.SetErrorString("failed to get architecture"); } } else @@ -622,32 +644,23 @@ ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; - if (PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; - else + PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); + if (m_error.Success()) { lldb_private::ArchSpec arch; if (monitor->GetArchitecture(arch)) - { - m_result = true; m_value.SetBytes((void *)(((unsigned char *)(regs)) + m_offset), 8, arch.GetByteOrder()); - } else - m_result = false; + else + m_error.SetErrorString("failed to get architecture"); } } #else Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); - // Set errno to zero so that we can detect a failed peek. - errno = 0; - lldb::addr_t data = PTRACE(PTRACE_PEEKUSER, m_tid, (void*)m_offset, NULL, 0); - if (errno) - m_result = false; - else - { + lldb::addr_t data = PTRACE(PTRACE_PEEKUSER, m_tid, (void*)m_offset, nullptr, 0, m_error); + if (m_error.Success()) m_value = data; - m_result = true; - } + if (log) log->Printf ("NativeProcessLinux::%s() reg %s: 0x%" PRIx64, __FUNCTION__, m_reg_name, data); @@ -661,9 +674,11 @@ { public: WriteRegOperation(lldb::tid_t tid, unsigned offset, const char *reg_name, - const RegisterValue &value, bool &result) - : m_tid(tid), m_offset(offset), m_reg_name(reg_name), - m_value(value), m_result(result) + const RegisterValue &value) + : m_tid(tid), + m_offset(offset), + m_reg_name(reg_name), + m_value(value) { } void Execute(NativeProcessLinux *monitor); @@ -673,7 +688,6 @@ uintptr_t m_offset; const char *m_reg_name; const RegisterValue &m_value; - bool &m_result; }; void @@ -685,26 +699,20 @@ uintptr_t offset = m_offset - sizeof(struct user_pt_regs); if (offset > sizeof(struct user_fpsimd_state)) { - m_result = false; + m_error.SetErrorString("invalid offset value"); + return; } - else + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); + if (m_error.Sucess()) { - elf_fpregset_t regs; - int regset = NT_FPREGSET; - struct iovec ioVec; - - ioVec.iov_base = ®s; - ioVec.iov_len = sizeof regs; - if (PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; - else - { - ::memcpy((void *)(((unsigned char *)(®s)) + offset), m_value.GetBytes(), 16); - if (PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; - else - m_result = true; - } + ::memcpy((void *)(((unsigned char *)(®s)) + offset), m_value.GetBytes(), 16); + PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); } } else @@ -715,15 +723,11 @@ ioVec.iov_base = ®s; ioVec.iov_len = sizeof regs; - if (PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; - else + PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); + if (m_error.Sucess()) { ::memcpy((void *)(((unsigned char *)(®s)) + m_offset), m_value.GetBytes(), 8); - if (PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, sizeof regs) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, sizeof regs, m_error); } } #else @@ -734,10 +738,7 @@ if (log) log->Printf ("NativeProcessLinux::%s() reg %s: %p", __FUNCTION__, m_reg_name, buf); - if (PTRACE(PTRACE_POKEUSER, m_tid, (void*)m_offset, buf, 0)) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_POKEUSER, m_tid, (void*)m_offset, buf, 0, m_error); #endif } @@ -747,8 +748,8 @@ class ReadGPROperation : public Operation { public: - ReadGPROperation(lldb::tid_t tid, void *buf, size_t buf_size, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_result(result) + ReadGPROperation(lldb::tid_t tid, void *buf, size_t buf_size) + : m_tid(tid), m_buf(buf), m_buf_size(buf_size) { } void Execute(NativeProcessLinux *monitor); @@ -757,7 +758,6 @@ lldb::tid_t m_tid; void *m_buf; size_t m_buf_size; - bool &m_result; }; void @@ -769,15 +769,9 @@ ioVec.iov_base = m_buf; ioVec.iov_len = m_buf_size; - if (PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_GETREGSET, m_tid, ®set, &ioVec, m_buf_size, m_error); #else - if (PTRACE(PTRACE_GETREGS, m_tid, NULL, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_GETREGS, m_tid, nullptr, m_buf, m_buf_size, m_error); #endif } @@ -787,8 +781,10 @@ class ReadFPROperation : public Operation { public: - ReadFPROperation(lldb::tid_t tid, void *buf, size_t buf_size, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_result(result) + ReadFPROperation(lldb::tid_t tid, void *buf, size_t buf_size) + : m_tid(tid), + m_buf(buf), + m_buf_size(buf_size) { } void Execute(NativeProcessLinux *monitor); @@ -797,7 +793,6 @@ lldb::tid_t m_tid; void *m_buf; size_t m_buf_size; - bool &m_result; }; void @@ -814,10 +809,7 @@ else m_result = true; #else - if (PTRACE(PTRACE_GETFPREGS, m_tid, NULL, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_GETFPREGS, m_tid, nullptr, m_buf, m_buf_size, m_error); #endif } @@ -827,8 +819,8 @@ class ReadRegisterSetOperation : public Operation { public: - ReadRegisterSetOperation(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_regset(regset), m_result(result) + ReadRegisterSetOperation(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) + : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_regset(regset) { } void Execute(NativeProcessLinux *monitor); @@ -838,16 +830,12 @@ void *m_buf; size_t m_buf_size; const unsigned int m_regset; - bool &m_result; }; void ReadRegisterSetOperation::Execute(NativeProcessLinux *monitor) { - if (PTRACE(PTRACE_GETREGSET, m_tid, (void *)&m_regset, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_GETREGSET, m_tid, (void *)&m_regset, m_buf, m_buf_size, m_error); } //------------------------------------------------------------------------------ @@ -856,8 +844,8 @@ class WriteGPROperation : public Operation { public: - WriteGPROperation(lldb::tid_t tid, void *buf, size_t buf_size, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_result(result) + WriteGPROperation(lldb::tid_t tid, void *buf, size_t buf_size) + : m_tid(tid), m_buf(buf), m_buf_size(buf_size) { } void Execute(NativeProcessLinux *monitor); @@ -866,7 +854,6 @@ lldb::tid_t m_tid; void *m_buf; size_t m_buf_size; - bool &m_result; }; void @@ -878,15 +865,9 @@ ioVec.iov_base = m_buf; ioVec.iov_len = m_buf_size; - if (PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, m_buf_size, m_error); #else - if (PTRACE(PTRACE_SETREGS, m_tid, NULL, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETREGS, m_tid, NULL, m_buf, m_buf_size, m_error); #endif } @@ -896,8 +877,8 @@ class WriteFPROperation : public Operation { public: - WriteFPROperation(lldb::tid_t tid, void *buf, size_t buf_size, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_result(result) + WriteFPROperation(lldb::tid_t tid, void *buf, size_t buf_size) + : m_tid(tid), m_buf(buf), m_buf_size(buf_size) { } void Execute(NativeProcessLinux *monitor); @@ -906,7 +887,6 @@ lldb::tid_t m_tid; void *m_buf; size_t m_buf_size; - bool &m_result; }; void @@ -918,15 +898,9 @@ ioVec.iov_base = m_buf; ioVec.iov_len = m_buf_size; - if (PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETREGSET, m_tid, ®set, &ioVec, m_buf_size, m_error); #else - if (PTRACE(PTRACE_SETFPREGS, m_tid, NULL, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETFPREGS, m_tid, NULL, m_buf, m_buf_size, m_error); #endif } @@ -936,8 +910,8 @@ class WriteRegisterSetOperation : public Operation { public: - WriteRegisterSetOperation(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset, bool &result) - : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_regset(regset), m_result(result) + WriteRegisterSetOperation(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) + : m_tid(tid), m_buf(buf), m_buf_size(buf_size), m_regset(regset) { } void Execute(NativeProcessLinux *monitor); @@ -947,16 +921,12 @@ void *m_buf; size_t m_buf_size; const unsigned int m_regset; - bool &m_result; }; void WriteRegisterSetOperation::Execute(NativeProcessLinux *monitor) { - if (PTRACE(PTRACE_SETREGSET, m_tid, (void *)&m_regset, m_buf, m_buf_size) < 0) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SETREGSET, m_tid, (void *)&m_regset, m_buf, m_buf_size, m_error); } //------------------------------------------------------------------------------ @@ -965,15 +935,14 @@ class ResumeOperation : public Operation { public: - ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) : - m_tid(tid), m_signo(signo), m_result(result) { } + ResumeOperation(lldb::tid_t tid, uint32_t signo) : + m_tid(tid), m_signo(signo) { } void Execute(NativeProcessLinux *monitor); private: lldb::tid_t m_tid; uint32_t m_signo; - bool &m_result; }; void @@ -984,16 +953,14 @@ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; - if (PTRACE(PTRACE_CONT, m_tid, NULL, (void*)data, 0)) + PTRACE(PTRACE_CONT, m_tid, nullptr, (void*)data, 0, m_error); + if (m_error.Fail()) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) - log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", m_tid, strerror(errno)); - m_result = false; + log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", m_tid, m_error.AsCString()); } - else - m_result = true; } //------------------------------------------------------------------------------ @@ -1002,15 +969,14 @@ class SingleStepOperation : public Operation { public: - SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result) - : m_tid(tid), m_signo(signo), m_result(result) { } + SingleStepOperation(lldb::tid_t tid, uint32_t signo) + : m_tid(tid), m_signo(signo) { } void Execute(NativeProcessLinux *monitor); private: lldb::tid_t m_tid; uint32_t m_signo; - bool &m_result; }; void @@ -1021,10 +987,7 @@ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; - if (PTRACE(PTRACE_SINGLESTEP, m_tid, NULL, (void*)data, 0)) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_SINGLESTEP, m_tid, nullptr, (void*)data, 0, m_error); } //------------------------------------------------------------------------------ @@ -1033,27 +996,20 @@ class SiginfoOperation : public Operation { public: - SiginfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) - : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) { } + SiginfoOperation(lldb::tid_t tid, void *info) + : m_tid(tid), m_info(info) { } void Execute(NativeProcessLinux *monitor); private: lldb::tid_t m_tid; void *m_info; - bool &m_result; - int &m_err; }; void SiginfoOperation::Execute(NativeProcessLinux *monitor) { - if (PTRACE(PTRACE_GETSIGINFO, m_tid, NULL, m_info, 0)) { - m_result = false; - m_err = errno; - } - else - m_result = true; + PTRACE(PTRACE_GETSIGINFO, m_tid, nullptr, m_info, 0, m_error); } //------------------------------------------------------------------------------ @@ -1062,43 +1018,37 @@ class EventMessageOperation : public Operation { public: - EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) - : m_tid(tid), m_message(message), m_result(result) { } + EventMessageOperation(lldb::tid_t tid, unsigned long *message) + : m_tid(tid), m_message(message) { } void Execute(NativeProcessLinux *monitor); private: lldb::tid_t m_tid; unsigned long *m_message; - bool &m_result; }; void EventMessageOperation::Execute(NativeProcessLinux *monitor) { - if (PTRACE(PTRACE_GETEVENTMSG, m_tid, NULL, m_message, 0)) - m_result = false; - else - m_result = true; + PTRACE(PTRACE_GETEVENTMSG, m_tid, nullptr, m_message, 0, m_error); } class DetachOperation : public Operation { public: - DetachOperation(lldb::tid_t tid, Error &result) : m_tid(tid), m_error(result) { } + DetachOperation(lldb::tid_t tid) : m_tid(tid) { } void Execute(NativeProcessLinux *monitor); private: lldb::tid_t m_tid; - Error &m_error; }; void DetachOperation::Execute(NativeProcessLinux *monitor) { - if (ptrace(PT_DETACH, m_tid, NULL, 0) < 0) - m_error.SetErrorToErrno(); + PTRACE(PTRACE_DETACH, m_tid, nullptr, 0, 0, m_error); } } @@ -1322,19 +1272,17 @@ NativeProcessLinux::NativeProcessLinux () : NativeProcessProtocol (LLDB_INVALID_PROCESS_ID), m_arch (), + m_operation_thread (), + m_monitor_thread (), m_operation (nullptr), m_operation_mutex (), m_operation_pending (), m_operation_done (), - m_wait_for_stop_tids (), - m_wait_for_stop_tids_mutex (), - m_wait_for_group_stop_tids (), - m_group_stop_signal_tid (LLDB_INVALID_THREAD_ID), - m_group_stop_signal (LLDB_INVALID_SIGNAL_NUMBER), - m_wait_for_group_stop_tids_mutex (), m_supports_mem_region (eLazyBoolCalculate), m_mem_region_cache (), - m_mem_region_cache_mutex () + m_mem_region_cache_mutex (), + m_coordinator_up (new ThreadStateCoordinator (GetThreadLoggerFunction ())), + m_coordinator_thread () { } @@ -1365,7 +1313,7 @@ if (module) m_arch = module->GetArchitecture (); - SetState(eStateLaunching); + SetState (eStateLaunching); std::unique_ptr args( new LaunchArgs( @@ -1373,11 +1321,15 @@ stdin_path, stdout_path, stderr_path, working_dir, launch_info)); - sem_init(&m_operation_pending, 0, 0); - sem_init(&m_operation_done, 0, 0); + sem_init (&m_operation_pending, 0, 0); + sem_init (&m_operation_done, 0, 0); - StartLaunchOpThread(args.get(), error); - if (!error.Success()) + StartLaunchOpThread (args.get(), error); + if (!error.Success ()) + return; + + error = StartCoordinatorThread (); + if (!error.Success ()) return; WAIT_AGAIN: @@ -1397,6 +1349,7 @@ if (!args->m_error.Success()) { StopOpThread(); + StopCoordinatorThread (); error = args->m_error; return; } @@ -1465,6 +1418,10 @@ if (!error.Success ()) return; + error = StartCoordinatorThread (); + if (!error.Success ()) + return; + WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait (&args->m_semaphore)) @@ -1482,6 +1439,7 @@ if (!args->m_error.Success ()) { StopOpThread (); + StopCoordinatorThread (); error = args->m_error; return; } @@ -1583,7 +1541,8 @@ // send log info to parent re: launch status, in place of the log lines removed here. // Start tracing this child that is about to exec. - if (PTRACE(PTRACE_TRACEME, 0, NULL, NULL, 0) < 0) + PTRACE(PTRACE_TRACEME, 0, nullptr, nullptr, 0, args->m_error); + if (args->m_error.Fail()) exit(ePtraceFailed); // Do not inherit setgid powers. @@ -1718,9 +1677,9 @@ if (log) log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__); - if (!SetDefaultPtraceOpts(pid)) + args->m_error = SetDefaultPtraceOpts(pid); + if (args->m_error.Fail()) { - args->m_error.SetErrorToErrno(); if (log) log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s", __FUNCTION__, @@ -1758,12 +1717,13 @@ if (log) log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid); - thread_sp = monitor->AddThread (static_cast (pid)); + thread_sp = monitor->AddThread (pid); assert (thread_sp && "AddThread() returned a nullptr thread"); + monitor->NotifyThreadCreateStopped (pid); reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGSTOP); - monitor->SetCurrentThreadID (thread_sp->GetID ()); // Let our process instance know the thread has stopped. + monitor->SetCurrentThreadID (thread_sp->GetID ()); monitor->SetState (StateType::eStateStopped); if (log) @@ -1800,11 +1760,11 @@ if (!Attach(args)) { sem_post(&args->m_semaphore); - return NULL; + return nullptr; } ServeOperation(args); - return NULL; + return nullptr; } bool @@ -1836,20 +1796,18 @@ // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. - if (PTRACE(PTRACE_ATTACH, tid, NULL, NULL, 0) < 0) + PTRACE(PTRACE_ATTACH, tid, nullptr, nullptr, 0, args->m_error); + if (args->m_error.Fail()) { // No such thread. The thread may have exited. // More error handling may be needed. - if (errno == ESRCH) + if (args->m_error.GetError() == ESRCH) { it = tids_to_attach.erase(it); continue; } else - { - args->m_error.SetErrorToErrno(); goto FINISH; - } } int status; @@ -1871,11 +1829,9 @@ } } - if (!SetDefaultPtraceOpts(tid)) - { - args->m_error.SetErrorToErrno(); + args->m_error = SetDefaultPtraceOpts(tid); + if (args->m_error.Fail()) goto FINISH; - } if (log) @@ -1886,6 +1842,9 @@ // Create the thread, mark it as stopped. NativeThreadProtocolSP thread_sp (monitor->AddThread (static_cast (tid))); assert (thread_sp && "AddThread() returned a nullptr"); + + // This will notify this is a new thread and tell the system it is stopped. + monitor->NotifyThreadCreateStopped (tid); reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGSTOP); monitor->SetCurrentThreadID (thread_sp->GetID ()); } @@ -1911,7 +1870,7 @@ return args->m_error.Success(); } -bool +Error NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { long ptrace_opts = 0; @@ -1929,7 +1888,9 @@ // (needed to disable legacy SIGTRAP generation) ptrace_opts |= PTRACE_O_TRACEEXEC; - return PTRACE(PTRACE_SETOPTIONS, pid, NULL, (void*)ptrace_opts, 0) >= 0; + Error error; + PTRACE(PTRACE_SETOPTIONS, pid, nullptr, (void*)ptrace_opts, 0, error); + return error; } static ExitType convert_pid_status_to_exit_type (int status) @@ -1991,17 +1952,20 @@ if (exited) { if (log) - log->Printf ("NativeProcessLinux::%s() got exit signal, tid = %" PRIu64 " (%s main thread)", __FUNCTION__, pid, is_main_thread ? "is" : "is not"); + log->Printf ("NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64 " (%s main thread)", __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not"); // This is a thread that exited. Ensure we're not tracking it anymore. const bool thread_found = process->StopTrackingThread (pid); + // Make sure the thread state coordinator knows about this. + process->NotifyThreadDeath (pid); + if (is_main_thread) { // We only set the exit status and notify the delegate if we haven't already set the process // state to an exited state. We normally should have received a SIGTRAP | (PTRACE_EVENT_EXIT << 8) // for the main thread. - const bool already_notified = (process->GetState() == StateType::eStateExited) | (process->GetState () == StateType::eStateCrashed); + const bool already_notified = (process->GetState() == StateType::eStateExited) || (process->GetState () == StateType::eStateCrashed); if (!already_notified) { if (log) @@ -2034,13 +1998,25 @@ // Get details on the signal raised. siginfo_t info; - int ptrace_err = 0; + const auto err = process->GetSignalInfo(pid, &info); + if (err.Success()) + { + // We have retrieved the signal info. Dispatch appropriately. + if (info.si_signo == SIGTRAP) + process->MonitorSIGTRAP(&info, pid); + else + process->MonitorSignal(&info, pid, exited); - if (!process->GetSignalInfo (pid, &info, ptrace_err)) + stop_monitoring = false; + } + else { - if (ptrace_err == EINVAL) + if (err.GetError() == EINVAL) { - process->OnGroupStop (pid); + // This is a group stop reception for this tid. + if (log) + log->Printf ("NativeThreadLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64, __FUNCTION__, process->GetID (), pid); + process->NotifyThreadStop (pid); } else { @@ -2056,9 +2032,12 @@ // Stop tracking the metadata for the thread since it's entirely off the system now. const bool thread_found = process->StopTrackingThread (pid); + // Make sure the thread state coordinator knows about this. + process->NotifyThreadDeath (pid); + if (log) log->Printf ("NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d (%s, %s, %s)", - __FUNCTION__, strerror(ptrace_err), pid, signal, status, ptrace_err == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not found"); + __FUNCTION__, err.AsCString(), pid, signal, status, err.GetError() == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not found"); if (is_main_thread) { @@ -2075,16 +2054,6 @@ } } } - else - { - // We have retrieved the signal info. Dispatch appropriately. - if (info.si_signo == SIGTRAP) - process->MonitorSIGTRAP(&info, pid); - else - process->MonitorSignal(&info, pid, exited); - - stop_monitoring = false; - } return stop_monitoring; } @@ -2117,53 +2086,73 @@ { lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + // The main thread is stopped here. + if (thread_sp) + reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); + NotifyThreadStop (pid); + unsigned long event_message = 0; - if (GetEventMessage(pid, &event_message)) + if (GetEventMessage (pid, &event_message).Success()) + { tid = static_cast (event_message); + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event for tid %" PRIu64, __FUNCTION__, pid, tid); - if (log) - log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event for tid %" PRIu64, __FUNCTION__, pid, tid); - - // If we don't track the thread yet: create it, mark as stopped. - // If we do track it, this is the wait we needed. Now resume the new thread. - // In all cases, resume the current (i.e. main process) thread. - bool created_now = false; - thread_sp = GetOrCreateThread (tid, created_now); - assert (thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread"); - - // If the thread was already tracked, it means the created thread already received its SI_USER notification of creation. - if (!created_now) - { - // FIXME loops like we want to stop all theads here. - // StopAllThreads + // If we don't track the thread yet: create it, mark as stopped. + // If we do track it, this is the wait we needed. Now resume the new thread. + // In all cases, resume the current (i.e. main process) thread. + bool created_now = false; + NativeThreadProtocolSP new_thread_sp = GetOrCreateThread (tid, created_now); + assert (new_thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread"); - // We can now resume the newly created thread since it is fully created. - reinterpret_cast (thread_sp.get ())->SetRunning (); - Resume (tid, LLDB_INVALID_SIGNAL_NUMBER); + // If the thread was already tracked, it means the created thread already received its SI_USER notification of creation. + if (!created_now) + { + // We can now resume the newly created thread since it is fully created. + NotifyThreadCreateStopped (tid); + m_coordinator_up->RequestThreadResume (tid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (new_thread_sp.get ())->SetRunning (); + return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); + } + else + { + // Mark the thread as currently launching. Need to wait for SIGTRAP clone on the main thread before + // this thread is ready to go. + reinterpret_cast (new_thread_sp.get ())->SetLaunching (); + } } else { - // Mark the thread as currently launching. Need to wait for SIGTRAP clone on the main thread before - // this thread is ready to go. - reinterpret_cast (thread_sp.get ())->SetLaunching (); + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event but GetEventMessage failed so we don't know the new tid", __FUNCTION__, pid); } // In all cases, we can resume the main thread here. - Resume (pid, LLDB_INVALID_SIGNAL_NUMBER); + m_coordinator_up->RequestThreadResume (pid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); + break; } case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): { NativeThreadProtocolSP main_thread_sp; - if (log) log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, info->si_code ^ SIGTRAP); - // Remove all but the main thread here. - // FIXME check if we really need to do this - how does ptrace behave under exec when multiple threads were present - // before the exec? If we get all the detach signals right, we don't need to do this. However, it makes it clearer - // what we should really be tracking. + // The thread state coordinator needs to reset due to the exec. + m_coordinator_up->ResetForExec (); + + // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state. { Mutex::Locker locker (m_threads_mutex); @@ -2181,6 +2170,7 @@ } else { + // Tell thread coordinator this thread is dead. if (log) log->Printf ("NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64 " due to exec", __FUNCTION__, thread_sp->GetID ()); } @@ -2202,11 +2192,19 @@ } } + // Tell coordinator about about the "new" (since exec) stopped main thread. + const lldb::tid_t main_thread_tid = GetID (); + NotifyThreadCreateStopped (main_thread_tid); + + // NOTE: ideally these next statements would execute at the same time as the coordinator thread create was executed. + // Consider a handler that can execute when that happens. // Let our delegate know we have just exec'd. NotifyDidExec (); // If we have a main thread, indicate we are stopped. assert (main_thread_sp && "exec called during ptraced process but no main thread metadata tracked"); + + // Let the process know we're stopped. SetState (StateType::eStateStopped); break; @@ -2215,8 +2213,12 @@ case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): { // The inferior process or one of its threads is about to exit. + + // This thread is currently stopped. It's not actually dead yet, just about to be. + NotifyThreadStop (pid); + unsigned long data = 0; - if (!GetEventMessage(pid, &data)) + if (GetEventMessage(pid, &data).Fail()) data = -1; if (log) @@ -2228,22 +2230,19 @@ is_main_thread ? "is main thread" : "not main thread"); } - // Set the thread to exited. - if (thread_sp) - reinterpret_cast (thread_sp.get ())->SetExited (); - else - { - if (log) - log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " failed to retrieve thread for tid %" PRIu64", cannot set thread state", __FUNCTION__, GetID (), pid); - } - if (is_main_thread) { SetExitStatus (convert_pid_status_to_exit_type (data), convert_pid_status_to_return_code (data), nullptr, true); } - // Resume the thread so it completely exits. - Resume (pid, LLDB_INVALID_SIGNAL_NUMBER); + const int signo = static_cast (data); + m_coordinator_up->RequestThreadResume (pid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo); + }, + CoordinatorErrorHandler); break; } @@ -2256,16 +2255,18 @@ if (thread_sp) { - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); - SetCurrentThreadID (thread_sp->GetID ()); - } - else - { - if (log) - log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 " single stepping received trace but thread not found", __FUNCTION__, GetID (), pid); + reinterpret_cast (thread_sp.get ())->SetStoppedByTrace (); } - // Tell the process we have a stop (from single stepping). + // This thread is currently stopped. + NotifyThreadStop (pid); + + // Here we don't have to request the rest of the threads to stop or request a deferred stop. + // This would have already happened at the time the Resume() with step operation was signaled. + // At this point, we just need to say we stopped, and the deferred notifcation will fire off + // once all running threads have checked in as stopped. + SetCurrentThreadID (pid); + // Tell the process we have a stop (from software breakpoint). SetState (StateType::eStateStopped, true); break; @@ -2274,10 +2275,13 @@ if (log) log->Printf ("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid); + // This thread is currently stopped. + NotifyThreadStop (pid); + // Mark the thread as stopped at breakpoint. if (thread_sp) { - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); + reinterpret_cast (thread_sp.get ())->SetStoppedByBreakpoint (); Error error = FixupBreakpointPCAsNeeded (thread_sp); if (error.Fail ()) { @@ -2292,36 +2296,62 @@ } - // Tell the process we have a stop from this thread. - SetCurrentThreadID (pid); - SetState (StateType::eStateStopped, true); + // We need to tell all other running threads before we notify the delegate about this stop. + CallAfterRunningThreadsStop (pid, + [=](lldb::tid_t deferred_notification_tid) + { + SetCurrentThreadID (deferred_notification_tid); + // Tell the process we have a stop (from software breakpoint). + SetState (StateType::eStateStopped, true); + }); break; case TRAP_HWBKPT: if (log) log->Printf ("NativeProcessLinux::%s() received watchpoint event, pid = %" PRIu64, __FUNCTION__, pid); + // This thread is currently stopped. + NotifyThreadStop (pid); + // Mark the thread as stopped at watchpoint. // The address is at (lldb::addr_t)info->si_addr if we need it. if (thread_sp) - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); + reinterpret_cast (thread_sp.get ())->SetStoppedByWatchpoint (); else { if (log) log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ": warning, cannot process hardware breakpoint since no thread metadata", __FUNCTION__, GetID (), pid); } - // Tell the process we have a stop from this thread. - SetCurrentThreadID (pid); - SetState (StateType::eStateStopped, true); + // We need to tell all other running threads before we notify the delegate about this stop. + CallAfterRunningThreadsStop (pid, + [=](lldb::tid_t deferred_notification_tid) + { + SetCurrentThreadID (deferred_notification_tid); + // Tell the process we have a stop (from hardware breakpoint). + SetState (StateType::eStateStopped, true); + }); break; case SIGTRAP: case (SIGTRAP | 0x80): if (log) - log->Printf ("NativeProcessLinux::%s() received system call stop event, pid %" PRIu64 "tid %" PRIu64, __FUNCTION__, GetID (), pid); + log->Printf ("NativeProcessLinux::%s() received unknown SIGTRAP system call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming", __FUNCTION__, GetID (), pid); + + // This thread is currently stopped. + NotifyThreadStop (pid); + if (thread_sp) + reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); + + // Ignore these signals until we know more about them. - Resume(pid, 0); + m_coordinator_up->RequestThreadResume (pid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); break; default: @@ -2393,9 +2423,15 @@ // If the thread was already tracked, it means the main thread already received its SIGTRAP for the create. if (!created_now) { - // We can now resume this thread up since it is fully created. - reinterpret_cast (thread_sp.get ())->SetRunning (); - Resume (thread_sp->GetID (), LLDB_INVALID_SIGNAL_NUMBER); + // We can now resume the newly created thread since it is fully created. + NotifyThreadCreateStopped (pid); + m_coordinator_up->RequestThreadResume (pid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); } else { @@ -2414,34 +2450,52 @@ // This is a tgkill()-based stop. if (thread_sp) { - // An inferior thread just stopped. Mark it as such. - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (signo); - SetCurrentThreadID (thread_sp->GetID ()); - - // Remove this tid from the wait-for-stop set. - Mutex::Locker locker (m_wait_for_stop_tids_mutex); - - auto removed_count = m_wait_for_stop_tids.erase (thread_sp->GetID ()); - if (removed_count < 1) + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread stopped", + __FUNCTION__, + GetID (), + pid); + + // Check that we're not already marked with a stop reason. + // Note this thread really shouldn't already be marked as stopped - if we were, that would imply that + // the kernel signaled us with the thread stopping which we handled and marked as stopped, + // and that, without an intervening resume, we received another stop. It is more likely + // that we are missing the marking of a run state somewhere if we find that the thread was + // marked as stopped. + NativeThreadLinux *const linux_thread_p = reinterpret_cast (thread_sp.get ()); + assert (linux_thread_p && "linux_thread_p is null!"); + + const StateType thread_state = linux_thread_p->GetState (); + if (!StateIsStoppedState (thread_state, false)) { - log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " tid %" PRIu64 ": tgkill()-stopped thread not in m_wait_for_stop_tids", - __FUNCTION__, GetID (), thread_sp->GetID ()); - + // An inferior thread just stopped, but was not the primary cause of the process stop. + // Instead, something else (like a breakpoint or step) caused the stop. Mark the + // stop signal as 0 to let lldb know this isn't the important stop. + linux_thread_p->SetStoppedBySignal (0); + SetCurrentThreadID (thread_sp->GetID ()); + m_coordinator_up->NotifyThreadStop (thread_sp->GetID (), true, CoordinatorErrorHandler); } - - // If this is the last thread in the m_wait_for_stop_tids, we need to notify - // the delegate that a stop has occurred now that every thread that was supposed - // to stop has stopped. - if (m_wait_for_stop_tids.empty ()) + else { if (log) { - log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", setting process state to stopped now that all tids marked for stop have completed", + // Retrieve the signal name if the thread was stopped by a signal. + int stop_signo = 0; + const bool stopped_by_signal = linux_thread_p->IsStopped (&stop_signo); + const char *signal_name = stopped_by_signal ? GetUnixSignals ().GetSignalAsCString (stop_signo) : ""; + if (!signal_name) + signal_name = ""; + + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread was already marked as a stopped state (state=%s, signal=%d (%s)), leaving stop signal as is", __FUNCTION__, GetID (), - pid); + linux_thread_p->GetID (), + StateAsCString (thread_state), + stop_signo, + signal_name); } - SetState (StateType::eStateStopped, true); + // Tell the thread state coordinator about the stop. + NotifyThreadStop (thread_sp->GetID ()); } } @@ -2452,71 +2506,11 @@ if (log) log->Printf ("NativeProcessLinux::%s() received signal %s", __FUNCTION__, GetUnixSignals ().GetSignalAsCString (signo)); + // This thread is stopped. + NotifyThreadStop (pid); + switch (signo) { - case SIGSEGV: - { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - - // FIXME figure out how to propagate this properly. Seems like it - // should go in ThreadStopInfo. - // We can get more details on the exact nature of the crash here. - // ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); - if (!exited) - { - // This is just a pre-signal-delivery notification of the incoming signal. - // Send a stop to the debugger. - if (thread_sp) - { - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (signo); - SetCurrentThreadID (thread_sp->GetID ()); - } - SetState (StateType::eStateStopped, true); - } - else - { - if (thread_sp) - { - // FIXME figure out what type this is. - const uint64_t exception_type = static_cast (SIGSEGV); - reinterpret_cast (thread_sp.get ())->SetCrashedWithException (exception_type, fault_addr); - } - SetState (StateType::eStateCrashed, true); - } - } - break; - - case SIGABRT: - case SIGILL: - case SIGFPE: - case SIGBUS: - { - // Break these out into separate cases once I have more data for each type of signal. - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - if (!exited) - { - // This is just a pre-signal-delivery notification of the incoming signal. - // Send a stop to the debugger. - if (thread_sp) - { - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (signo); - SetCurrentThreadID (thread_sp->GetID ()); - } - SetState (StateType::eStateStopped, true); - } - else - { - if (thread_sp) - { - // FIXME figure out how to report exit by signal correctly. - const uint64_t exception_type = static_cast (SIGABRT); - reinterpret_cast (thread_sp.get ())->SetCrashedWithException (exception_type, fault_addr); - } - SetState (StateType::eStateCrashed, true); - } - } - break; - case SIGSTOP: { if (log) @@ -2527,165 +2521,40 @@ log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " received SIGSTOP from outside of debugger", __FUNCTION__, GetID (), pid); } - // Save group stop tids to wait for. - SetGroupStopTids (pid, SIGSTOP); - // Fall through to deliver signal to thread. - // This will trigger a group stop sequence, after which we'll notify the process that everything stopped. + // Resume this thread to get the group-stop mechanism to fire off the true group stops. + // This thread will get stopped again as part of the group-stop completion. + m_coordinator_up->RequestThreadResume (pid, + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + // Pass this signal number on to the inferior to handle. + return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo); + }, + CoordinatorErrorHandler); } - + break; + case SIGSEGV: + case SIGILL: + case SIGFPE: + case SIGBUS: + if (thread_sp) + reinterpret_cast (thread_sp.get ())->SetCrashedWithException (*info); + break; default: - { - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " resuming thread with signal %s (%d)", __FUNCTION__, GetID (), pid, GetUnixSignals().GetSignalAsCString (signo), signo); - - // Pass the signal on to the inferior. - const bool resume_success = Resume (pid, signo); - - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " resume %s", __FUNCTION__, GetID (), pid, resume_success ? "SUCCESS" : "FAILURE"); + // This is just a pre-signal-delivery notification of the incoming signal. + if (thread_sp) + reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (signo); - } break; } -} -void -NativeProcessLinux::SetGroupStopTids (lldb::tid_t signaled_thread_tid, int signo) -{ - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - - // Lock 1 - thread lock. - { - Mutex::Locker locker (m_threads_mutex); - // Lock 2 - group stop tids - { - Mutex::Locker locker (m_wait_for_group_stop_tids_mutex); - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " loading up known threads in set%s", - __FUNCTION__, - GetID (), - signaled_thread_tid, - m_wait_for_group_stop_tids.empty () ? " (currently empty)" - : "(group_stop_tids not empty?!?)"); - - // Add all known threads not already stopped into the wait for group-stop tids. - for (auto thread_sp : m_threads) - { - int unused_signo = LLDB_INVALID_SIGNAL_NUMBER; - if (thread_sp && !((NativeThreadLinux*)thread_sp.get())->IsStopped (&unused_signo)) - { - // Wait on this thread for a group stop before we notify the delegate about the process state change. - m_wait_for_group_stop_tids.insert (thread_sp->GetID ()); - } - } - - m_group_stop_signal_tid = signaled_thread_tid; - m_group_stop_signal = signo; - } - } -} - -void -NativeProcessLinux::OnGroupStop (lldb::tid_t tid) -{ - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - bool should_tell_delegate = false; - - // Lock 1 - thread lock. - { - Mutex::Locker locker (m_threads_mutex); - // Lock 2 - group stop tids - { - Mutex::Locker locker (m_wait_for_group_stop_tids_mutex); - - // Remove this thread from the set. - auto remove_result = m_wait_for_group_stop_tids.erase (tid); - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " tried to remove tid from group-stop set: %s", - __FUNCTION__, - GetID (), - tid, - remove_result > 0 ? "SUCCESS" : "FAILURE"); - - // Grab the thread metadata for this thread. - NativeThreadProtocolSP thread_sp = GetThreadByIDUnlocked (tid); - if (thread_sp) - { - NativeThreadLinux *const linux_thread = static_cast (thread_sp.get ()); - if (thread_sp->GetID () == m_group_stop_signal_tid) - { - linux_thread->SetStoppedBySignal (m_group_stop_signal); - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " set group stop tid to state 'stopped by signal %d'", - __FUNCTION__, - GetID (), - tid, - m_group_stop_signal); - } - else - { - int stopping_signal = LLDB_INVALID_SIGNAL_NUMBER; - if (linux_thread->IsStopped (&stopping_signal)) - { - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " thread is already stopped with signal %d, not clearing", - __FUNCTION__, - GetID (), - tid, - stopping_signal); - - } - else - { - linux_thread->SetStoppedBySignal (0); - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " set stopped by signal with signal 0 (i.e. debugger-initiated stop)", - __FUNCTION__, - GetID (), - tid); - - } - } - } - else - { - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " WARNING failed to find thread metadata for tid", - __FUNCTION__, - GetID (), - tid); - - } - - // If there are no more threads we're waiting on for group stop, signal the process. - if (m_wait_for_group_stop_tids.empty ()) - { - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " done waiting for group stop, will notify delegate of process state change", - __FUNCTION__, - GetID (), - tid); - - SetCurrentThreadID (m_group_stop_signal_tid); - - // Tell the delegate about the stop event, after we release our mutexes. - should_tell_delegate = true; - } - } - } - - // If we're ready to broadcast the process event change, do it now that we're no longer - // holding any locks. Note this does introduce a potential race, we should think about - // adding a notification queue. - if (should_tell_delegate) - { - if (log) - log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " done waiting for group stop, notifying delegate of process state change", - __FUNCTION__, - GetID (), - tid); - SetState (StateType::eStateStopped, true); - } + // Send a stop to the debugger after we get all other threads to stop. + CallAfterRunningThreadsStop (pid, + [=] (lldb::tid_t signaling_tid) + { + SetCurrentThreadID (signaling_tid); + SetState (StateType::eStateStopped, true); + }); } Error @@ -2697,119 +2566,110 @@ if (log) log->Printf ("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__, GetID ()); - int run_thread_count = 0; - int stop_thread_count = 0; - int step_thread_count = 0; - - std::vector new_stop_threads; + lldb::tid_t deferred_signal_tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t deferred_signal_skip_tid = LLDB_INVALID_THREAD_ID; + int deferred_signo = 0; + NativeThreadProtocolSP deferred_signal_thread_sp; + int resume_count = 0; + bool stepping = false; - Mutex::Locker locker (m_threads_mutex); - for (auto thread_sp : m_threads) - { - assert (thread_sp && "thread list should not contain NULL threads"); - NativeThreadLinux *const linux_thread_p = reinterpret_cast (thread_sp.get ()); - const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); - assert (action && "NULL ResumeAction returned for thread during Resume ()"); + // std::vector new_stop_threads; - if (log) + // Scope for threads mutex. + { + Mutex::Locker locker (m_threads_mutex); + for (auto thread_sp : m_threads) { - log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64, - __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); - } + assert (thread_sp && "thread list should not contain NULL threads"); - switch (action->state) - { - case eStateRunning: - // Run the thread, possibly feeding it the signal. - linux_thread_p->SetRunning (); - if (action->signal > 0) + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + + if (action == nullptr) { - // Resume the thread and deliver the given signal, - // then mark as delivered. - Resume (thread_sp->GetID (), action->signal); - resume_actions.SetSignalHandledForThread (thread_sp->GetID ()); + if (log) + log->Printf ("NativeProcessLinux::%s no action specified for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, GetID (), thread_sp->GetID ()); + continue; } - else + + if (log) { - // Just resume the thread with no signal. - Resume (thread_sp->GetID (), LLDB_INVALID_SIGNAL_NUMBER); + log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); } - ++run_thread_count; - break; - case eStateStepping: - // Note: if we have multiple threads, we may need to stop - // the other threads first, then step this one. - linux_thread_p->SetStepping (); - if (SingleStep (thread_sp->GetID (), 0)) + switch (action->state) { - if (log) - log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " single step succeeded", - __FUNCTION__, GetID (), thread_sp->GetID ()); - } - else + case eStateRunning: { - if (log) - log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " single step failed", - __FUNCTION__, GetID (), thread_sp->GetID ()); + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + m_coordinator_up->RequestThreadResumeAsNeeded (thread_sp->GetID (), + [=](lldb::tid_t tid_to_resume, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetRunning (); + // Pass this signal number on to the inferior to handle. + return Resume (tid_to_resume, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); + ++resume_count; + break; } - ++step_thread_count; - break; - case eStateSuspended: - case eStateStopped: - if (!StateIsStoppedState (linux_thread_p->GetState (), false)) - new_stop_threads.push_back (thread_sp); - else + case eStateStepping: { - if (log) - log->Printf ("NativeProcessLinux::%s no need to stop pid %" PRIu64 " tid %" PRIu64 ", thread state already %s", - __FUNCTION__, GetID (), thread_sp->GetID (), StateAsCString (linux_thread_p->GetState ())); + // Request the step. + const int signo = action->signal; + m_coordinator_up->RequestThreadResume (thread_sp->GetID (), + [=](lldb::tid_t tid_to_step, bool supress_signal) + { + reinterpret_cast (thread_sp.get ())->SetStepping (); + const auto step_result = SingleStep (tid_to_step,(signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER); + assert (step_result.Success() && "SingleStep() failed"); + return step_result; + }, + CoordinatorErrorHandler); + stepping = true; + break; } - ++stop_thread_count; - break; + case eStateSuspended: + case eStateStopped: + // if we haven't chosen a deferred signal tid yet, use this one. + if (deferred_signal_tid == LLDB_INVALID_THREAD_ID) + { + deferred_signal_tid = thread_sp->GetID (); + deferred_signal_thread_sp = thread_sp; + deferred_signo = SIGSTOP; + } + break; - default: - return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64, - __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + default: + return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + } } } - // If any thread was set to run, notify the process state as running. - if (run_thread_count > 0) - SetState (StateType::eStateRunning, true); - - // Now do a tgkill SIGSTOP on each thread we want to stop. - if (!new_stop_threads.empty ()) + // If we had any thread stopping, then do a deferred notification of the chosen stop thread id and signal + // after all other running threads have stopped. + // If there is a stepping thread involved we'll be eventually stopped by SIGTRAP trace signal. + if (deferred_signal_tid != LLDB_INVALID_THREAD_ID && !stepping) { - // Lock the m_wait_for_stop_tids set so we can fill it with every thread we expect to have stopped. - Mutex::Locker stop_thread_id_locker (m_wait_for_stop_tids_mutex); - for (auto thread_sp : new_stop_threads) - { - // Send a stop signal to the thread. - const int result = tgkill (GetID (), thread_sp->GetID (), SIGSTOP); - if (result != 0) - { - // tgkill failed. - if (log) - log->Printf ("NativeProcessLinux::%s error: tgkill SIGSTOP for pid %" PRIu64 " tid %" PRIu64 "failed, retval %d", - __FUNCTION__, GetID (), thread_sp->GetID (), result); - } - else - { - // tgkill succeeded. Don't mark the thread state, though. Let the signal - // handling mark it. - if (log) - log->Printf ("NativeProcessLinux::%s tgkill SIGSTOP for pid %" PRIu64 " tid %" PRIu64 " succeeded", - __FUNCTION__, GetID (), thread_sp->GetID ()); + CallAfterRunningThreadsStopWithSkipTID (deferred_signal_tid, + deferred_signal_skip_tid, + [=](lldb::tid_t deferred_notification_tid) + { + // Set the signal thread to the current thread. + SetCurrentThreadID (deferred_notification_tid); - // Add it to the set of threads we expect to signal a stop. - // We won't tell the delegate about it until this list drains to empty. - m_wait_for_stop_tids.insert (thread_sp->GetID ()); - } - } + // Set the thread state as stopped by the deferred signo. + reinterpret_cast (deferred_signal_thread_sp.get ())->SetStoppedBySignal (deferred_signo); + + // Tell the process delegate that the process is in a stopped state. + SetState (StateType::eStateStopped, true); + }); } return error; @@ -2820,11 +2680,6 @@ { Error error; - // FIXME check if we're already stopped - const bool is_stopped = false; - if (is_stopped) - return error; - if (kill (GetID (), SIGSTOP) != 0) error.SetErrorToErrno (); @@ -2864,6 +2719,79 @@ } Error +NativeProcessLinux::Interrupt () +{ + // Pick a running thread (or if none, a not-dead stopped thread) as + // the chosen thread that will be the stop-reason thread. + Error error; + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + NativeThreadProtocolSP running_thread_sp; + NativeThreadProtocolSP stopped_thread_sp; + { + Mutex::Locker locker (m_threads_mutex); + + if (log) + log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__); + + for (auto thread_sp : m_threads) + { + // The thread shouldn't be null but lets just cover that here. + if (!thread_sp) + continue; + + // If we have a running or stepping thread, we'll call that the + // target of the interrupt. + const auto thread_state = thread_sp->GetState (); + if (thread_state == eStateRunning || + thread_state == eStateStepping) + { + running_thread_sp = thread_sp; + break; + } + else if (!stopped_thread_sp && StateIsStoppedState (thread_state, true)) + { + // Remember the first non-dead stopped thread. We'll use that as a backup if there are no running threads. + stopped_thread_sp = thread_sp; + } + } + } + + if (!running_thread_sp && !stopped_thread_sp) + { + error.SetErrorString ("found no running/stepping or live stopped threads as target for interrupt"); + if (log) + { + log->Printf ("NativeProcessLinux::%s skipping due to error: %s", __FUNCTION__, error.AsCString ()); + } + return error; + } + + NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp; + + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64 " chosen for interrupt target", + __FUNCTION__, + GetID (), + running_thread_sp ? "running" : "stopped", + deferred_signal_thread_sp->GetID ()); + + CallAfterRunningThreadsStop (deferred_signal_thread_sp->GetID (), + [=](lldb::tid_t deferred_notification_tid) + { + // Set the signal thread to the current thread. + SetCurrentThreadID (deferred_notification_tid); + + // Set the thread state as stopped by the deferred signo. + reinterpret_cast (deferred_signal_thread_sp.get ())->SetStoppedBySignal (SIGSTOP); + + // Tell the process delegate that the process is in a stopped state. + SetState (StateType::eStateStopped, true); + }); + return error; +} + +Error NativeProcessLinux::Kill () { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); @@ -3480,133 +3408,120 @@ return op.GetError (); } -bool +Error NativeProcessLinux::ReadRegisterValue(lldb::tid_t tid, uint32_t offset, const char* reg_name, uint32_t size, RegisterValue &value) { - bool result; - ReadRegOperation op(tid, offset, reg_name, value, result); + ReadRegOperation op(tid, offset, reg_name, value); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, const RegisterValue &value) { - bool result; - WriteRegOperation op(tid, offset, reg_name, value, result); + WriteRegOperation op(tid, offset, reg_name, value); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { - bool result; - ReadGPROperation op(tid, buf, buf_size, result); + ReadGPROperation op(tid, buf, buf_size); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { - bool result; - ReadFPROperation op(tid, buf, buf_size, result); + ReadFPROperation op(tid, buf, buf_size); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { - bool result; - ReadRegisterSetOperation op(tid, buf, buf_size, regset, result); + ReadRegisterSetOperation op(tid, buf, buf_size, regset); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { - bool result; - WriteGPROperation op(tid, buf, buf_size, result); + WriteGPROperation op(tid, buf, buf_size); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { - bool result; - WriteFPROperation op(tid, buf, buf_size, result); + WriteFPROperation op(tid, buf, buf_size); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { - bool result; - WriteRegisterSetOperation op(tid, buf, buf_size, regset, result); + WriteRegisterSetOperation op(tid, buf, buf_size, regset); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) { - bool result; Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, GetUnixSignals().GetSignalAsCString (signo)); - ResumeOperation op (tid, signo, result); + ResumeOperation op (tid, signo); DoOperation (&op); if (log) - log->Printf ("NativeProcessLinux::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); - return result; + log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, op.GetError().Success() ? "true" : "false"); + return op.GetError(); } -bool +Error NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) { - bool result; - SingleStepOperation op(tid, signo, result); + SingleStepOperation op(tid, signo); DoOperation(&op); - return result; + return op.GetError(); } -bool -NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo, int &ptrace_err) +Error +NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { - bool result; - SiginfoOperation op(tid, siginfo, result, ptrace_err); + SiginfoOperation op(tid, siginfo); DoOperation(&op); - return result; + return op.GetError(); } -bool +Error NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) { - bool result; - EventMessageOperation op(tid, message, result); + EventMessageOperation op(tid, message); DoOperation(&op); - return result; + return op.GetError(); } lldb_private::Error NativeProcessLinux::Detach(lldb::tid_t tid) { - lldb_private::Error error; - if (tid != LLDB_INVALID_THREAD_ID) - { - DetachOperation op(tid, error); - DoOperation(&op); - } - return error; + if (tid == LLDB_INVALID_THREAD_ID) + return Error(); + + DetachOperation op(tid); + DoOperation(&op); + return op.GetError(); } bool @@ -3635,6 +3550,7 @@ { StopMonitoringChildProcess(); StopOpThread(); + StopCoordinatorThread (); sem_destroy(&m_operation_pending); sem_destroy(&m_operation_done); @@ -3655,6 +3571,69 @@ m_operation_thread.Join(nullptr); } +Error +NativeProcessLinux::StartCoordinatorThread () +{ + Error error; + static const char *g_thread_name = "lldb.process.linux.ts_coordinator"; + Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + // Skip if thread is already running + if (m_coordinator_thread.IsJoinable()) + { + error.SetErrorString ("ThreadStateCoordinator's run loop is already running"); + if (log) + log->Printf ("NativeProcessLinux::%s %s", __FUNCTION__, error.AsCString ()); + return error; + } + + // Enable verbose logging if lldb thread logging is enabled. + m_coordinator_up->LogEnableEventProcessing (log != nullptr); + + if (log) + log->Printf ("NativeProcessLinux::%s launching ThreadStateCoordinator thread for pid %" PRIu64, __FUNCTION__, GetID ()); + m_coordinator_thread = ThreadLauncher::LaunchThread(g_thread_name, CoordinatorThread, this, &error); + return error; +} + +void * +NativeProcessLinux::CoordinatorThread (void *arg) +{ + Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + NativeProcessLinux *const process = static_cast (arg); + assert (process && "null process passed to CoordinatorThread"); + if (!process) + { + if (log) + log->Printf ("NativeProcessLinux::%s null process, exiting ThreadStateCoordinator processing loop", __FUNCTION__); + return nullptr; + } + + // Run the thread state coordinator loop until it is done. This call uses + // efficient waiting for an event to be ready. + while (process->m_coordinator_up->ProcessNextEvent () == ThreadStateCoordinator::eventLoopResultContinue) + { + } + + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " exiting ThreadStateCoordinator processing loop due to coordinator indicating completion", __FUNCTION__, process->GetID ()); + + return nullptr; +} + +void +NativeProcessLinux::StopCoordinatorThread() +{ + Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeProcessLinux::%s requesting ThreadStateCoordinator stop for pid %" PRIu64, __FUNCTION__, GetID ()); + + // Tell the coordinator we're done. This will cause the coordinator + // run loop thread to exit when the processing queue hits this message. + m_coordinator_up->StopCoordinator (); +} + bool NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id) { @@ -3869,3 +3848,79 @@ return error; } + +void +NativeProcessLinux::NotifyThreadCreateStopped (lldb::tid_t tid) +{ + const bool is_stopped = true; + m_coordinator_up->NotifyThreadCreate (tid, is_stopped, CoordinatorErrorHandler); +} + +void +NativeProcessLinux::NotifyThreadDeath (lldb::tid_t tid) +{ + m_coordinator_up->NotifyThreadDeath (tid, CoordinatorErrorHandler); +} + +void +NativeProcessLinux::NotifyThreadStop (lldb::tid_t tid) +{ + m_coordinator_up->NotifyThreadStop (tid, false, CoordinatorErrorHandler); +} + +void +NativeProcessLinux::CallAfterRunningThreadsStop (lldb::tid_t tid, + const std::function &call_after_function) +{ + Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("NativeProcessLinux::%s tid %" PRIu64, __FUNCTION__, tid); + + const lldb::pid_t pid = GetID (); + m_coordinator_up->CallAfterRunningThreadsStop (tid, + [=](lldb::tid_t request_stop_tid) + { + return RequestThreadStop(pid, request_stop_tid); + }, + call_after_function, + CoordinatorErrorHandler); +} + +void +NativeProcessLinux::CallAfterRunningThreadsStopWithSkipTID (lldb::tid_t deferred_signal_tid, + lldb::tid_t skip_stop_request_tid, + const std::function &call_after_function) +{ + Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("NativeProcessLinux::%s deferred_signal_tid %" PRIu64 ", skip_stop_request_tid %" PRIu64, __FUNCTION__, deferred_signal_tid, skip_stop_request_tid); + + const lldb::pid_t pid = GetID (); + m_coordinator_up->CallAfterRunningThreadsStopWithSkipTIDs (deferred_signal_tid, + skip_stop_request_tid != LLDB_INVALID_THREAD_ID ? ThreadStateCoordinator::ThreadIDSet {skip_stop_request_tid} : ThreadStateCoordinator::ThreadIDSet (), + [=](lldb::tid_t request_stop_tid) + { + return RequestThreadStop(pid, request_stop_tid); + }, + call_after_function, + CoordinatorErrorHandler); +} + +lldb_private::Error +NativeProcessLinux::RequestThreadStop (const lldb::pid_t pid, const lldb::tid_t tid) +{ + Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeProcessLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid); + + Error err; + errno = 0; + if (::tgkill (pid, tid, SIGSTOP) != 0) + { + err.SetErrorToErrno (); + if (log) + log->Printf ("NativeProcessLinux::%s tgkill(%" PRIu64 ", %" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, tid, err.AsCString ()); + } + + return err; +} Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -11,7 +11,7 @@ #ifndef lldb_NativeRegisterContextLinux_x86_64_h #define lldb_NativeRegisterContextLinux_x86_64_h -#include "lldb/Target/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h" #include "Plugins/Process/Utility/RegisterContext_x86.h" #include "Plugins/Process/Utility/lldb-x86-register-enums.h" @@ -42,6 +42,32 @@ Error WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + Error + IsWatchpointHit(uint8_t wp_index); + + Error + IsWatchpointVacant(uint32_t wp_index); + + bool + ClearHardwareWatchpoint(uint32_t wp_index); + + Error + ClearAllHardwareWatchpoints (); + + Error + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags); + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index); + + uint32_t + NumSupportedHardwareWatchpoints(); + private: // Private member types. Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -13,8 +13,8 @@ #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" #include "lldb/Core/RegisterValue.h" -#include "Host/common/NativeProcessProtocol.h" -#include "Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" using namespace lldb_private; @@ -447,14 +447,11 @@ } NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); - if (!process_p->ReadRegisterValue(m_thread.GetID(), - reg_info->byte_offset, - reg_info->name, - reg_info->byte_size, - reg_value)) - error.SetErrorString ("NativeProcessLinux::ReadRegisterValue() failed"); - - return error; + return process_p->ReadRegisterValue(m_thread.GetID(), + reg_info->byte_offset, + reg_info->name, + reg_info->byte_size, + reg_value); } lldb_private::Error @@ -634,13 +631,10 @@ } NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); - if (!process_p->WriteRegisterValue(m_thread.GetID(), - register_to_write_info_p->byte_offset, - register_to_write_info_p->name, - value_to_write)) - error.SetErrorString ("NativeProcessLinux::WriteRegisterValue() failed"); - - return error; + return process_p->WriteRegisterValue(m_thread.GetID(), + register_to_write_info_p->byte_offset, + register_to_write_info_p->name, + value_to_write); } lldb_private::Error @@ -920,10 +914,10 @@ NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); if (GetFPRType() == eFPRTypeFXSAVE) - return process_p->WriteFPR (m_thread.GetID (), &m_fpr.xstate.fxsave, sizeof (m_fpr.xstate.fxsave)); + return process_p->WriteFPR (m_thread.GetID (), &m_fpr.xstate.fxsave, sizeof (m_fpr.xstate.fxsave)).Success(); if (GetFPRType() == eFPRTypeXSAVE) - return process_p->WriteRegisterSet (m_thread.GetID (), &m_iovec, sizeof (m_fpr.xstate.xsave), NT_X86_XSTATE); + return process_p->WriteRegisterSet (m_thread.GetID (), &m_iovec, sizeof (m_fpr.xstate.xsave), NT_X86_XSTATE).Success(); return false; } @@ -1006,10 +1000,10 @@ switch (fpr_type) { case FPRType::eFPRTypeFXSAVE: - return process_p->ReadFPR (m_thread.GetID (), &m_fpr.xstate.fxsave, sizeof (m_fpr.xstate.fxsave)); + return process_p->ReadFPR (m_thread.GetID (), &m_fpr.xstate.fxsave, sizeof (m_fpr.xstate.fxsave)).Success(); case FPRType::eFPRTypeXSAVE: - return process_p->ReadRegisterSet (m_thread.GetID (), &m_iovec, sizeof (m_fpr.xstate.xsave), NT_X86_XSTATE); + return process_p->ReadRegisterSet (m_thread.GetID (), &m_iovec, sizeof (m_fpr.xstate.xsave), NT_X86_XSTATE).Success(); default: return false; @@ -1024,7 +1018,7 @@ return false; NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); - return process_p->ReadGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()); + return process_p->ReadGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()).Success(); } bool @@ -1035,6 +1029,173 @@ return false; NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); - return process_p->WriteGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()); + return process_p->WriteGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()).Success(); +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint8_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return error; + + uint64_t status_bits = reg_value.GetAsUInt64(); + + bool is_hit = status_bits & (1 << wp_index); + + error.SetError (!is_hit, lldb::eErrorTypeInvalid); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return error; + + uint64_t control_bits = reg_value.GetAsUInt64(); + + bool is_vacant = !(control_bits & (1 << (2 * wp_index))); + + error.SetError (!is_vacant, lldb::eErrorTypeInvalid); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Error ("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Error ("Invalid size for watchpoint"); + + Error error = IsWatchpointVacant (wp_index); + if (error.Fail()) return error; + + RegisterValue reg_value; + error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return error; + + // for watchpoints 0, 1, 2, or 3, respectively, + // set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + error = WriteRegister(m_reg_info.first_dr + wp_index, RegisterValue(addr)); + if (error.Fail()) return error; + + error = WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)); + if (error.Fail()) return error; + + error.Clear(); + return error; +} + +bool +NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue reg_value; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits 0, 1, 2, or 3 of the debug status register (DR6) + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return false; + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(lldb_dr6_x86_64, RegisterValue(status_bits)); + if (error.Fail()) return false; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} + // of the debug control register (DR7) + error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return false; + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)).Success(); } +Error +NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints() +{ + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(lldb_dr6_x86_64, RegisterValue(status_bits)); + if (error.Fail()) return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)); +} + +uint32_t +NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + if (IsWatchpointVacant(wp_index).Success()) + { + if (SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index).Fail()) + continue; + return wp_index; + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t +NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints () +{ + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} Index: source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.h +++ source/Plugins/Process/Linux/NativeThreadLinux.h @@ -11,7 +11,9 @@ #define liblldb_NativeThreadLinux_H_ #include "lldb/lldb-private-forward.h" -#include "../../../Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include namespace lldb_private { @@ -34,7 +36,7 @@ GetState () override; bool - GetStopReason (ThreadStopInfo &stop_info) override; + GetStopReason (ThreadStopInfo &stop_info, std::string& description) override; NativeRegisterContextSP GetRegisterContext () override; @@ -45,9 +47,6 @@ Error RemoveWatchpoint (lldb::addr_t addr) override; - uint32_t - TranslateStopInfoToGdbSignal (const ThreadStopInfo &stop_info) const override; - private: // --------------------------------------------------------------------- // Interface for friend classes @@ -76,11 +75,20 @@ void SetStoppedByBreakpoint (); + void + SetStoppedByWatchpoint (); + bool IsStoppedAtBreakpoint (); + bool + IsStoppedAtWatchpoint (); + + void + SetStoppedByTrace (); + void - SetCrashedWithException (uint64_t exception_type, lldb::addr_t exception_addr); + SetCrashedWithException (const siginfo_t& info); void SetSuspended (); @@ -100,6 +108,9 @@ lldb::StateType m_state; ThreadStopInfo m_stop_info; NativeRegisterContextSP m_reg_context_sp; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; }; } Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -10,6 +10,7 @@ #include "NativeThreadLinux.h" #include +#include #include "NativeProcessLinux.h" #include "NativeRegisterContextLinux_x86_64.h" @@ -24,6 +25,8 @@ #include "llvm/ADT/SmallString.h" +#include "Plugins/Process/POSIX/CrashReason.h" + #include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" #include "Plugins/Process/Utility/RegisterContextLinux_i386.h" #include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" @@ -39,10 +42,10 @@ switch (stop_info.reason) { case eStopReasonSignal: - log.Printf ("%s: %s signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + log.Printf ("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); return; case eStopReasonException: - log.Printf ("%s: %s exception type 0x%" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); + log.Printf ("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); return; case eStopReasonExec: log.Printf ("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); @@ -57,7 +60,8 @@ NativeThreadProtocol (process, tid), m_state (StateType::eStateInvalid), m_stop_info (), - m_reg_context_sp () + m_reg_context_sp (), + m_stop_description () { } @@ -82,9 +86,12 @@ bool -NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info) +NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& description) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + description.clear(); + switch (m_state) { case eStateStopped: @@ -95,8 +102,18 @@ if (log) LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:"); stop_info = m_stop_info; + switch (m_stop_info.reason) + { + case StopReason::eStopReasonException: + case StopReason::eStopReasonBreakpoint: + case StopReason::eStopReasonWatchpoint: + description = m_stop_description; + default: + break; + } if (log) LogThreadStopInfo (*log, stop_info, "returned stop_info:"); + return true; case eStateInvalid: @@ -200,15 +217,30 @@ Error NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { - // TODO implement - return Error ("not implemented"); + if (!hardware) + return Error ("not implemented"); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext (); + uint32_t wp_index = + reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error ("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error (); } Error NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) { - // TODO implement - return Error ("not implemented"); + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error (); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error (); + return Error ("Clearing hardware watchpoint failed."); } void @@ -233,6 +265,21 @@ m_state = new_state; m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, + // then this is a new thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) + { + const auto &watchpoint_map = GetProcess()->GetWatchpointMap(); + if (watchpoint_map.empty()) return; + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) + { + const auto& wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } } void @@ -250,7 +297,7 @@ { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); if (log) - log->Printf ("NativeThreadLinux::%s called with signal 0x%" PRIx32, __FUNCTION__, signo); + log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); const StateType new_state = StateType::eStateStopped; MaybeLogStateChange (new_state); @@ -301,35 +348,84 @@ MaybeLogStateChange (new_state); m_state = new_state; - m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void +NativeThreadLinux::SetStoppedByWatchpoint () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; + + NativeRegisterContextLinux_x86_64 *reg_ctx = + reinterpret_cast (GetRegisterContext().get()); + const uint32_t num_hw_watchpoints = + reg_ctx->NumSupportedHardwareWatchpoints(); + + m_stop_description.clear (); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + if (reg_ctx->IsWatchpointHit(wp_index).Success()) + { + std::ostringstream ostr; + ostr << reg_ctx->GetWatchpointAddress(wp_index) << " " << wp_index; + m_stop_description = ostr.str(); + return; + } + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + { + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID; + log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") " + "stopped by a watchpoint, but failed to find it", + pid, GetID ()); + } } bool NativeThreadLinux::IsStoppedAtBreakpoint () { - // Are we stopped? If not, this can't be a breakpoint. - if (GetState () != StateType::eStateStopped) - return false; + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool +NativeThreadLinux::IsStoppedAtWatchpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void +NativeThreadLinux::SetStoppedByTrace () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; - // Was the stop reason a signal with signal number SIGTRAP? If not, not a breakpoint. - return (m_stop_info.reason == StopReason::eStopReasonSignal) && - (m_stop_info.details.signal.signo == SIGTRAP); + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; } void -NativeThreadLinux::SetCrashedWithException (uint64_t exception_type, lldb::addr_t exception_addr) +NativeThreadLinux::SetCrashedWithException (const siginfo_t& info) { const StateType new_state = StateType::eStateCrashed; MaybeLogStateChange (new_state); m_state = new_state; m_stop_info.reason = StopReason::eStopReasonException; - m_stop_info.details.exception.type = exception_type; - m_stop_info.details.exception.data_count = 1; - m_stop_info.details.exception.data[0] = exception_addr; -} + m_stop_info.details.signal.signo = info.si_signo; + const auto reason = GetCrashReason (info); + m_stop_description = GetCrashReasonString (reason, reinterpret_cast (info.si_addr)); +} void NativeThreadLinux::SetSuspended () @@ -371,33 +467,3 @@ // Log it. log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); } - -uint32_t -NativeThreadLinux::TranslateStopInfoToGdbSignal (const ThreadStopInfo &stop_info) const -{ - switch (stop_info.reason) - { - case eStopReasonSignal: - // No translation. - return stop_info.details.signal.signo; - - case eStopReasonException: - { - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - // FIXME I think the eStopReasonException is a xnu/Mach exception, which we - // shouldn't see on Linux. - // No translation. - if (log) - log->Printf ("NativeThreadLinux::%s saw an exception stop type (signo %" - PRIu64 "), not expecting to see exceptions on Linux", - __FUNCTION__, - stop_info.details.exception.type); - return static_cast (stop_info.details.exception.type); - } - - default: - assert (0 && "unexpected stop_info.reason found"); - return 0; - } -} - Index: source/Plugins/Process/Linux/ProcessMonitor.h =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.h +++ source/Plugins/Process/Linux/ProcessMonitor.h @@ -299,18 +299,6 @@ MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::pid_t pid); - static ProcessMessage::CrashReason - GetCrashReasonForSIGSEGV(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGILL(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGFPE(const siginfo_t *info); - - static ProcessMessage::CrashReason - GetCrashReasonForSIGBUS(const siginfo_t *info); - void DoOperation(Operation *op); Index: source/Plugins/Process/Linux/ProcessMonitor.cpp =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.cpp +++ source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -46,6 +46,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Utility/PseudoTerminal.h" +#include "Plugins/Process/POSIX/CrashReason.h" #include "Plugins/Process/POSIX/POSIXThread.h" #include "ProcessLinux.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" @@ -1836,27 +1837,14 @@ if (log) log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo)); - if (signo == SIGSEGV) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); - } - - if (signo == SIGILL) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); - } - - if (signo == SIGFPE) { - lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); - return ProcessMessage::Crash(pid, reason, signo, fault_addr); - } - - if (signo == SIGBUS) { + switch (signo) + { + case SIGSEGV: + case SIGILL: + case SIGFPE: + case SIGBUS: lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); - ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); + const auto reason = GetCrashReason(*info); return ProcessMessage::Crash(pid, reason, signo, fault_addr); } @@ -2114,147 +2102,6 @@ return false; } -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGSEGV); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGSEGV"); - break; - case SI_KERNEL: - // Linux will occasionally send spurious SI_KERNEL codes. - // (this is poorly documented in sigaction) - // One way to get this is via unaligned SIMD loads. - reason = ProcessMessage::eInvalidAddress; // for lack of anything better - break; - case SEGV_MAPERR: - reason = ProcessMessage::eInvalidAddress; - break; - case SEGV_ACCERR: - reason = ProcessMessage::ePrivilegedAddress; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGILL); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGILL"); - break; - case ILL_ILLOPC: - reason = ProcessMessage::eIllegalOpcode; - break; - case ILL_ILLOPN: - reason = ProcessMessage::eIllegalOperand; - break; - case ILL_ILLADR: - reason = ProcessMessage::eIllegalAddressingMode; - break; - case ILL_ILLTRP: - reason = ProcessMessage::eIllegalTrap; - break; - case ILL_PRVOPC: - reason = ProcessMessage::ePrivilegedOpcode; - break; - case ILL_PRVREG: - reason = ProcessMessage::ePrivilegedRegister; - break; - case ILL_COPROC: - reason = ProcessMessage::eCoprocessorError; - break; - case ILL_BADSTK: - reason = ProcessMessage::eInternalStackError; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGFPE); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGFPE"); - break; - case FPE_INTDIV: - reason = ProcessMessage::eIntegerDivideByZero; - break; - case FPE_INTOVF: - reason = ProcessMessage::eIntegerOverflow; - break; - case FPE_FLTDIV: - reason = ProcessMessage::eFloatDivideByZero; - break; - case FPE_FLTOVF: - reason = ProcessMessage::eFloatOverflow; - break; - case FPE_FLTUND: - reason = ProcessMessage::eFloatUnderflow; - break; - case FPE_FLTRES: - reason = ProcessMessage::eFloatInexactResult; - break; - case FPE_FLTINV: - reason = ProcessMessage::eFloatInvalidOperation; - break; - case FPE_FLTSUB: - reason = ProcessMessage::eFloatSubscriptRange; - break; - } - - return reason; -} - -ProcessMessage::CrashReason -ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info) -{ - ProcessMessage::CrashReason reason; - assert(info->si_signo == SIGBUS); - - reason = ProcessMessage::eInvalidCrashReason; - - switch (info->si_code) - { - default: - assert(false && "unexpected si_code for SIGBUS"); - break; - case BUS_ADRALN: - reason = ProcessMessage::eIllegalAlignment; - break; - case BUS_ADRERR: - reason = ProcessMessage::eIllegalAddress; - break; - case BUS_OBJERR: - reason = ProcessMessage::eHardwareError; - break; - } - - return reason; -} - void ProcessMonitor::ServeOperation(OperationArgs *args) { Index: source/Plugins/Process/Linux/ThreadStateCoordinator.h =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -19,6 +19,8 @@ #include "lldb/lldb-types.h" +#include "lldb/Core/Error.h" + namespace lldb_private { class ThreadStateCoordinator @@ -38,6 +40,8 @@ typedef std::function ThreadIDFunction; typedef std::function LogFunction; typedef std::function ErrorFunction; + typedef std::function StopThreadFunction; + typedef std::function ResumeThreadFunction; // Constructors. ThreadStateCoordinator (const LogFunction &log_function); @@ -67,7 +71,7 @@ void CallAfterThreadsStop (lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function); @@ -77,24 +81,48 @@ // be fired if the triggering tid is unknown at the time of execution. void CallAfterRunningThreadsStop (lldb::tid_t triggering_tid, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function); + // This method is the main purpose of the class: triggering a deferred + // action after all non-stopped threads stop. The triggering_tid is the + // thread id passed to the call_after_function. The error_function will + // be fired if the triggering tid is unknown at the time of execution. + // This variant will send stop requests to all non-stopped threads except + // for any contained in skip_stop_request_tids. + void + CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid, + const ThreadIDSet &skip_stop_request_tids, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function); + // Notify the thread stopped. Will trigger error at time of execution if we // already think it is stopped. void NotifyThreadStop (lldb::tid_t tid, + bool initiated_by_llgs, const ErrorFunction &error_function); // Request that the given thread id should have the request_thread_resume_function // called. Will trigger the error_function if the thread is thought to be running - // already at that point. + // already at that point. This call signals an error if the thread resume is for + // a thread that is already in a running state. void RequestThreadResume (lldb::tid_t tid, - const ThreadIDFunction &request_thread_resume_function, + const ResumeThreadFunction &request_thread_resume_function, const ErrorFunction &error_function); + // Request that the given thread id should have the request_thread_resume_function + // called. Will trigger the error_function if the thread is thought to be running + // already at that point. This call ignores threads that are already running and + // does not trigger an error in that case. + void + RequestThreadResumeAsNeeded (lldb::tid_t tid, + const ResumeThreadFunction &request_thread_resume_function, + const ErrorFunction &error_function); + // Indicate the calling process did an exec and that the thread state // should be 100% cleared. // @@ -115,6 +143,10 @@ EventLoopResult ProcessNextEvent (); + // Enable/disable verbose logging of event processing. + void + LogEnableEventProcessing (bool enabled); + private: // Typedefs. @@ -133,7 +165,19 @@ typedef std::queue QueueType; - typedef std::unordered_map TIDBoolMap; + enum class ThreadState + { + Running, + Stopped + }; + + struct ThreadContext + { + ThreadState m_state; + bool m_stop_requested = false; + ResumeThreadFunction m_request_resume_function; + }; + typedef std::unordered_map TIDContextMap; // Private member functions. @@ -147,7 +191,7 @@ SetPendingNotification (const EventBaseSP &event_sp); void - ThreadDidStop (lldb::tid_t tid, ErrorFunction &error_function); + ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, ErrorFunction &error_function); void ThreadWasCreated (lldb::tid_t tid, bool is_stopped, ErrorFunction &error_function); @@ -158,6 +202,9 @@ void ResetNow (); + bool + IsKnownThread(lldb::tid_t tid) const; + void Log (const char *format, ...); @@ -177,8 +224,10 @@ EventBaseSP m_pending_notification_sp; - // Maps known TIDs to stop (true) or not-stopped (false) state. - TIDBoolMap m_tid_stop_map; + // Maps known TIDs to ThreadContext. + TIDContextMap m_tid_map; + + bool m_log_event_processing; }; } Index: source/Plugins/Process/Linux/ThreadStateCoordinator.cpp =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -35,6 +35,9 @@ { } + virtual std::string + GetDescription () = 0; + // Return false if the coordinator should terminate running. virtual EventLoopResult ProcessEvent (ThreadStateCoordinator &coordinator) = 0; @@ -55,6 +58,12 @@ { return eventLoopResultStop; } + + std::string + GetDescription () override + { + return "EventStopCoordinator"; + } }; //===----------------------------------------------------------------------===// @@ -64,7 +73,7 @@ public: EventCallAfterThreadsStop (lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function): EventBase (), @@ -74,12 +83,13 @@ m_request_thread_stop_function (request_thread_stop_function), m_call_after_function (call_after_function), m_error_function (error_function), - m_request_stop_on_all_unstopped_threads (false) + m_request_stop_on_all_unstopped_threads (false), + m_skip_stop_request_tids () { } EventCallAfterThreadsStop (lldb::tid_t triggering_tid, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function) : EventBase (), @@ -89,7 +99,25 @@ m_request_thread_stop_function (request_thread_stop_function), m_call_after_function (call_after_function), m_error_function (error_function), - m_request_stop_on_all_unstopped_threads (true) + m_request_stop_on_all_unstopped_threads (true), + m_skip_stop_request_tids () + { + } + + EventCallAfterThreadsStop (lldb::tid_t triggering_tid, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ThreadIDSet &skip_stop_request_tids, + const ErrorFunction &error_function) : + EventBase (), + m_triggering_tid (triggering_tid), + m_wait_for_stop_tids (), + m_original_wait_for_stop_tids (), + m_request_thread_stop_function (request_thread_stop_function), + m_call_after_function (call_after_function), + m_error_function (error_function), + m_request_stop_on_all_unstopped_threads (true), + m_skip_stop_request_tids (skip_stop_request_tids) { } @@ -121,7 +149,7 @@ ProcessEvent(ThreadStateCoordinator &coordinator) override { // Validate we know about the deferred trigger thread. - if (coordinator.m_tid_stop_map.find (m_triggering_tid) == coordinator.m_tid_stop_map.end ()) + if (!coordinator.IsKnownThread (m_triggering_tid)) { // We don't know about this thread. This is an error condition. std::ostringstream error_message; @@ -144,7 +172,7 @@ if (m_wait_for_stop_tids.empty ()) { - // We're not waiting for any threads. Fire off the deferred signal delivery event. + // We're not waiting for any threads. Fire off the deferred signal delivery event. NotifyNow (); } else @@ -190,6 +218,14 @@ m_request_thread_stop_function (tid); } + std::string + GetDescription () override + { + std::ostringstream description; + description << "EventCallAfterThreadsStop (triggering_tid=" << m_triggering_tid << ", request_stop_on_all_unstopped_threads=" << m_request_stop_on_all_unstopped_threads << ", skip_stop_request_tids.size()=" << m_skip_stop_request_tids.size () << ")"; + return description.str (); + } + private: void @@ -199,7 +235,7 @@ } bool - RequestStopOnAllSpecifiedThreads (const ThreadStateCoordinator &coordinator) + RequestStopOnAllSpecifiedThreads (ThreadStateCoordinator &coordinator) { // Request a stop for all the thread stops that need to be stopped // and are not already known to be stopped. Keep a list of all the @@ -210,8 +246,8 @@ { // Validate we know about all tids for which we must first receive a stop before // triggering the deferred stop notification. - auto find_it = coordinator.m_tid_stop_map.find (tid); - if (find_it == coordinator.m_tid_stop_map.end ()) + auto find_it = coordinator.m_tid_map.find (tid); + if (find_it == coordinator.m_tid_map.end ()) { // This is an error. We shouldn't be asking for waiting pids that aren't known. // NOTE: we may be stripping out the specification of wait tids and handle this @@ -225,9 +261,10 @@ } // If the pending stop thread is currently running, we need to send it a stop request. - if (!find_it->second) + auto& context = find_it->second; + if (context.m_state == ThreadState::Running) { - m_request_thread_stop_function (tid); + RequestThreadStop (tid, coordinator, context); sent_tids.insert (tid); } } @@ -242,22 +279,28 @@ } void - RequestStopOnAllRunningThreads (const ThreadStateCoordinator &coordinator) + RequestStopOnAllRunningThreads (ThreadStateCoordinator &coordinator) { // Request a stop for all the thread stops that need to be stopped // and are not already known to be stopped. Keep a list of all the // threads from which we still need to hear a stop reply. ThreadIDSet sent_tids; - for (auto it = coordinator.m_tid_stop_map.begin(); it != coordinator.m_tid_stop_map.end(); ++it) + for (auto it = coordinator.m_tid_map.begin(); it != coordinator.m_tid_map.end(); ++it) { // We only care about threads not stopped. - const bool running = !it->second; + const bool running = it->second.m_state == ThreadState::Running; if (running) { - // Request this thread stop. const lldb::tid_t tid = it->first; - m_request_thread_stop_function (tid); + + // Request this thread stop if the tid stop request is not explicitly ignored. + const bool skip_stop_request = m_skip_stop_request_tids.count (tid) > 0; + if (!skip_stop_request) + RequestThreadStop (tid, coordinator, it->second); + + // Even if we skipped sending the stop request for other reasons (like stepping), + // we still need to wait for that stepping thread to notify completion/stop. sent_tids.insert (tid); } } @@ -266,13 +309,29 @@ m_wait_for_stop_tids.swap (sent_tids); } + void + RequestThreadStop (lldb::tid_t tid, ThreadStateCoordinator &coordinator, ThreadContext& context) + { + const auto error = m_request_thread_stop_function (tid); + if (error.Success ()) + { + context.m_stop_requested = true; + } + else + { + coordinator.Log ("EventCallAfterThreadsStop::%s failed to request thread stop tid %" PRIu64 ": %s", + __FUNCTION__, tid, error.AsCString ()); + } + } + const lldb::tid_t m_triggering_tid; ThreadIDSet m_wait_for_stop_tids; const ThreadIDSet m_original_wait_for_stop_tids; - ThreadIDFunction m_request_thread_stop_function; + StopThreadFunction m_request_thread_stop_function; ThreadIDFunction m_call_after_function; ErrorFunction m_error_function; const bool m_request_stop_on_all_unstopped_threads; + ThreadIDSet m_skip_stop_request_tids; }; //===----------------------------------------------------------------------===// @@ -291,6 +350,12 @@ coordinator.ResetNow (); return eventLoopResultContinue; } + + std::string + GetDescription () override + { + return "EventReset"; + } }; //===----------------------------------------------------------------------===// @@ -299,9 +364,11 @@ { public: EventThreadStopped (lldb::tid_t tid, + bool initiated_by_llgs, const ErrorFunction &error_function): EventBase (), m_tid (tid), + m_initiated_by_llgs (initiated_by_llgs), m_error_function (error_function) { } @@ -309,13 +376,22 @@ EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { - coordinator.ThreadDidStop (m_tid, m_error_function); + coordinator.ThreadDidStop (m_tid, m_initiated_by_llgs, m_error_function); return eventLoopResultContinue; } + std::string + GetDescription () override + { + std::ostringstream description; + description << "EventThreadStopped (tid=" << m_tid << ")"; + return description.str (); + } + private: const lldb::tid_t m_tid; + const bool m_initiated_by_llgs; ErrorFunction m_error_function; }; @@ -341,6 +417,14 @@ return eventLoopResultContinue; } + std::string + GetDescription () override + { + std::ostringstream description; + description << "EventThreadCreate (tid=" << m_tid << ", " << (m_is_stopped ? "stopped" : "running") << ")"; + return description.str (); + } + private: const lldb::tid_t m_tid; @@ -368,6 +452,14 @@ return eventLoopResultContinue; } + std::string + GetDescription () override + { + std::ostringstream description; + description << "EventThreadDeath (tid=" << m_tid << ")"; + return description.str (); + } + private: const lldb::tid_t m_tid; @@ -380,12 +472,14 @@ { public: EventRequestResume (lldb::tid_t tid, - const ThreadIDFunction &request_thread_resume_function, - const ErrorFunction &error_function): + const ResumeThreadFunction &request_thread_resume_function, + const ErrorFunction &error_function, + bool error_when_already_running): EventBase (), m_tid (tid), m_request_thread_resume_function (request_thread_resume_function), - m_error_function (error_function) + m_error_function (error_function), + m_error_when_already_running (error_when_already_running) { } @@ -393,8 +487,8 @@ ProcessEvent(ThreadStateCoordinator &coordinator) override { // Ensure we know about the thread. - auto find_it = coordinator.m_tid_stop_map.find (m_tid); - if (find_it == coordinator.m_tid_stop_map.end ()) + auto find_it = coordinator.m_tid_map.find (m_tid); + if (find_it == coordinator.m_tid_map.end ()) { // We don't know about this thread. This is an error condition. std::ostringstream error_message; @@ -402,15 +496,30 @@ m_error_function (error_message.str ()); return eventLoopResultContinue; } - + auto& context = find_it->second; // Tell the thread to resume if we don't already think it is running. - const bool is_stopped = find_it->second; + const bool is_stopped = context.m_state == ThreadState::Stopped; if (!is_stopped) { - // Skip the resume call - we have tracked it to be running. - std::ostringstream error_message; - error_message << "error: tid " << m_tid << " asked to resume but we think it is already running"; - m_error_function (error_message.str ()); + // It's not an error, just a log, if the m_already_running_no_error flag is set. + // This covers cases where, for instance, we're just trying to resume all threads + // from the user side. + if (!m_error_when_already_running) + { + coordinator.Log ("EventRequestResume::%s tid %" PRIu64 " optional resume skipped since it is already running", + __FUNCTION__, + m_tid); + } + else + { + // Skip the resume call - we have tracked it to be running. And we unconditionally + // expected to resume this thread. Flag this as an error. + std::ostringstream error_message; + error_message << "error: tid " << m_tid << " asked to resume but we think it is already running"; + m_error_function (error_message.str ()); + } + + // Error or not, we're done. return eventLoopResultContinue; } @@ -442,19 +551,36 @@ // Request a resume. We expect this to be synchronous and the system // to reflect it is running after this completes. - m_request_thread_resume_function (m_tid); - - // Now mark it is running. - find_it->second = false; + const auto error = m_request_thread_resume_function (m_tid, false); + if (error.Success ()) + { + // Now mark it is running. + context.m_state = ThreadState::Running; + context.m_request_resume_function = m_request_thread_resume_function; + } + else + { + coordinator.Log ("EventRequestResume::%s failed to resume thread tid %" PRIu64 ": %s", + __FUNCTION__, m_tid, error.AsCString ()); + } return eventLoopResultContinue; } + std::string + GetDescription () override + { + std::ostringstream description; + description << "EventRequestResume (tid=" << m_tid << ")"; + return description.str (); + } + private: const lldb::tid_t m_tid; - ThreadIDFunction m_request_thread_resume_function; + ResumeThreadFunction m_request_thread_resume_function; ErrorFunction m_error_function; + const bool m_error_when_already_running; }; //===----------------------------------------------------------------------===// @@ -464,7 +590,8 @@ m_event_queue (), m_queue_condition (), m_queue_mutex (), - m_tid_stop_map () + m_tid_map (), + m_log_event_processing (false) { } @@ -472,7 +599,11 @@ ThreadStateCoordinator::EnqueueEvent (EventBaseSP event_sp) { std::lock_guard lock (m_queue_mutex); + m_event_queue.push (event_sp); + if (m_log_event_processing) + Log ("ThreadStateCoordinator::%s enqueued event: %s", __FUNCTION__, event_sp->GetDescription ().c_str ()); + m_queue_condition.notify_one (); } @@ -517,7 +648,7 @@ void ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function) { @@ -530,7 +661,7 @@ void ThreadStateCoordinator::CallAfterRunningThreadsStop (const lldb::tid_t triggering_tid, - const ThreadIDFunction &request_thread_stop_function, + const StopThreadFunction &request_thread_stop_function, const ThreadIDFunction &call_after_function, const ErrorFunction &error_function) { @@ -541,11 +672,26 @@ } void -ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, ErrorFunction &error_function) +ThreadStateCoordinator::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid, + const ThreadIDSet &skip_stop_request_tids, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) +{ + EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid, + request_thread_stop_function, + call_after_function, + skip_stop_request_tids, + error_function))); +} + + +void +ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, ErrorFunction &error_function) { // Ensure we know about the thread. - auto find_it = m_tid_stop_map.find (tid); - if (find_it == m_tid_stop_map.end ()) + auto find_it = m_tid_map.find (tid); + if (find_it == m_tid_map.end ()) { // We don't know about this thread. This is an error condition. std::ostringstream error_message; @@ -555,7 +701,10 @@ } // Update the global list of known thread states. This one is definitely stopped. - find_it->second = true; + auto& context = find_it->second; + const auto stop_was_requested = context.m_stop_requested; + context.m_state = ThreadState::Stopped; + context.m_stop_requested = false; // If we have a pending notification, remove this from the set. EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); @@ -568,14 +717,31 @@ m_pending_notification_sp.reset (); } } + + if (initiated_by_llgs && context.m_request_resume_function && !stop_was_requested) + { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Log ("Resuming thread %" PRIu64 " since stop wasn't requested", tid); + const auto error = context.m_request_resume_function (tid, true); + if (error.Success ()) + { + context.m_state = ThreadState::Running; + } + else + { + Log ("ThreadStateCoordinator::%s failed to resume thread tid %" PRIu64 ": %s", + __FUNCTION__, tid, error.AsCString ()); + } + } } void ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid, bool is_stopped, ErrorFunction &error_function) { // Ensure we don't already know about the thread. - auto find_it = m_tid_stop_map.find (tid); - if (find_it != m_tid_stop_map.end ()) + auto find_it = m_tid_map.find (tid); + if (find_it != m_tid_map.end ()) { // We already know about this thread. This is an error condition. std::ostringstream error_message; @@ -585,7 +751,9 @@ } // Add the new thread to the stop map. - m_tid_stop_map[tid] = is_stopped; + ThreadContext ctx; + ctx.m_state = (is_stopped) ? ThreadState::Stopped : ThreadState::Running; + m_tid_map[tid] = std::move(ctx); EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); if (call_after_event && !is_stopped) @@ -600,8 +768,8 @@ ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid, ErrorFunction &error_function) { // Ensure we know about the thread. - auto find_it = m_tid_stop_map.find (tid); - if (find_it == m_tid_stop_map.end ()) + auto find_it = m_tid_map.find (tid); + if (find_it == m_tid_map.end ()) { // We don't know about this thread. This is an error condition. std::ostringstream error_message; @@ -613,7 +781,7 @@ // Update the global list of known thread states. While this one is stopped, it is also dead. // So stop tracking it. We assume the user of this coordinator will not keep trying to add // dependencies on a thread after it is known to be dead. - m_tid_stop_map.erase (find_it); + m_tid_map.erase (find_it); // If we have a pending notification, remove this from the set. EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); @@ -638,7 +806,7 @@ // The caller is expected to reset thread states for all threads, and we // will assume anything we haven't heard about is running and requires a // stop. - m_tid_stop_map.clear (); + m_tid_map.clear (); } void @@ -654,17 +822,26 @@ void ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid, + bool initiated_by_llgs, const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventThreadStopped (tid, error_function))); + EnqueueEvent (EventBaseSP (new EventThreadStopped (tid, initiated_by_llgs, error_function))); } void ThreadStateCoordinator::RequestThreadResume (lldb::tid_t tid, - const ThreadIDFunction &request_thread_resume_function, + const ResumeThreadFunction &request_thread_resume_function, const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function))); + EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function, true))); +} + +void +ThreadStateCoordinator::RequestThreadResumeAsNeeded (lldb::tid_t tid, + const ResumeThreadFunction &request_thread_resume_function, + const ErrorFunction &error_function) +{ + EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function, false))); } void @@ -711,7 +888,39 @@ ThreadStateCoordinator::EventLoopResult ThreadStateCoordinator::ProcessNextEvent () { - return DequeueEventWithWait()->ProcessEvent (*this); + // Dequeue the next event, synchronous. + if (m_log_event_processing) + Log ("ThreadStateCoordinator::%s about to dequeue next event in blocking mode", __FUNCTION__); + + EventBaseSP event_sp = DequeueEventWithWait(); + assert (event_sp && "event should never be null"); + if (!event_sp) + { + Log ("ThreadStateCoordinator::%s error: event_sp was null, signaling exit of event loop.", __FUNCTION__); + return eventLoopResultStop; + } + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: %s", __FUNCTION__, event_sp->GetDescription ().c_str ()); + } + + // Process the event. + const EventLoopResult result = event_sp->ProcessEvent (*this); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing returned value %s", __FUNCTION__, + result == eventLoopResultContinue ? "eventLoopResultContinue" : "eventLoopResultStop"); + } + + return result; +} + +void +ThreadStateCoordinator::LogEnableEventProcessing (bool enabled) +{ + m_log_event_processing = enabled; } ThreadStateCoordinator::EventCallAfterThreadsStop * @@ -719,3 +928,9 @@ { return static_cast (m_pending_notification_sp.get ()); } + +bool +ThreadStateCoordinator::IsKnownThread (lldb::tid_t tid) const +{ + return m_tid_map.find (tid) != m_tid_map.end (); +} Index: source/Plugins/Process/POSIX/CMakeLists.txt =================================================================== --- source/Plugins/Process/POSIX/CMakeLists.txt +++ source/Plugins/Process/POSIX/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories(../Utility) add_lldb_library(lldbPluginProcessPOSIX + CrashReason.cpp POSIXStopInfo.cpp POSIXThread.cpp ProcessMessage.cpp Index: source/Plugins/Process/POSIX/CrashReason.h =================================================================== --- /dev/null +++ source/Plugins/Process/POSIX/CrashReason.h @@ -0,0 +1,62 @@ +//===-- CrashReason.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CrashReason_H_ +#define liblldb_CrashReason_H_ + +#include "lldb/lldb-types.h" + +#include + +#include + +enum class CrashReason +{ + eInvalidCrashReason, + + // SIGSEGV crash reasons. + eInvalidAddress, + ePrivilegedAddress, + + // SIGILL crash reasons. + eIllegalOpcode, + eIllegalOperand, + eIllegalAddressingMode, + eIllegalTrap, + ePrivilegedOpcode, + ePrivilegedRegister, + eCoprocessorError, + eInternalStackError, + + // SIGBUS crash reasons, + eIllegalAlignment, + eIllegalAddress, + eHardwareError, + + // SIGFPE crash reasons, + eIntegerDivideByZero, + eIntegerOverflow, + eFloatDivideByZero, + eFloatOverflow, + eFloatUnderflow, + eFloatInexactResult, + eFloatInvalidOperation, + eFloatSubscriptRange +}; + +std::string +GetCrashReasonString (CrashReason reason, lldb::addr_t fault_addr); + +const char * +CrashReasonAsString (CrashReason reason); + +CrashReason +GetCrashReason(const siginfo_t& info); + +#endif // #ifndef liblldb_CrashReason_H_ Index: source/Plugins/Process/POSIX/CrashReason.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/POSIX/CrashReason.cpp @@ -0,0 +1,315 @@ +//===-- CrashReason.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CrashReason.h" + +#include + +namespace { + +void +AppendFaultAddr (std::string& str, lldb::addr_t addr) +{ + std::stringstream ss; + ss << " (fault address: 0x" << std::hex << addr << ")"; + str += ss.str(); +} + +CrashReason +GetCrashReasonForSIGSEGV(const siginfo_t& info) +{ + assert(info.si_signo == SIGSEGV); + + switch (info.si_code) + { + case SI_KERNEL: + // Linux will occasionally send spurious SI_KERNEL codes. + // (this is poorly documented in sigaction) + // One way to get this is via unaligned SIMD loads. + return CrashReason::eInvalidAddress; // for lack of anything better + case SEGV_MAPERR: + return CrashReason::eInvalidAddress; + case SEGV_ACCERR: + return CrashReason::ePrivilegedAddress; + } + + assert(false && "unexpected si_code for SIGSEGV"); + return CrashReason::eInvalidCrashReason; +} + +CrashReason +GetCrashReasonForSIGILL(const siginfo_t& info) +{ + assert(info.si_signo == SIGILL); + + switch (info.si_code) + { + case ILL_ILLOPC: + return CrashReason::eIllegalOpcode; + case ILL_ILLOPN: + return CrashReason::eIllegalOperand; + case ILL_ILLADR: + return CrashReason::eIllegalAddressingMode; + case ILL_ILLTRP: + return CrashReason::eIllegalTrap; + case ILL_PRVOPC: + return CrashReason::ePrivilegedOpcode; + case ILL_PRVREG: + return CrashReason::ePrivilegedRegister; + case ILL_COPROC: + return CrashReason::eCoprocessorError; + case ILL_BADSTK: + return CrashReason::eInternalStackError; + } + + assert(false && "unexpected si_code for SIGILL"); + return CrashReason::eInvalidCrashReason; +} + +CrashReason +GetCrashReasonForSIGFPE(const siginfo_t& info) +{ + assert(info.si_signo == SIGFPE); + + switch (info.si_code) + { + case FPE_INTDIV: + return CrashReason::eIntegerDivideByZero; + case FPE_INTOVF: + return CrashReason::eIntegerOverflow; + case FPE_FLTDIV: + return CrashReason::eFloatDivideByZero; + case FPE_FLTOVF: + return CrashReason::eFloatOverflow; + case FPE_FLTUND: + return CrashReason::eFloatUnderflow; + case FPE_FLTRES: + return CrashReason::eFloatInexactResult; + case FPE_FLTINV: + return CrashReason::eFloatInvalidOperation; + case FPE_FLTSUB: + return CrashReason::eFloatSubscriptRange; + } + + assert(false && "unexpected si_code for SIGFPE"); + return CrashReason::eInvalidCrashReason; +} + +CrashReason +GetCrashReasonForSIGBUS(const siginfo_t& info) +{ + assert(info.si_signo == SIGBUS); + + switch (info.si_code) + { + case BUS_ADRALN: + return CrashReason::eIllegalAlignment; + case BUS_ADRERR: + return CrashReason::eIllegalAddress; + case BUS_OBJERR: + return CrashReason::eHardwareError; + } + + assert(false && "unexpected si_code for SIGBUS"); + return CrashReason::eInvalidCrashReason; +} + +} + +std::string +GetCrashReasonString (CrashReason reason, lldb::addr_t fault_addr) +{ + std::string str; + + switch (reason) + { + default: + assert(false && "invalid CrashReason"); + break; + + case CrashReason::eInvalidAddress: + str = "invalid address"; + AppendFaultAddr (str, fault_addr); + break; + case CrashReason::ePrivilegedAddress: + str = "address access protected"; + AppendFaultAddr (str, fault_addr); + break; + case CrashReason::eIllegalOpcode: + str = "illegal instruction"; + break; + case CrashReason::eIllegalOperand: + str = "illegal instruction operand"; + break; + case CrashReason::eIllegalAddressingMode: + str = "illegal addressing mode"; + break; + case CrashReason::eIllegalTrap: + str = "illegal trap"; + break; + case CrashReason::ePrivilegedOpcode: + str = "privileged instruction"; + break; + case CrashReason::ePrivilegedRegister: + str = "privileged register"; + break; + case CrashReason::eCoprocessorError: + str = "coprocessor error"; + break; + case CrashReason::eInternalStackError: + str = "internal stack error"; + break; + case CrashReason::eIllegalAlignment: + str = "illegal alignment"; + break; + case CrashReason::eIllegalAddress: + str = "illegal address"; + break; + case CrashReason::eHardwareError: + str = "hardware error"; + break; + case CrashReason::eIntegerDivideByZero: + str = "integer divide by zero"; + break; + case CrashReason::eIntegerOverflow: + str = "integer overflow"; + break; + case CrashReason::eFloatDivideByZero: + str = "floating point divide by zero"; + break; + case CrashReason::eFloatOverflow: + str = "floating point overflow"; + break; + case CrashReason::eFloatUnderflow: + str = "floating point underflow"; + break; + case CrashReason::eFloatInexactResult: + str = "inexact floating point result"; + break; + case CrashReason::eFloatInvalidOperation: + str = "invalid floating point operation"; + break; + case CrashReason::eFloatSubscriptRange: + str = "invalid floating point subscript range"; + break; + } + + return str; +} + +const char * +CrashReasonAsString (CrashReason reason) +{ +#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION + // Just return the code in ascii for integration builds. + chcar str[8]; + sprintf(str, "%d", reason); +#else + const char *str = nullptr; + + switch (reason) + { + case CrashReason::eInvalidCrashReason: + str = "eInvalidCrashReason"; + break; + + // SIGSEGV crash reasons. + case CrashReason::eInvalidAddress: + str = "eInvalidAddress"; + break; + case CrashReason::ePrivilegedAddress: + str = "ePrivilegedAddress"; + break; + + // SIGILL crash reasons. + case CrashReason::eIllegalOpcode: + str = "eIllegalOpcode"; + break; + case CrashReason::eIllegalOperand: + str = "eIllegalOperand"; + break; + case CrashReason::eIllegalAddressingMode: + str = "eIllegalAddressingMode"; + break; + case CrashReason::eIllegalTrap: + str = "eIllegalTrap"; + break; + case CrashReason::ePrivilegedOpcode: + str = "ePrivilegedOpcode"; + break; + case CrashReason::ePrivilegedRegister: + str = "ePrivilegedRegister"; + break; + case CrashReason::eCoprocessorError: + str = "eCoprocessorError"; + break; + case CrashReason::eInternalStackError: + str = "eInternalStackError"; + break; + + // SIGBUS crash reasons: + case CrashReason::eIllegalAlignment: + str = "eIllegalAlignment"; + break; + case CrashReason::eIllegalAddress: + str = "eIllegalAddress"; + break; + case CrashReason::eHardwareError: + str = "eHardwareError"; + break; + + // SIGFPE crash reasons: + case CrashReason::eIntegerDivideByZero: + str = "eIntegerDivideByZero"; + break; + case CrashReason::eIntegerOverflow: + str = "eIntegerOverflow"; + break; + case CrashReason::eFloatDivideByZero: + str = "eFloatDivideByZero"; + break; + case CrashReason::eFloatOverflow: + str = "eFloatOverflow"; + break; + case CrashReason::eFloatUnderflow: + str = "eFloatUnderflow"; + break; + case CrashReason::eFloatInexactResult: + str = "eFloatInexactResult"; + break; + case CrashReason::eFloatInvalidOperation: + str = "eFloatInvalidOperation"; + break; + case CrashReason::eFloatSubscriptRange: + str = "eFloatSubscriptRange"; + break; + } +#endif + + return str; +} + +CrashReason +GetCrashReason(const siginfo_t& info) +{ + switch(info.si_signo) + { + case SIGSEGV: + return GetCrashReasonForSIGSEGV(info); + case SIGBUS: + return GetCrashReasonForSIGBUS(info); + case SIGFPE: + return GetCrashReasonForSIGFPE(info); + case SIGILL: + return GetCrashReasonForSIGILL(info); + } + + assert(false && "unexpected signal"); + return CrashReason::eInvalidCrashReason; +} Index: source/Plugins/Process/POSIX/POSIXStopInfo.h =================================================================== --- source/Plugins/Process/POSIX/POSIXStopInfo.h +++ source/Plugins/Process/POSIX/POSIXStopInfo.h @@ -16,8 +16,10 @@ // Project includes #include "lldb/Target/StopInfo.h" +#include "CrashReason.h" #include "POSIXThread.h" -#include "ProcessMessage.h" + +#include //===----------------------------------------------------------------------===// /// @class POSIXStopInfo @@ -69,25 +71,13 @@ { public: POSIXCrashStopInfo(POSIXThread &thread, uint32_t status, - ProcessMessage::CrashReason reason, - lldb::addr_t fault_addr) - : POSIXStopInfo(thread, status), - m_crash_reason(reason), - m_fault_addr(fault_addr) - { } - + CrashReason reason, + lldb::addr_t fault_addr); ~POSIXCrashStopInfo(); lldb::StopReason GetStopReason() const; - - const char * - GetDescription(); - -private: - ProcessMessage::CrashReason m_crash_reason; - lldb::addr_t m_fault_addr; -}; +}; //===----------------------------------------------------------------------===// /// @class POSIXNewThreadStopInfo Index: source/Plugins/Process/POSIX/POSIXStopInfo.cpp =================================================================== --- source/Plugins/Process/POSIX/POSIXStopInfo.cpp +++ source/Plugins/Process/POSIX/POSIXStopInfo.cpp @@ -45,6 +45,15 @@ //===----------------------------------------------------------------------===// // POSIXCrashStopInfo +POSIXCrashStopInfo::POSIXCrashStopInfo(POSIXThread &thread, + uint32_t status, + CrashReason reason, + lldb::addr_t fault_addr) + : POSIXStopInfo(thread, status) +{ + m_description = ::GetCrashReasonString(reason, fault_addr); +} + POSIXCrashStopInfo::~POSIXCrashStopInfo() { } lldb::StopReason @@ -53,12 +62,6 @@ return lldb::eStopReasonException; } -const char * -POSIXCrashStopInfo::GetDescription() -{ - return ProcessMessage::GetCrashReasonString(m_crash_reason, m_fault_addr); -} - //===----------------------------------------------------------------------===// // POSIXNewThreadStopInfo Index: source/Plugins/Process/POSIX/ProcessMessage.h =================================================================== --- source/Plugins/Process/POSIX/ProcessMessage.h +++ source/Plugins/Process/POSIX/ProcessMessage.h @@ -10,7 +10,10 @@ #ifndef liblldb_ProcessMessage_H_ #define liblldb_ProcessMessage_H_ +#include "CrashReason.h" + #include +#include #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" @@ -36,44 +39,10 @@ eExecMessage }; - enum CrashReason - { - eInvalidCrashReason, - - // SIGSEGV crash reasons. - eInvalidAddress, - ePrivilegedAddress, - - // SIGILL crash reasons. - eIllegalOpcode, - eIllegalOperand, - eIllegalAddressingMode, - eIllegalTrap, - ePrivilegedOpcode, - ePrivilegedRegister, - eCoprocessorError, - eInternalStackError, - - // SIGBUS crash reasons, - eIllegalAlignment, - eIllegalAddress, - eHardwareError, - - // SIGFPE crash reasons, - eIntegerDivideByZero, - eIntegerOverflow, - eFloatDivideByZero, - eFloatOverflow, - eFloatUnderflow, - eFloatInexactResult, - eFloatInvalidOperation, - eFloatSubscriptRange - }; - ProcessMessage() : m_tid(LLDB_INVALID_PROCESS_ID), m_kind(eInvalidMessage), - m_crash_reason(eInvalidCrashReason), + m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0), m_addr(0) { } @@ -175,15 +144,9 @@ return m_child_tid; } - static const char * - GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr); - const char * PrintCrashReason() const; - static const char * - PrintCrashReason(CrashReason reason); - const char * PrintKind() const; @@ -195,7 +158,7 @@ int status = 0, lldb::addr_t addr = 0) : m_tid(tid), m_kind(kind), - m_crash_reason(eInvalidCrashReason), + m_crash_reason(CrashReason::eInvalidCrashReason), m_status(status), m_addr(addr), m_child_tid(0) { } @@ -203,14 +166,14 @@ ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid) : m_tid(tid), m_kind(kind), - m_crash_reason(eInvalidCrashReason), + m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0), m_addr(0), m_child_tid(child_tid) { } lldb::tid_t m_tid; Kind m_kind : 8; - CrashReason m_crash_reason : 8; + CrashReason m_crash_reason; int m_status; lldb::addr_t m_addr; lldb::tid_t m_child_tid; Index: source/Plugins/Process/POSIX/ProcessMessage.cpp =================================================================== --- source/Plugins/Process/POSIX/ProcessMessage.cpp +++ source/Plugins/Process/POSIX/ProcessMessage.cpp @@ -9,205 +9,19 @@ #include "ProcessMessage.h" -#include - using namespace lldb_private; -namespace { - -inline void AppendFaultAddr(std::string& str, lldb::addr_t addr) -{ - std::stringstream ss; - ss << " (fault address: 0x" << std::hex << addr << ")"; - str += ss.str(); -} - -} - -const char * -ProcessMessage::GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) -{ - static std::string str; - - switch (reason) - { - default: - assert(false && "invalid CrashReason"); - break; - - case eInvalidAddress: - str = "invalid address"; - AppendFaultAddr(str, fault_addr); - break; - case ePrivilegedAddress: - str = "address access protected"; - AppendFaultAddr(str, fault_addr); - break; - case eIllegalOpcode: - str = "illegal instruction"; - break; - case eIllegalOperand: - str = "illegal instruction operand"; - break; - case eIllegalAddressingMode: - str = "illegal addressing mode"; - break; - case eIllegalTrap: - str = "illegal trap"; - break; - case ePrivilegedOpcode: - str = "privileged instruction"; - break; - case ePrivilegedRegister: - str = "privileged register"; - break; - case eCoprocessorError: - str = "coprocessor error"; - break; - case eInternalStackError: - str = "internal stack error"; - break; - case eIllegalAlignment: - str = "illegal alignment"; - break; - case eIllegalAddress: - str = "illegal address"; - break; - case eHardwareError: - str = "hardware error"; - break; - case eIntegerDivideByZero: - str = "integer divide by zero"; - break; - case eIntegerOverflow: - str = "integer overflow"; - break; - case eFloatDivideByZero: - str = "floating point divide by zero"; - break; - case eFloatOverflow: - str = "floating point overflow"; - break; - case eFloatUnderflow: - str = "floating point underflow"; - break; - case eFloatInexactResult: - str = "inexact floating point result"; - break; - case eFloatInvalidOperation: - str = "invalid floating point operation"; - break; - case eFloatSubscriptRange: - str = "invalid floating point subscript range"; - break; - } - - return str.c_str(); -} - -const char * -ProcessMessage::PrintCrashReason(CrashReason reason) -{ -#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION - // Just return the code in asci for integration builds. - chcar str[8]; - sprintf(str, "%d", reason); -#else - const char *str = NULL; - - switch (reason) - { - case eInvalidCrashReason: - str = "eInvalidCrashReason"; - break; - - // SIGSEGV crash reasons. - case eInvalidAddress: - str = "eInvalidAddress"; - break; - case ePrivilegedAddress: - str = "ePrivilegedAddress"; - break; - - // SIGILL crash reasons. - case eIllegalOpcode: - str = "eIllegalOpcode"; - break; - case eIllegalOperand: - str = "eIllegalOperand"; - break; - case eIllegalAddressingMode: - str = "eIllegalAddressingMode"; - break; - case eIllegalTrap: - str = "eIllegalTrap"; - break; - case ePrivilegedOpcode: - str = "ePrivilegedOpcode"; - break; - case ePrivilegedRegister: - str = "ePrivilegedRegister"; - break; - case eCoprocessorError: - str = "eCoprocessorError"; - break; - case eInternalStackError: - str = "eInternalStackError"; - break; - - // SIGBUS crash reasons: - case eIllegalAlignment: - str = "eIllegalAlignment"; - break; - case eIllegalAddress: - str = "eIllegalAddress"; - break; - case eHardwareError: - str = "eHardwareError"; - break; - - // SIGFPE crash reasons: - case eIntegerDivideByZero: - str = "eIntegerDivideByZero"; - break; - case eIntegerOverflow: - str = "eIntegerOverflow"; - break; - case eFloatDivideByZero: - str = "eFloatDivideByZero"; - break; - case eFloatOverflow: - str = "eFloatOverflow"; - break; - case eFloatUnderflow: - str = "eFloatUnderflow"; - break; - case eFloatInexactResult: - str = "eFloatInexactResult"; - break; - case eFloatInvalidOperation: - str = "eFloatInvalidOperation"; - break; - case eFloatSubscriptRange: - str = "eFloatSubscriptRange"; - break; - } -#endif - - return str; -} - const char * ProcessMessage::PrintCrashReason() const { - return PrintCrashReason(m_crash_reason); + return CrashReasonAsString(m_crash_reason); } const char * ProcessMessage::PrintKind(Kind kind) { #ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION - // Just return the code in asci for integration builds. + // Just return the code in ascii for integration builds. chcar str[8]; sprintf(str, "%d", reason); #else Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -27,6 +27,16 @@ #include "Utility/StringExtractorGDBRemote.h" +typedef enum +{ + eStoppointInvalid = -1, + eBreakpointSoftware = 0, + eBreakpointHardware, + eWatchpointWrite, + eWatchpointRead, + eWatchpointReadWrite +} GDBStoppointType; + class ProcessGDBRemote; class GDBRemoteCommunication : public lldb_private::Communication Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -21,15 +21,6 @@ #include "GDBRemoteCommunication.h" -typedef enum -{ - eBreakpointSoftware = 0, - eBreakpointHardware, - eWatchpointWrite, - eWatchpointRead, - eWatchpointReadWrite -} GDBStoppointType; - class GDBRemoteCommunicationClient : public GDBRemoteCommunication { public: @@ -375,8 +366,8 @@ case eWatchpointWrite: return m_supports_z2; case eWatchpointRead: return m_supports_z3; case eWatchpointReadWrite: return m_supports_z4; + case eStoppointInvalid: return false; } - return false; } uint8_t SendGDBStoppointTypePacket (GDBStoppointType type, // Type of breakpoint or watchpoint Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -3018,6 +3018,7 @@ case eWatchpointWrite: m_supports_z2 = false; break; case eWatchpointRead: m_supports_z3 = false; break; case eWatchpointReadWrite: m_supports_z4 = false; break; + case eStoppointInvalid: return UINT8_MAX; } } } Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -23,7 +23,7 @@ #include "lldb/Target/Process.h" #include "GDBRemoteCommunication.h" -#include "../../../Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeProcessProtocol.h" class ProcessGDBRemote; class StringExtractorGDBRemote; @@ -465,6 +465,9 @@ PacketResult Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); + PacketResult + Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet); + void SetCurrentThreadID (lldb::tid_t tid); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -39,9 +39,9 @@ #include "lldb/Target/FileAction.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" -#include "lldb/Target/NativeRegisterContext.h" -#include "Host/common/NativeProcessProtocol.h" -#include "Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -278,6 +278,10 @@ packet_result = Handle_qPlatform_shell (packet); break; + case StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo: + packet_result = Handle_qWatchpointSupportInfo (packet); + break; + case StringExtractorGDBRemote::eServerPacketType_C: packet_result = Handle_C (packet); break; @@ -837,12 +841,12 @@ // Grab the reason this thread stopped. struct ThreadStopInfo tid_stop_info; - if (!thread_sp->GetStopReason (tid_stop_info)) + std::string description; + if (!thread_sp->GetStopReason (tid_stop_info, description)) return SendErrorResponse (52); - const bool did_exec = tid_stop_info.reason == eStopReasonExec; // FIXME implement register handling for exec'd inferiors. - // if (did_exec) + // if (tid_stop_info.reason == eStopReasonExec) // { // const bool force = true; // InitializeRegisters(force); @@ -863,25 +867,6 @@ tid_stop_info.details.exception.type); } - switch (tid_stop_info.reason) - { - case eStopReasonSignal: - case eStopReasonException: - signum = thread_sp->TranslateStopInfoToGdbSignal (tid_stop_info); - break; - default: - signum = 0; - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " has stop reason %d, using signo = 0 in stop reply response", - __FUNCTION__, - m_debugged_process_sp->GetID (), - tid, - tid_stop_info.reason); - } - break; - } - // Print the signal number. response.PutHex8 (signum & 0xff); @@ -908,14 +893,6 @@ response.PutChar (';'); } - // FIXME look for analog - // thread_identifier_info_data_t thread_ident_info; - // if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info)) - // { - // if (thread_ident_info.dispatch_qaddr != 0) - // ostrm << std::hex << "qaddr:" << thread_ident_info.dispatch_qaddr << ';'; - // } - // If a 'QListThreadsInStopReply' was sent to enable this feature, we // will send all thread IDs back in the "threads" key whose value is // a list of hex thread IDs separated by commas: @@ -980,9 +957,45 @@ } } - if (did_exec) + const char* reason_str = nullptr; + switch (tid_stop_info.reason) + { + case eStopReasonTrace: + reason_str = "trace"; + break; + case eStopReasonBreakpoint: + reason_str = "breakpoint"; + break; + case eStopReasonWatchpoint: + reason_str = "watchpoint"; + break; + case eStopReasonSignal: + reason_str = "signal"; + break; + case eStopReasonException: + reason_str = "exception"; + break; + case eStopReasonExec: + reason_str = "exec"; + break; + case eStopReasonInstrumentation: + case eStopReasonInvalid: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonNone: + break; + } + if (reason_str != nullptr) + { + response.Printf ("reason:%s;", reason_str); + } + + if (!description.empty()) { - response.PutCString ("reason:exec;"); + // Description may contains special chars, send as hex bytes. + response.PutCString ("description:"); + response.PutCStringAsRawHex8 (description.c_str ()); + response.PutChar (';'); } else if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) { @@ -2517,10 +2530,6 @@ thread_actions.Append (thread_action); } - // If a default action for all other threads wasn't mentioned - // then we should stop the threads. - thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0); - Error error = m_debugged_process_sp->Resume (thread_actions); if (error.Fail ()) { @@ -3279,7 +3288,8 @@ } // Parse out the value. - const uint64_t raw_value = packet.GetHexMaxU64 (process_arch.GetByteOrder () == lldb::eByteOrderLittle, std::numeric_limits::max ()); + uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register + size_t reg_size = packet.GetHexBytesAvail (reg_bytes, sizeof(reg_bytes)); // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); @@ -3299,7 +3309,7 @@ return SendErrorResponse (0x15); } - const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); + const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex (reg_index); if (!reg_info) { if (log) @@ -3315,13 +3325,16 @@ return SendErrorResponse (0x47); } + if (reg_size != reg_info->byte_size) + { + return SendIllFormedResponse (packet, "P packet register size is incorrect"); + } // Build the reginfos response. StreamGDBRemote response; - // FIXME Could be suffixed with a thread: parameter. - // That thread then needs to be fed back into the reg context retrieval above. - Error error = reg_context_sp->WriteRegisterFromUnsigned (reg_info, raw_value); + RegisterValue reg_value (reg_bytes, reg_size, process_arch.GetByteOrder ()); + Error error = reg_context_sp->WriteRegister (reg_info, reg_value); if (error.Fail ()) { if (log) @@ -3719,8 +3732,6 @@ GDBRemoteCommunicationServer::PacketResult GDBRemoteCommunicationServer::Handle_Z (StringExtractorGDBRemote &packet) { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - // We don't support if we're not llgs. if (!IsGdbServer()) return SendUnimplementedResponse (""); @@ -3728,12 +3739,13 @@ // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); return SendErrorResponse (0x15); } - // Parse out software or hardware breakpoint requested. + // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos (strlen("Z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing software/hardware specifier"); @@ -3741,61 +3753,82 @@ bool want_breakpoint = true; bool want_hardware = false; - const char breakpoint_type_char = packet.GetChar (); - switch (breakpoint_type_char) - { - case '0': want_hardware = false; want_breakpoint = true; break; - case '1': want_hardware = true; want_breakpoint = true; break; - case '2': want_breakpoint = false; break; - case '3': want_breakpoint = false; break; - default: + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) + { + case eBreakpointSoftware: + want_hardware = false; want_breakpoint = true; break; + case eBreakpointHardware: + want_hardware = true; want_breakpoint = true; break; + case eWatchpointWrite: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointRead: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointReadWrite: + want_hardware = true; want_breakpoint = false; break; + case eStoppointInvalid: return SendIllFormedResponse(packet, "Z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after breakpoint type"); - - // FIXME implement watchpoint support. - if (!want_breakpoint) - return SendUnimplementedResponse ("watchpoint support not yet implemented"); + return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after stoppoint type"); - // Parse out the breakpoint address. + // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing address"); - const lldb::addr_t breakpoint_addr = packet.GetHexMaxU64(false, 0); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after address"); - // Parse out the breakpoint kind (i.e. size hint for opcode size). - const uint32_t kind = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); - if (kind == std::numeric_limits::max ()) - return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse kind argument"); + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); + if (size == std::numeric_limits::max ()) + return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse size argument"); if (want_breakpoint) { // Try to set the breakpoint. - const Error error = m_debugged_process_sp->SetBreakpoint (breakpoint_addr, kind, want_hardware); + const Error error = m_debugged_process_sp->SetBreakpoint (addr, size, want_hardware); if (error.Success ()) return SendOKResponse (); - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to set breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x09); - } + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to set breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); } + else + { + uint32_t watch_flags = + stoppoint_type == eWatchpointWrite + ? watch_flags = 0x1 // Write + : watch_flags = 0x3; // ReadWrite - // FIXME fix up after watchpoints are handled. - return SendUnimplementedResponse (""); + // Try to set the watchpoint. + const Error error = m_debugged_process_sp->SetWatchpoint ( + addr, size, watch_flags, want_hardware); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to set watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } } GDBRemoteCommunicationServer::PacketResult GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - // We don't support if we're not llgs. if (!IsGdbServer()) return SendUnimplementedResponse (""); @@ -3803,66 +3836,81 @@ // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); return SendErrorResponse (0x15); } - // Parse out software or hardware breakpoint requested. + // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos (strlen("z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing software/hardware specifier"); bool want_breakpoint = true; - const char breakpoint_type_char = packet.GetChar (); - switch (breakpoint_type_char) + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) { - case '0': want_breakpoint = true; break; - case '1': want_breakpoint = true; break; - case '2': want_breakpoint = false; break; - case '3': want_breakpoint = false; break; + case eBreakpointHardware: want_breakpoint = true; break; + case eBreakpointSoftware: want_breakpoint = true; break; + case eWatchpointWrite: want_breakpoint = false; break; + case eWatchpointRead: want_breakpoint = false; break; + case eWatchpointReadWrite: want_breakpoint = false; break; default: return SendIllFormedResponse(packet, "z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after breakpoint type"); - - // FIXME implement watchpoint support. - if (!want_breakpoint) - return SendUnimplementedResponse ("watchpoint support not yet implemented"); + return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after stoppoint type"); - // Parse out the breakpoint address. + // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing address"); - const lldb::addr_t breakpoint_addr = packet.GetHexMaxU64(false, 0); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after address"); - // Parse out the breakpoint kind (i.e. size hint for opcode size). - const uint32_t kind = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); - if (kind == std::numeric_limits::max ()) - return SendIllFormedResponse(packet, "Malformed z packet, failed to parse kind argument"); + /* + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); + if (size == std::numeric_limits::max ()) + return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); + */ if (want_breakpoint) { // Try to clear the breakpoint. - const Error error = m_debugged_process_sp->RemoveBreakpoint (breakpoint_addr); + const Error error = m_debugged_process_sp->RemoveBreakpoint (addr); if (error.Success ()) return SendOKResponse (); - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to remove breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x09); - } + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to remove breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } + else + { + // Try to clear the watchpoint. + const Error error = m_debugged_process_sp->RemoveWatchpoint (addr); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to remove watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); } - - // FIXME fix up after watchpoints are handled. - return SendUnimplementedResponse (""); } GDBRemoteCommunicationServer::PacketResult @@ -4298,6 +4346,30 @@ return SendStopReplyPacketForThread (tid); } +GDBRemoteCommunicationServer::PacketResult +GDBRemoteCommunicationServer::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet) +{ + // Only the gdb server handles this. + if (!IsGdbServer ()) + return SendUnimplementedResponse (packet.GetStringRef ().c_str ()); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse (68); + + packet.SetFilePos(strlen("qWatchpointSupportInfo")); + if (packet.GetBytesLeft() == 0) + return SendOKResponse(); + if (packet.GetChar() != ':') + return SendErrorResponse(67); + + uint32_t num = m_debugged_process_sp->GetMaxWatchpoints(); + StreamGDBRemote response; + response.Printf ("num:%d;", num); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + void GDBRemoteCommunicationServer::FlushInferiorOutput () { Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1742,7 +1742,8 @@ // Swap "value" over into "name_extractor" desc_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value - desc_extractor.GetHexByteString (thread_name); + desc_extractor.GetHexByteString (value); + description.swap(value); } else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1])) { @@ -1843,8 +1844,24 @@ } else if (reason.compare("watchpoint") == 0) { - break_id_t watch_id = LLDB_INVALID_WATCH_ID; - // TODO: locate the watchpoint somehow... + StringExtractor desc_extractor(description.c_str()); + addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); + uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + if (wp_addr != LLDB_INVALID_ADDRESS) + { + WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); + if (wp_sp) + { + wp_sp->SetHardwareIndex(wp_index); + watch_id = wp_sp->GetID(); + } + } + if (watch_id == LLDB_INVALID_WATCH_ID) + { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); + if (log) log->Printf ("failed to find watchpoint"); + } thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); handled = true; } Index: source/Target/CMakeLists.txt =================================================================== --- source/Target/CMakeLists.txt +++ source/Target/CMakeLists.txt @@ -14,8 +14,6 @@ LanguageRuntime.cpp Memory.cpp MemoryHistory.cpp - NativeRegisterContext.cpp - NativeRegisterContextRegisterInfo.cpp ObjCLanguageRuntime.cpp OperatingSystem.cpp PathMappingList.cpp Index: test/functionalities/inferior-crashing/TestInferiorCrashing.py =================================================================== --- test/functionalities/inferior-crashing/TestInferiorCrashing.py +++ test/functionalities/inferior-crashing/TestInferiorCrashing.py @@ -101,6 +101,9 @@ return stop_reason + def get_api_stop_reason(self): + return lldb.eStopReasonException + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -136,7 +139,7 @@ "instead the actual state is: '%s'" % lldbutil.state_type_to_str(process.GetState())) - thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonException) + thread = lldbutil.get_stopped_thread(process, self.get_api_stop_reason()) if not thread: self.fail("Fail to stop the thread upon bad access exception") Index: test/functionalities/process_launch/TestProcessLaunch.py =================================================================== --- test/functionalities/process_launch/TestProcessLaunch.py +++ test/functionalities/process_launch/TestProcessLaunch.py @@ -120,8 +120,9 @@ self.my_working_dir_test() @skipIfFreeBSD # llvm.org/pr16684 - @dwarf_test @expectedFailureDarwin("llvm.org/pr20265") + @expectedFailureLLGS("llvm.org/pr20265") + @dwarf_test def test_set_working_dir_with_dwarf (self): """Test that '-w dir' sets the working dir when running the inferior.""" self.buildDwarf(dictionary=self.d) Index: test/functionalities/thread/break_after_join/TestBreakAfterJoin.py =================================================================== --- test/functionalities/thread/break_after_join/TestBreakAfterJoin.py +++ test/functionalities/thread/break_after_join/TestBreakAfterJoin.py @@ -22,6 +22,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_with_dwarf(self): """Test breakpoint handling after a thread join.""" Index: test/functionalities/thread/concurrent_events/TestConcurrentEvents.py =================================================================== --- test/functionalities/thread/concurrent_events/TestConcurrentEvents.py +++ test/functionalities/thread/concurrent_events/TestConcurrentEvents.py @@ -372,7 +372,7 @@ """ return self.finish_breakpoint.GetHitCount() > 0 or \ self.crash_count > 0 or \ - self.inferior_process.GetState == lldb.eStateExited + self.inferior_process.GetState() == lldb.eStateExited def do_thread_actions(self, num_breakpoint_threads = 0, Index: test/functionalities/thread/create_during_step/TestCreateDuringStep.py =================================================================== --- test/functionalities/thread/create_during_step/TestCreateDuringStep.py +++ test/functionalities/thread/create_during_step/TestCreateDuringStep.py @@ -38,6 +38,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_step_inst_with_dwarf(self): """Test thread creation during step-inst handling.""" @@ -46,6 +47,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_step_over_with_dwarf(self): """Test thread creation during step-over handling.""" @@ -54,6 +56,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_step_in_with_dwarf(self): """Test thread creation during step-in handling.""" Index: test/functionalities/thread/exit_during_break/TestExitDuringBreak.py =================================================================== --- test/functionalities/thread/exit_during_break/TestExitDuringBreak.py +++ test/functionalities/thread/exit_during_break/TestExitDuringBreak.py @@ -22,6 +22,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_with_dwarf(self): """Test thread exit during breakpoint handling.""" Index: test/functionalities/thread/exit_during_step/TestExitDuringStep.py =================================================================== --- test/functionalities/thread/exit_during_step/TestExitDuringStep.py +++ test/functionalities/thread/exit_during_step/TestExitDuringStep.py @@ -22,6 +22,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_thread_state_is_stopped_with_dwarf(self): """Test thread exit during step handling.""" @@ -57,6 +58,7 @@ self.exit_during_step_inst_test() @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_step_over_with_dwarf(self): """Test thread exit during step-over handling.""" @@ -64,6 +66,7 @@ self.exit_during_step_over_test() @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_step_in_with_dwarf(self): """Test thread exit during step-in handling.""" Index: test/functionalities/thread/multi_break/TestMultipleBreakpoints.py =================================================================== --- test/functionalities/thread/multi_break/TestMultipleBreakpoints.py +++ test/functionalities/thread/multi_break/TestMultipleBreakpoints.py @@ -22,6 +22,7 @@ @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test def test_with_dwarf(self): """Test simultaneous breakpoints in multiple threads.""" Index: test/functionalities/thread/state/TestThreadStates.py =================================================================== --- test/functionalities/thread/state/TestThreadStates.py +++ test/functionalities/thread/state/TestThreadStates.py @@ -20,9 +20,10 @@ self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_breakpoint_test() + @expectedFailureDarwin("rdar://15367566") @expectedFailureFreeBSD('llvm.org/pr15824') + @expectedFailureLLGS("llvm.org/pr15824") # thread states not properly maintained @dwarf_test - @expectedFailureDarwin("rdar://15367566") def test_state_after_breakpoint_with_dwarf(self): """Test thread state after breakpoint.""" self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) Index: test/lldbtest.py =================================================================== --- test/lldbtest.py +++ test/lldbtest.py @@ -519,6 +519,11 @@ def expectedFailureWindows(bugnumber=None, compilers=None): if bugnumber: return expectedFailureOS('win32', bugnumber, compilers) +def expectedFailureLLGS(bugnumber=None, compilers=None): + def fn(self): + return 'PLATFORM_LINUX_FORCE_LLGS_LOCAL' in os.environ and self.expectedCompiler(compilers) + if bugnumber: return expectedFailure(fn, bugnumber) + def skipIfRemote(func): """Decorate the item to skip tests if testing remotely.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase):