diff --git a/lldb/include/lldb/Target/UnixSignals.h b/lldb/include/lldb/Target/UnixSignals.h --- a/lldb/include/lldb/Target/UnixSignals.h +++ b/lldb/include/lldb/Target/UnixSignals.h @@ -16,6 +16,7 @@ #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" #include "llvm/ADT/Optional.h" +#include "llvm/Support/JSON.h" namespace lldb_private { @@ -80,6 +81,18 @@ void RemoveSignal(int signo); + /// Track how many times signals are hit as stop reasons. + void IncrementSignalHitCount(int signo); + + /// Get the hit count statistics for signals. + /// + /// Gettings statistics on the hit counts of signals can help explain why some + /// debug sessions are slow since each stop takes a few hundred ms and some + /// software use signals a lot and can cause slow debugging performance if + /// they are used too often. Even if a signal is not stopped at, it will auto + /// continue the process and a delay will happen. + llvm::json::Value GetHitCountStatistics() const; + // Returns a current version of the data stored in this class. Version gets // incremented each time Set... method is called. uint64_t GetVersion() const; @@ -99,6 +112,7 @@ ConstString m_name; ConstString m_alias; std::string m_description; + uint32_t m_hit_count = 0; bool m_suppress : 1, m_stop : 1, m_notify : 1; Signal(const char *name, bool default_suppress, bool default_stop, diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -11,7 +11,9 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" using namespace lldb; using namespace lldb_private; @@ -95,6 +97,13 @@ } } + ProcessSP process_sp = target.GetProcessSP(); + if (process_sp) { + UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals(); + if (unix_signals_sp) + target_metrics_json.try_emplace("signals", + unix_signals_sp->GetHitCountStatistics()); + } target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array)); target_metrics_json.try_emplace("totalBreakpointResolveTime", totalBreakpointResolveTime); diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -307,7 +307,7 @@ // There's one other complication here. We may have run an async // breakpoint callback that said we should stop. We only want to - // override that if another breakpoint action says we shouldn't + // override that if another breakpoint action says we shouldn't // stop. If nobody else has an opinion, then we should stop if the // async callback says we should. An example of this is the async // shared library load notification breakpoint and the setting @@ -425,7 +425,7 @@ } internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal(); - + // First run the precondition, but since the precondition is per // breakpoint, only run it once per breakpoint. std::pair::iterator, bool> result = @@ -535,7 +535,7 @@ else actually_said_continue = true; } - + // If we are going to stop for this breakpoint, then remove the // breakpoint. if (callback_says_stop && bp_loc_sp && @@ -579,7 +579,7 @@ // Override should_stop decision when we have completed step plan // additionally to the breakpoint m_should_stop = true; - + // We know we're stopping for a completed plan and we don't want to // show the breakpoint stop, so compute the public stop info immediately // here. @@ -615,7 +615,7 @@ // performing watchpoint actions. class WatchpointSentry { public: - WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp), + WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp), watchpoint_sp(w_sp) { if (process_sp && watchpoint_sp) { const bool notify = false; @@ -624,7 +624,7 @@ process_sp->AddPreResumeAction(SentryPreResumeAction, this); } } - + void DoReenable() { if (process_sp && watchpoint_sp) { bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode(); @@ -637,13 +637,13 @@ } } } - + ~WatchpointSentry() { DoReenable(); if (process_sp) process_sp->ClearPreResumeAction(SentryPreResumeAction, this); } - + static bool SentryPreResumeAction(void *sentry_void) { WatchpointSentry *sentry = (WatchpointSentry *) sentry_void; sentry->DoReenable(); @@ -724,14 +724,14 @@ // course of this code. Also by default we're going to stop, so set that // here. m_should_stop = true; - + ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp( thread_sp->CalculateTarget()->GetWatchpointList().FindByID( - GetValue())); + GetValue())); if (wp_sp) { ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); ProcessSP process_sp = exe_ctx.GetProcessSP(); @@ -889,12 +889,12 @@ bool old_async = debugger.GetAsyncExecution(); debugger.SetAsyncExecution(true); - + StoppointCallbackContext context(event_ptr, exe_ctx, false); bool stop_requested = wp_sp->InvokeCallback(&context); - + debugger.SetAsyncExecution(old_async); - + // Also make sure that the callback hasn't continued the target. If // it did, when we'll set m_should_stop to false and get out of here. if (HasTargetRunSinceMe()) @@ -1272,6 +1272,7 @@ StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo, const char *description) { + thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo); return StopInfoSP(new StopInfoUnixSignal(thread, signo, description)); } diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp --- a/lldb/source/Target/UnixSignals.cpp +++ b/lldb/source/Target/UnixSignals.cpp @@ -15,6 +15,7 @@ #include "lldb/Utility/ArchSpec.h" using namespace lldb_private; +using namespace llvm; UnixSignals::Signal::Signal(const char *name, bool default_suppress, bool default_stop, bool default_notify, @@ -312,3 +313,20 @@ return result; } + +void UnixSignals::IncrementSignalHitCount(int signo) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + pos->second.m_hit_count += 1; +} + +json::Value UnixSignals::GetHitCountStatistics() const { + json::Array json_signals; + for (const auto &pair: m_signals) { + if (pair.second.m_hit_count > 0) + json_signals.emplace_back(json::Object{ + { pair.second.m_name.GetCString(), pair.second.m_hit_count } + }); + } + return std::move(json_signals); +} diff --git a/lldb/test/API/functionalities/signal/TestSendSignal.py b/lldb/test/API/functionalities/signal/TestSendSignal.py --- a/lldb/test/API/functionalities/signal/TestSendSignal.py +++ b/lldb/test/API/functionalities/signal/TestSendSignal.py @@ -95,6 +95,10 @@ thread.GetStopReasonDataAtIndex(0), lldbutil.get_signal_number('SIGUSR1'), "The stop signal was SIGUSR1") + self.match("statistics dump", + [r'"signals": \[', r'"SIGUSR1": 1']) + + def match_state(self, process_listener, expected_state): num_seconds = 5 broadcaster = self.process().GetBroadcaster()