[llvm] dbae717 - Reapply "[llvm]Add a simple Telemetry framework" (#120769) (#121003)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 23 14:23:46 PST 2024
Author: Vy Nguyen
Date: 2024-12-23T17:23:43-05:00
New Revision: dbae7176a6ecf558dc5e92016cdda387c9d74d66
URL: https://github.com/llvm/llvm-project/commit/dbae7176a6ecf558dc5e92016cdda387c9d74d66
DIFF: https://github.com/llvm/llvm-project/commit/dbae7176a6ecf558dc5e92016cdda387c9d74d66.diff
LOG: Reapply "[llvm]Add a simple Telemetry framework" (#120769) (#121003)
This reverts commit 2ec6174bef4bc9ef3d5cedbffd7169017c9669c3.
New changes:
- Use explicit overloads of write(<int types>)
- Fix link error due to missing dependency (lib/Support)
- Updated tests and docs
Added:
llvm/docs/Telemetry.rst
llvm/docs/llvm_telemetry_design.png
llvm/include/llvm/Telemetry/Telemetry.h
llvm/lib/Telemetry/CMakeLists.txt
llvm/lib/Telemetry/Telemetry.cpp
llvm/unittests/Telemetry/CMakeLists.txt
llvm/unittests/Telemetry/TelemetryTest.cpp
Modified:
llvm/docs/UserGuides.rst
llvm/lib/CMakeLists.txt
llvm/unittests/CMakeLists.txt
Removed:
################################################################################
diff --git a/llvm/docs/Telemetry.rst b/llvm/docs/Telemetry.rst
new file mode 100644
index 00000000000000..4f30ae82b5628f
--- /dev/null
+++ b/llvm/docs/Telemetry.rst
@@ -0,0 +1,257 @@
+===========================
+Telemetry framework in LLVM
+===========================
+
+.. contents::
+ :local:
+
+.. toctree::
+ :hidden:
+
+Objective
+=========
+
+Provides a common framework in LLVM for collecting various usage and performance
+metrics.
+It is located at ``llvm/Telemetry/Telemetry.h``.
+
+Characteristics
+---------------
+* Configurable and extensible by:
+
+ * Tools: any tool that wants to use Telemetry can extend and customize it.
+ * Vendors: Toolchain vendors can also provide custom implementation of the
+ library, which could either override or extend the given tool's upstream
+ implementation, to best fit their organization's usage and privacy models.
+ * End users of such tool can also configure Telemetry (as allowed by their
+ vendor).
+
+Important notes
+---------------
+
+* There is no concrete implementation of a Telemetry library in upstream LLVM.
+ We only provide the abstract API here. Any tool that wants telemetry will
+ implement one.
+
+ The rationale for this is that all the tools in LLVM are very
diff erent in
+ what they care about (what/where/when to instrument data). Hence, it might not
+ be practical to have a single implementation.
+ However, in the future, if we see enough common pattern, we can extract them
+ into a shared place. This is TBD - contributions are welcome.
+
+* No implementation of Telemetry in upstream LLVM shall store any of the
+ collected data due to privacy and security reasons:
+
+ * Different organizations have
diff erent privacy models:
+
+ * Which data is sensitive, which is not?
+ * Whether it is acceptable for instrumented data to be stored anywhere?
+ (to a local file, what not?)
+
+ * Data ownership and data collection consents are hard to accommodate from
+ LLVM developers' point of view:
+
+ * E.g., data collected by Telemetry is not necessarily owned by the user
+ of an LLVM tool with Telemetry enabled, hence the user's consent to data
+ collection is not meaningful. On the other hand, LLVM developers have no
+ reasonable ways to request consent from the "real" owners.
+
+
+High-level design
+=================
+
+Key components
+--------------
+
+The framework consists of four important classes:
+
+* ``llvm::telemetry::Manager``: The class responsible for collecting and
+ transmitting telemetry data. This is the main point of interaction between the
+ framework and any tool that wants to enable telemetry.
+* ``llvm::telemetry::TelemetryInfo``: Data courier
+* ``llvm::telemetry::Destination``: Data sink to which the Telemetry framework
+ sends data.
+ Its implementation is transparent to the framework.
+ It is up to the vendor to decide which pieces of data to forward and where
+ to forward them to for their final storage.
+* ``llvm::telemetry::Config``: Configurations for the ``Manager``.
+
+.. image:: llvm_telemetry_design.png
+
+How to implement and interact with the API
+------------------------------------------
+
+To use Telemetry in your tool, you need to provide a concrete implementation of the ``Manager`` class and ``Destination``.
+
+1) Define a custom ``Serializer``, ``Manager``, ``Destination`` and optionally a subclass of ``TelemetryInfo``
+
+.. code-block:: c++
+
+ 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.
+ };
+
+
+2) Use the library in your tool.
+
+Logging the tool init-process:
+
+.. code-block:: c++
+
+ // 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);
+
+Similar code can be used for logging the tool's exit.
diff --git a/llvm/docs/UserGuides.rst b/llvm/docs/UserGuides.rst
index 0b204d512876a3..6eee564713d6de 100644
--- a/llvm/docs/UserGuides.rst
+++ b/llvm/docs/UserGuides.rst
@@ -72,6 +72,7 @@ intermediate LLVM representation.
SupportLibrary
TableGen/index
TableGenFundamentals
+ Telemetry
Vectorizers
WritingAnLLVMPass
WritingAnLLVMNewPMPass
@@ -293,3 +294,6 @@ Additional Topics
:doc:`Sandbox IR <SandboxIR>`
This document describes the design and usage of Sandbox IR, a transactional layer over LLVM IR.
+
+:doc:`Telemetry`
+ This document describes the Telemetry framework in LLVM.
diff --git a/llvm/docs/llvm_telemetry_design.png b/llvm/docs/llvm_telemetry_design.png
new file mode 100644
index 00000000000000..48fbc003da18e6
Binary files /dev/null and b/llvm/docs/llvm_telemetry_design.png
diff er
diff --git a/llvm/include/llvm/Telemetry/Telemetry.h b/llvm/include/llvm/Telemetry/Telemetry.h
new file mode 100644
index 00000000000000..bbf0ac84d667ac
--- /dev/null
+++ b/llvm/include/llvm/Telemetry/Telemetry.h
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides the basic framework for Telemetry.
+/// Refer to its documentation at llvm/docs/Telemetry.rst for more details.
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TELEMETRY_TELEMETRY_H
+#define LLVM_TELEMETRY_TELEMETRY_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace llvm {
+namespace telemetry {
+
+class Serializer {
+public:
+ virtual Error init() = 0;
+ virtual void write(StringRef KeyName, bool Value) = 0;
+ virtual void write(StringRef KeyName, StringRef Value) = 0;
+ virtual void write(StringRef KeyName, int Value) = 0;
+ virtual void write(StringRef KeyName, long Value) = 0;
+ virtual void write(StringRef KeyName, long long Value) = 0;
+ virtual void write(StringRef KeyName, unsigned int Value) = 0;
+ virtual void write(StringRef KeyName, unsigned long Value) = 0;
+ virtual void write(StringRef KeyName, unsigned long long Value) = 0;
+ virtual void beginObject(StringRef KeyName) = 0;
+ virtual void endObject() = 0;
+ virtual Error finalize() = 0;
+
+ template <typename T, typename = typename T::mapped_type>
+ void write(StringRef KeyName, const T &Map) {
+ static_assert(std::is_convertible_v<typename T::key_type, StringRef>,
+ "KeyType must be convertible to string");
+ beginObject(KeyName);
+ for (const auto &KeyVal : Map)
+ write(KeyVal.first, KeyVal.second);
+ endObject();
+ }
+};
+
+/// Configuration for the Manager class.
+/// This stores configurations from both users and vendors and is passed
+/// to the Manager upon construction. (Any changes to the config after
+/// the Manager's construction will not have any effect on it).
+///
+/// This struct can be extended as needed to add additional configuration
+/// points specific to a vendor's implementation.
+struct Config {
+ // If true, telemetry will be enabled.
+ const bool EnableTelemetry;
+ Config(bool E) : EnableTelemetry(E) {}
+
+ virtual std::optional<std::string> makeSessionId() { return std::nullopt; }
+};
+
+/// For isa, dyn_cast, etc operations on TelemetryInfo.
+typedef unsigned KindType;
+/// This struct is used by TelemetryInfo to support isa<>, dyn_cast<>
+/// operations.
+/// It is defined as a struct (rather than an enum) because it is
+/// expected to be extended by subclasses which may have
+/// additional TelemetryInfo types defined to describe
diff erent events.
+struct EntryKind {
+ static const KindType Base = 0;
+};
+
+/// TelemetryInfo is the data courier, used to move instrumented data
+/// from the tool being monitored to the Telemetry framework.
+///
+/// This base class contains only the basic set of telemetry data.
+/// Downstream implementations can define more subclasses with
+/// additional fields to describe
diff erent events and concepts.
+///
+/// For example, The LLDB debugger can define a DebugCommandInfo subclass
+/// which has additional fields about the debug-command being instrumented,
+/// such as `CommandArguments` or `CommandName`.
+struct TelemetryInfo {
+ // This represents a unique-id, conventionally corresponding to
+ // a tool's session - i.e., every time the tool starts until it exits.
+ //
+ // Note: a tool could have multiple sessions running at once, in which
+ // case, these shall be multiple sets of TelemetryInfo with multiple unique
+ // IDs.
+ //
+ // Different usages can assign
diff erent types of IDs to this field.
+ std::string SessionId;
+
+ TelemetryInfo() = default;
+ virtual ~TelemetryInfo() = default;
+
+ virtual void serialize(Serializer &serializer) const;
+
+ // For isa, dyn_cast, etc, operations.
+ virtual KindType getKind() const { return EntryKind::Base; }
+ static bool classof(const TelemetryInfo *T) {
+ return T->getKind() == EntryKind::Base;
+ }
+};
+
+/// This class presents a data sink to which the Telemetry framework
+/// sends data.
+///
+/// Its implementation is transparent to the framework.
+/// It is up to the vendor to decide which pieces of data to forward
+/// and where to forward them.
+class Destination {
+public:
+ virtual ~Destination() = default;
+ virtual Error receiveEntry(const TelemetryInfo *Entry) = 0;
+ virtual StringLiteral name() const = 0;
+};
+
+/// This class is the main interaction point between any LLVM tool
+/// and this framework.
+/// It is responsible for collecting telemetry data from the tool being
+/// monitored and transmitting the data elsewhere.
+class Manager {
+public:
+ // Optional callback for subclasses to perform additional tasks before
+ // dispatching to Destinations.
+ virtual Error preDispatch(TelemetryInfo *Entry) = 0;
+
+ // Dispatch Telemetry data to the Destination(s).
+ // The argument is non-const because the Manager may add or remove
+ // data from the entry.
+ virtual Error dispatch(TelemetryInfo *Entry);
+
+ // Register a Destination.
+ void addDestination(std::unique_ptr<Destination> Destination);
+
+private:
+ std::vector<std::unique_ptr<Destination>> Destinations;
+};
+
+} // namespace telemetry
+} // namespace llvm
+
+#endif // LLVM_TELEMETRY_TELEMETRY_H
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 503c77cb13bd07..f6465612d30c0b 100644
--- a/llvm/lib/CMakeLists.txt
+++ b/llvm/lib/CMakeLists.txt
@@ -41,6 +41,7 @@ add_subdirectory(ProfileData)
add_subdirectory(Passes)
add_subdirectory(TargetParser)
add_subdirectory(TextAPI)
+add_subdirectory(Telemetry)
add_subdirectory(ToolDrivers)
add_subdirectory(XRay)
if (LLVM_INCLUDE_TESTS)
diff --git a/llvm/lib/Telemetry/CMakeLists.txt b/llvm/lib/Telemetry/CMakeLists.txt
new file mode 100644
index 00000000000000..a30875747f13df
--- /dev/null
+++ b/llvm/lib/Telemetry/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_llvm_component_library(LLVMTelemetry
+ Telemetry.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ "${LLVM_MAIN_INCLUDE_DIR}/llvm/Telemetry"
+
+ LINK_COMPONENTS
+ Support
+)
diff --git a/llvm/lib/Telemetry/Telemetry.cpp b/llvm/lib/Telemetry/Telemetry.cpp
new file mode 100644
index 00000000000000..9e13d08334e3b4
--- /dev/null
+++ b/llvm/lib/Telemetry/Telemetry.cpp
@@ -0,0 +1,26 @@
+#include "llvm/Telemetry/Telemetry.h"
+
+namespace llvm {
+namespace telemetry {
+
+void TelemetryInfo::serialize(Serializer &serializer) const {
+ serializer.write("SessionId", SessionId);
+}
+
+Error Manager::dispatch(TelemetryInfo *Entry) {
+ if (Error Err = preDispatch(Entry))
+ return Err;
+
+ Error AllErrs = Error::success();
+ for (auto &Dest : Destinations) {
+ AllErrs = joinErrors(std::move(AllErrs), Dest->receiveEntry(Entry));
+ }
+ return AllErrs;
+}
+
+void Manager::addDestination(std::unique_ptr<Destination> Dest) {
+ Destinations.push_back(std::move(Dest));
+}
+
+} // namespace telemetry
+} // namespace llvm
diff --git a/llvm/unittests/CMakeLists.txt b/llvm/unittests/CMakeLists.txt
index 8892f3e75729ab..81abce51b8939f 100644
--- a/llvm/unittests/CMakeLists.txt
+++ b/llvm/unittests/CMakeLists.txt
@@ -63,6 +63,7 @@ add_subdirectory(Support)
add_subdirectory(TableGen)
add_subdirectory(Target)
add_subdirectory(TargetParser)
+add_subdirectory(Telemetry)
add_subdirectory(Testing)
add_subdirectory(TextAPI)
add_subdirectory(Transforms)
diff --git a/llvm/unittests/Telemetry/CMakeLists.txt b/llvm/unittests/Telemetry/CMakeLists.txt
new file mode 100644
index 00000000000000..a40ae4b2f55607
--- /dev/null
+++ b/llvm/unittests/Telemetry/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(LLVM_LINK_COMPONENTS
+ Telemetry
+ Core
+ Support
+ )
+
+add_llvm_unittest(TelemetryTests
+ TelemetryTest.cpp
+ )
diff --git a/llvm/unittests/Telemetry/TelemetryTest.cpp b/llvm/unittests/Telemetry/TelemetryTest.cpp
new file mode 100644
index 00000000000000..f06cee04277fdc
--- /dev/null
+++ b/llvm/unittests/Telemetry/TelemetryTest.cpp
@@ -0,0 +1,258 @@
+//===- llvm/unittest/Telemetry/TelemetryTest.cpp - Telemetry unittests ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Telemetry/Telemetry.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+#include "gtest/gtest.h"
+#include <optional>
+#include <vector>
+
+namespace llvm {
+namespace telemetry {
+// Testing parameters.
+//
+// These are set by each test to force certain outcomes.
+struct TestContext {
+ // Controlling whether there is vendor plugin. In "real" implementation, the
+ // plugin-registration framework will handle the overrides but for tests, we
+ // just use a bool flag to decide which function to call.
+ bool HasVendorPlugin = false;
+
+ // This field contains data emitted by the framework for later
+ // verification by the tests.
+ std::string Buffer = "";
+
+ // The expected Uuid generated by the fake tool.
+ std::string ExpectedUuid = "";
+};
+
+class StringSerializer : public Serializer {
+public:
+ const std::string &getString() { return Buffer; }
+
+ Error init() override {
+ if (Started)
+ return createStringError("Serializer already in use");
+ Started = true;
+ Buffer.clear();
+ return Error::success();
+ }
+
+ void write(StringRef KeyName, bool Value) override {
+ writeHelper(KeyName, Value);
+ }
+
+ void write(StringRef KeyName, StringRef 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 beginObject(StringRef KeyName) override {
+ Children.push_back(std::string("\n"));
+ ChildrenNames.push_back(KeyName.str());
+ }
+
+ void endObject() override {
+ assert(!Children.empty() && !ChildrenNames.empty());
+ std::string ChildBuff = Children.back();
+ std::string Name = ChildrenNames.back();
+ Children.pop_back();
+ ChildrenNames.pop_back();
+ writeHelper(Name, ChildBuff);
+ }
+
+ Error finalize() override {
+ assert(Children.empty() && ChildrenNames.empty());
+ 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())
+ Buffer.append((Name + ":" + Twine(Value) + "\n").str());
+ else
+ Children.back().append((Name + ":" + Twine(Value) + "\n").str());
+ }
+
+ bool Started = false;
+ std::string Buffer;
+ std::vector<std::string> Children;
+ std::vector<std::string> ChildrenNames;
+};
+
+namespace vendor {
+struct VendorConfig : public Config {
+ VendorConfig(bool Enable) : Config(Enable) {}
+ std::optional<std::string> makeSessionId() override {
+ static int seed = 0;
+ return std::to_string(seed++);
+ }
+};
+
+std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
+ return std::make_shared<VendorConfig>(/*EnableTelemetry=*/true);
+}
+
+class TestStorageDestination : public Destination {
+public:
+ TestStorageDestination(TestContext *Ctxt) : CurrentContext(Ctxt) {}
+
+ Error receiveEntry(const TelemetryInfo *Entry) override {
+ if (Error Err = serializer.init())
+ return Err;
+
+ Entry->serialize(serializer);
+ if (Error Err = serializer.finalize())
+ return Err;
+
+ CurrentContext->Buffer.append(serializer.getString());
+ return Error::success();
+ }
+
+ StringLiteral name() const override { return "TestDestination"; }
+
+private:
+ TestContext *CurrentContext;
+ StringSerializer serializer;
+};
+
+struct StartupInfo : public TelemetryInfo {
+ std::string ToolName;
+ std::map<std::string, std::string> MetaData;
+
+ void serialize(Serializer &serializer) const override {
+ TelemetryInfo::serialize(serializer);
+ serializer.write("ToolName", ToolName);
+ serializer.write("MetaData", MetaData);
+ }
+};
+
+struct ExitInfo : public TelemetryInfo {
+ int ExitCode;
+ std::string ExitDesc;
+ void serialize(Serializer &serializer) const override {
+ TelemetryInfo::serialize(serializer);
+ serializer.write("ExitCode", ExitCode);
+ serializer.write("ExitDesc", ExitDesc);
+ }
+};
+
+class TestManager : public Manager {
+public:
+ static std::unique_ptr<TestManager>
+ createInstance(Config *Config, TestContext *CurrentContext) {
+ if (!Config->EnableTelemetry)
+ return nullptr;
+ CurrentContext->ExpectedUuid = *(Config->makeSessionId());
+ std::unique_ptr<TestManager> Ret = std::make_unique<TestManager>(
+ CurrentContext, CurrentContext->ExpectedUuid);
+
+ // Add a destination.
+ Ret->addDestination(
+ std::make_unique<TestStorageDestination>(CurrentContext));
+
+ return Ret;
+ }
+
+ TestManager(TestContext *Ctxt, std::string Id)
+ : CurrentContext(Ctxt), SessionId(Id) {}
+
+ Error preDispatch(TelemetryInfo *Entry) override {
+ Entry->SessionId = SessionId;
+ return Error::success();
+ }
+
+ std::string getSessionId() { return SessionId; }
+
+private:
+ TestContext *CurrentContext;
+ const std::string SessionId;
+};
+} // namespace vendor
+
+std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
+ if (Ctxt.HasVendorPlugin)
+ return vendor::getTelemetryConfig(Ctxt);
+
+ return std::make_shared<Config>(false);
+}
+
+TEST(TelemetryTest, TelemetryDisabled) {
+ TestContext Context;
+ Context.HasVendorPlugin = false;
+
+ std::shared_ptr<Config> Config = getTelemetryConfig(Context);
+ auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
+ EXPECT_EQ(nullptr, Manager);
+}
+
+TEST(TelemetryTest, TelemetryEnabled) {
+ const std::string ToolName = "TelemetryTestTool";
+
+ // Preset some params.
+ TestContext Context;
+ Context.HasVendorPlugin = true;
+ Context.Buffer.clear();
+
+ std::shared_ptr<Config> Config = getTelemetryConfig(Context);
+ auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
+
+ EXPECT_STREQ(Manager->getSessionId().c_str(), Context.ExpectedUuid.c_str());
+
+ vendor::StartupInfo S;
+ S.ToolName = ToolName;
+ S.MetaData["a"] = "A";
+ S.MetaData["b"] = "B";
+
+ Error startupEmitStatus = Manager->dispatch(&S);
+ EXPECT_FALSE(startupEmitStatus);
+ std::string ExpectedBuffer =
+ "SessionId:0\nToolName:TelemetryTestTool\nMetaData:\na:A\nb:B\n\n";
+ EXPECT_EQ(ExpectedBuffer, Context.Buffer);
+ Context.Buffer.clear();
+
+ vendor::ExitInfo E;
+ E.ExitCode = 0;
+ E.ExitDesc = "success";
+ Error exitEmitStatus = Manager->dispatch(&E);
+ EXPECT_FALSE(exitEmitStatus);
+ ExpectedBuffer = "SessionId:0\nExitCode:0\nExitDesc:success\n";
+ EXPECT_EQ(ExpectedBuffer, Context.Buffer);
+}
+
+} // namespace telemetry
+} // namespace llvm
More information about the llvm-commits
mailing list