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/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
20using namespace lldb;
21using namespace lldb_private;
22using namespace llvm;
23
24static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
25 const std::string &str) {
26 if (str.empty())
27 return;
28 if (LLVM_LIKELY(llvm::json::isUTF8(str)))
29 obj.try_emplace(K: key, Args: str);
30 else
31 obj.try_emplace(K: key, Args: llvm::json::fixUTF8(S: str));
32}
33
34json::Value StatsSuccessFail::ToJSON() const {
35 return json::Object{{.K: "successes", .V: successes}, {.K: "failures", .V: failures}};
36}
37
38static double elapsed(const StatsTimepoint &start, const StatsTimepoint &end) {
39 StatsDuration::Duration elapsed =
40 end.time_since_epoch() - start.time_since_epoch();
41 return elapsed.count();
42}
43
44void TargetStats::CollectStats(Target &target) {
45 m_module_identifiers.clear();
46 for (ModuleSP module_sp : target.GetImages().Modules())
47 m_module_identifiers.emplace_back(args: (intptr_t)module_sp.get());
48}
49
50json::Value ModuleStats::ToJSON() const {
51 json::Object module;
52 EmplaceSafeString(obj&: module, key: "path", str: path);
53 EmplaceSafeString(obj&: module, key: "uuid", str: uuid);
54 EmplaceSafeString(obj&: module, key: "triple", str: triple);
55 module.try_emplace(K: "identifier", Args: identifier);
56 module.try_emplace(K: "symbolTableParseTime", Args: symtab_parse_time);
57 module.try_emplace(K: "symbolTableIndexTime", Args: symtab_index_time);
58 module.try_emplace(K: "symbolTableLoadedFromCache", Args: symtab_loaded_from_cache);
59 module.try_emplace(K: "symbolTableSavedToCache", Args: symtab_saved_to_cache);
60 module.try_emplace(K: "debugInfoParseTime", Args: debug_parse_time);
61 module.try_emplace(K: "debugInfoIndexTime", Args: debug_index_time);
62 module.try_emplace(K: "debugInfoByteSize", Args: (int64_t)debug_info_size);
63 module.try_emplace(K: "debugInfoIndexLoadedFromCache",
64 Args: debug_info_index_loaded_from_cache);
65 module.try_emplace(K: "debugInfoIndexSavedToCache",
66 Args: debug_info_index_saved_to_cache);
67 module.try_emplace(K: "debugInfoEnabled", Args: debug_info_enabled);
68 module.try_emplace(K: "debugInfoHadVariableErrors",
69 Args: debug_info_had_variable_errors);
70 module.try_emplace(K: "debugInfoHadIncompleteTypes",
71 Args: debug_info_had_incomplete_types);
72 module.try_emplace(K: "symbolTableStripped", Args: symtab_stripped);
73 if (!symfile_path.empty())
74 module.try_emplace(K: "symbolFilePath", Args: symfile_path);
75
76 if (!symfile_modules.empty()) {
77 json::Array symfile_ids;
78 for (const auto symfile_id: symfile_modules)
79 symfile_ids.emplace_back(A: symfile_id);
80 module.try_emplace(K: "symbolFileModuleIdentifiers", Args: std::move(symfile_ids));
81 }
82
83 if (!type_system_stats.empty()) {
84 json::Array type_systems;
85 for (const auto &entry : type_system_stats) {
86 json::Object obj;
87 obj.try_emplace(K: entry.first().str(), Args: entry.second);
88 type_systems.emplace_back(A: std::move(obj));
89 }
90 module.try_emplace(K: "typeSystemInfo", Args: std::move(type_systems));
91 }
92
93 return module;
94}
95
96llvm::json::Value ConstStringStats::ToJSON() const {
97 json::Object obj;
98 obj.try_emplace<int64_t>(K: "bytesTotal", Args: stats.GetBytesTotal());
99 obj.try_emplace<int64_t>(K: "bytesUsed", Args: stats.GetBytesUsed());
100 obj.try_emplace<int64_t>(K: "bytesUnused", Args: stats.GetBytesUnused());
101 return obj;
102}
103
104json::Value
105TargetStats::ToJSON(Target &target,
106 const lldb_private::StatisticsOptions &options) {
107 json::Object target_metrics_json;
108 ProcessSP process_sp = target.GetProcessSP();
109 const bool summary_only = options.summary_only;
110 if (!summary_only) {
111 CollectStats(target);
112
113 json::Array json_module_uuid_array;
114 for (auto module_identifier : m_module_identifiers)
115 json_module_uuid_array.emplace_back(A&: module_identifier);
116
117 target_metrics_json.try_emplace(K: m_expr_eval.name, Args: m_expr_eval.ToJSON());
118 target_metrics_json.try_emplace(K: m_frame_var.name, Args: m_frame_var.ToJSON());
119 target_metrics_json.try_emplace(K: "moduleIdentifiers",
120 Args: std::move(json_module_uuid_array));
121
122 if (m_launch_or_attach_time && m_first_private_stop_time) {
123 double elapsed_time =
124 elapsed(start: *m_launch_or_attach_time, end: *m_first_private_stop_time);
125 target_metrics_json.try_emplace(K: "launchOrAttachTime", Args&: elapsed_time);
126 }
127 if (m_launch_or_attach_time && m_first_public_stop_time) {
128 double elapsed_time =
129 elapsed(start: *m_launch_or_attach_time, end: *m_first_public_stop_time);
130 target_metrics_json.try_emplace(K: "firstStopTime", Args&: elapsed_time);
131 }
132 target_metrics_json.try_emplace(K: "targetCreateTime",
133 Args: m_create_time.get().count());
134
135 json::Array breakpoints_array;
136 double totalBreakpointResolveTime = 0.0;
137 // Report both the normal breakpoint list and the internal breakpoint list.
138 for (int i = 0; i < 2; ++i) {
139 BreakpointList &breakpoints = target.GetBreakpointList(internal: i == 1);
140 std::unique_lock<std::recursive_mutex> lock;
141 breakpoints.GetListMutex(lock);
142 size_t num_breakpoints = breakpoints.GetSize();
143 for (size_t i = 0; i < num_breakpoints; i++) {
144 Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
145 breakpoints_array.push_back(E: bp->GetStatistics());
146 totalBreakpointResolveTime += bp->GetResolveTime().count();
147 }
148 }
149 target_metrics_json.try_emplace(K: "breakpoints",
150 Args: std::move(breakpoints_array));
151 target_metrics_json.try_emplace(K: "totalBreakpointResolveTime",
152 Args&: totalBreakpointResolveTime);
153
154 if (process_sp) {
155 UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals();
156 if (unix_signals_sp)
157 target_metrics_json.try_emplace(
158 K: "signals", Args: unix_signals_sp->GetHitCountStatistics());
159 }
160 }
161
162 // Counting "totalSharedLibraryEventHitCount" from breakpoints of kind
163 // "shared-library-event".
164 {
165 uint32_t shared_library_event_breakpoint_hit_count = 0;
166 // The "shared-library-event" is only found in the internal breakpoint list.
167 BreakpointList &breakpoints = target.GetBreakpointList(/* internal */ true);
168 std::unique_lock<std::recursive_mutex> lock;
169 breakpoints.GetListMutex(lock);
170 size_t num_breakpoints = breakpoints.GetSize();
171 for (size_t i = 0; i < num_breakpoints; i++) {
172 Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
173 if (strcmp(s1: bp->GetBreakpointKind(), s2: "shared-library-event") == 0)
174 shared_library_event_breakpoint_hit_count += bp->GetHitCount();
175 }
176
177 target_metrics_json.try_emplace(K: "totalSharedLibraryEventHitCount",
178 Args&: shared_library_event_breakpoint_hit_count);
179 }
180
181 if (process_sp) {
182 uint32_t stop_id = process_sp->GetStopID();
183 target_metrics_json.try_emplace(K: "stopCount", Args&: stop_id);
184
185 llvm::StringRef dyld_plugin_name;
186 if (process_sp->GetDynamicLoader())
187 dyld_plugin_name = process_sp->GetDynamicLoader()->GetPluginName();
188 target_metrics_json.try_emplace(K: "dyldPluginName", Args&: dyld_plugin_name);
189 }
190 target_metrics_json.try_emplace(K: "sourceMapDeduceCount",
191 Args&: m_source_map_deduce_count);
192 return target_metrics_json;
193}
194
195void TargetStats::SetLaunchOrAttachTime() {
196 m_launch_or_attach_time = StatsClock::now();
197 m_first_private_stop_time = std::nullopt;
198}
199
200void TargetStats::SetFirstPrivateStopTime() {
201 // Launching and attaching has many paths depending on if synchronous mode
202 // was used or if we are stopping at the entry point or not. Only set the
203 // first stop time if it hasn't already been set.
204 if (!m_first_private_stop_time)
205 m_first_private_stop_time = StatsClock::now();
206}
207
208void TargetStats::SetFirstPublicStopTime() {
209 // Launching and attaching has many paths depending on if synchronous mode
210 // was used or if we are stopping at the entry point or not. Only set the
211 // first stop time if it hasn't already been set.
212 if (!m_first_public_stop_time)
213 m_first_public_stop_time = StatsClock::now();
214}
215
216void TargetStats::IncreaseSourceMapDeduceCount() {
217 ++m_source_map_deduce_count;
218}
219
220bool DebuggerStats::g_collecting_stats = false;
221
222llvm::json::Value DebuggerStats::ReportStatistics(
223 Debugger &debugger, Target *target,
224 const lldb_private::StatisticsOptions &options) {
225
226 const bool summary_only = options.summary_only;
227 const bool load_all_debug_info = options.load_all_debug_info;
228
229 json::Array json_targets;
230 json::Array json_modules;
231 double symtab_parse_time = 0.0;
232 double symtab_index_time = 0.0;
233 double debug_parse_time = 0.0;
234 double debug_index_time = 0.0;
235 uint32_t symtabs_loaded = 0;
236 uint32_t symtabs_saved = 0;
237 uint32_t debug_index_loaded = 0;
238 uint32_t debug_index_saved = 0;
239 uint64_t debug_info_size = 0;
240
241 std::vector<ModuleStats> modules;
242 std::lock_guard<std::recursive_mutex> guard(
243 Module::GetAllocationModuleCollectionMutex());
244 const uint64_t num_modules = Module::GetNumberAllocatedModules();
245 uint32_t num_debug_info_enabled_modules = 0;
246 uint32_t num_modules_has_debug_info = 0;
247 uint32_t num_modules_with_variable_errors = 0;
248 uint32_t num_modules_with_incomplete_types = 0;
249 uint32_t num_stripped_modules = 0;
250 for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
251 Module *module = Module::GetAllocatedModuleAtIndex(idx: image_idx);
252 ModuleStats module_stat;
253 module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
254 module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
255 Symtab *symtab = module->GetSymtab();
256 if (symtab) {
257 module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
258 if (module_stat.symtab_loaded_from_cache)
259 ++symtabs_loaded;
260 module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
261 if (module_stat.symtab_saved_to_cache)
262 ++symtabs_saved;
263 }
264 SymbolFile *sym_file = module->GetSymbolFile();
265 if (sym_file) {
266 if (!summary_only) {
267 if (sym_file->GetObjectFile() != module->GetObjectFile())
268 module_stat.symfile_path =
269 sym_file->GetObjectFile()->GetFileSpec().GetPath();
270 ModuleList symbol_modules = sym_file->GetDebugInfoModules();
271 for (const auto &symbol_module : symbol_modules.Modules())
272 module_stat.symfile_modules.push_back(x: (intptr_t)symbol_module.get());
273 }
274 module_stat.debug_info_index_loaded_from_cache =
275 sym_file->GetDebugInfoIndexWasLoadedFromCache();
276 if (module_stat.debug_info_index_loaded_from_cache)
277 ++debug_index_loaded;
278 module_stat.debug_info_index_saved_to_cache =
279 sym_file->GetDebugInfoIndexWasSavedToCache();
280 if (module_stat.debug_info_index_saved_to_cache)
281 ++debug_index_saved;
282 module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
283 module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
284 module_stat.debug_info_size =
285 sym_file->GetDebugInfoSize(load_all_debug_info);
286 module_stat.symtab_stripped = module->GetObjectFile()->IsStripped();
287 if (module_stat.symtab_stripped)
288 ++num_stripped_modules;
289 module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() &&
290 module_stat.debug_info_size > 0;
291 module_stat.debug_info_had_variable_errors =
292 sym_file->GetDebugInfoHadFrameVariableErrors();
293 if (module_stat.debug_info_enabled)
294 ++num_debug_info_enabled_modules;
295 if (module_stat.debug_info_size > 0)
296 ++num_modules_has_debug_info;
297 if (module_stat.debug_info_had_variable_errors)
298 ++num_modules_with_variable_errors;
299 }
300 symtab_parse_time += module_stat.symtab_parse_time;
301 symtab_index_time += module_stat.symtab_index_time;
302 debug_parse_time += module_stat.debug_parse_time;
303 debug_index_time += module_stat.debug_index_time;
304 debug_info_size += module_stat.debug_info_size;
305 module->ForEachTypeSystem(callback: [&](lldb::TypeSystemSP ts) {
306 if (auto stats = ts->ReportStatistics())
307 module_stat.type_system_stats.insert(KV: {ts->GetPluginName(), *stats});
308 if (ts->GetHasForcefullyCompletedTypes())
309 module_stat.debug_info_had_incomplete_types = true;
310 return true;
311 });
312 if (module_stat.debug_info_had_incomplete_types)
313 ++num_modules_with_incomplete_types;
314
315 if (!summary_only) {
316 module_stat.identifier = (intptr_t)module;
317 module_stat.path = module->GetFileSpec().GetPath();
318 if (ConstString object_name = module->GetObjectName()) {
319 module_stat.path.append(n: 1, c: '(');
320 module_stat.path.append(str: object_name.GetStringRef().str());
321 module_stat.path.append(n: 1, c: ')');
322 }
323 module_stat.uuid = module->GetUUID().GetAsString();
324 module_stat.triple = module->GetArchitecture().GetTriple().str();
325 json_modules.emplace_back(A: module_stat.ToJSON());
326 }
327 }
328
329 json::Object global_stats{
330 {.K: "totalSymbolTableParseTime", .V: symtab_parse_time},
331 {.K: "totalSymbolTableIndexTime", .V: symtab_index_time},
332 {.K: "totalSymbolTablesLoadedFromCache", .V: symtabs_loaded},
333 {.K: "totalSymbolTablesSavedToCache", .V: symtabs_saved},
334 {.K: "totalDebugInfoParseTime", .V: debug_parse_time},
335 {.K: "totalDebugInfoIndexTime", .V: debug_index_time},
336 {.K: "totalDebugInfoIndexLoadedFromCache", .V: debug_index_loaded},
337 {.K: "totalDebugInfoIndexSavedToCache", .V: debug_index_saved},
338 {.K: "totalDebugInfoByteSize", .V: debug_info_size},
339 {.K: "totalModuleCount", .V: num_modules},
340 {.K: "totalModuleCountHasDebugInfo", .V: num_modules_has_debug_info},
341 {.K: "totalModuleCountWithVariableErrors", .V: num_modules_with_variable_errors},
342 {.K: "totalModuleCountWithIncompleteTypes",
343 .V: num_modules_with_incomplete_types},
344 {.K: "totalDebugInfoEnabled", .V: num_debug_info_enabled_modules},
345 {.K: "totalSymbolTableStripped", .V: num_stripped_modules},
346 };
347
348 if (target) {
349 json_targets.emplace_back(A: target->ReportStatistics(options));
350 } else {
351 for (const auto &target : debugger.GetTargetList().Targets())
352 json_targets.emplace_back(A: target->ReportStatistics(options));
353 }
354 global_stats.try_emplace(K: "targets", Args: std::move(json_targets));
355
356 if (!summary_only) {
357 ConstStringStats const_string_stats;
358 json::Object json_memory{
359 {.K: "strings", .V: const_string_stats.ToJSON()},
360 };
361 json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics();
362 global_stats.try_emplace(K: "modules", Args: std::move(json_modules));
363 global_stats.try_emplace(K: "memory", Args: std::move(json_memory));
364 global_stats.try_emplace(K: "commands", Args: std::move(cmd_stats));
365 }
366
367 return std::move(global_stats);
368}
369

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