blob: 9dfc8dd08e8bc88782f9d73691e824934a63ab1c [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 <fidl/fuchsia.tracing.controller/cpp/fidl.h>
#include <fidl/fuchsia.tracing/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/socket.h>
#include <stdlib.h>
#include <gtest/gtest.h>
#include <trace-test-utils/read_records.h>
TEST(PerfettoBridgeIntegrationTest, Init) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
zx::result client_end = component::Connect<fuchsia_tracing_controller::Provisioner>();
ASSERT_TRUE(client_end.is_ok());
const fidl::SyncClient client{std::move(*client_end)};
// Wait for perfetto bridge to attach
for (unsigned retries = 0; retries < 5; retries++) {
fidl::Result<fuchsia_tracing_controller::Provisioner::GetProviders> providers =
client->GetProviders();
ASSERT_TRUE(providers.is_ok());
if (providers->providers().size() == 1) {
break;
}
sleep(1);
}
zx::socket in_socket;
zx::socket outgoing_socket;
ASSERT_EQ(zx::socket::create(0u, &in_socket, &outgoing_socket), ZX_OK);
const fuchsia_tracing_controller::TraceConfig config{{
.buffer_size_megabytes_hint = uint32_t{4},
.buffering_mode = fuchsia_tracing::BufferingMode::kOneshot,
}};
fidl::Result<fuchsia_tracing_controller::Provisioner::GetProviders> providers =
client->GetProviders();
// We should have a single provider: perfetto
ASSERT_TRUE(providers.is_ok());
ASSERT_EQ(providers->providers().size(), size_t{1});
ASSERT_EQ(providers->providers().begin()->name(), "perfetto");
auto endpoints = fidl::Endpoints<fuchsia_tracing_controller::Session>::Create();
auto init_response =
client->InitializeTracing({std::move(endpoints.server), config, std::move(outgoing_socket)});
ASSERT_TRUE(init_response.is_ok());
const fidl::SyncClient controller_client{std::move(endpoints.client)};
controller_client->StartTracing({});
loop.Run(zx::deadline_after(zx::sec(1)));
controller_client->StopTracing({{.write_results = true}});
size_t num_perfetto_bytes = 0;
trace::TraceReader::RecordConsumer handle_perfetto_blob =
[&num_perfetto_bytes](trace::Record record) {
if (record.type() == trace::RecordType::kBlob &&
record.GetBlob().type == TRACE_BLOB_TYPE_PERFETTO) {
num_perfetto_bytes += record.GetBlob().blob_size;
}
};
trace::TraceReader reader(std::move(handle_perfetto_blob), [](fbl::String&&) {});
uint8_t buffer[ZX_PAGE_SIZE];
uint8_t* buffer_base = buffer;
size_t leftover_bytes = 0;
for (;;) {
size_t actual = 0;
zx_status_t read_result =
in_socket.read(0, buffer_base, sizeof(buffer) - leftover_bytes, &actual);
EXPECT_TRUE(read_result == ZX_OK || read_result == ZX_ERR_SHOULD_WAIT);
if (read_result == ZX_ERR_SHOULD_WAIT || actual == 0) {
EXPECT_EQ(leftover_bytes, size_t{0});
break;
}
trace::Chunk data{reinterpret_cast<uint64_t*>(buffer), (actual + leftover_bytes) >> 3};
reader.ReadRecords(data);
// trace::Chunk only deals in full words so we may have some bytes we rounded off
size_t unchunked_bytes = (actual + leftover_bytes) % 8;
// We may have leftover data in the chunk if our read boundary was not on a record boundary.
// Copy it back into the buffer for the next round.
leftover_bytes = (data.remaining_words() * 8) + unchunked_bytes;
if (leftover_bytes != 0) {
memcpy(buffer, buffer + data.current_byte_offset(), leftover_bytes);
}
buffer_base = buffer + leftover_bytes;
}
EXPECT_GT(num_perfetto_bytes, size_t{30000});
loop.RunUntilIdle();
}
// TODO(https://fxbug.dev/42071555): Add a test to cover calls to Controller::GetKnownCategories once
// perfetto_producer.cc has a working backend. This would test the plumbing all the
// way from perfetto to ffx trace.
// TEST(PerfettoBridgeIntegrationTest, GetKnownCategories)