Skip to content
File

Blob: src/workerd/util/perfetto-tracing.c++

3.3 KB
1#include "perfetto-tracing.h"
2 
3#ifdef WORKERD_USE_PERFETTO
4 
5#include "protos/perfetto/config/data_source_config.gen.h"
6#include "protos/perfetto/config/trace_config.gen.h"
7#include "protos/perfetto/config/track_event/track_event_config.gen.h"
8 
9#include <fcntl.h>
10#include <perfetto/tracing/track_event_legacy.h>
11 
12#include <kj/debug.h>
13#include <kj/filesystem.h>
14#include <kj/memory.h>
15#include <kj/vector.h>
16 
17PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(workerd::traces);
18 
19namespace workerd {
20namespace {
21kj::OwnFd openTraceFile(kj::StringPtr path) {
22 int fd = open(path.cStr(), O_RDWR | O_CREAT | O_TRUNC, 0600);
23 KJ_REQUIRE(fd >= 0, "Unable to open tracing file");
24 return kj::OwnFd(fd);
25}
26 
27void initializePerfettoOnce() {
28 if (perfetto::Tracing::IsInitialized()) return;
29 perfetto::TracingInitArgs args;
30 args.backends |= perfetto::kInProcessBackend;
31 perfetto::Tracing::Initialize(args);
32 PerfettoSession::registerWorkerdTracks();
33}
34 
35std::unique_ptr<perfetto::TracingSession> createTracingSession(int fd, kj::StringPtr categories) {
36 initializePerfettoOnce();
37 perfetto::protos::gen::TrackEventConfig track_event_cfg;
38 track_event_cfg.add_disabled_categories("*");
39 
40 // The categories is a comma-separated list
41 auto cats = PerfettoSession::parseCategories(categories);
42 for (auto category: cats) {
43 auto view = std::string(category.begin(), category.size());
44 track_event_cfg.add_enabled_categories(view);
45 }
46 
47 perfetto::TraceConfig cfg;
48 cfg.add_buffers()->set_size_kb(1024); // Record up to 1 MiB.
49 auto* ds_cfg = cfg.add_data_sources()->mutable_config();
50 ds_cfg->set_name("track_event");
51 ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
52 std::unique_ptr<perfetto::TracingSession> tracing_session(perfetto::Tracing::NewTrace());
53 tracing_session->Setup(cfg, fd);
54 return kj::mv(tracing_session);
55}
56} // namespace
57 
58void PerfettoSession::registerWorkerdTracks() {
59 static bool once = true;
60 KJ_ASSERT(once, "workerd perfetto tracks are already registered");
61 if (perfetto::Tracing::IsInitialized()) {
62 workerd::traces::TrackEvent::Register();
63 once = false;
64 }
65}
66 
67kj::Array<kj::ArrayPtr<const char>> PerfettoSession::parseCategories(kj::StringPtr categories) {
68 kj::Vector<kj::ArrayPtr<const char>> results;
69 while (categories.size() > 0) {
70 KJ_IF_SOME(pos, categories.findFirst(',')) {
71 results.add(categories.first(pos));
72 categories = categories.slice(pos + 1);
73 } else {
74 results.add(categories);
75 categories = nullptr;
76 }
77 }
78 return results.releaseAsArray();
79}
80 
81struct PerfettoSession::Impl {
82 kj::OwnFd fd;
83 std::unique_ptr<perfetto::TracingSession> session;
84 
85 Impl(kj::OwnFd dest, kj::StringPtr categories)
86 : fd(kj::mv(dest)),
87 session(createTracingSession(fd.get(), categories)) {
88 session->StartBlocking();
89 }
90};
91 
92PerfettoSession::PerfettoSession(kj::StringPtr path, kj::StringPtr categories)
93 : impl(kj::heap<Impl>(openTraceFile(path), categories)) {}
94 
95PerfettoSession::PerfettoSession(int fd, kj::StringPtr categories)
96 : impl(kj::heap<Impl>(kj::OwnFd(fd), categories)) {}
97 
98PerfettoSession::~PerfettoSession() noexcept(false) {
99 if (impl) {
100 impl->session->FlushBlocking();
101 impl->session->StopBlocking();
102 }
103}
104 
105void PerfettoSession::flush() {
106 if (impl) {
107 impl->session->FlushBlocking();
108 }
109}
110 
111} // namespace workerd
112 
113#endif // defined(WORKERD_USE_PERFETTO)