Add unix signal hit counts to the target statistics.
authorGreg Clayton <gclayton@fb.com>
Thu, 28 Oct 2021 01:33:17 +0000 (18:33 -0700)
committerGreg Clayton <gclayton@fb.com>
Thu, 28 Oct 2021 05:31:14 +0000 (22:31 -0700)
Android and other platforms make wide use of signals when running applications and this can slow down debug sessions. Tracking this statistic can help us to determine why a debug session is slow.

The new data appears inside each target object and reports the signal hit counts:

      "signals": [
        {
          "SIGSTOP": 1
        },
        {
          "SIGUSR1": 1
        }
      ],

Differential Revision: https://reviews.llvm.org/D112683

lldb/include/lldb/Target/UnixSignals.h
lldb/source/Target/Statistics.cpp
lldb/source/Target/StopInfo.cpp
lldb/source/Target/UnixSignals.cpp
lldb/test/API/functionalities/signal/TestSendSignal.py

index 6fecdda12defc542c72096f833e7b29ddfa068be..1c91c9fdd48957aadacfc87e3fada50e2295164a 100644 (file)
@@ -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 @@ public:
 
   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 @@ protected:
     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,
index a7207ac5d990d9f6d8e04a4656b4ac9bd0ace062..41639264a949b74684526290bbd1841af53da267 100644 (file)
@@ -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 @@ json::Value TargetStats::ToJSON(Target &target) {
     }
   }
 
+  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);
index 8258705990eb0ec6e131390450518c21fea60718..ef7dff4052ead8da64049c092adb26acf487995c 100644 (file)
@@ -307,7 +307,7 @@ protected:
 
           // 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 @@ protected:
             }
 
             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<std::unordered_set<break_id_t>::iterator, bool> result =
@@ -535,7 +535,7 @@ protected:
               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 @@ protected:
         // 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 @@ public:
   // 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 @@ public:
         process_sp->AddPreResumeAction(SentryPreResumeAction, this);
       }
     }
-    
+
     void DoReenable() {
       if (process_sp && watchpoint_sp) {
         bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode();
@@ -637,13 +637,13 @@ public:
         }
       }
     }
-    
+
     ~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 @@ protected:
     // 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 @@ protected:
 
           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 @@ StopInfo::CreateStopReasonWithWatchpointID(Thread &thread, break_id_t watch_id,
 
 StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo,
                                                 const char *description) {
+  thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo);
   return StopInfoSP(new StopInfoUnixSignal(thread, signo, description));
 }
 
index 6e857e6618d39f31281dd273396542a3db80f675..26ff0bbd3825f2ca2fc299f6d4bfd7c5efc755af 100644 (file)
@@ -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 @@ UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress,
 
   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);
+}
index f1ccc6d024fb3d17aee2fa85443d0bae417760ce..88caa2daaf42e3203a01ea438452784c3f794bac 100644 (file)
@@ -95,6 +95,10 @@ class SendSignalTestCase(TestBase):
             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()