• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Statistics.h --------------------------------------------*- C++ -*-===//
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 #ifndef LLDB_TARGET_STATISTICS_H
10 #define LLDB_TARGET_STATISTICS_H
11 
12 #include "lldb/Utility/ConstString.h"
13 #include "lldb/Utility/RealpathPrefixes.h"
14 #include "lldb/Utility/Stream.h"
15 #include "lldb/lldb-forward.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/Support/JSON.h"
18 #include <atomic>
19 #include <chrono>
20 #include <optional>
21 #include <ratio>
22 #include <string>
23 #include <vector>
24 
25 namespace lldb_private {
26 
27 using StatsClock = std::chrono::high_resolution_clock;
28 using StatsTimepoint = std::chrono::time_point<StatsClock>;
29 
30 class StatsDuration {
31 public:
32   using Duration = std::chrono::duration<double>;
33 
get()34   Duration get() const {
35     return Duration(InternalDuration(value.load(std::memory_order_relaxed)));
36   }
Duration()37   operator Duration() const { return get(); }
38 
39   StatsDuration &operator+=(Duration dur) {
40     value.fetch_add(std::chrono::duration_cast<InternalDuration>(dur).count(),
41                     std::memory_order_relaxed);
42     return *this;
43   }
44 
45 private:
46   using InternalDuration = std::chrono::duration<uint64_t, std::micro>;
47   std::atomic<uint64_t> value{0};
48 };
49 
50 /// A class that measures elapsed time in an exception safe way.
51 ///
52 /// This is a RAII class is designed to help gather timing statistics within
53 /// LLDB where objects have optional Duration variables that get updated with
54 /// elapsed times. This helps LLDB measure statistics for many things that are
55 /// then reported in LLDB commands.
56 ///
57 /// Objects that need to measure elapsed times should have a variable of type
58 /// "StatsDuration m_time_xxx;" which can then be used in the constructor of
59 /// this class inside a scope that wants to measure something:
60 ///
61 ///   ElapsedTime elapsed(m_time_xxx);
62 ///   // Do some work
63 ///
64 /// This class will increment the m_time_xxx variable with the elapsed time
65 /// when the object goes out of scope. The "m_time_xxx" variable will be
66 /// incremented when the class goes out of scope. This allows a variable to
67 /// measure something that might happen in stages at different times, like
68 /// resolving a breakpoint each time a new shared library is loaded.
69 class ElapsedTime {
70 public:
71   /// Set to the start time when the object is created.
72   StatsTimepoint m_start_time;
73   /// Elapsed time in seconds to increment when this object goes out of scope.
74   StatsDuration &m_elapsed_time;
75 
76 public:
ElapsedTime(StatsDuration & opt_time)77   ElapsedTime(StatsDuration &opt_time) : m_elapsed_time(opt_time) {
78     m_start_time = StatsClock::now();
79   }
~ElapsedTime()80   ~ElapsedTime() {
81     StatsClock::duration elapsed = StatsClock::now() - m_start_time;
82     m_elapsed_time += elapsed;
83   }
84 };
85 
86 /// A class to count success/fail statistics.
87 struct StatsSuccessFail {
StatsSuccessFailStatsSuccessFail88   StatsSuccessFail(llvm::StringRef n) : name(n.str()) {}
89 
NotifySuccessStatsSuccessFail90   void NotifySuccess() { ++successes; }
NotifyFailureStatsSuccessFail91   void NotifyFailure() { ++failures; }
92 
93   llvm::json::Value ToJSON() const;
94   std::string name;
95   uint32_t successes = 0;
96   uint32_t failures = 0;
97 };
98 
99 /// A class that represents statistics for a since lldb_private::Module.
100 struct ModuleStats {
101   llvm::json::Value ToJSON() const;
102   intptr_t identifier;
103   std::string path;
104   std::string uuid;
105   std::string triple;
106   // Path separate debug info file, or empty if none.
107   std::string symfile_path;
108   // If the debug info is contained in multiple files where each one is
109   // represented as a separate lldb_private::Module, then these are the
110   // identifiers of these modules in the global module list. This allows us to
111   // track down all of the stats that contribute to this module.
112   std::vector<intptr_t> symfile_modules;
113   llvm::StringMap<llvm::json::Value> type_system_stats;
114   double symtab_parse_time = 0.0;
115   double symtab_index_time = 0.0;
116   double debug_parse_time = 0.0;
117   double debug_index_time = 0.0;
118   uint64_t debug_info_size = 0;
119   bool symtab_loaded_from_cache = false;
120   bool symtab_saved_to_cache = false;
121   bool debug_info_index_loaded_from_cache = false;
122   bool debug_info_index_saved_to_cache = false;
123   bool debug_info_enabled = true;
124   bool symtab_stripped = false;
125   bool debug_info_had_variable_errors = false;
126   bool debug_info_had_incomplete_types = false;
127 };
128 
129 struct ConstStringStats {
130   llvm::json::Value ToJSON() const;
131   ConstString::MemoryStats stats = ConstString::GetMemoryStats();
132 };
133 
134 struct StatisticsOptions {
135 public:
SetSummaryOnlyStatisticsOptions136   void SetSummaryOnly(bool value) { m_summary_only = value; }
GetSummaryOnlyStatisticsOptions137   bool GetSummaryOnly() const { return m_summary_only.value_or(false); }
138 
SetLoadAllDebugInfoStatisticsOptions139   void SetLoadAllDebugInfo(bool value) { m_load_all_debug_info = value; }
GetLoadAllDebugInfoStatisticsOptions140   bool GetLoadAllDebugInfo() const {
141     return m_load_all_debug_info.value_or(false);
142   }
143 
SetIncludeTargetsStatisticsOptions144   void SetIncludeTargets(bool value) { m_include_targets = value; }
GetIncludeTargetsStatisticsOptions145   bool GetIncludeTargets() const {
146     if (m_include_targets.has_value())
147       return m_include_targets.value();
148     // Default to true in both default mode and summary mode.
149     return true;
150   }
151 
SetIncludeModulesStatisticsOptions152   void SetIncludeModules(bool value) { m_include_modules = value; }
GetIncludeModulesStatisticsOptions153   bool GetIncludeModules() const {
154     if (m_include_modules.has_value())
155       return m_include_modules.value();
156     // `m_include_modules` has no value set, so return a value based on
157     // `m_summary_only`.
158     return !GetSummaryOnly();
159   }
160 
SetIncludeTranscriptStatisticsOptions161   void SetIncludeTranscript(bool value) { m_include_transcript = value; }
GetIncludeTranscriptStatisticsOptions162   bool GetIncludeTranscript() const {
163     if (m_include_transcript.has_value())
164       return m_include_transcript.value();
165     // `m_include_transcript` has no value set, so return a value based on
166     // `m_summary_only`.
167     return !GetSummaryOnly();
168   }
169 
170 private:
171   std::optional<bool> m_summary_only;
172   std::optional<bool> m_load_all_debug_info;
173   std::optional<bool> m_include_targets;
174   std::optional<bool> m_include_modules;
175   std::optional<bool> m_include_transcript;
176 };
177 
178 /// A class that represents statistics for a since lldb_private::Target.
179 class TargetStats {
180 public:
181   llvm::json::Value ToJSON(Target &target,
182                            const lldb_private::StatisticsOptions &options);
183 
184   void SetLaunchOrAttachTime();
185   void SetFirstPrivateStopTime();
186   void SetFirstPublicStopTime();
187   void IncreaseSourceMapDeduceCount();
188   void IncreaseSourceRealpathAttemptCount(uint32_t count);
189   void IncreaseSourceRealpathCompatibleCount(uint32_t count);
190 
GetCreateTime()191   StatsDuration &GetCreateTime() { return m_create_time; }
GetExpressionStats()192   StatsSuccessFail &GetExpressionStats() { return m_expr_eval; }
GetFrameVariableStats()193   StatsSuccessFail &GetFrameVariableStats() { return m_frame_var; }
194 
195 protected:
196   StatsDuration m_create_time;
197   std::optional<StatsTimepoint> m_launch_or_attach_time;
198   std::optional<StatsTimepoint> m_first_private_stop_time;
199   std::optional<StatsTimepoint> m_first_public_stop_time;
200   StatsSuccessFail m_expr_eval{"expressionEvaluation"};
201   StatsSuccessFail m_frame_var{"frameVariable"};
202   std::vector<intptr_t> m_module_identifiers;
203   uint32_t m_source_map_deduce_count = 0;
204   uint32_t m_source_realpath_attempt_count = 0;
205   uint32_t m_source_realpath_compatible_count = 0;
206   void CollectStats(Target &target);
207 };
208 
209 class DebuggerStats {
210 public:
SetCollectingStats(bool enable)211   static void SetCollectingStats(bool enable) { g_collecting_stats = enable; }
GetCollectingStats()212   static bool GetCollectingStats() { return g_collecting_stats; }
213 
214   /// Get metrics associated with one or all targets in a debugger in JSON
215   /// format.
216   ///
217   /// \param debugger
218   ///   The debugger to get the target list from if \a target is NULL.
219   ///
220   /// \param target
221   ///   The single target to emit statistics for if non NULL, otherwise dump
222   ///   statistics only for the specified target.
223   ///
224   /// \param summary_only
225   ///   If true, only report high level summary statistics without
226   ///   targets/modules/breakpoints etc.. details.
227   ///
228   /// \return
229   ///     Returns a JSON value that contains all target metrics.
230   static llvm::json::Value
231   ReportStatistics(Debugger &debugger, Target *target,
232                    const lldb_private::StatisticsOptions &options);
233 
234 protected:
235   // Collecting stats can be set to true to collect stats that are expensive
236   // to collect. By default all stats that are cheap to collect are enabled.
237   // This settings is here to maintain compatibility with "statistics enable"
238   // and "statistics disable".
239   static bool g_collecting_stats;
240 };
241 
242 } // namespace lldb_private
243 
244 #endif // LLDB_TARGET_STATISTICS_H
245