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