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
EmplaceSafeString(llvm::json::Object & obj,llvm::StringRef key,const std::string & str)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
ToJSON() const32 json::Value StatsSuccessFail::ToJSON() const {
33 return json::Object{{"successes", successes}, {"failures", failures}};
34 }
35
elapsed(const StatsTimepoint & start,const StatsTimepoint & end)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
CollectStats(Target & target)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
ToJSON() const48 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("symbolTableStripped", symtab_stripped);
67 if (!symfile_path.empty())
68 module.try_emplace("symbolFilePath", symfile_path);
69
70 if (!symfile_modules.empty()) {
71 json::Array symfile_ids;
72 for (const auto symfile_id: symfile_modules)
73 symfile_ids.emplace_back(symfile_id);
74 module.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids));
75 }
76 return module;
77 }
78
ToJSON() const79 llvm::json::Value ConstStringStats::ToJSON() const {
80 json::Object obj;
81 obj.try_emplace<int64_t>("bytesTotal", stats.GetBytesTotal());
82 obj.try_emplace<int64_t>("bytesUsed", stats.GetBytesUsed());
83 obj.try_emplace<int64_t>("bytesUnused", stats.GetBytesUnused());
84 return obj;
85 }
86
ToJSON(Target & target)87 json::Value TargetStats::ToJSON(Target &target) {
88 CollectStats(target);
89
90 json::Array json_module_uuid_array;
91 for (auto module_identifier : m_module_identifiers)
92 json_module_uuid_array.emplace_back(module_identifier);
93
94 json::Object target_metrics_json{
95 {m_expr_eval.name, m_expr_eval.ToJSON()},
96 {m_frame_var.name, m_frame_var.ToJSON()},
97 {"moduleIdentifiers", std::move(json_module_uuid_array)}};
98
99 if (m_launch_or_attach_time && m_first_private_stop_time) {
100 double elapsed_time =
101 elapsed(*m_launch_or_attach_time, *m_first_private_stop_time);
102 target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time);
103 }
104 if (m_launch_or_attach_time && m_first_public_stop_time) {
105 double elapsed_time =
106 elapsed(*m_launch_or_attach_time, *m_first_public_stop_time);
107 target_metrics_json.try_emplace("firstStopTime", elapsed_time);
108 }
109 target_metrics_json.try_emplace("targetCreateTime",
110 m_create_time.get().count());
111
112 json::Array breakpoints_array;
113 double totalBreakpointResolveTime = 0.0;
114 // Rport both the normal breakpoint list and the internal breakpoint list.
115 for (int i = 0; i < 2; ++i) {
116 BreakpointList &breakpoints = target.GetBreakpointList(i == 1);
117 std::unique_lock<std::recursive_mutex> lock;
118 breakpoints.GetListMutex(lock);
119 size_t num_breakpoints = breakpoints.GetSize();
120 for (size_t i = 0; i < num_breakpoints; i++) {
121 Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
122 breakpoints_array.push_back(bp->GetStatistics());
123 totalBreakpointResolveTime += bp->GetResolveTime().count();
124 }
125 }
126
127 ProcessSP process_sp = target.GetProcessSP();
128 if (process_sp) {
129 UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals();
130 if (unix_signals_sp)
131 target_metrics_json.try_emplace("signals",
132 unix_signals_sp->GetHitCountStatistics());
133 uint32_t stop_id = process_sp->GetStopID();
134 target_metrics_json.try_emplace("stopCount", stop_id);
135 }
136 target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array));
137 target_metrics_json.try_emplace("totalBreakpointResolveTime",
138 totalBreakpointResolveTime);
139
140 return target_metrics_json;
141 }
142
SetLaunchOrAttachTime()143 void TargetStats::SetLaunchOrAttachTime() {
144 m_launch_or_attach_time = StatsClock::now();
145 m_first_private_stop_time = llvm::None;
146 }
147
SetFirstPrivateStopTime()148 void TargetStats::SetFirstPrivateStopTime() {
149 // Launching and attaching has many paths depending on if synchronous mode
150 // was used or if we are stopping at the entry point or not. Only set the
151 // first stop time if it hasn't already been set.
152 if (!m_first_private_stop_time)
153 m_first_private_stop_time = StatsClock::now();
154 }
155
SetFirstPublicStopTime()156 void TargetStats::SetFirstPublicStopTime() {
157 // Launching and attaching has many paths depending on if synchronous mode
158 // was used or if we are stopping at the entry point or not. Only set the
159 // first stop time if it hasn't already been set.
160 if (!m_first_public_stop_time)
161 m_first_public_stop_time = StatsClock::now();
162 }
163
164 bool DebuggerStats::g_collecting_stats = false;
165
ReportStatistics(Debugger & debugger,Target * target)166 llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
167 Target *target) {
168 json::Array json_targets;
169 json::Array json_modules;
170 double symtab_parse_time = 0.0;
171 double symtab_index_time = 0.0;
172 double debug_parse_time = 0.0;
173 double debug_index_time = 0.0;
174 uint32_t symtabs_loaded = 0;
175 uint32_t symtabs_saved = 0;
176 uint32_t debug_index_loaded = 0;
177 uint32_t debug_index_saved = 0;
178 uint64_t debug_info_size = 0;
179 if (target) {
180 json_targets.emplace_back(target->ReportStatistics());
181 } else {
182 for (const auto &target : debugger.GetTargetList().Targets())
183 json_targets.emplace_back(target->ReportStatistics());
184 }
185 std::vector<ModuleStats> modules;
186 std::lock_guard<std::recursive_mutex> guard(
187 Module::GetAllocationModuleCollectionMutex());
188 const uint64_t num_modules = Module::GetNumberAllocatedModules();
189 uint32_t num_debug_info_enabled_modules = 0;
190 uint32_t num_modules_has_debug_info = 0;
191 uint32_t num_stripped_modules = 0;
192 for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
193 Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
194 ModuleStats module_stat;
195 module_stat.identifier = (intptr_t)module;
196 module_stat.path = module->GetFileSpec().GetPath();
197 if (ConstString object_name = module->GetObjectName()) {
198 module_stat.path.append(1, '(');
199 module_stat.path.append(object_name.GetStringRef().str());
200 module_stat.path.append(1, ')');
201 }
202 module_stat.uuid = module->GetUUID().GetAsString();
203 module_stat.triple = module->GetArchitecture().GetTriple().str();
204 module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
205 module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
206 Symtab *symtab = module->GetSymtab();
207 if (symtab) {
208 module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
209 if (module_stat.symtab_loaded_from_cache)
210 ++symtabs_loaded;
211 module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
212 if (module_stat.symtab_saved_to_cache)
213 ++symtabs_saved;
214 }
215 SymbolFile *sym_file = module->GetSymbolFile();
216 if (sym_file) {
217
218 if (sym_file->GetObjectFile() != module->GetObjectFile())
219 module_stat.symfile_path =
220 sym_file->GetObjectFile()->GetFileSpec().GetPath();
221 module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
222 module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
223 module_stat.debug_info_size = sym_file->GetDebugInfoSize();
224 module_stat.debug_info_index_loaded_from_cache =
225 sym_file->GetDebugInfoIndexWasLoadedFromCache();
226 if (module_stat.debug_info_index_loaded_from_cache)
227 ++debug_index_loaded;
228 module_stat.debug_info_index_saved_to_cache =
229 sym_file->GetDebugInfoIndexWasSavedToCache();
230 if (module_stat.debug_info_index_saved_to_cache)
231 ++debug_index_saved;
232 ModuleList symbol_modules = sym_file->GetDebugInfoModules();
233 for (const auto &symbol_module: symbol_modules.Modules())
234 module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
235 module_stat.symtab_stripped = module->GetObjectFile()->IsStripped();
236 if (module_stat.symtab_stripped)
237 ++num_stripped_modules;
238 module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() &&
239 module_stat.debug_info_size > 0;
240 if (module_stat.debug_info_enabled)
241 ++num_debug_info_enabled_modules;
242 if (module_stat.debug_info_size > 0)
243 ++num_modules_has_debug_info;
244 }
245 symtab_parse_time += module_stat.symtab_parse_time;
246 symtab_index_time += module_stat.symtab_index_time;
247 debug_parse_time += module_stat.debug_parse_time;
248 debug_index_time += module_stat.debug_index_time;
249 debug_info_size += module_stat.debug_info_size;
250 json_modules.emplace_back(module_stat.ToJSON());
251 }
252
253 ConstStringStats const_string_stats;
254 json::Object json_memory{
255 {"strings", const_string_stats.ToJSON()},
256 };
257
258 json::Object global_stats{
259 {"targets", std::move(json_targets)},
260 {"modules", std::move(json_modules)},
261 {"memory", std::move(json_memory)},
262 {"totalSymbolTableParseTime", symtab_parse_time},
263 {"totalSymbolTableIndexTime", symtab_index_time},
264 {"totalSymbolTablesLoadedFromCache", symtabs_loaded},
265 {"totalSymbolTablesSavedToCache", symtabs_saved},
266 {"totalDebugInfoParseTime", debug_parse_time},
267 {"totalDebugInfoIndexTime", debug_index_time},
268 {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded},
269 {"totalDebugInfoIndexSavedToCache", debug_index_saved},
270 {"totalDebugInfoByteSize", debug_info_size},
271 {"totalModuleCount", num_modules},
272 {"totalModuleCountHasDebugInfo", num_modules_has_debug_info},
273 {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
274 {"totalSymbolTableStripped", num_stripped_modules},
275 };
276 return std::move(global_stats);
277 }
278