--- /dev/null
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -std=c99
+
+include Makefile.rules
--- /dev/null
+"""
+debugserver used to block replying to the 'D' packet
+till it had joined the profiling thread. If the profiling interval
+was too long, that would mean it would take longer than the packet
+timeout to reply, and the detach would time out. Make sure that doesn't
+happen.
+"""
+
+
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import os
+import signal
+
+class TestDetachVrsProfile(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipUnlessDarwin
+ @skipIfOutOfTreeDebugserver
+ def test_profile_and_detach(self):
+ """There can be many tests in a test case - describe this test here."""
+ self.build()
+ self.main_source_file = lldb.SBFileSpec("main.c")
+ self.do_profile_and_detach()
+
+ def do_profile_and_detach(self):
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+ "Set a breakpoint here", self.main_source_file)
+
+ interp = self.dbg.GetCommandInterpreter()
+ result = lldb.SBCommandReturnObject()
+
+ # First make sure we are getting async data. Set a short interval, continue a bit and check:
+ interp.HandleCommand("process plugin packet send 'QSetEnableAsyncProfiling;enable:1;interval_usec:500000;scan_type=0x5;'", result)
+ self.assertTrue(result.Succeeded(), "process packet send failed: %s"%(result.GetError()))
+
+ # Run a bit to give us a change to collect profile data:
+ bkpt.SetIgnoreCount(1)
+ threads = lldbutil.continue_to_breakpoint(process, bkpt)
+ self.assertEqual(len(threads), 1, "Hit our breakpoint again.")
+ str = process.GetAsyncProfileData(1000)
+ self.assertTrue(len(str) > 0, "Got some profile data")
+
+ # Now make the profiling interval very long and try to detach.
+ interp.HandleCommand("process plugin packet send 'QSetEnableAsyncProfiling;enable:1;interval_usec:10000000;scan_type=0x5;'", result)
+ self.assertTrue(result.Succeeded(), "process packet send failed: %s"%(result.GetError()))
+ self.dbg.SetAsync(True)
+ listener = self.dbg.GetListener()
+
+ # We don't want to hit our breakpoint anymore.
+ bkpt.SetEnabled(False)
+
+ # Record our process pid so we can kill it since we are going to detach...
+ self.pid = process.GetProcessID()
+ def cleanup():
+ self.dbg.SetAsync(False)
+ os.kill(self.pid, signal.SIGKILL)
+ self.addTearDownHook(cleanup)
+
+ process.Continue()
+
+ event = lldb.SBEvent()
+ success = listener.WaitForEventForBroadcaster(0, process.GetBroadcaster(), event)
+ self.assertTrue(success, "Got an event which should be running.")
+ event_state = process.GetStateFromEvent(event)
+ self.assertEqual(event_state, lldb.eStateRunning, "Got the running event")
+
+ # Now detach:
+ error = process.Detach()
+ self.assertTrue(error.Success(), "Detached successfully")
--- /dev/null
+#include <stdio.h>
+
+int
+main()
+{
+ while (1) {
+ sleep(1); // Set a breakpoint here
+ printf("I slept\n");
+ }
+ return 0;
+}
eMachProcessFlagsUsingFBS = (1 << 3), // only read via ProcessUsingFrontBoard()
eMachProcessFlagsBoardCalculated = (1 << 4)
};
+
+ enum {
+ eMachProcessProfileNone = 0,
+ eMachProcessProfileCancel = (1 << 0)
+ };
+
void Clear(bool detaching = false);
void ReplyToAllExceptions();
void PrivateResume();
+ void StopProfileThread();
uint32_t Flags() const { return m_flags; }
nub_state_t DoSIGSTOP(bool clear_bps_and_wps, bool allow_running,
m_profile_data_mutex; // Multithreaded protection for profile info data
std::vector<std::string>
m_profile_data; // Profile data, must be protected by m_profile_data_mutex
-
+ PThreadEvent m_profile_events; // Used for the profile thread cancellable wait
DNBThreadResumeActions m_thread_actions; // The thread actions for the current
// MachProcess::Resume() call
MachException::Message::collection m_exception_messages; // A collection of
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <algorithm>
+#include <chrono>
#include <map>
#import <Foundation/Foundation.h>
m_stdio_mutex(PTHREAD_MUTEX_RECURSIVE), m_stdout_data(),
m_profile_enabled(false), m_profile_interval_usec(0), m_profile_thread(0),
m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data(),
+ m_profile_events(0, eMachProcessProfileCancel),
m_thread_actions(), m_exception_messages(),
m_exception_messages_mutex(PTHREAD_MUTEX_RECURSIVE), m_thread_list(),
m_activities(), m_state(eStateUnloaded),
m_exception_messages.clear();
}
m_activities.Clear();
- if (m_profile_thread) {
- pthread_join(m_profile_thread, NULL);
- m_profile_thread = NULL;
- }
+ StopProfileThread();
}
bool MachProcess::StartSTDIOThread() {
if (m_profile_enabled && (m_profile_thread == NULL)) {
StartProfileThread();
} else if (!m_profile_enabled && m_profile_thread) {
- pthread_join(m_profile_thread, NULL);
- m_profile_thread = NULL;
+ StopProfileThread();
}
}
+void MachProcess::StopProfileThread() {
+ if (m_profile_thread == NULL)
+ return;
+ m_profile_events.SetEvents(eMachProcessProfileCancel);
+ pthread_join(m_profile_thread, NULL);
+ m_profile_thread = NULL;
+ m_profile_events.ResetEvents(eMachProcessProfileCancel);
+}
+
bool MachProcess::StartProfileThread() {
DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
// Create the thread that profiles the inferior and reports back if enabled
// Done. Get out of this thread.
break;
}
-
- // A simple way to set up the profile interval. We can also use select() or
- // dispatch timer source if necessary.
- usleep(proc->ProfileInterval());
+ timespec ts;
+ {
+ using namespace std::chrono;
+ std::chrono::microseconds dur(proc->ProfileInterval());
+ const auto dur_secs = duration_cast<seconds>(dur);
+ const auto dur_usecs = dur % std::chrono::seconds(1);
+ DNBTimer::OffsetTimeOfDay(&ts, dur_secs.count(),
+ dur_usecs.count());
+ }
+ uint32_t bits_set =
+ proc->m_profile_events.WaitForSetEvents(eMachProcessProfileCancel, &ts);
+ // If we got bits back, we were told to exit. Do so.
+ if (bits_set & eMachProcessProfileCancel)
+ break;
}
return NULL;
}