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 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 35 json::Value StatsSuccessFail::ToJSON() const { 36 return json::Object{{"successes", successes}, {"failures", failures}}; 37 } 38 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 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 51 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 97 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 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 198 void TargetStats::SetLaunchOrAttachTime() { 199 m_launch_or_attach_time = StatsClock::now(); 200 m_first_private_stop_time = std::nullopt; 201 } 202 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 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 219 void TargetStats::IncreaseSourceMapDeduceCount() { 220 ++m_source_map_deduce_count; 221 } 222 223 bool DebuggerStats::g_collecting_stats = false; 224 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