clayborg created this revision.
clayborg added reviewers: JDevlieghere, wallace, aadsm, labath, jingham.
clayborg requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This patch adds breakpoints to each target's statistics so we can track how 
long it takes to resolve each breakpoint. It also includes the structured data 
for each breakpoint so the exact breakpoint details are logged to allow for 
reproduction of slow resolving breakpoints. Each target gets a new 
"breakpoints" array that contains breakpoint details. Each breakpoint has 
"details" which is the JSON representation of a serialized breakpoint resolver 
and filter, "id" which is the breakpoint ID, and "resolveTime" which is the 
time in seconds it took to resolve the breakpoint. A snippet of the new data is 
shown here:

  "targets": [
    {
      "breakpoints": [
        {
          "details": {...},
          "id": 1,
          "resolveTime": 0.00039291599999999999
        },
        {
          "details": {...},
          "id": 2,
          "resolveTime": 0.00022679199999999999
        }
      ],
      "totalBreakpointResolveTime": 0.00061970799999999996
    }
  ]

This provides full details on exactly how breakpoints were set and how long it 
took to resolve them.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112587

Files:
  lldb/include/lldb/Breakpoint/Breakpoint.h
  lldb/source/Breakpoint/Breakpoint.cpp
  lldb/source/Target/Statistics.cpp
  lldb/test/API/commands/statistics/basic/TestStats.py

Index: lldb/test/API/commands/statistics/basic/TestStats.py
===================================================================
--- lldb/test/API/commands/statistics/basic/TestStats.py
+++ lldb/test/API/commands/statistics/basic/TestStats.py
@@ -283,3 +283,82 @@
         ]
         self.assertNotEqual(exe_module, None)
         self.verify_keys(exe_module, 'module dict for "%s"' % (exe), module_keys)
+
+    def test_breakpoints(self):
+        """Test "statistics dump"
+
+        Output expected to be something like:
+
+        {
+          "modules" : [...],
+          "targets" : [
+                {
+                    "firstStopTime": 0.34164492800000001,
+                    "launchOrAttachTime": 0.31969605400000001,
+                    "moduleIdentifiers": [...],
+                    "targetCreateTime": 0.0040863039999999998
+                    "expressionEvaluation": {
+                        "failures": 0,
+                        "successes": 0
+                    },
+                    "frameVariable": {
+                        "failures": 0,
+                        "successes": 0
+                    },
+                    "breakpoints": [
+                        {
+                            "details": {...},
+                            "id": 1,
+                            "resolveTime": 2.65438675
+                        },
+                        {
+                            "details": {...},
+                            "id": 2,
+                            "resolveTime": 4.3632581669999997
+                        }                        
+                    ]
+                }
+            ],
+            "totalDebugInfoByteSize": 182522234,
+            "totalDebugInfoIndexTime": 2.33343,
+            "totalDebugInfoParseTime": 8.2121400240000071,
+            "totalSymbolTableParseTime": 0.123,
+            "totalSymbolTableIndexTime": 0.234,
+            "totalBreakpointResolveTime": 7.0176449170000001
+        }
+
+        """
+        target = self.createTestTarget()
+        self.runCmd("b main.cpp:7")
+        self.runCmd("b a_function")
+        debug_stats = self.get_stats()
+        debug_stat_keys = [
+            'modules', 
+            'targets',
+            'totalSymbolTableParseTime',
+            'totalSymbolTableIndexTime',
+            'totalDebugInfoParseTime',
+            'totalDebugInfoIndexTime',
+            'totalDebugInfoByteSize',
+        ]
+        self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
+        target_stats = debug_stats['targets'][0]
+        keys_exist = [
+            'breakpoints',
+            'expressionEvaluation',
+            'frameVariable',
+            'targetCreateTime',
+            'moduleIdentifiers',
+            'totalBreakpointResolveTime',
+        ]
+        self.verify_keys(target_stats, '"stats"', keys_exist, None)
+        self.assertGreater(target_stats['totalBreakpointResolveTime'], 0.0)
+        breakpoints = target_stats['breakpoints']
+        bp_keys_exist = [
+            'details',
+            'id',
+            'resolveTime'
+        ]
+        for breakpoint in breakpoints:
+            self.verify_keys(breakpoint, 'target_stats["breakpoints"]', 
+                             bp_keys_exist, None)
Index: lldb/source/Target/Statistics.cpp
===================================================================
--- lldb/source/Target/Statistics.cpp
+++ lldb/source/Target/Statistics.cpp
@@ -80,6 +80,22 @@
   }
   target_metrics_json.try_emplace("targetCreateTime", m_create_time.count());
 
