Skip to content

Commit dbae717

Browse files
authoredDec 23, 2024
Reapply "[llvm]Add a simple Telemetry framework" (llvm#120769) (llvm#121003)
This reverts commit 2ec6174. New changes: - Use explicit overloads of write(<int types>) - Fix link error due to missing dependency (lib/Support) - Updated tests and docs
1 parent ac586fd commit dbae717

File tree

10 files changed

+719
-0
lines changed

10 files changed

+719
-0
lines changed
 

‎llvm/docs/Telemetry.rst

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
===========================
2+
Telemetry framework in LLVM
3+
===========================
4+
5+
.. contents::
6+
:local:
7+
8+
.. toctree::
9+
:hidden:
10+
11+
Objective
12+
=========
13+
14+
Provides a common framework in LLVM for collecting various usage and performance
15+
metrics.
16+
It is located at ``llvm/Telemetry/Telemetry.h``.
17+
18+
Characteristics
19+
---------------
20+
* Configurable and extensible by:
21+
22+
* Tools: any tool that wants to use Telemetry can extend and customize it.
23+
* Vendors: Toolchain vendors can also provide custom implementation of the
24+
library, which could either override or extend the given tool's upstream
25+
implementation, to best fit their organization's usage and privacy models.
26+
* End users of such tool can also configure Telemetry (as allowed by their
27+
vendor).
28+
29+
Important notes
30+
---------------
31+
32+
* There is no concrete implementation of a Telemetry library in upstream LLVM.
33+
We only provide the abstract API here. Any tool that wants telemetry will
34+
implement one.
35+
36+
The rationale for this is that all the tools in LLVM are very different in
37+
what they care about (what/where/when to instrument data). Hence, it might not
38+
be practical to have a single implementation.
39+
However, in the future, if we see enough common pattern, we can extract them
40+
into a shared place. This is TBD - contributions are welcome.
41+
42+
* No implementation of Telemetry in upstream LLVM shall store any of the
43+
collected data due to privacy and security reasons:
44+
45+
* Different organizations have different privacy models:
46+
47+
* Which data is sensitive, which is not?
48+
* Whether it is acceptable for instrumented data to be stored anywhere?
49+
(to a local file, what not?)
50+
51+
* Data ownership and data collection consents are hard to accommodate from
52+
LLVM developers' point of view:
53+
54+
* E.g., data collected by Telemetry is not necessarily owned by the user
55+
of an LLVM tool with Telemetry enabled, hence the user's consent to data
56+
collection is not meaningful. On the other hand, LLVM developers have no
57+
reasonable ways to request consent from the "real" owners.
58+
59+
60+
High-level design
61+
=================
62+
63+
Key components
64+
--------------
65+
66+
The framework consists of four important classes:
67+
68+
* ``llvm::telemetry::Manager``: The class responsible for collecting and
69+
transmitting telemetry data. This is the main point of interaction between the
70+
framework and any tool that wants to enable telemetry.
71+
* ``llvm::telemetry::TelemetryInfo``: Data courier
72+
* ``llvm::telemetry::Destination``: Data sink to which the Telemetry framework
73+
sends data.
74+
Its implementation is transparent to the framework.
75+
It is up to the vendor to decide which pieces of data to forward and where
76+
to forward them to for their final storage.
77+
* ``llvm::telemetry::Config``: Configurations for the ``Manager``.
78+
79+
.. image:: llvm_telemetry_design.png
80+
81+
How to implement and interact with the API
82+
------------------------------------------
83+
84+
To use Telemetry in your tool, you need to provide a concrete implementation of the ``Manager`` class and ``Destination``.
85+
86+
1) Define a custom ``Serializer``, ``Manager``, ``Destination`` and optionally a subclass of ``TelemetryInfo``
87+
88+
.. code-block:: c++
89+
90+
class JsonSerializer : public Serializer {
91+
public:
92+
json::Object *getOutputObject() { return Out.get(); }
93+
94+
Error init() override {
95+
if (Started)
96+
return createStringError("Serializer already in use");
97+
started = true;
98+
Out = std::make_unique<json::Object>();
99+
return Error::success();
100+
}
101+
102+
// Serialize the given value.
103+
void write(StringRef KeyName, bool Value) override {
104+
writeHelper(KeyName, Value);
105+
}
106+
107+
void write(StringRef KeyName, int Value) override {
108+
writeHelper(KeyName, Value);
109+
}
110+
111+
void write(StringRef KeyName, long Value) override {
112+
writeHelper(KeyName, Value);
113+
}
114+
115+
void write(StringRef KeyName, long long Value ) override {
116+
writeHelper(KeyName, Value);
117+
}
118+
119+
void write(StringRef KeyName, unsigned int Value) override {
120+
writeHelper(KeyName, Value);
121+
}
122+
123+
void write(StringRef KeyName, unsigned long Value) override {
124+
writeHelper(KeyName, Value);
125+
}
126+
127+
void write(StringRef KeyName, unsigned long long Value) override {
128+
writeHelper(KeyName, Value);
129+
}
130+
131+
void write(StringRef KeyName, StringRef Value) override {
132+
writeHelper(KeyName, Value);
133+
}
134+
135+
void beginObject(StringRef KeyName) override {
136+
Children.push_back(json::Object());
137+
ChildrenNames.push_back(KeyName.str());
138+
}
139+
140+
void endObject() override {
141+
assert(!Children.empty() && !ChildrenNames.empty());
142+
json::Value Val = json::Value(std::move(Children.back()));
143+
std::string Name = ChildrenNames.back();
144+
145+
Children.pop_back();
146+
ChildrenNames.pop_back();
147+
writeHelper(Name, std::move(Val));
148+
}
149+
150+
Error finalize() override {
151+
if (!Started)
152+
return createStringError("Serializer not currently in use");
153+
Started = false;
154+
return Error::success();
155+
}
156+
157+
private:
158+
template <typename T> void writeHelper(StringRef Name, T Value) {
159+
assert(Started && "serializer not started");
160+
if (Children.empty())
161+
Out->try_emplace(Name, Value);
162+
else
163+
Children.back().try_emplace(Name, Value);
164+
}
165+
bool Started = false;
166+
std::unique_ptr<json::Object> Out;
167+
std::vector<json::Object> Children;
168+
std::vector<std::string> ChildrenNames;
169+
};
170+
171+
class MyManager : public telemery::Manager {
172+
public:
173+
static std::unique_ptr<MyManager> createInstatnce(telemetry::Config *Config) {
174+
// If Telemetry is not enabled, then just return null;
175+
if (!Config->EnableTelemetry)
176+
return nullptr;
177+
return std::make_unique<MyManager>();
178+
}
179+
MyManager() = default;
180+
181+
Error preDispatch(TelemetryInfo *Entry) override {
182+
Entry->SessionId = SessionId;
183+
return Error::success();
184+
}
185+
186+
// You can also define additional instrumentation points.
187+
void logStartup(TelemetryInfo *Entry) {
188+
// Add some additional data to entry.
189+
Entry->Msg = "Some message";
190+
dispatch(Entry);
191+
}
192+
193+
void logAdditionalPoint(TelemetryInfo *Entry) {
194+
// .... code here
195+
}
196+
197+
private:
198+
const std::string SessionId;
199+
};
200+
201+
class MyDestination : public telemetry::Destination {
202+
public:
203+
Error receiveEntry(const TelemetryInfo *Entry) override {
204+
if (Error Err = Serializer.init())
205+
return Err;
206+
207+
Entry->serialize(Serializer);
208+
if (Error Err = Serializer.finalize())
209+
return Err;
210+
211+
json::Object Copied = *Serializer.getOutputObject();
212+
// Send the `Copied` object to wherever.
213+
return Error::success();
214+
}
215+
216+
private:
217+
JsonSerializer Serializer;
218+
};
219+
220+
// This defines a custom TelemetryInfo that has an additional Msg field.
221+
struct MyTelemetryInfo : public telemetry::TelemetryInfo {
222+
std::string Msg;
223+
224+
Error serialize(Serializer &Serializer) const override {
225+
TelemetryInfo::serialize(serializer);
226+
Serializer.writeString("MyMsg", Msg);
227+
}
228+
229+
// Note: implement getKind() and classof() to support dyn_cast operations.
230+
};
231+
232+
233+
2) Use the library in your tool.
234+
235+
Logging the tool init-process:
236+
237+
.. code-block:: c++
238+
239+
// In tool's initialization code.
240+
auto StartTime = std::chrono::time_point<std::chrono::steady_clock>::now();
241+
telemetry::Config MyConfig = makeConfig(); // Build up the appropriate Config struct here.
242+
auto Manager = MyManager::createInstance(&MyConfig);
243+
244+
245+
// Any other tool's init code can go here.
246+
// ...
247+
248+
// Finally, take a snapshot of the time now so we know how long it took the
249+
// init process to finish.
250+
auto EndTime = std::chrono::time_point<std::chrono::steady_clock>::now();
251+
MyTelemetryInfo Entry;
252+
253+
Entry.Start = StartTime;
254+
Entry.End = EndTime;
255+
Manager->logStartup(&Entry);
256+
257+
Similar code can be used for logging the tool's exit.

