File
Blob: src/workerd/util/perfetto-tracing.c++
| 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 | |
| 17 | PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(workerd::traces); |
| 18 | |
| 19 | namespace workerd { |
| 20 | namespace { |
| 21 | kj::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 | |
| 27 | void 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 | |
| 35 | std::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 | |
| 58 | void 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 | |
| 67 | kj::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 | |
| 81 | struct 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 | |
| 92 | PerfettoSession::PerfettoSession(kj::StringPtr path, kj::StringPtr categories) |
| 93 | : impl(kj::heap<Impl>(openTraceFile(path), categories)) {} |
| 94 | |
| 95 | PerfettoSession::PerfettoSession(int fd, kj::StringPtr categories) |
| 96 | : impl(kj::heap<Impl>(kj::OwnFd(fd), categories)) {} |
| 97 | |
| 98 | PerfettoSession::~PerfettoSession() noexcept(false) { |
| 99 | if (impl) { |
| 100 | impl->session->FlushBlocking(); |
| 101 | impl->session->StopBlocking(); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | void PerfettoSession::flush() { |
| 106 | if (impl) { |
| 107 | impl->session->FlushBlocking(); |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | } // namespace workerd |
| 112 | |
| 113 | #endif // defined(WORKERD_USE_PERFETTO) |