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