‎llvm/docs/UserGuides.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ intermediate LLVM representation.
7272
SupportLibrary
7373
TableGen/index
7474
TableGenFundamentals
75+
Telemetry
7576
Vectorizers
7677
WritingAnLLVMPass
7778
WritingAnLLVMNewPMPass
@@ -293,3 +294,6 @@ Additional Topics
293294

294295
:doc:`Sandbox IR <SandboxIR>`
295296
This document describes the design and usage of Sandbox IR, a transactional layer over LLVM IR.
297+
298+
:doc:`Telemetry`
299+
This document describes the Telemetry framework in LLVM.

‎llvm/docs/llvm_telemetry_design.png

92.4 KB
Loading
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//===----------------------------------------------------------------------===//
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+
/// \file
10+
/// This file provides the basic framework for Telemetry.
11+
/// Refer to its documentation at llvm/docs/Telemetry.rst for more details.
12+
//===---------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_TELEMETRY_TELEMETRY_H
15+
#define LLVM_TELEMETRY_TELEMETRY_H
16+
17+
#include "llvm/ADT/DenseMap.h"
18+
#include "llvm/ADT/StringExtras.h"
19+
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/Support/Error.h"
21+
#include <map>
22+
#include <memory>
23+
#include <optional>
24+
#include <string>
25+
#include <type_traits>
26+
#include <vector>
27+
28+
namespace llvm {
29+
namespace telemetry {
30+
31+
class Serializer {
32+
public:
33+
virtual Error init() = 0;
34+
virtual void write(StringRef KeyName, bool Value) = 0;
35+
virtual void write(StringRef KeyName, StringRef Value) = 0;
36+
virtual void write(StringRef KeyName, int Value) = 0;
37+
virtual void write(StringRef KeyName, long Value) = 0;
38+
virtual void write(StringRef KeyName, long long Value) = 0;
39+
virtual void write(StringRef KeyName, unsigned int Value) = 0;
40+
virtual void write(StringRef KeyName, unsigned long Value) = 0;
41+
virtual void write(StringRef KeyName, unsigned long long Value) = 0;
42+
virtual void beginObject(StringRef KeyName) = 0;
43+
virtual void endObject() = 0;
44+
virtual Error finalize() = 0;
45+
46+
template <typename T, typename = typename T::mapped_type>
47+
void write(StringRef KeyName, const T &Map) {
48+
static_assert(std::is_convertible_v<typename T::key_type, StringRef>,
49+
"KeyType must be convertible to string");
50+
beginObject(KeyName);
51+
for (const auto &KeyVal : Map)
52+
write(KeyVal.first, KeyVal.second);
53+
endObject();
54+
}
55+
};
56+
57+
/// Configuration for the Manager class.
58+
/// This stores configurations from both users and vendors and is passed
59+
/// to the Manager upon construction. (Any changes to the config after
60+
/// the Manager's construction will not have any effect on it).
61+
///
62+
/// This struct can be extended as needed to add additional configuration
63+
/// points specific to a vendor's implementation.
64+
struct Config {
65+
// If true, telemetry will be enabled.
66+
const bool EnableTelemetry;
67+
Config(bool E) : EnableTelemetry(E) {}
68+
69+
virtual std::optional<std::string> makeSessionId() { return std::nullopt; }
70+
};
71+
72+
/// For isa, dyn_cast, etc operations on TelemetryInfo.
73+
typedef unsigned KindType;
74+
/// This struct is used by TelemetryInfo to support isa<>, dyn_cast<>
75+
/// operations.
76+
/// It is defined as a struct (rather than an enum) because it is
77+
/// expected to be extended by subclasses which may have
78+
/// additional TelemetryInfo types defined to describe different events.
79+
struct EntryKind {
80+
static const KindType Base = 0;
81+
};
82+
83+
/// TelemetryInfo is the data courier, used to move instrumented data
84+
/// from the tool being monitored to the Telemetry framework.
85+
///
86+
/// This base class contains only the basic set of telemetry data.
87+
/// Downstream implementations can define more subclasses with
88+
/// additional fields to describe different events and concepts.
89+
///
90+
/// For example, The LLDB debugger can define a DebugCommandInfo subclass
91+
/// which has additional fields about the debug-command being instrumented,
92+
/// such as `CommandArguments` or `CommandName`.
93+
struct TelemetryInfo {
94+
// This represents a unique-id, conventionally corresponding to
95+
// a tool's session - i.e., every time the tool starts until it exits.
96+
//
97+
// Note: a tool could have multiple sessions running at once, in which
98+
// case, these shall be multiple sets of TelemetryInfo with multiple unique
99+
// IDs.
100+
//
101+
// Different usages can assign different types of IDs to this field.
102+
std::string SessionId;
103+
104+
TelemetryInfo() = default;
105+
virtual ~TelemetryInfo() = default;
106+
107+
virtual void serialize(Serializer &serializer) const;
108+
109+
// For isa, dyn_cast, etc, operations.
110+
virtual KindType getKind() const { return EntryKind::Base; }
111+
static bool classof(const TelemetryInfo *T) {
112+
return T->getKind() == EntryKind::Base;
113+
}
114+
};
115+
116+
/// This class presents a data sink to which the Telemetry framework
117+
/// sends data.
118+
///
119+
/// Its implementation is transparent to the framework.
120+
/// It is up to the vendor to decide which pieces of data to forward
121+
/// and where to forward them.
122+
class Destination {
123+
public:
124+
virtual ~Destination() = default;
125+
virtual Error receiveEntry(const TelemetryInfo *Entry) = 0;
126+
virtual StringLiteral name() const = 0;
127+
};
128+
129+
/// This class is the main interaction point between any LLVM tool
130+
/// and this framework.
131+
/// It is responsible for collecting telemetry data from the tool being
132+
/// monitored and transmitting the data elsewhere.
133+
class Manager {
134+
public:
135+
// Optional callback for subclasses to perform additional tasks before
136+
// dispatching to Destinations.
137+
virtual Error preDispatch(TelemetryInfo *Entry) = 0;
138+
139+
// Dispatch Telemetry data to the Destination(s).
140+
// The argument is non-const because the Manager may add or remove
141+
// data from the entry.
142+
virtual Error dispatch(TelemetryInfo *Entry);
143+
144+
// Register a Destination.
145+
void addDestination(std::unique_ptr<Destination> Destination);
146+
147+
private:
148+
std::vector<std::unique_ptr<Destination>> Destinations;
149+
};
150+
151+
} // namespace telemetry
152+
} // namespace llvm
153+
154+
#endif // LLVM_TELEMETRY_TELEMETRY_H

