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
22using namespace lldb;
23using namespace lldb_private;
24using namespace llvm;
25
26static 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
36json::Value StatsSuccessFail::ToJSON() const {
37 return json::Object{{.K: "successes", .V: successes}, {.K: "failures", .V: failures}};
38}
39
40static 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
46void 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
52json::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
107llvm::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
115json::Value
116TargetStats::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
214void 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
232void TargetStats::SetLaunchOrAttachTime() {
233 m_launch_or_attach_time = StatsClock::now();
234 m_first_private_stop_time = std::nullopt;
235}
236
237void 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
245void 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
253void TargetStats::IncreaseSourceMapDeduceCount() {
254 ++m_source_map_deduce_count;
255}
256
257void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count) {
258 m_source_realpath_attempt_count += count;
259}
260
261void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) {
262 m_source_realpath_compatible_count += count;
263}
264
265bool DebuggerStats::g_collecting_stats = false;
266
267void 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
289llvm::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
501llvm::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
510json::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
519void SummaryStatisticsCache::Reset() {
520 for (const auto &summary_stat : m_summary_stats_map)
521 summary_stat.second->Reset();
522}
523

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Target/Statistics.cpp