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