
This relands changes in #144424 for adding a count of DWO files
parsed/loaded and the total number of DWO files.
The previous PR was reverted in #145494 due to the newly added unit
tests failing on Windows and MacOS CIs since these platforms don't
support DWO. This change add an additional
`@add_test_categories(["dwo"])` to the new tests to
[skip](cd46354dbd/lldb/packages/Python/lldbsuite/test/test_categories.py (L56)
)
these tests on Windows/MacOS.
Original PR: #144424
### Testing
Ran unit tests
```
$ bin/lldb-dotest -p TestStats.py llvm-project/lldb/test/API/commands/statistics/basic/
----------------------------------------------------------------------
Ran 24 tests in 211.391s
OK (skipped=3)
```
533 lines
22 KiB
C++
533 lines
22 KiB
C++
//===-- 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/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_file_count);
|
|
module.try_emplace("loadedDwoFileCount", loaded_dwo_file_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(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);
|
|
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;
|
|
uint32_t total_loaded_dwo_file_count = 0;
|
|
uint32_t total_dwo_file_count = 0;
|
|
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());
|
|
}
|
|
std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) =
|
|
sym_file->GetDwoFileCounts();
|
|
total_dwo_file_count += module_stat.dwo_file_count;
|
|
total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
|
|
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_loaded_dwo_file_count},
|
|
{"totalDwoFileCount", total_dwo_file_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()));
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|