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 #include "lldb/Utility/StructuredData.h"
20
21 using namespace lldb;
22 using namespace lldb_private;
23 using namespace llvm;
24
EmplaceSafeString(llvm::json::Object & obj,llvm::StringRef key,const std::string & str)25 static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
26 const std::string &str) {
27 if (str.empty())
28 return;
29 if (LLVM_LIKELY(llvm::json::isUTF8(str)))
30 obj.try_emplace(key, str);
31 else
32 obj.try_emplace(key, llvm::json::fixUTF8(str));
33 }
34
ToJSON() const35 json::Value StatsSuccessFail::ToJSON() const {
36 return json::Object{{"successes", successes}, {"failures", failures}};
37 }
38
elapsed(const StatsTimepoint & start,const StatsTimepoint & end)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();
43 }
44
CollectStats(Target & target)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());
49 }
50
ToJSON() const51 json::Value ModuleStats::ToJSON() const {
52 json::Object module;
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);
76
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));
82 }
83
84 if (!type_system_stats.empty()) {
85 json::Array type_systems;
86 for (const auto &entry : type_system_stats) {
87 json::Object obj;
88 obj.try_emplace(entry.first().str(), entry.second);
89 type_systems.emplace_back(std::move(obj));
90 }
91 module.try_emplace("typeSystemInfo", std::move(type_systems));
92 }
93
94 return module;
95 }
96
ToJSON() const97 llvm::json::Value ConstStringStats::ToJSON() const {
98 json::Object obj;
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());
102 return obj;
103 }
104
105 json::Value
ToJSON(Target & target,const lldb_private::StatisticsOptions & options)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();
112 if (!summary_only) {
113 CollectStats(target);
114
115 json::Array json_module_uuid_array;
116 for (auto module_identifier : m_module_identifiers)
117 json_module_uuid_array.emplace_back(module_identifier);
118
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());
121 if (include_modules)
122 target_metrics_json.try_emplace("moduleIdentifiers",
123 std::move(json_module_uuid_array));
124
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);
129 }
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);
134 }
135 target_metrics_json.try_emplace("targetCreateTime",
136 m_create_time.get().count());
137
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();
150 }
151 }
152 target_metrics_json.try_emplace("breakpoints",
153 std::move(breakpoints_array));
154 target_metrics_json.try_emplace("totalBreakpointResolveTime",
155 totalBreakpointResolveTime);
156
157 if (process_sp) {
158 UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals();
159 if (unix_signals_sp)
160 target_metrics_json.try_emplace(
161 "signals", unix_signals_sp->GetHitCountStatistics());
162 }
163 }
164
165 // Counting "totalSharedLibraryEventHitCount" from breakpoints of kind
166 // "shared-library-event".
167 {
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();
178 }
179
180 target_metrics_json.try_emplace("totalSharedLibraryEventHitCount",
181 shared_library_event_breakpoint_hit_count);
182 }
183
184 if (process_sp) {
185 uint32_t stop_id = process_sp->GetStopID();
186 target_metrics_json.try_emplace("stopCount", stop_id);
187
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);
192 }
193 target_metrics_json.try_emplace("sourceMapDeduceCount",
194 m_source_map_deduce_count);
195 return target_metrics_json;
196 }
197
SetLaunchOrAttachTime()198 void TargetStats::SetLaunchOrAttachTime() {
199 m_launch_or_attach_time = StatsClock::now();
200 m_first_private_stop_time = std::nullopt;
201 }
202
SetFirstPrivateStopTime()203 void TargetStats::SetFirstPrivateStopTime() {
204 // Launching and attaching has many paths depending on if synchronous mode
205 // was used or if we are stopping at the entry point or not. Only set the
206 // first stop time if it hasn't already been set.
207 if (!m_first_private_stop_time)
208 m_first_private_stop_time = StatsClock::now();
209 }
210
SetFirstPublicStopTime()211 void TargetStats::SetFirstPublicStopTime() {
212 // Launching and attaching has many paths depending on if synchronous mode
213 // was used or if we are stopping at the entry point or not. Only set the
214 // first stop time if it hasn't already been set.
215 if (!m_first_public_stop_time)
216 m_first_public_stop_time = StatsClock::now();
217 }
218
IncreaseSourceMapDeduceCount()219 void TargetStats::IncreaseSourceMapDeduceCount() {
220 ++m_source_map_deduce_count;
221 }
222
223 bool DebuggerStats::g_collecting_stats = false;
224
ReportStatistics(Debugger & debugger,Target * target,const lldb_private::StatisticsOptions & options)225 llvm::json::Value DebuggerStats::ReportStatistics(
226 Debugger &debugger, Target *target,
227 const lldb_private::StatisticsOptions &options) {
228
229 const bool summary_only = options.GetSummaryOnly();
230 const bool load_all_debug_info = options.GetLoadAllDebugInfo();
231 const bool include_targets = options.GetIncludeTargets();
232 const bool include_modules = options.GetIncludeModules();
233 const bool include_transcript = options.GetIncludeTranscript();
234
235 json::Array json_targets;
236 json::Array json_modules;
237 double symtab_parse_time = 0.0;
238 double symtab_index_time = 0.0;
239 double debug_parse_time = 0.0;
240 double debug_index_time = 0.0;
241 uint32_t symtabs_loaded = 0;
242 uint32_t symtabs_saved = 0;
243 uint32_t debug_index_loaded = 0;
244 uint32_t debug_index_saved = 0;
245 uint64_t debug_info_size = 0;
246
247 std::vector<ModuleStats> modules;
248 std::lock_guard<std::recursive_mutex> guard(
249 Module::GetAllocationModuleCollectionMutex());
250 const uint64_t num_modules = Module::GetNumberAllocatedModules();
251 uint32_t num_debug_info_enabled_modules = 0;
252 uint32_t num_modules_has_debug_info = 0;
253 uint32_t num_modules_with_variable_errors = 0;
254 uint32_t num_modules_with_incomplete_types = 0;
255 uint32_t num_stripped_modules = 0;
256 for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
257 Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
258 ModuleStats module_stat;
259 module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
260 module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
261 Symtab *symtab = module->GetSymtab();
262 if (symtab) {
263 module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
264 if (module_stat.symtab_loaded_from_cache)
265 ++symtabs_loaded;
266 module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
267 if (module_stat.symtab_saved_to_cache)
268 ++symtabs_saved;
269 }
270 SymbolFile *sym_file = module->GetSymbolFile();
271 if (sym_file) {
272 if (!summary_only) {
273 if (sym_file->GetObjectFile() != module->GetObjectFile())
274 module_stat.symfile_path =
275 sym_file->GetObjectFile()->GetFileSpec().GetPath();
276 ModuleList symbol_modules = sym_file->GetDebugInfoModules();
277 for (const auto &symbol_module : symbol_modules.Modules())
278 module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
279 }
280 module_stat.debug_info_index_loaded_from_cache =
281 sym_file->GetDebugInfoIndexWasLoadedFromCache();
282 if (module_stat.debug_info_index_loaded_from_cache)
283 ++debug_index_loaded;
284 module_stat.debug_info_index_saved_to_cache =
285 sym_file->GetDebugInfoIndexWasSavedToCache();
286 if (module_stat.debug_info_index_saved_to_cache)
287 ++debug_index_saved;
288 module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
289 module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
290 module_stat.debug_info_size =
291 sym_file->GetDebugInfoSize(load_all_debug_info);
292 module_stat.symtab_stripped = module->GetObjectFile()->IsStripped();
293 if (module_stat.symtab_stripped)
294 ++num_stripped_modules;
295 module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() &&
296 module_stat.debug_info_size > 0;
297 module_stat.debug_info_had_variable_errors =
298 sym_file->GetDebugInfoHadFrameVariableErrors();
299 if (module_stat.debug_info_enabled)
300 ++num_debug_info_enabled_modules;
301 if (module_stat.debug_info_size > 0)
302 ++num_modules_has_debug_info;
303 if (module_stat.debug_info_had_variable_errors)
304 ++num_modules_with_variable_errors;
305 }
306 symtab_parse_time += module_stat.symtab_parse_time;
307 symtab_index_time += module_stat.symtab_index_time;
308 debug_parse_time += module_stat.debug_parse_time;
309 debug_index_time += module_stat.debug_index_time;
310 debug_info_size += module_stat.debug_info_size;
311 module->ForEachTypeSystem([&](lldb::TypeSystemSP ts) {
312 if (auto stats = ts->ReportStatistics())
313 module_stat.type_system_stats.insert({ts->GetPluginName(), *stats});
314 if (ts->GetHasForcefullyCompletedTypes())
315 module_stat.debug_info_had_incomplete_types = true;
316 return true;
317 });
318 if (module_stat.debug_info_had_incomplete_types)
319 ++num_modules_with_incomplete_types;
320
321 if (include_modules) {
322 module_stat.identifier = (intptr_t)module;
323 module_stat.path = module->GetFileSpec().GetPath();
324 if (ConstString object_name = module->GetObjectName()) {
325 module_stat.path.append(1, '(');
326 module_stat.path.append(object_name.GetStringRef().str());
327 module_stat.path.append(1, ')');
328 }
329 module_stat.uuid = module->GetUUID().GetAsString();
330 module_stat.triple = module->GetArchitecture().GetTriple().str();
331 json_modules.emplace_back(module_stat.ToJSON());
332 }
333 }
334
335 json::Object global_stats{
336 {"totalSymbolTableParseTime", symtab_parse_time},
337 {"totalSymbolTableIndexTime", symtab_index_time},
338 {"totalSymbolTablesLoadedFromCache", symtabs_loaded},
339 {"totalSymbolTablesSavedToCache", symtabs_saved},
340 {"totalDebugInfoParseTime", debug_parse_time},
341 {"totalDebugInfoIndexTime", debug_index_time},
342 {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded},
343 {"totalDebugInfoIndexSavedToCache", debug_index_saved},
344 {"totalDebugInfoByteSize", debug_info_size},
345 {"totalModuleCount", num_modules},
346 {"totalModuleCountHasDebugInfo", num_modules_has_debug_info},
347 {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors},
348 {"totalModuleCountWithIncompleteTypes",
349 num_modules_with_incomplete_types},
350 {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
351 {"totalSymbolTableStripped", num_stripped_modules},
352 };
353
354 if (include_targets) {
355 if (target) {
356 json_targets.emplace_back(target->ReportStatistics(options));
357 } else {
358 for (const auto &target : debugger.GetTargetList().Targets())
359 json_targets.emplace_back(target->ReportStatistics(options));
360 }
361 global_stats.try_emplace("targets", std::move(json_targets));
362 }
363
364 ConstStringStats const_string_stats;
365 json::Object json_memory{
366 {"strings", const_string_stats.ToJSON()},
367 };
368 global_stats.try_emplace("memory", std::move(json_memory));
369 if (!summary_only) {
370 json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics();
371 global_stats.try_emplace("commands", std::move(cmd_stats));
372 }
373
374 if (include_modules) {
375 global_stats.try_emplace("modules", std::move(json_modules));
376 }
377
378 if (include_transcript) {
379 // When transcript is available, add it to the to-be-returned statistics.
380 //
381 // NOTE:
382 // When the statistics is polled by an LLDB command:
383 // - The transcript in the returned statistics *will NOT* contain the
384 // returned statistics itself (otherwise infinite recursion).
385 // - The returned statistics *will* be written to the internal transcript
386 // buffer. It *will* appear in the next statistcs or transcript poll.
387 //
388 // For example, let's say the following commands are run in order:
389 // - "version"
390 // - "statistics dump" <- call it "A"
391 // - "statistics dump" <- call it "B"
392 // The output of "A" will contain the transcript of "version" and
393 // "statistics dump" (A), with the latter having empty output. The output
394 // of B will contain the trascnript of "version", "statistics dump" (A),
395 // "statistics dump" (B), with A's output populated and B's output empty.
396 const StructuredData::Array &transcript =
397 debugger.GetCommandInterpreter().GetTranscript();
398 if (transcript.GetSize() != 0) {
399 std::string buffer;
400 llvm::raw_string_ostream ss(buffer);
401 json::OStream json_os(ss);
402 transcript.Serialize(json_os);
403 if (auto json_transcript = llvm::json::parse(ss.str()))
404 global_stats.try_emplace("transcript",
405 std::move(json_transcript.get()));
406 }
407 }
408
409 return std::move(global_stats);
410 }
411