‎llvm/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ add_subdirectory(ProfileData)
4141
add_subdirectory(Passes)
4242
add_subdirectory(TargetParser)
4343
add_subdirectory(TextAPI)
44+
add_subdirectory(Telemetry)
4445
add_subdirectory(ToolDrivers)
4546
add_subdirectory(XRay)
4647
if (LLVM_INCLUDE_TESTS)

‎llvm/lib/Telemetry/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_llvm_component_library(LLVMTelemetry
2+
Telemetry.cpp
3+
4+
ADDITIONAL_HEADER_DIRS
5+
"${LLVM_MAIN_INCLUDE_DIR}/llvm/Telemetry"
6+
7+
LINK_COMPONENTS
8+
Support
9+
)

‎llvm/lib/Telemetry/Telemetry.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "llvm/Telemetry/Telemetry.h"
2+
3+
namespace llvm {
4+
namespace telemetry {
5+
6+
void TelemetryInfo::serialize(Serializer &serializer) const {
7+
serializer.write("SessionId", SessionId);
8+
}
9+
10+
Error Manager::dispatch(TelemetryInfo *Entry) {
11+
if (Error Err = preDispatch(Entry))
12+
return Err;
13+
14+
Error AllErrs = Error::success();
15+
for (auto &Dest : Destinations) {
16+
AllErrs = joinErrors(std::move(AllErrs), Dest->receiveEntry(Entry));
17+
}
18+
return AllErrs;
19+
}
20+
21+
void Manager::addDestination(std::unique_ptr<Destination> Dest) {
22+
Destinations.push_back(std::move(Dest));
23+
}
24+
25+
} // namespace telemetry
26+
} // namespace llvm

