Skip to content

Commit 5a992af

Browse files
committed
[LLDB][Telemetry]Defind telemetry::CommandInfo and collect telemetry about a command's execution.
1 parent 823a597 commit 5a992af

File tree

3 files changed

+158
-3
lines changed

3 files changed

+158
-3
lines changed

lldb/include/lldb/Core/Telemetry.h

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
#include "lldb/Core/StructuredDataImpl.h"
1313
#include "lldb/Interpreter/CommandReturnObject.h"
1414
#include "lldb/Utility/StructuredData.h"
15+
#include "lldb/Utility/LLDBLog.h"
1516
#include "lldb/lldb-forward.h"
1617
#include "llvm/ADT/StringExtras.h"
1718
#include "llvm/ADT/StringRef.h"
19+
#include "llvm/ADT/FunctionExtras.h"
1820
#include "llvm/Support/JSON.h"
1921
#include "llvm/Telemetry/Telemetry.h"
22+
#include <atomic>
2023
#include <chrono>
2124
#include <ctime>
2225
#include <memory>
@@ -27,8 +30,16 @@
2730
namespace lldb_private {
2831
namespace telemetry {
2932

33+
struct LLDBConfig : public ::llvm::telemetry::Config {
34+
const bool m_collect_original_command;
35+
36+
explicit LLDBConfig(bool enable_telemetry, bool collect_original_command)
37+
: ::llvm::telemetry::Config(enable_telemetry), m_collect_original_command(collect_original_command) {}
38+
};
39+
3040
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
31-
static const llvm::telemetry::KindType BaseInfo = 0b11000;
41+
static const llvm::telemetry::KindType BaseInfo = 0b11000000;
42+
static const llvm::telemetry::KindType CommandInfo = 0b11010000;
3243
};
3344

3445
/// Defines a convenient type for timestamp of various events.
@@ -41,6 +52,7 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
4152
std::optional<SteadyTimePoint> end_time;
4253
// TBD: could add some memory stats here too?
4354

55+
lldb::user_id_t debugger_id = LLDB_INVALID_UID;
4456
Debugger *debugger;
4557

4658
// For dyn_cast, isa, etc operations.
@@ -56,26 +68,135 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
5668
void serialize(llvm::telemetry::Serializer &serializer) const override;
5769
};
5870

71+
72+
struct CommandInfo : public LLDBBaseTelemetryInfo {
73+
74+
// If the command is/can be associated with a target entry this field contains
75+
// that target's UUID. <EMPTY> otherwise.
76+
std::string target_uuid;
77+
// A unique ID for a command so the manager can match the start entry with
78+
// its end entry. These values only need to be unique within the same session.
79+
// Necessary because we'd send off an entry right before a command's execution
80+
// and another right after. This is to avoid losing telemetry if the command
81+
// does not execute successfully.
82+
int command_id;
83+
84+
// Eg., "breakpoint set"
85+
std::string command_name;
86+
87+
// !!NOTE!! These two fields are not collected (upstream) due to PII risks.
88+
// (Downstream impl may add them if needed).
89+
// std::string original_command;
90+
// std::string args;
91+
92+
lldb::ReturnStatus ret_status;
93+
std::string error_data;
94+
95+
96+
CommandInfo() = default;
97+
98+
llvm::telemetry::KindType getKind() const override { return LLDBEntryKind::CommandInfo; }
99+
100+
static bool classof(const llvm::telemetry::TelemetryInfo *T) {
101+
return (T->getKind() & LLDBEntryKind::CommandInfo) == LLDBEntryKind::CommandInfo;
102+
}
103+
104+
void serialize(Serializer &serializer) const override;
105+
};
106+
59107
/// The base Telemetry manager instance in LLDB.
60108
/// This class declares additional instrumentation points
61109
/// applicable to LLDB.
62110
class TelemetryManager : public llvm::telemetry::Manager {
63111
public:
64112
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;
65113

114+
int MakeNextCommandId();
115+
116+
LLDBConfig* GetConfig() { return m_config.get(); }
117+
66118
virtual llvm::StringRef GetInstanceName() const = 0;
67119
static TelemetryManager *getInstance();
68120

69121
protected:
70-
TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config);
122+
TelemetryManager(std::unique_ptr<LLDBConfig> config);
71123

72124
static void setInstance(std::unique_ptr<TelemetryManager> manger);
73125

74126
private:
75-
std::unique_ptr<llvm::telemetry::Config> m_config;
127+
std::unique_ptr<LLDBConfig> m_config;
128+
const std::string m_id;
129+
// We assign each command (in the same session) a unique id so that their
130+
// "start" and "end" entries can be matched up.
131+
// These values don't need to be unique across runs (because they are
132+
// secondary-key), hence a simple counter is sufficent.
133+
std::atomic<int> command_id_seed = 0;
76134
static std::unique_ptr<TelemetryManager> g_instance;
77135
};
78136

