123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- // 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 <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/files/file_util.h"
- #include "base/json/json_reader.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/test/bind.h"
- #include "base/test/test_simple_task_runner.h"
- #include "base/threading/thread.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/trace_event/trace_config.h"
- #include "build/build_config.h"
- #include "mojo/public/cpp/bindings/receiver.h"
- #include "mojo/public/cpp/bindings/remote.h"
- #include "services/tracing/perfetto/test_utils.h"
- #include "services/tracing/public/cpp/perfetto/perfetto_config.h"
- #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
- #include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h"
- #include "services/tracing/public/cpp/traced_process_impl.h"
- #include "services/tracing/public/mojom/perfetto_service.mojom.h"
- #include "services/tracing/public/mojom/traced_process.mojom.h"
- #include "services/tracing/public/mojom/tracing_service.mojom.h"
- #include "services/tracing/tracing_service.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
- #include "third_party/perfetto/include/perfetto/tracing/data_source.h"
- #include "third_party/perfetto/include/perfetto/tracing/tracing.h"
- #include "third_party/perfetto/protos/perfetto/common/trace_stats.pb.h"
- #include "third_party/perfetto/protos/perfetto/trace/test_event.pbzero.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"
- namespace tracing {
- class TracingServiceTest : public TracingUnitTest {
- public:
- TracingServiceTest() : service_(&perfetto_service_) {}
- TracingServiceTest(const TracingServiceTest&) = delete;
- TracingServiceTest& operator=(const TracingServiceTest&) = delete;
- void SetUp() override {
- TracingUnitTest::SetUp();
- perfetto_service()->SetActiveServicePidsInitialized();
- }
- void TearDown() override { TracingUnitTest::TearDown(); }
- protected:
- PerfettoService* perfetto_service() { return &perfetto_service_; }
- mojom::TracingService* service() { return &service_; }
- void EnableClientApiConsumer() {
- // Tell PerfettoTracedProcess how to connect to the service. This enables
- // the consumer part of the client API.
- static mojom::TracingService* s_service;
- s_service = service();
- auto factory = []() -> mojom::TracingService& { return *s_service; };
- PerfettoTracedProcess::Get()->SetConsumerConnectionFactory(
- factory, base::SequencedTaskRunnerHandle::Get());
- }
- void EnableClientApiProducer() {
- // Connect the producer part of the client API. AddClient() will end up
- // calling TracedProcessImpl::ConnectToTracingService(), which will in turn
- // route the PerfettoService interface to the client API backend.
- mojo::PendingRemote<tracing::mojom::TracedProcess> traced_process_remote;
- traced_process_receiver_ =
- std::make_unique<mojo::Receiver<tracing::mojom::TracedProcess>>(
- tracing::TracedProcessImpl::GetInstance());
- traced_process_receiver_->Bind(
- traced_process_remote.InitWithNewPipeAndPassReceiver());
- auto client_info = mojom::ClientInfo::New(base::GetCurrentProcId(),
- std::move(traced_process_remote));
- service()->AddClient(std::move(client_info));
- perfetto_service()->SetActiveServicePidsInitialized();
- }
- static size_t CountTestPackets(const char* data, size_t length) {
- if (!length)
- return 0;
- size_t test_packet_count = 0;
- perfetto::protos::Trace trace;
- EXPECT_TRUE(trace.ParseFromArray(data, length));
- for (const auto& packet : trace.packet()) {
- if (packet.has_for_testing()) {
- EXPECT_EQ(kPerfettoTestString, packet.for_testing().str());
- test_packet_count++;
- }
- }
- return test_packet_count;
- }
- size_t ReadAndCountTestPackets(perfetto::TracingSession& session) {
- size_t test_packet_count = 0;
- base::RunLoop wait_for_data_loop;
- session.ReadTrace(
- [&wait_for_data_loop, &test_packet_count](
- perfetto::TracingSession::ReadTraceCallbackArgs args) {
- test_packet_count += CountTestPackets(args.data, args.size);
- if (!args.has_more)
- wait_for_data_loop.Quit();
- });
- wait_for_data_loop.Run();
- return test_packet_count;
- }
- private:
- PerfettoService perfetto_service_;
- TracingService service_;
- std::unique_ptr<mojo::Receiver<tracing::mojom::TracedProcess>>
- traced_process_receiver_;
- };
- class TestTracingClient : public mojom::TracingSessionClient {
- public:
- void StartTracing(mojom::TracingService* service,
- base::OnceClosure on_tracing_enabled) {
- service->BindConsumerHost(consumer_host_.BindNewPipeAndPassReceiver());
- perfetto::TraceConfig perfetto_config =
- tracing::GetDefaultPerfettoConfig(base::trace_event::TraceConfig(""),
- /*privacy_filtering_enabled=*/false);
- consumer_host_->EnableTracing(
- tracing_session_host_.BindNewPipeAndPassReceiver(),
- receiver_.BindNewPipeAndPassRemote(), std::move(perfetto_config),
- base::File());
- tracing_session_host_->RequestBufferUsage(
- base::BindOnce([](base::OnceClosure on_response, bool, float,
- bool) { std::move(on_response).Run(); },
- std::move(on_tracing_enabled)));
- }
- void StopTracing(base::OnceClosure on_tracing_stopped) {
- tracing_disabled_callback_ = std::move(on_tracing_stopped);
- tracing_session_host_->DisableTracing();
- }
- // tracing::mojom::TracingSessionClient implementation:
- void OnTracingEnabled() override {}
- void OnTracingDisabled(bool) override {
- std::move(tracing_disabled_callback_).Run();
- }
- private:
- base::OnceClosure tracing_disabled_callback_;
- mojo::Remote<mojom::ConsumerHost> consumer_host_;
- mojo::Remote<mojom::TracingSessionHost> tracing_session_host_;
- mojo::Receiver<mojom::TracingSessionClient> receiver_{this};
- };
- TEST_F(TracingServiceTest, TracingServiceInstantiate) {
- TestTracingClient tracing_client;
- base::RunLoop tracing_started;
- tracing_client.StartTracing(service(), tracing_started.QuitClosure());
- tracing_started.Run();
- base::RunLoop tracing_stopped;
- tracing_client.StopTracing(tracing_stopped.QuitClosure());
- tracing_stopped.Run();
- }
- TEST_F(TracingServiceTest, PerfettoClientConsumer) {
- // Set up API bindings.
- EnableClientApiConsumer();
- // Register a mock producer with an in-process Perfetto service.
- auto pid = 123;
- size_t kNumPackets = 10;
- base::RunLoop wait_for_start;
- base::RunLoop wait_for_registration;
- std::unique_ptr<MockProducer> producer = std::make_unique<MockProducer>(
- std::string("org.chromium-") + base::NumberToString(pid),
- "com.example.mock_data_source", perfetto_service(),
- wait_for_registration.QuitClosure(), wait_for_start.QuitClosure(),
- kNumPackets);
- wait_for_registration.Run();
- // Start a tracing session using the client API.
- auto session =
- perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
- perfetto::TraceConfig perfetto_config;
- perfetto_config.add_buffers()->set_size_kb(1024);
- auto* ds_cfg = perfetto_config.add_data_sources()->mutable_config();
- ds_cfg->set_name("com.example.mock_data_source");
- session->Setup(perfetto_config);
- session->Start();
- wait_for_start.Run();
- // Stop the session and wait for it to stop. Note that we can't use the
- // blocking API here because the service runs on the current sequence.
- base::RunLoop wait_for_stop_loop;
- session->SetOnStopCallback(
- [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); });
- session->Stop();
- wait_for_stop_loop.Run();
- // Verify tracing session statistics.
- base::RunLoop wait_for_stats_loop;
- perfetto::protos::TraceStats stats;
- auto stats_callback =
- [&wait_for_stats_loop,
- &stats](perfetto::TracingSession::GetTraceStatsCallbackArgs args) {
- EXPECT_TRUE(args.success);
- EXPECT_TRUE(stats.ParseFromArray(args.trace_stats_data.data(),
- args.trace_stats_data.size()));
- wait_for_stats_loop.Quit();
- };
- session->GetTraceStats(std::move(stats_callback));
- wait_for_stats_loop.Run();
- EXPECT_EQ(1024u * 1024u, stats.buffer_stats(0).buffer_size());
- EXPECT_GT(stats.buffer_stats(0).bytes_written(), 0u);
- EXPECT_EQ(0u, stats.buffer_stats(0).trace_writer_packet_loss());
- // Read and verify the data.
- EXPECT_EQ(kNumPackets, ReadAndCountTestPackets(*session));
- }
- TEST_F(TracingServiceTest, PerfettoClientConsumerLegacyJson) {
- // Set up API bindings.
- EnableClientApiConsumer();
- // Start a tracing session with legacy JSON exporting.
- auto session =
- perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
- perfetto::TraceConfig perfetto_config = GetDefaultPerfettoConfig(
- base::trace_event::TraceConfig(), /*privacy_filtering_enabled=*/false,
- /*convert_to_legacy_json=*/true);
- session->Setup(perfetto_config);
- session->Start();
- // Stop the session and wait for it to stop. Note that we can't use the
- // blocking API here because the service runs on the current sequence.
- base::RunLoop wait_for_stop_loop;
- session->SetOnStopCallback(
- [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); });
- session->Stop();
- wait_for_stop_loop.Run();
- // Read and verify the JSON data.
- base::RunLoop wait_for_data_loop;
- TracePacketTokenizer tokenizer;
- std::string json;
- session->ReadTrace([&wait_for_data_loop, &tokenizer, &json](
- perfetto::TracingSession::ReadTraceCallbackArgs args) {
- if (args.size) {
- auto packets = tokenizer.Parse(
- reinterpret_cast<const uint8_t*>(args.data), args.size);
- for (const auto& packet : packets) {
- for (const auto& slice : packet.slices()) {
- json += std::string(reinterpret_cast<const char*>(slice.start),
- slice.size);
- }
- }
- }
- if (!args.has_more)
- wait_for_data_loop.Quit();
- });
- wait_for_data_loop.Run();
- DCHECK(!tokenizer.has_more());
- absl::optional<base::Value> result = base::JSONReader::Read(json);
- ASSERT_TRUE(result.has_value());
- EXPECT_TRUE(result->FindKey("traceEvents"));
- }
- #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
- class CustomDataSource : public perfetto::DataSource<CustomDataSource> {
- public:
- struct Events {
- base::RunLoop wait_for_setup_loop;
- base::RunLoop wait_for_start_loop;
- base::RunLoop wait_for_stop_loop;
- };
- static void set_events(Events* events) { events_ = events; }
- void OnSetup(const SetupArgs&) override {
- events_->wait_for_setup_loop.Quit();
- }
- void OnStart(const StartArgs&) override {
- events_->wait_for_start_loop.Quit();
- }
- void OnStop(const StopArgs&) override { events_->wait_for_stop_loop.Quit(); }
- private:
- static Events* events_;
- };
- CustomDataSource::Events* CustomDataSource::events_;
- TEST_F(TracingServiceTest, PerfettoClientProducer) {
- // Set up API bindings.
- EnableClientApiConsumer();
- EnableClientApiProducer();
- perfetto::DataSourceDescriptor dsd;
- dsd.set_name("com.example.custom_data_source");
- CustomDataSource::Events ds_events;
- CustomDataSource::set_events(&ds_events);
- CustomDataSource::Register(dsd);
- // Start a tracing session using the client API.
- auto session =
- perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
- perfetto::TraceConfig perfetto_config;
- perfetto_config.add_buffers()->set_size_kb(1024);
- auto* ds_cfg = perfetto_config.add_data_sources()->mutable_config();
- ds_cfg->set_name("com.example.custom_data_source");
- session->Setup(perfetto_config);
- session->Start();
- ds_events.wait_for_setup_loop.Run();
- ds_events.wait_for_start_loop.Run();
- // Write more data to check the commit flow works too.
- size_t kNumPackets = 1000;
- CustomDataSource::Trace([kNumPackets](CustomDataSource::TraceContext ctx) {
- for (size_t i = 0; i < kNumPackets / 2; i++) {
- ctx.NewTracePacket()->set_for_testing()->set_str(
- tracing::kPerfettoTestString);
- }
- ctx.Flush();
- });
- // Write half of the data from another thread to check TLS is hooked up
- // properly.
- base::Thread thread("TestThread");
- thread.Start();
- thread.task_runner()->PostTask(
- FROM_HERE, base::BindLambdaForTesting([kNumPackets] {
- CustomDataSource::Trace(
- [kNumPackets](CustomDataSource::TraceContext ctx) {
- for (size_t i = 0; i < kNumPackets / 2; i++) {
- ctx.NewTracePacket()->set_for_testing()->set_str(
- tracing::kPerfettoTestString);
- }
- ctx.Flush();
- });
- }));
- thread.Stop();
- // Stop the session and wait for it to stop. Note that we can't use the
- // blocking variants here because the service runs on the current sequence.
- base::RunLoop wait_for_stop_loop;
- session->SetOnStopCallback(
- [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); });
- session->Stop();
- ds_events.wait_for_stop_loop.Run();
- wait_for_stop_loop.Run();
- // Read and verify the data.
- EXPECT_EQ(kNumPackets, ReadAndCountTestPackets(*session));
- }
- #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
- #if !BUILDFLAG(IS_WIN)
- // TODO(crbug.com/1158482): Support tracing to file on Windows.
- TEST_F(TracingServiceTest, TraceToFile) {
- // Set up API bindings.
- EnableClientApiConsumer();
- // Register a mock producer with an in-process Perfetto service.
- auto pid = 123;
- size_t kNumPackets = 10;
- base::RunLoop wait_for_start;
- base::RunLoop wait_for_registration;
- std::unique_ptr<MockProducer> producer = std::make_unique<MockProducer>(
- std::string("org.chromium-") + base::NumberToString(pid),
- "com.example.mock_data_source", perfetto_service(),
- wait_for_registration.QuitClosure(), wait_for_start.QuitClosure(),
- kNumPackets);
- wait_for_registration.Run();
- base::FilePath output_file_path;
- ASSERT_TRUE(base::CreateTemporaryFile(&output_file_path));
- base::File output_file;
- output_file.Initialize(output_file_path,
- base::File::FLAG_OPEN | base::File::FLAG_WRITE);
- // Start a tracing session using the client API.
- auto session =
- perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
- perfetto::TraceConfig perfetto_config;
- perfetto_config.add_buffers()->set_size_kb(1024);
- auto* ds_cfg = perfetto_config.add_data_sources()->mutable_config();
- ds_cfg->set_name("com.example.mock_data_source");
- session->Setup(perfetto_config, output_file.TakePlatformFile());
- session->Start();
- wait_for_start.Run();
- // Stop the session and wait for it to stop. Note that we can't use the
- // blocking API here because the service runs on the current sequence.
- base::RunLoop wait_for_stop_loop;
- session->SetOnStopCallback(
- [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); });
- session->Stop();
- wait_for_stop_loop.Run();
- // Read and verify the data.
- std::string trace;
- base::ScopedAllowBlockingForTesting allow_blocking;
- ASSERT_TRUE(base::ReadFileToString(output_file_path, &trace));
- EXPECT_EQ(kNumPackets, CountTestPackets(trace.data(), trace.length()));
- }
- #endif
- } // namespace tracing
|