diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-06-26 20:33:56 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-06-26 20:33:56 +0000 |
commit | fdea456ad833fbab0d3a296a58250950f11a498c (patch) | |
tree | 3db481072633e348326ee97c01d69518ed66b145 | |
parent | 4befb1f96d641a958548654b2c3b674f0ce8404c (diff) |
Vendor import of lldb trunk r306325:vendor/lldb/lldb-trunk-r306325
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=320384
svn path=/vendor/lldb/lldb-trunk-r306325/; revision=320385; tag=vendor/lldb/lldb-trunk-r306325
62 files changed, 2268 insertions, 1170 deletions
diff --git a/include/lldb/Host/Host.h b/include/lldb/Host/Host.h index c474dccab5db..bf48e207f1f2 100644 --- a/include/lldb/Host/Host.h +++ b/include/lldb/Host/Host.h @@ -29,6 +29,27 @@ class FileAction; class ProcessLaunchInfo; //---------------------------------------------------------------------- +// Exit Type for inferior processes +//---------------------------------------------------------------------- +struct WaitStatus { + enum Type : uint8_t { + Exit, // The status represents the return code from normal + // program exit (i.e. WIFEXITED() was true) + Signal, // The status represents the signal number that caused + // the program to exit (i.e. WIFSIGNALED() was true) + Stop, // The status represents the signal number that caused the + // program to stop (i.e. WIFSTOPPED() was true) + }; + + Type type; + uint8_t status; + + WaitStatus(Type type, uint8_t status) : type(type), status(status) {} + + static WaitStatus Decode(int wstatus); +}; + +//---------------------------------------------------------------------- /// @class Host Host.h "lldb/Host/Host.h" /// @brief A class that provides host computer information. /// @@ -111,15 +132,6 @@ public: static const char *GetSignalAsCString(int signo); - typedef void (*ThreadLocalStorageCleanupCallback)(void *p); - - static lldb::thread_key_t - ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback); - - static void *ThreadLocalStorageGet(lldb::thread_key_t key); - - static void ThreadLocalStorageSet(lldb::thread_key_t key, void *value); - //------------------------------------------------------------------ /// Given an address in the current process (the process that /// is running the LLDB code), return the name of the module that @@ -184,22 +196,6 @@ public: static bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info); -#if (defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \ - defined(__GLIBC__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \ - !defined(__ANDROID__) - - static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info); - - static Status LaunchProcessPosixSpawn(const char *exe_path, - const ProcessLaunchInfo &launch_info, - lldb::pid_t &pid); - - static bool AddPosixSpawnFileAction(void *file_actions, - const FileAction *info, Log *log, - Status &error); - -#endif - static const lldb::UnixSignalsSP &GetUnixSignals(); static Status LaunchProcess(ProcessLaunchInfo &launch_info); @@ -246,5 +242,14 @@ public: } // namespace lldb_private +namespace llvm { +template <> struct format_provider<lldb_private::WaitStatus> { + /// Options = "" gives a human readable description of the status + /// Options = "g" gives a gdb-remote protocol status (e.g., X09) + static void format(const lldb_private::WaitStatus &WS, raw_ostream &OS, + llvm::StringRef Options); +}; +} // namespace llvm + #endif // #if defined(__cplusplus) #endif // liblldb_Host_h_ diff --git a/include/lldb/Host/common/NativeProcessProtocol.h b/include/lldb/Host/common/NativeProcessProtocol.h index 55eca0fa0b65..c43299a1df3d 100644 --- a/include/lldb/Host/common/NativeProcessProtocol.h +++ b/include/lldb/Host/common/NativeProcessProtocol.h @@ -11,6 +11,7 @@ #define liblldb_NativeProcessProtocol_h_ #include "lldb/Core/TraceOptions.h" +#include "lldb/Host/Host.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private-forward.h" @@ -158,12 +159,9 @@ public: //---------------------------------------------------------------------- // Exit Status //---------------------------------------------------------------------- - virtual bool GetExitStatus(lldb_private::ExitType *exit_type, int *status, - std::string &exit_description); + virtual llvm::Optional<WaitStatus> GetExitStatus(); - virtual bool SetExitStatus(lldb_private::ExitType exit_type, int status, - const char *exit_description, - bool bNotifyStateChange); + virtual bool SetExitStatus(WaitStatus status, bool bNotifyStateChange); //---------------------------------------------------------------------- // Access to threads @@ -421,9 +419,8 @@ protected: lldb::StateType m_state; mutable std::recursive_mutex m_state_mutex; - lldb_private::ExitType m_exit_type; - int m_exit_status; - std::string m_exit_description; + llvm::Optional<WaitStatus> m_exit_status; + std::recursive_mutex m_delegates_mutex; std::vector<NativeDelegate *> m_delegates; NativeBreakpointList m_breakpoint_list; diff --git a/include/lldb/Host/posix/ProcessLauncherPosix.h b/include/lldb/Host/posix/ProcessLauncherPosix.h deleted file mode 100644 index 4800c4066049..000000000000 --- a/include/lldb/Host/posix/ProcessLauncherPosix.h +++ /dev/null @@ -1,24 +0,0 @@ -//===-- ProcessLauncherPosix.h ----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef lldb_Host_posix_ProcessLauncherPosix_h_ -#define lldb_Host_posix_ProcessLauncherPosix_h_ - -#include "lldb/Host/ProcessLauncher.h" - -namespace lldb_private { - -class ProcessLauncherPosix : public ProcessLauncher { -public: - HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, - Status &error) override; -}; -} - -#endif diff --git a/include/lldb/lldb-enumerations.h b/include/lldb/lldb-enumerations.h index f62b3cc0b19b..3c60873f6796 100644 --- a/include/lldb/lldb-enumerations.h +++ b/include/lldb/lldb-enumerations.h @@ -454,6 +454,8 @@ enum LanguageType { enum InstrumentationRuntimeType { eInstrumentationRuntimeTypeAddressSanitizer = 0x0000, eInstrumentationRuntimeTypeThreadSanitizer = 0x0001, + eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer = 0x0002, + eInstrumentationRuntimeTypeMainThreadChecker = 0x0003, eNumInstrumentationRuntimeTypes }; diff --git a/include/lldb/lldb-private-enumerations.h b/include/lldb/lldb-private-enumerations.h index 332265665237..983ddf3d23d6 100644 --- a/include/lldb/lldb-private-enumerations.h +++ b/include/lldb/lldb-private-enumerations.h @@ -212,19 +212,6 @@ enum class LineStatus { }; //---------------------------------------------------------------------- -// Exit Type for inferior processes -//---------------------------------------------------------------------- -typedef enum ExitType { - eExitTypeInvalid, - eExitTypeExit, // The exit status represents the return code from normal - // program exit (i.e. WIFEXITED() was true) - eExitTypeSignal, // The exit status represents the signal number that caused - // the program to exit (i.e. WIFSIGNALED() was true) - eExitTypeStop, // The exit status represents the stop signal that caused the - // program to exit (i.e. WIFSTOPPED() was true) -} ExitType; - -//---------------------------------------------------------------------- // Boolean result of running a Type Validator //---------------------------------------------------------------------- enum class TypeValidatorResult : bool { Success = true, Failure = false }; diff --git a/lldb.xcodeproj/project.pbxproj b/lldb.xcodeproj/project.pbxproj index a01a1798ce9a..f4c5c7a0e842 100644 --- a/lldb.xcodeproj/project.pbxproj +++ b/lldb.xcodeproj/project.pbxproj @@ -744,6 +744,7 @@ 4CF3D80C15AF4DC800845BF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB919B414F6F10D008FF64B /* Security.framework */; }; 4CF52AF51428291E0051E832 /* SBFileSpecList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CF52AF41428291E0051E832 /* SBFileSpecList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4CF52AF8142829390051E832 /* SBFileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */; }; + 54067BF11DF2041B00749AA5 /* UndefinedBehaviorSanitizerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 54067BEC1DF2034B00749AA5 /* UndefinedBehaviorSanitizerRuntime.cpp */; }; 6D0F61431C80AAAE00A4ECEE /* JavaASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D0F61411C80AAAA00A4ECEE /* JavaASTContext.cpp */; }; 6D0F61481C80AAD600A4ECEE /* DWARFASTParserJava.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D0F61441C80AACF00A4ECEE /* DWARFASTParserJava.cpp */; }; 6D0F614E1C80AB0700A4ECEE /* JavaLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D0F614A1C80AB0400A4ECEE /* JavaLanguageRuntime.cpp */; }; @@ -766,6 +767,7 @@ 8C26C4261C3EA5F90031DF7C /* ThreadSanitizerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C26C4241C3EA4340031DF7C /* ThreadSanitizerRuntime.cpp */; }; 8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */; }; 8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */; }; + 8C3BD9961EF45DA50016C343 /* MainThreadCheckerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */; }; 8CCB017E19BA28A80009FD44 /* ThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */; }; 8CCB018219BA4E270009FD44 /* SBThreadCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8CCB018319BA51BF0009FD44 /* SBThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */; }; @@ -2576,6 +2578,8 @@ 4CEDAED311754F5E00E875A6 /* ThreadPlanStepUntil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepUntil.h; path = include/lldb/Target/ThreadPlanStepUntil.h; sourceTree = "<group>"; }; 4CF52AF41428291E0051E832 /* SBFileSpecList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBFileSpecList.h; path = include/lldb/API/SBFileSpecList.h; sourceTree = "<group>"; }; 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBFileSpecList.cpp; path = source/API/SBFileSpecList.cpp; sourceTree = "<group>"; }; + 54067BEC1DF2034B00749AA5 /* UndefinedBehaviorSanitizerRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = UndefinedBehaviorSanitizerRuntime.cpp; path = UndefinedBehaviorSanitizer/UndefinedBehaviorSanitizerRuntime.cpp; sourceTree = "<group>"; }; + 54067BED1DF2034B00749AA5 /* UndefinedBehaviorSanitizerRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UndefinedBehaviorSanitizerRuntime.h; path = UndefinedBehaviorSanitizer/UndefinedBehaviorSanitizerRuntime.h; sourceTree = "<group>"; }; 69A01E1C1236C5D400C660B5 /* Host.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Host.cpp; sourceTree = "<group>"; }; 69A01E1F1236C5D400C660B5 /* Symbols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbols.cpp; sourceTree = "<group>"; }; 6D0F613C1C80AA8900A4ECEE /* DebugMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DebugMacros.h; path = include/lldb/Symbol/DebugMacros.h; sourceTree = "<group>"; }; @@ -2624,6 +2628,8 @@ 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = "<group>"; }; 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = "<group>"; }; 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = "<group>"; }; + 8C3BD9931EF45D9B0016C343 /* MainThreadCheckerRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainThreadCheckerRuntime.h; sourceTree = "<group>"; }; + 8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MainThreadCheckerRuntime.cpp; sourceTree = "<group>"; }; 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadCollection.cpp; path = source/Target/ThreadCollection.cpp; sourceTree = "<group>"; }; 8CCB017C19BA289B0009FD44 /* ThreadCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadCollection.h; path = include/lldb/Target/ThreadCollection.h; sourceTree = "<group>"; }; 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SBThreadCollection.cpp; path = source/API/SBThreadCollection.cpp; sourceTree = "<group>"; }; @@ -5912,6 +5918,15 @@ path = "gdb-remote"; sourceTree = "<group>"; }; + 54067BEA1DF2033700749AA5 /* UndefinedBehaviorSanitizer */ = { + isa = PBXGroup; + children = ( + 54067BEC1DF2034B00749AA5 /* UndefinedBehaviorSanitizerRuntime.cpp */, + 54067BED1DF2034B00749AA5 /* UndefinedBehaviorSanitizerRuntime.h */, + ); + name = UndefinedBehaviorSanitizer; + sourceTree = "<group>"; + }; 69A01E1A1236C5D400C660B5 /* common */ = { isa = PBXGroup; children = ( @@ -6011,11 +6026,22 @@ path = asan; sourceTree = "<group>"; }; + 8C3BD9911EF45D9B0016C343 /* MainThreadChecker */ = { + isa = PBXGroup; + children = ( + 8C3BD9931EF45D9B0016C343 /* MainThreadCheckerRuntime.h */, + 8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */, + ); + path = MainThreadChecker; + sourceTree = "<group>"; + }; 8CF02ADD19DCBEC200B14BE0 /* InstrumentationRuntime */ = { isa = PBXGroup; children = ( + 54067BEA1DF2033700749AA5 /* UndefinedBehaviorSanitizer */, 8C26C4221C3EA4050031DF7C /* ThreadSanitizer */, 8CF02ADE19DCBEE600B14BE0 /* AddressSanitizer */, + 8C3BD9911EF45D9B0016C343 /* MainThreadChecker */, ); path = InstrumentationRuntime; sourceTree = "<group>"; @@ -7133,6 +7159,7 @@ 2689001213353DDE00698AC0 /* CommandObjectApropos.cpp in Sources */, 4C88BC2A1BA3722B00AA0964 /* Expression.cpp in Sources */, AE44FB4C1BB4BB540033EB62 /* GoFormatterFunctions.cpp in Sources */, + 8C3BD9961EF45DA50016C343 /* MainThreadCheckerRuntime.cpp in Sources */, 23042D121976CA1D00621B2C /* PlatformKalimba.cpp in Sources */, 2689001313353DDE00698AC0 /* CommandObjectArgs.cpp in Sources */, 2689001413353DDE00698AC0 /* CommandObjectBreakpoint.cpp in Sources */, @@ -7623,6 +7650,7 @@ 26954EBE1401EE8B00294D09 /* DynamicRegisterInfo.cpp in Sources */, 6D9AB3DD1BB2B74E003F2289 /* TypeMap.cpp in Sources */, 255EFF761AFABA950069F277 /* LockFilePosix.cpp in Sources */, + 54067BF11DF2041B00749AA5 /* UndefinedBehaviorSanitizerRuntime.cpp in Sources */, 3FBA69EC1B6067430008F44A /* PythonDataObjects.cpp in Sources */, 26274FA714030F79006BA130 /* DynamicLoaderDarwinKernel.cpp in Sources */, 94FA3DE01405D50400833217 /* ValueObjectConstResultChild.cpp in Sources */, diff --git a/packages/Python/lldbsuite/test/decorators.py b/packages/Python/lldbsuite/test/decorators.py index 866923607867..2a36ee4b3707 100644 --- a/packages/Python/lldbsuite/test/decorators.py +++ b/packages/Python/lldbsuite/test/decorators.py @@ -681,6 +681,53 @@ def skipUnlessThreadSanitizer(func): return None return skipTestIfFn(is_compiler_clang_with_thread_sanitizer)(func) +def skipUnlessUndefinedBehaviorSanitizer(func): + """Decorate the item to skip test unless -fsanitize=undefined is supported.""" + + def is_compiler_clang_with_ubsan(self): + # Write out a temp file which exhibits UB. + inputf = tempfile.NamedTemporaryFile(suffix='.c') + inputf.write('int main() { int x = 0; return x / x; }\n') + inputf.flush() + + # We need to write out the object into a named temp file for inspection. + outputf = tempfile.NamedTemporaryFile() + + # Try to compile with ubsan turned on. + cmd = '%s -fsanitize=undefined %s -o %s' % (self.getCompiler(), inputf.name, outputf.name) + if os.popen(cmd).close() is not None: + return "Compiler cannot compile with -fsanitize=undefined" + + # Check that we actually see ubsan instrumentation in the binary. + cmd = 'nm %s' % outputf.name + with os.popen(cmd) as nm_output: + if '___ubsan_handle_divrem_overflow' not in nm_output.read(): + return "Division by zero instrumentation is missing" + + # Find the ubsan dylib. + # FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report. + cmd = '%s -fsanitize=undefined -x c - -o - -### 2>&1' % self.getCompiler() + with os.popen(cmd) as cc_output: + driver_jobs = cc_output.read() + m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs) + if not m: + return "Could not find the ubsan dylib used by the driver" + ubsan_dylib = m.group(1) + + # Check that the ubsan dylib has special monitor hooks. + cmd = 'nm -gU %s' % ubsan_dylib + with os.popen(cmd) as nm_output: + syms = nm_output.read() + if '___ubsan_on_report' not in syms: + return "Missing ___ubsan_on_report" + if '___ubsan_get_current_report_data' not in syms: + return "Missing ___ubsan_get_current_report_data" + + # OK, this dylib + compiler works for us. + return None + + return skipTestIfFn(is_compiler_clang_with_ubsan)(func) + def skipUnlessAddressSanitizer(func): """Decorate the item to skip test unless Clang -fsanitize=thread is supported.""" diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py index d0a1728b87e6..bc39d8d7b152 100644 --- a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py @@ -186,16 +186,27 @@ class ObjCDataFormatterTestCase(TestBase): def nsnumber_data_formatter_commands(self): # Now enable AppKit and check we are displaying Cocoa classes correctly - self.expect('frame variable num1 num2 num3 num4 num5 num6 num7 num9', + self.expect('frame variable num1 num2 num3 num5 num6 num7 num9', substrs=['(NSNumber *) num1 = ', ' (int)5', '(NSNumber *) num2 = ', ' (float)3.1', '(NSNumber *) num3 = ', ' (double)3.14', - '(NSNumber *) num4 = ', ' (long)-2', '(NSNumber *) num5 = ', ' (char)65', '(NSNumber *) num6 = ', ' (long)255', '(NSNumber *) num7 = ', '2000000', '(NSNumber *) num9 = ', ' (short)-31616']) + + self.runCmd('frame variable num4', check=True) + output = self.res.GetOutput() + i128_handled_correctly = False + + if output.find('long') >= 0: + i128_handled_correctly = (output.find('(long)-2') >= 0) + if output.find('int128_t') >= 0: + i128_handled_correctly = (output.find('(int128_t)18446744073709551614') >= 0) # deliberately broken, should be ..14 + + self.assertTrue(i128_handled_correctly, "Expected valid output for int128_t; got " + output) + self.expect('frame variable num_at1 num_at2 num_at3 num_at4', substrs=['(NSNumber *) num_at1 = ', ' (int)12', '(NSNumber *) num_at2 = ', ' (int)-12', diff --git a/packages/Python/lldbsuite/test/functionalities/ubsan/basic/Makefile b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/Makefile new file mode 100644 index 000000000000..6e7d19b6f48c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS := -fsanitize=undefined -g + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/ubsan/basic/TestUbsanBasic.py b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/TestUbsanBasic.py new file mode 100644 index 000000000000..8dcee97e32f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/TestUbsanBasic.py @@ -0,0 +1,90 @@ +""" +Tests basic UndefinedBehaviorSanitizer support (detecting an alignment error). +""" + +import os +import time +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil +import json + + +class UbsanBasicTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessUndefinedBehaviorSanitizer + def test(self): + self.build() + self.ubsan_tests() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line_align = line_number('main.c', '// align line') + + def ubsan_tests(self): + # Load the test + exe = os.path.join(os.getcwd(), "a.out") + self.expect( + "file " + exe, + patterns=["Current executable set to .*a.out"]) + + self.runCmd("run") + + process = self.dbg.GetSelectedTarget().process + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + + # the stop reason of the thread should be breakpoint. + self.expect("thread list", "A ubsan issue should be detected", + substrs=['stopped', 'stop reason =']) + + stop_reason = thread.GetStopReason() + self.assertEqual(stop_reason, lldb.eStopReasonInstrumentation) + + # test that the UBSan dylib is present + self.expect( + "image lookup -n __ubsan_on_report", + "__ubsan_on_report should be present", + substrs=['1 match found']) + + # We should be stopped in __ubsan_on_report + self.assertTrue("__ubsan_on_report" in frame.GetFunctionName()) + + # The stopped thread backtrace should contain either 'align line' + found = False + for i in range(thread.GetNumFrames()): + frame = thread.GetFrameAtIndex(i) + if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c": + if frame.GetLineEntry().GetLine() == self.line_align: + found = True + self.assertTrue(found) + + backtraces = thread.GetStopReasonExtendedBacktraces( + lldb.eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer) + self.assertTrue(backtraces.GetSize() == 1) + + self.expect( + "thread info -s", + "The extended stop info should contain the UBSan provided fields", + substrs=[ + "instrumentation_class", + "memory_address", + "description", + "filename", + "line", + "col"]) + + output_lines = self.res.GetOutput().split('\n') + json_line = '\n'.join(output_lines[2:]) + data = json.loads(json_line) + + self.assertEqual(data["instrumentation_class"], "UndefinedBehaviorSanitizer") + self.assertEqual(data["description"], "misaligned-pointer-use") + self.assertEqual(data["filename"], "main.c") + self.assertEqual(data["line"], self.line_align) + + self.runCmd("continue") diff --git a/packages/Python/lldbsuite/test/functionalities/ubsan/basic/main.c b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/main.c new file mode 100644 index 000000000000..4991fc074d09 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/ubsan/basic/main.c @@ -0,0 +1,4 @@ +int main() { + int data[4]; + return *(int *)(((char *)&data[0]) + 2); // align line +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py index 57f373545380..96c5a33f14b0 100644 --- a/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py +++ b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py @@ -121,8 +121,6 @@ class ObjCNewSyntaxTestCase(TestBase): '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) - @expectedFailureAll( - bugnumber="rdar://32777981") def test_update_dictionary(self): self.runToBreakpoint() @@ -165,8 +163,6 @@ class ObjCNewSyntaxTestCase(TestBase): '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) - @expectedFailureAll( - bugnumber="rdar://32777981") def test_dictionary_literal(self): self.runToBreakpoint() diff --git a/scripts/Python/python-wrapper.swig b/scripts/Python/python-wrapper.swig index a92102e32838..96b8dda80f82 100644 --- a/scripts/Python/python-wrapper.swig +++ b/scripts/Python/python-wrapper.swig @@ -929,7 +929,8 @@ void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton); void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton) { if (baton != Py_None) { SWIG_PYTHON_THREAD_BEGIN_BLOCK; - PyObject_CallFunction(reinterpret_cast<PyObject*>(baton), const_cast<char*>("s"), str); + PyObject *result = PyObject_CallFunction(reinterpret_cast<PyObject*>(baton), const_cast<char*>("s"), str); + Py_XDECREF(result); SWIG_PYTHON_THREAD_END_BLOCK; } } diff --git a/source/API/SBThread.cpp b/source/API/SBThread.cpp index 65ccb465c8da..d9ce6be3f893 100644 --- a/source/API/SBThread.cpp +++ b/source/API/SBThread.cpp @@ -293,10 +293,6 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) { ThreadCollectionSP threads; threads.reset(new ThreadCollection()); - // We currently only support ThreadSanitizer. - if (type != eInstrumentationRuntimeTypeThreadSanitizer) - return threads; - std::unique_lock<std::recursive_mutex> lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); diff --git a/source/API/SystemInitializerFull.cpp b/source/API/SystemInitializerFull.cpp index 6be352567e8b..7be37898c5aa 100644 --- a/source/API/SystemInitializerFull.cpp +++ b/source/API/SystemInitializerFull.cpp @@ -49,8 +49,10 @@ #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" -#include "Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h" -#include "Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h" +#include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" +#include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" +#include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h" +#include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" #include "Plugins/JITLoader/GDB/JITLoaderGDB.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/Go/GoLanguage.h" @@ -310,6 +312,8 @@ void SystemInitializerFull::Initialize() { MemoryHistoryASan::Initialize(); AddressSanitizerRuntime::Initialize(); ThreadSanitizerRuntime::Initialize(); + UndefinedBehaviorSanitizerRuntime::Initialize(); + MainThreadCheckerRuntime::Initialize(); SymbolVendorELF::Initialize(); SymbolFileDWARF::Initialize(); @@ -434,6 +438,8 @@ void SystemInitializerFull::Terminate() { MemoryHistoryASan::Terminate(); AddressSanitizerRuntime::Terminate(); ThreadSanitizerRuntime::Terminate(); + UndefinedBehaviorSanitizerRuntime::Terminate(); + MainThreadCheckerRuntime::Terminate(); SymbolVendorELF::Terminate(); SymbolFileDWARF::Terminate(); SymbolFilePDB::Terminate(); diff --git a/source/Commands/CommandObjectFrame.cpp b/source/Commands/CommandObjectFrame.cpp index 8b981a93ad7f..9e1805f2eed6 100644 --- a/source/Commands/CommandObjectFrame.cpp +++ b/source/Commands/CommandObjectFrame.cpp @@ -655,32 +655,30 @@ protected: if (num_variables > 0) { for (size_t i = 0; i < num_variables; i++) { var_sp = variable_list->GetVariableAtIndex(i); - switch (var_sp->GetScope()) - { - case eValueTypeVariableGlobal: - if (!m_option_variable.show_globals) - continue; - break; - case eValueTypeVariableStatic: - if (!m_option_variable.show_globals) - continue; - break; - case eValueTypeVariableArgument: - if (!m_option_variable.show_args) - continue; - break; - case eValueTypeVariableLocal: - if (!m_option_variable.show_locals) - continue; - break; - default: + switch (var_sp->GetScope()) { + case eValueTypeVariableGlobal: + if (!m_option_variable.show_globals) + continue; + break; + case eValueTypeVariableStatic: + if (!m_option_variable.show_globals) + continue; + break; + case eValueTypeVariableArgument: + if (!m_option_variable.show_args) + continue; + break; + case eValueTypeVariableLocal: + if (!m_option_variable.show_locals) continue; - break; - + break; + default: + continue; + break; } - std::string scope_string; - if (m_option_variable.show_scope) - scope_string = GetScopeString(var_sp).str(); + std::string scope_string; + if (m_option_variable.show_scope) + scope_string = GetScopeString(var_sp).str(); // Use the variable object code to make sure we are // using the same APIs as the public API will be diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp index 3cb1ffab3a0e..194fec8a8798 100644 --- a/source/Core/IOHandler.cpp +++ b/source/Core/IOHandler.cpp @@ -58,7 +58,7 @@ #include "llvm/ADT/StringRef.h" // for StringRef #ifdef _MSC_VER -#include <windows.h> +#include "lldb/Host/windows/windows.h" #endif #include <memory> // for shared_ptr diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index c2e9b8904a0a..4dac0b8f3bf1 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -10,7 +10,7 @@ #include "lldb/Core/Mangled.h" #if defined(_WIN32) -#include <windows.h> +#include "lldb/Host/windows/windows.h" #include <dbghelp.h> #pragma comment(lib, "dbghelp.lib") diff --git a/source/Core/Timer.cpp b/source/Core/Timer.cpp index 6de4516f721d..59c3e13a717e 100644 --- a/source/Core/Timer.cpp +++ b/source/Core/Timer.cpp @@ -38,20 +38,9 @@ static std::mutex &GetFileMutex() { return *g_file_mutex_ptr; } -static void ThreadSpecificCleanup(void *p) { - delete static_cast<TimerStack *>(p); -} - -static TimerStack *GetTimerStackForCurrentThread() { - static lldb::thread_key_t g_key = - Host::ThreadLocalStorageCreate(ThreadSpecificCleanup); - - void *timer_stack = Host::ThreadLocalStorageGet(g_key); - if (timer_stack == NULL) { - Host::ThreadLocalStorageSet(g_key, new TimerStack); - timer_stack = Host::ThreadLocalStorageGet(g_key); - } - return (TimerStack *)timer_stack; +static TimerStack &GetTimerStackForCurrentThread() { + static thread_local TimerStack g_stack; + return g_stack; } Timer::Category::Category(const char *cat) : m_name(cat) { @@ -66,16 +55,14 @@ void Timer::SetQuiet(bool value) { g_quiet = value; } Timer::Timer(Timer::Category &category, const char *format, ...) : m_category(category), m_total_start(std::chrono::steady_clock::now()) { - TimerStack *stack = GetTimerStackForCurrentThread(); - if (!stack) - return; + TimerStack &stack = GetTimerStackForCurrentThread(); - stack->push_back(this); - if (g_quiet && stack->size() <= g_display_depth) { + stack.push_back(this); + if (g_quiet && stack.size() <= g_display_depth) { std::lock_guard<std::mutex> lock(GetFileMutex()); // Indent - ::fprintf(stdout, "%*s", int(stack->size() - 1) * TIMER_INDENT_AMOUNT, ""); + ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, ""); // Print formatted string va_list args; va_start(args, format); @@ -90,26 +77,23 @@ Timer::Timer(Timer::Category &category, const char *format, ...) Timer::~Timer() { using namespace std::chrono; - TimerStack *stack = GetTimerStackForCurrentThread(); - if (!stack) - return; - auto stop_time = steady_clock::now(); auto total_dur = stop_time - m_total_start; auto timer_dur = total_dur - m_child_duration; - if (g_quiet && stack->size() <= g_display_depth) { + TimerStack &stack = GetTimerStackForCurrentThread(); + if (g_quiet && stack.size() <= g_display_depth) { std::lock_guard<std::mutex> lock(GetFileMutex()); ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n", - int(stack->size() - 1) * TIMER_INDENT_AMOUNT, "", + int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "", duration<double>(total_dur).count(), duration<double>(timer_dur).count()); } - assert(stack->back() == this); - stack->pop_back(); - if (!stack->empty()) - stack->back()->ChildDuration(total_dur); + assert(stack.back() == this); + stack.pop_back(); + if (!stack.empty()) + stack.back()->ChildDuration(total_dur); // Keep total results for each category so we can dump results. m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count(); diff --git a/source/Host/CMakeLists.txt b/source/Host/CMakeLists.txt index 2a73c30f8524..b9079ce26a2c 100644 --- a/source/Host/CMakeLists.txt +++ b/source/Host/CMakeLists.txt @@ -90,12 +90,6 @@ else() posix/ProcessLauncherPosixFork.cpp ) - if (NOT (CMAKE_SYSTEM_NAME MATCHES "Android")) - add_host_subdirectory(posix - posix/ProcessLauncherPosix.cpp - ) - endif() - if (CMAKE_SYSTEM_NAME MATCHES "Darwin") include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) add_host_subdirectory(macosx diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp index d78961e5bffc..af0b57248922 100644 --- a/source/Host/common/Host.cpp +++ b/source/Host/common/Host.cpp @@ -68,15 +68,14 @@ #include "lldb/Utility/Status.h" #include "lldb/lldb-private-forward.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #if defined(_WIN32) #include "lldb/Host/windows/ProcessLauncherWindows.h" -#elif defined(__linux__) || defined(__NetBSD__) -#include "lldb/Host/posix/ProcessLauncherPosixFork.h" #else -#include "lldb/Host/posix/ProcessLauncherPosix.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" #endif #if defined(__APPLE__) @@ -407,25 +406,6 @@ const char *Host::GetSignalAsCString(int signo) { #endif -#ifndef _WIN32 - -lldb::thread_key_t -Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) { - pthread_key_t key; - ::pthread_key_create(&key, callback); - return key; -} - -void *Host::ThreadLocalStorageGet(lldb::thread_key_t key) { - return ::pthread_getspecific(key); -} - -void Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) { - ::pthread_setspecific(key, value); -} - -#endif - #if !defined(__APPLE__) // see Host.mm bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) { @@ -602,359 +582,14 @@ Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, return error; } -// LaunchProcessPosixSpawn for Apple, Linux, FreeBSD, NetBSD and other GLIBC -// systems - -#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \ - defined(__GLIBC__) || defined(__NetBSD__) -#if !defined(__ANDROID__) -// this method needs to be visible to macosx/Host.cpp and -// common/Host.cpp. - -short Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { - short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; - -#if defined(__APPLE__) - if (launch_info.GetFlags().Test(eLaunchFlagExec)) - flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag - - if (launch_info.GetFlags().Test(eLaunchFlagDebug)) - flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag - - if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) - flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag - - if (launch_info.GetLaunchInSeparateProcessGroup()) - flags |= POSIX_SPAWN_SETPGROUP; - -#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT -#if defined(__APPLE__) && (defined(__x86_64__) || defined(__i386__)) - static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; - if (g_use_close_on_exec_flag == eLazyBoolCalculate) { - g_use_close_on_exec_flag = eLazyBoolNo; - - uint32_t major, minor, update; - if (HostInfo::GetOSVersion(major, minor, update)) { - // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or - // earlier - if (major > 10 || (major == 10 && minor > 7)) { - // Only enable for 10.8 and later OS versions - g_use_close_on_exec_flag = eLazyBoolYes; - } - } - } -#else - static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; -#endif - // Close all files exception those with file actions if this is supported. - if (g_use_close_on_exec_flag == eLazyBoolYes) - flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; -#endif -#endif // #if defined (__APPLE__) - return flags; -} - -Status Host::LaunchProcessPosixSpawn(const char *exe_path, - const ProcessLaunchInfo &launch_info, - lldb::pid_t &pid) { - Status error; - Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | - LIBLLDB_LOG_PROCESS)); - - posix_spawnattr_t attr; - error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX); - - if (error.Fail()) { - LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); - return error; - } - - // Make a quick class that will cleanup the posix spawn attributes in case - // we return in the middle of this function. - lldb_utility::CleanUp<posix_spawnattr_t *, int> posix_spawnattr_cleanup( - &attr, posix_spawnattr_destroy); - - sigset_t no_signals; - sigset_t all_signals; - sigemptyset(&no_signals); - sigfillset(&all_signals); - ::posix_spawnattr_setsigmask(&attr, &no_signals); -#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) - ::posix_spawnattr_setsigdefault(&attr, &no_signals); -#else - ::posix_spawnattr_setsigdefault(&attr, &all_signals); -#endif - - short flags = GetPosixspawnFlags(launch_info); - - error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX); - if (error.Fail()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", - error, flags); - return error; - } - -// posix_spawnattr_setbinpref_np appears to be an Apple extension per: -// http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ -#if defined(__APPLE__) && !defined(__arm__) - - // Don't set the binpref if a shell was provided. After all, that's only - // going to affect what version of the shell - // is launched, not what fork of the binary is launched. We insert "arch - // --arch <ARCH> as part of the shell invocation - // to do that job on OSX. - - if (launch_info.GetShell() == nullptr) { - // We don't need to do this for ARM, and we really shouldn't now that we - // have multiple CPU subtypes and no posix_spawnattr call that allows us - // to set which CPU subtype to launch... - const ArchSpec &arch_spec = launch_info.GetArchitecture(); - cpu_type_t cpu = arch_spec.GetMachOCPUType(); - cpu_type_t sub = arch_spec.GetMachOCPUSubType(); - if (cpu != 0 && cpu != static_cast<cpu_type_t>(UINT32_MAX) && - cpu != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE) && - !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try - // to set the CPU type or we will fail - { - size_t ocount = 0; - error.SetError(::posix_spawnattr_setbinpref_np(&attr, 1, &cpu, &ocount), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " - "cpu_type = {1:x}, count => {2} )", - error, cpu, ocount); - - if (error.Fail() || ocount != 1) - return error; - } - } - -#endif - - const char *tmp_argv[2]; - char *const *argv = const_cast<char *const *>( - launch_info.GetArguments().GetConstArgumentVector()); - char *const *envp = const_cast<char *const *>( - launch_info.GetEnvironmentEntries().GetConstArgumentVector()); - if (argv == NULL) { - // posix_spawn gets very unhappy if it doesn't have at least the program - // name in argv[0]. One of the side affects I have noticed is the - // environment - // variables don't make it into the child process if "argv == NULL"!!! - tmp_argv[0] = exe_path; - tmp_argv[1] = NULL; - argv = const_cast<char *const *>(tmp_argv); - } - +// The functions below implement process launching for non-Apple-based platforms #if !defined(__APPLE__) - // manage the working directory - char current_dir[PATH_MAX]; - current_dir[0] = '\0'; -#endif - - FileSpec working_dir{launch_info.GetWorkingDirectory()}; - if (working_dir) { -#if defined(__APPLE__) - // Set the working directory on this thread only - if (__pthread_chdir(working_dir.GetCString()) < 0) { - if (errno == ENOENT) { - error.SetErrorStringWithFormat("No such file or directory: %s", - working_dir.GetCString()); - } else if (errno == ENOTDIR) { - error.SetErrorStringWithFormat("Path doesn't name a directory: %s", - working_dir.GetCString()); - } else { - error.SetErrorStringWithFormat("An unknown error occurred when " - "changing directory for process " - "execution."); - } - return error; - } -#else - if (::getcwd(current_dir, sizeof(current_dir)) == NULL) { - error.SetError(errno, eErrorTypePOSIX); - LLDB_LOG(log, "error: {0}, unable to save the current directory", error); - return error; - } - - if (::chdir(working_dir.GetCString()) == -1) { - error.SetError(errno, eErrorTypePOSIX); - LLDB_LOG(log, "error: {0}, unable to change working directory to {1}", - error, working_dir); - return error; - } -#endif - } - - ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; - const size_t num_file_actions = launch_info.GetNumFileActions(); - if (num_file_actions > 0) { - posix_spawn_file_actions_t file_actions; - error.SetError(::posix_spawn_file_actions_init(&file_actions), - eErrorTypePOSIX); - if (error.Fail()) { - LLDB_LOG(log, - "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", - error); - return error; - } - - // Make a quick class that will cleanup the posix spawn attributes in case - // we return in the middle of this function. - lldb_utility::CleanUp<posix_spawn_file_actions_t *, int> - posix_spawn_file_actions_cleanup(&file_actions, - posix_spawn_file_actions_destroy); - - for (size_t i = 0; i < num_file_actions; ++i) { - const FileAction *launch_file_action = - launch_info.GetFileActionAtIndex(i); - if (launch_file_action) { - if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, - error)) - return error; - } - } - - error.SetError( - ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), - eErrorTypePOSIX); - - if (error.Fail()) { - LLDB_LOG(log, "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " - "file_actions = {3}, " - "attr = {4}, argv = {5}, envp = {6} )", - error, result_pid, exe_path, &file_actions, &attr, argv, envp); - if (log) { - for (int ii = 0; argv[ii]; ++ii) - LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); - } - } - - } else { - error.SetError( - ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), - eErrorTypePOSIX); - - if (error.Fail()) { - LLDB_LOG(log, "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " - "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", - error, result_pid, exe_path, &attr, argv, envp); - if (log) { - for (int ii = 0; argv[ii]; ++ii) - LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); - } - } - } - pid = result_pid; - - if (working_dir) { -#if defined(__APPLE__) - // No more thread specific current working directory - __pthread_fchdir(-1); -#else - if (::chdir(current_dir) == -1 && error.Success()) { - error.SetError(errno, eErrorTypePOSIX); - LLDB_LOG(log, - "error: {0}, unable to change current directory back to {1}", - error, current_dir); - } -#endif - } - - return error; -} - -bool Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, - Log *log, Status &error) { - if (info == NULL) - return false; - - posix_spawn_file_actions_t *file_actions = - reinterpret_cast<posix_spawn_file_actions_t *>(_file_actions); - - switch (info->GetAction()) { - case FileAction::eFileActionNone: - error.Clear(); - break; - - case FileAction::eFileActionClose: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd for posix_spawn_file_actions_addclose(...)"); - else { - error.SetError( - ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, "error: {0}, posix_spawn_file_actions_addclose " - "(action={1}, fd={2})", - error, file_actions, info->GetFD()); - } - break; - - case FileAction::eFileActionDuplicate: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd for posix_spawn_file_actions_adddup2(...)"); - else if (info->GetActionArgument() == -1) - error.SetErrorString( - "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); - else { - error.SetError( - ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), - info->GetActionArgument()), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG(log, "error: {0}, posix_spawn_file_actions_adddup2 " - "(action={1}, fd={2}, dup_fd={3})", - error, file_actions, info->GetFD(), info->GetActionArgument()); - } - break; - - case FileAction::eFileActionOpen: - if (info->GetFD() == -1) - error.SetErrorString( - "invalid fd in posix_spawn_file_actions_addopen(...)"); - else { - int oflag = info->GetActionArgument(); - - mode_t mode = 0; - - if (oflag & O_CREAT) - mode = 0640; - - error.SetError(::posix_spawn_file_actions_addopen( - file_actions, info->GetFD(), - info->GetPath().str().c_str(), oflag, mode), - eErrorTypePOSIX); - if (error.Fail()) - LLDB_LOG( - log, "error: {0}, posix_spawn_file_actions_addopen (action={1}, " - "fd={2}, path='{3}', oflag={4}, mode={5})", - error, file_actions, info->GetFD(), info->GetPath(), oflag, mode); - } - break; - } - return error.Success(); -} -#endif // !defined(__ANDROID__) -#endif // defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || - // defined (__GLIBC__) || defined(__NetBSD__) - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) || \ - defined(__NetBSD__) || defined(_WIN32) -// The functions below implement process launching via posix_spawn() for Linux, -// FreeBSD and NetBSD. - Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { std::unique_ptr<ProcessLauncher> delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); -#elif defined(__linux__) || defined(__NetBSD__) - delegate_launcher.reset(new ProcessLauncherPosixFork()); #else - delegate_launcher.reset(new ProcessLauncherPosix()); + delegate_launcher.reset(new ProcessLauncherPosixFork()); #endif MonitoringProcessLauncher launcher(std::move(delegate_launcher)); @@ -968,7 +603,7 @@ Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { return error; } -#endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) +#endif // !defined(__APPLE__) #ifndef _WIN32 void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); } @@ -988,3 +623,51 @@ const UnixSignalsSP &Host::GetUnixSignals() { UnixSignals::Create(HostInfo::GetArchitecture()); return s_unix_signals_sp; } + +#if defined(LLVM_ON_UNIX) +WaitStatus WaitStatus::Decode(int wstatus) { + if (WIFEXITED(wstatus)) + return {Exit, uint8_t(WEXITSTATUS(wstatus))}; + else if (WIFSIGNALED(wstatus)) + return {Signal, uint8_t(WTERMSIG(wstatus))}; + else if (WIFSTOPPED(wstatus)) + return {Stop, uint8_t(WSTOPSIG(wstatus))}; + llvm_unreachable("Unknown wait status"); +} +#endif + +void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS, + raw_ostream &OS, + StringRef Options) { + if (Options == "g") { + char type; + switch (WS.type) { + case WaitStatus::Exit: + type = 'W'; + break; + case WaitStatus::Signal: + type = 'X'; + break; + case WaitStatus::Stop: + type = 'S'; + break; + } + OS << formatv("{0}{1:x-2}", type, WS.status); + return; + } + + assert(Options.empty()); + const char *desc; + switch(WS.type) { + case WaitStatus::Exit: + desc = "Exited with status"; + break; + case WaitStatus::Signal: + desc = "Killed by signal"; + break; + case WaitStatus::Stop: + desc = "Stopped by signal"; + break; + } + OS << desc << " " << int(WS.status); +} diff --git a/source/Host/common/NativeProcessProtocol.cpp b/source/Host/common/NativeProcessProtocol.cpp index 6f1a9f895b61..341c301dc9c5 100644 --- a/source/Host/common/NativeProcessProtocol.cpp +++ b/source/Host/common/NativeProcessProtocol.cpp @@ -32,7 +32,6 @@ using namespace lldb_private; NativeProcessProtocol::NativeProcessProtocol(lldb::pid_t pid) : m_pid(pid), m_threads(), m_current_thread_id(LLDB_INVALID_THREAD_ID), m_threads_mutex(), m_state(lldb::eStateInvalid), m_state_mutex(), - m_exit_type(eExitTypeInvalid), m_exit_status(0), m_exit_description(), m_delegates_mutex(), m_delegates(), m_breakpoint_list(), m_watchpoint_list(), m_terminal_fd(-1), m_stop_id(0) {} @@ -59,46 +58,29 @@ NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr, return Status("not implemented"); } -bool NativeProcessProtocol::GetExitStatus(ExitType *exit_type, int *status, - std::string &exit_description) { - if (m_state == lldb::eStateExited) { - *exit_type = m_exit_type; - *status = m_exit_status; - exit_description = m_exit_description; - return true; - } +llvm::Optional<WaitStatus> NativeProcessProtocol::GetExitStatus() { + if (m_state == lldb::eStateExited) + return m_exit_status; - *status = 0; - return false; + return llvm::None; } -bool NativeProcessProtocol::SetExitStatus(ExitType exit_type, int status, - const char *exit_description, +bool NativeProcessProtocol::SetExitStatus(WaitStatus status, bool bNotifyStateChange) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf("NativeProcessProtocol::%s(%d, %d, %s, %s) called", - __FUNCTION__, exit_type, status, - exit_description ? exit_description : "nullptr", - bNotifyStateChange ? "true" : "false"); + LLDB_LOG(log, "status = {0}, notify = {1}", status, bNotifyStateChange); // Exit status already set if (m_state == lldb::eStateExited) { - if (log) - log->Printf("NativeProcessProtocol::%s exit status already set to %d, " - "ignoring new set to %d", - __FUNCTION__, m_exit_status, status); + if (m_exit_status) + LLDB_LOG(log, "exit status already set to {0}", *m_exit_status); + else + LLDB_LOG(log, "state is exited, but status not set"); return false; } m_state = lldb::eStateExited; - - m_exit_type = exit_type; m_exit_status = status; - if (exit_description && exit_description[0]) - m_exit_description = exit_description; - else - m_exit_description.clear(); if (bNotifyStateChange) SynchronouslyNotifyProcessStateChanged(lldb::eStateExited); diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm index be205f953862..919b35624e60 100644 --- a/source/Host/macosx/Host.mm +++ b/source/Host/macosx/Host.mm @@ -1024,6 +1024,47 @@ static Status getXPCAuthorization(ProcessLaunchInfo &launch_info) { } #endif +static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { + short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + + if (launch_info.GetFlags().Test(eLaunchFlagExec)) + flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) + flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) + flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag + + if (launch_info.GetLaunchInSeparateProcessGroup()) + flags |= POSIX_SPAWN_SETPGROUP; + +#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT +#if defined(__x86_64__) || defined(__i386__) + static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; + if (g_use_close_on_exec_flag == eLazyBoolCalculate) { + g_use_close_on_exec_flag = eLazyBoolNo; + + uint32_t major, minor, update; + if (HostInfo::GetOSVersion(major, minor, update)) { + // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or + // earlier + if (major > 10 || (major == 10 && minor > 7)) { + // Only enable for 10.8 and later OS versions + g_use_close_on_exec_flag = eLazyBoolYes; + } + } + } +#else + static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; +#endif // defined(__x86_64__) || defined(__i386__) + // Close all files exception those with file actions if this is supported. + if (g_use_close_on_exec_flag == eLazyBoolYes) + flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; +#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT + return flags; +} + static Status LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid) { @@ -1107,7 +1148,7 @@ static Status LaunchProcessXPC(const char *exe_path, xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, - Host::GetPosixspawnFlags(launch_info)); + GetPosixspawnFlags(launch_info)); const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (file_action && !file_action->GetPath().empty()) { xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, @@ -1161,6 +1202,262 @@ static Status LaunchProcessXPC(const char *exe_path, #endif } +static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, + Log *log, Status &error) { + if (info == NULL) + return false; + + posix_spawn_file_actions_t *file_actions = + reinterpret_cast<posix_spawn_file_actions_t *>(_file_actions); + + switch (info->GetAction()) { + case FileAction::eFileActionNone: + error.Clear(); + break; + + case FileAction::eFileActionClose: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd for posix_spawn_file_actions_addclose(...)"); + else { + error.SetError( + ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_addclose " + "(action={1}, fd={2})", + error, file_actions, info->GetFD()); + } + break; + + case FileAction::eFileActionDuplicate: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd for posix_spawn_file_actions_adddup2(...)"); + else if (info->GetActionArgument() == -1) + error.SetErrorString( + "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); + else { + error.SetError( + ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), + info->GetActionArgument()), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_adddup2 " + "(action={1}, fd={2}, dup_fd={3})", + error, file_actions, info->GetFD(), info->GetActionArgument()); + } + break; + + case FileAction::eFileActionOpen: + if (info->GetFD() == -1) + error.SetErrorString( + "invalid fd in posix_spawn_file_actions_addopen(...)"); + else { + int oflag = info->GetActionArgument(); + + mode_t mode = 0; + + if (oflag & O_CREAT) + mode = 0640; + + error.SetError(::posix_spawn_file_actions_addopen( + file_actions, info->GetFD(), + info->GetPath().str().c_str(), oflag, mode), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_addopen (action={1}, " + "fd={2}, path='{3}', oflag={4}, mode={5})", + error, file_actions, info->GetFD(), info->GetPath(), oflag, + mode); + } + break; + } + return error.Success(); +} + +static Status LaunchProcessPosixSpawn(const char *exe_path, + const ProcessLaunchInfo &launch_info, + lldb::pid_t &pid) { + Status error; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | + LIBLLDB_LOG_PROCESS)); + + posix_spawnattr_t attr; + error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX); + + if (error.Fail()) { + LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); + return error; + } + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp<posix_spawnattr_t *, int> posix_spawnattr_cleanup( + &attr, posix_spawnattr_destroy); + + sigset_t no_signals; + sigset_t all_signals; + sigemptyset(&no_signals); + sigfillset(&all_signals); + ::posix_spawnattr_setsigmask(&attr, &no_signals); + ::posix_spawnattr_setsigdefault(&attr, &all_signals); + + short flags = GetPosixspawnFlags(launch_info); + + error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX); + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", + error, flags); + return error; + } + +// posix_spawnattr_setbinpref_np appears to be an Apple extension per: +// http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ +#if !defined(__arm__) + + // Don't set the binpref if a shell was provided. After all, that's only + // going to affect what version of the shell + // is launched, not what fork of the binary is launched. We insert "arch + // --arch <ARCH> as part of the shell invocation + // to do that job on OSX. + + if (launch_info.GetShell() == nullptr) { + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + cpu_type_t cpu = arch_spec.GetMachOCPUType(); + cpu_type_t sub = arch_spec.GetMachOCPUSubType(); + if (cpu != 0 && cpu != static_cast<cpu_type_t>(UINT32_MAX) && + cpu != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE) && + !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try + // to set the CPU type or we will fail + { + size_t ocount = 0; + error.SetError(::posix_spawnattr_setbinpref_np(&attr, 1, &cpu, &ocount), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " + "cpu_type = {1:x}, count => {2} )", + error, cpu, ocount); + + if (error.Fail() || ocount != 1) + return error; + } + } +#endif // !defined(__arm__) + + const char *tmp_argv[2]; + char *const *argv = const_cast<char *const *>( + launch_info.GetArguments().GetConstArgumentVector()); + char *const *envp = const_cast<char *const *>( + launch_info.GetEnvironmentEntries().GetConstArgumentVector()); + if (argv == NULL) { + // posix_spawn gets very unhappy if it doesn't have at least the program + // name in argv[0]. One of the side affects I have noticed is the + // environment + // variables don't make it into the child process if "argv == NULL"!!! + tmp_argv[0] = exe_path; + tmp_argv[1] = NULL; + argv = const_cast<char *const *>(tmp_argv); + } + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) { + // Set the working directory on this thread only + if (__pthread_chdir(working_dir.GetCString()) < 0) { + if (errno == ENOENT) { + error.SetErrorStringWithFormat("No such file or directory: %s", + working_dir.GetCString()); + } else if (errno == ENOTDIR) { + error.SetErrorStringWithFormat("Path doesn't name a directory: %s", + working_dir.GetCString()); + } else { + error.SetErrorStringWithFormat("An unknown error occurred when " + "changing directory for process " + "execution."); + } + return error; + } + } + + ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; + const size_t num_file_actions = launch_info.GetNumFileActions(); + if (num_file_actions > 0) { + posix_spawn_file_actions_t file_actions; + error.SetError(::posix_spawn_file_actions_init(&file_actions), + eErrorTypePOSIX); + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", + error); + return error; + } + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp<posix_spawn_file_actions_t *, int> + posix_spawn_file_actions_cleanup(&file_actions, + posix_spawn_file_actions_destroy); + + for (size_t i = 0; i < num_file_actions; ++i) { + const FileAction *launch_file_action = + launch_info.GetFileActionAtIndex(i); + if (launch_file_action) { + if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, + error)) + return error; + } + } + + error.SetError( + ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), + eErrorTypePOSIX); + + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " + "file_actions = {3}, " + "attr = {4}, argv = {5}, envp = {6} )", + error, result_pid, exe_path, &file_actions, &attr, argv, envp); + if (log) { + for (int ii = 0; argv[ii]; ++ii) + LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); + } + } + + } else { + error.SetError( + ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), + eErrorTypePOSIX); + + if (error.Fail()) { + LLDB_LOG(log, + "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " + "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", + error, result_pid, exe_path, &attr, argv, envp); + if (log) { + for (int ii = 0; argv[ii]; ++ii) + LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); + } + } + } + pid = result_pid; + + if (working_dir) { + // No more thread specific current working directory + __pthread_fchdir(-1); + } + + return error; +} + static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { bool result = false; diff --git a/source/Host/posix/ProcessLauncherPosix.cpp b/source/Host/posix/ProcessLauncherPosix.cpp deleted file mode 100644 index 6d07be1eec64..000000000000 --- a/source/Host/posix/ProcessLauncherPosix.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//===-- ProcessLauncherPosix.cpp --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/posix/ProcessLauncherPosix.h" -#include "lldb/Host/Host.h" -#include "lldb/Host/HostProcess.h" - -#include "lldb/Target/ProcessLaunchInfo.h" - -#include <limits.h> - -using namespace lldb; -using namespace lldb_private; - -HostProcess -ProcessLauncherPosix::LaunchProcess(const ProcessLaunchInfo &launch_info, - Status &error) { - lldb::pid_t pid; - char exe_path[PATH_MAX]; - - launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); - - // TODO(zturner): Move the code from LaunchProcessPosixSpawn to here, and make - // MacOSX re-use this - // ProcessLauncher when it wants a posix_spawn launch. - error = Host::LaunchProcessPosixSpawn(exe_path, launch_info, pid); - return HostProcess(pid); -} diff --git a/source/Host/posix/ProcessLauncherPosixFork.cpp b/source/Host/posix/ProcessLauncherPosixFork.cpp index 1eace5cd24cd..0b40c24256ef 100644 --- a/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -52,10 +52,10 @@ static void FixupEnvironment(Args &env) { static void LLVM_ATTRIBUTE_NORETURN ExitWithError(int error_fd, const char *operation) { - std::ostringstream os; - os << operation << " failed: " << strerror(errno); - write(error_fd, os.str().data(), os.str().size()); - close(error_fd); + int err = errno; + llvm::raw_fd_ostream os(error_fd, true); + os << operation << " failed: " << llvm::sys::StrError(err); + os.flush(); _exit(1); } diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp index e1acd23d5c81..69a7c2ef4f74 100644 --- a/source/Host/windows/Host.cpp +++ b/source/Host/windows/Host.cpp @@ -101,19 +101,6 @@ lldb::thread_t Host::GetCurrentThread() { return lldb::thread_t(::GetCurrentThread()); } -lldb::thread_key_t -Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) { - return TlsAlloc(); -} - -void *Host::ThreadLocalStorageGet(lldb::thread_key_t key) { - return ::TlsGetValue(key); -} - -void Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) { - ::TlsSetValue(key, value); -} - void Host::Kill(lldb::pid_t pid, int signo) { TerminateProcess((HANDLE)pid, 1); } diff --git a/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp b/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp index b202c8395776..ce8f8a65e3e3 100644 --- a/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp +++ b/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp @@ -1951,22 +1951,20 @@ bool ABISysV_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { uint32_t lr_reg_num = arm64_dwarf::lr; uint32_t sp_reg_num = arm64_dwarf::sp; - uint32_t pc_reg_num = arm64_dwarf::pc; UnwindPlan::RowSP row(new UnwindPlan::Row); // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0); - // Our previous PC is in the LR - row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); - unwind_plan.AppendRow(row); + unwind_plan.SetReturnAddressRegister(lr_reg_num); // All other registers are the same. unwind_plan.SetSourceName("arm64 at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); return true; } diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 5f56bfc027a4..0092535648bd 100644 --- a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/Section.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" @@ -484,6 +485,27 @@ DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, return thread_plan_sp; } +void DynamicLoaderPOSIXDYLD::LoadVDSO(ModuleList &modules) { + if (m_vdso_base == LLDB_INVALID_ADDRESS) + return; + + FileSpec file("[vdso]", false); + + MemoryRegionInfo info; + Status status = m_process->GetMemoryRegionInfo(m_vdso_base, info); + if (status.Fail()) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + LLDB_LOG(log, "Failed to get vdso region info: {0}", status); + return; + } + + if (ModuleSP module_sp = m_process->ReadModuleFromMemory( + file, m_vdso_base, info.GetRange().GetByteSize())) { + UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_vdso_base, false); + m_process->GetTarget().GetImages().AppendIfNeeded(module_sp); + } +} + void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { DYLDRendezvous::iterator I; DYLDRendezvous::iterator E; @@ -502,14 +524,7 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { // that ourselves here. ModuleSP executable = GetTargetExecutable(); m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); - if (m_vdso_base != LLDB_INVALID_ADDRESS) { - FileSpec file_spec("[vdso]", false); - ModuleSP module_sp = LoadModuleAtAddress(file_spec, LLDB_INVALID_ADDRESS, - m_vdso_base, false); - if (module_sp.get()) { - module_list.Append(module_sp); - } - } + LoadVDSO(module_list); std::vector<FileSpec> module_names; for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h index 86e0311c2919..2e9670587e66 100644 --- a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -17,11 +17,11 @@ // Other libraries and framework includes // Project includes +#include "DYLDRendezvous.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/ModuleList.h" #include "lldb/Target/DynamicLoader.h" -#include "DYLDRendezvous.h" - class AuxVector; class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader { @@ -138,6 +138,8 @@ protected: /// of all dependent modules. virtual void LoadAllCurrentModules(); + void LoadVDSO(lldb_private::ModuleList &modules); + /// Computes a value for m_load_offset returning the computed address on /// success and LLDB_INVALID_ADDRESS on failure. lldb::addr_t ComputeLoadOffset(); diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp b/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp index 91c5d6ce3d60..af242d786a5f 100644 --- a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp +++ b/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp @@ -1,4 +1,4 @@ -//===-- AddressSanitizerRuntime.cpp -----------------------------*- C++ -*-===// +//===-- ASanRuntime.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "AddressSanitizerRuntime.h" +#include "ASanRuntime.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" @@ -308,13 +308,6 @@ void AddressSanitizerRuntime::Activate() { breakpoint->SetBreakpointKind("address-sanitizer-report"); SetBreakpointID(breakpoint->GetID()); - StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile()); - if (stream_sp) { - stream_sp->Printf("AddressSanitizer debugger support is active. Memory " - "error breakpoint has been installed and you can now use " - "the 'memory history' command.\n"); - } - SetActive(true); } diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h b/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h index 9fd21c06f30c..9fd21c06f30c 100644 --- a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h +++ b/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/ASan/CMakeLists.txt index 1adfc6ba5322..dc7464fd1939 100644 --- a/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt +++ b/source/Plugins/InstrumentationRuntime/ASan/CMakeLists.txt @@ -1,5 +1,5 @@ -add_lldb_library(lldbPluginInstrumentationRuntimeAddressSanitizer PLUGIN - AddressSanitizerRuntime.cpp +add_lldb_library(lldbPluginInstrumentationRuntimeASan PLUGIN + ASanRuntime.cpp LINK_LIBS lldbBreakpoint diff --git a/source/Plugins/InstrumentationRuntime/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/CMakeLists.txt index ae7c6e5972d3..55e8752e7424 100644 --- a/source/Plugins/InstrumentationRuntime/CMakeLists.txt +++ b/source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -1,2 +1,4 @@ -add_subdirectory(AddressSanitizer) -add_subdirectory(ThreadSanitizer) +add_subdirectory(ASan) +add_subdirectory(MainThreadChecker) +add_subdirectory(TSan) +add_subdirectory(UBSan) diff --git a/source/Plugins/InstrumentationRuntime/MainThreadChecker/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/MainThreadChecker/CMakeLists.txt new file mode 100644 index 000000000000..440b176b2709 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/MainThreadChecker/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeMainThreadChecker PLUGIN + MainThreadCheckerRuntime.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbExpression + lldbInterpreter + lldbSymbol + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp b/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp new file mode 100644 index 000000000000..3c22b81df7a4 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp @@ -0,0 +1,273 @@ +//===-- MainThreadCheckerRuntime.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MainThreadCheckerRuntime.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegularExpression.h" +#include "Plugins/Process/Utility/HistoryThread.h" + +using namespace lldb; +using namespace lldb_private; + +MainThreadCheckerRuntime::~MainThreadCheckerRuntime() { + Deactivate(); +} + +lldb::InstrumentationRuntimeSP +MainThreadCheckerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) { + return InstrumentationRuntimeSP(new MainThreadCheckerRuntime(process_sp)); +} + +void MainThreadCheckerRuntime::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), "MainThreadChecker instrumentation runtime plugin.", + CreateInstance, GetTypeStatic); +} + +void MainThreadCheckerRuntime::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString MainThreadCheckerRuntime::GetPluginNameStatic() { + return ConstString("MainThreadChecker"); +} + +lldb::InstrumentationRuntimeType MainThreadCheckerRuntime::GetTypeStatic() { + return eInstrumentationRuntimeTypeMainThreadChecker; +} + +const RegularExpression & +MainThreadCheckerRuntime::GetPatternForRuntimeLibrary() { + static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib")); + return regex; +} + +bool MainThreadCheckerRuntime::CheckIfRuntimeIsValid( + const lldb::ModuleSP module_sp) { + static ConstString test_sym("__main_thread_checker_on_report"); + const Symbol *symbol = + module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny); + return symbol != nullptr; +} + +StructuredData::ObjectSP +MainThreadCheckerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) { + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) + return StructuredData::ObjectSP(); + + ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + ModuleSP runtime_module_sp = GetRuntimeModuleSP(); + Target &target = process_sp->GetTarget(); + + if (!frame_sp) + return StructuredData::ObjectSP(); + + RegisterContextSP regctx_sp = frame_sp->GetRegisterContext(); + if (!regctx_sp) + return StructuredData::ObjectSP(); + + const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1"); + if (!reginfo) + return StructuredData::ObjectSP(); + + uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0); + if (!apiname_ptr) + return StructuredData::ObjectSP(); + + std::string apiName = ""; + Status read_error; + target.ReadCStringFromMemory(apiname_ptr, apiName, read_error); + if (read_error.Fail()) + return StructuredData::ObjectSP(); + + std::string className = ""; + std::string selector = ""; + if (apiName.substr(0, 2) == "-[") { + size_t spacePos = apiName.find(" "); + if (spacePos != std::string::npos) { + className = apiName.substr(2, spacePos - 2); + selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2); + } + } + + // Gather the PCs of the user frames in the backtrace. + StructuredData::Array *trace = new StructuredData::Array(); + auto trace_sp = StructuredData::ObjectSP(trace); + StackFrameSP responsible_frame; + for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { + StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I); + Address addr = frame->GetFrameCodeAddress(); + if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime. + continue; + + // The first non-runtime frame is responsible for the bug. + if (!responsible_frame) + responsible_frame = frame; + + // First frame in stacktrace should point to a real PC, not return address. + if (I != 0 && trace->GetSize() == 0) { + addr.Slide(-1); + } + + lldb::addr_t PC = addr.GetLoadAddress(&target); + trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); + } + + auto *d = new StructuredData::Dictionary(); + auto dict_sp = StructuredData::ObjectSP(d); + d->AddStringItem("instrumentation_class", "MainThreadChecker"); + d->AddStringItem("api_name", apiName); + d->AddStringItem("class_name", className); + d->AddStringItem("selector", selector); + d->AddStringItem("description", + apiName + " must be called from main thread only"); + d->AddIntegerItem("tid", thread_sp->GetIndexID()); + d->AddItem("trace", trace_sp); + return dict_sp; +} + +bool MainThreadCheckerRuntime::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; //< false => resume execution. + + MainThreadCheckerRuntime *const instance = + static_cast<MainThreadCheckerRuntime *>(baton); + + ProcessSP process_sp = instance->GetProcessSP(); + ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); + if (!process_sp || !thread_sp || + process_sp != context->exe_ctx_ref.GetProcessSP()) + return false; + + StructuredData::ObjectSP report = + instance->RetrieveReportData(context->exe_ctx_ref); + + if (report) { + std::string description = report->GetAsDictionary() + ->GetValueForKey("description") + ->GetAsString() + ->GetValue(); + thread_sp->SetStopInfo( + InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( + *thread_sp, description, report)); + return true; + } + + return false; +} + +void MainThreadCheckerRuntime::Activate() { + if (IsActive()) + return; + + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) + return; + + ModuleSP runtime_module_sp = GetRuntimeModuleSP(); + + ConstString symbol_name("__main_thread_checker_on_report"); + const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( + symbol_name, eSymbolTypeCode); + + if (symbol == nullptr) + return; + + if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) + return; + + Target &target = process_sp->GetTarget(); + addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); + + if (symbol_address == LLDB_INVALID_ADDRESS) + return; + + Breakpoint *breakpoint = + process_sp->GetTarget() + .CreateBreakpoint(symbol_address, /*internal=*/true, + /*hardware=*/false) + .get(); + breakpoint->SetCallback(MainThreadCheckerRuntime::NotifyBreakpointHit, this, + true); + breakpoint->SetBreakpointKind("main-thread-checker-report"); + SetBreakpointID(breakpoint->GetID()); + + SetActive(true); +} + +void MainThreadCheckerRuntime::Deactivate() { + SetActive(false); + + auto BID = GetBreakpointID(); + if (BID == LLDB_INVALID_BREAK_ID) + return; + + if (ProcessSP process_sp = GetProcessSP()) { + process_sp->GetTarget().RemoveBreakpointByID(BID); + SetBreakpointID(LLDB_INVALID_BREAK_ID); + } +} + +lldb::ThreadCollectionSP +MainThreadCheckerRuntime::GetBacktracesFromExtendedStopInfo( + StructuredData::ObjectSP info) { + ThreadCollectionSP threads; + threads.reset(new ThreadCollection()); + + ProcessSP process_sp = GetProcessSP(); + + if (info->GetObjectForDotSeparatedPath("instrumentation_class") + ->GetStringValue() != "MainThreadChecker") + return threads; + + std::vector<lldb::addr_t> PCs; + auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); + trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { + PCs.push_back(PC->GetAsInteger()->GetValue()); + return true; + }); + + if (PCs.empty()) + return threads; + + StructuredData::ObjectSP thread_id_obj = + info->GetObjectForDotSeparatedPath("tid"); + tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; + + uint32_t stop_id = 0; + bool stop_id_is_valid = false; + HistoryThread *history_thread = + new HistoryThread(*process_sp, tid, PCs, stop_id, stop_id_is_valid); + ThreadSP new_thread_sp(history_thread); + + // Save this in the Process' ExtendedThreadList so a strong pointer + // retains the object + process_sp->GetExtendedThreadList().AddThread(new_thread_sp); + threads->AddThread(new_thread_sp); + + return threads; +} diff --git a/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h b/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h new file mode 100644 index 000000000000..87440a2489e6 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h @@ -0,0 +1,68 @@ +//===-- MainThreadCheckerRuntime.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_MainThreadCheckerRuntime_h_ +#define liblldb_MainThreadCheckerRuntime_h_ + +#include "lldb/Core/StructuredData.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + + class MainThreadCheckerRuntime : public lldb_private::InstrumentationRuntime { + public: + ~MainThreadCheckerRuntime() override; + + static lldb::InstrumentationRuntimeSP + CreateInstance(const lldb::ProcessSP &process_sp); + + static void Initialize(); + + static void Terminate(); + + static lldb_private::ConstString GetPluginNameStatic(); + + static lldb::InstrumentationRuntimeType GetTypeStatic(); + + lldb_private::ConstString GetPluginName() override { + return GetPluginNameStatic(); + } + + virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); } + + uint32_t GetPluginVersion() override { return 1; } + + lldb::ThreadCollectionSP + GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override; + + private: + MainThreadCheckerRuntime(const lldb::ProcessSP &process_sp) + : lldb_private::InstrumentationRuntime(process_sp) {} + + const RegularExpression &GetPatternForRuntimeLibrary() override; + + bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override; + + void Activate() override; + + void Deactivate(); + + static bool NotifyBreakpointHit(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref); + }; + +} // namespace lldb_private + +#endif // liblldb_MainThreadCheckerRuntime_h_ diff --git a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/TSan/CMakeLists.txt index db7c4a2518a1..4dcd34131b8e 100644 --- a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/CMakeLists.txt +++ b/source/Plugins/InstrumentationRuntime/TSan/CMakeLists.txt @@ -1,5 +1,5 @@ -add_lldb_library(lldbPluginInstrumentationRuntimeThreadSanitizer PLUGIN - ThreadSanitizerRuntime.cpp +add_lldb_library(lldbPluginInstrumentationRuntimeTSan PLUGIN + TSanRuntime.cpp LINK_LIBS lldbBreakpoint diff --git a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp b/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp index d7b518982fcf..f60df0463346 100644 --- a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp +++ b/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp @@ -1,4 +1,4 @@ -//===-- ThreadSanitizerRuntime.cpp ------------------------------*- C++ -*-===// +//===-- TSanRuntime.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "ThreadSanitizerRuntime.h" +#include "TSanRuntime.h" #include "Plugins/Process/Utility/HistoryThread.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" @@ -921,11 +921,6 @@ void ThreadSanitizerRuntime::Activate() { breakpoint->SetBreakpointKind("thread-sanitizer-report"); SetBreakpointID(breakpoint->GetID()); - StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile()); - if (stream_sp) { - stream_sp->Printf("ThreadSanitizer debugger support is active.\n"); - } - SetActive(true); } diff --git a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h b/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h index 2a10582b65d2..2a10582b65d2 100644 --- a/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.h +++ b/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h diff --git a/source/Plugins/InstrumentationRuntime/UBSan/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/UBSan/CMakeLists.txt new file mode 100644 index 000000000000..984bf86f83b5 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/UBSan/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeUBSan PLUGIN + UBSanRuntime.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbExpression + lldbInterpreter + lldbSymbol + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp b/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp new file mode 100644 index 000000000000..023af84179aa --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp @@ -0,0 +1,340 @@ +//===-- UBSanRuntime.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UBSanRuntime.h" + +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include <ctype.h> + +using namespace lldb; +using namespace lldb_private; + +UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() { + Deactivate(); +} + +lldb::InstrumentationRuntimeSP +UndefinedBehaviorSanitizerRuntime::CreateInstance( + const lldb::ProcessSP &process_sp) { + return InstrumentationRuntimeSP( + new UndefinedBehaviorSanitizerRuntime(process_sp)); +} + +void UndefinedBehaviorSanitizerRuntime::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + "UndefinedBehaviorSanitizer instrumentation runtime plugin.", + CreateInstance, GetTypeStatic); +} + +void UndefinedBehaviorSanitizerRuntime::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString +UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() { + return ConstString("UndefinedBehaviorSanitizer"); +} + +lldb::InstrumentationRuntimeType +UndefinedBehaviorSanitizerRuntime::GetTypeStatic() { + return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer; +} + +static const char *ub_sanitizer_retrieve_report_data_prefix = R"( +extern "C" { +void +__ubsan_get_current_report_data(const char **OutIssueKind, + const char **OutMessage, const char **OutFilename, unsigned *OutLine, + unsigned *OutCol, char **OutMemoryAddr); +} + +struct data { + const char *issue_kind; + const char *message; + const char *filename; + unsigned line; + unsigned col; + char *memory_addr; +}; +)"; + +static const char *ub_sanitizer_retrieve_report_data_command = R"( +data t; +__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line, + &t.col, &t.memory_addr); +t; +)"; + +static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp, + ProcessSP process_sp, + const std::string &expression_path) { + return return_value_sp->GetValueForExpressionPath(expression_path.c_str()) + ->GetValueAsUnsigned(0); +} + +static std::string RetrieveString(ValueObjectSP return_value_sp, + ProcessSP process_sp, + const std::string &expression_path) { + addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path); + std::string str; + Status error; + process_sp->ReadCStringFromMemory(ptr, str, error); + return str; +} + +StructuredData::ObjectSP UndefinedBehaviorSanitizerRuntime::RetrieveReportData( + ExecutionContextRef exe_ctx_ref) { + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) + return StructuredData::ObjectSP(); + + ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + ModuleSP runtime_module_sp = GetRuntimeModuleSP(); + Target &target = process_sp->GetTarget(); + + if (!frame_sp) + return StructuredData::ObjectSP(); + + StreamFileSP Stream(target.GetDebugger().GetOutputFile()); + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeout(std::chrono::seconds(2)); + options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix); + options.SetAutoApplyFixIts(false); + options.SetLanguage(eLanguageTypeObjC_plus_plus); + + ValueObjectSP main_value; + ExecutionContext exe_ctx; + Status eval_error; + frame_sp->CalculateExecutionContext(exe_ctx); + ExpressionResults result = UserExpression::Evaluate( + exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "", + main_value, eval_error); + if (result != eExpressionCompleted) { + target.GetDebugger().GetAsyncOutputStream()->Printf( + "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n", + eval_error.AsCString()); + return StructuredData::ObjectSP(); + } + + // Gather the PCs of the user frames in the backtrace. + StructuredData::Array *trace = new StructuredData::Array(); + auto trace_sp = StructuredData::ObjectSP(trace); + for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { + const Address FCA = + thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress(); + if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime. + continue; + + lldb::addr_t PC = FCA.GetLoadAddress(&target); + trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); + } + + std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind"); + std::string ErrMessage = RetrieveString(main_value, process_sp, ".message"); + std::string Filename = RetrieveString(main_value, process_sp, ".filename"); + unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line"); + unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col"); + uintptr_t MemoryAddr = + RetrieveUnsigned(main_value, process_sp, ".memory_addr"); + + auto *d = new StructuredData::Dictionary(); + auto dict_sp = StructuredData::ObjectSP(d); + d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer"); + d->AddStringItem("description", IssueKind); + d->AddStringItem("summary", ErrMessage); + d->AddStringItem("filename", Filename); + d->AddIntegerItem("line", Line); + d->AddIntegerItem("col", Col); + d->AddIntegerItem("memory_address", MemoryAddr); + d->AddIntegerItem("tid", thread_sp->GetID()); + d->AddItem("trace", trace_sp); + return dict_sp; +} + +static std::string GetStopReasonDescription(StructuredData::ObjectSP report) { + llvm::StringRef stop_reason_description_ref; + report->GetAsDictionary()->GetValueForKeyAsString("description", + stop_reason_description_ref); + std::string stop_reason_description = stop_reason_description_ref; + + if (!stop_reason_description.size()) { + stop_reason_description = "Undefined behavior detected"; + } else { + stop_reason_description[0] = toupper(stop_reason_description[0]); + for (unsigned I = 1; I < stop_reason_description.size(); ++I) + if (stop_reason_description[I] == '-') + stop_reason_description[I] = ' '; + } + return stop_reason_description; +} + +bool UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; //< false => resume execution. + + UndefinedBehaviorSanitizerRuntime *const instance = + static_cast<UndefinedBehaviorSanitizerRuntime *>(baton); + + ProcessSP process_sp = instance->GetProcessSP(); + ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); + if (!process_sp || !thread_sp || + process_sp != context->exe_ctx_ref.GetProcessSP()) + return false; + + StructuredData::ObjectSP report = + instance->RetrieveReportData(context->exe_ctx_ref); + + if (report) { + thread_sp->SetStopInfo( + InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( + *thread_sp, GetStopReasonDescription(report), report)); + return true; + } + + return false; +} + +const RegularExpression & +UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() { + static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_")); + return regex; +} + +bool UndefinedBehaviorSanitizerRuntime::CheckIfRuntimeIsValid( + const lldb::ModuleSP module_sp) { + static ConstString ubsan_test_sym("__ubsan_on_report"); + const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( + ubsan_test_sym, lldb::eSymbolTypeAny); + return symbol != nullptr; +} + +// FIXME: Factor out all the logic we have in common with the {a,t}san plugins. +void UndefinedBehaviorSanitizerRuntime::Activate() { + if (IsActive()) + return; + + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) + return; + + ModuleSP runtime_module_sp = GetRuntimeModuleSP(); + + ConstString symbol_name("__ubsan_on_report"); + const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( + symbol_name, eSymbolTypeCode); + + if (symbol == nullptr) + return; + + if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) + return; + + Target &target = process_sp->GetTarget(); + addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); + + if (symbol_address == LLDB_INVALID_ADDRESS) + return; + + Breakpoint *breakpoint = + process_sp->GetTarget() + .CreateBreakpoint(symbol_address, /*internal=*/true, + /*hardware=*/false) + .get(); + breakpoint->SetCallback( + UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit, this, true); + breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report"); + SetBreakpointID(breakpoint->GetID()); + + SetActive(true); +} + +void UndefinedBehaviorSanitizerRuntime::Deactivate() { + SetActive(false); + + auto BID = GetBreakpointID(); + if (BID == LLDB_INVALID_BREAK_ID) + return; + + if (ProcessSP process_sp = GetProcessSP()) { + process_sp->GetTarget().RemoveBreakpointByID(BID); + SetBreakpointID(LLDB_INVALID_BREAK_ID); + } +} + +lldb::ThreadCollectionSP +UndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo( + StructuredData::ObjectSP info) { + ThreadCollectionSP threads; + threads.reset(new ThreadCollection()); + + ProcessSP process_sp = GetProcessSP(); + + if (info->GetObjectForDotSeparatedPath("instrumentation_class") + ->GetStringValue() != "UndefinedBehaviorSanitizer") + return threads; + + std::vector<lldb::addr_t> PCs; + auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); + trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { + PCs.push_back(PC->GetAsInteger()->GetValue()); + return true; + }); + + if (PCs.empty()) + return threads; + + StructuredData::ObjectSP thread_id_obj = + info->GetObjectForDotSeparatedPath("tid"); + tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; + + uint32_t stop_id = 0; + bool stop_id_is_valid = false; + HistoryThread *history_thread = + new HistoryThread(*process_sp, tid, PCs, stop_id, stop_id_is_valid); + ThreadSP new_thread_sp(history_thread); + std::string stop_reason_description = GetStopReasonDescription(info); + new_thread_sp->SetName(stop_reason_description.c_str()); + + // Save this in the Process' ExtendedThreadList so a strong pointer + // retains the object + process_sp->GetExtendedThreadList().AddThread(new_thread_sp); + threads->AddThread(new_thread_sp); + + return threads; +} diff --git a/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h b/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h new file mode 100644 index 000000000000..0c478bd57e82 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h @@ -0,0 +1,69 @@ +//===-- UndefinedBehaviorSanitizerRuntime.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_UndefinedBehaviorSanitizerRuntime_h_ +#define liblldb_UndefinedBehaviorSanitizerRuntime_h_ + +#include "lldb/Core/StructuredData.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class UndefinedBehaviorSanitizerRuntime + : public lldb_private::InstrumentationRuntime { +public: + ~UndefinedBehaviorSanitizerRuntime() override; + + static lldb::InstrumentationRuntimeSP + CreateInstance(const lldb::ProcessSP &process_sp); + + static void Initialize(); + + static void Terminate(); + + static lldb_private::ConstString GetPluginNameStatic(); + + static lldb::InstrumentationRuntimeType GetTypeStatic(); + + lldb_private::ConstString GetPluginName() override { + return GetPluginNameStatic(); + } + + virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); } + + uint32_t GetPluginVersion() override { return 1; } + + lldb::ThreadCollectionSP + GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override; + +private: + UndefinedBehaviorSanitizerRuntime(const lldb::ProcessSP &process_sp) + : lldb_private::InstrumentationRuntime(process_sp) {} + + const RegularExpression &GetPatternForRuntimeLibrary() override; + + bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override; + + void Activate() override; + + void Deactivate(); + + static bool NotifyBreakpointHit(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref); +}; + +} // namespace lldb_private + +#endif // liblldb_UndefinedBehaviorSanitizerRuntime_h_ diff --git a/source/Plugins/Language/ObjC/Cocoa.cpp b/source/Plugins/Language/ObjC/Cocoa.cpp index dd3dc434f753..6aaf66cbb486 100644 --- a/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/source/Plugins/Language/ObjC/Cocoa.cpp @@ -31,6 +31,8 @@ #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" +#include "llvm/ADT/APInt.h" + #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" #include "NSString.h" @@ -369,6 +371,28 @@ static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream, stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str()); } +static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream, + const llvm::APInt &value, + lldb::LanguageType lang) { + static ConstString g_TypeHint("NSNumber:int128_t"); + + std::string prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) { + if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, + suffix)) { + prefix.clear(); + suffix.clear(); + } + } + + stream.PutCString(prefix.c_str()); + const int radix = 10; + const bool isSigned = true; + std::string str = value.toString(radix, isSigned); + stream.PutCString(str.c_str()); + stream.PutCString(suffix.c_str()); +} + static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream, float value, lldb::LanguageType lang) { static ConstString g_TypeHint("NSNumber:float"); @@ -462,22 +486,72 @@ bool lldb_private::formatters::NSNumberSummaryProvider( return true; } else { Status error; - uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory( - valobj_addr + ptr_size, 1, 0, error) & - 0x1F); + + AppleObjCRuntime *runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>( + process_sp->GetObjCLanguageRuntime()); + + const bool new_format = + (runtime && runtime->GetFoundationVersion() >= 1400); + + enum class TypeCodes : int { + sint8 = 0x0, + sint16 = 0x1, + sint32 = 0x2, + sint64 = 0x3, + f32 = 0x4, + f64 = 0x5, + sint128 = 0x6 + }; + uint64_t data_location = valobj_addr + 2 * ptr_size; + TypeCodes type_code; + + if (new_format) { + uint64_t cfinfoa = + process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + + if (error.Fail()) + return false; + + bool is_preserved_number = cfinfoa & 0x8; + if (is_preserved_number) { + lldbassert(!"We should handle preserved numbers!"); + return false; + } + + type_code = (TypeCodes)(cfinfoa & 0x7); + } else { + uint8_t data_type = + process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, + 0, error) & 0x1F; + + if (error.Fail()) + return false; + + switch (data_type) { + case 1: type_code = TypeCodes::sint8; break; + case 2: type_code = TypeCodes::sint16; break; + case 3: type_code = TypeCodes::sint32; break; + case 17: data_location += 8; LLVM_FALLTHROUGH; + case 4: type_code = TypeCodes::sint64; break; + case 5: type_code = TypeCodes::f32; break; + case 6: type_code = TypeCodes::f64; break; + default: return false; + } + } + uint64_t value = 0; - if (error.Fail()) - return false; - switch (data_type) { - case 1: // 0B00001 + switch (type_code) { + case TypeCodes::sint8: value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error); if (error.Fail()) return false; NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); break; - case 2: // 0B0010 + case TypeCodes::sint16: value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error); if (error.Fail()) @@ -485,24 +559,21 @@ bool lldb_private::formatters::NSNumberSummaryProvider( NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); break; - case 3: // 0B0011 + case TypeCodes::sint32: value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); if (error.Fail()) return false; NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); break; - case 17: // 0B10001 - data_location += 8; - LLVM_FALLTHROUGH; - case 4: // 0B0100 + case TypeCodes::sint64: value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); if (error.Fail()) return false; NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); break; - case 5: // 0B0101 + case TypeCodes::f32: { uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( data_location, 4, 0, error); @@ -513,7 +584,7 @@ bool lldb_private::formatters::NSNumberSummaryProvider( NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); break; } - case 6: // 0B0110 + case TypeCodes::f64: { uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( data_location, 8, 0, error); @@ -524,6 +595,21 @@ bool lldb_private::formatters::NSNumberSummaryProvider( NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); break; } + case TypeCodes::sint128: // internally, this is the same + { + uint64_t words[2]; + words[1] = process_sp->ReadUnsignedIntegerFromMemory( + data_location, 8, 0, error); + if (error.Fail()) + return false; + words[0] = process_sp->ReadUnsignedIntegerFromMemory( + data_location + 8, 8, 0, error); + if (error.Fail()) + return false; + llvm::APInt i128_value(128, words); + NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage()); + break; + } default: return false; } diff --git a/source/Plugins/Language/ObjC/NSArray.cpp b/source/Plugins/Language/ObjC/NSArray.cpp index b07b9ba5888f..27cb9558c482 100644 --- a/source/Plugins/Language/ObjC/NSArray.cpp +++ b/source/Plugins/Language/ObjC/NSArray.cpp @@ -160,11 +160,47 @@ private: DataDescriptor_64 *m_data_64; }; -class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd { +class NSArrayMSyntheticFrontEnd_1400 : public NSArrayMSyntheticFrontEnd { public: - NSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + NSArrayMSyntheticFrontEnd_1400(lldb::ValueObjectSP valobj_sp); - ~NSArrayISyntheticFrontEnd() override = default; + ~NSArrayMSyntheticFrontEnd_1400() override; + + bool Update() override; + +protected: + lldb::addr_t GetDataAddress() override; + + uint64_t GetUsedCount() override; + + uint64_t GetOffset() override; + + uint64_t GetSize() override; + +private: + struct DataDescriptor_32 { + uint32_t used; + uint32_t offset; + uint32_t size; + uint32_t list; + }; + + struct DataDescriptor_64 { + uint64_t used; + uint64_t offset; + uint64_t size; + uint64_t list; + }; + + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; +}; + +class NSArrayISyntheticFrontEnd_1300 : public SyntheticChildrenFrontEnd { +public: + NSArrayISyntheticFrontEnd_1300(lldb::ValueObjectSP valobj_sp); + + ~NSArrayISyntheticFrontEnd_1300() override = default; size_t CalculateNumChildren() override; @@ -184,6 +220,45 @@ private: CompilerType m_id_type; }; +class NSArrayISyntheticFrontEnd_1400 : public SyntheticChildrenFrontEnd { +public: + NSArrayISyntheticFrontEnd_1400(lldb::ValueObjectSP valobj_sp); + + ~NSArrayISyntheticFrontEnd_1400() override; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(const ConstString &name) override; + +private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + + struct DataDescriptor_32 { + uint32_t used; + uint32_t offset; + uint32_t size; + uint32_t list; + }; + + struct DataDescriptor_64 { + uint64_t used; + uint64_t offset; + uint64_t size; + uint64_t list; + }; + + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + CompilerType m_id_type; +}; + class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); @@ -257,6 +332,8 @@ bool lldb_private::formatters::NSArraySummaryProvider( static const ConstString g_NSArray0("__NSArray0"); static const ConstString g_NSArray1("__NSSingleObjectArrayI"); static const ConstString g_NSArrayCF("__NSCFArray"); + static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy"); + static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable"); if (class_name.IsEmpty()) return false; @@ -273,6 +350,18 @@ bool lldb_private::formatters::NSArraySummaryProvider( ptr_size, 0, error); if (error.Fail()) return false; + } else if (class_name == g_NSArrayMLegacy) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSArrayMImmutable) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; } else if (class_name == g_NSArray0) { value = 0; } else if (class_name == g_NSArray1) { @@ -332,6 +421,11 @@ lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010:: : NSArrayMSyntheticFrontEnd(valobj_sp), m_data_32(nullptr), m_data_64(nullptr) {} +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400:: + NSArrayMSyntheticFrontEnd_1400(lldb::ValueObjectSP valobj_sp) + : NSArrayMSyntheticFrontEnd(valobj_sp), m_data_32(nullptr), + m_data_64(nullptr) {} + size_t lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren() { return GetUsedCount(); @@ -416,6 +510,37 @@ bool lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::Update() { return false; } +bool lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + if (error.Fail()) + return false; + return false; +} + bool lldb_private::formatters::NSArrayMSyntheticFrontEnd::MightHaveChildren() { return true; } @@ -498,7 +623,42 @@ uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_1010::GetSize() { return m_data_32 ? m_data_32->_size : m_data_64->_size; } -lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd( +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400:: + ~NSArrayMSyntheticFrontEnd_1400() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +lldb::addr_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400::GetDataAddress() { + if (!m_data_32 && !m_data_64) + return LLDB_INVALID_ADDRESS; + return m_data_32 ? m_data_32->list : m_data_64->list; +} + +uint64_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400::GetUsedCount() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->used : m_data_64->used; +} + +uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400::GetOffset() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->offset : m_data_64->offset; +} + +uint64_t lldb_private::formatters::NSArrayMSyntheticFrontEnd_1400::GetSize() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->size : m_data_64->size; +} + + +lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::NSArrayISyntheticFrontEnd_1300( lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), m_items(0), m_data_ptr(0) { @@ -516,7 +676,7 @@ lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd( } size_t -lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName( +lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::GetIndexOfChildWithName( const ConstString &name) { const char *item_name = name.GetCString(); uint32_t idx = ExtractIndexFromString(item_name); @@ -526,11 +686,11 @@ lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName( } size_t -lldb_private::formatters::NSArrayISyntheticFrontEnd::CalculateNumChildren() { +lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::CalculateNumChildren() { return m_items; } -bool lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() { +bool lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::Update() { m_ptr_size = 0; m_items = 0; m_data_ptr = 0; @@ -552,12 +712,12 @@ bool lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() { return false; } -bool lldb_private::formatters::NSArrayISyntheticFrontEnd::MightHaveChildren() { +bool lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::MightHaveChildren() { return true; } lldb::ValueObjectSP -lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex( +lldb_private::formatters::NSArrayISyntheticFrontEnd_1300::GetChildAtIndex( size_t idx) { if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); @@ -575,6 +735,99 @@ lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex( m_exe_ctx_ref, m_id_type); } +lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::NSArrayISyntheticFrontEnd_1400( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), + m_data_32(nullptr), m_data_64(nullptr) { + if (valobj_sp) { + CompilerType type = valobj_sp->GetCompilerType(); + if (type) { + ClangASTContext *ast = valobj_sp->GetExecutionContextRef() + .GetTargetSP() + ->GetScratchClangASTContext(); + if (ast) + m_id_type = CompilerType(ast->getASTContext(), + ast->getASTContext()->ObjCBuiltinIdTy); + } + } +} + +lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::~NSArrayISyntheticFrontEnd_1400() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +size_t +lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::GetIndexOfChildWithName( + const ConstString &name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::CalculateNumChildren() { + return m_data_32 ? m_data_32->used : m_data_64->used; +} + +bool lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + if (error.Fail()) + return false; + return false; +} + +bool lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayISyntheticFrontEnd_1400::GetChildAtIndex( + size_t idx) { + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = m_data_32 ? m_data_32->list : m_data_64->list; + object_at_idx += (idx * m_ptr_size); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + if (error.Fail()) + return lldb::ValueObjectSP(); + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx, + m_exe_ctx_ref, m_id_type); +} + lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd( lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp) {} @@ -683,17 +936,24 @@ lldb_private::formatters::NSArraySyntheticFrontEndCreator( static const ConstString g_NSArrayM("__NSArrayM"); static const ConstString g_NSArray0("__NSArray0"); static const ConstString g_NSArray1("__NSSingleObjectArrayI"); + static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy"); + static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable"); if (class_name.IsEmpty()) return nullptr; if (class_name == g_NSArrayI) { - return (new NSArrayISyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() >= 1400) + return (new NSArrayISyntheticFrontEnd_1400(valobj_sp)); + else + return (new NSArrayISyntheticFrontEnd_1300(valobj_sp)); } else if (class_name == g_NSArray0) { return (new NSArray0SyntheticFrontEnd(valobj_sp)); } else if (class_name == g_NSArray1) { return (new NSArray1SyntheticFrontEnd(valobj_sp)); } else if (class_name == g_NSArrayM) { + if (runtime->GetFoundationVersion() >= 1400) + return (new NSArrayMSyntheticFrontEnd_1400(valobj_sp)); if (runtime->GetFoundationVersion() >= 1100) return (new NSArrayMSyntheticFrontEnd_1010(valobj_sp)); else diff --git a/source/Plugins/Language/ObjC/NSDictionary.cpp b/source/Plugins/Language/ObjC/NSDictionary.cpp index 6df83d52acca..50febbe39758 100644 --- a/source/Plugins/Language/ObjC/NSDictionary.cpp +++ b/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -17,6 +17,8 @@ // Project includes #include "NSDictionary.h" +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" + #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/DataFormatters/FormattersHelpers.h" @@ -181,6 +183,52 @@ public: private: struct DataDescriptor_32 { + uint32_t used : 26; + uint32_t kvo : 1; + uint32_t size; + uint32_t buffer; + }; + + struct DataDescriptor_64 { + uint64_t used : 58; + uint32_t kvo : 1; + uint64_t size; + uint64_t buffer; + }; + + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; +}; + +class NSDictionaryMLegacySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSDictionaryMLegacySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSDictionaryMLegacySyntheticFrontEnd() override; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(const ConstString &name) override; + +private: + struct DataDescriptor_32 { uint32_t _used : 26; uint32_t _kvo : 1; uint32_t _size; @@ -250,19 +298,21 @@ bool lldb_private::formatters::NSDictionarySummaryProvider( static const ConstString g_DictionaryI("__NSDictionaryI"); static const ConstString g_DictionaryM("__NSDictionaryM"); + static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); + static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable"); static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); if (class_name.IsEmpty()) return false; - if (class_name == g_DictionaryI) { + if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) { Status error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); if (error.Fail()) return false; value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); - } else if (class_name == g_DictionaryM) { + } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) { Status error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); @@ -311,9 +361,8 @@ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator( lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); if (!process_sp) return nullptr; - ObjCLanguageRuntime *runtime = - (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( - lldb::eLanguageTypeObjC); + AppleObjCRuntime *runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime()); if (!runtime) return nullptr; @@ -338,6 +387,8 @@ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator( static const ConstString g_DictionaryI("__NSDictionaryI"); static const ConstString g_DictionaryM("__NSDictionaryM"); static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); + static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable"); + static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); if (class_name.IsEmpty()) return nullptr; @@ -345,7 +396,13 @@ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator( if (class_name == g_DictionaryI) { return (new NSDictionaryISyntheticFrontEnd(valobj_sp)); } else if (class_name == g_DictionaryM) { - return (new NSDictionaryMSyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() > 1400) { + return (new NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } else { + return (new NSDictionaryMLegacySyntheticFrontEnd(valobj_sp)); + } + } else if (class_name == g_DictionaryMLegacy) { + return (new NSDictionaryMLegacySyntheticFrontEnd(valobj_sp)); } else if (class_name == g_Dictionary1) { return (new NSDictionary1SyntheticFrontEnd(valobj_sp)); } else { @@ -611,7 +668,7 @@ size_t lldb_private::formatters::NSDictionaryMSyntheticFrontEnd:: CalculateNumChildren() { if (!m_data_32 && !m_data_64) return 0; - return (m_data_32 ? m_data_32->_used : m_data_64->_used); + return (m_data_32 ? m_data_32->used : m_data_64->used); } bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::Update() { @@ -655,6 +712,165 @@ bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd:: lldb::ValueObjectSP lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex( size_t idx) { + lldb::addr_t m_keys_ptr; + lldb::addr_t m_values_ptr; + if (m_data_32) { + uint32_t size = m_data_32->size; + m_keys_ptr = m_data_32->buffer; + m_values_ptr = m_data_32->buffer + (m_ptr_size * size); + } else { + uint32_t size = m_data_64->size; + m_keys_ptr = m_data_64->buffer; + m_values_ptr = m_data_64->buffer + (m_ptr_size * size); + } + + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + ; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + + +lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd:: + NSDictionaryMLegacySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), + m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr), + m_pair_type() {} + +lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd:: + ~NSDictionaryMLegacySyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +size_t lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd:: + GetIndexOfChildWithName(const ConstString &name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd:: + CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +bool lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + if (error.Fail()) + return false; + return false; +} + +bool lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::GetChildAtIndex( + size_t idx) { lldb::addr_t m_keys_ptr = (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr); lldb::addr_t m_values_ptr = diff --git a/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/source/Plugins/Language/ObjC/ObjCLanguage.cpp index 4d9227598cef..ea2eec7b33b7 100644 --- a/source/Plugins/Language/ObjC/ObjCLanguage.cpp +++ b/source/Plugins/Language/ObjC/ObjCLanguage.cpp @@ -1026,6 +1026,7 @@ bool ObjCLanguage::GetFormatterPrefixSuffix(ValueObject &valobj, static ConstString g_NSNumberShort("NSNumber:short"); static ConstString g_NSNumberInt("NSNumber:int"); static ConstString g_NSNumberLong("NSNumber:long"); + static ConstString g_NSNumberInt128("NSNumber:int128_t"); static ConstString g_NSNumberFloat("NSNumber:float"); static ConstString g_NSNumberDouble("NSNumber:double"); @@ -1061,6 +1062,10 @@ bool ObjCLanguage::GetFormatterPrefixSuffix(ValueObject &valobj, prefix = "(long)"; return true; } + if (type_hint == g_NSNumberInt128) { + prefix = "(int128_t)"; + return true; + } if (type_hint == g_NSNumberFloat) { prefix = "(float)"; return true; diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index b71d6fa4ebea..20e9a930b486 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -707,10 +707,7 @@ size_t ObjectFileELF::GetModuleSpecifications( SectionHeaderColl section_headers; lldb_private::UUID &uuid = spec.GetUUID(); - using namespace std::placeholders; - const SetDataFunction set_data = - std::bind(&ObjectFileELF::SetData, std::cref(data), _1, _2, _3); - GetSectionHeaderInfo(section_headers, set_data, header, uuid, + GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc, spec.GetArchitecture()); @@ -748,7 +745,7 @@ size_t ObjectFileELF::GetModuleSpecifications( data.SetData(data_sp); } ProgramHeaderColl program_headers; - GetProgramHeaderInfo(program_headers, set_data, header); + GetProgramHeaderInfo(program_headers, data, header); size_t segment_data_end = 0; for (ProgramHeaderCollConstIter I = program_headers.begin(); @@ -950,29 +947,7 @@ size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const { bool ObjectFileELF::ParseHeader() { lldb::offset_t offset = 0; - if (!m_header.Parse(m_data, &offset)) - return false; - - if (!IsInMemory()) - return true; - - // For in memory object files m_data might not contain the full object file. - // Try to load it - // until the end of the "Section header table" what is at the end of the ELF - // file. - addr_t file_size = m_header.e_shoff + m_header.e_shnum * m_header.e_shentsize; - if (m_data.GetByteSize() < file_size) { - ProcessSP process_sp(m_process_wp.lock()); - if (!process_sp) - return false; - - DataBufferSP data_sp = ReadMemory(process_sp, m_memory_addr, file_size); - if (!data_sp) - return false; - m_data.SetData(data_sp, 0, file_size); - } - - return true; + return m_header.Parse(m_data, &offset); } bool ObjectFileELF::GetUUID(lldb_private::UUID *uuid) { @@ -1188,7 +1163,7 @@ size_t ObjectFileELF::ParseDependentModules() { // GetProgramHeaderInfo //---------------------------------------------------------------------- size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, - const SetDataFunction &set_data, + DataExtractor &object_data, const ELFHeader &header) { // We have already parsed the program headers if (!program_headers.empty()) @@ -1205,7 +1180,7 @@ size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, const size_t ph_size = header.e_phnum * header.e_phentsize; const elf_off ph_offset = header.e_phoff; DataExtractor data; - if (set_data(data, ph_offset, ph_size) != ph_size) + if (data.SetData(object_data, ph_offset, ph_size) != ph_size) return 0; uint32_t idx; @@ -1225,12 +1200,7 @@ size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, // ParseProgramHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseProgramHeaders() { - using namespace std::placeholders; - return GetProgramHeaderInfo( - m_program_headers, - std::bind(&ObjectFileELF::SetDataWithReadMemoryFallback, this, _1, _2, - _3), - m_header); + return GetProgramHeaderInfo(m_program_headers, m_data, m_header); } lldb_private::Status @@ -1562,7 +1532,7 @@ void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length, // GetSectionHeaderInfo //---------------------------------------------------------------------- size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, - const SetDataFunction &set_data, + DataExtractor &object_data, const elf::ELFHeader &header, lldb_private::UUID &uuid, std::string &gnu_debuglink_file, @@ -1634,7 +1604,7 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, const size_t sh_size = header.e_shnum * header.e_shentsize; const elf_off sh_offset = header.e_shoff; DataExtractor sh_data; - if (set_data(sh_data, sh_offset, sh_size) != sh_size) + if (sh_data.SetData(object_data, sh_offset, sh_size) != sh_size) return 0; uint32_t idx; @@ -1653,7 +1623,7 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, const Elf64_Off offset = sheader.sh_offset; lldb_private::DataExtractor shstr_data; - if (set_data(shstr_data, offset, byte_size) == byte_size) { + if (shstr_data.SetData(object_data, offset, byte_size) == byte_size) { for (SectionHeaderCollIter I = section_headers.begin(); I != section_headers.end(); ++I) { static ConstString g_sect_name_gnu_debuglink(".gnu_debuglink"); @@ -1669,8 +1639,8 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, DataExtractor data; if (sheader.sh_type == SHT_MIPS_ABIFLAGS) { - if (section_size && (set_data(data, sheader.sh_offset, - section_size) == section_size)) { + if (section_size && (data.SetData(object_data, sheader.sh_offset, + section_size) == section_size)) { // MIPS ASE Mask is at offset 12 in MIPS.abiflags section lldb::offset_t offset = 12; // MIPS ABI Flags Version: 0 arch_flags |= data.GetU32(&offset); @@ -1723,7 +1693,7 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, // ABI Mask doesn't cover N32 and N64 ABI. if (header.e_ident[EI_CLASS] == llvm::ELF::ELFCLASS64) arch_flags |= lldb_private::ArchSpec::eMIPSABI_N64; - else if (header.e_flags && llvm::ELF::EF_MIPS_ABI2) + else if (header.e_flags & llvm::ELF::EF_MIPS_ABI2) arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32; break; } @@ -1735,14 +1705,14 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, DataExtractor data; if (sheader.sh_type == SHT_ARM_ATTRIBUTES && section_size != 0 && - set_data(data, sheader.sh_offset, section_size) == section_size) + data.SetData(object_data, sheader.sh_offset, section_size) == section_size) ParseARMAttributes(data, section_size, arch_spec); } if (name == g_sect_name_gnu_debuglink) { DataExtractor data; - if (section_size && (set_data(data, sheader.sh_offset, - section_size) == section_size)) { + if (section_size && (data.SetData(object_data, sheader.sh_offset, + section_size) == section_size)) { lldb::offset_t gnu_debuglink_offset = 0; gnu_debuglink_file = data.GetCStr(&gnu_debuglink_offset); gnu_debuglink_offset = llvm::alignTo(gnu_debuglink_offset, 4); @@ -1762,8 +1732,8 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, if (is_note_header) { // Allow notes to refine module info. DataExtractor data; - if (section_size && (set_data(data, sheader.sh_offset, - section_size) == section_size)) { + if (section_size && (data.SetData(object_data, sheader.sh_offset, + section_size) == section_size)) { Status error = RefineModuleDetailsFromNote(data, arch_spec, uuid); if (error.Fail()) { if (log) @@ -1819,40 +1789,9 @@ ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { // ParseSectionHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseSectionHeaders() { - using namespace std::placeholders; - - return GetSectionHeaderInfo( - m_section_headers, - std::bind(&ObjectFileELF::SetDataWithReadMemoryFallback, this, _1, _2, - _3), - m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc, m_arch_spec); -} - -lldb::offset_t ObjectFileELF::SetData(const lldb_private::DataExtractor &src, - lldb_private::DataExtractor &dst, - lldb::offset_t offset, - lldb::offset_t length) { - return dst.SetData(src, offset, length); -} - -lldb::offset_t -ObjectFileELF::SetDataWithReadMemoryFallback(lldb_private::DataExtractor &dst, - lldb::offset_t offset, - lldb::offset_t length) { - if (offset + length <= m_data.GetByteSize()) - return dst.SetData(m_data, offset, length); - - const auto process_sp = m_process_wp.lock(); - if (process_sp != nullptr) { - addr_t file_size = offset + length; - - DataBufferSP data_sp = ReadMemory(process_sp, m_memory_addr, file_size); - if (!data_sp) - return false; - m_data.SetData(data_sp, 0, file_size); - } - - return dst.SetData(m_data, offset, length); + return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, + m_gnu_debuglink_file, m_gnu_debuglink_crc, + m_arch_spec); } const ObjectFileELF::ELFSectionHeaderInfo * diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index 06f1a4d22d2d..6d8717b0ef25 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -14,7 +14,6 @@ #include <stdint.h> // C++ Includes -#include <functional> #include <vector> // Other libraries and framework includes @@ -182,9 +181,6 @@ private: typedef std::map<lldb::addr_t, lldb::AddressClass> FileAddressToAddressClassMap; - typedef std::function<lldb::offset_t(lldb_private::DataExtractor &, - lldb::offset_t, lldb::offset_t)> - SetDataFunction; /// Version of this reader common to all plugins based on this class. static const uint32_t m_plugin_version = 1; @@ -230,7 +226,7 @@ private: // Parses the ELF program headers. static size_t GetProgramHeaderInfo(ProgramHeaderColl &program_headers, - const SetDataFunction &set_data, + lldb_private::DataExtractor &object_data, const elf::ELFHeader &header); // Finds PT_NOTE segments and calculates their crc sum. @@ -255,7 +251,7 @@ private: /// Parses the elf section headers and returns the uuid, debug link name, crc, /// archspec. static size_t GetSectionHeaderInfo(SectionHeaderColl §ion_headers, - const SetDataFunction &set_data, + lldb_private::DataExtractor &object_data, const elf::ELFHeader &header, lldb_private::UUID &uuid, std::string &gnu_debuglink_file, @@ -379,14 +375,6 @@ private: RefineModuleDetailsFromNote(lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch_spec, lldb_private::UUID &uuid); - - static lldb::offset_t SetData(const lldb_private::DataExtractor &src, - lldb_private::DataExtractor &dst, - lldb::offset_t offset, lldb::offset_t length); - - lldb::offset_t SetDataWithReadMemoryFallback(lldb_private::DataExtractor &dst, - lldb::offset_t offset, - lldb::offset_t length); }; #endif // liblldb_ObjectFileELF_h_ diff --git a/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp b/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp index f6c8c78ccb73..518f0d2da4f2 100644 --- a/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp +++ b/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp @@ -1085,8 +1085,7 @@ Status NativeProcessDarwin::HandleWaitpidResult() { "waitpid exiting pid from the pipe. Will notify " "as if parent process died with exit status -1.", __FUNCTION__); - SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result", - notify_status); + SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status); return error; } @@ -1099,8 +1098,7 @@ Status NativeProcessDarwin::HandleWaitpidResult() { "waitpid exit status from the pipe. Will notify " "as if parent process died with exit status -1.", __FUNCTION__); - SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result", - notify_status); + SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status); return error; } @@ -1111,18 +1109,7 @@ Status NativeProcessDarwin::HandleWaitpidResult() { __FUNCTION__, pid, (pid == m_pid) ? "the inferior" : "not the inferior", status); - ExitType exit_type = eExitTypeInvalid; - int exit_status = -1; - - if (WIFEXITED(status)) { - exit_type = eExitTypeExit; - exit_status = WEXITSTATUS(status); - } else if (WIFSIGNALED(status)) { - exit_type = eExitTypeSignal; - exit_status = WTERMSIG(status); - } - - SetExitStatus(exit_type, exit_status, nullptr, notify_status); + SetExitStatus(WaitStatus::Decode(status), notify_status); return error; } diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp index a130472c72d0..e39a1788fc00 100644 --- a/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -503,35 +503,9 @@ Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } -static ExitType convert_pid_status_to_exit_type(int status) { - if (WIFEXITED(status)) - return ExitType::eExitTypeExit; - else if (WIFSIGNALED(status)) - return ExitType::eExitTypeSignal; - else if (WIFSTOPPED(status)) - return ExitType::eExitTypeStop; - else { - // We don't know what this is. - return ExitType::eExitTypeInvalid; - } -} - -static int convert_pid_status_to_return_code(int status) { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - return WTERMSIG(status); - else if (WIFSTOPPED(status)) - return WSTOPSIG(status); - else { - // We don't know what this is. - return ExitType::eExitTypeInvalid; - } -} - // Handles all waitpid events from the inferior process. void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, - int signal, int status) { + WaitStatus status) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Certain activities differ based on whether the pid is the tid of the main @@ -564,8 +538,7 @@ void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, : "thread metadata not found", GetState()); // The main thread exited. We're done monitoring. Report to delegate. - SetExitStatus(convert_pid_status_to_exit_type(status), - convert_pid_status_to_return_code(status), nullptr, true); + SetExitStatus(status, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); @@ -658,8 +631,7 @@ void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, // Notify the delegate - our process is not available but appears to // have been killed outside // our control. Is eStateExited the right exit state in this case? - SetExitStatus(convert_pid_status_to_exit_type(status), - convert_pid_status_to_return_code(status), nullptr, true); + SetExitStatus(status, true); SetState(StateType::eStateExited, true); } else { // This thread was pulled out from underneath us. Anything to do here? @@ -830,10 +802,8 @@ void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, data, WIFEXITED(data), WIFSIGNALED(data), thread.GetID(), is_main_thread); - if (is_main_thread) { - SetExitStatus(convert_pid_status_to_exit_type(data), - convert_pid_status_to_return_code(data), nullptr, true); - } + if (is_main_thread) + SetExitStatus(WaitStatus::Decode(data), true); StateType state = thread.GetState(); if (!StateIsRunningState(state)) { @@ -2384,33 +2354,17 @@ void NativeProcessLinux::SigchldHandler() { break; } - bool exited = false; - int signal = 0; - int exit_status = 0; - const char *status_cstr = nullptr; - if (WIFSTOPPED(status)) { - signal = WSTOPSIG(status); - status_cstr = "STOPPED"; - } else if (WIFEXITED(status)) { - exit_status = WEXITSTATUS(status); - status_cstr = "EXITED"; - exited = true; - } else if (WIFSIGNALED(status)) { - signal = WTERMSIG(status); - status_cstr = "SIGNALED"; - if (wait_pid == static_cast<::pid_t>(GetID())) { - exited = true; - exit_status = -1; - } - } else - status_cstr = "(\?\?\?)"; + WaitStatus wait_status = WaitStatus::Decode(status); + bool exited = wait_status.type == WaitStatus::Exit || + (wait_status.type == WaitStatus::Signal && + wait_pid == static_cast<::pid_t>(GetID())); - LLDB_LOG(log, - "waitpid (-1, &status, _) => pid = {0}, status = {1:x} " - "({2}), signal = {3}, exit_state = {4}", - wait_pid, status, status_cstr, signal, exit_status); + LLDB_LOG( + log, + "waitpid (-1, &status, _) => pid = {0}, status = {1}, exited = {2}", + wait_pid, wait_status, exited); - MonitorCallback(wait_pid, exited, signal, exit_status); + MonitorCallback(wait_pid, exited, wait_status); } } diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.h b/source/Plugins/Process/Linux/NativeProcessLinux.h index 98fc88baab6e..aa6fe4412068 100644 --- a/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -153,7 +153,7 @@ private: static void *MonitorThread(void *baton); - void MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status); + void MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status); void WaitForNewThread(::pid_t tid); diff --git a/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp index efb19fc414f9..a4d775860a65 100644 --- a/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ b/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -40,32 +40,6 @@ using namespace lldb_private; using namespace lldb_private::process_netbsd; using namespace llvm; -static ExitType convert_pid_status_to_exit_type(int status) { - if (WIFEXITED(status)) - return ExitType::eExitTypeExit; - else if (WIFSIGNALED(status)) - return ExitType::eExitTypeSignal; - else if (WIFSTOPPED(status)) - return ExitType::eExitTypeStop; - else { - // We don't know what this is. - return ExitType::eExitTypeInvalid; - } -} - -static int convert_pid_status_to_return_code(int status) { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - return WTERMSIG(status); - else if (WIFSTOPPED(status)) - return WSTOPSIG(status); - else { - // We don't know what this is. - return ExitType::eExitTypeInvalid; - } -} - // Simple helper function to ensure flags are enabled on the given file // descriptor. static Status EnsureFDFlags(int fd, int flags) { @@ -177,17 +151,15 @@ void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) { } } -void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, int signal, - int status) { +void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "got exit signal({0}) , pid = {1}", signal, pid); + LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid); /* Stop Tracking All Threads attached to Process */ m_threads.clear(); - SetExitStatus(convert_pid_status_to_exit_type(status), - convert_pid_status_to_return_code(status), nullptr, true); + SetExitStatus(status, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); @@ -861,36 +833,21 @@ void NativeProcessNetBSD::SigchldHandler() { LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); } - bool exited = false; - int signal = 0; - int exit_status = 0; - const char *status_cstr = nullptr; - if (WIFSTOPPED(status)) { - signal = WSTOPSIG(status); - status_cstr = "STOPPED"; - } else if (WIFEXITED(status)) { - exit_status = WEXITSTATUS(status); - status_cstr = "EXITED"; - exited = true; - } else if (WIFSIGNALED(status)) { - signal = WTERMSIG(status); - status_cstr = "SIGNALED"; - if (wait_pid == static_cast<::pid_t>(GetID())) { - exited = true; - exit_status = -1; - } - } else - status_cstr = "(\?\?\?)"; + WaitStatus wait_status = WaitStatus::Decode(status); + bool exited = wait_status.type == WaitStatus::Exit || + (wait_status.type == WaitStatus::Signal && + wait_pid == static_cast<::pid_t>(GetID())); LLDB_LOG(log, - "waitpid ({0}, &status, _) => pid = {1}, status = {2:x} " - "({3}), signal = {4}, exit_state = {5}", - GetID(), wait_pid, status, status_cstr, signal, exit_status); + "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", + GetID(), wait_pid, status, exited); if (exited) - MonitorExited(wait_pid, signal, exit_status); - else - MonitorCallback(wait_pid, signal); + MonitorExited(wait_pid, wait_status); + else { + assert(wait_status.type == WaitStatus::Stop); + MonitorCallback(wait_pid, wait_status.status); + } } bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) { diff --git a/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h index 758956e3dca1..7a1303faea68 100644 --- a/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h +++ b/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h @@ -123,7 +123,7 @@ private: void AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error); void MonitorCallback(lldb::pid_t pid, int signal); - void MonitorExited(lldb::pid_t pid, int signal, int status); + void MonitorExited(lldb::pid_t pid, WaitStatus status); void MonitorSIGSTOP(lldb::pid_t pid); void MonitorSIGTRAP(lldb::pid_t pid); void MonitorSignal(lldb::pid_t pid, int signal); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index d318c35366f1..d34a79453fed 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -370,53 +370,23 @@ GDBRemoteCommunicationServerLLGS::SendWResponse( Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // send W notification - ExitType exit_type = ExitType::eExitTypeInvalid; - int return_code = 0; - std::string exit_description; - - const bool got_exit_info = - process->GetExitStatus(&exit_type, &return_code, exit_description); - if (!got_exit_info) { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - ", failed to retrieve process exit status", - __FUNCTION__, process->GetID()); + auto wait_status = process->GetExitStatus(); + if (!wait_status) { + LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status", + process->GetID()); StreamGDBRemote response; response.PutChar('E'); response.PutHex8(GDBRemoteServerError::eErrorExitStatus); return SendPacketNoLock(response.GetString()); - } else { - if (log) - log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 - ", returning exit type %d, return code %d [%s]", - __FUNCTION__, process->GetID(), exit_type, return_code, - exit_description.c_str()); - - StreamGDBRemote response; - - char return_type_code; - switch (exit_type) { - case ExitType::eExitTypeExit: - return_type_code = 'W'; - break; - case ExitType::eExitTypeSignal: - return_type_code = 'X'; - break; - case ExitType::eExitTypeStop: - return_type_code = 'S'; - break; - case ExitType::eExitTypeInvalid: - return_type_code = 'E'; - break; - } - response.PutChar(return_type_code); + } - // POSIX exit status limited to unsigned 8 bits. - response.PutHex8(return_code); + LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(), + *wait_status); - return SendPacketNoLock(response.GetString()); - } + StreamGDBRemote response; + response.Format("{0:g}", *wait_status); + return SendPacketNoLock(response.GetString()); } static void AppendHexValue(StreamString &response, const uint8_t *buf, diff --git a/unittests/Host/CMakeLists.txt b/unittests/Host/CMakeLists.txt index 7b2ce3bbfde5..0c42eb533fa0 100644 --- a/unittests/Host/CMakeLists.txt +++ b/unittests/Host/CMakeLists.txt @@ -1,6 +1,7 @@ set (FILES FileSpecTest.cpp FileSystemTest.cpp + HostTest.cpp MainLoopTest.cpp SocketAddressTest.cpp SocketTest.cpp diff --git a/unittests/Host/HostTest.cpp b/unittests/Host/HostTest.cpp new file mode 100644 index 000000000000..14a459ebf257 --- /dev/null +++ b/unittests/Host/HostTest.cpp @@ -0,0 +1,22 @@ +//===-- HostTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace llvm; + +TEST(Host, WaitStatusFormat) { + EXPECT_EQ("W01", formatv("{0:g}", WaitStatus{WaitStatus::Exit, 1}).str()); + EXPECT_EQ("X02", formatv("{0:g}", WaitStatus{WaitStatus::Signal, 2}).str()); + EXPECT_EQ("S03", formatv("{0:g}", WaitStatus{WaitStatus::Stop, 3}).str()); + EXPECT_EQ("Exited with status 4", + formatv("{0}", WaitStatus{WaitStatus::Exit, 4}).str()); +} diff --git a/unittests/Process/gdb-remote/CMakeLists.txt b/unittests/Process/gdb-remote/CMakeLists.txt index 694ba182d42f..0ec5554a3d8d 100644 --- a/unittests/Process/gdb-remote/CMakeLists.txt +++ b/unittests/Process/gdb-remote/CMakeLists.txt @@ -9,6 +9,9 @@ add_lldb_unittest(ProcessGdbRemoteTests lldbPluginPlatformMacOSX lldbPluginProcessUtility lldbPluginProcessGDBRemote + + LLVMTestingSupport + LINK_COMPONENTS Support ) diff --git a/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp index 5e709815b2d5..22b7148af5ba 100644 --- a/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp +++ b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp @@ -14,8 +14,8 @@ #include "Plugins/Process/gdb-remote/GDBRemoteClientBase.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" #include "lldb/Utility/StreamGDBRemote.h" - #include "llvm/ADT/STLExtras.h" +#include "llvm/Testing/Support/Error.h" using namespace lldb_private::process_gdb_remote; using namespace lldb_private; @@ -45,13 +45,20 @@ struct TestClient : public GDBRemoteClientBase { } }; -struct ContinueFixture { - MockDelegate delegate; +class GDBRemoteClientBaseTest : public GDBRemoteTest { +public: + void SetUp() override { + ASSERT_THAT_ERROR(Connect(client, server), llvm::Succeeded()); + ASSERT_EQ(TestClient::eBroadcastBitRunPacketSent, + listener_sp->StartListeningForEvents( + &client, TestClient::eBroadcastBitRunPacketSent)); + } + +protected: TestClient client; MockServer server; - ListenerSP listener_sp; - - ContinueFixture(); + MockDelegate delegate; + ListenerSP listener_sp = Listener::MakeListener("listener"); StateType SendCPacket(StringExtractorGDBRemote &response) { return client.SendContinuePacketAndWaitForResponse(delegate, LinuxSignals(), @@ -65,76 +72,61 @@ struct ContinueFixture { } }; -ContinueFixture::ContinueFixture() - : listener_sp(Listener::MakeListener("listener")) { - Connect(client, server); - listener_sp->StartListeningForEvents(&client, - TestClient::eBroadcastBitRunPacketSent); -} - } // end anonymous namespace -class GDBRemoteClientBaseTest : public GDBRemoteTest {}; - TEST_F(GDBRemoteClientBaseTest, SendContinueAndWait) { StringExtractorGDBRemote response; - ContinueFixture fix; - if (HasFailure()) - return; // Continue. The inferior will stop with a signal. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); - ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); ASSERT_EQ("T01", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); // Continue. The inferior will exit. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("W01")); - ASSERT_EQ(eStateExited, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket("W01")); + ASSERT_EQ(eStateExited, SendCPacket(response)); ASSERT_EQ("W01", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); // Continue. The inferior will get killed. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("X01")); - ASSERT_EQ(eStateExited, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket("X01")); + ASSERT_EQ(eStateExited, SendCPacket(response)); ASSERT_EQ("X01", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); } TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) { StringExtractorGDBRemote continue_response, response; - ContinueFixture fix; - if (HasFailure()) - return; // SendAsyncSignal should do nothing when we are not running. - ASSERT_FALSE(fix.client.SendAsyncSignal(0x47)); + ASSERT_FALSE(client.SendAsyncSignal(0x47)); // Continue. After the run packet is sent, send an async signal. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); std::future<bool> async_result = std::async( - std::launch::async, [&] { return fix.client.SendAsyncSignal(0x47); }); + std::launch::async, [&] { return client.SendAsyncSignal(0x47); }); // First we'll get interrupted. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); // Then we get the signal packet. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("C47", response.GetStringRef()); ASSERT_TRUE(async_result.get()); // And we report back a signal stop. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T47")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T47")); ASSERT_EQ(eStateStopped, continue_state.get()); ASSERT_EQ("T47", continue_response.GetStringRef()); } @@ -142,72 +134,66 @@ TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) { TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncPacket) { StringExtractorGDBRemote continue_response, async_response, response; const bool send_async = true; - ContinueFixture fix; - if (HasFailure()) - return; // Continue. After the run packet is sent, send an async packet. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); // Sending without async enabled should fail. ASSERT_EQ( PacketResult::ErrorSendFailed, - fix.client.SendPacketAndWaitForResponse("qTest1", response, !send_async)); + client.SendPacketAndWaitForResponse("qTest1", response, !send_async)); std::future<PacketResult> async_result = std::async(std::launch::async, [&] { - return fix.client.SendPacketAndWaitForResponse("qTest2", async_response, - send_async); + return client.SendPacketAndWaitForResponse("qTest2", async_response, + send_async); }); // First we'll get interrupted. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); // Then we get the async packet. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("qTest2", response.GetStringRef()); // Send the response and receive it. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest2")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("QTest2")); ASSERT_EQ(PacketResult::Success, async_result.get()); ASSERT_EQ("QTest2", async_response.GetStringRef()); // And we get resumed again. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); ASSERT_EQ(eStateStopped, continue_state.get()); ASSERT_EQ("T01", continue_response.GetStringRef()); } TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) { StringExtractorGDBRemote continue_response, response; - ContinueFixture fix; - if (HasFailure()) - return; // Interrupt should do nothing when we're not running. - ASSERT_FALSE(fix.client.Interrupt()); + ASSERT_FALSE(client.Interrupt()); // Continue. After the run packet is sent, send an interrupt. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); std::future<bool> async_result = - std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + std::async(std::launch::async, [&] { return client.Interrupt(); }); // We get interrupted. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); // And that's it. ASSERT_EQ(eStateStopped, continue_state.get()); @@ -217,62 +203,56 @@ TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) { TEST_F(GDBRemoteClientBaseTest, SendContinueAndLateInterrupt) { StringExtractorGDBRemote continue_response, response; - ContinueFixture fix; - if (HasFailure()) - return; // Continue. After the run packet is sent, send an interrupt. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); std::future<bool> async_result = - std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + std::async(std::launch::async, [&] { return client.Interrupt(); }); // However, the target stops due to a different reason than the original // interrupt. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); ASSERT_EQ(eStateStopped, continue_state.get()); ASSERT_EQ("T01", continue_response.GetStringRef()); ASSERT_TRUE(async_result.get()); // The subsequent continue packet should work normally. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); - ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); ASSERT_EQ("T01", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); } TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) { StringExtractorGDBRemote continue_response, async_response, response; const bool send_async = true; - ContinueFixture fix; - if (HasFailure()) - return; // Interrupt should do nothing when we're not running. - ASSERT_FALSE(fix.client.Interrupt()); + ASSERT_FALSE(client.Interrupt()); // Continue. After the run packet is sent, send an async signal. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); std::future<bool> interrupt_result = - std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + std::async(std::launch::async, [&] { return client.Interrupt(); }); // We get interrupted. We'll send two packets to simulate a buggy stub. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); // We should stop. ASSERT_EQ(eStateStopped, continue_state.get()); @@ -281,37 +261,34 @@ TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) { // Packet stream should remain synchronized. std::future<PacketResult> send_result = std::async(std::launch::async, [&] { - return fix.client.SendPacketAndWaitForResponse("qTest", async_response, - !send_async); + return client.SendPacketAndWaitForResponse("qTest", async_response, + !send_async); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("qTest", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("QTest")); ASSERT_EQ(PacketResult::Success, send_result.get()); ASSERT_EQ("QTest", async_response.GetStringRef()); } TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateInterface) { StringExtractorGDBRemote response; - ContinueFixture fix; - if (HasFailure()) - return; // Continue. We'll have the server send a bunch of async packets before it // stops. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4142")); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Apro")); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4344")); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Afile")); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); - ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O4142")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("Apro")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O4344")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("Afile")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); ASSERT_EQ("T01", response.GetStringRef()); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - EXPECT_EQ("ABCD", fix.delegate.output); - EXPECT_EQ("profile", fix.delegate.misc_data); - EXPECT_EQ(1u, fix.delegate.stop_reply_called); + EXPECT_EQ("ABCD", delegate.output); + EXPECT_EQ("profile", delegate.misc_data); + EXPECT_EQ(1u, delegate.stop_reply_called); } TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateStructuredDataReceipt) { @@ -327,42 +304,35 @@ TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateStructuredDataReceipt) { stream.PutEscapedBytes(json_packet.c_str(), json_packet.length()); stream.Flush(); - // Set up the StringExtractorGDBRemote response; - ContinueFixture fix; - if (HasFailure()) - return; // Send async structured data packet, then stop. - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket(stream.GetData())); - ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); - ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); + ASSERT_EQ(PacketResult::Success, server.SendPacket(stream.GetData())); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); ASSERT_EQ("T01", response.GetStringRef()); - ASSERT_EQ(1ul, fix.delegate.structured_data_packets.size()); + ASSERT_EQ(1ul, delegate.structured_data_packets.size()); // Verify the packet contents. It should have been unescaped upon packet // reception. - ASSERT_EQ(json_packet, fix.delegate.structured_data_packets[0]); + ASSERT_EQ(json_packet, delegate.structured_data_packets[0]); } TEST_F(GDBRemoteClientBaseTest, InterruptNoResponse) { StringExtractorGDBRemote continue_response, response; - ContinueFixture fix; - if (HasFailure()) - return; // Continue. After the run packet is sent, send an interrupt. std::future<StateType> continue_state = std::async( - std::launch::async, [&] { return fix.SendCPacket(continue_response); }); - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("c", response.GetStringRef()); - fix.WaitForRunEvent(); + WaitForRunEvent(); std::future<bool> async_result = - std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); + std::async(std::launch::async, [&] { return client.Interrupt(); }); // We get interrupted, but we don't send a stop packet. - ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); ASSERT_EQ("\x03", response.GetStringRef()); // The functions should still terminate (after a timeout). diff --git a/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp index 8f6f9f0684a3..cd2583bb8f54 100644 --- a/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -11,14 +11,14 @@ #include "GDBRemoteTestUtils.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" -#include "lldb/lldb-enumerations.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/TraceOptions.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/DataBuffer.h" - +#include "lldb/lldb-enumerations.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/Testing/Support/Error.h" using namespace lldb_private::process_gdb_remote; using namespace lldb_private; @@ -58,15 +58,18 @@ std::string one_register_hex = "41424344"; } // end anonymous namespace -class GDBRemoteCommunicationClientTest : public GDBRemoteTest {}; +class GDBRemoteCommunicationClientTest : public GDBRemoteTest { +public: + void SetUp() override { + ASSERT_THAT_ERROR(Connect(client, server), llvm::Succeeded()); + } -TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { +protected: TestClient client; MockServer server; - Connect(client, server); - if (HasFailure()) - return; +}; +TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future<bool> write_result = std::async(std::launch::async, [&] { @@ -87,12 +90,6 @@ TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { } TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future<bool> write_result = std::async(std::launch::async, [&] { @@ -113,12 +110,6 @@ TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { } TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future<bool> async_result = std::async( @@ -145,12 +136,6 @@ TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { } TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::tid_t tid = 0x47; uint32_t save_id; std::future<bool> async_result = std::async(std::launch::async, [&] { @@ -170,12 +155,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { } TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::tid_t tid = 0x47; std::future<bool> async_result = std::async( std::launch::async, [&] { return client.SyncThreadState(tid); }); @@ -185,12 +164,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - llvm::Triple triple("i386-pc-linux"); FileSpec file_specs[] = { @@ -225,12 +198,6 @@ TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - llvm::Triple triple("i386-pc-linux"); FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix); @@ -267,13 +234,7 @@ TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { } TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - - std::thread server_thread([&server] { + std::thread server_thread([this] { for (;;) { StringExtractorGDBRemote request; PacketResult result = server.GetPacket(request); @@ -312,12 +273,6 @@ TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { } TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - std::future<Status> result = std::async(std::launch::async, [&] { return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11}); }); @@ -334,12 +289,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::addr_t addr = 0xa000; MemoryRegionInfo region_info; std::future<Status> result = std::async(std::launch::async, [&] { @@ -355,12 +304,6 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - const lldb::addr_t addr = 0x4000; MemoryRegionInfo region_info; std::future<Status> result = std::async(std::launch::async, [&] { @@ -372,12 +315,6 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { } TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - TraceOptions options; Status error; @@ -417,12 +354,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) { } TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; @@ -444,12 +375,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) { } TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; @@ -482,12 +407,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) { } TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; @@ -520,12 +439,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) { } TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) { - TestClient client; - MockServer server; - Connect(client, server); - if (HasFailure()) - return; - lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; TraceOptions options; diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp index 4a86b91c0489..2acf72744d20 100644 --- a/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp +++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp @@ -30,30 +30,33 @@ void GDBRemoteTest::TearDownTestCase() { #endif } -void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server) { +llvm::Error GDBRemoteTest::Connect(GDBRemoteCommunication &client, + GDBRemoteCommunication &server) { bool child_processes_inherit = false; - Status error; TCPSocket listen_socket(true, child_processes_inherit); - ASSERT_FALSE(error.Fail()); - error = listen_socket.Listen("127.0.0.1:0", 5); - ASSERT_FALSE(error.Fail()); + if (llvm::Error error = listen_socket.Listen("127.0.0.1:0", 5).ToError()) + return error; Socket *accept_socket; - std::future<Status> accept_error = std::async( + std::future<Status> accept_status = std::async( std::launch::async, [&] { return listen_socket.Accept(accept_socket); }); - char connect_remote_address[64]; - snprintf(connect_remote_address, sizeof(connect_remote_address), - "connect://localhost:%u", listen_socket.GetLocalPortNumber()); + llvm::SmallString<32> remote_addr; + llvm::raw_svector_ostream(remote_addr) + << "connect://localhost:" << listen_socket.GetLocalPortNumber(); - std::unique_ptr<ConnectionFileDescriptor> conn_ap( + std::unique_ptr<ConnectionFileDescriptor> conn_up( new ConnectionFileDescriptor()); - ASSERT_EQ(conn_ap->Connect(connect_remote_address, nullptr), - lldb::eConnectionStatusSuccess); + if (conn_up->Connect(remote_addr, nullptr) != lldb::eConnectionStatusSuccess) + return llvm::make_error<llvm::StringError>("Unable to connect", + llvm::inconvertibleErrorCode()); + + client.SetConnection(conn_up.release()); + if (llvm::Error error = accept_status.get().ToError()) + return error; - client.SetConnection(conn_ap.release()); - ASSERT_TRUE(accept_error.get().Success()); server.SetConnection(new ConnectionFileDescriptor(accept_socket)); + return llvm::Error::success(); } } // namespace process_gdb_remote diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.h b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h index d7700f23e5ab..cc17422049bc 100644 --- a/unittests/Process/gdb-remote/GDBRemoteTestUtils.h +++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h @@ -19,11 +19,12 @@ namespace process_gdb_remote { class GDBRemoteTest : public testing::Test { public: static void SetUpTestCase(); - static void TearDownTestCase(); -}; -void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server); +protected: + llvm::Error Connect(GDBRemoteCommunication &client, + GDBRemoteCommunication &server); +}; struct MockServer : public GDBRemoteCommunicationServer { MockServer() diff --git a/unittests/tools/lldb-server/tests/TestClient.cpp b/unittests/tools/lldb-server/tests/TestClient.cpp index 540d7979055c..9553ddd65fac 100644 --- a/unittests/tools/lldb-server/tests/TestClient.cpp +++ b/unittests/tools/lldb-server/tests/TestClient.cpp @@ -12,7 +12,6 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" -#include "lldb/Host/posix/ProcessLauncherPosix.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "llvm/ADT/StringExtras.h" |