blob: 7a39f9d0682c8a4d3e32d27da9f3ae2d1fed315c [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_PERFETTO_UTILS_H_
#define RUNTIME_VM_PERFETTO_UTILS_H_
#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
#include <memory>
#include <tuple>
#include <utility>
#include "perfetto/ext/tracing/core/trace_packet.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "vm/json_stream.h"
#include "vm/os.h"
#include "vm/protos/perfetto/common/builtin_clock.pbzero.h"
#include "vm/protos/perfetto/trace/clock_snapshot.pbzero.h"
#include "vm/protos/perfetto/trace/trace_packet.pbzero.h"
#include "vm/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "vm/protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
namespace dart {
namespace perfetto_utils {
inline void SetTrustedPacketSequenceId(
perfetto::protos::pbzero::TracePacket* packet) {
// trusted_packet_sequence_id uniquely identifies a trace producer + writer
// pair. We set the trusted_packet_sequence_id of all packets that we write to
// the arbitrary value of 1.
packet->set_trusted_packet_sequence_id(1);
}
inline void SetTimestampAndMonotonicClockId(
perfetto::protos::pbzero::TracePacket* packet,
int64_t timestamp_micros) {
ASSERT(packet != nullptr);
// TODO(derekx): We should be able to set the unit_multiplier_ns field in a
// ClockSnapshot to avoid manually converting from microseconds to
// nanoseconds, but I haven't been able to get it to work.
packet->set_timestamp(timestamp_micros * 1000);
packet->set_timestamp_clock_id(
perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC);
}
inline void PopulateClockSnapshotPacket(
perfetto::protos::pbzero::TracePacket* packet) {
SetTrustedPacketSequenceId(packet);
perfetto::protos::pbzero::ClockSnapshot& clock_snapshot =
*packet->set_clock_snapshot();
clock_snapshot.set_primary_trace_clock(
perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC);
perfetto::protos::pbzero::ClockSnapshot_Clock& clock =
*clock_snapshot.add_clocks();
clock.set_clock_id(
perfetto::protos::pbzero::BuiltinClock::BUILTIN_CLOCK_MONOTONIC);
clock.set_timestamp(OS::GetCurrentMonotonicMicrosForTimeline() * 1000);
}
inline void PopulateProcessDescriptorPacket(
perfetto::protos::pbzero::TracePacket* packet) {
perfetto_utils::SetTrustedPacketSequenceId(packet);
perfetto::protos::pbzero::TrackDescriptor& track_descriptor =
*packet->set_track_descriptor();
const int64_t pid = OS::ProcessId();
track_descriptor.set_uuid(pid);
perfetto::protos::pbzero::ProcessDescriptor& process_descriptor =
*track_descriptor.set_process();
process_descriptor.set_pid(pid);
// TODO(derekx): Add the process name.
}
inline const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>
GetProtoPreamble(
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) {
ASSERT(packet != nullptr);
intptr_t size = 0;
for (const protozero::ScatteredHeapBuffer::Slice& slice :
packet->GetSlices()) {
size += slice.size() - slice.unused_bytes();
}
std::unique_ptr<uint8_t[]> preamble =
std::make_unique<uint8_t[]>(perfetto::TracePacket::kMaxPreambleBytes);
uint8_t* ptr = &preamble[0];
const uint8_t tag = protozero::proto_utils::MakeTagLengthDelimited(
perfetto::TracePacket::kPacketFieldNumber);
static_assert(tag < 0x80, "TracePacket tag should fit in one byte");
*(ptr++) = tag;
ptr = protozero::proto_utils::WriteVarInt(size, ptr);
intptr_t preamble_size = reinterpret_cast<intptr_t>(ptr) -
reinterpret_cast<intptr_t>(&preamble[0]);
return std::make_tuple(std::move(preamble), preamble_size);
}
inline void AppendPacketToJSONBase64String(
JSONBase64String* jsonBase64String,
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) {
ASSERT(jsonBase64String != nullptr);
ASSERT(packet != nullptr);
const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>& response =
perfetto_utils::GetProtoPreamble(packet);
const uint8_t* preamble = std::get<0>(response).get();
const intptr_t preamble_length = std::get<1>(response);
jsonBase64String->AppendBytes(preamble, preamble_length);
for (const protozero::ScatteredHeapBuffer::Slice& slice :
packet->GetSlices()) {
jsonBase64String->AppendBytes(slice.start(),
slice.size() - slice.unused_bytes());
}
}
} // namespace perfetto_utils
} // namespace dart
#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
#endif // RUNTIME_VM_PERFETTO_UTILS_H_