| //===-- 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/Core/PluginManager.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/LLDBLog.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); | |
| module.try_emplace("symbolTableSymbolCount", symtab_symbol_count); | |
| module.try_emplace("dwoFileCount", dwo_stats.dwo_file_count); | |
| module.try_emplace("loadedDwoFileCount", dwo_stats.loaded_dwo_file_count); | |
| module.try_emplace("dwoErrorCount", dwo_stats.dwo_error_count); | |
| if (!symbol_locator_time.map.empty()) { | |
| json::Object obj; | |
| for (const auto &entry : symbol_locator_time.map) | |
| obj.try_emplace(entry.first().str(), entry.second); | |
| module.try_emplace("symbolLocatorTime", std::move(obj)); | |
| } | |
| 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("expressionEvaluation", | |
| m_expr_eval.ToJSON()); | |
| target_metrics_json.try_emplace("frameVariable", 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()); | |
| if (m_load_core_time.get().count() > 0) { | |
| target_metrics_json.try_emplace("loadCoreTime", | |
| m_load_core_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); | |
| if (process_sp->GetCoreFile()) | |
| target_metrics_json.try_emplace( | |
| "coreFile", process_sp->GetCoreFile().GetFilename().str()); | |
| } | |
| target_metrics_json.try_emplace("sourceMapDeduceCount", | |
| m_source_map_deduce_count); | |
| target_metrics_json.try_emplace("sourceRealpathAttemptCount", | |
| m_source_realpath_attempt_count); | |
| target_metrics_json.try_emplace("sourceRealpathCompatibleCount", | |
| m_source_realpath_compatible_count); | |
| target_metrics_json.try_emplace("summaryProviderStatistics", | |
| target.GetSummaryStatisticsCache().ToJSON()); | |
| return target_metrics_json; | |
| } | |
| void TargetStats::Reset(Target &target) { | |
| m_launch_or_attach_time.reset(); | |
| m_first_private_stop_time.reset(); | |
| m_first_public_stop_time.reset(); | |
| // 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(); | |
| bp->ResetStatistics(); | |
| } | |
| } | |
| target.GetSummaryStatisticsCache().Reset(); | |
| } | |
| 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; | |
| } | |
| void TargetStats::IncreaseSourceRealpathAttemptCount(uint32_t count) { | |
| m_source_realpath_attempt_count += count; | |
| } | |
| void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) { | |
| m_source_realpath_compatible_count += count; | |
| } | |
| bool DebuggerStats::g_collecting_stats = false; | |
| void DebuggerStats::ResetStatistics(Debugger &debugger, Target *target) { | |
| std::lock_guard<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex()); | |
| const uint64_t num_modules = target != nullptr | |
| ? target->GetImages().GetSize() | |
| : Module::GetNumberAllocatedModules(); | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| Module *module = target != nullptr | |
| ? target->GetImages().GetModuleAtIndex(image_idx).get() | |
| : Module::GetAllocatedModuleAtIndex(image_idx); | |
| if (module == nullptr) | |
| continue; | |
| module->ResetStatistics(); | |
| } | |
| if (target) | |
| target->ResetStatistics(); | |
| else { | |
| for (const auto &target : debugger.GetTargetList().Targets()) | |
| target->ResetStatistics(); | |
| } | |
| } | |
| 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(); | |
| const bool include_plugins = options.GetIncludePlugins(); | |
| json::Array json_targets; | |
| json::Array json_modules; | |
| StatisticsMap symbol_locator_total_time; | |
| 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_loaded_from_cache = 0; | |
| uint32_t symtabs_saved_to_cache = 0; | |
| uint32_t debug_index_loaded = 0; | |
| uint32_t debug_index_saved = 0; | |
| uint64_t debug_info_size = 0; | |
| std::lock_guard<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex()); | |
| const uint64_t num_modules = target != nullptr | |
| ? target->GetImages().GetSize() | |
| : 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; | |
| uint32_t symtab_symbol_count = 0; | |
| DWOStats total_dwo_stats; | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| Module *module = target != nullptr | |
| ? target->GetImages().GetModuleAtIndex(image_idx).get() | |
| : 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(); | |
| module_stat.symbol_locator_time = module->GetSymbolLocatorStatistics(); | |
| symbol_locator_total_time.merge(module_stat.symbol_locator_time); | |
| Symtab *symtab = module->GetSymtab(/*can_create=*/false); | |
| if (symtab) { | |
| module_stat.symtab_symbol_count = symtab->GetNumSymbols(); | |
| symtab_symbol_count += module_stat.symtab_symbol_count; | |
| ++symtabs_loaded; | |
| module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache(); | |
| if (module_stat.symtab_loaded_from_cache) | |
| ++symtabs_loaded_from_cache; | |
| module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache(); | |
| if (module_stat.symtab_saved_to_cache) | |
| ++symtabs_saved_to_cache; | |
| } | |
| SymbolFile *sym_file = module->GetSymbolFile(/*can_create=*/false); | |
| 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()); | |
| } | |
| DWOStats current_dwo_stats = sym_file->GetDwoStats(); | |
| module_stat.dwo_stats += current_dwo_stats; | |
| total_dwo_stats += current_dwo_stats; | |
| 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}, | |
| {"totalSymbolTablesLoaded", symtabs_loaded}, | |
| {"totalSymbolTablesLoadedFromCache", symtabs_loaded_from_cache}, | |
| {"totalSymbolTablesSavedToCache", symtabs_saved_to_cache}, | |
| {"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}, | |
| {"totalSymbolTableSymbolCount", symtab_symbol_count}, | |
| {"totalLoadedDwoFileCount", total_dwo_stats.loaded_dwo_file_count}, | |
| {"totalDwoFileCount", total_dwo_stats.dwo_file_count}, | |
| {"totalDwoErrorCount", total_dwo_stats.dwo_error_count}, | |
| }; | |
| 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)); | |
| } | |
| if (!symbol_locator_total_time.map.empty()) { | |
| json::Object obj; | |
| for (const auto &entry : symbol_locator_total_time.map) | |
| obj.try_emplace(entry.first().str(), entry.second); | |
| global_stats.try_emplace("totalSymbolLocatorTime", std::move(obj)); | |
| } | |
| 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(buffer)) | |
| global_stats.try_emplace("transcript", | |
| std::move(json_transcript.get())); | |
| else | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Target), json_transcript.takeError(), | |
| "failed to parse transcript JSON: {0}"); | |
| } | |
| } | |
| if (include_plugins) { | |
| global_stats.try_emplace("plugins", PluginManager::GetJSON()); | |
| } | |
| return std::move(global_stats); | |
| } | |
| llvm::json::Value SummaryStatistics::ToJSON() const { | |
| return json::Object{{ | |
| {"name", GetName()}, | |
| {"type", GetSummaryKindName()}, | |
| {"count", GetSummaryCount()}, | |
| {"totalTime", GetTotalTime()}, | |
| }}; | |
| } | |
| json::Value SummaryStatisticsCache::ToJSON() { | |
| std::lock_guard<std::mutex> guard(m_map_mutex); | |
| json::Array json_summary_stats; | |
| for (const auto &summary_stat : m_summary_stats_map) | |
| json_summary_stats.emplace_back(summary_stat.second->ToJSON()); | |
| return json_summary_stats; | |
| } | |
| void SummaryStatisticsCache::Reset() { | |
| for (const auto &summary_stat : m_summary_stats_map) | |
| summary_stat.second->Reset(); | |
| } |