1 //===-- Telemetry.h -------------------------------------------------------===// 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_CORE_TELEMETRY_H 10 #define LLDB_CORE_TELEMETRY_H 11 12 #include "lldb/Core/StructuredDataImpl.h" 13 #include "lldb/Interpreter/CommandReturnObject.h" 14 #include "lldb/Utility/LLDBLog.h" 15 #include "lldb/Utility/StructuredData.h" 16 #include "lldb/Utility/UUID.h" 17 #include "lldb/lldb-forward.h" 18 #include "llvm/ADT/FunctionExtras.h" 19 #include "llvm/ADT/StringExtras.h" 20 #include "llvm/ADT/StringRef.h" 21 #include "llvm/Support/JSON.h" 22 #include "llvm/Telemetry/Telemetry.h" 23 #include <atomic> 24 #include <chrono> 25 #include <ctime> 26 #include <functional> 27 #include <memory> 28 #include <optional> 29 #include <string> 30 #include <type_traits> 31 #include <utility> 32 33 namespace lldb_private { 34 namespace telemetry { 35 36 struct LLDBConfig : public ::llvm::telemetry::Config { 37 // If true, we will collect full details about a debug command (eg., args and 38 // original command). Note: This may contain PII, hence can only be enabled by 39 // the vendor while creating the Manager. 40 const bool detailed_command_telemetry; 41 42 // If true, we will collect telemetry from LLDB's clients (eg., lldb-dap) via 43 // the SB interface. Must also be enabled by the vendor while creating the 44 // manager. 45 const bool enable_client_telemetry; 46 LLDBConfigLLDBConfig47 explicit LLDBConfig(bool enable_telemetry, bool detailed_command_telemetry, 48 bool enable_client_telemetry) 49 : ::llvm::telemetry::Config(enable_telemetry), 50 detailed_command_telemetry(detailed_command_telemetry), 51 enable_client_telemetry(enable_client_telemetry) {} 52 }; 53 54 // We expect each (direct) subclass of LLDBTelemetryInfo to 55 // have an LLDBEntryKind in the form 0b11xxxxxxxx 56 // Specifically: 57 // - Length: 8 bits 58 // - First two bits (MSB) must be 11 - the common prefix 59 // - Last two bits (LSB) are reserved for grand-children of LLDBTelemetryInfo 60 // If any of the subclass has descendents, those descendents 61 // must have their LLDBEntryKind in the similar form (ie., share common prefix 62 // and differ by the last two bits) 63 struct LLDBEntryKind : public ::llvm::telemetry::EntryKind { 64 // clang-format off 65 static const llvm::telemetry::KindType BaseInfo = 0b11000000; 66 static const llvm::telemetry::KindType ClientInfo = 0b11100000; 67 static const llvm::telemetry::KindType CommandInfo = 0b11010000; 68 static const llvm::telemetry::KindType DebuggerInfo = 0b11001000; 69 static const llvm::telemetry::KindType ExecModuleInfo = 0b11000100; 70 static const llvm::telemetry::KindType ProcessExitInfo = 0b11001100; 71 // clang-format on 72 }; 73 74 /// Defines a convenient type for timestamp of various events. 75 using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock, 76 std::chrono::nanoseconds>; 77 struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo { 78 /// Start time of an event 79 SteadyTimePoint start_time; 80 /// End time of an event - may be empty if not meaningful. 81 std::optional<SteadyTimePoint> end_time; 82 // TBD: could add some memory stats here too? 83 84 lldb::user_id_t debugger_id = LLDB_INVALID_UID; 85 Debugger *debugger = nullptr; 86 87 // For dyn_cast, isa, etc operations. getKindLLDBBaseTelemetryInfo88 llvm::telemetry::KindType getKind() const override { 89 return LLDBEntryKind::BaseInfo; 90 } 91 classofLLDBBaseTelemetryInfo92 static bool classof(const llvm::telemetry::TelemetryInfo *t) { 93 // Subclasses of this is also acceptable. 94 return (t->getKind() & LLDBEntryKind::BaseInfo) == LLDBEntryKind::BaseInfo; 95 } 96 97 void serialize(llvm::telemetry::Serializer &serializer) const override; 98 }; 99 100 struct ClientInfo : public LLDBBaseTelemetryInfo { 101 std::string client_name; 102 std::string client_data; 103 std::optional<std::string> error_msg; 104 105 // For dyn_cast, isa, etc operations. getKindClientInfo106 llvm::telemetry::KindType getKind() const override { 107 return LLDBEntryKind::ClientInfo; 108 } 109 classofClientInfo110 static bool classof(const llvm::telemetry::TelemetryInfo *t) { 111 // Subclasses of this is also acceptable. 112 return (t->getKind() & LLDBEntryKind::ClientInfo) == 113 LLDBEntryKind::ClientInfo; 114 } 115 116 void serialize(llvm::telemetry::Serializer &serializer) const override; 117 }; 118 119 struct CommandInfo : public LLDBBaseTelemetryInfo { 120 /// If the command is/can be associated with a target entry this field 121 /// contains that target's UUID. <EMPTY> otherwise. 122 UUID target_uuid; 123 /// A unique ID for a command so the manager can match the start entry with 124 /// its end entry. These values only need to be unique within the same 125 /// session. Necessary because we'd send off an entry right before a command's 126 /// execution and another right after. This is to avoid losing telemetry if 127 /// the command does not execute successfully. 128 uint64_t command_id = 0; 129 /// The command name(eg., "breakpoint set") 130 std::string command_name; 131 /// These two fields are not collected by default due to PII risks. 132 /// Vendor may allow them by setting the 133 /// LLDBConfig::detailed_command_telemetry. 134 /// @{ 135 std::optional<std::string> original_command; 136 std::optional<std::string> args; 137 /// @} 138 /// Return status of a command and any error description in case of error. 139 std::optional<lldb::ReturnStatus> ret_status; 140 std::optional<std::string> error_data; 141 142 CommandInfo() = default; 143 getKindCommandInfo144 llvm::telemetry::KindType getKind() const override { 145 return LLDBEntryKind::CommandInfo; 146 } 147 classofCommandInfo148 static bool classof(const llvm::telemetry::TelemetryInfo *T) { 149 return (T->getKind() & LLDBEntryKind::CommandInfo) == 150 LLDBEntryKind::CommandInfo; 151 } 152 153 void serialize(llvm::telemetry::Serializer &serializer) const override; 154 155 static uint64_t GetNextID(); 156 157 private: 158 // We assign each command (in the same session) a unique id so that their 159 // "start" and "end" entries can be matched up. 160 // These values don't need to be unique across runs (because they are 161 // secondary-key), hence a simple counter is sufficent. 162 static std::atomic<uint64_t> g_command_id_seed; 163 }; 164 165 struct DebuggerInfo : public LLDBBaseTelemetryInfo { 166 std::string lldb_version; 167 168 bool is_exit_entry = false; 169 170 DebuggerInfo() = default; 171 getKindDebuggerInfo172 llvm::telemetry::KindType getKind() const override { 173 return LLDBEntryKind::DebuggerInfo; 174 } 175 classofDebuggerInfo176 static bool classof(const llvm::telemetry::TelemetryInfo *T) { 177 // Subclasses of this is also acceptable 178 return (T->getKind() & LLDBEntryKind::DebuggerInfo) == 179 LLDBEntryKind::DebuggerInfo; 180 } 181 182 void serialize(llvm::telemetry::Serializer &serializer) const override; 183 }; 184 185 struct ExecutableModuleInfo : public LLDBBaseTelemetryInfo { 186 lldb::ModuleSP exec_mod; 187 /// The same as the executable-module's UUID. 188 UUID uuid; 189 /// PID of the process owned by this target. 190 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 191 /// The triple of this executable module. 192 std::string triple; 193 194 /// If true, this entry was emitted at the beginning of an event (eg., before 195 /// the executable is set). Otherwise, it was emitted at the end of an 196 /// event (eg., after the module and any dependency were loaded.) 197 bool is_start_entry = false; 198 199 ExecutableModuleInfo() = default; 200 getKindExecutableModuleInfo201 llvm::telemetry::KindType getKind() const override { 202 return LLDBEntryKind::ExecModuleInfo; 203 } 204 classofExecutableModuleInfo205 static bool classof(const TelemetryInfo *T) { 206 // Subclasses of this is also acceptable 207 return (T->getKind() & LLDBEntryKind::ExecModuleInfo) == 208 LLDBEntryKind::ExecModuleInfo; 209 } 210 void serialize(llvm::telemetry::Serializer &serializer) const override; 211 }; 212 213 /// Describes an exit status. 214 struct ExitDescription { 215 int exit_code; 216 std::string description; 217 }; 218 219 struct ProcessExitInfo : public LLDBBaseTelemetryInfo { 220 // The executable-module's UUID. 221 UUID module_uuid; 222 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 223 bool is_start_entry = false; 224 std::optional<ExitDescription> exit_desc; 225 getKindProcessExitInfo226 llvm::telemetry::KindType getKind() const override { 227 return LLDBEntryKind::ProcessExitInfo; 228 } 229 classofProcessExitInfo230 static bool classof(const TelemetryInfo *T) { 231 // Subclasses of this is also acceptable 232 return (T->getKind() & LLDBEntryKind::ProcessExitInfo) == 233 LLDBEntryKind::ProcessExitInfo; 234 } 235 void serialize(llvm::telemetry::Serializer &serializer) const override; 236 }; 237 238 /// The base Telemetry manager instance in LLDB. 239 /// This class declares additional instrumentation points 240 /// applicable to LLDB. 241 class TelemetryManager : public llvm::telemetry::Manager { 242 public: 243 llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override; 244 GetConfig()245 const LLDBConfig *GetConfig() { return m_config.get(); } 246 247 virtual void 248 DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry, 249 Debugger *debugger); 250 virtual llvm::StringRef GetInstanceName() const = 0; 251 252 static TelemetryManager *GetInstance(); 253 254 protected: 255 TelemetryManager(std::unique_ptr<LLDBConfig> config); 256 GetSessionId()257 inline const std::string &GetSessionId() const { return m_id; } 258 259 static void SetInstance(std::unique_ptr<TelemetryManager> manger); 260 261 private: 262 std::unique_ptr<LLDBConfig> m_config; 263 // Each instance of a TelemetryManager is assigned a unique ID. 264 const std::string m_id; 265 static std::unique_ptr<TelemetryManager> g_instance; 266 }; 267 268 /// Helper RAII class for collecting telemetry. 269 template <typename Info> struct ScopedDispatcher { 270 // The debugger pointer is optional because we may not have a debugger yet. 271 // In that case, caller must set the debugger later. 272 ScopedDispatcher(Debugger *debugger = nullptr) { 273 // Start the timer. 274 m_start_time = std::chrono::steady_clock::now(); 275 this->debugger = debugger; 276 } 277 ScopedDispatcher(llvm::unique_function<void(Info *info)> final_callback, 278 Debugger *debugger = nullptr) m_final_callbackScopedDispatcher279 : m_final_callback(std::move(final_callback)) { 280 // Start the timer. 281 m_start_time = std::chrono::steady_clock::now(); 282 this->debugger = debugger; 283 } 284 SetDebuggerScopedDispatcher285 void SetDebugger(Debugger *debugger) { this->debugger = debugger; } 286 DispatchOnExitScopedDispatcher287 void DispatchOnExit(llvm::unique_function<void(Info *info)> final_callback) { 288 // We probably should not be overriding previously set cb. 289 assert(!m_final_callback); 290 m_final_callback = std::move(final_callback); 291 } 292 DispatchNowScopedDispatcher293 void DispatchNow(llvm::unique_function<void(Info *info)> populate_fields_cb) { 294 TelemetryManager *manager = TelemetryManager::GetInstance(); 295 if (!manager->GetConfig()->EnableTelemetry) 296 return; 297 Info info; 298 // Populate the common fields we know about. 299 info.start_time = m_start_time; 300 info.end_time = std::chrono::steady_clock::now(); 301 info.debugger = debugger; 302 // The callback will set the rest. 303 populate_fields_cb(&info); 304 // And then we dispatch. 305 if (llvm::Error er = manager->dispatch(&info)) { 306 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er), 307 "Failed to dispatch entry of type {1}: {0}", 308 info.getKind()); 309 } 310 } 311 ~ScopedDispatcherScopedDispatcher312 ~ScopedDispatcher() { 313 if (m_final_callback) 314 DispatchNow(std::move(m_final_callback)); 315 } 316 317 private: 318 SteadyTimePoint m_start_time; 319 llvm::unique_function<void(Info *info)> m_final_callback; 320 Debugger *debugger; 321 }; 322 323 } // namespace telemetry 324 } // namespace lldb_private 325 #endif // LLDB_CORE_TELEMETRY_H 326