Vendor import of lldb trunk r306325:
https://llvm.org/svn/llvm-project/lldb/trunk@306325
This commit is contained in:
parent
4befb1f96d
commit
fdea456ad8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/lldb/dist/; revision=320384 svn path=/vendor/lldb/lldb-trunk-r306325/; revision=320385; tag=vendor/lldb/lldb-trunk-r306325
@ -28,6 +28,27 @@ namespace lldb_private {
|
||||
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 @@ class Host {
|
||||
|
||||
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 @@ class Host {
|
||||
|
||||
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 @@ class Host {
|
||||
|
||||
} // 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_
|
||||
|
@ -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 @@ class NativeProcessProtocol
|
||||
//----------------------------------------------------------------------
|
||||
// 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 @@ class NativeProcessProtocol
|
||||
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;
|
||||
|
@ -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
|
@ -454,6 +454,8 @@ enum LanguageType {
|
||||
enum InstrumentationRuntimeType {
|
||||
eInstrumentationRuntimeTypeAddressSanitizer = 0x0000,
|
||||
eInstrumentationRuntimeTypeThreadSanitizer = 0x0001,
|
||||
eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer = 0x0002,
|
||||
eInstrumentationRuntimeTypeMainThreadChecker = 0x0003,
|
||||
eNumInstrumentationRuntimeTypes
|
||||
};
|
||||
|
||||
|
@ -211,19 +211,6 @@ enum class LineStatus {
|
||||
Done // Lines are complete
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -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 */,
|
||||
|
@ -681,6 +681,53 @@ def is_compiler_clang_with_thread_sanitizer(self):
|
||||
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."""
|
||||
|
||||
|
@ -186,16 +186,27 @@ def cleanup():
|
||||
|
||||
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',
|
||||
|
@ -0,0 +1,6 @@
|
||||
LEVEL = ../../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
CFLAGS_EXTRAS := -fsanitize=undefined -g
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
@ -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")
|
@ -0,0 +1,4 @@
|
||||
int main() {
|
||||
int data[4];
|
||||
return *(int *)(((char *)&data[0]) + 2); // align line
|
||||
}
|
@ -121,8 +121,6 @@ def test_read_dictionary(self):
|
||||
'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 @@ def test_array_literal(self):
|
||||
'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()
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -655,32 +655,30 @@ class CommandObjectFrameVariable : public CommandObjectParsed {
|
||||
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;
|
||||
|
||||
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:
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -17,10 +17,10 @@
|
||||
|
||||
// Other libraries and framework includes
|
||||
// Project includes
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Target/DynamicLoader.h"
|
||||
|
||||
#include "DYLDRendezvous.h"
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Core/ModuleList.h"
|
||||
#include "lldb/Target/DynamicLoader.h"
|
||||
|
||||
class AuxVector;
|
||||
|
||||
@ -138,6 +138,8 @@ class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
|
||||
/// 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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeAddressSanitizer PLUGIN
|
||||
AddressSanitizerRuntime.cpp
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeASan PLUGIN
|
||||
ASanRuntime.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbBreakpoint
|
@ -1,2 +1,4 @@
|
||||
add_subdirectory(AddressSanitizer)
|
||||
add_subdirectory(ThreadSanitizer)
|
||||
add_subdirectory(ASan)
|
||||
add_subdirectory(MainThreadChecker)
|
||||
add_subdirectory(TSan)
|
||||
add_subdirectory(UBSan)
|
||||
|
@ -0,0 +1,13 @@
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeMainThreadChecker PLUGIN
|
||||
MainThreadCheckerRuntime.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbBreakpoint
|
||||
lldbCore
|
||||
lldbExpression
|
||||
lldbInterpreter
|
||||
lldbSymbol
|
||||
lldbTarget
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
)
|
@ -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;
|
||||
}
|
@ -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_
|
@ -1,5 +1,5 @@
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeThreadSanitizer PLUGIN
|
||||
ThreadSanitizerRuntime.cpp
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeTSan PLUGIN
|
||||
TSanRuntime.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbBreakpoint
|
@ -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);
|
||||
}
|
||||
|
13
source/Plugins/InstrumentationRuntime/UBSan/CMakeLists.txt
Normal file
13
source/Plugins/InstrumentationRuntime/UBSan/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
add_lldb_library(lldbPluginInstrumentationRuntimeUBSan PLUGIN
|
||||
UBSanRuntime.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbBreakpoint
|
||||
lldbCore
|
||||
lldbExpression
|
||||
lldbInterpreter
|
||||
lldbSymbol
|
||||
lldbTarget
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
)
|
340
source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
Normal file
340
source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
Normal file
@ -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;
|
||||
}
|
69
source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
Normal file
69
source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
Normal file
@ -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_
|
@ -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;
|
||||
}
|
||||
|
@ -160,11 +160,47 @@ class NSArrayMSyntheticFrontEnd_1010 : public NSArrayMSyntheticFrontEnd {
|
||||
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 @@ class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
||||
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
|
||||
|
@ -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"
|
||||
@ -179,6 +181,52 @@ class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
||||
|
||||
size_t GetIndexOfChildWithName(const ConstString &name) override;
|
||||
|
||||
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;
|
||||
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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 *
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// C++ Includes
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
// Other libraries and framework includes
|
||||
@ -182,9 +181,6 @@ class ObjectFileELF : public lldb_private::ObjectFile {
|
||||
|
||||
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 @@ class ObjectFileELF : public lldb_private::ObjectFile {
|
||||
|
||||
// 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 @@ class ObjectFileELF : public lldb_private::ObjectFile {
|
||||
/// 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 @@ class ObjectFileELF : public lldb_private::ObjectFile {
|
||||
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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ class NativeProcessLinux : public NativeProcessProtocol {
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -123,7 +123,7 @@ class NativeProcessNetBSD : public NativeProcessProtocol {
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
return SendPacketNoLock(response.GetString());
|
||||
}
|
||||
|
||||
LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(),
|
||||
*wait_status);
|
||||
|
||||
StreamGDBRemote response;
|
||||
response.Format("{0:g}", *wait_status);
|
||||
return SendPacketNoLock(response.GetString());
|
||||
}
|
||||
|
||||
static void AppendHexValue(StreamString &response, const uint8_t *buf,
|
||||
|
@ -1,6 +1,7 @@
|
||||
set (FILES
|
||||
FileSpecTest.cpp
|
||||
FileSystemTest.cpp
|
||||
HostTest.cpp
|
||||
MainLoopTest.cpp
|
||||
SocketAddressTest.cpp
|
||||
SocketTest.cpp
|
||||
|
22
unittests/Host/HostTest.cpp
Normal file
22
unittests/Host/HostTest.cpp
Normal file
@ -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());
|
||||
}
|
@ -9,6 +9,9 @@ add_lldb_unittest(ProcessGdbRemoteTests
|
||||
lldbPluginPlatformMacOSX
|
||||
lldbPluginProcessUtility
|
||||
lldbPluginProcessGDBRemote
|
||||
|
||||
LLVMTestingSupport
|
||||
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
)
|
||||
|
@ -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).
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user