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/Symbol/SymbolFile.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/Target.h"
16 #include "lldb/Target/UnixSignals.h"
19 using namespace lldb_private
;
22 static void EmplaceSafeString(llvm::json::Object
&obj
, llvm::StringRef key
,
23 const std::string
&str
) {
26 if (LLVM_LIKELY(llvm::json::isUTF8(str
)))
27 obj
.try_emplace(key
, str
);
29 obj
.try_emplace(key
, llvm::json::fixUTF8(str
));
32 json::Value
StatsSuccessFail::ToJSON() const {
33 return json::Object
{{"successes", successes
}, {"failures", failures
}};
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();
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());
48 json::Value
ModuleStats::ToJSON() const {
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
);
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
));
81 if (!type_system_stats
.empty()) {
82 json::Array type_systems
;
83 for (const auto &entry
: type_system_stats
) {
85 obj
.try_emplace(entry
.first().str(), entry
.second
);
86 type_systems
.emplace_back(std::move(obj
));
88 module
.try_emplace("typeSystemInfo", std::move(type_systems
));
94 llvm::json::Value
ConstStringStats::ToJSON() const {
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());
102 json::Value
TargetStats::ToJSON(Target
&target
) {
103 CollectStats(target
);
105 json::Array json_module_uuid_array
;
106 for (auto module_identifier
: m_module_identifiers
)
107 json_module_uuid_array
.emplace_back(module_identifier
);
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
)}};
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
);
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
);
124 target_metrics_json
.try_emplace("targetCreateTime",
125 m_create_time
.get().count());
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();
142 ProcessSP process_sp
= target
.GetProcessSP();
144 UnixSignalsSP unix_signals_sp
= process_sp
->GetUnixSignals();
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
);
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
);
156 return target_metrics_json
;
159 void TargetStats::SetLaunchOrAttachTime() {
160 m_launch_or_attach_time
= StatsClock::now();
161 m_first_private_stop_time
= std::nullopt
;
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();
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();
180 void TargetStats::IncreaseSourceMapDeduceCount() {
181 ++m_source_map_deduce_count
;
184 bool DebuggerStats::g_collecting_stats
= false;
186 llvm::json::Value
DebuggerStats::ReportStatistics(Debugger
&debugger
,
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;
200 json_targets
.emplace_back(target
->ReportStatistics());
202 for (const auto &target
: debugger
.GetTargetList().Targets())
203 json_targets
.emplace_back(target
->ReportStatistics());
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, ')');
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();
230 module_stat
.symtab_loaded_from_cache
= symtab
->GetWasLoadedFromCache();
231 if (module_stat
.symtab_loaded_from_cache
)
233 module_stat
.symtab_saved_to_cache
= symtab
->GetWasSavedToCache();
234 if (module_stat
.symtab_saved_to_cache
)
237 SymbolFile
*sym_file
= module
->GetSymbolFile();
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
)
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
;
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;
283 if (module_stat
.debug_info_had_incomplete_types
)
284 ++num_modules_with_incomplete_types
;
286 json_modules
.emplace_back(module_stat
.ToJSON());
289 ConstStringStats const_string_stats
;
290 json::Object json_memory
{
291 {"strings", const_string_stats
.ToJSON()},
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
},
314 return std::move(global_stats
);