This patch adds breakpoints to each target's statistics so we can track how long it takes to resolve each breakpoint. It also includes the structured data for each breakpoint so the exact breakpoint details are logged to allow for reproduction of slow resolving breakpoints. Each target gets a new "breakpoints" array that contains breakpoint details. Each breakpoint has "details" which is the JSON representation of a serialized breakpoint resolver and filter, "id" which is the breakpoint ID, and "resolveTime" which is the time in seconds it took to resolve the breakpoint. A snippet of the new data is shown here:
"targets": [
{
"breakpoints": [
{
"details": {...},
"id": 1,
"resolveTime": 0.00039291599999999999
},
{
"details": {...},
"id": 2,
"resolveTime": 0.00022679199999999999
}
],
"totalBreakpointResolveTime": 0.00061970799999999996
}
]
This provides full details on exactly how breakpoints were set and how long it took to resolve them.
Differential Revision: https://reviews.llvm.org/D112587
186 lines
7.2 KiB
C++
186 lines
7.2 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/Symbol/SymbolFile.h"
|
|
#include "lldb/Target/Target.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 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("debugInfoParseTime", debug_parse_time);
|
|
module.try_emplace("debugInfoIndexTime", debug_index_time);
|
|
module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size);
|
|
return module;
|
|
}
|
|
|
|
json::Value TargetStats::ToJSON(Target &target) {
|
|
CollectStats(target);
|
|
|
|
json::Array json_module_uuid_array;
|
|
for (auto module_identifier : m_module_identifiers)
|
|
json_module_uuid_array.emplace_back(module_identifier);
|
|
|
|
json::Object target_metrics_json{
|
|
{m_expr_eval.name, m_expr_eval.ToJSON()},
|
|
{m_frame_var.name, m_frame_var.ToJSON()},
|
|
{"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.count());
|
|
|
|
json::Array breakpoints_array;
|
|
double totalBreakpointResolveTime = 0.0;
|
|
// Rport 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);
|
|
|
|
return target_metrics_json;
|
|
}
|
|
|
|
void TargetStats::SetLaunchOrAttachTime() {
|
|
m_launch_or_attach_time = StatsClock::now();
|
|
m_first_private_stop_time = llvm::None;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool DebuggerStats::g_collecting_stats = false;
|
|
|
|
llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
|
|
Target *target) {
|
|
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;
|
|
uint64_t debug_info_size = 0;
|
|
if (target) {
|
|
json_targets.emplace_back(target->ReportStatistics());
|
|
} else {
|
|
for (const auto &target : debugger.GetTargetList().Targets())
|
|
json_targets.emplace_back(target->ReportStatistics());
|
|
}
|
|
std::vector<ModuleStats> modules;
|
|
std::lock_guard<std::recursive_mutex> guard(
|
|
Module::GetAllocationModuleCollectionMutex());
|
|
const size_t num_modules = Module::GetNumberAllocatedModules();
|
|
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
|
|
Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
|
|
ModuleStats module_stat;
|
|
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();
|
|
module_stat.symtab_parse_time = module->GetSymtabParseTime().count();
|
|
module_stat.symtab_index_time = module->GetSymtabIndexTime().count();
|
|
SymbolFile *sym_file = module->GetSymbolFile();
|
|
if (sym_file) {
|
|
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();
|
|
}
|
|
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;
|
|
json_modules.emplace_back(module_stat.ToJSON());
|
|
}
|
|
|
|
json::Object global_stats{
|
|
{"targets", std::move(json_targets)},
|
|
{"modules", std::move(json_modules)},
|
|
{"totalSymbolTableParseTime", symtab_parse_time},
|
|
{"totalSymbolTableIndexTime", symtab_index_time},
|
|
{"totalDebugInfoParseTime", debug_parse_time},
|
|
{"totalDebugInfoIndexTime", debug_index_time},
|
|
{"totalDebugInfoByteSize", debug_info_size},
|
|
};
|
|
return std::move(global_stats);
|
|
}
|