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