+  json::Array breakpoints_array;
+  std::unique_lock<std::recursive_mutex> lock;
+  target.GetBreakpointList().GetListMutex(lock);
+
+  const BreakpointList &breakpoints = target.GetBreakpointList();
+  size_t num_breakpoints = breakpoints.GetSize();
+  double totalBreakpointResolveTime = 0.0;
+  for (size_t i = 0; i < num_breakpoints; i++) {
+    Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
+    breakpoints_array.push_back(bp->GetStatistics());
+    totalBreakpointResolveTime += bp->GetResolveTime().count();
+  }
+  target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array));
+  target_metrics_json.try_emplace("totalBreakpointResolveTime",
+                                  totalBreakpointResolveTime);
+
   return target_metrics_json;
 }
 
Index: lldb/source/Breakpoint/Breakpoint.cpp
===================================================================
--- lldb/source/Breakpoint/Breakpoint.cpp
+++ lldb/source/Breakpoint/Breakpoint.cpp
@@ -439,12 +439,15 @@
 const BreakpointOptions &Breakpoint::GetOptions() const { return m_options; }
 
 void Breakpoint::ResolveBreakpoint() {
-  if (m_resolver_sp)
+  if (m_resolver_sp) {
+    ElapsedTime elapsed(m_resolve_time);
     m_resolver_sp->ResolveBreakpoint(*m_filter_sp);
+  }
 }
 
 void Breakpoint::ResolveBreakpointInModules(
     ModuleList &module_list, BreakpointLocationCollection &new_locations) {
+  ElapsedTime elapsed(m_resolve_time);
   m_locations.StartRecordingNewLocations(new_locations);
 
   m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
@@ -470,6 +473,7 @@
       } else
         delete new_locations_event;
     } else {
+      ElapsedTime elapsed(m_resolve_time);
       m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
     }
   }
@@ -1086,3 +1090,34 @@
 
   return bp_loc_sp;
 }
+
+json::Value Breakpoint::GetStatistics() {
+  json::Object bp;
+  bp.try_emplace("id", GetID());
+  if (!m_name_list.empty()) {
+    json::Array names;
+    for (const auto &name : m_name_list)
+      names.emplace_back(name);
+    bp.try_emplace("names", std::move(names));
+  }
+  bp.try_emplace("resolveTime", m_resolve_time.count());
+  if (!m_kind_description.empty())
+    bp.try_emplace("kindDescription", m_kind_description);
+  // Put the full structured data for reproducing this breakpoint in a key/value
+  // pair named "details". This allows the breakpoint's details to be visible
+  // in the stats in case we need to reproduce a breakpoint that has long 
+  // resolve times
+  StructuredData::ObjectSP bp_data_sp = SerializeToStructuredData();
+  if (bp_data_sp) {
+    std::string buffer;
+    llvm::raw_string_ostream ss(buffer);
+    json::OStream json_os(ss);
+    bp_data_sp->Serialize(json_os);
+    if (llvm::Expected<llvm::json::Value> expected_value =
+            llvm::json::parse(ss.str()))
+      bp.try_emplace("details", std::move(*expected_value));
+    else
+      llvm::consumeError(expected_value.takeError());
+  }
+  return json::Value(std::move(bp));
+}
Index: lldb/include/lldb/Breakpoint/Breakpoint.h
===================================================================
--- lldb/include/lldb/Breakpoint/Breakpoint.h
+++ lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -22,6 +22,7 @@
 #include "lldb/Breakpoint/Stoppoint.h"
 #include "lldb/Breakpoint/StoppointHitCounter.h"
 #include "lldb/Core/SearchFilter.h"
+#include "lldb/Target/Statistics.h"
 #include "lldb/Utility/Event.h"
 #include "lldb/Utility/StringList.h"
 #include "lldb/Utility/StructuredData.h"
@@ -576,6 +577,12 @@
   static lldb::BreakpointSP CopyFromBreakpoint(lldb::TargetSP new_target,
       const Breakpoint &bp_to_copy_from);
 
+  /// Get statistics associated with this breakpoint in JSON format.
+  llvm::json::Value GetStatistics();
+
+  /// Get the time it took to resolve all locations in this breakpoint.
+  StatsDuration GetResolveTime() const { return m_resolve_time; }
+
 protected:
   friend class Target;
   // Protected Methods
@@ -653,6 +660,8 @@
 
   BreakpointName::Permissions m_permissions;
 
+  StatsDuration m_resolve_time{0.0};
+
   void SendBreakpointChangedEvent(lldb::BreakpointEventType eventKind);
 
   void SendBreakpointChangedEvent(BreakpointEventData *data);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to