blob: 0954b6d4684356a5b4fecc16a19b722ba6d0354d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/user_metrics.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/common/task_annotator.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/trace_event/task_execution_macros.h"
#include "base/trace_event/thread_instruction_count.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_log.h"
#include "base/tracing/trace_time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/tracing/common/tracing_switches.h"
#include "services/tracing/perfetto/test_utils.h"
#include "services/tracing/public/cpp/perfetto/macros.h"
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
#include "services/tracing/public/cpp/perfetto/producer_test_utils.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "third_party/perfetto/include/perfetto/tracing/track_event_interned_data_index.h"
#include "third_party/perfetto/protos/perfetto/trace/clock_snapshot.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/track_descriptor.pb.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#endif
using TrackEvent = perfetto::protos::TrackEvent;
namespace tracing {
namespace {
constexpr char kTestProcess[] = "Browser";
constexpr char kTestThread[] = "CrTestMain";
constexpr const char kCategoryGroup[] = "browser";
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
std::unique_ptr<perfetto::TracingSession> g_tracing_session;
#else
constexpr uint32_t kClockIdAbsolute = 64;
constexpr uint32_t kClockIdIncremental = 65;
#endif
class TraceEventDataSourceTest
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
: public testing::Test
#else
: public TracingUnitTest
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
{
public:
void SetUp() override {
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TracingUnitTest::SetUp();
TraceEventDataSource::GetInstance()->RegisterStartupHooks();
// TODO(eseckler): Initialize the entire perfetto client library instead.
perfetto::internal::TrackRegistry::InitializeInstance();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
old_thread_name_ =
base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
base::ThreadIdNameManager::GetInstance()->SetName(kTestThread);
old_process_name_ =
base::trace_event::TraceLog::GetInstance()->process_name();
base::trace_event::TraceLog::GetInstance()->set_process_name(kTestProcess);
base::trace_event::TraceLog::GetInstance()->SetRecordHostAppPackageName(
false);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
PerfettoTracedProcess::GetTaskRunner()->ResetTaskRunnerForTesting(
base::ThreadTaskRunnerHandle::Get());
TraceEventDataSource::GetInstance()->ResetForTesting();
TraceEventMetadataSource::GetInstance()->ResetForTesting();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner();
auto perfetto_wrapper = std::make_unique<base::tracing::PerfettoTaskRunner>(
base::ThreadTaskRunnerHandle::Get());
producer_client_ =
std::make_unique<TestProducerClient>(std::move(perfetto_wrapper));
TraceEventMetadataSource::GetInstance()->ResetForTesting();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void TearDown() override {
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (base::trace_event::TraceLog::GetInstance()->IsEnabled()) {
base::RunLoop wait_for_tracelog_flush;
TraceEventDataSource::GetInstance()->StopTracing(
wait_for_tracelog_flush.QuitClosure());
wait_for_tracelog_flush.Run();
}
// As MockTraceWriter keeps a pointer to our TestProducerClient,
// we need to make sure to clean it up from TLS. The other sequences
// get DummyTraceWriters that we don't care about.
TraceEventDataSource::GetInstance()->FlushCurrentThread();
producer_client_.reset();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
base::ThreadIdNameManager::GetInstance()->SetName(old_thread_name_);
base::trace_event::TraceLog::GetInstance()->set_process_name(
old_process_name_);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TracingUnitTest::TearDown();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Destroy after task environment shuts down so that no other threads try to
// add trace events.
TraceEventDataSource::ResetForTesting();
}
void StartTraceEventDataSource(
bool privacy_filtering_enabled = false,
std::string chrome_trace_config = "foo,cat1,cat2,cat3,browser,-*",
std::vector<std::string> histograms = {}) {
base::trace_event::TraceConfig base_config(chrome_trace_config, "");
for (const auto& histogram : histograms)
base_config.EnableHistogram(histogram);
chrome_trace_config = base_config.ToString();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceConfig trace_config(
tracing::GetPerfettoConfigWithDataSources(
base_config,
{{ tracing::mojom::kTraceEventDataSourceName }},
privacy_filtering_enabled));
g_tracing_session = perfetto::Tracing::NewTrace();
g_tracing_session->Setup(trace_config);
g_tracing_session->StartBlocking();
// Make sure TraceEventDataSource::StartTracingImpl gets run.
base::RunLoop().RunUntilIdle();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::DataSourceConfig config;
config.mutable_chrome_config()->set_privacy_filtering_enabled(
privacy_filtering_enabled);
config.mutable_chrome_config()->set_trace_config(chrome_trace_config);
TraceEventDataSource::GetInstance()->StartTracingImpl(producer_client(),
config);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void StartMetaDataSource(bool privacy_filtering_enabled = false) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceConfig trace_config(
tracing::GetPerfettoConfigWithDataSources(
base::trace_event::TraceConfig(),
{{ tracing::mojom::kMetaDataSourceName }},
privacy_filtering_enabled));
auto* data_source = &(*trace_config.mutable_data_sources())[0];
data_source->mutable_config()->mutable_chrome_config()->set_trace_config(
"");
g_tracing_session = perfetto::Tracing::NewTrace();
g_tracing_session->Setup(trace_config);
g_tracing_session->StartBlocking();
// Make sure TraceEventMetadataSource::StartTracingImpl gets run.
base::RunLoop().RunUntilIdle();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::DataSourceConfig source_config;
source_config.mutable_chrome_config()->set_privacy_filtering_enabled(
privacy_filtering_enabled);
TraceEventMetadataSource::GetInstance()->StartTracingImpl(producer_client(),
source_config);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void StopMetaDataSource() {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EnsureTraceStopped();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
base::RunLoop wait_for_stop;
TraceEventMetadataSource::GetInstance()->StopTracing(
wait_for_stop.QuitClosure());
wait_for_stop.Run();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
void EnsureTraceStopped() {
if (!g_tracing_session)
return;
StopAndParseTrace();
}
void StopAndParseTrace() {
perfetto::TrackEvent::Flush();
base::RunLoop wait_for_stop;
g_tracing_session->SetOnStopCallback(
[&wait_for_stop] { wait_for_stop.Quit(); });
g_tracing_session->Stop();
wait_for_stop.Run();
std::vector<char> serialized_data = g_tracing_session->ReadTraceBlocking();
g_tracing_session.reset();
perfetto::protos::Trace trace;
EXPECT_TRUE(
trace.ParseFromArray(serialized_data.data(), serialized_data.size()));
for (const auto& packet : trace.packet()) {
// Filter out packets from the tracing service.
if (packet.trusted_packet_sequence_id() == 1)
continue;
if (packet.has_chrome_metadata()) {
metadata_packets_.push_back(packet.chrome_metadata());
}
if (packet.has_chrome_events() &&
packet.chrome_events().metadata_size() > 0) {
legacy_metadata_packets_.push_back(packet.chrome_events());
}
auto proto = std::make_unique<perfetto::protos::TracePacket>();
*proto = packet;
finalized_packets_.push_back(std::move(proto));
}
}
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TestProducerClient* producer_client() { return producer_client_.get(); }
const perfetto::protos::TracePacket* GetFinalizedPacket(size_t packet_index) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EnsureTraceStopped();
EXPECT_GT(finalized_packets_.size(), packet_index);
return finalized_packets_[packet_index].get();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return producer_client()->GetFinalizedPacket(packet_index);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
size_t GetFinalizedPacketCount() {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return finalized_packets_.size();
#else
return producer_client()->GetFinalizedPacketCount();
#endif
}
const perfetto::protos::ChromeMetadataPacket* GetProtoChromeMetadata(
size_t packet_index = 0) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EnsureTraceStopped();
EXPECT_GT(metadata_packets_.size(), packet_index);
return &metadata_packets_[packet_index];
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return producer_client()->GetProtoChromeMetadata(packet_index);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
const google::protobuf::RepeatedPtrField<perfetto::protos::ChromeMetadata>&
GetChromeMetadata(size_t packet_index = 0) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EnsureTraceStopped();
EXPECT_GT(legacy_metadata_packets_.size(), packet_index);
return legacy_metadata_packets_[packet_index].metadata();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return *producer_client()->GetChromeMetadata(packet_index);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void ExpectClockSnapshotAndDefaults(
const perfetto::protos::TracePacket* packet,
uint64_t min_timestamp = 1u) {
// In Perfetto mode, the tracing service emits clock snapshots.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// ClockSnapshot for absolute & incremental microsecond clocks.
ASSERT_TRUE(packet->has_clock_snapshot());
ASSERT_EQ(packet->clock_snapshot().clocks().size(), 3);
EXPECT_EQ(packet->clock_snapshot().clocks()[0].clock_id(),
static_cast<uint32_t>(base::tracing::kTraceClockId));
EXPECT_FALSE(packet->clock_snapshot().clocks()[0].has_unit_multiplier_ns());
EXPECT_FALSE(packet->clock_snapshot().clocks()[0].has_is_incremental());
EXPECT_EQ(packet->clock_snapshot().clocks()[1].clock_id(),
kClockIdAbsolute);
EXPECT_GE(packet->clock_snapshot().clocks()[1].timestamp(), min_timestamp);
EXPECT_EQ(packet->clock_snapshot().clocks()[1].unit_multiplier_ns(), 1000u);
EXPECT_FALSE(packet->clock_snapshot().clocks()[1].has_is_incremental());
EXPECT_EQ(packet->clock_snapshot().clocks()[2].clock_id(),
kClockIdIncremental);
EXPECT_GE(packet->clock_snapshot().clocks()[2].timestamp(), min_timestamp);
EXPECT_EQ(packet->clock_snapshot().clocks()[2].unit_multiplier_ns(), 1000u);
EXPECT_TRUE(packet->clock_snapshot().clocks()[2].is_incremental());
EXPECT_EQ(packet->clock_snapshot().clocks()[1].timestamp(),
packet->clock_snapshot().clocks()[2].timestamp());
EXPECT_GE(packet->clock_snapshot().clocks()[1].timestamp(),
last_timestamp_);
EXPECT_LE(packet->clock_snapshot().clocks()[1].timestamp(),
static_cast<uint64_t>(
TRACE_TIME_TICKS_NOW().since_origin().InMicroseconds()));
last_timestamp_ = packet->clock_snapshot().clocks()[1].timestamp();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Default to incremental clock and thread track.
ASSERT_TRUE(packet->has_trace_packet_defaults());
// TODO(skyostil): Use incremental timestamps with Perfetto.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(packet->trace_packet_defaults().timestamp_clock_id(),
kClockIdIncremental);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ASSERT_TRUE(packet->trace_packet_defaults().has_track_event_defaults());
EXPECT_NE(
packet->trace_packet_defaults().track_event_defaults().track_uuid(),
0u);
// TODO(skyostil): Add support for thread ticks.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (base::ThreadTicks::IsSupported()) {
EXPECT_GT(packet->trace_packet_defaults()
.track_event_defaults()
.extra_counter_track_uuids_size(),
0);
}
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
default_track_uuid_ =
packet->trace_packet_defaults().track_event_defaults().track_uuid();
default_extra_counter_track_uuids_.clear();
for (const auto& uuid : packet->trace_packet_defaults()
.track_event_defaults()
.extra_counter_track_uuids()) {
default_extra_counter_track_uuids_.push_back(uuid);
}
// ClockSnapshot is only emitted when incremental state was reset, and
// thus also always serves as indicator for the state reset to the consumer.
EXPECT_EQ(packet->sequence_flags(),
static_cast<uint32_t>(perfetto::protos::pbzero::TracePacket::
SEQ_INCREMENTAL_STATE_CLEARED));
}
void ExpectThreadTrack(const perfetto::protos::TracePacket* packet,
int thread_id = 0,
uint64_t min_timestamp = 1u,
bool filtering_enabled = false) {
ASSERT_TRUE(packet->has_track_descriptor());
ASSERT_TRUE(packet->track_descriptor().has_thread());
EXPECT_NE(packet->track_descriptor().uuid(), 0u);
EXPECT_NE(packet->track_descriptor().parent_uuid(), 0u);
EXPECT_NE(packet->track_descriptor().thread().pid(), 0);
EXPECT_NE(packet->track_descriptor().thread().tid(), 0);
if (thread_id) {
EXPECT_EQ(packet->track_descriptor().thread().tid(), thread_id);
}
EXPECT_FALSE(
packet->track_descriptor().thread().has_reference_timestamp_us());
EXPECT_FALSE(
packet->track_descriptor().thread().has_reference_thread_time_us());
if (filtering_enabled || thread_id) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_TRUE(packet->track_descriptor().thread().has_thread_name());
#else
EXPECT_FALSE(packet->track_descriptor().thread().has_thread_name());
#endif
} else {
EXPECT_EQ(packet->track_descriptor().thread().thread_name(), kTestThread);
}
if (!thread_id) {
EXPECT_EQ(packet->track_descriptor().uuid(), default_track_uuid_);
if (process_track_uuid_) {
EXPECT_EQ(packet->track_descriptor().parent_uuid(),
process_track_uuid_);
}
// TODO(skyostil): Record the Chrome thread descriptor.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ASSERT_TRUE(packet->track_descriptor().has_chrome_thread());
EXPECT_EQ(perfetto::protos::ChromeThreadDescriptor::THREAD_MAIN,
packet->track_descriptor().chrome_thread().thread_type());
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
} else {
EXPECT_EQ(packet->track_descriptor().uuid(),
perfetto::ThreadTrack::ForThread(thread_id).uuid);
if (process_track_uuid_) {
EXPECT_EQ(packet->track_descriptor().parent_uuid(),
process_track_uuid_);
}
}
EXPECT_EQ(packet->interned_data().event_categories_size(), 0);
EXPECT_EQ(packet->interned_data().event_names_size(), 0);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(packet->sequence_flags(), 0u);
#endif
}
void ExpectThreadTimeCounterTrack(const perfetto::protos::TracePacket* packet,
int thread_id = 0) {
EXPECT_NE(packet->track_descriptor().uuid(), 0u);
EXPECT_NE(packet->track_descriptor().parent_uuid(), 0u);
if (!thread_id) {
EXPECT_EQ(packet->track_descriptor().uuid(),
default_extra_counter_track_uuids_[0]);
EXPECT_EQ(packet->track_descriptor().parent_uuid(), default_track_uuid_);
EXPECT_TRUE(packet->track_descriptor().counter().is_incremental());
last_thread_time_ = 0;
} else {
constexpr uint64_t kAbsoluteThreadTimeTrackUuidBit =
static_cast<uint64_t>(1u) << 33;
EXPECT_EQ(packet->track_descriptor().uuid(),
perfetto::ThreadTrack::ForThread(thread_id).uuid ^
kAbsoluteThreadTimeTrackUuidBit);
EXPECT_EQ(packet->track_descriptor().parent_uuid(),
perfetto::ThreadTrack::ForThread(thread_id).uuid);
EXPECT_FALSE(packet->track_descriptor().counter().is_incremental());
}
EXPECT_EQ(packet->track_descriptor().counter().type(),
perfetto::protos::CounterDescriptor::COUNTER_THREAD_TIME_NS);
EXPECT_EQ(packet->track_descriptor().counter().unit_multiplier(), 1000u);
}
void ExpectProcessTrack(const perfetto::protos::TracePacket* packet,
bool filtering_enabled = false) {
ASSERT_TRUE(packet->has_track_descriptor());
ASSERT_TRUE(packet->track_descriptor().has_process());
// TODO(skyostil): Write Chrome process descriptors.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ASSERT_TRUE(packet->track_descriptor().has_chrome_process());
#endif
EXPECT_NE(packet->track_descriptor().uuid(), 0u);
EXPECT_FALSE(packet->track_descriptor().has_parent_uuid());
process_track_uuid_ = packet->track_descriptor().uuid();
EXPECT_NE(packet->track_descriptor().process().pid(), 0);
if (filtering_enabled) {
EXPECT_FALSE(packet->track_descriptor().process().has_process_name());
} else {
EXPECT_EQ(packet->track_descriptor().process().process_name(),
kTestProcess);
}
// TODO(skyostil): Record the Chrome process descriptor.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ASSERT_TRUE(packet->track_descriptor().has_chrome_process());
EXPECT_EQ(packet->track_descriptor().chrome_process().process_type(),
perfetto::protos::ChromeProcessDescriptor::PROCESS_BROWSER);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// ProcessDescriptors is only emitted when incremental state was reset, and
// thus also always serves as indicator for the state reset to the consumer
// (for the TraceEventDataSource's TraceWriter sequence).
EXPECT_EQ(packet->sequence_flags(),
static_cast<uint32_t>(perfetto::protos::pbzero::TracePacket::
SEQ_INCREMENTAL_STATE_CLEARED));
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void ExpectChildTrack(const perfetto::protos::TracePacket* packet,
uint64_t uuid,
uint64_t parent_uuid) {
EXPECT_EQ(packet->track_descriptor().uuid(), uuid);
EXPECT_EQ(packet->track_descriptor().parent_uuid(), parent_uuid);
}
size_t ExpectStandardPreamble(size_t packet_index = 0,
bool privacy_filtering_enabled = false) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* clock_packet = GetFinalizedPacket(packet_index++);
auto* tt_packet = GetFinalizedPacket(packet_index++);
auto* pt_packet = GetFinalizedPacket(packet_index++);
#else
auto* pt_packet = GetFinalizedPacket(packet_index++);
auto* clock_packet = GetFinalizedPacket(packet_index++);
auto* tt_packet = GetFinalizedPacket(packet_index++);
#endif
ExpectProcessTrack(pt_packet, privacy_filtering_enabled);
ExpectClockSnapshotAndDefaults(clock_packet);
ExpectThreadTrack(tt_packet, /*thread_id=*/0, /*min_timestamp=*/1u,
privacy_filtering_enabled);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Add support for thread time.
if (base::ThreadTicks::IsSupported()) {
auto* ttt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTimeCounterTrack(ttt_packet);
}
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return packet_index;
}
struct StringOrIid {
// implicit
StringOrIid(uint32_t iid) : iid(iid) {}
// implicit
StringOrIid(std::string value) : value(value) {}
// implicit
StringOrIid(const char* value) : value(value) {}
std::string value;
uint32_t iid = 0;
};
void ExpectTraceEvent(const perfetto::protos::TracePacket* packet,
uint32_t category_iid,
StringOrIid name,
char phase,
uint32_t flags = 0,
uint64_t id = 0,
uint64_t absolute_timestamp = 0,
int32_t tid_override = 0,
const perfetto::Track& track = perfetto::Track(),
int64_t explicit_thread_time = 0,
base::Location from_here = base::Location::Current()) {
SCOPED_TRACE(from_here.ToString());
// All TrackEvents need incremental state for delta timestamps / interning.
EXPECT_EQ(packet->sequence_flags(),
static_cast<uint32_t>(perfetto::protos::pbzero::TracePacket::
SEQ_NEEDS_INCREMENTAL_STATE));
EXPECT_TRUE(packet->has_track_event());
if (absolute_timestamp > 0) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Implement delta timestamps.
EXPECT_EQ(packet->timestamp(), absolute_timestamp * 1000);
#else
EXPECT_EQ(packet->timestamp_clock_id(), kClockIdAbsolute);
EXPECT_EQ(packet->timestamp(), absolute_timestamp);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
} else {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Implement delta timestamps.
EXPECT_LE(last_timestamp_, packet->timestamp());
last_timestamp_ = packet->timestamp();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Default to kClockIdIncremental.
EXPECT_FALSE(packet->has_timestamp_clock_id());
EXPECT_TRUE(packet->has_timestamp());
EXPECT_GE(packet->timestamp(), 0u);
EXPECT_LE(last_timestamp_ + packet->timestamp(),
static_cast<uint64_t>(
TRACE_TIME_TICKS_NOW().since_origin().InMicroseconds()));
last_timestamp_ += packet->timestamp();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
if (explicit_thread_time) {
// Absolute thread time counter value for a different thread. Chrome uses
// this for early java events. These events only specify thread time, no
// instruction count.
EXPECT_EQ(packet->track_event().extra_counter_track_uuids_size(), 1);
EXPECT_NE(packet->track_event().extra_counter_track_uuids(0), 0u);
EXPECT_EQ(packet->track_event().extra_counter_values_size(), 1);
EXPECT_EQ(packet->track_event().extra_counter_values(0),
explicit_thread_time);
} else {
EXPECT_EQ(packet->track_event().extra_counter_track_uuids_size(), 0);
if (packet->track_event().extra_counter_values_size()) {
// If the event is for a different thread or track, we shouldn't have
// thread timestamps except for the explicit thread timestamps above.
EXPECT_TRUE(tid_override == 0);
EXPECT_EQ(track.uuid, 0u);
int64_t thread_time_delta =
packet->track_event().extra_counter_values()[0];
EXPECT_LE(last_thread_time_ + thread_time_delta,
TRACE_TIME_TICKS_NOW().since_origin().InMicroseconds());
last_thread_time_ += thread_time_delta;
}
}
if (category_iid > 0) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (packet->track_event().category_iids_size()) {
ASSERT_EQ(packet->track_event().category_iids_size(), 1);
EXPECT_EQ(packet->track_event().category_iids(0), category_iid);
} else {
// Perfetto doesn't use interning for categories that are looked up
// through TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED, but since only
// one category is used in this test suite through this mechanism, we
// can hardcode the expectation here.
EXPECT_EQ(packet->track_event().categories(0), kCategoryGroup);
}
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ASSERT_EQ(packet->track_event().category_iids_size(), 1);
EXPECT_EQ(packet->track_event().category_iids(0), category_iid);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
} else {
EXPECT_EQ(packet->track_event().category_iids_size(), 0);
}
if (name.iid > 0) {
EXPECT_EQ(packet->track_event().name_iid(), name.iid);
} else if (!name.value.empty()) {
EXPECT_EQ(packet->track_event().name(), name.value);
}
TrackEvent::Type track_event_type;
switch (phase) {
case TRACE_EVENT_PHASE_BEGIN:
track_event_type = TrackEvent::TYPE_SLICE_BEGIN;
break;
case TRACE_EVENT_PHASE_END:
track_event_type = TrackEvent::TYPE_SLICE_END;
break;
case TRACE_EVENT_PHASE_INSTANT:
track_event_type = TrackEvent::TYPE_INSTANT;
break;
default:
track_event_type = TrackEvent::TYPE_UNSPECIFIED;
break;
}
if (track_event_type != TrackEvent::TYPE_UNSPECIFIED) {
EXPECT_EQ(packet->track_event().type(), track_event_type);
if (phase == TRACE_EVENT_PHASE_INSTANT) {
switch (flags & TRACE_EVENT_FLAG_SCOPE_MASK) {
case TRACE_EVENT_SCOPE_GLOBAL:
// Use the global track.
EXPECT_EQ(packet->track_event().track_uuid(), 0u);
break;
case TRACE_EVENT_SCOPE_PROCESS:
EXPECT_EQ(packet->track_event().track_uuid(), process_track_uuid_);
break;
case TRACE_EVENT_SCOPE_THREAD:
if (!track) {
// Default to thread track.
EXPECT_FALSE(packet->track_event().has_track_uuid());
} else {
EXPECT_TRUE(packet->track_event().has_track_uuid());
EXPECT_EQ(packet->track_event().track_uuid(), track.uuid);
}
break;
}
} else {
if (!track) {
// Default to thread track.
EXPECT_FALSE(packet->track_event().has_track_uuid());
} else {
EXPECT_TRUE(packet->track_event().has_track_uuid());
EXPECT_EQ(packet->track_event().track_uuid(), track.uuid);
}
}
} else {
// Track is cleared, to fall back on legacy tracks (async ids / thread
// descriptor track).
EXPECT_TRUE(packet->track_event().has_track_uuid());
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Perfetto uses tid/pid overrides only for non-local processes.
EXPECT_EQ(packet->track_event().track_uuid(), track.uuid);
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(packet->track_event().track_uuid(), 0u);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
// We don't emit the legacy event if we don't need it.
bool needs_legacy_event = false;
// These events need a phase.
needs_legacy_event = track_event_type == TrackEvent::TYPE_UNSPECIFIED;
// These events have some flag that is emitted in LegacyEvent.
needs_legacy_event |=
(flags & ~TRACE_EVENT_FLAG_SCOPE_MASK & ~TRACE_EVENT_FLAG_COPY &
~TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP);
if (!needs_legacy_event) {
EXPECT_FALSE(packet->track_event().has_legacy_event());
return;
}
EXPECT_TRUE(packet->track_event().has_legacy_event());
const auto& legacy_event = packet->track_event().legacy_event();
if (track_event_type == TrackEvent::TYPE_UNSPECIFIED) {
EXPECT_EQ(legacy_event.phase(), phase);
}
EXPECT_FALSE(legacy_event.has_instant_event_scope());
switch (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) {
case TRACE_EVENT_FLAG_HAS_ID:
EXPECT_EQ(legacy_event.unscoped_id(), id);
EXPECT_EQ(legacy_event.local_id(), 0u);
EXPECT_EQ(legacy_event.global_id(), 0u);
break;
case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
EXPECT_EQ(legacy_event.unscoped_id(), 0u);
EXPECT_EQ(legacy_event.local_id(), id);
EXPECT_EQ(legacy_event.global_id(), 0u);
break;
case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
EXPECT_EQ(legacy_event.unscoped_id(), 0u);
EXPECT_EQ(legacy_event.local_id(), 0u);
EXPECT_EQ(legacy_event.global_id(), id);
break;
default:
EXPECT_EQ(legacy_event.unscoped_id(), 0u);
EXPECT_EQ(legacy_event.local_id(), 0u);
EXPECT_EQ(legacy_event.global_id(), 0u);
break;
}
EXPECT_EQ(legacy_event.use_async_tts(), flags & TRACE_EVENT_FLAG_ASYNC_TTS);
switch (flags & (TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN)) {
case TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN:
EXPECT_EQ(legacy_event.flow_direction(),
TrackEvent::LegacyEvent::FLOW_INOUT);
break;
case TRACE_EVENT_FLAG_FLOW_OUT:
EXPECT_EQ(legacy_event.flow_direction(),
TrackEvent::LegacyEvent::FLOW_OUT);
break;
case TRACE_EVENT_FLAG_FLOW_IN:
EXPECT_EQ(legacy_event.flow_direction(),
TrackEvent::LegacyEvent::FLOW_IN);
break;
default:
EXPECT_EQ(legacy_event.flow_direction(),
TrackEvent::LegacyEvent::FLOW_UNSPECIFIED);
break;
}
EXPECT_EQ(legacy_event.bind_to_enclosing(),
flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING);
if (track_event_type == TrackEvent::TYPE_UNSPECIFIED) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Perfetto uses tid/pid overrides only for non-local processes.
EXPECT_FALSE(legacy_event.has_tid_override());
EXPECT_EQ(packet->track_event().track_uuid(), track.uuid);
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(legacy_event.tid_override(), tid_override);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
}
void ExpectEventCategories(
const perfetto::protos::TracePacket* packet,
std::initializer_list<std::pair<uint32_t, std::string>> entries,
base::Location from_here = base::Location::Current()) {
// Perfetto doesn't use interning for test-only categories.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ExpectInternedNames(packet->interned_data().event_categories(), entries,
from_here);
#endif
}
void ExpectInternedEventNames(
const perfetto::protos::TracePacket* packet,
std::initializer_list<std::pair<uint32_t, std::string>> entries,
base::Location from_here = base::Location::Current()) {
ExpectInternedNames(packet->interned_data().event_names(), entries,
from_here);
}
void ExpectInternedDebugAnnotationNames(
const perfetto::protos::TracePacket* packet,
std::initializer_list<std::pair<uint32_t, std::string>> entries,
base::Location from_here = base::Location::Current()) {
ExpectInternedNames(packet->interned_data().debug_annotation_names(),
entries, from_here);
}
template <typename T>
void ExpectInternedNames(
const google::protobuf::RepeatedPtrField<T>& field,
std::initializer_list<std::pair<uint32_t, std::string>> expected_entries,
base::Location from_here = base::Location::Current()) {
SCOPED_TRACE(from_here.ToString());
std::vector<std::pair<uint32_t, std::string>> entries;
for (int i = 0; i < field.size(); ++i) {
entries.emplace_back(field[i].iid(), field[i].name());
}
EXPECT_THAT(entries, testing::ElementsAreArray(expected_entries));
}
protected:
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
base::test::TaskEnvironment task_environment_;
base::test::TracingEnvironment tracing_environment_;
std::vector<std::unique_ptr<perfetto::protos::TracePacket>>
finalized_packets_;
std::vector<perfetto::protos::ChromeMetadataPacket> metadata_packets_;
std::vector<perfetto::protos::ChromeEventBundle> legacy_metadata_packets_;
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
std::unique_ptr<TestProducerClient> producer_client_;
uint64_t last_timestamp_ = 0;
int64_t last_thread_time_ = 0;
uint64_t default_track_uuid_ = 0u;
uint64_t process_track_uuid_ = 0u;
std::vector<uint64_t> default_extra_counter_track_uuids_;
std::string old_thread_name_;
std::string old_process_name_;
};
void HasMetadataValue(const perfetto::protos::ChromeMetadata& entry,
const char* value) {
EXPECT_TRUE(entry.has_string_value());
EXPECT_EQ(entry.string_value(), value);
}
void HasMetadataValue(const perfetto::protos::ChromeMetadata& entry,
int value) {
EXPECT_TRUE(entry.has_int_value());
EXPECT_EQ(entry.int_value(), value);
}
void HasMetadataValue(const perfetto::protos::ChromeMetadata& entry,
bool value) {
EXPECT_TRUE(entry.has_bool_value());
EXPECT_EQ(entry.bool_value(), value);
}
void HasMetadataValue(const perfetto::protos::ChromeMetadata& entry,
const base::Value& value) {
EXPECT_TRUE(entry.has_json_value());
absl::optional<base::Value> child_dict =
base::JSONReader::Read(entry.json_value());
EXPECT_EQ(*child_dict, value);
}
template <typename T>
void MetadataHasNamedValue(const google::protobuf::RepeatedPtrField<
perfetto::protos::ChromeMetadata>& metadata,
const char* name,
const T& value) {
for (int i = 0; i < metadata.size(); i++) {
auto& entry = metadata[i];
if (entry.name() == name) {
HasMetadataValue(entry, value);
return;
}
}
NOTREACHED();
}
absl::optional<base::Value> AddJsonMetadataGenerator() {
base::Value metadata(base::Value::Type::DICTIONARY);
metadata.SetIntKey("foo_int", 42);
metadata.SetStringKey("foo_str", "bar");
metadata.SetBoolKey("foo_bool", true);
base::Value child_dict(base::Value::Type::DICTIONARY);
child_dict.SetStringKey("child_str", "child_val");
metadata.SetKey("child_dict", std::move(child_dict));
return metadata;
}
TEST_F(TraceEventDataSourceTest, MetadataGeneratorBeforeTracing) {
auto* metadata_source = TraceEventMetadataSource::GetInstance();
metadata_source->AddGeneratorFunction(
base::BindRepeating(&AddJsonMetadataGenerator));
StartMetaDataSource();
StopMetaDataSource();
auto& metadata = GetChromeMetadata();
EXPECT_EQ(4, metadata.size());
MetadataHasNamedValue(metadata, "foo_int", 42);
MetadataHasNamedValue(metadata, "foo_str", "bar");
MetadataHasNamedValue(metadata, "foo_bool", true);
base::Value child_dict(base::Value::Type::DICTIONARY);
child_dict.SetStringKey("child_str", "child_val");
MetadataHasNamedValue(metadata, "child_dict", child_dict);
}
TEST_F(TraceEventDataSourceTest, MetadataGeneratorWhileTracing) {
auto* metadata_source = TraceEventMetadataSource::GetInstance();
StartMetaDataSource();
metadata_source->AddGeneratorFunction(
base::BindRepeating(&AddJsonMetadataGenerator));
StopMetaDataSource();
auto& metadata = GetChromeMetadata();
EXPECT_EQ(4, metadata.size());
MetadataHasNamedValue(metadata, "foo_int", 42);
MetadataHasNamedValue(metadata, "foo_str", "bar");
MetadataHasNamedValue(metadata, "foo_bool", true);
base::Value child_dict(base::Value::Type::DICTIONARY);
child_dict.SetStringKey("child_str", "child_val");
MetadataHasNamedValue(metadata, "child_dict", child_dict);
}
TEST_F(TraceEventDataSourceTest, MultipleMetadataGenerators) {
auto* metadata_source = TraceEventMetadataSource::GetInstance();
metadata_source->AddGeneratorFunction(base::BindRepeating([]() {
base::Value metadata(base::Value::Type::DICTIONARY);
metadata.SetIntKey("before_int", 42);
return absl::optional<base::Value>(std::move(metadata));
}));
StartMetaDataSource();
metadata_source->AddGeneratorFunction(
base::BindRepeating(&AddJsonMetadataGenerator));
StopMetaDataSource();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto& metadata = GetChromeMetadata(1);
auto& metadata1 = GetChromeMetadata(0);
#else
auto& metadata = GetChromeMetadata(0);
auto& metadata1 = GetChromeMetadata(1);
#endif
EXPECT_EQ(4, metadata.size());
MetadataHasNamedValue(metadata, "foo_int", 42);
MetadataHasNamedValue(metadata, "foo_str", "bar");
MetadataHasNamedValue(metadata, "foo_bool", true);
base::Value child_dict(base::Value::Type::DICTIONARY);
child_dict.SetStringKey("child_str", "child_val");
MetadataHasNamedValue(metadata, "child_dict", child_dict);
EXPECT_EQ(1, metadata1.size());
MetadataHasNamedValue(metadata1, "before_int", 42);
}
#if BUILDFLAG(IS_ANDROID)
TEST_F(TraceEventDataSourceTest,
PackageNameNotRecordedPrivacyFilteringDisabledTraceLogNotSet) {
StartTraceEventDataSource(/* privacy_filtering_enabled = false */);
auto* e_packet = producer_client()->GetFinalizedPacket(0);
ExpectProcessTrack(e_packet /* privacy_filtering_enabled = false */);
ASSERT_FALSE(e_packet->track_descriptor()
.chrome_process()
.has_host_app_package_name());
}
TEST_F(TraceEventDataSourceTest,
PackageNameNotRecordedPrivacyFilteringEnabledTraceLogNotSet) {
StartTraceEventDataSource(true /* privacy_filtering_enabled */);
auto* e_packet = producer_client()->GetFinalizedPacket(0);
ExpectProcessTrack(e_packet, true /* privacy_filtering_enabled */);
ASSERT_FALSE(e_packet->track_descriptor()
.chrome_process()
.has_host_app_package_name());
}
TEST_F(TraceEventDataSourceTest,
PackageNameNotRecordedPrivacyFilteringEnabledTraceLogSet) {
base::trace_event::TraceLog::GetInstance()->SetRecordHostAppPackageName(true);
StartTraceEventDataSource(true /* privacy_filtering_enabled */);
auto* e_packet = producer_client()->GetFinalizedPacket(0);
ExpectProcessTrack(e_packet, true /* privacy_filtering_enabled */);
ASSERT_FALSE(e_packet->track_descriptor()
.chrome_process()
.has_host_app_package_name());
}
TEST_F(TraceEventDataSourceTest,
PackageNameRecordedPrivacyFilteringDisabledTraceLogSet) {
base::trace_event::TraceLog::GetInstance()->SetRecordHostAppPackageName(true);
StartTraceEventDataSource(/* privacy_filtering_enabled = false */);
auto* e_packet = producer_client()->GetFinalizedPacket(0);
ExpectProcessTrack(e_packet /* privacy_filtering_enabled = false */);
ASSERT_TRUE(e_packet->track_descriptor()
.chrome_process()
.has_host_app_package_name());
EXPECT_EQ(
base::android::BuildInfo::GetInstance()->host_package_name(),
e_packet->track_descriptor().chrome_process().host_app_package_name());
}
#endif
TEST_F(TraceEventDataSourceTest, BasicTraceEvent) {
StartTraceEventDataSource();
TRACE_EVENT_BEGIN0(kCategoryGroup, "bar");
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
// producer_client() is null under USE_PERFETTO_CLIENT_LIBRARY.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(producer_client()->empty_finalized_packets_count(), 0);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
TEST_F(TraceEventDataSourceTest, TimestampedTraceEvent) {
StartTraceEventDataSource();
TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
kCategoryGroup, "bar", 42, 4242,
base::TimeTicks() + base::Microseconds(424242));
size_t packet_index = ExpectStandardPreamble();
// Thread track for the overridden tid.
auto* tt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTrack(tt_packet, /*thread_id=*/4242);
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_ASYNC_BEGIN,
TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP | TRACE_EVENT_FLAG_HAS_ID, /*id=*/42u,
/*absolute_timestamp=*/424242, /*tid_override=*/4242,
perfetto::ThreadTrack::ForThread(4242));
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
}
TEST_F(TraceEventDataSourceTest, InstantTraceEvent) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT0(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
}
TEST_F(TraceEventDataSourceTest, InstantTraceEventOnOtherThread) {
StartTraceEventDataSource();
INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(
TRACE_EVENT_PHASE_INSTANT, kCategoryGroup, "bar",
static_cast<uint64_t>(trace_event_internal::kNoId),
/*thread_id=*/1, base::TimeTicks() + base::Microseconds(10),
/*/flags=*/TRACE_EVENT_SCOPE_THREAD);
size_t packet_index = ExpectStandardPreamble();
// Thread track for the overridden tid.
auto* tt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTrack(tt_packet, /*thread_id=*/1);
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT,
TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP |
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_HAS_ID,
/*id=*/0u,
/*absolute_timestamp=*/10, /*tid_override=*/1,
perfetto::ThreadTrack::ForThread(1));
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
}
TEST_F(TraceEventDataSourceTest, EventWithStringArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD,
"arg1_name", "arg1_val", "arg2_name", "arg2_val");
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].name_iid(), 1u);
EXPECT_EQ(annotations[0].string_value(), "arg1_val");
EXPECT_EQ(annotations[1].name_iid(), 2u);
EXPECT_EQ(annotations[1].string_value(), "arg2_val");
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ExpectInternedDebugAnnotationNames(e_packet,
{{1u, "arg1_name"}, {2u, "arg2_name"}});
}
TEST_F(TraceEventDataSourceTest, EventWithCopiedStrings) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar",
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY,
"arg1_name", "arg1_val", "arg2_name", "arg2_val");
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Perfetto uses interning even for copied strings.
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT,
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY);
EXPECT_EQ(annotations[0].name_iid(), 1u);
EXPECT_EQ(annotations[1].name_iid(), 2u);
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name=*/"bar",
TRACE_EVENT_PHASE_INSTANT,
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY);
EXPECT_EQ(e_packet->track_event().name(), "bar");
EXPECT_EQ(annotations[0].name(), "arg1_name");
EXPECT_EQ(annotations[1].name(), "arg2_name");
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(annotations[0].string_value(), "arg1_val");
EXPECT_EQ(annotations[1].string_value(), "arg2_val");
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
}
TEST_F(TraceEventDataSourceTest, EventWithUIntArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
42u, "bar", 4242u);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].uint_value(), 42u);
EXPECT_EQ(annotations[1].uint_value(), 4242u);
}
TEST_F(TraceEventDataSourceTest, EventWithIntArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
42, "bar", 4242);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].int_value(), 42);
EXPECT_EQ(annotations[1].int_value(), 4242);
}
TEST_F(TraceEventDataSourceTest, EventWithBoolArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
true, "bar", false);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_TRUE(annotations[0].has_bool_value());
EXPECT_EQ(annotations[0].bool_value(), true);
EXPECT_TRUE(annotations[1].has_bool_value());
EXPECT_EQ(annotations[1].bool_value(), false);
}
TEST_F(TraceEventDataSourceTest, EventWithDoubleArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
42.42, "bar", 4242.42);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].double_value(), 42.42);
EXPECT_EQ(annotations[1].double_value(), 4242.42);
}
TEST_F(TraceEventDataSourceTest, EventWithPointerArgs) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
reinterpret_cast<void*>(0xBEEF), "bar",
reinterpret_cast<void*>(0xF00D));
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].pointer_value(), static_cast<uintptr_t>(0xBEEF));
EXPECT_EQ(annotations[1].pointer_value(), static_cast<uintptr_t>(0xF00D));
}
TEST_F(TraceEventDataSourceTest, EventWithConvertableArgs) {
StartTraceEventDataSource();
static const char kArgValue1[] = "\"conv_value1\"";
static const char kArgValue2[] = "\"conv_value2\"";
int num_calls = 0;
class Convertable : public base::trace_event::ConvertableToTraceFormat {
public:
explicit Convertable(int* num_calls, const char* arg_value)
: num_calls_(num_calls), arg_value_(arg_value) {}
~Convertable() override = default;
void AppendAsTraceFormat(std::string* out) const override {
(*num_calls_)++;
out->append(arg_value_);
}
private:
raw_ptr<int> num_calls_;
const char* arg_value_;
};
std::unique_ptr<Convertable> conv1(new Convertable(&num_calls, kArgValue1));
std::unique_ptr<Convertable> conv2(new Convertable(&num_calls, kArgValue2));
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD,
"foo_arg1", std::move(conv1), "foo_arg2",
std::move(conv2));
EXPECT_EQ(2, num_calls);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 2);
EXPECT_EQ(annotations[0].legacy_json_value(), kArgValue1);
EXPECT_EQ(annotations[1].legacy_json_value(), kArgValue2);
}
TEST_F(TraceEventDataSourceTest, TaskExecutionEvent) {
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false, "toplevel");
base::TaskAnnotator task_annotator;
for (int i = 0; i < 2; ++i) {
base::PendingTask task;
task.task = base::DoNothing();
task.posted_from =
base::Location("my_func", "my_file", 0, /*program_counter=*/&task);
// TaskAnnotator::RunTask is responsible for emitting the task execution
// event.
task_annotator.RunTask("ThreadControllerImpl::RunTask1", task);
}
size_t packet_index = ExpectStandardPreamble();
size_t category_iid = 1;
size_t name_iid = 1;
size_t posted_from_iid = 1;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Since the "toplevel" category involves events other than the two being
// tested above, we need to skip forward and ignore ones we don't care about
// in the test.
while (true) {
auto* e_packet = GetFinalizedPacket(packet_index);
if (e_packet->interned_data().event_names_size() &&
e_packet->interned_data().event_names()[0].name() ==
"ThreadControllerImpl::RunTask1") {
category_iid = e_packet->track_event().category_iids()[0];
name_iid = e_packet->track_event().name_iid();
posted_from_iid =
e_packet->track_event().task_execution().posted_from_iid();
EXPECT_NE(category_iid, 0u);
EXPECT_NE(name_iid, 0u);
EXPECT_NE(posted_from_iid, 0u);
break;
}
packet_index++;
}
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, category_iid, name_iid, TRACE_EVENT_PHASE_BEGIN);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 0);
EXPECT_EQ(e_packet->track_event().task_execution().posted_from_iid(),
posted_from_iid);
const auto& locations = e_packet->interned_data().source_locations();
EXPECT_EQ(locations.size(), 1);
EXPECT_EQ(locations[0].file_name(), "my_file");
EXPECT_EQ(locations[0].function_name(), "my_func");
// Second event should refer to the same interning entries.
auto* e_packet2 = GetFinalizedPacket(++packet_index);
ExpectTraceEvent(e_packet2, category_iid, name_iid, TRACE_EVENT_PHASE_BEGIN);
EXPECT_EQ(e_packet2->track_event().task_execution().posted_from_iid(),
posted_from_iid);
EXPECT_EQ(e_packet2->interned_data().source_locations().size(), 0);
}
TEST_F(TraceEventDataSourceTest, TaskExecutionEventWithoutFunction) {
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false, "toplevel");
base::TaskAnnotator task_annotator;
base::PendingTask task;
task.task = base::DoNothing();
task.posted_from = base::Location(/*function_name=*/nullptr, "my_file", 0,
/*program_counter=*/&task);
// TaskAnnotator::RunTask is responsible for emitting the task execution
// event.
task_annotator.RunTask("ThreadControllerImpl::RunTask1", task);
size_t packet_index = ExpectStandardPreamble();
size_t category_iid = 1;
size_t name_iid = 1;
size_t posted_from_iid = 1;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Since the "toplevel" category involves events other than the two being
// tested above, we need to skip forward and ignore ones we don't care about
// in the test.
while (true) {
auto* e_packet = GetFinalizedPacket(packet_index);
if (e_packet->interned_data().event_names_size() &&
e_packet->interned_data().event_names()[0].name() ==
"ThreadControllerImpl::RunTask1") {
category_iid = e_packet->track_event().category_iids()[0];
name_iid = e_packet->track_event().name_iid();
posted_from_iid =
e_packet->track_event().task_execution().posted_from_iid();
EXPECT_NE(category_iid, 0u);
EXPECT_NE(name_iid, 0u);
EXPECT_NE(posted_from_iid, 0u);
break;
}
packet_index++;
}
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, category_iid, name_iid, TRACE_EVENT_PHASE_BEGIN,
TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 0);
EXPECT_EQ(e_packet->track_event().task_execution().posted_from_iid(),
posted_from_iid);
const auto& locations = e_packet->interned_data().source_locations();
EXPECT_EQ(locations.size(), 1);
EXPECT_EQ(locations[0].file_name(), "my_file");
EXPECT_FALSE(locations[0].has_function_name());
}
TEST_F(TraceEventDataSourceTest, UpdateDurationOfCompleteEvent) {
StartTraceEventDataSource();
static const char kEventName[] = "bar";
auto* category_group_enabled =
TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(kCategoryGroup);
trace_event_internal::TraceID trace_event_trace_id(
trace_event_internal::kNoId);
// COMPLETE events are split into a BEGIN/END event pair. Adding the event
// writes the BEGIN event immediately.
auto handle = trace_event_internal::AddTraceEventWithThreadIdAndTimestamp(
TRACE_EVENT_PHASE_COMPLETE, category_group_enabled, kEventName,
trace_event_trace_id.scope(), trace_event_trace_id.raw_id(),
/*thread_id=*/1, base::TimeTicks() + base::Microseconds(10),
trace_event_trace_id.id_flags() | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP,
trace_event_internal::kNoId);
// Updating the duration of the event as it goes out of scope results in the
// corresponding END event being written. These END events don't contain any
// event names or categories in the proto format.
base::trace_event::TraceLog::GetInstance()->UpdateTraceEventDurationExplicit(
category_group_enabled, kEventName, handle, /*thread_id=*/1,
/*explicit_timestamps=*/true, base::TimeTicks() + base::Microseconds(30),
base::ThreadTicks(), base::trace_event::ThreadInstructionCount());
// Updating the duration of an event that wasn't added before tracing begun
// will only emit an END event, again without category or name.
handle.event_index = 0;
base::trace_event::TraceLog::GetInstance()->UpdateTraceEventDurationExplicit(
category_group_enabled, "other_event_name", handle, /*thread_id=*/1,
/*explicit_timestamps=*/true, base::TimeTicks() + base::Microseconds(40),
base::ThreadTicks(), base::trace_event::ThreadInstructionCount());
// Complete event for the current thread emits thread time, too.
trace_event_internal::AddTraceEventWithThreadIdAndTimestamp(
TRACE_EVENT_PHASE_COMPLETE, category_group_enabled, kEventName,
trace_event_trace_id.scope(), trace_event_trace_id.raw_id(),
base::PlatformThread::CurrentId(),
base::TimeTicks() + base::Microseconds(10),
trace_event_trace_id.id_flags() | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP,
trace_event_internal::kNoId);
size_t packet_index = ExpectStandardPreamble();
// Thread track for the overridden tid.
auto* tt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTrack(tt_packet, /*thread_id=*/1);
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
b_packet, /*category_iid=*/1u, /*name_iid=*/1u, TRACE_EVENT_PHASE_BEGIN,
TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP | TRACE_EVENT_FLAG_HAS_ID, /*id=*/0u,
/*absolute_timestamp=*/10, /*tid_override=*/1,
perfetto::ThreadTrack::ForThread(1));
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP,
/*id=*/0u,
/*absolute_timestamp=*/30, /*tid_override=*/1,
perfetto::ThreadTrack::ForThread(1));
auto* e2_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e2_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP,
/*id=*/0u,
/*absolute_timestamp=*/40, /*tid_override=*/1,
perfetto::ThreadTrack::ForThread(1));
auto* b2_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
b2_packet, /*category_iid=*/1u, /*name_iid=*/1u, TRACE_EVENT_PHASE_BEGIN,
TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP | TRACE_EVENT_FLAG_HAS_ID, /*id=*/0u,
/*absolute_timestamp=*/10, /*tid_override=*/0);
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Add support for thread time.
#define MAYBE_ExplicitThreadTimeForDifferentThread \
DISABLED_ExplicitThreadTimeForDifferentThread
#else
#define MAYBE_ExplicitThreadTimeForDifferentThread \
ExplicitThreadTimeForDifferentThread
#endif
TEST_F(TraceEventDataSourceTest, MAYBE_ExplicitThreadTimeForDifferentThread) {
StartTraceEventDataSource();
static const char kEventName[] = "bar";
auto* category_group_enabled =
TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(kCategoryGroup);
trace_event_internal::TraceID trace_event_trace_id(
trace_event_internal::kNoId);
// Chrome's main thread buffers and later flushes EarlyJava events on behalf
// of other threads, including explicit thread time values. Such an event
// should add descriptors for the other thread's track and for an absolute
// thread time track for the other thread.
trace_event_internal::AddTraceEventWithThreadIdAndTimestamps(
TRACE_EVENT_PHASE_BEGIN, category_group_enabled, kEventName,
trace_event_trace_id.scope(), trace_event_trace_id.raw_id(),
/*thread_id=*/1, base::TimeTicks() + base::Microseconds(10),
base::ThreadTicks() + base::Microseconds(20),
trace_event_trace_id.id_flags() | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP);
size_t packet_index = ExpectStandardPreamble();
// Thread track for the overridden tid.
auto* tt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTrack(tt_packet, /*thread_id=*/1);
// Absolute thread time track for the overridden tid.
auto* ttt_packet = GetFinalizedPacket(packet_index++);
ExpectThreadTimeCounterTrack(ttt_packet, /*thread_id=*/1);
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
b_packet, /*category_iid=*/1u, /*name_iid=*/1u, TRACE_EVENT_PHASE_BEGIN,
TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP | TRACE_EVENT_FLAG_HAS_ID, /*id=*/0u,
/*absolute_timestamp=*/10, /*tid_override=*/1,
perfetto::ThreadTrack::ForThread(1), /*explicit_thread_time=*/20);
}
TEST_F(TraceEventDataSourceTest, TrackSupportOnBeginAndEndWithLambda) {
StartTraceEventDataSource();
auto track = perfetto::Track(1);
bool begin_called = false;
bool end_called = false;
TRACE_EVENT_BEGIN("browser", "bar", track,
[&](perfetto::EventContext ctx) { begin_called = true; });
EXPECT_TRUE(begin_called);
TRACE_EVENT_END("browser", track,
[&](perfetto::EventContext ctx) { end_called = true; });
EXPECT_TRUE(end_called);
size_t packet_index = ExpectStandardPreamble();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Custom track descriptor.
auto* td_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(td_packet, track.uuid, track.parent_uuid);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(b_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* t_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(t_packet, track.uuid, track.parent_uuid);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
}
TEST_F(TraceEventDataSourceTest, TrackSupportOnBeginAndEnd) {
StartTraceEventDataSource();
auto track = perfetto::Track(1);
TRACE_EVENT_BEGIN("browser", "bar", track);
TRACE_EVENT_END("browser", track);
size_t packet_index = ExpectStandardPreamble();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Custom track descriptor.
auto* td_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(td_packet, track.uuid, track.parent_uuid);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(b_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* t_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(t_packet, track.uuid, track.parent_uuid);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
}
TEST_F(TraceEventDataSourceTest, TrackSupportWithTimestamp) {
StartTraceEventDataSource();
auto timestamp = TRACE_TIME_TICKS_NOW() - base::Microseconds(100);
auto track = perfetto::Track(1);
TRACE_EVENT_BEGIN("browser", "bar", track, timestamp);
size_t packet_index = ExpectStandardPreamble();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Custom track descriptor.
auto* td_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(td_packet, track.uuid, track.parent_uuid);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
b_packet, /*category_iid=*/1u, /*name_iid=*/1u, TRACE_EVENT_PHASE_BEGIN,
/*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/timestamp.since_origin().InMicroseconds(),
/*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
}
TEST_F(TraceEventDataSourceTest, TrackSupportWithTimestampAndLambda) {
StartTraceEventDataSource();
auto timestamp = TRACE_TIME_TICKS_NOW() - base::Microseconds(100);
auto track = perfetto::Track(1);
bool lambda_called = false;
TRACE_EVENT_BEGIN("browser", "bar", track, timestamp,
[&](perfetto::EventContext ctx) { lambda_called = true; });
EXPECT_TRUE(lambda_called);
size_t packet_index = ExpectStandardPreamble();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Custom track descriptor.
auto* td_packet = GetFinalizedPacket(packet_index++);
ExpectChildTrack(td_packet, track.uuid, track.parent_uuid);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(
b_packet, /*category_iid=*/1u, /*name_iid=*/1u, TRACE_EVENT_PHASE_BEGIN,
/*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/timestamp.since_origin().InMicroseconds(),
/*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
}
// TODO(ddrone): following tests should be re-enabled once we figure out how
// tracks on scoped events supposed to work
TEST_F(TraceEventDataSourceTest, DISABLED_TrackSupport) {
StartTraceEventDataSource();
auto track = perfetto::Track(1);
{ TRACE_EVENT("browser", "bar", track); }
size_t packet_index = ExpectStandardPreamble();
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(b_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
}
TEST_F(TraceEventDataSourceTest, DISABLED_TrackSupportWithLambda) {
StartTraceEventDataSource();
auto track = perfetto::Track(1);
bool lambda_called = false;
{
TRACE_EVENT("browser", "bar", track,
[&](perfetto::EventContext ctx) { lambda_called = true; });
}
EXPECT_TRUE(lambda_called);
size_t packet_index = ExpectStandardPreamble();
auto* b_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(b_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
ExpectEventCategories(b_packet, {{1u, "browser"}});
ExpectInternedEventNames(b_packet, {{1u, "bar"}});
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END, /*flags=*/0, /*id=*/0,
/*absolute_timestamp=*/0, /*tid_override=*/0, track);
}
// TODO(eseckler): Add a test with multiple events + same strings (cat, name,
// arg names).
// TODO(eseckler): Add a test with multiple events + same strings with reset.
TEST_F(TraceEventDataSourceTest, InternedStrings) {
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false,
"browser,ui,-*");
size_t packet_index = 0u;
for (size_t i = 0; i < 2; i++) {
TRACE_EVENT_INSTANT1("browser", "e1", TRACE_EVENT_SCOPE_THREAD, "arg1", 4);
TRACE_EVENT_INSTANT1("browser", "e1", TRACE_EVENT_SCOPE_THREAD, "arg1", 2);
TRACE_EVENT_INSTANT1("ui", "e2", TRACE_EVENT_SCOPE_THREAD, "arg2", 1);
packet_index = ExpectStandardPreamble(packet_index);
// First packet needs to emit new interning entries
auto* e_packet1 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet1, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations1 = e_packet1->track_event().debug_annotations();
EXPECT_EQ(annotations1.size(), 1);
EXPECT_EQ(annotations1[0].name_iid(), 1u);
EXPECT_EQ(annotations1[0].int_value(), 4);
ExpectEventCategories(e_packet1, {{1u, "browser"}});
ExpectInternedEventNames(e_packet1, {{1u, "e1"}});
ExpectInternedDebugAnnotationNames(e_packet1, {{1u, "arg1"}});
// Second packet refers to the interning entries from packet 1.
auto* e_packet2 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet2, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations2 = e_packet2->track_event().debug_annotations();
EXPECT_EQ(annotations2.size(), 1);
EXPECT_EQ(annotations2[0].name_iid(), 1u);
EXPECT_EQ(annotations2[0].int_value(), 2);
ExpectEventCategories(e_packet2, {});
ExpectInternedEventNames(e_packet2, {});
ExpectInternedDebugAnnotationNames(e_packet2, {});
// Third packet uses different names, so emits new entries.
auto* e_packet3 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet3, /*category_iid=*/2u, /*name_iid=*/2u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations3 = e_packet3->track_event().debug_annotations();
EXPECT_EQ(annotations3.size(), 1);
EXPECT_EQ(annotations3[0].name_iid(), 2u);
EXPECT_EQ(annotations3[0].int_value(), 1);
ExpectEventCategories(e_packet3, {{2u, "ui"}});
ExpectInternedEventNames(e_packet3, {{2u, "e2"}});
ExpectInternedDebugAnnotationNames(e_packet3, {{2u, "arg2"}});
// Resetting the interning state causes ThreadDescriptor and interning
// entries to be emitted again, with the same interning IDs.
TraceEventDataSource::GetInstance()->ClearIncrementalState();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Perfetto doesn't let us clear the interning state
// explicitly, so not testing that here for now.
if (!i)
break;
#endif
}
}
// TODO(skyostil): Implement post-process event filtering.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TEST_F(TraceEventDataSourceTest, FilteringSimpleTraceEvent) {
StartTraceEventDataSource(/* privacy_filtering_enabled =*/true);
TRACE_EVENT_BEGIN0(kCategoryGroup, "bar");
size_t packet_index = ExpectStandardPreamble(
/*start_packet_index=*/0u,
/*privacy_filtering_enabled=*/true);
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ExpectInternedDebugAnnotationNames(e_packet, {});
}
TEST_F(TraceEventDataSourceTest, FilteringEventWithArgs) {
StartTraceEventDataSource(/* privacy_filtering_enabled =*/true);
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
42, "bar", "string_val");
size_t packet_index = ExpectStandardPreamble(
/*start_packet_index=*/0u,
/*privacy_filtering_enabled=*/true);
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 0);
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ExpectInternedDebugAnnotationNames(e_packet, {});
}
TEST_F(TraceEventDataSourceTest, FilteringEventWithFlagCopy) {
StartTraceEventDataSource(/* privacy_filtering_enabled =*/true);
TRACE_EVENT_INSTANT2(kCategoryGroup, "bar",
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY,
"arg1_name", "arg1_val", "arg2_name", "arg2_val");
TRACE_EVENT_INSTANT2(kCategoryGroup, "javaName",
TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY |
TRACE_EVENT_FLAG_JAVA_STRING_LITERALS,
"arg1_name", "arg1_val", "arg2_name", "arg2_val");
size_t packet_index = ExpectStandardPreamble(
/*start_packet_index=*/0u,
/*privacy_filtering_enabled=*/true);
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, "PRIVACY_FILTERED",
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations.size(), 0);
ExpectEventCategories(e_packet, {{1u, kCategoryGroup}});
ExpectInternedDebugAnnotationNames(e_packet, {});
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, "javaName",
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations2 = e_packet->track_event().debug_annotations();
EXPECT_EQ(annotations2.size(), 0);
ExpectInternedDebugAnnotationNames(e_packet, {});
}
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TEST_F(TraceEventDataSourceTest, FilteringMetadataSource) {
auto* metadata_source = TraceEventMetadataSource::GetInstance();
metadata_source->AddGeneratorFunction(base::BindRepeating([]() {
base::Value metadata(base::Value::Type::DICTIONARY);
metadata.SetIntKey("foo_int", 42);
metadata.SetStringKey("foo_str", "bar");
metadata.SetBoolKey("foo_bool", true);
base::Value child_dict(base::Value::Type::DICTIONARY);
child_dict.SetStringKey("child_str", "child_val");
metadata.SetKey("child_dict", std::move(child_dict));
return absl::optional<base::Value>(std::move(metadata));
}));
StartMetaDataSource(/*privacy_filtering_enabled=*/true);
StopMetaDataSource();
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_TRUE(legacy_metadata_packets_.empty());
#else
EXPECT_FALSE(producer_client()->GetChromeMetadata());
#endif
}
TEST_F(TraceEventDataSourceTest, ProtoMetadataSource) {
auto* metadata_source = TraceEventMetadataSource::GetInstance();
metadata_source->AddGeneratorFunction(base::BindRepeating(
[](perfetto::protos::pbzero::ChromeMetadataPacket* metadata,
bool privacy_filtering_enabled) {
EXPECT_TRUE(privacy_filtering_enabled);
auto* field1 = metadata->set_background_tracing_metadata();
auto* rule = field1->set_triggered_rule();
rule->set_trigger_type(
perfetto::protos::pbzero::BackgroundTracingMetadata::TriggerRule::
MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE);
rule->set_histogram_rule()->set_histogram_min_trigger(123);
}));
StartMetaDataSource(/*privacy_filtering_enabled=*/true);
StopMetaDataSource();
const auto* metadata = GetProtoChromeMetadata();
EXPECT_TRUE(metadata->has_background_tracing_metadata());
const auto& rule = metadata->background_tracing_metadata().triggered_rule();
EXPECT_EQ(perfetto::protos::BackgroundTracingMetadata::TriggerRule::
MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE,
rule.trigger_type());
EXPECT_EQ(123, rule.histogram_rule().histogram_min_trigger());
}
class TraceEventDataSourceNoInterningTest : public TraceEventDataSourceTest {
public:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kPerfettoDisableInterning);
// Reset the data source to pick it up the command line flag.
PerfettoTracedProcess::Get()->ClearDataSourcesForTesting();
TraceEventDataSource::ResetForTesting();
TraceEventDataSourceTest::SetUp();
}
};
// TODO(skyostil): Add support for disabling interning in Perfetto.
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
#define MAYBE_InterningScopedToPackets DISABLED_InterningScopedToPackets
#else
#define MAYBE_InterningScopedToPackets InterningScopedToPackets
#endif
TEST_F(TraceEventDataSourceNoInterningTest, MAYBE_InterningScopedToPackets) {
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false,
"browser,ui,-*");
TRACE_EVENT_INSTANT1("browser", "e1", TRACE_EVENT_SCOPE_THREAD, "arg1", 4);
TRACE_EVENT_INSTANT1("browser", "e1", TRACE_EVENT_SCOPE_THREAD, "arg1", 2);
TRACE_EVENT_INSTANT1("ui", "e2", TRACE_EVENT_SCOPE_THREAD, "arg2", 1);
size_t packet_index = ExpectStandardPreamble();
// First packet needs to emit new interning entries
auto* e_packet1 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet1, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations1 = e_packet1->track_event().debug_annotations();
EXPECT_EQ(annotations1.size(), 1);
EXPECT_EQ(annotations1[0].name_iid(), 1u);
EXPECT_EQ(annotations1[0].int_value(), 4);
ExpectEventCategories(e_packet1, {{1u, "browser"}});
ExpectInternedEventNames(e_packet1, {{1u, "e1"}});
ExpectInternedDebugAnnotationNames(e_packet1, {{1u, "arg1"}});
// Second packet reemits the entries the same way.
auto* e_packet2 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet2, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations2 = e_packet2->track_event().debug_annotations();
EXPECT_EQ(annotations2.size(), 1);
EXPECT_EQ(annotations2[0].name_iid(), 1u);
EXPECT_EQ(annotations2[0].int_value(), 2);
ExpectEventCategories(e_packet1, {{1u, "browser"}});
ExpectInternedEventNames(e_packet1, {{1u, "e1"}});
ExpectInternedDebugAnnotationNames(e_packet1, {{1u, "arg1"}});
// Third packet emits entries with the same IDs but different strings.
auto* e_packet3 = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet3, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
const auto& annotations3 = e_packet3->track_event().debug_annotations();
EXPECT_EQ(annotations3.size(), 1);
EXPECT_EQ(annotations3[0].name_iid(), 1u);
EXPECT_EQ(annotations3[0].int_value(), 1);
ExpectEventCategories(e_packet3, {{1u, "ui"}});
ExpectInternedEventNames(e_packet3, {{1u, "e2"}});
ExpectInternedDebugAnnotationNames(e_packet3, {{1u, "arg2"}});
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// TODO(skyostil): Add support for startup tracing.
#define MAYBE_StartupTracingTimeout DISABLED_StartupTracingTimeout
#else
#define MAYBE_StartupTracingTimeout StartupTracingTimeout
#endif
TEST_F(TraceEventDataSourceTest, MAYBE_StartupTracingTimeout) {
constexpr char kStartupTestEvent1[] = "startup_registry";
auto* data_source = TraceEventDataSource::GetInstance();
PerfettoTracedProcess::Get()->AddDataSource(data_source);
// Start startup tracing with no timeout. This would cause startup tracing to
// abort and flush as soon the current thread can run tasks.
producer_client()->set_startup_tracing_timeout_for_testing(base::TimeDelta());
producer_client()->SetupStartupTracing(
base::trace_event::TraceConfig("foo,-*", ""),
/*privacy_filtering_enabled=*/true);
// The trace event will be added to the SMB for the (soon to be aborted)
// startup tracing session, since the abort didn't run yet.
TRACE_EVENT_BEGIN0(kCategoryGroup, kStartupTestEvent1);
// Run task on background thread to add trace events while aborting and
// starting tracing on the data source. This is to test we do not have any
// crashes when a background thread is trying to create trace writers when
// aborting startup tracing and resetting tracing for the next session.
auto wait_for_start_tracing = std::make_unique<base::WaitableEvent>();
base::WaitableEvent* wait_ptr = wait_for_start_tracing.get();
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT},
base::BindOnce(
[](std::unique_ptr<base::WaitableEvent> wait_for_start_tracing) {
// This event can be hit anytime before startup tracing is
// aborted to after tracing is started using the producer.
TRACE_EVENT_BEGIN0(kCategoryGroup, "maybe_lost");
base::ScopedAllowBaseSyncPrimitivesForTesting allow;
wait_for_start_tracing->Wait();
// This event can be hit while flushing the startup tracing session,
// or when the subsequent tracing session is started or when even
// that one was already stopped.
TRACE_EVENT_BEGIN0(kCategoryGroup, "maybe_lost");
// Make sure that this thread's the trace writer is cleared away.
TraceEventDataSource::FlushCurrentThread();
},
std::move(wait_for_start_tracing)));
// Let tasks run on this thread, which should abort startup tracing and flush
// TraceLog, since the data source hasn't been started by a producer.
producer_client()->OnThreadPoolAvailable();
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
// Start tracing while flush is running.
perfetto::DataSourceConfig config;
data_source->StartTracingImpl(producer_client(), config);
wait_ptr->Signal();
// Verify that the trace buffer does not have the event added to startup
// tracing session.
producer_client()->FlushPacketIfPossible();
std::set<std::string> event_names;
for (const auto& packet : producer_client()->finalized_packets()) {
if (packet->has_interned_data()) {
for (const auto& name : packet->interned_data().event_names()) {
event_names.insert(name.name());
}
}
}
EXPECT_EQ(event_names.end(), event_names.find(kStartupTestEvent1));
// Stop tracing must be called even if tracing is not started to clear the
// pending task.
base::RunLoop wait_for_stop;
data_source->StopTracing(base::BindOnce(
[](const base::RepeatingClosure& quit_closure) { quit_closure.Run(); },
wait_for_stop.QuitClosure()));
wait_for_stop.Run();
// Make sure that the TraceWriter destruction task posted from the ThreadPool
// task's flush is executed.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
RunUntilIdle();
#endif
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOff) {
TRACE_EVENT_BEGIN("log", "LogMessage", [](perfetto::EventContext ctx) {
ADD_FAILURE() << "lambda was called when tracing was off";
});
TRACE_EVENT_END("log", [](perfetto::EventContext ctx) {
ADD_FAILURE() << "lambda was called when tracing was off";
});
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnBegin) {
StartTraceEventDataSource();
bool begin_called = false;
TRACE_EVENT_BEGIN("browser", "bar", [&](perfetto::EventContext ctx) {
begin_called = true;
ctx.event()->set_log_message()->set_body_iid(42);
});
EXPECT_TRUE(begin_called);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnEnd) {
StartTraceEventDataSource();
bool end_called = false;
TRACE_EVENT_END("browser", [&](perfetto::EventContext ctx) {
end_called = true;
ctx.event()->set_log_message()->set_body_iid(42);
});
EXPECT_TRUE(end_called);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnBeginAndEnd) {
StartTraceEventDataSource();
TRACE_EVENT_BEGIN("browser", "bar", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(42);
});
TRACE_EVENT_END("browser", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(84);
});
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 84u);
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnInstant) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT("browser", "bar", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(42);
});
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnScoped) {
StartTraceEventDataSource();
// Use a if statement with no brackets to ensure that the Scoped TRACE_EVENT
// macro properly emits the end event when leaving the single expression
// associated with the if(true) statement.
if (true)
TRACE_EVENT("browser", "bar", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(42);
});
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
EXPECT_FALSE(e_packet->track_event().has_log_message());
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnScopedCapture) {
StartTraceEventDataSource();
bool called = false;
{
TRACE_EVENT("browser", "bar", [&](perfetto::EventContext ctx) {
called = true;
ctx.event()->set_log_message()->set_body_iid(42);
});
}
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
EXPECT_FALSE(e_packet->track_event().has_log_message());
EXPECT_TRUE(called);
}
TEST_F(TraceEventDataSourceTest, TypedArgumentsTracingOnScopedMultipleEvents) {
StartTraceEventDataSource();
{
TRACE_EVENT("browser", "bar", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(42);
});
TRACE_EVENT("browser", "bar", [&](perfetto::EventContext ctx) {
ctx.event()->set_log_message()->set_body_iid(43);
});
}
size_t packet_index = ExpectStandardPreamble();
// The first TRACE_EVENT begin.
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 42u);
// The second TRACE_EVENT begin.
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
TRACE_EVENT_PHASE_BEGIN);
ASSERT_TRUE(e_packet->track_event().has_log_message());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(), 43u);
// The second TRACE_EVENT end.
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
EXPECT_FALSE(e_packet->track_event().has_log_message());
// The first TRACE_EVENT end.
e_packet = GetFinalizedPacket(packet_index++);
ExpectTraceEvent(e_packet, /*category_iid=*/0u, /*name_iid=*/0u,
TRACE_EVENT_PHASE_END);
EXPECT_FALSE(e_packet->track_event().has_log_message());
}
TEST_F(TraceEventDataSourceTest, HistogramSampleTraceConfigEmpty) {
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false,
"-*,disabled-by-default-histogram_samples");
UMA_HISTOGRAM_BOOLEAN("Foo.Bar", true);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(e_packet,
{{1u, TRACE_DISABLED_BY_DEFAULT("histogram_samples")}});
ExpectInternedEventNames(e_packet, {{1u, "HistogramSample"}});
ASSERT_TRUE(e_packet->track_event().has_chrome_histogram_sample());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().sample(), 1u);
ASSERT_TRUE(e_packet->has_interned_data());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().name_iid(),
e_packet->interned_data().histogram_names()[0].iid());
EXPECT_EQ(e_packet->interned_data().histogram_names()[0].name(), "Foo.Bar");
}
TEST_F(TraceEventDataSourceTest, HistogramSampleTraceConfigNotEmpty) {
std::vector<std::string> histograms;
histograms.push_back("Foo1.Bar1");
histograms.push_back("Foo3.Bar3");
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false,
"-*,disabled-by-default-histogram_samples",
std::move(histograms));
UMA_HISTOGRAM_BOOLEAN("Foo1.Bar1", true);
UMA_HISTOGRAM_BOOLEAN("Foo2.Bar2", true);
UMA_HISTOGRAM_BOOLEAN("Foo3.Bar3", true);
base::RunLoop().RunUntilIdle();
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(e_packet,
{{1u, TRACE_DISABLED_BY_DEFAULT("histogram_samples")}});
ExpectInternedEventNames(e_packet, {{1u, "HistogramSample"}});
ASSERT_TRUE(e_packet->track_event().has_chrome_histogram_sample());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().name_hash(),
base::HashMetricName("Foo1.Bar1"));
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().sample(), 1u);
ASSERT_TRUE(e_packet->has_interned_data());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().name_iid(),
e_packet->interned_data().histogram_names()[0].iid());
EXPECT_EQ(e_packet->interned_data().histogram_names()[0].name(), "Foo1.Bar1");
e_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(e_packet, {});
ExpectInternedEventNames(e_packet, {});
ASSERT_TRUE(e_packet->track_event().has_chrome_histogram_sample());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().name_hash(),
base::HashMetricName("Foo3.Bar3"));
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().sample(), 1u);
ASSERT_TRUE(e_packet->has_interned_data());
EXPECT_EQ(e_packet->track_event().chrome_histogram_sample().name_iid(),
e_packet->interned_data().histogram_names()[0].iid());
EXPECT_EQ(e_packet->interned_data().histogram_names()[0].name(), "Foo3.Bar3");
EXPECT_EQ(packet_index, GetFinalizedPacketCount());
}
TEST_F(TraceEventDataSourceTest, UserActionEvent) {
base::SetRecordActionTaskRunner(base::ThreadTaskRunnerHandle::Get());
StartTraceEventDataSource(/*privacy_filtering_enabled=*/false,
"-*,disabled-by-default-user_action_samples");
// Wait for registering callback on current thread.
base::RunLoop().RunUntilIdle();
base::RecordAction(base::UserMetricsAction("Test_Action"));
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(
e_packet, {{1u, TRACE_DISABLED_BY_DEFAULT("user_action_samples")}});
ExpectInternedEventNames(e_packet, {{1u, "UserAction"}});
ASSERT_TRUE(e_packet->track_event().has_chrome_user_event());
EXPECT_EQ(e_packet->track_event().chrome_user_event().action_hash(),
base::HashMetricName("Test_Action"));
}
namespace {
struct InternedLogMessageBody
: public perfetto::TrackEventInternedDataIndex<
InternedLogMessageBody,
perfetto::protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
std::string> {
static void Add(perfetto::protos::pbzero::InternedData* interned_data,
size_t iid,
const std::string& body) {
auto* msg = interned_data->add_log_message_body();
msg->set_iid(iid);
msg->set_body(body);
}
};
} // namespace
TEST_F(TraceEventDataSourceTest, TypedEventInterning) {
StartTraceEventDataSource();
{
TRACE_EVENT("browser", "bar", [&](perfetto::EventContext ctx) {
size_t iid = InternedLogMessageBody::Get(&ctx, "Hello interned world!");
ctx.event()->set_log_message()->set_body_iid(iid);
});
}
size_t packet_index = ExpectStandardPreamble();
auto* e_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(e_packet, {{1u, "browser"}});
ExpectInternedEventNames(e_packet, {{1u, "bar"}});
ASSERT_TRUE(e_packet->track_event().has_log_message());
ASSERT_TRUE(e_packet->has_interned_data());
EXPECT_EQ(e_packet->track_event().log_message().body_iid(),
e_packet->interned_data().log_message_body()[0].iid());
ASSERT_EQ("Hello interned world!",
e_packet->interned_data().log_message_body()[0].body());
}
TEST_F(TraceEventDataSourceTest, TypedAndUntypedEventsWithDebugAnnotations) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT1("browser", "Event1", TRACE_EVENT_SCOPE_THREAD, "arg1",
1);
TRACE_EVENT_INSTANT("browser", "Event2", "arg2", 2);
size_t packet_index = ExpectStandardPreamble();
auto* e_packet1 = GetFinalizedPacket(packet_index++);
ExpectEventCategories(e_packet1, {{1u, "browser"}});
ExpectInternedEventNames(e_packet1, {{1u, "Event1"}});
ExpectInternedDebugAnnotationNames(e_packet1, {{1u, "arg1"}});
auto* e_packet2 = GetFinalizedPacket(packet_index++);
ExpectInternedEventNames(e_packet2, {{2u, "Event2"}});
ExpectInternedDebugAnnotationNames(e_packet2, {{2u, "arg2"}});
}
TEST_F(TraceEventDataSourceTest, EmptyPacket) {
StartTraceEventDataSource();
TRACE_EVENT_INSTANT("browser", "Event");
PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
// Second empty packet should not be emitted because the prior packet was
// already an empty packet.
PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
size_t packet_index = ExpectStandardPreamble();
auto* instant_packet = GetFinalizedPacket(packet_index++);
ExpectEventCategories(instant_packet, {{1u, "browser"}});
ExpectInternedEventNames(instant_packet, {{1u, "Event"}});
// The client library employs a real tracing service, which skips empty packets
// when reading from the trace buffer. The functionality of the
// PERFETTO_INTERNAL_ADD_EMPTY_EVENT macro is instead tested in Perfetto's API
// integration tests.
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
EXPECT_EQ(producer_client()->empty_finalized_packets_count(), 1);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
// TODO(eseckler): Add startup tracing unittests.
} // namespace
} // namespace tracing