diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Target/Statistics.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Target/Statistics.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Target/Statistics.cpp b/contrib/llvm-project/lldb/source/Target/Statistics.cpp new file mode 100644 index 000000000000..583d1524881f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Target/Statistics.cpp @@ -0,0 +1,410 @@ +//===-- Statistics.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Statistics.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/StructuredData.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + const std::string &str) { + if (str.empty()) + return; + if (LLVM_LIKELY(llvm::json::isUTF8(str))) + obj.try_emplace(key, str); + else + obj.try_emplace(key, llvm::json::fixUTF8(str)); +} + +json::Value StatsSuccessFail::ToJSON() const { + return json::Object{{"successes", successes}, {"failures", failures}}; +} + +static double elapsed(const StatsTimepoint &start, const StatsTimepoint &end) { + StatsDuration::Duration elapsed = + end.time_since_epoch() - start.time_since_epoch(); + return elapsed.count(); +} + +void TargetStats::CollectStats(Target &target) { + m_module_identifiers.clear(); + for (ModuleSP module_sp : target.GetImages().Modules()) + m_module_identifiers.emplace_back((intptr_t)module_sp.get()); +} + +json::Value ModuleStats::ToJSON() const { + json::Object module; + EmplaceSafeString(module, "path", path); + EmplaceSafeString(module, "uuid", uuid); + EmplaceSafeString(module, "triple", triple); + module.try_emplace("identifier", identifier); + module.try_emplace("symbolTableParseTime", symtab_parse_time); + module.try_emplace("symbolTableIndexTime", symtab_index_time); + module.try_emplace("symbolTableLoadedFromCache", symtab_loaded_from_cache); + module.try_emplace("symbolTableSavedToCache", symtab_saved_to_cache); + module.try_emplace("debugInfoParseTime", debug_parse_time); + module.try_emplace("debugInfoIndexTime", debug_index_time); + module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size); + module.try_emplace("debugInfoIndexLoadedFromCache", + debug_info_index_loaded_from_cache); + module.try_emplace("debugInfoIndexSavedToCache", + debug_info_index_saved_to_cache); + module.try_emplace("debugInfoEnabled", debug_info_enabled); + module.try_emplace("debugInfoHadVariableErrors", + debug_info_had_variable_errors); + module.try_emplace("debugInfoHadIncompleteTypes", + debug_info_had_incomplete_types); + module.try_emplace("symbolTableStripped", symtab_stripped); + if (!symfile_path.empty()) + module.try_emplace("symbolFilePath", symfile_path); + + if (!symfile_modules.empty()) { + json::Array symfile_ids; + for (const auto symfile_id: symfile_modules) + symfile_ids.emplace_back(symfile_id); + module.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids)); + } + + if (!type_system_stats.empty()) { + json::Array type_systems; + for (const auto &entry : type_system_stats) { + json::Object obj; + obj.try_emplace(entry.first().str(), entry.second); + type_systems.emplace_back(std::move(obj)); + } + module.try_emplace("typeSystemInfo", std::move(type_systems)); + } + + return module; +} + +llvm::json::Value ConstStringStats::ToJSON() const { + json::Object obj; + obj.try_emplace<int64_t>("bytesTotal", stats.GetBytesTotal()); + obj.try_emplace<int64_t>("bytesUsed", stats.GetBytesUsed()); + obj.try_emplace<int64_t>("bytesUnused", stats.GetBytesUnused()); + return obj; +} + +json::Value +TargetStats::ToJSON(Target &target, + const lldb_private::StatisticsOptions &options) { + json::Object target_metrics_json; + ProcessSP process_sp = target.GetProcessSP(); + const bool summary_only = options.GetSummaryOnly(); + const bool include_modules = options.GetIncludeModules(); + if (!summary_only) { + CollectStats(target); + + json::Array json_module_uuid_array; + for (auto module_identifier : m_module_identifiers) + json_module_uuid_array.emplace_back(module_identifier); + + target_metrics_json.try_emplace(m_expr_eval.name, m_expr_eval.ToJSON()); + target_metrics_json.try_emplace(m_frame_var.name, m_frame_var.ToJSON()); + if (include_modules) + target_metrics_json.try_emplace("moduleIdentifiers", + std::move(json_module_uuid_array)); + + if (m_launch_or_attach_time && m_first_private_stop_time) { + double elapsed_time = + elapsed(*m_launch_or_attach_time, *m_first_private_stop_time); + target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time); + } + if (m_launch_or_attach_time && m_first_public_stop_time) { + double elapsed_time = + elapsed(*m_launch_or_attach_time, *m_first_public_stop_time); + target_metrics_json.try_emplace("firstStopTime", elapsed_time); + } + target_metrics_json.try_emplace("targetCreateTime", + m_create_time.get().count()); + + json::Array breakpoints_array; + double totalBreakpointResolveTime = 0.0; + // Report both the normal breakpoint list and the internal breakpoint list. + for (int i = 0; i < 2; ++i) { + BreakpointList &breakpoints = target.GetBreakpointList(i == 1); + std::unique_lock<std::recursive_mutex> lock; + breakpoints.GetListMutex(lock); + size_t num_breakpoints = breakpoints.GetSize(); + for (size_t i = 0; i < num_breakpoints; i++) { + Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); + breakpoints_array.push_back(bp->GetStatistics()); + totalBreakpointResolveTime += bp->GetResolveTime().count(); + } + } + target_metrics_json.try_emplace("breakpoints", + std::move(breakpoints_array)); + target_metrics_json.try_emplace("totalBreakpointResolveTime", + totalBreakpointResolveTime); + + if (process_sp) { + UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals(); + if (unix_signals_sp) + target_metrics_json.try_emplace( + "signals", unix_signals_sp->GetHitCountStatistics()); + } + } + + // Counting "totalSharedLibraryEventHitCount" from breakpoints of kind + // "shared-library-event". + { + uint32_t shared_library_event_breakpoint_hit_count = 0; + // The "shared-library-event" is only found in the internal breakpoint list. + BreakpointList &breakpoints = target.GetBreakpointList(/* internal */ true); + std::unique_lock<std::recursive_mutex> lock; + breakpoints.GetListMutex(lock); + size_t num_breakpoints = breakpoints.GetSize(); + for (size_t i = 0; i < num_breakpoints; i++) { + Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); + if (strcmp(bp->GetBreakpointKind(), "shared-library-event") == 0) + shared_library_event_breakpoint_hit_count += bp->GetHitCount(); + } + + target_metrics_json.try_emplace("totalSharedLibraryEventHitCount", + shared_library_event_breakpoint_hit_count); + } + + if (process_sp) { + uint32_t stop_id = process_sp->GetStopID(); + target_metrics_json.try_emplace("stopCount", stop_id); + + llvm::StringRef dyld_plugin_name; + if (process_sp->GetDynamicLoader()) + dyld_plugin_name = process_sp->GetDynamicLoader()->GetPluginName(); + target_metrics_json.try_emplace("dyldPluginName", dyld_plugin_name); + } + target_metrics_json.try_emplace("sourceMapDeduceCount", + m_source_map_deduce_count); + return target_metrics_json; +} + +void TargetStats::SetLaunchOrAttachTime() { + m_launch_or_attach_time = StatsClock::now(); + m_first_private_stop_time = std::nullopt; +} + +void TargetStats::SetFirstPrivateStopTime() { + // Launching and attaching has many paths depending on if synchronous mode + // was used or if we are stopping at the entry point or not. Only set the + // first stop time if it hasn't already been set. + if (!m_first_private_stop_time) + m_first_private_stop_time = StatsClock::now(); +} + +void TargetStats::SetFirstPublicStopTime() { + // Launching and attaching has many paths depending on if synchronous mode + // was used or if we are stopping at the entry point or not. Only set the + // first stop time if it hasn't already been set. + if (!m_first_public_stop_time) + m_first_public_stop_time = StatsClock::now(); +} + +void TargetStats::IncreaseSourceMapDeduceCount() { + ++m_source_map_deduce_count; +} + +bool DebuggerStats::g_collecting_stats = false; + +llvm::json::Value DebuggerStats::ReportStatistics( + Debugger &debugger, Target *target, + const lldb_private::StatisticsOptions &options) { + + const bool summary_only = options.GetSummaryOnly(); + const bool load_all_debug_info = options.GetLoadAllDebugInfo(); + const bool include_targets = options.GetIncludeTargets(); + const bool include_modules = options.GetIncludeModules(); + const bool include_transcript = options.GetIncludeTranscript(); + + json::Array json_targets; + json::Array json_modules; + double symtab_parse_time = 0.0; + double symtab_index_time = 0.0; + double debug_parse_time = 0.0; + double debug_index_time = 0.0; + uint32_t symtabs_loaded = 0; + uint32_t symtabs_saved = 0; + uint32_t debug_index_loaded = 0; + uint32_t debug_index_saved = 0; + uint64_t debug_info_size = 0; + + std::vector<ModuleStats> modules; + std::lock_guard<std::recursive_mutex> guard( + Module::GetAllocationModuleCollectionMutex()); + const uint64_t num_modules = Module::GetNumberAllocatedModules(); + uint32_t num_debug_info_enabled_modules = 0; + uint32_t num_modules_has_debug_info = 0; + uint32_t num_modules_with_variable_errors = 0; + uint32_t num_modules_with_incomplete_types = 0; + uint32_t num_stripped_modules = 0; + for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { + Module *module = Module::GetAllocatedModuleAtIndex(image_idx); + ModuleStats module_stat; + module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count(); + module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count(); + Symtab *symtab = module->GetSymtab(); + if (symtab) { + module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache(); + if (module_stat.symtab_loaded_from_cache) + ++symtabs_loaded; + module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache(); + if (module_stat.symtab_saved_to_cache) + ++symtabs_saved; + } + SymbolFile *sym_file = module->GetSymbolFile(); + if (sym_file) { + if (!summary_only) { + if (sym_file->GetObjectFile() != module->GetObjectFile()) + module_stat.symfile_path = + sym_file->GetObjectFile()->GetFileSpec().GetPath(); + ModuleList symbol_modules = sym_file->GetDebugInfoModules(); + for (const auto &symbol_module : symbol_modules.Modules()) + module_stat.symfile_modules.push_back((intptr_t)symbol_module.get()); + } + module_stat.debug_info_index_loaded_from_cache = + sym_file->GetDebugInfoIndexWasLoadedFromCache(); + if (module_stat.debug_info_index_loaded_from_cache) + ++debug_index_loaded; + module_stat.debug_info_index_saved_to_cache = + sym_file->GetDebugInfoIndexWasSavedToCache(); + if (module_stat.debug_info_index_saved_to_cache) + ++debug_index_saved; + module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count(); + module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count(); + module_stat.debug_info_size = + sym_file->GetDebugInfoSize(load_all_debug_info); + module_stat.symtab_stripped = module->GetObjectFile()->IsStripped(); + if (module_stat.symtab_stripped) + ++num_stripped_modules; + module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() && + module_stat.debug_info_size > 0; + module_stat.debug_info_had_variable_errors = + sym_file->GetDebugInfoHadFrameVariableErrors(); + if (module_stat.debug_info_enabled) + ++num_debug_info_enabled_modules; + if (module_stat.debug_info_size > 0) + ++num_modules_has_debug_info; + if (module_stat.debug_info_had_variable_errors) + ++num_modules_with_variable_errors; + } + symtab_parse_time += module_stat.symtab_parse_time; + symtab_index_time += module_stat.symtab_index_time; + debug_parse_time += module_stat.debug_parse_time; + debug_index_time += module_stat.debug_index_time; + debug_info_size += module_stat.debug_info_size; + module->ForEachTypeSystem([&](lldb::TypeSystemSP ts) { + if (auto stats = ts->ReportStatistics()) + module_stat.type_system_stats.insert({ts->GetPluginName(), *stats}); + if (ts->GetHasForcefullyCompletedTypes()) + module_stat.debug_info_had_incomplete_types = true; + return true; + }); + if (module_stat.debug_info_had_incomplete_types) + ++num_modules_with_incomplete_types; + + if (include_modules) { + module_stat.identifier = (intptr_t)module; + module_stat.path = module->GetFileSpec().GetPath(); + if (ConstString object_name = module->GetObjectName()) { + module_stat.path.append(1, '('); + module_stat.path.append(object_name.GetStringRef().str()); + module_stat.path.append(1, ')'); + } + module_stat.uuid = module->GetUUID().GetAsString(); + module_stat.triple = module->GetArchitecture().GetTriple().str(); + json_modules.emplace_back(module_stat.ToJSON()); + } + } + + json::Object global_stats{ + {"totalSymbolTableParseTime", symtab_parse_time}, + {"totalSymbolTableIndexTime", symtab_index_time}, + {"totalSymbolTablesLoadedFromCache", symtabs_loaded}, + {"totalSymbolTablesSavedToCache", symtabs_saved}, + {"totalDebugInfoParseTime", debug_parse_time}, + {"totalDebugInfoIndexTime", debug_index_time}, + {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded}, + {"totalDebugInfoIndexSavedToCache", debug_index_saved}, + {"totalDebugInfoByteSize", debug_info_size}, + {"totalModuleCount", num_modules}, + {"totalModuleCountHasDebugInfo", num_modules_has_debug_info}, + {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors}, + {"totalModuleCountWithIncompleteTypes", + num_modules_with_incomplete_types}, + {"totalDebugInfoEnabled", num_debug_info_enabled_modules}, + {"totalSymbolTableStripped", num_stripped_modules}, + }; + + if (include_targets) { + if (target) { + json_targets.emplace_back(target->ReportStatistics(options)); + } else { + for (const auto &target : debugger.GetTargetList().Targets()) + json_targets.emplace_back(target->ReportStatistics(options)); + } + global_stats.try_emplace("targets", std::move(json_targets)); + } + + ConstStringStats const_string_stats; + json::Object json_memory{ + {"strings", const_string_stats.ToJSON()}, + }; + global_stats.try_emplace("memory", std::move(json_memory)); + if (!summary_only) { + json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics(); + global_stats.try_emplace("commands", std::move(cmd_stats)); + } + + if (include_modules) { + global_stats.try_emplace("modules", std::move(json_modules)); + } + + if (include_transcript) { + // When transcript is available, add it to the to-be-returned statistics. + // + // NOTE: + // When the statistics is polled by an LLDB command: + // - The transcript in the returned statistics *will NOT* contain the + // returned statistics itself (otherwise infinite recursion). + // - The returned statistics *will* be written to the internal transcript + // buffer. It *will* appear in the next statistcs or transcript poll. + // + // For example, let's say the following commands are run in order: + // - "version" + // - "statistics dump" <- call it "A" + // - "statistics dump" <- call it "B" + // The output of "A" will contain the transcript of "version" and + // "statistics dump" (A), with the latter having empty output. The output + // of B will contain the trascnript of "version", "statistics dump" (A), + // "statistics dump" (B), with A's output populated and B's output empty. + const StructuredData::Array &transcript = + debugger.GetCommandInterpreter().GetTranscript(); + if (transcript.GetSize() != 0) { + std::string buffer; + llvm::raw_string_ostream ss(buffer); + json::OStream json_os(ss); + transcript.Serialize(json_os); + if (auto json_transcript = llvm::json::parse(ss.str())) + global_stats.try_emplace("transcript", + std::move(json_transcript.get())); + } + } + + return std::move(global_stats); +} |