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 target_metrics_json.try_emplace("sourceRealpathAttemptCount", 196 m_source_realpath_attempt_count); 197 target_metrics_json.try_emplace("sourceRealpathCompatibleCount", 198 m_source_realpath_compatible_count); 199 target_metrics_json.try_emplace("summaryProviderStatistics", 200 target.GetSummaryStatisticsCache().ToJSON()); 201 return target_metrics_json; 202 } 203 204 void TargetStats::Reset(Target &target) { 205 m_launch_or_attach_time.reset(); 206 m_first_private_stop_time.reset(); 207 m_first_public_stop_time.reset(); 208 // Report both the normal breakpoint list and the internal breakpoint list. 209 for (int i = 0; i < 2; ++i) { 210 BreakpointList &breakpoints = target.GetBreakpointList(i == 1); 211 std::unique_lock<std::recursive_mutex> lock; 212 breakpoints.GetListMutex(lock); 213 size_t num_breakpoints = breakpoints.GetSize(); 214 for (size_t i = 0; i < num_breakpoints; i++) { 215 Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); 216 bp->ResetStatistics(); 217 } 218 } 219 target.GetSummaryStatisticsCache().Reset(); 220 } 221 222 void TargetStats::SetLaunchOrAttachTime() { 223 m_launch_or_attach_time = StatsClock::now(); 224 m_first_private_stop_time = std::nullopt; 225 } 226 227 void TargetStats::SetFirstPrivateStopTime() { 228 // Launching and attaching has many paths depending on if synchronous mode 229 // was used or if we are stopping at the entry point or not. Only set the 230 // first stop time if it hasn't already been set. 231 if (!m_first_private_stop_time) 232 m_first_private_stop_time = StatsClock::now(); 233 } 234 235 void TargetStats::SetFirstPublicStopTime() { 236 // Launching and attaching has many paths depending on if synchronous mode 237 // was used or if we are stopping at the entry point or not. Only set the 238 // first stop time if it hasn't already been set. 239 if (!m_first_public_stop_time) 240 m_first_public_stop_time = StatsClock::now(); 241 } 242 243 void TargetStats::IncreaseSourceMapDeduceCount() { 244 ++m_source_map_deduce_count; 245 } 246 247 void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count) { 248 m_source_realpath_attempt_count += count; 249 } 250 251 void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) { 252 m_source_realpath_compatible_count += count; 253 } 254 255 bool DebuggerStats::g_collecting_stats = false; 256 257 void DebuggerStats::ResetStatistics(Debugger &debugger, Target *target) { 258 std::lock_guard<std::recursive_mutex> guard( 259 Module::GetAllocationModuleCollectionMutex()); 260 const uint64_t num_modules = target != nullptr 261 ? target->GetImages().GetSize() 262 : Module::GetNumberAllocatedModules(); 263 for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { 264 Module *module = target != nullptr 265 ? target->GetImages().GetModuleAtIndex(image_idx).get() 266 : Module::GetAllocatedModuleAtIndex(image_idx); 267 if (module == nullptr) 268 continue; 269 module->ResetStatistics(); 270 } 271 if (target) 272 target->ResetStatistics(); 273 else { 274 for (const auto &target : debugger.GetTargetList().Targets()) 275 target->ResetStatistics(); 276 } 277 } 278 279 llvm::json::Value DebuggerStats::ReportStatistics( 280 Debugger &debugger, Target *target, 281 const lldb_private::StatisticsOptions &options) { 282 283 const bool summary_only = options.GetSummaryOnly(); 284 const bool load_all_debug_info = options.GetLoadAllDebugInfo(); 285 const bool include_targets = options.GetIncludeTargets(); 286 const bool include_modules = options.GetIncludeModules(); 287 const bool include_transcript = options.GetIncludeTranscript(); 288 289 json::Array json_targets; 290 json::Array json_modules; 291 double symtab_parse_time = 0.0; 292 double symtab_index_time = 0.0; 293 double debug_parse_time = 0.0; 294 double debug_index_time = 0.0; 295 uint32_t symtabs_loaded = 0; 296 uint32_t symtabs_saved = 0; 297 uint32_t debug_index_loaded = 0; 298 uint32_t debug_index_saved = 0; 299 uint64_t debug_info_size = 0; 300 301 std::vector<ModuleStats> modules; 302 std::lock_guard<std::recursive_mutex> guard( 303 Module::GetAllocationModuleCollectionMutex()); 304 const uint64_t num_modules = target != nullptr 305 ? target->GetImages().GetSize() 306 : Module::GetNumberAllocatedModules(); 307 uint32_t num_debug_info_enabled_modules = 0; 308 uint32_t num_modules_has_debug_info = 0; 309 uint32_t num_modules_with_variable_errors = 0; 310 uint32_t num_modules_with_incomplete_types = 0; 311 uint32_t num_stripped_modules = 0; 312 for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { 313 Module *module = target != nullptr 314 ? target->GetImages().GetModuleAtIndex(image_idx).get() 315 : Module::GetAllocatedModuleAtIndex(image_idx); 316 ModuleStats module_stat; 317 module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count(); 318 module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count(); 319 Symtab *symtab = module->GetSymtab(); 320 if (symtab) { 321 module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache(); 322 if (module_stat.symtab_loaded_from_cache) 323 ++symtabs_loaded; 324 module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache(); 325 if (module_stat.symtab_saved_to_cache) 326 ++symtabs_saved; 327 } 328 SymbolFile *sym_file = module->GetSymbolFile(); 329 if (sym_file) { 330 if (!summary_only) { 331 if (sym_file->GetObjectFile() != module->GetObjectFile()) 332 module_stat.symfile_path = 333 sym_file->GetObjectFile()->GetFileSpec().GetPath(); 334 ModuleList symbol_modules = sym_file->GetDebugInfoModules(); 335 for (const auto &symbol_module : symbol_modules.Modules()) 336 module_stat.symfile_modules.push_back((intptr_t)symbol_module.get()); 337 } 338 module_stat.debug_info_index_loaded_from_cache = 339 sym_file->GetDebugInfoIndexWasLoadedFromCache(); 340 if (module_stat.debug_info_index_loaded_from_cache) 341 ++debug_index_loaded; 342 module_stat.debug_info_index_saved_to_cache = 343 sym_file->GetDebugInfoIndexWasSavedToCache(); 344 if (module_stat.debug_info_index_saved_to_cache) 345 ++debug_index_saved; 346 module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count(); 347 module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count(); 348 module_stat.debug_info_size = 349 sym_file->GetDebugInfoSize(load_all_debug_info); 350 module_stat.symtab_stripped = module->GetObjectFile()->IsStripped(); 351 if (module_stat.symtab_stripped) 352 ++num_stripped_modules; 353 module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() && 354 module_stat.debug_info_size > 0; 355 module_stat.debug_info_had_variable_errors = 356 sym_file->GetDebugInfoHadFrameVariableErrors(); 357 if (module_stat.debug_info_enabled) 358 ++num_debug_info_enabled_modules; 359 if (module_stat.debug_info_size > 0) 360 ++num_modules_has_debug_info; 361 if (module_stat.debug_info_had_variable_errors) 362 ++num_modules_with_variable_errors; 363 } 364 symtab_parse_time += module_stat.symtab_parse_time; 365 symtab_index_time += module_stat.symtab_index_time; 366 debug_parse_time += module_stat.debug_parse_time; 367 debug_index_time += module_stat.debug_index_time; 368 debug_info_size += module_stat.debug_info_size; 369 module->ForEachTypeSystem([&](lldb::TypeSystemSP ts) { 370 if (auto stats = ts->ReportStatistics()) 371 module_stat.type_system_stats.insert({ts->GetPluginName(), *stats}); 372 if (ts->GetHasForcefullyCompletedTypes()) 373 module_stat.debug_info_had_incomplete_types = true; 374 return true; 375 }); 376 if (module_stat.debug_info_had_incomplete_types) 377 ++num_modules_with_incomplete_types; 378 379 if (include_modules) { 380 module_stat.identifier = (intptr_t)module; 381 module_stat.path = module->GetFileSpec().GetPath(); 382 if (ConstString object_name = module->GetObjectName()) { 383 module_stat.path.append(1, '('); 384 module_stat.path.append(object_name.GetStringRef().str()); 385 module_stat.path.append(1, ')'); 386 } 387 module_stat.uuid = module->GetUUID().GetAsString(); 388 module_stat.triple = module->GetArchitecture().GetTriple().str(); 389 json_modules.emplace_back(module_stat.ToJSON()); 390 } 391 } 392 393 json::Object global_stats{ 394 {"totalSymbolTableParseTime", symtab_parse_time}, 395 {"totalSymbolTableIndexTime", symtab_index_time}, 396 {"totalSymbolTablesLoadedFromCache", symtabs_loaded}, 397 {"totalSymbolTablesSavedToCache", symtabs_saved}, 398 {"totalDebugInfoParseTime", debug_parse_time}, 399 {"totalDebugInfoIndexTime", debug_index_time}, 400 {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded}, 401 {"totalDebugInfoIndexSavedToCache", debug_index_saved}, 402 {"totalDebugInfoByteSize", debug_info_size}, 403 {"totalModuleCount", num_modules}, 404 {"totalModuleCountHasDebugInfo", num_modules_has_debug_info}, 405 {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors}, 406 {"totalModuleCountWithIncompleteTypes", 407 num_modules_with_incomplete_types}, 408 {"totalDebugInfoEnabled", num_debug_info_enabled_modules}, 409 {"totalSymbolTableStripped", num_stripped_modules}, 410 }; 411 412 if (include_targets) { 413 if (target) { 414 json_targets.emplace_back(target->ReportStatistics(options)); 415 } else { 416 for (const auto &target : debugger.GetTargetList().Targets()) 417 json_targets.emplace_back(target->ReportStatistics(options)); 418 } 419 global_stats.try_emplace("targets", std::move(json_targets)); 420 } 421 422 ConstStringStats const_string_stats; 423 json::Object json_memory{ 424 {"strings", const_string_stats.ToJSON()}, 425 }; 426 global_stats.try_emplace("memory", std::move(json_memory)); 427 if (!summary_only) { 428 json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics(); 429 global_stats.try_emplace("commands", std::move(cmd_stats)); 430 } 431 432 if (include_modules) { 433 global_stats.try_emplace("modules", std::move(json_modules)); 434 } 435 436 if (include_transcript) { 437 // When transcript is available, add it to the to-be-returned statistics. 438 // 439 // NOTE: 440 // When the statistics is polled by an LLDB command: 441 // - The transcript in the returned statistics *will NOT* contain the 442 // returned statistics itself (otherwise infinite recursion). 443 // - The returned statistics *will* be written to the internal transcript 444 // buffer. It *will* appear in the next statistcs or transcript poll. 445 // 446 // For example, let's say the following commands are run in order: 447 // - "version" 448 // - "statistics dump" <- call it "A" 449 // - "statistics dump" <- call it "B" 450 // The output of "A" will contain the transcript of "version" and 451 // "statistics dump" (A), with the latter having empty output. The output 452 // of B will contain the trascnript of "version", "statistics dump" (A), 453 // "statistics dump" (B), with A's output populated and B's output empty. 454 const StructuredData::Array &transcript = 455 debugger.GetCommandInterpreter().GetTranscript(); 456 if (transcript.GetSize() != 0) { 457 std::string buffer; 458 llvm::raw_string_ostream ss(buffer); 459 json::OStream json_os(ss); 460 transcript.Serialize(json_os); 461 if (auto json_transcript = llvm::json::parse(buffer)) 462 global_stats.try_emplace("transcript", 463 std::move(json_transcript.get())); 464 } 465 } 466 467 return std::move(global_stats); 468 } 469 470 llvm::json::Value SummaryStatistics::ToJSON() const { 471 return json::Object{{ 472 {"name", GetName()}, 473 {"type", GetSummaryKindName()}, 474 {"count", GetSummaryCount()}, 475 {"totalTime", GetTotalTime()}, 476 }}; 477 } 478 479 json::Value SummaryStatisticsCache::ToJSON() { 480 std::lock_guard<std::mutex> guard(m_map_mutex); 481 json::Array json_summary_stats; 482 for (const auto &summary_stat : m_summary_stats_map) 483 json_summary_stats.emplace_back(summary_stat.second->ToJSON()); 484 485 return json_summary_stats; 486 } 487 488 void SummaryStatisticsCache::Reset() { 489 for (const auto &summary_stat : m_summary_stats_map) 490 summary_stat.second->Reset(); 491 } 492