+++ /dev/null
-//===-- Host.h --------------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_HOST_LINUX_HOST_H
-#define LLDB_HOST_LINUX_HOST_H
-
-#include "lldb/lldb-types.h"
-#include "llvm/ADT/Optional.h"
-
-namespace lldb_private {
-
-// Get PID (i.e. the primary thread ID) corresponding to the specified TID.
-llvm::Optional<lldb::pid_t> getPIDForTID(lldb::pid_t tid);
-
-} // namespace lldb_private
-
-#endif // #ifndef LLDB_HOST_LINUX_HOST_H
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
-#include "lldb/Host/linux/Host.h"
#include "lldb/Host/linux/Support.h"
#include "lldb/Utility/DataExtractor.h"
}
static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
- ProcessState &State, ::pid_t &TracerPid,
- ::pid_t &Tgid) {
+ ProcessState &State, ::pid_t &TracerPid) {
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
auto BufferOrError = getProcFile(Pid, "status");
} else if (Line.consume_front("TracerPid:")) {
Line = Line.ltrim();
Line.consumeInteger(10, TracerPid);
- } else if (Line.consume_front("Tgid:")) {
- Line = Line.ltrim();
- Line.consumeInteger(10, Tgid);
}
}
return true;
static bool GetProcessAndStatInfo(::pid_t pid,
ProcessInstanceInfo &process_info,
ProcessState &State, ::pid_t &tracerpid) {
- ::pid_t tgid;
tracerpid = 0;
process_info.Clear();
GetProcessEnviron(pid, process_info);
// Get User and Group IDs and get tracer pid.
- if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))
+ if (!GetStatusInfo(pid, process_info, State, tracerpid))
return false;
return true;
Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
return Status("unimplemented");
}
-
-llvm::Optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) {
- ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;
- ProcessInstanceInfo process_info;
- ProcessState state;
-
- if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) ||
- tgid == LLDB_INVALID_PROCESS_ID)
- return llvm::None;
- return tgid;
-}
return;
}
- if (info.pl_flags & PL_FLAG_FORKED) {
- MonitorClone(info.pl_child_pid);
- return;
- }
-
- if (info.pl_flags & PL_FLAG_VFORK_DONE) {
- Status error =
- PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0);
- if (error.Fail())
- SetState(StateType::eStateInvalid);
- return;
- }
-
if (info.pl_lwpid > 0) {
for (const auto &t : m_threads) {
if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid))
void NativeProcessFreeBSD::SigchldHandler() {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // Process all pending waitpid notifications.
int status;
::pid_t wait_pid =
llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG);
if (wait_pid == 0)
- return;
+ return; // We are done.
if (wait_pid == -1) {
Status error(errno, eErrorTypePOSIX);
LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
- return;
}
WaitStatus wait_status = WaitStatus::Decode(status);
PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
- events |= PTRACE_LWP | PTRACE_FORK | PTRACE_VFORK;
+ events |= PTRACE_LWP;
status = PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
bool NativeProcessFreeBSD::SupportHardwareSingleStepping() const {
return !m_arch.IsMIPS();
}
-
-void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid) {
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
- LLDB_LOG(log, "fork, child_pid={0}", child_pid);
-
- int status;
- ::pid_t wait_pid =
- llvm::sys::RetryAfterSignal(-1, ::waitpid, child_pid, &status, 0);
- if (wait_pid != child_pid) {
- LLDB_LOG(log,
- "waiting for pid {0} failed. Assuming the pid has "
- "disappeared in the meantime",
- child_pid);
- return;
- }
- if (WIFEXITED(status)) {
- LLDB_LOG(log,
- "waiting for pid {0} returned an 'exited' event. Not "
- "tracking it.",
- child_pid);
- return;
- }
-
- MainLoop unused_loop;
- NativeProcessFreeBSD child_process{static_cast<::pid_t>(child_pid),
- m_terminal_fd, m_delegate, m_arch,
- unused_loop};
- child_process.ReinitializeThreads();
- auto *child_thread =
- static_cast<NativeThreadFreeBSD *>(child_process.GetCurrentThread());
- assert(child_thread);
- // new processes inherit dbregs, so we need to clear them
- llvm::Error error = child_thread->GetRegisterContext().ClearDBRegs();
- if (error) {
- LLDB_LOG_ERROR(log, std::move(error),
- "failed to clear dbregs in forked process {1}: {0}",
- child_pid);
- SetState(StateType::eStateInvalid);
- return;
- }
-
- child_process.Detach();
- Status pt_error =
- PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
- if (pt_error.Fail()) {
- LLDB_LOG_ERROR(log, pt_error.ToError(),
- "unable to resume parent process {1}: {0}", GetID());
- SetState(StateType::eStateInvalid);
- }
-}
void MonitorSIGSTOP(lldb::pid_t pid);
void MonitorSIGTRAP(lldb::pid_t pid);
void MonitorSignal(lldb::pid_t pid, int signal);
- void MonitorClone(::pid_t child_pid);
Status PopulateMemoryRegionCache();
void SigchldHandler();
virtual llvm::Error
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0;
- virtual llvm::Error ClearDBRegs() { return llvm::Error::success(); }
-
protected:
virtual NativeProcessFreeBSD &GetProcess();
virtual ::pid_t GetProcessPid();
#endif
}
-llvm::Error NativeRegisterContextFreeBSD_arm64::ClearDBRegs() {
-#ifdef LLDB_HAS_FREEBSD_WATCHPOINT
- if (llvm::Error error = ReadHardwareDebugInfo())
- return error;
-
- for (uint32_t i = 0; i < m_max_hbp_supported; i++)
- m_hbp_regs[i].control = 0;
- for (uint32_t i = 0; i < m_max_hwp_supported; i++)
- m_hwp_regs[i].control = 0;
- return WriteHardwareDebugRegs(eDREGTypeWATCH);
-#else
- return llvm::error::success();
-#endif
-}
-
#endif // defined (__aarch64__)
llvm::Error
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
- llvm::Error ClearDBRegs() override;
-
private:
// Due to alignment, FreeBSD reg/fpreg are a few bytes larger than
// LLDB's GPR/FPU structs. However, all fields have matching offsets
return YMMSplitPtr{&fpreg->sv_xmm[reg_index], &ymmreg[reg_index]};
}
-llvm::Error NativeRegisterContextFreeBSD_x86_64::ClearDBRegs() {
- uint64_t zero = 0;
- RegisterValue dr7{zero};
- return WriteRegister(GetDR(7), dr7).ToError();
-}
-
#endif // defined(__x86_64__)
llvm::Error
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
- llvm::Error ClearDBRegs() override;
-
private:
// Private member types.
enum RegSetKind {
#include "lldb/Host/PseudoTerminal.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Host/common/NativeRegisterContext.h"
-#include "lldb/Host/linux/Host.h"
#include "lldb/Host/linux/Ptrace.h"
#include "lldb/Host/linux/Uio.h"
#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
ptrace_opts |= PTRACE_O_TRACEEXIT;
// Have the tracer trace threads which spawn in the inferior process.
+ // TODO: if we want to support tracing the inferiors' child, add the
+ // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK)
ptrace_opts |= PTRACE_O_TRACECLONE;
// Have the tracer notify us before execve returns (needed to disable legacy
// SIGTRAP generation)
ptrace_opts |= PTRACE_O_TRACEEXEC;
- // Have the tracer trace forked children.
- ptrace_opts |= PTRACE_O_TRACEFORK;
-
- // Have the tracer trace vforks.
- ptrace_opts |= PTRACE_O_TRACEVFORK;
-
- // Have the tracer trace vfork-done in order to restore breakpoints after
- // the child finishes sharing memory.
- ptrace_opts |= PTRACE_O_TRACEVFORKDONE;
-
return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
}
LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code,
info.si_pid);
- MonitorClone(pid, llvm::None);
+ NativeThreadLinux &thread = AddThread(pid, /*resume*/ true);
+ ThreadWasCreated(thread);
return;
}
}
}
-void NativeProcessLinux::WaitForCloneNotification(::pid_t pid) {
+void NativeProcessLinux::WaitForNewThread(::pid_t tid) {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
- // The PID is not tracked yet, let's wait for it to appear.
+ if (GetThreadByID(tid)) {
+ // We are already tracking the thread - we got the event on the new thread
+ // (see MonitorSignal) before this one. We are done.
+ return;
+ }
+
+ // The thread is not tracked yet, let's wait for it to appear.
int status = -1;
LLDB_LOG(log,
- "received clone event for pid {0}. pid not tracked yet, "
- "waiting for it to appear...",
- pid);
- ::pid_t wait_pid =
- llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, __WALL);
- // Since we are waiting on a specific pid, this must be the creation event.
+ "received thread creation event for tid {0}. tid not tracked "
+ "yet, waiting for thread to appear...",
+ tid);
+ ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, __WALL);
+ // Since we are waiting on a specific tid, this must be the creation event.
// But let's do some checks just in case.
- if (wait_pid != pid) {
+ if (wait_pid != tid) {
LLDB_LOG(log,
- "waiting for pid {0} failed. Assuming the pid has "
+ "waiting for tid {0} failed. Assuming the thread has "
"disappeared in the meantime",
- pid);
+ tid);
// The only way I know of this could happen is if the whole process was
// SIGKILLed in the mean time. In any case, we can't do anything about that
// now.
}
if (WIFEXITED(status)) {
LLDB_LOG(log,
- "waiting for pid {0} returned an 'exited' event. Not "
- "tracking it.",
- pid);
+ "waiting for tid {0} returned an 'exited' event. Not "
+ "tracking the thread.",
+ tid);
// Also a very improbable event.
- m_pending_pid_map.erase(pid);
return;
}
- MonitorClone(pid, llvm::None);
+ LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid);
+ NativeThreadLinux &new_thread = AddThread(tid, /*resume*/ true);
+
+ ThreadWasCreated(new_thread);
}
void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info,
assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
switch (info.si_code) {
- case (SIGTRAP | (PTRACE_EVENT_FORK << 8)):
- case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)):
+ // TODO: these two cases are required if we want to support tracing of the
+ // inferiors' children. We'd need this to debug a monitor. case (SIGTRAP |
+ // (PTRACE_EVENT_FORK << 8)): case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)):
+
case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): {
- // This can either mean a new thread or a new process spawned via
- // clone(2) without SIGCHLD or CLONE_VFORK flag. Note that clone(2)
- // can also cause PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK if one
- // of these flags are passed.
+ // This is the notification on the parent thread which informs us of new
+ // thread creation. We don't want to do anything with the parent thread so
+ // we just resume it. In case we want to implement "break on thread
+ // creation" functionality, we would need to stop here.
unsigned long event_message = 0;
if (GetEventMessage(thread.GetID(), &event_message).Fail()) {
LLDB_LOG(log,
- "pid {0} received clone() event but GetEventMessage failed "
- "so we don't know the new pid/tid",
+ "pid {0} received thread creation event but "
+ "GetEventMessage failed so we don't know the new tid",
thread.GetID());
- ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
- } else {
- if (!MonitorClone(event_message, {{(info.si_code >> 8), thread.GetID()}}))
- WaitForCloneNotification(event_message);
- }
+ } else
+ WaitForNewThread(event_message);
+ ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
break;
}
break;
}
- case (SIGTRAP | (PTRACE_EVENT_VFORK_DONE << 8)): {
- ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
- break;
- }
-
case 0:
case TRAP_TRACE: // We receive this on single stepping.
case TRAP_HWBKPT: // We receive this on watchpoint hit
StopRunningThreads(thread.GetID());
}
-bool NativeProcessLinux::MonitorClone(
- lldb::pid_t child_pid,
- llvm::Optional<NativeProcessLinux::CloneInfo> clone_info) {
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
- LLDB_LOG(log, "clone, child_pid={0}, clone info?={1}", child_pid,
- clone_info.hasValue());
-
- auto find_it = m_pending_pid_map.find(child_pid);
- if (find_it == m_pending_pid_map.end()) {
- // not in the map, so this is the first signal for the PID
- m_pending_pid_map.insert({child_pid, clone_info});
- return false;
- }
- m_pending_pid_map.erase(find_it);
-
- // second signal for the pid
- assert(clone_info.hasValue() != find_it->second.hasValue());
- if (!clone_info) {
- // child signal does not indicate the event, so grab the one stored
- // earlier
- clone_info = find_it->second;
- }
-
- LLDB_LOG(log, "second signal for child_pid={0}, parent_tid={1}, event={2}",
- child_pid, clone_info->parent_tid, clone_info->event);
-
- auto *parent_thread = GetThreadByID(clone_info->parent_tid);
- assert(parent_thread);
-
- switch (clone_info->event) {
- case PTRACE_EVENT_CLONE: {
- // PTRACE_EVENT_CLONE can either mean a new thread or a new process.
- // Try to grab the new process' PGID to figure out which one it is.
- // If PGID is the same as the PID, then it's a new process. Otherwise,
- // it's a thread.
- auto tgid_ret = getPIDForTID(child_pid);
- if (tgid_ret != child_pid) {
- // A new thread should have PGID matching our process' PID.
- assert(!tgid_ret || tgid_ret.getValue() == GetID());
-
- NativeThreadLinux &child_thread = AddThread(child_pid, /*resume*/ true);
- // Resume the newly created thread.
- ResumeThread(child_thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
- ThreadWasCreated(child_thread);
-
- // Resume the parent.
- ResumeThread(*parent_thread, parent_thread->GetState(),
- LLDB_INVALID_SIGNAL_NUMBER);
- break;
- }
- }
- LLVM_FALLTHROUGH;
- case PTRACE_EVENT_FORK:
- case PTRACE_EVENT_VFORK: {
- MainLoop unused_loop;
- NativeProcessLinux child_process{static_cast<::pid_t>(child_pid),
- m_terminal_fd,
- m_delegate,
- m_arch,
- unused_loop,
- {static_cast<::pid_t>(child_pid)}};
- child_process.Detach();
- ResumeThread(*parent_thread, parent_thread->GetState(),
- LLDB_INVALID_SIGNAL_NUMBER);
- break;
- }
- default:
- llvm_unreachable("unknown clone_info.event");
- }
-
- return true;
-}
-
bool NativeProcessLinux::SupportHardwareSingleStepping() const {
if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.IsMIPS())
return false;
void MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status);
- void WaitForCloneNotification(::pid_t pid);
+ void WaitForNewThread(::pid_t tid);
void MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread);
/// Manages Intel PT process and thread traces.
IntelPTManager m_intel_pt_manager;
-
- struct CloneInfo {
- int event;
- lldb::tid_t parent_tid;
- };
-
- // Map of child processes that have been signaled once, and we are
- // waiting for the second signal.
- llvm::DenseMap<lldb::pid_t, llvm::Optional<CloneInfo>> m_pending_pid_map;
-
- // Handle a clone()-like event. If received by parent, clone_info contains
- // additional info. Returns true if the event is handled, or false if it
- // is pending second notification.
- bool MonitorClone(lldb::pid_t child_pid,
- llvm::Optional<CloneInfo> clone_info);
};
} // namespace process_linux
SetState(StateType::eStateStopped, true);
return;
}
- case TRAP_CHLD: {
- ptrace_state_t pst;
- Status error = PtraceWrapper(PT_GET_PROCESS_STATE, pid, &pst, sizeof(pst));
- if (error.Fail()) {
- SetState(StateType::eStateInvalid);
- return;
- }
-
- if (pst.pe_report_event == PTRACE_VFORK_DONE) {
- Status error =
- PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0);
- if (error.Fail())
- SetState(StateType::eStateInvalid);
- return;
- } else
- MonitorClone(pst.pe_other_pid);
- return;
- }
case TRAP_LWP: {
ptrace_state_t pst;
Status error = PtraceWrapper(PT_GET_PROCESS_STATE, pid, &pst, sizeof(pst));
if (GetID() == LLDB_INVALID_PROCESS_ID)
return error;
- return PtraceWrapper(PT_DETACH, GetID(), reinterpret_cast<void *>(1));
+ return PtraceWrapper(PT_DETACH, GetID());
}
Status NativeProcessNetBSD::Signal(int signo) {
void NativeProcessNetBSD::SigchldHandler() {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // Process all pending waitpid notifications.
int status;
::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status,
WALLSIG | WNOHANG);
if (wait_pid == 0)
- return;
+ return; // We are done.
if (wait_pid == -1) {
Status error(errno, eErrorTypePOSIX);
LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
- return;
}
WaitStatus wait_status = WaitStatus::Decode(status);
PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
- // TODO: PTRACE_POSIX_SPAWN?
- events.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT | PTRACE_FORK |
- PTRACE_VFORK | PTRACE_VFORK_DONE;
+ // TODO: PTRACE_FORK | PTRACE_VFORK | PTRACE_POSIX_SPAWN?
+ events.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT;
status = PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
return error;
}
-
-void NativeProcessNetBSD::MonitorClone(::pid_t child_pid) {
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
- LLDB_LOG(log, "clone, child_pid={0}", child_pid);
-
- int status;
- ::pid_t wait_pid =
- llvm::sys::RetryAfterSignal(-1, ::waitpid, child_pid, &status, 0);
- if (wait_pid != child_pid) {
- LLDB_LOG(log,
- "waiting for pid {0} failed. Assuming the pid has "
- "disappeared in the meantime",
- child_pid);
- return;
- }
- if (WIFEXITED(status)) {
- LLDB_LOG(log,
- "waiting for pid {0} returned an 'exited' event. Not "
- "tracking it.",
- child_pid);
- return;
- }
-
- MainLoop unused_loop;
- NativeProcessNetBSD child_process{static_cast<::pid_t>(child_pid),
- m_terminal_fd, m_delegate, m_arch,
- unused_loop};
- child_process.Detach();
- Status pt_error =
- PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
- if (pt_error.Fail()) {
- LLDB_LOG_ERROR(log, std::move(pt_error.ToError()),
- "unable to resume parent process {1}: {0}", GetID());
- SetState(StateType::eStateInvalid);
- }
-}
void MonitorSIGSTOP(lldb::pid_t pid);
void MonitorSIGTRAP(lldb::pid_t pid);
void MonitorSignal(lldb::pid_t pid, int signal);
- void MonitorClone(::pid_t child_pid);
Status PopulateMemoryRegionCache();
void SigchldHandler();
from __future__ import print_function
import lldb
+import unittest
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from gdbclientutils import *
+++ /dev/null
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <assert.h>
-#if defined(TEST_CLONE)
-#include <sched.h>
-#endif
-#include <stdint.h>
-#include <stdio.h>
-#include <unistd.h>
-
-int g_val = 0;
-
-void parent_func() {
- g_val = 1;
- printf("function run in parent\n");
-}
-
-int child_func(void *unused) {
- // we need to avoid memory modifications for vfork(), yet we want
- // to be able to test watchpoints, so do the next best thing
- // and restore the original value
- g_val = 2;
- g_val = 0;
- return 0;
-}
-
-int main() {
- alignas(uintmax_t) char stack[4096];
-
-#if defined(TEST_CLONE)
- pid_t pid = clone(child_func, &stack[sizeof(stack)], 0, NULL);
-#elif defined(TEST_FORK)
- pid_t pid = TEST_FORK();
- if (pid == 0)
- _exit(child_func(NULL));
-#endif
- assert(pid != -1);
-
- parent_func();
- int status, wait_flags = 0;
-#if defined(TEST_CLONE)
- wait_flags = __WALL;
-#endif
- pid_t waited = waitpid(pid, &status, wait_flags);
- assert(waited == pid);
- assert(WIFEXITED(status));
- printf("child exited: %d\n", WEXITSTATUS(status));
-
- return 0;
-}
+++ /dev/null
-# REQUIRES: native && (system-linux || system-netbsd) && dbregs-set
-# clone() tests fails on arm64 Linux, PR #49899
-# UNSUPPORTED: system-linux && target-aarch64
-# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_CLONE -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-process launch -s
-watchpoint set variable -w write g_val
-# CHECK: Watchpoint created:
-continue
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = watchpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0
+++ /dev/null
-# REQUIRES: native && (system-linux || system-netbsd)
-# clone() tests fails on arm64 Linux, PR #49899
-# UNSUPPORTED: system-linux && target-aarch64
-# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_CLONE -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-b parent_func
-process launch
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = breakpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0
+++ /dev/null
-# REQUIRES: native && dbregs-set
-# UNSUPPORTED: system-windows
-# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-process launch -s
-watchpoint set variable -w write g_val
-# CHECK: Watchpoint created:
-continue
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = watchpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0
+++ /dev/null
-# REQUIRES: native
-# UNSUPPORTED: system-windows
-# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-b parent_func
-process launch
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = breakpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0
+++ /dev/null
-if 'lldb-repro' in config.available_features:
- config.unsupported = True
+++ /dev/null
-# REQUIRES: native && dbregs-set
-# UNSUPPORTED: system-windows
-# UNSUPPORTED: system-darwin
-# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-process launch -s
-watchpoint set variable -w write g_val
-# CHECK: Watchpoint created:
-continue
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = watchpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0
+++ /dev/null
-# REQUIRES: native
-# UNSUPPORTED: system-windows
-# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
-# RUN: %lldb -b -s %s %t | FileCheck %s
-b parent_func
-process launch
-# CHECK-NOT: function run in parent
-# CHECK: stop reason = breakpoint
-continue
-# CHECK: function run in parent
-# CHECK: child exited: 0