blob: b15ba7767779bcf1b2b103a57d94fd0810da9cba [file] [log] [blame] [edit]
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/shared_lib/track_event/serialization.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/no_destructor.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/thread_utils.h"
#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
namespace perfetto::shlib {
namespace {
using perfetto::internal::TrackEventInternal;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
std::vector<std::string> GetCmdLine() {
std::vector<std::string> cmdline_str;
std::string cmdline;
if (perfetto::base::ReadFile("/proc/self/cmdline", &cmdline)) {
perfetto::base::StringSplitter splitter(std::move(cmdline), '\0');
while (splitter.Next()) {
cmdline_str.emplace_back(splitter.cur_token(), splitter.cur_token_size());
}
}
return cmdline_str;
}
#endif
} // namespace
protozero::MessageHandle<perfetto::protos::pbzero::TracePacket>
NewTracePacketInternal(perfetto::TraceWriterBase* trace_writer,
perfetto::shlib::TrackEventIncrementalState* incr_state,
const perfetto::shlib::TrackEventTlsState& tls_state,
perfetto::TraceTimestamp timestamp,
uint32_t seq_flags) {
// PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL is the default timestamp returned
// by TrackEventInternal::GetTraceTime(). If the configuration in `tls_state`
// uses a different clock, we have to use that instead.
if (PERFETTO_UNLIKELY(tls_state.default_clock_id !=
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL &&
timestamp.clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
timestamp.clock_id = tls_state.default_clock_id;
}
auto packet = trace_writer->NewTracePacket();
auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
if (PERFETTO_LIKELY(timestamp.clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
if (PERFETTO_LIKELY(incr_state->last_timestamp_ns <= timestamp.value)) {
// No need to set the clock id here, since
// PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL is the clock id assumed by
// default.
auto time_diff_ns = timestamp.value - incr_state->last_timestamp_ns;
auto time_diff_units = time_diff_ns / ts_unit_multiplier;
packet->set_timestamp(time_diff_units);
incr_state->last_timestamp_ns += time_diff_units * ts_unit_multiplier;
} else {
packet->set_timestamp(timestamp.value / ts_unit_multiplier);
packet->set_timestamp_clock_id(
ts_unit_multiplier == 1
? static_cast<uint32_t>(PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH)
: static_cast<uint32_t>(PERFETTO_TE_TIMESTAMP_TYPE_ABSOLUTE));
}
} else if (PERFETTO_LIKELY(timestamp.clock_id ==
tls_state.default_clock_id)) {
packet->set_timestamp(timestamp.value / ts_unit_multiplier);
} else {
packet->set_timestamp(timestamp.value);
packet->set_timestamp_clock_id(timestamp.clock_id);
}
packet->set_sequence_flags(seq_flags);
return packet;
}
void ResetIncrementalStateIfRequired(
perfetto::TraceWriterBase* trace_writer,
perfetto::shlib::TrackEventIncrementalState* incr_state,
const perfetto::shlib::TrackEventTlsState& tls_state,
const perfetto::TraceTimestamp& timestamp) {
if (!incr_state->was_cleared) {
return;
}
incr_state->was_cleared = false;
auto sequence_timestamp = timestamp;
if (timestamp.clock_id != PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH &&
timestamp.clock_id != PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL) {
sequence_timestamp = TrackEventInternal::GetTraceTime();
}
incr_state->last_timestamp_ns = sequence_timestamp.value;
auto tid = perfetto::base::GetThreadId();
auto pid = perfetto::Platform::GetCurrentProcessId();
uint64_t thread_track_uuid =
perfetto_te_process_track_uuid ^ static_cast<uint64_t>(tid);
auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
{
// Mark any incremental state before this point invalid. Also set up
// defaults so that we don't need to repeat constant data for each packet.
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
auto defaults = packet->set_trace_packet_defaults();
defaults->set_timestamp_clock_id(tls_state.default_clock_id);
// Establish the default track for this event sequence.
auto track_defaults = defaults->set_track_event_defaults();
track_defaults->set_track_uuid(thread_track_uuid);
if (tls_state.default_clock_id != PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH) {
perfetto::protos::pbzero::ClockSnapshot* clocks =
packet->set_clock_snapshot();
// Trace clock.
perfetto::protos::pbzero::ClockSnapshot::Clock* trace_clock =
clocks->add_clocks();
trace_clock->set_clock_id(PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH);
trace_clock->set_timestamp(sequence_timestamp.value);
if (PERFETTO_LIKELY(tls_state.default_clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
// Delta-encoded incremental clock in nanoseconds by default but
// configurable by |tls_state.timestamp_unit_multiplier|.
perfetto::protos::pbzero::ClockSnapshot::Clock* clock_incremental =
clocks->add_clocks();
clock_incremental->set_clock_id(PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL);
clock_incremental->set_timestamp(sequence_timestamp.value /
ts_unit_multiplier);
clock_incremental->set_is_incremental(true);
clock_incremental->set_unit_multiplier_ns(ts_unit_multiplier);
}
if (ts_unit_multiplier > 1) {
// absolute clock with custom timestamp_unit_multiplier.
perfetto::protos::pbzero::ClockSnapshot::Clock* absolute_clock =
clocks->add_clocks();
absolute_clock->set_clock_id(PERFETTO_TE_TIMESTAMP_TYPE_ABSOLUTE);
absolute_clock->set_timestamp(sequence_timestamp.value /
ts_unit_multiplier);
absolute_clock->set_is_incremental(false);
absolute_clock->set_unit_multiplier_ns(ts_unit_multiplier);
}
}
}
// Every thread should write a descriptor for its default track, because most
// trace points won't explicitly reference it. We also write the process
// descriptor from every thread that writes trace events to ensure it gets
// emitted at least once.
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto* track = packet->set_track_descriptor();
track->set_uuid(thread_track_uuid);
track->set_parent_uuid(perfetto_te_process_track_uuid);
auto* td = track->set_thread();
td->set_pid(static_cast<int32_t>(pid));
td->set_tid(static_cast<int32_t>(tid));
std::string thread_name;
if (perfetto::base::GetThreadName(thread_name))
td->set_thread_name(thread_name);
}
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto* track = packet->set_track_descriptor();
track->set_uuid(perfetto_te_process_track_uuid);
auto* pd = track->set_process();
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
static perfetto::base::NoDestructor<std::vector<std::string>> cmdline(
GetCmdLine());
if (!cmdline.ref().empty()) {
// Since cmdline is a zero-terminated list of arguments, this ends up
// writing just the first element, i.e., the process name, into the
// process name field.
pd->set_process_name(cmdline.ref()[0]);
for (const std::string& arg : cmdline.ref()) {
pd->add_cmdline(arg);
}
}
#endif
pd->set_pid(static_cast<int32_t>(pid));
}
}
} // namespace perfetto::shlib