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/Symbol/SymbolFile.h"
14 #include "lldb/Target/Target.h"
15 
16 using namespace lldb;
17 using namespace lldb_private;
18 using namespace llvm;
19 
20 static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
21                               const std::string &str) {
22   if (str.empty())
23     return;
24   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
25     obj.try_emplace(key, str);
26   else
27     obj.try_emplace(key, llvm::json::fixUTF8(str));
28 }
29 
30 json::Value StatsSuccessFail::ToJSON() const {
31   return json::Object{{"successes", successes}, {"failures", failures}};
32 }
33 
34 static double elapsed(const StatsTimepoint &start, const StatsTimepoint &end) {
35   StatsDuration elapsed = end.time_since_epoch() - start.time_since_epoch();
36   return elapsed.count();
37 }
38 
39 void TargetStats::CollectStats(Target &target) {
40   m_module_identifiers.clear();
41   for (ModuleSP module_sp : target.GetImages().Modules())
42     m_module_identifiers.emplace_back((intptr_t)module_sp.get());
43 }
44 
45 json::Value ModuleStats::ToJSON() const {
46   json::Object module;
47   EmplaceSafeString(module, "path", path);
48   EmplaceSafeString(module, "uuid", uuid);
49   EmplaceSafeString(module, "triple", triple);
50   module.try_emplace("identifier", identifier);
51   module.try_emplace("symbolTableParseTime", symtab_parse_time);
52   module.try_emplace("symbolTableIndexTime", symtab_index_time);
53   module.try_emplace("debugInfoParseTime", debug_parse_time);
54   module.try_emplace("debugInfoIndexTime", debug_index_time);
55   module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size);
56   return module;
57 }
58 
59 json::Value TargetStats::ToJSON(Target &target) {
60   CollectStats(target);
61 
62   json::Array json_module_uuid_array;
63   for (auto module_identifier : m_module_identifiers)
64     json_module_uuid_array.emplace_back(module_identifier);
65 
66   json::Object target_metrics_json{
67       {m_expr_eval.name, m_expr_eval.ToJSON()},
68       {m_frame_var.name, m_frame_var.ToJSON()},
69       {"moduleIdentifiers", std::move(json_module_uuid_array)}};
70 
71   if (m_launch_or_attach_time && m_first_private_stop_time) {
72     double elapsed_time =
73         elapsed(*m_launch_or_attach_time, *m_first_private_stop_time);
74     target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time);
75   }
76   if (m_launch_or_attach_time && m_first_public_stop_time) {
77     double elapsed_time =
78         elapsed(*m_launch_or_attach_time, *m_first_public_stop_time);
79     target_metrics_json.try_emplace("firstStopTime", elapsed_time);
80   }
81   target_metrics_json.try_emplace("targetCreateTime", m_create_time.count());
82 
83   return target_metrics_json;
84 }
85 
86 void TargetStats::SetLaunchOrAttachTime() {
87   m_launch_or_attach_time = StatsClock::now();
88   m_first_private_stop_time = llvm::None;
89 }
90 
91 void TargetStats::SetFirstPrivateStopTime() {
92   // Launching and attaching has many paths depending on if synchronous mode
93   // was used or if we are stopping at the entry point or not. Only set the
94   // first stop time if it hasn't already been set.
95   if (!m_first_private_stop_time)
96     m_first_private_stop_time = StatsClock::now();
97 }
98 
99 void TargetStats::SetFirstPublicStopTime() {
100   // Launching and attaching has many paths depending on if synchronous mode
101   // was used or if we are stopping at the entry point or not. Only set the
102   // first stop time if it hasn't already been set.
103   if (!m_first_public_stop_time)
104     m_first_public_stop_time = StatsClock::now();
105 }
106 
107 bool DebuggerStats::g_collecting_stats = false;
108 
109 llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
110                                                   Target *target) {
111   json::Array json_targets;
112   json::Array json_modules;
113   double symtab_parse_time = 0.0;
114   double symtab_index_time = 0.0;
115   double debug_parse_time = 0.0;
116   double debug_index_time = 0.0;
117   uint64_t debug_info_size = 0;
118   if (target) {
119     json_targets.emplace_back(target->ReportStatistics());
120   } else {
121     for (const auto &target : debugger.GetTargetList().Targets())
122       json_targets.emplace_back(target->ReportStatistics());
123   }
124   std::vector<ModuleStats> modules;
125   std::lock_guard<std::recursive_mutex> guard(
126       Module::GetAllocationModuleCollectionMutex());
127   const size_t num_modules = Module::GetNumberAllocatedModules();
128   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
129     Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
130     ModuleStats module_stat;
131     module_stat.identifier = (intptr_t)module;
132     module_stat.path = module->GetFileSpec().GetPath();
133     if (ConstString object_name = module->GetObjectName()) {
134       module_stat.path.append(1, '(');
135       module_stat.path.append(object_name.GetStringRef().str());
136       module_stat.path.append(1, ')');
137     }
138     module_stat.uuid = module->GetUUID().GetAsString();
139     module_stat.triple = module->GetArchitecture().GetTriple().str();
140     module_stat.symtab_parse_time = module->GetSymtabParseTime().count();
141     module_stat.symtab_index_time = module->GetSymtabIndexTime().count();
142     SymbolFile *sym_file = module->GetSymbolFile();
143     if (sym_file) {
144       module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
145       module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
146       module_stat.debug_info_size = sym_file->GetDebugInfoSize();
147     }
148     symtab_parse_time += module_stat.symtab_parse_time;
149     symtab_index_time += module_stat.symtab_index_time;
150     debug_parse_time += module_stat.debug_parse_time;
151     debug_index_time += module_stat.debug_index_time;
152     debug_info_size += module_stat.debug_info_size;
153     json_modules.emplace_back(module_stat.ToJSON());
154   }
155 
156   json::Object global_stats{
157       {"targets", std::move(json_targets)},
158       {"modules", std::move(json_modules)},
159       {"totalSymbolTableParseTime", symtab_parse_time},
160       {"totalSymbolTableIndexTime", symtab_index_time},
161       {"totalDebugInfoParseTime", debug_parse_time},
162       {"totalDebugInfoIndexTime", debug_index_time},
163       {"totalDebugInfoByteSize", debug_info_size},
164   };
165   return std::move(global_stats);
166 }
167