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