xref: /freebsd/contrib/llvm-project/lldb/include/lldb/Core/Telemetry.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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