xref: /freebsd/contrib/llvm-project/lldb/source/Target/Statistics.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- Statistics.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Target/Statistics.h"
10 
11 #include "lldb/Core/Debugger.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Core/PluginManager.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Symbol/SymbolFile.h"
16 #include "lldb/Target/DynamicLoader.h"
17 #include "lldb/Target/Process.h"
18 #include "lldb/Target/Target.h"
19 #include "lldb/Target/UnixSignals.h"
20 #include "lldb/Utility/StructuredData.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 using namespace llvm;
25 
EmplaceSafeString(llvm::json::Object & obj,llvm::StringRef key,const std::string & str)26 static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
27                               const std::string &str) {
28   if (str.empty())
29     return;
30   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
31     obj.try_emplace(key, str);
32   else
33     obj.try_emplace(key, llvm::json::fixUTF8(str));
34 }
35 
ToJSON() const36 json::Value StatsSuccessFail::ToJSON() const {
37   return json::Object{{"successes", successes}, {"failures", failures}};
38 }
39 
elapsed(const StatsTimepoint & start,const StatsTimepoint & end)40 static double elapsed(const StatsTimepoint &start, const StatsTimepoint &end) {
41   StatsDuration::Duration elapsed =
42       end.time_since_epoch() - start.time_since_epoch();
43   return elapsed.count();
44 }
45 
CollectStats(Target & target)46 void TargetStats::CollectStats(Target &target) {
47   m_module_identifiers.clear();
48   for (ModuleSP module_sp : target.GetImages().Modules())
49     m_module_identifiers.emplace_back((intptr_t)module_sp.get());
50 }
51 
ToJSON() const52 json::Value ModuleStats::ToJSON() const {
53   json::Object module;
54   EmplaceSafeString(module, "path", path);
55   EmplaceSafeString(module, "uuid", uuid);
56   EmplaceSafeString(module, "triple", triple);
57   module.try_emplace("identifier", identifier);
58   module.try_emplace("symbolTableParseTime", symtab_parse_time);
59   module.try_emplace("symbolTableIndexTime", symtab_index_time);
60   module.try_emplace("symbolTableLoadedFromCache", symtab_loaded_from_cache);
61   module.try_emplace("symbolTableSavedToCache", symtab_saved_to_cache);
62   module.try_emplace("debugInfoParseTime", debug_parse_time);
63   module.try_emplace("debugInfoIndexTime", debug_index_time);
64   module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size);
65   module.try_emplace("debugInfoIndexLoadedFromCache",
66                      debug_info_index_loaded_from_cache);
67   module.try_emplace("debugInfoIndexSavedToCache",
68                      debug_info_index_saved_to_cache);
69   module.try_emplace("debugInfoEnabled", debug_info_enabled);
70   module.try_emplace("debugInfoHadVariableErrors",
71                      debug_info_had_variable_errors);
72   module.try_emplace("debugInfoHadIncompleteTypes",
73                      debug_info_had_incomplete_types);
74   module.try_emplace("symbolTableStripped", symtab_stripped);
75   module.try_emplace("symbolTableSymbolCount", symtab_symbol_count);
76   module.try_emplace("dwoFileCount", dwo_file_count);
77   module.try_emplace("loadedDwoFileCount", loaded_dwo_file_count);
78 
79   if (!symbol_locator_time.map.empty()) {
80     json::Object obj;
81     for (const auto &entry : symbol_locator_time.map)
82       obj.try_emplace(entry.first().str(), entry.second);
83     module.try_emplace("symbolLocatorTime", std::move(obj));
84   }
85 
86   if (!symfile_path.empty())
87     module.try_emplace("symbolFilePath", symfile_path);
88 
89   if (!symfile_modules.empty()) {
90     json::Array symfile_ids;
91     for (const auto symfile_id : symfile_modules)
92       symfile_ids.emplace_back(symfile_id);
93     module.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids));
94   }
95 
96   if (!type_system_stats.empty()) {
97     json::Array type_systems;
98     for (const auto &entry : type_system_stats) {
99       json::Object obj;
100       obj.try_emplace(entry.first().str(), entry.second);
101       type_systems.emplace_back(std::move(obj));
102     }
103     module.try_emplace("typeSystemInfo", std::move(type_systems));
104   }
105 
106   return module;
107 }
108 
ToJSON() const109 llvm::json::Value ConstStringStats::ToJSON() const {
110   json::Object obj;
111   obj.try_emplace<int64_t>("bytesTotal", stats.GetBytesTotal());
112   obj.try_emplace<int64_t>("bytesUsed", stats.GetBytesUsed());
113   obj.try_emplace<int64_t>("bytesUnused", stats.GetBytesUnused());
114   return obj;
115 }
116 
117 json::Value
ToJSON(Target & target,const lldb_private::StatisticsOptions & options)118 TargetStats::ToJSON(Target &target,
119                     const lldb_private::StatisticsOptions &options) {
120   json::Object target_metrics_json;
121   ProcessSP process_sp = target.GetProcessSP();
122   const bool summary_only = options.GetSummaryOnly();
123   const bool include_modules = options.GetIncludeModules();
124   if (!summary_only) {
125     CollectStats(target);
126 
127     json::Array json_module_uuid_array;
128     for (auto module_identifier : m_module_identifiers)
129       json_module_uuid_array.emplace_back(module_identifier);
130 
131     target_metrics_json.try_emplace(m_expr_eval.name, m_expr_eval.ToJSON());
132     target_metrics_json.try_emplace(m_frame_var.name, m_frame_var.ToJSON());
133     if (include_modules)
134       target_metrics_json.try_emplace("moduleIdentifiers",
135                                       std::move(json_module_uuid_array));
136 
137     if (m_launch_or_attach_time && m_first_private_stop_time) {
138       double elapsed_time =
139           elapsed(*m_launch_or_attach_time, *m_first_private_stop_time);
140       target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time);
141     }
142     if (m_launch_or_attach_time && m_first_public_stop_time) {
143       double elapsed_time =
144           elapsed(*m_launch_or_attach_time, *m_first_public_stop_time);
145       target_metrics_json.try_emplace("firstStopTime", elapsed_time);
146     }
147     target_metrics_json.try_emplace("targetCreateTime",
148                                     m_create_time.get().count());
149 
150     json::Array breakpoints_array;
151     double totalBreakpointResolveTime = 0.0;
152     // Report both the normal breakpoint list and the internal breakpoint list.
153     for (int i = 0; i < 2; ++i) {
154       BreakpointList &breakpoints = target.GetBreakpointList(i == 1);
155       std::unique_lock<std::recursive_mutex> lock;
156       breakpoints.GetListMutex(lock);
157       size_t num_breakpoints = breakpoints.GetSize();
158       for (size_t i = 0; i < num_breakpoints; i++) {
159         Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
160         breakpoints_array.push_back(bp->GetStatistics());
161         totalBreakpointResolveTime += bp->GetResolveTime().count();
162       }
163     }
164     target_metrics_json.try_emplace("breakpoints",
165                                     std::move(breakpoints_array));
166     target_metrics_json.try_emplace("totalBreakpointResolveTime",
167                                     totalBreakpointResolveTime);
168 
169     if (process_sp) {
170       UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals();
171       if (unix_signals_sp)
172         target_metrics_json.try_emplace(
173             "signals", unix_signals_sp->GetHitCountStatistics());
174     }
175   }
176 
177   // Counting "totalSharedLibraryEventHitCount" from breakpoints of kind
178   // "shared-library-event".
179   {
180     uint32_t shared_library_event_breakpoint_hit_count = 0;
181     // The "shared-library-event" is only found in the internal breakpoint list.
182     BreakpointList &breakpoints = target.GetBreakpointList(/* internal */ true);
183     std::unique_lock<std::recursive_mutex> lock;
184     breakpoints.GetListMutex(lock);
185     size_t num_breakpoints = breakpoints.GetSize();
186     for (size_t i = 0; i < num_breakpoints; i++) {
187       Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
188       if (strcmp(bp->GetBreakpointKind(), "shared-library-event") == 0)
189         shared_library_event_breakpoint_hit_count += bp->GetHitCount();
190     }
191 
192     target_metrics_json.try_emplace("totalSharedLibraryEventHitCount",
193                                     shared_library_event_breakpoint_hit_count);
194   }
195 
196   if (process_sp) {
197     uint32_t stop_id = process_sp->GetStopID();
198     target_metrics_json.try_emplace("stopCount", stop_id);
199 
200     llvm::StringRef dyld_plugin_name;
201     if (process_sp->GetDynamicLoader())
202       dyld_plugin_name = process_sp->GetDynamicLoader()->GetPluginName();
203     target_metrics_json.try_emplace("dyldPluginName", dyld_plugin_name);
204   }
205   target_metrics_json.try_emplace("sourceMapDeduceCount",
206                                   m_source_map_deduce_count);
207   target_metrics_json.try_emplace("sourceRealpathAttemptCount",
208                                   m_source_realpath_attempt_count);
209   target_metrics_json.try_emplace("sourceRealpathCompatibleCount",
210                                   m_source_realpath_compatible_count);
211   target_metrics_json.try_emplace("summaryProviderStatistics",
212                                   target.GetSummaryStatisticsCache().ToJSON());
213   return target_metrics_json;
214 }
215 
Reset(Target & target)216 void TargetStats::Reset(Target &target) {
217   m_launch_or_attach_time.reset();
218   m_first_private_stop_time.reset();
219   m_first_public_stop_time.reset();
220   // Report both the normal breakpoint list and the internal breakpoint list.
221   for (int i = 0; i < 2; ++i) {
222     BreakpointList &breakpoints = target.GetBreakpointList(i == 1);
223     std::unique_lock<std::recursive_mutex> lock;
224     breakpoints.GetListMutex(lock);
225     size_t num_breakpoints = breakpoints.GetSize();
226     for (size_t i = 0; i < num_breakpoints; i++) {
227       Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
228       bp->ResetStatistics();
229     }
230   }
231   target.GetSummaryStatisticsCache().Reset();
232 }
233 
SetLaunchOrAttachTime()234 void TargetStats::SetLaunchOrAttachTime() {
235   m_launch_or_attach_time = StatsClock::now();
236   m_first_private_stop_time = std::nullopt;
237 }
238 
SetFirstPrivateStopTime()239 void TargetStats::SetFirstPrivateStopTime() {
240   // Launching and attaching has many paths depending on if synchronous mode
241   // was used or if we are stopping at the entry point or not. Only set the
242   // first stop time if it hasn't already been set.
243   if (!m_first_private_stop_time)
244     m_first_private_stop_time = StatsClock::now();
245 }
246 
SetFirstPublicStopTime()247 void TargetStats::SetFirstPublicStopTime() {
248   // Launching and attaching has many paths depending on if synchronous mode
249   // was used or if we are stopping at the entry point or not. Only set the
250   // first stop time if it hasn't already been set.
251   if (!m_first_public_stop_time)
252     m_first_public_stop_time = StatsClock::now();
253 }
254 
IncreaseSourceMapDeduceCount()255 void TargetStats::IncreaseSourceMapDeduceCount() {
256   ++m_source_map_deduce_count;
257 }
258 
IncreaseSourceRealpathAttemptCount(uint32_t count)259 void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count) {
260   m_source_realpath_attempt_count += count;
261 }
262 
IncreaseSourceRealpathCompatibleCount(uint32_t count)263 void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) {
264   m_source_realpath_compatible_count += count;
265 }
266 
267 bool DebuggerStats::g_collecting_stats = false;
268 
ResetStatistics(Debugger & debugger,Target * target)269 void DebuggerStats::ResetStatistics(Debugger &debugger, Target *target) {
270   std::lock_guard<std::recursive_mutex> guard(
271       Module::GetAllocationModuleCollectionMutex());
272   const uint64_t num_modules = target != nullptr
273                                    ? target->GetImages().GetSize()
274                                    : Module::GetNumberAllocatedModules();
275   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
276     Module *module = target != nullptr
277                          ? target->GetImages().GetModuleAtIndex(image_idx).get()
278                          : Module::GetAllocatedModuleAtIndex(image_idx);
279     if (module == nullptr)
280       continue;
281     module->ResetStatistics();
282   }
283   if (target)
284     target->ResetStatistics();
285   else {
286     for (const auto &target : debugger.GetTargetList().Targets())
287       target->ResetStatistics();
288   }
289 }
290 
ReportStatistics(Debugger & debugger,Target * target,const lldb_private::StatisticsOptions & options)291 llvm::json::Value DebuggerStats::ReportStatistics(
292     Debugger &debugger, Target *target,
293     const lldb_private::StatisticsOptions &options) {
294 
295   const bool summary_only = options.GetSummaryOnly();
296   const bool load_all_debug_info = options.GetLoadAllDebugInfo();
297   const bool include_targets = options.GetIncludeTargets();
298   const bool include_modules = options.GetIncludeModules();
299   const bool include_transcript = options.GetIncludeTranscript();
300   const bool include_plugins = options.GetIncludePlugins();
301 
302   json::Array json_targets;
303   json::Array json_modules;
304   StatisticsMap symbol_locator_total_time;
305   double symtab_parse_time = 0.0;
306   double symtab_index_time = 0.0;
307   double debug_parse_time = 0.0;
308   double debug_index_time = 0.0;
309   uint32_t symtabs_loaded = 0;
310   uint32_t symtabs_loaded_from_cache = 0;
311   uint32_t symtabs_saved_to_cache = 0;
312   uint32_t debug_index_loaded = 0;
313   uint32_t debug_index_saved = 0;
314   uint64_t debug_info_size = 0;
315 
316   std::lock_guard<std::recursive_mutex> guard(
317       Module::GetAllocationModuleCollectionMutex());
318   const uint64_t num_modules = target != nullptr
319                                    ? target->GetImages().GetSize()
320                                    : Module::GetNumberAllocatedModules();
321   uint32_t num_debug_info_enabled_modules = 0;
322   uint32_t num_modules_has_debug_info = 0;
323   uint32_t num_modules_with_variable_errors = 0;
324   uint32_t num_modules_with_incomplete_types = 0;
325   uint32_t num_stripped_modules = 0;
326   uint32_t symtab_symbol_count = 0;
327   uint32_t total_loaded_dwo_file_count = 0;
328   uint32_t total_dwo_file_count = 0;
329   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
330     Module *module = target != nullptr
331                          ? target->GetImages().GetModuleAtIndex(image_idx).get()
332                          : Module::GetAllocatedModuleAtIndex(image_idx);
333     ModuleStats module_stat;
334     module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
335     module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
336     module_stat.symbol_locator_time = module->GetSymbolLocatorStatistics();
337     symbol_locator_total_time.merge(module_stat.symbol_locator_time);
338     Symtab *symtab = module->GetSymtab(/*can_create=*/false);
339     if (symtab) {
340       module_stat.symtab_symbol_count = symtab->GetNumSymbols();
341       symtab_symbol_count += module_stat.symtab_symbol_count;
342       ++symtabs_loaded;
343       module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
344       if (module_stat.symtab_loaded_from_cache)
345         ++symtabs_loaded_from_cache;
346       module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
347       if (module_stat.symtab_saved_to_cache)
348         ++symtabs_saved_to_cache;
349     }
350     SymbolFile *sym_file = module->GetSymbolFile(/*can_create=*/false);
351     if (sym_file) {
352       if (!summary_only) {
353         if (sym_file->GetObjectFile() != module->GetObjectFile())
354           module_stat.symfile_path =
355               sym_file->GetObjectFile()->GetFileSpec().GetPath();
356         ModuleList symbol_modules = sym_file->GetDebugInfoModules();
357         for (const auto &symbol_module : symbol_modules.Modules())
358           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
359       }
360       std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) =
361           sym_file->GetDwoFileCounts();
362       total_dwo_file_count += module_stat.dwo_file_count;
363       total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
364       module_stat.debug_info_index_loaded_from_cache =
365           sym_file->GetDebugInfoIndexWasLoadedFromCache();
366       if (module_stat.debug_info_index_loaded_from_cache)
367         ++debug_index_loaded;
368       module_stat.debug_info_index_saved_to_cache =
369           sym_file->GetDebugInfoIndexWasSavedToCache();
370       if (module_stat.debug_info_index_saved_to_cache)
371         ++debug_index_saved;
372       module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
373       module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
374       module_stat.debug_info_size =
375           sym_file->GetDebugInfoSize(load_all_debug_info);
376       module_stat.symtab_stripped = module->GetObjectFile()->IsStripped();
377       if (module_stat.symtab_stripped)
378         ++num_stripped_modules;
379       module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() &&
380                                        module_stat.debug_info_size > 0;
381       module_stat.debug_info_had_variable_errors =
382           sym_file->GetDebugInfoHadFrameVariableErrors();
383       if (module_stat.debug_info_enabled)
384         ++num_debug_info_enabled_modules;
385       if (module_stat.debug_info_size > 0)
386         ++num_modules_has_debug_info;
387       if (module_stat.debug_info_had_variable_errors)
388         ++num_modules_with_variable_errors;
389     }
390     symtab_parse_time += module_stat.symtab_parse_time;
391     symtab_index_time += module_stat.symtab_index_time;
392     debug_parse_time += module_stat.debug_parse_time;
393     debug_index_time += module_stat.debug_index_time;
394     debug_info_size += module_stat.debug_info_size;
395     module->ForEachTypeSystem([&](lldb::TypeSystemSP ts) {
396       if (auto stats = ts->ReportStatistics())
397         module_stat.type_system_stats.insert({ts->GetPluginName(), *stats});
398       if (ts->GetHasForcefullyCompletedTypes())
399         module_stat.debug_info_had_incomplete_types = true;
400       return true;
401     });
402     if (module_stat.debug_info_had_incomplete_types)
403       ++num_modules_with_incomplete_types;
404 
405     if (include_modules) {
406       module_stat.identifier = (intptr_t)module;
407       module_stat.path = module->GetFileSpec().GetPath();
408       if (ConstString object_name = module->GetObjectName()) {
409         module_stat.path.append(1, '(');
410         module_stat.path.append(object_name.GetStringRef().str());
411         module_stat.path.append(1, ')');
412       }
413       module_stat.uuid = module->GetUUID().GetAsString();
414       module_stat.triple = module->GetArchitecture().GetTriple().str();
415       json_modules.emplace_back(module_stat.ToJSON());
416     }
417   }
418 
419   json::Object global_stats{
420       {"totalSymbolTableParseTime", symtab_parse_time},
421       {"totalSymbolTableIndexTime", symtab_index_time},
422       {"totalSymbolTablesLoaded", symtabs_loaded},
423       {"totalSymbolTablesLoadedFromCache", symtabs_loaded_from_cache},
424       {"totalSymbolTablesSavedToCache", symtabs_saved_to_cache},
425       {"totalDebugInfoParseTime", debug_parse_time},
426       {"totalDebugInfoIndexTime", debug_index_time},
427       {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded},
428       {"totalDebugInfoIndexSavedToCache", debug_index_saved},
429       {"totalDebugInfoByteSize", debug_info_size},
430       {"totalModuleCount", num_modules},
431       {"totalModuleCountHasDebugInfo", num_modules_has_debug_info},
432       {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors},
433       {"totalModuleCountWithIncompleteTypes",
434        num_modules_with_incomplete_types},
435       {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
436       {"totalSymbolTableStripped", num_stripped_modules},
437       {"totalSymbolTableSymbolCount", symtab_symbol_count},
438       {"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
439       {"totalDwoFileCount", total_dwo_file_count},
440   };
441 
442   if (include_targets) {
443     if (target) {
444       json_targets.emplace_back(target->ReportStatistics(options));
445     } else {
446       for (const auto &target : debugger.GetTargetList().Targets())
447         json_targets.emplace_back(target->ReportStatistics(options));
448     }
449     global_stats.try_emplace("targets", std::move(json_targets));
450   }
451 
452   if (!symbol_locator_total_time.map.empty()) {
453     json::Object obj;
454     for (const auto &entry : symbol_locator_total_time.map)
455       obj.try_emplace(entry.first().str(), entry.second);
456     global_stats.try_emplace("totalSymbolLocatorTime", std::move(obj));
457   }
458 
459   ConstStringStats const_string_stats;
460   json::Object json_memory{
461       {"strings", const_string_stats.ToJSON()},
462   };
463   global_stats.try_emplace("memory", std::move(json_memory));
464   if (!summary_only) {
465     json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics();
466     global_stats.try_emplace("commands", std::move(cmd_stats));
467   }
468 
469   if (include_modules) {
470     global_stats.try_emplace("modules", std::move(json_modules));
471   }
472 
473   if (include_transcript) {
474     // When transcript is available, add it to the to-be-returned statistics.
475     //
476     // NOTE:
477     // When the statistics is polled by an LLDB command:
478     // - The transcript in the returned statistics *will NOT* contain the
479     //   returned statistics itself (otherwise infinite recursion).
480     // - The returned statistics *will* be written to the internal transcript
481     //   buffer. It *will* appear in the next statistcs or transcript poll.
482     //
483     // For example, let's say the following commands are run in order:
484     // - "version"
485     // - "statistics dump"  <- call it "A"
486     // - "statistics dump"  <- call it "B"
487     // The output of "A" will contain the transcript of "version" and
488     // "statistics dump" (A), with the latter having empty output. The output
489     // of B will contain the trascnript of "version", "statistics dump" (A),
490     // "statistics dump" (B), with A's output populated and B's output empty.
491     const StructuredData::Array &transcript =
492         debugger.GetCommandInterpreter().GetTranscript();
493     if (transcript.GetSize() != 0) {
494       std::string buffer;
495       llvm::raw_string_ostream ss(buffer);
496       json::OStream json_os(ss);
497       transcript.Serialize(json_os);
498       if (auto json_transcript = llvm::json::parse(buffer))
499         global_stats.try_emplace("transcript",
500                                  std::move(json_transcript.get()));
501     }
502   }
503 
504   if (include_plugins) {
505     global_stats.try_emplace("plugins", PluginManager::GetJSON());
506   }
507 
508   return std::move(global_stats);
509 }
510 
ToJSON() const511 llvm::json::Value SummaryStatistics::ToJSON() const {
512   return json::Object{{
513       {"name", GetName()},
514       {"type", GetSummaryKindName()},
515       {"count", GetSummaryCount()},
516       {"totalTime", GetTotalTime()},
517   }};
518 }
519 
ToJSON()520 json::Value SummaryStatisticsCache::ToJSON() {
521   std::lock_guard<std::mutex> guard(m_map_mutex);
522   json::Array json_summary_stats;
523   for (const auto &summary_stat : m_summary_stats_map)
524     json_summary_stats.emplace_back(summary_stat.second->ToJSON());
525 
526   return json_summary_stats;
527 }
528 
Reset()529 void SummaryStatisticsCache::Reset() {
530   for (const auto &summary_stat : m_summary_stats_map)
531     summary_stat.second->Reset();
532 }
533