1 //===-- Statistics.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Target/Statistics.h"
11 #include "lldb/Core/Debugger.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Interpreter/CommandInterpreter.h"
14 #include "lldb/Symbol/SymbolFile.h"
15 #include "lldb/Target/DynamicLoader.h"
16 #include "lldb/Target/Process.h"
17 #include "lldb/Target/Target.h"
18 #include "lldb/Target/UnixSignals.h"
19 #include "lldb/Utility/StructuredData.h"
22 using namespace lldb_private
;
25 static void EmplaceSafeString(llvm::json::Object
&obj
, llvm::StringRef key
,
26 const std::string
&str
) {
29 if (LLVM_LIKELY(llvm::json::isUTF8(str
)))
30 obj
.try_emplace(key
, str
);
32 obj
.try_emplace(key
, llvm::json::fixUTF8(str
));
35 json::Value
StatsSuccessFail::ToJSON() const {
36 return json::Object
{{"successes", successes
}, {"failures", failures
}};
39 static double elapsed(const StatsTimepoint
&start
, const StatsTimepoint
&end
) {
40 StatsDuration::Duration elapsed
=
41 end
.time_since_epoch() - start
.time_since_epoch();
42 return elapsed
.count();
45 void TargetStats::CollectStats(Target
&target
) {
46 m_module_identifiers
.clear();
47 for (ModuleSP module_sp
: target
.GetImages().Modules())
48 m_module_identifiers
.emplace_back((intptr_t)module_sp
.get());
51 json::Value
ModuleStats::ToJSON() const {
53 EmplaceSafeString(module
, "path", path
);
54 EmplaceSafeString(module
, "uuid", uuid
);
55 EmplaceSafeString(module
, "triple", triple
);
56 module
.try_emplace("identifier", identifier
);
57 module
.try_emplace("symbolTableParseTime", symtab_parse_time
);
58 module
.try_emplace("symbolTableIndexTime", symtab_index_time
);
59 module
.try_emplace("symbolTableLoadedFromCache", symtab_loaded_from_cache
);
60 module
.try_emplace("symbolTableSavedToCache", symtab_saved_to_cache
);
61 module
.try_emplace("debugInfoParseTime", debug_parse_time
);
62 module
.try_emplace("debugInfoIndexTime", debug_index_time
);
63 module
.try_emplace("debugInfoByteSize", (int64_t)debug_info_size
);
64 module
.try_emplace("debugInfoIndexLoadedFromCache",
65 debug_info_index_loaded_from_cache
);
66 module
.try_emplace("debugInfoIndexSavedToCache",
67 debug_info_index_saved_to_cache
);
68 module
.try_emplace("debugInfoEnabled", debug_info_enabled
);
69 module
.try_emplace("debugInfoHadVariableErrors",
70 debug_info_had_variable_errors
);
71 module
.try_emplace("debugInfoHadIncompleteTypes",
72 debug_info_had_incomplete_types
);
73 module
.try_emplace("symbolTableStripped", symtab_stripped
);
74 if (!symfile_path
.empty())
75 module
.try_emplace("symbolFilePath", symfile_path
);
77 if (!symfile_modules
.empty()) {
78 json::Array symfile_ids
;
79 for (const auto symfile_id
: symfile_modules
)
80 symfile_ids
.emplace_back(symfile_id
);
81 module
.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids
));
84 if (!type_system_stats
.empty()) {
85 json::Array type_systems
;
86 for (const auto &entry
: type_system_stats
) {
88 obj
.try_emplace(entry
.first().str(), entry
.second
);
89 type_systems
.emplace_back(std::move(obj
));
91 module
.try_emplace("typeSystemInfo", std::move(type_systems
));
97 llvm::json::Value
ConstStringStats::ToJSON() const {
99 obj
.try_emplace
<int64_t>("bytesTotal", stats
.GetBytesTotal());
100 obj
.try_emplace
<int64_t>("bytesUsed", stats
.GetBytesUsed());
101 obj
.try_emplace
<int64_t>("bytesUnused", stats
.GetBytesUnused());
106 TargetStats::ToJSON(Target
&target
,
107 const lldb_private::StatisticsOptions
&options
) {
108 json::Object target_metrics_json
;
109 ProcessSP process_sp
= target
.GetProcessSP();
110 const bool summary_only
= options
.GetSummaryOnly();
111 const bool include_modules
= options
.GetIncludeModules();
113 CollectStats(target
);
115 json::Array json_module_uuid_array
;
116 for (auto module_identifier
: m_module_identifiers
)
117 json_module_uuid_array
.emplace_back(module_identifier
);
119 target_metrics_json
.try_emplace(m_expr_eval
.name
, m_expr_eval
.ToJSON());
120 target_metrics_json
.try_emplace(m_frame_var
.name
, m_frame_var
.ToJSON());
122 target_metrics_json
.try_emplace("moduleIdentifiers",
123 std::move(json_module_uuid_array
));
125 if (m_launch_or_attach_time
&& m_first_private_stop_time
) {
126 double elapsed_time
=
127 elapsed(*m_launch_or_attach_time
, *m_first_private_stop_time
);
128 target_metrics_json
.try_emplace("launchOrAttachTime", elapsed_time
);
130 if (m_launch_or_attach_time
&& m_first_public_stop_time
) {
131 double elapsed_time
=
132 elapsed(*m_launch_or_attach_time
, *m_first_public_stop_time
);
133 target_metrics_json
.try_emplace("firstStopTime", elapsed_time
);
135 target_metrics_json
.try_emplace("targetCreateTime",
136 m_create_time
.get().count());
138 json::Array breakpoints_array
;
139 double totalBreakpointResolveTime
= 0.0;
140 // Report both the normal breakpoint list and the internal breakpoint list.
141 for (int i
= 0; i
< 2; ++i
) {
142 BreakpointList
&breakpoints
= target
.GetBreakpointList(i
== 1);
143 std::unique_lock
<std::recursive_mutex
> lock
;
144 breakpoints
.GetListMutex(lock
);
145 size_t num_breakpoints
= breakpoints
.GetSize();
146 for (size_t i
= 0; i
< num_breakpoints
; i
++) {
147 Breakpoint
*bp
= breakpoints
.GetBreakpointAtIndex(i
).get();
148 breakpoints_array
.push_back(bp
->GetStatistics());
149 totalBreakpointResolveTime
+= bp
->GetResolveTime().count();
152 target_metrics_json
.try_emplace("breakpoints",
153 std::move(breakpoints_array
));
154 target_metrics_json
.try_emplace("totalBreakpointResolveTime",
155 totalBreakpointResolveTime
);
158 UnixSignalsSP unix_signals_sp
= process_sp
->GetUnixSignals();
160 target_metrics_json
.try_emplace(
161 "signals", unix_signals_sp
->GetHitCountStatistics());
165 // Counting "totalSharedLibraryEventHitCount" from breakpoints of kind
166 // "shared-library-event".
168 uint32_t shared_library_event_breakpoint_hit_count
= 0;
169 // The "shared-library-event" is only found in the internal breakpoint list.
170 BreakpointList
&breakpoints
= target
.GetBreakpointList(/* internal */ true);
171 std::unique_lock
<std::recursive_mutex
> lock
;
172 breakpoints
.GetListMutex(lock
);
173 size_t num_breakpoints
= breakpoints
.GetSize();
174 for (size_t i
= 0; i
< num_breakpoints
; i
++) {
175 Breakpoint
*bp
= breakpoints
.GetBreakpointAtIndex(i
).get();
176 if (strcmp(bp
->GetBreakpointKind(), "shared-library-event") == 0)
177 shared_library_event_breakpoint_hit_count
+= bp
->GetHitCount();
180 target_metrics_json
.try_emplace("totalSharedLibraryEventHitCount",
181 shared_library_event_breakpoint_hit_count
);
185 uint32_t stop_id
= process_sp
->GetStopID();
186 target_metrics_json
.try_emplace("stopCount", stop_id
);
188 llvm::StringRef dyld_plugin_name
;
189 if (process_sp
->GetDynamicLoader())
190 dyld_plugin_name
= process_sp
->GetDynamicLoader()->GetPluginName();
191 target_metrics_json
.try_emplace("dyldPluginName", dyld_plugin_name
);
193 target_metrics_json
.try_emplace("sourceMapDeduceCount",
194 m_source_map_deduce_count
);
195 target_metrics_json
.try_emplace("sourceRealpathAttemptCount",
196 m_source_realpath_attempt_count
);
197 target_metrics_json
.try_emplace("sourceRealpathCompatibleCount",
198 m_source_realpath_compatible_count
);
199 target_metrics_json
.try_emplace("summaryProviderStatistics",
200 target
.GetSummaryStatisticsCache().ToJSON());
201 return target_metrics_json
;
204 void TargetStats::Reset(Target
&target
) {
205 m_launch_or_attach_time
.reset();
206 m_first_private_stop_time
.reset();
207 m_first_public_stop_time
.reset();
208 // Report both the normal breakpoint list and the internal breakpoint list.
209 for (int i
= 0; i
< 2; ++i
) {
210 BreakpointList
&breakpoints
= target
.GetBreakpointList(i
== 1);
211 std::unique_lock
<std::recursive_mutex
> lock
;
212 breakpoints
.GetListMutex(lock
);
213 size_t num_breakpoints
= breakpoints
.GetSize();
214 for (size_t i
= 0; i
< num_breakpoints
; i
++) {
215 Breakpoint
*bp
= breakpoints
.GetBreakpointAtIndex(i
).get();
216 bp
->ResetStatistics();
219 target
.GetSummaryStatisticsCache().Reset();
222 void TargetStats::SetLaunchOrAttachTime() {
223 m_launch_or_attach_time
= StatsClock::now();
224 m_first_private_stop_time
= std::nullopt
;
227 void TargetStats::SetFirstPrivateStopTime() {
228 // Launching and attaching has many paths depending on if synchronous mode
229 // was used or if we are stopping at the entry point or not. Only set the
230 // first stop time if it hasn't already been set.
231 if (!m_first_private_stop_time
)
232 m_first_private_stop_time
= StatsClock::now();
235 void TargetStats::SetFirstPublicStopTime() {
236 // Launching and attaching has many paths depending on if synchronous mode
237 // was used or if we are stopping at the entry point or not. Only set the
238 // first stop time if it hasn't already been set.
239 if (!m_first_public_stop_time
)
240 m_first_public_stop_time
= StatsClock::now();
243 void TargetStats::IncreaseSourceMapDeduceCount() {
244 ++m_source_map_deduce_count
;
247 void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count
) {
248 m_source_realpath_attempt_count
+= count
;
251 void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count
) {
252 m_source_realpath_compatible_count
+= count
;
255 bool DebuggerStats::g_collecting_stats
= false;
257 void DebuggerStats::ResetStatistics(Debugger
&debugger
, Target
*target
) {
258 std::lock_guard
<std::recursive_mutex
> guard(
259 Module::GetAllocationModuleCollectionMutex());
260 const uint64_t num_modules
= target
!= nullptr
261 ? target
->GetImages().GetSize()
262 : Module::GetNumberAllocatedModules();
263 for (size_t image_idx
= 0; image_idx
< num_modules
; ++image_idx
) {
264 Module
*module
= target
!= nullptr
265 ? target
->GetImages().GetModuleAtIndex(image_idx
).get()
266 : Module::GetAllocatedModuleAtIndex(image_idx
);
267 if (module
== nullptr)
269 module
->ResetStatistics();
272 target
->ResetStatistics();
274 for (const auto &target
: debugger
.GetTargetList().Targets())
275 target
->ResetStatistics();
279 llvm::json::Value
DebuggerStats::ReportStatistics(
280 Debugger
&debugger
, Target
*target
,
281 const lldb_private::StatisticsOptions
&options
) {
283 const bool summary_only
= options
.GetSummaryOnly();
284 const bool load_all_debug_info
= options
.GetLoadAllDebugInfo();
285 const bool include_targets
= options
.GetIncludeTargets();
286 const bool include_modules
= options
.GetIncludeModules();
287 const bool include_transcript
= options
.GetIncludeTranscript();
289 json::Array json_targets
;
290 json::Array json_modules
;
291 double symtab_parse_time
= 0.0;
292 double symtab_index_time
= 0.0;
293 double debug_parse_time
= 0.0;
294 double debug_index_time
= 0.0;
295 uint32_t symtabs_loaded
= 0;
296 uint32_t symtabs_saved
= 0;
297 uint32_t debug_index_loaded
= 0;
298 uint32_t debug_index_saved
= 0;
299 uint64_t debug_info_size
= 0;
301 std::vector
<ModuleStats
> modules
;
302 std::lock_guard
<std::recursive_mutex
> guard(
303 Module::GetAllocationModuleCollectionMutex());
304 const uint64_t num_modules
= target
!= nullptr
305 ? target
->GetImages().GetSize()
306 : Module::GetNumberAllocatedModules();
307 uint32_t num_debug_info_enabled_modules
= 0;
308 uint32_t num_modules_has_debug_info
= 0;
309 uint32_t num_modules_with_variable_errors
= 0;
310 uint32_t num_modules_with_incomplete_types
= 0;
311 uint32_t num_stripped_modules
= 0;
312 for (size_t image_idx
= 0; image_idx
< num_modules
; ++image_idx
) {
313 Module
*module
= target
!= nullptr
314 ? target
->GetImages().GetModuleAtIndex(image_idx
).get()
315 : Module::GetAllocatedModuleAtIndex(image_idx
);
316 ModuleStats module_stat
;
317 module_stat
.symtab_parse_time
= module
->GetSymtabParseTime().get().count();
318 module_stat
.symtab_index_time
= module
->GetSymtabIndexTime().get().count();
319 Symtab
*symtab
= module
->GetSymtab();
321 module_stat
.symtab_loaded_from_cache
= symtab
->GetWasLoadedFromCache();
322 if (module_stat
.symtab_loaded_from_cache
)
324 module_stat
.symtab_saved_to_cache
= symtab
->GetWasSavedToCache();
325 if (module_stat
.symtab_saved_to_cache
)
328 SymbolFile
*sym_file
= module
->GetSymbolFile();
331 if (sym_file
->GetObjectFile() != module
->GetObjectFile())
332 module_stat
.symfile_path
=
333 sym_file
->GetObjectFile()->GetFileSpec().GetPath();
334 ModuleList symbol_modules
= sym_file
->GetDebugInfoModules();
335 for (const auto &symbol_module
: symbol_modules
.Modules())
336 module_stat
.symfile_modules
.push_back((intptr_t)symbol_module
.get());
338 module_stat
.debug_info_index_loaded_from_cache
=
339 sym_file
->GetDebugInfoIndexWasLoadedFromCache();
340 if (module_stat
.debug_info_index_loaded_from_cache
)
341 ++debug_index_loaded
;
342 module_stat
.debug_info_index_saved_to_cache
=
343 sym_file
->GetDebugInfoIndexWasSavedToCache();
344 if (module_stat
.debug_info_index_saved_to_cache
)
346 module_stat
.debug_index_time
= sym_file
->GetDebugInfoIndexTime().count();
347 module_stat
.debug_parse_time
= sym_file
->GetDebugInfoParseTime().count();
348 module_stat
.debug_info_size
=
349 sym_file
->GetDebugInfoSize(load_all_debug_info
);
350 module_stat
.symtab_stripped
= module
->GetObjectFile()->IsStripped();
351 if (module_stat
.symtab_stripped
)
352 ++num_stripped_modules
;
353 module_stat
.debug_info_enabled
= sym_file
->GetLoadDebugInfoEnabled() &&
354 module_stat
.debug_info_size
> 0;
355 module_stat
.debug_info_had_variable_errors
=
356 sym_file
->GetDebugInfoHadFrameVariableErrors();
357 if (module_stat
.debug_info_enabled
)
358 ++num_debug_info_enabled_modules
;
359 if (module_stat
.debug_info_size
> 0)
360 ++num_modules_has_debug_info
;
361 if (module_stat
.debug_info_had_variable_errors
)
362 ++num_modules_with_variable_errors
;
364 symtab_parse_time
+= module_stat
.symtab_parse_time
;
365 symtab_index_time
+= module_stat
.symtab_index_time
;
366 debug_parse_time
+= module_stat
.debug_parse_time
;
367 debug_index_time
+= module_stat
.debug_index_time
;
368 debug_info_size
+= module_stat
.debug_info_size
;
369 module
->ForEachTypeSystem([&](lldb::TypeSystemSP ts
) {
370 if (auto stats
= ts
->ReportStatistics())
371 module_stat
.type_system_stats
.insert({ts
->GetPluginName(), *stats
});
372 if (ts
->GetHasForcefullyCompletedTypes())
373 module_stat
.debug_info_had_incomplete_types
= true;
376 if (module_stat
.debug_info_had_incomplete_types
)
377 ++num_modules_with_incomplete_types
;
379 if (include_modules
) {
380 module_stat
.identifier
= (intptr_t)module
;
381 module_stat
.path
= module
->GetFileSpec().GetPath();
382 if (ConstString object_name
= module
->GetObjectName()) {
383 module_stat
.path
.append(1, '(');
384 module_stat
.path
.append(object_name
.GetStringRef().str());
385 module_stat
.path
.append(1, ')');
387 module_stat
.uuid
= module
->GetUUID().GetAsString();
388 module_stat
.triple
= module
->GetArchitecture().GetTriple().str();
389 json_modules
.emplace_back(module_stat
.ToJSON());
393 json::Object global_stats
{
394 {"totalSymbolTableParseTime", symtab_parse_time
},
395 {"totalSymbolTableIndexTime", symtab_index_time
},
396 {"totalSymbolTablesLoadedFromCache", symtabs_loaded
},
397 {"totalSymbolTablesSavedToCache", symtabs_saved
},
398 {"totalDebugInfoParseTime", debug_parse_time
},
399 {"totalDebugInfoIndexTime", debug_index_time
},
400 {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded
},
401 {"totalDebugInfoIndexSavedToCache", debug_index_saved
},
402 {"totalDebugInfoByteSize", debug_info_size
},
403 {"totalModuleCount", num_modules
},
404 {"totalModuleCountHasDebugInfo", num_modules_has_debug_info
},
405 {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors
},
406 {"totalModuleCountWithIncompleteTypes",
407 num_modules_with_incomplete_types
},
408 {"totalDebugInfoEnabled", num_debug_info_enabled_modules
},
409 {"totalSymbolTableStripped", num_stripped_modules
},
412 if (include_targets
) {
414 json_targets
.emplace_back(target
->ReportStatistics(options
));
416 for (const auto &target
: debugger
.GetTargetList().Targets())
417 json_targets
.emplace_back(target
->ReportStatistics(options
));
419 global_stats
.try_emplace("targets", std::move(json_targets
));
422 ConstStringStats const_string_stats
;
423 json::Object json_memory
{
424 {"strings", const_string_stats
.ToJSON()},
426 global_stats
.try_emplace("memory", std::move(json_memory
));
428 json::Value cmd_stats
= debugger
.GetCommandInterpreter().GetStatistics();
429 global_stats
.try_emplace("commands", std::move(cmd_stats
));
432 if (include_modules
) {
433 global_stats
.try_emplace("modules", std::move(json_modules
));
436 if (include_transcript
) {
437 // When transcript is available, add it to the to-be-returned statistics.
440 // When the statistics is polled by an LLDB command:
441 // - The transcript in the returned statistics *will NOT* contain the
442 // returned statistics itself (otherwise infinite recursion).
443 // - The returned statistics *will* be written to the internal transcript
444 // buffer. It *will* appear in the next statistcs or transcript poll.
446 // For example, let's say the following commands are run in order:
448 // - "statistics dump" <- call it "A"
449 // - "statistics dump" <- call it "B"
450 // The output of "A" will contain the transcript of "version" and
451 // "statistics dump" (A), with the latter having empty output. The output
452 // of B will contain the trascnript of "version", "statistics dump" (A),
453 // "statistics dump" (B), with A's output populated and B's output empty.
454 const StructuredData::Array
&transcript
=
455 debugger
.GetCommandInterpreter().GetTranscript();
456 if (transcript
.GetSize() != 0) {
458 llvm::raw_string_ostream
ss(buffer
);
459 json::OStream
json_os(ss
);
460 transcript
.Serialize(json_os
);
461 if (auto json_transcript
= llvm::json::parse(buffer
))
462 global_stats
.try_emplace("transcript",
463 std::move(json_transcript
.get()));
467 return std::move(global_stats
);
470 llvm::json::Value
SummaryStatistics::ToJSON() const {
471 return json::Object
{{
473 {"type", GetSummaryKindName()},
474 {"count", GetSummaryCount()},
475 {"totalTime", GetTotalTime()},
479 json::Value
SummaryStatisticsCache::ToJSON() {
480 std::lock_guard
<std::mutex
> guard(m_map_mutex
);
481 json::Array json_summary_stats
;
482 for (const auto &summary_stat
: m_summary_stats_map
)
483 json_summary_stats
.emplace_back(summary_stat
.second
->ToJSON());
485 return json_summary_stats
;
488 void SummaryStatisticsCache::Reset() {
489 for (const auto &summary_stat
: m_summary_stats_map
)
490 summary_stat
.second
->Reset();