‎llvm/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ add_subdirectory(Support)
6363
add_subdirectory(TableGen)
6464
add_subdirectory(Target)
6565
add_subdirectory(TargetParser)
66+
add_subdirectory(Telemetry)
6667
add_subdirectory(Testing)
6768
add_subdirectory(TextAPI)
6869
add_subdirectory(Transforms)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Telemetry
3+
Core
4+
Support
5+
)
6+
7+
add_llvm_unittest(TelemetryTests
8+
TelemetryTest.cpp
9+
)
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
//===- llvm/unittest/Telemetry/TelemetryTest.cpp - Telemetry unittests ---===//
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+
#include "llvm/Telemetry/Telemetry.h"
10+
#include "llvm/ADT/StringRef.h"
11+
#include "llvm/Support/Casting.h"
12+
#include "llvm/Support/Error.h"
13+
#include "gtest/gtest.h"
14+
#include <optional>
15+
#include <vector>
16+
17+
namespace llvm {
18+
namespace telemetry {
19+
// Testing parameters.
20+
//
21+
// These are set by each test to force certain outcomes.
22+
struct TestContext {
23+
// Controlling whether there is vendor plugin. In "real" implementation, the
24+
// plugin-registration framework will handle the overrides but for tests, we
25+
// just use a bool flag to decide which function to call.
26+
bool HasVendorPlugin = false;
27+
28+
// This field contains data emitted by the framework for later
29+
// verification by the tests.
30+
std::string Buffer = "";
31+
32+
// The expected Uuid generated by the fake tool.
33+
std::string ExpectedUuid = "";
34+
};
35+
36+
class StringSerializer : public Serializer {
37+
public:
38+
const std::string &getString() { return Buffer; }
39+
40+
Error init() override {
41+
if (Started)
42+
return createStringError("Serializer already in use");
43+
Started = true;
44+
Buffer.clear();
45+
return Error::success();
46+
}
47+
48+
void write(StringRef KeyName, bool Value) override {
49+
writeHelper(KeyName, Value);
50+
}
51+
52+
void write(StringRef KeyName, StringRef Value) override {
53+
writeHelper(KeyName, Value);
54+
}
55+
56+
void write(StringRef KeyName, int Value) override {
57+
writeHelper(KeyName, Value);
58+
}
59+
60+
void write(StringRef KeyName, long Value) override {
61+
writeHelper(KeyName, Value);
62+
}
63+
64+
void write(StringRef KeyName, long long Value) override {
65+
writeHelper(KeyName, Value);
66+
}
67+
68+
void write(StringRef KeyName, unsigned int Value) override {
69+
writeHelper(KeyName, Value);
70+
}
71+
72+
void write(StringRef KeyName, unsigned long Value) override {
73+
writeHelper(KeyName, Value);
74+
}
75+
76+
void write(StringRef KeyName, unsigned long long Value) override {
77+
writeHelper(KeyName, Value);
78+
}
79+
80+
void beginObject(StringRef KeyName) override {
81+
Children.push_back(std::string("\n"));
82+
ChildrenNames.push_back(KeyName.str());
83+
}
84+
85+
void endObject() override {
86+
assert(!Children.empty() && !ChildrenNames.empty());
87+
std::string ChildBuff = Children.back();
88+
std::string Name = ChildrenNames.back();
89+
Children.pop_back();
90+
ChildrenNames.pop_back();
91+
writeHelper(Name, ChildBuff);
92+
}
93+
94+
Error finalize() override {
95+
assert(Children.empty() && ChildrenNames.empty());
96+
if (!Started)
97+
return createStringError("Serializer not currently in use");
98+
Started = false;
99+
return Error::success();
100+
}
101+
102+
private:
103+
template <typename T> void writeHelper(StringRef Name, T Value) {
104+
assert(Started && "serializer not started");
105+
if (Children.empty())
106+
Buffer.append((Name + ":" + Twine(Value) + "\n").str());
107+
else
108+
Children.back().append((Name + ":" + Twine(Value) + "\n").str());
109+
}
110+
111+
bool Started = false;
112+
std::string Buffer;
113+
std::vector<std::string> Children;
114+
std::vector<std::string> ChildrenNames;
115+
};
116+
117+
namespace vendor {
118+
struct VendorConfig : public Config {
119+
VendorConfig(bool Enable) : Config(Enable) {}
120+
std::optional<std::string> makeSessionId() override {
121+
static int seed = 0;
122+
return std::to_string(seed++);
123+
}
124+
};
125+
126+
std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
127+
return std::make_shared<VendorConfig>(/*EnableTelemetry=*/true);
128+
}
129+
130+
class TestStorageDestination : public Destination {
131+
public:
132+
TestStorageDestination(TestContext *Ctxt) : CurrentContext(Ctxt) {}
133+
134+
Error receiveEntry(const TelemetryInfo *Entry) override {
135+
if (Error Err = serializer.init())
136+
return Err;
137+
138+
Entry->serialize(serializer);
139+
if (Error Err = serializer.finalize())
140+
return Err;
141+
142+
CurrentContext->Buffer.append(serializer.getString());
143+
return Error::success();
144+
}
145+
146+
StringLiteral name() const override { return "TestDestination"; }
147+
148+
private:
149+
TestContext *CurrentContext;
150+
StringSerializer serializer;
151+
};
152+
153+
struct StartupInfo : public TelemetryInfo {
154+
std::string ToolName;
155+
std::map<std::string, std::string> MetaData;
156+
157+
void serialize(Serializer &serializer) const override {
158+
TelemetryInfo::serialize(serializer);
159+
serializer.write("ToolName", ToolName);
160+
serializer.write("MetaData", MetaData);
161+
}
162+
};
163+
164+
struct ExitInfo : public TelemetryInfo {
165+
int ExitCode;
166+
std::string ExitDesc;
167+
void serialize(Serializer &serializer) const override {
168+
TelemetryInfo::serialize(serializer);
169+
serializer.write("ExitCode", ExitCode);
170+
serializer.write("ExitDesc", ExitDesc);
171+
}
172+
};
173+
174+
class TestManager : public Manager {
175+
public:
176+
static std::unique_ptr<TestManager>
177+
createInstance(Config *Config, TestContext *CurrentContext) {
178+
if (!Config->EnableTelemetry)
179+
return nullptr;
180+
CurrentContext->ExpectedUuid = *(Config->makeSessionId());
181+
std::unique_ptr<TestManager> Ret = std::make_unique<TestManager>(
182+
CurrentContext, CurrentContext->ExpectedUuid);
183+
184+
// Add a destination.
185+
Ret->addDestination(
186+
std::make_unique<TestStorageDestination>(CurrentContext));
187+
188+
return Ret;
189+
}
190+
191+
TestManager(TestContext *Ctxt, std::string Id)
192+
: CurrentContext(Ctxt), SessionId(Id) {}
193+
194+
Error preDispatch(TelemetryInfo *Entry) override {
195+
Entry->SessionId = SessionId;
196+
return Error::success();
197+
}
198+
199+
std::string getSessionId() { return SessionId; }
200+
201+
private:
202+
TestContext *CurrentContext;
203+
const std::string SessionId;
204+
};
205+
} // namespace vendor
206+
207+
std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
208+
if (Ctxt.HasVendorPlugin)
209+
return vendor::getTelemetryConfig(Ctxt);
210+
211+
return std::make_shared<Config>(false);
212+
}
213+
214+
TEST(TelemetryTest, TelemetryDisabled) {
215+
TestContext Context;
216+
Context.HasVendorPlugin = false;
217+
218+
std::shared_ptr<Config> Config = getTelemetryConfig(Context);
219+
auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
220+
EXPECT_EQ(nullptr, Manager);
221+
}
222+
223+
TEST(TelemetryTest, TelemetryEnabled) {
224+
const std::string ToolName = "TelemetryTestTool";
225+
226+
// Preset some params.
227+
TestContext Context;
228+
Context.HasVendorPlugin = true;
229+
Context.Buffer.clear();
230+
231+
std::shared_ptr<Config> Config = getTelemetryConfig(Context);
232+
auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
233+
234+
EXPECT_STREQ(Manager->getSessionId().c_str(), Context.ExpectedUuid.c_str());
235+
236+
vendor::StartupInfo S;
237+
S.ToolName = ToolName;
238+
S.MetaData["a"] = "A";
239+
S.MetaData["b"] = "B";
240+
241+
Error startupEmitStatus = Manager->dispatch(&S);
242+
EXPECT_FALSE(startupEmitStatus);
243+
std::string ExpectedBuffer =
244+
"SessionId:0\nToolName:TelemetryTestTool\nMetaData:\na:A\nb:B\n\n";
245+
EXPECT_EQ(ExpectedBuffer, Context.Buffer);
246+
Context.Buffer.clear();
247+
248+
vendor::ExitInfo E;
249+
E.ExitCode = 0;
250+
E.ExitDesc = "success";
251+
Error exitEmitStatus = Manager->dispatch(&E);
252+
EXPECT_FALSE(exitEmitStatus);
253+
ExpectedBuffer = "SessionId:0\nExitCode:0\nExitDesc:success\n";
254+
EXPECT_EQ(ExpectedBuffer, Context.Buffer);
255+
}
256+
257+
} // namespace telemetry
258+
} // namespace llvm

0 commit comments

Comments
 (0)
Please sign in to comment.