137+
/// Helper RAII class for collecting telemetry.
138+
template <typename Info> struct ScopedDispatcher {
139+
// The debugger pointer is optional because we may not have a debugger yet.
140+
// In that case, caller must set the debugger later.
141+
ScopedDispatcher(Debugger *debugger = nullptr) {
142+
// Start the timer.
143+
m_start_time = std::chrono::steady_clock::now();
144+
debugger = debugger;
145+
}
146+
ScopedDispatcher(llvm::unique_function<void(Info *info)> final_callback,
147+
Debugger *debugger = nullptr)
148+
: m_final_callback(std::move(final_callback)) {
149+
// Start the timer.
150+
m_start_time = std::chrono::steady_clock::now();
151+
debugger = debugger;
152+
}
153+
154+
155+
template typename<T>
156+
T GetIfEnable(llvm::unique_function<T(TelemetryManager*)> callable,
157+
T default_value) {
158+
TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
159+
if (!manager)
160+
return default_value;
161+
return callable(manager);
162+
}
163+
164+
void SetDebugger(Debugger *debugger) { debugger = debugger; }
165+
166+
void SetFinalCallback(llvm::unique_function<void(Info *info)> final_callback) {
167+
m_final_callback(std::move(final_callback));
168+
}
169+
170+
void DispatchIfEnable(llvm::unique_function<void(Info *info)> populate_fields_cb) {
171+
TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
172+
if (!manager)
173+
return;
174+
Info info;
175+
// Populate the common fields we know aboutl
176+
info.start_time = m_start_time;
177+
info.end_time = std::chrono::steady_clock::now();
178+
info.debugger = debugger;
179+
// The callback will set the rest.
180+
populate_fields_cb(&info);
181+
// And then we dispatch.
182+
if (llvm::Error er = manager->dispatch(&info)) {
183+
LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
184+
"Failed to dispatch entry of type: {0}", m_info.getKind());
185+
}
186+
187+
}
188+
189+
~ScopedDispatcher() {
190+
// TODO: check if there's a cb to call?
191+
DispatchIfEnable(std::move(m_final_callback));
192+
}
193+
194+
private:
195+
SteadyTimePoint m_start_time;
196+
llvm::unique_function<void(Info *info)> m_final_callback;
197+
Debugger * debugger;
198+
};
199+
79200
} // namespace telemetry
80201
} // namespace lldb_private
81202
#endif // LLDB_CORE_TELEMETRY_H

lldb/source/Core/Telemetry.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
4343
serializer.write("end_time", ToNanosec(end_time.value()));
4444
}
4545

46+
void CommandInfo::serialize(Serializer &serializer) const {
47+
LLDBBaseTelemetryInfo::serializer(serializer);
48+
49+
serializer.write("target_uuid", target_uuid);
50+
serializer.write("command_id", command_id);
51+
serializer.write("command_name", command_name);
52+
serializer.write("ret_status", ret_status);
53+
serializer.write("error_data", error_data);
54+
}
55+
4656
[[maybe_unused]] static std::string MakeUUID(Debugger *debugger) {
4757
uint8_t random_bytes[16];
4858
if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
@@ -66,6 +76,10 @@ llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
6676
return llvm::Error::success();
6777
}
6878

79+
int TelemetryManager::MakeNextCommandId() {
80+
81+
}
82+
6983
std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
7084
TelemetryManager *TelemetryManager::getInstance() { return g_instance.get(); }
7185

lldb/source/Interpreter/CommandInterpreter.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
#include "lldb/Core/Debugger.h"
4949
#include "lldb/Core/PluginManager.h"
50+
#include "lldb/Core/Telemetry.h"
5051
#include "lldb/Host/StreamFile.h"
5152
#include "lldb/Utility/ErrorMessages.h"
5253
#include "lldb/Utility/LLDBLog.h"
@@ -88,6 +89,7 @@
8889
#include "llvm/Support/Path.h"
8990
#include "llvm/Support/PrettyStackTrace.h"
9091
#include "llvm/Support/ScopedPrinter.h"
92+
#include "llvm/Telemetry/Telemetry.h"
9193

9294
#if defined(__APPLE__)
9395
#include <TargetConditionals.h>
@@ -1883,10 +1885,28 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
18831885
LazyBool lazy_add_to_history,
18841886
CommandReturnObject &result,
18851887
bool force_repeat_command) {
1888+
lldb_private::telemetry::ScopedDispatcher<
1889+
lldb_private::telemetry:CommandInfo> helper;
1890+
const int command_id = helper.GetIfEnable<int>([](lldb_private::telemetry::TelemetryManager* ins){
1891+
return ins->MakeNextCommandId(); }, 0);
1892+
18861893
std::string command_string(command_line);
18871894
std::string original_command_string(command_string);
18881895
std::string real_original_command_string(command_string);
18891896

1897+
helper.DispatchIfEnable([&](lldb_private::telemetry:CommandInfo* info,
1898+
lldb_private::telemetry::TelemetryManager* ins){
1899+
info.command_id = command_id;
1900+
if (Target* target = GetExecutionContext().GetTargetPtr()) {
1901+
// If we have a target attached to this command, then get the UUID.
1902+
info.target_uuid = target->GetExecutableModule() != nullptr
1903+
? GetExecutableModule()->GetUUID().GetAsString()
1904+
: "";
1905+
}
1906+
if (ins->GetConfig()->m_collect_original_command)
1907+
info.original_command = original_command_string;
1908+
});
1909+
18901910
Log *log = GetLog(LLDBLog::Commands);
18911911
LLDB_LOGF(log, "Processing command: %s", command_line);
18921912
LLDB_SCOPED_TIMERF("Processing command: %s.", command_line);

0 commit comments

Comments
 (0)