LLVM 中的遥测框架¶
目标¶
在 LLVM 中提供一个通用框架,用于收集各种使用情况和性能指标。它位于 llvm/Telemetry/Telemetry.h
。
特性¶
可配置和可扩展,通过:
工具:任何想要使用遥测的工具都可以扩展和自定义它。
供应商:工具链供应商也可以提供库的自定义实现,这可以覆盖或扩展给定工具的上游实现,以最好地适应其组织的使用和隐私模型。
此类工具的最终用户也可以配置遥测(在其供应商允许的范围内)。
重要说明¶
在上游 LLVM 中没有遥测库的具体实现。我们在这里只提供抽象 API。任何想要遥测的工具都将实现一个。
这样做的理由是 LLVM 中的所有工具在他们关心的内容(检测数据的什么/哪里/何时)方面都非常不同。因此,拥有单一实现可能不切实际。但是,在未来,如果我们看到足够的共同模式,我们可以将它们提取到一个共享位置。 这待定 - 欢迎贡献。
由于隐私和安全原因,上游 LLVM 中的遥测实现不应存储任何收集的数据。
不同的组织有不同的隐私模型。
哪些数据是敏感的,哪些不是?
检测到的数据是否可以存储在任何地方?(例如本地文件?)
从 LLVM 开发人员的角度来看,数据所有权和数据收集同意很难协调。
例如,遥测收集的数据不一定由启用了遥测的 LLVM 工具的用户拥有,因此用户对数据收集的同意没有意义。另一方面,LLVM 开发人员没有合理的方式向“真正”的所有者请求同意。
高层设计¶
关键组件¶
该框架由四个重要的类组成:
llvm::telemetry::Manager
:负责收集和传输遥测数据的类。这是框架和任何想要启用遥测的工具之间交互的主要入口点。llvm::telemetry::TelemetryInfo
:数据传递者llvm::telemetry::Destination
:遥测框架向其发送数据的数据接收器。它的实现对框架是透明的。由供应商决定转发哪些数据以及将它们转发到哪里以进行最终存储。llvm::telemetry::Config
:Manager
的配置。

如何实现和与 API 交互¶
要在您的工具中使用遥测,您需要提供 Manager
类和 Destination
的具体实现。
定义自定义的
Serializer
、Manager
、Destination
以及可选的TelemetryInfo
子类
class JsonSerializer : public Serializer {
public:
json::Object *getOutputObject() { return Out.get(); }
Error init() override {
if (Started)
return createStringError("Serializer already in use");
started = true;
Out = std::make_unique<json::Object>();
return Error::success();
}
// Serialize the given value.
void write(StringRef KeyName, bool Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, int Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, long Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, long long Value ) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, unsigned int Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, unsigned long Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, unsigned long long Value) override {
writeHelper(KeyName, Value);
}
void write(StringRef KeyName, StringRef Value) override {
writeHelper(KeyName, Value);
}
void beginObject(StringRef KeyName) override {
Children.push_back(json::Object());
ChildrenNames.push_back(KeyName.str());
}
void endObject() override {
assert(!Children.empty() && !ChildrenNames.empty());
json::Value Val = json::Value(std::move(Children.back()));
std::string Name = ChildrenNames.back();
Children.pop_back();
ChildrenNames.pop_back();
writeHelper(Name, std::move(Val));
}
Error finalize() override {
if (!Started)
return createStringError("Serializer not currently in use");
Started = false;
return Error::success();
}
private:
template <typename T> void writeHelper(StringRef Name, T Value) {
assert(Started && "serializer not started");
if (Children.empty())
Out->try_emplace(Name, Value);
else
Children.back().try_emplace(Name, Value);
}
bool Started = false;
std::unique_ptr<json::Object> Out;
std::vector<json::Object> Children;
std::vector<std::string> ChildrenNames;
};
class MyManager : public telemery::Manager {
public:
static std::unique_ptr<MyManager> createInstatnce(telemetry::Config *Config) {
// If Telemetry is not enabled, then just return null;
if (!Config->EnableTelemetry)
return nullptr;
return std::make_unique<MyManager>();
}
MyManager() = default;
Error preDispatch(TelemetryInfo *Entry) override {
Entry->SessionId = SessionId;
return Error::success();
}
// You can also define additional instrumentation points.
void logStartup(TelemetryInfo *Entry) {
// Add some additional data to entry.
Entry->Msg = "Some message";
dispatch(Entry);
}
void logAdditionalPoint(TelemetryInfo *Entry) {
// .... code here
}
private:
const std::string SessionId;
};
class MyDestination : public telemetry::Destination {
public:
Error receiveEntry(const TelemetryInfo *Entry) override {
if (Error Err = Serializer.init())
return Err;
Entry->serialize(Serializer);
if (Error Err = Serializer.finalize())
return Err;
json::Object Copied = *Serializer.getOutputObject();
// Send the `Copied` object to wherever.
return Error::success();
}
private:
JsonSerializer Serializer;
};
// This defines a custom TelemetryInfo that has an additional Msg field.
struct MyTelemetryInfo : public telemetry::TelemetryInfo {
std::string Msg;
Error serialize(Serializer &Serializer) const override {
TelemetryInfo::serialize(serializer);
Serializer.writeString("MyMsg", Msg);
}
// Note: implement getKind() and classof() to support dyn_cast operations.
};
在您的工具中使用该库。
记录工具初始化过程
// In tool's initialization code.
auto StartTime = std::chrono::time_point<std::chrono::steady_clock>::now();
telemetry::Config MyConfig = makeConfig(); // Build up the appropriate Config struct here.
auto Manager = MyManager::createInstance(&MyConfig);
// Any other tool's init code can go here.
// ...
// Finally, take a snapshot of the time now so we know how long it took the
// init process to finish.
auto EndTime = std::chrono::time_point<std::chrono::steady_clock>::now();
MyTelemetryInfo Entry;
Entry.Start = StartTime;
Entry.End = EndTime;
Manager->logStartup(&Entry);
类似的代码可以用于记录工具的退出。