#include "services/tracing/perfetto/consumer_host.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/extend.h"
#include "base/containers/span.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "base/trace_event/trace_config.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "services/tracing/perfetto/perfetto_service.h"
#include "services/tracing/perfetto/test_utils.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/core/trace_config.h"
#include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
namespace tracing {
const std::string kDataSourceName = "track_event";
constexpr base::ProcessId kProducerPid = 1234;
using testing::_;
class ThreadedPerfettoService : public mojom::TracingSessionClient {
public:
ThreadedPerfettoService()
: task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
base::WithBaseSyncPrimitives(),
base::TaskPriority::BEST_EFFORT})) {
perfetto_service_ = std::make_unique<PerfettoService>(task_runner_);
tracing_session_host_ =
std::make_unique<mojo::Remote<mojom::TracingSessionHost>>();
base::RunLoop wait_for_construct;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::CreateConsumerOnSequence,
base::Unretained(this)),
wait_for_construct.QuitClosure());
wait_for_construct.Run();
}
~ThreadedPerfettoService() override {
if (receiver_) {
task_runner_->DeleteSoon(FROM_HERE, std::move(receiver_));
}
task_runner_->DeleteSoon(FROM_HERE, std::move(producer_));
if (consumer_) {
task_runner_->DeleteSoon(FROM_HERE, std::move(consumer_));
}
if (tracing_session_host_)
task_runner_->DeleteSoon(FROM_HERE, std::move(tracing_session_host_));
{
base::RunLoop wait_for_destruction;
task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
wait_for_destruction.QuitClosure());
wait_for_destruction.Run();
}
{
base::RunLoop wait_for_destruction;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<PerfettoService> service) { service.reset(); },
std::move(perfetto_service_)),
wait_for_destruction.QuitClosure());
wait_for_destruction.Run();
}
{
base::RunLoop wait_for_destruction;
PerfettoTracedProcess::GetTaskRunner()
->PostTaskAndReply(FROM_HERE, base::DoNothing(),
wait_for_destruction.QuitClosure());
wait_for_destruction.Run();
}
}
void OnTracingEnabled() override {
EXPECT_FALSE(tracing_enabled_);
tracing_enabled_ = true;
}
void OnTracingDisabled(bool) override {}
void CreateProducer() {
base::RunLoop wait_for_producer;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::CreateProducerOnSequence,
base::Unretained(this),
wait_for_producer.QuitClosure()));
wait_for_producer.Run();
}
std::unique_ptr<perfetto::TraceWriter> RegisterDataSource(
const std::string& data_source_name) {
base::RunLoop wait_for_datasource;
std::unique_ptr<perfetto::TraceWriter> trace_writer;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::RegisterDataSourceOnSequence,
base::Unretained(this), data_source_name,
wait_for_datasource.QuitClosure()));
wait_for_datasource.Run();
return trace_writer;
}
std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter(
const std::string& data_source_name) {
base::RunLoop wait_for_datasource;
std::unique_ptr<perfetto::TraceWriter> trace_writer;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::CreateTraceWriterOnSequence,
base::Unretained(this), data_source_name,
base::BindLambdaForTesting(
[&](std::unique_ptr<perfetto::TraceWriter> writer) {
trace_writer = std::move(writer);
wait_for_datasource.Quit();
})));
wait_for_datasource.Run();
return trace_writer;
}
perfetto::DataSourceConfig GetDataSourceConfig(
const std::string& data_source_name) {
base::RunLoop wait_for_datasource;
perfetto::DataSourceConfig ds_config;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&ThreadedPerfettoService::GetDataSourceConfigOnSequence,
base::Unretained(this), data_source_name,
base::BindLambdaForTesting([&](perfetto::DataSourceConfig config) {
ds_config = config;
wait_for_datasource.Quit();
})));
wait_for_datasource.Run();
return ds_config;
}
void CreateConsumerOnSequence() {
consumer_ = std::make_unique<ConsumerHost>(perfetto_service_.get());
}
void CreateProducerOnSequence(base::OnceClosure on_producer_connected) {
producer_ = std::make_unique<MockProducer>();
producer_->Connect(perfetto_service_.get(),
base::StrCat({mojom::kPerfettoProducerNamePrefix,
base::NumberToString(kProducerPid)}));
EXPECT_CALL(*producer_, OnConnect())
.WillOnce(base::test::RunOnceClosure(std::move(on_producer_connected)));
}
void RegisterDataSourceOnSequence(const std::string& data_source_name,
base::OnceClosure on_data_source_start) {
producer_->RegisterDataSource(data_source_name);
EXPECT_CALL(*producer_, OnStartDataSource(data_source_name, _))
.WillOnce(base::test::RunOnceClosure(std::move(on_data_source_start)));
}
void CreateTraceWriterOnSequence(
const std::string& data_source_name,
base::RepeatingCallback<void(std::unique_ptr<perfetto::TraceWriter>)>
on_data_source_start) {
producer_->RegisterDataSource(data_source_name);
EXPECT_CALL(*producer_, OnStartDataSource(data_source_name, _))
.WillOnce(
[on_data_source_start, this](const std::string& name,
perfetto::DataSourceInstanceID ds_id) {
on_data_source_start.Run(producer_->CreateTraceWriter(ds_id));
});
}
void GetDataSourceConfigOnSequence(
const std::string& data_source_name,
base::RepeatingCallback<void(perfetto::DataSourceConfig)>
on_data_source_start) {
producer_->RegisterDataSource(data_source_name);
EXPECT_CALL(*producer_, OnStartDataSource(data_source_name, _))
.WillOnce(
[on_data_source_start, this](const std::string& name,
perfetto::DataSourceInstanceID ds_id) {
on_data_source_start.Run(producer_->GetDataSourceConfig(ds_id));
});
}
void EnableTracingWithConfig(const perfetto::TraceConfig& config) {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::EnableTracingOnSequence,
base::Unretained(this), std::move(config)),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
void EnableTracingOnSequence(const perfetto::TraceConfig& config) {
mojo::PendingRemote<tracing::mojom::TracingSessionClient>
tracing_session_client;
receiver_ = std::make_unique<mojo::Receiver<mojom::TracingSessionClient>>(
this, tracing_session_client.InitWithNewPipeAndPassReceiver());
consumer_->EnableTracing(
tracing_session_host_->BindNewPipeAndPassReceiver(),
std::move(tracing_session_client), std::move(config), base::File());
}
void ReadBuffers(mojo::ScopedDataPipeProducerHandle stream,
ConsumerHost::TracingSession::ReadBuffersCallback callback) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&ConsumerHost::TracingSession::ReadBuffers,
base::Unretained(consumer_.get()->tracing_session_for_testing()),
std::move(stream), std::move(callback)));
}
void DisableTracing() {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&ConsumerHost::TracingSession::DisableTracing,
base::Unretained(consumer_.get()->tracing_session_for_testing())),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
void DisableTracingAndEmitJson(
mojo::ScopedDataPipeProducerHandle stream,
ConsumerHost::TracingSession::DisableTracingAndEmitJsonCallback callback,
bool enable_privacy_filtering) {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&ConsumerHost::TracingSession::DisableTracingAndEmitJson,
base::Unretained(consumer_.get()->tracing_session_for_testing()),
std::string(), std::move(stream), enable_privacy_filtering,
std::move(callback)),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
void Flush(perfetto::TraceWriter& writer_to_flush,
base::OnceClosure on_flush_complete) {
EXPECT_CALL(*producer_, OnFlush()).WillOnce([&]() {
writer_to_flush.Flush();
});
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&ConsumerHost::TracingSession::Flush,
base::Unretained(consumer_.get()->tracing_session_for_testing()),
10000u,
base::BindOnce(
[](base::OnceClosure callback, bool success) {
EXPECT_TRUE(success);
std::move(callback).Run();
},
std::move(on_flush_complete))));
}
void ExpectPid(base::ProcessId pid) {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&PerfettoService::AddActiveServicePid,
base::Unretained(perfetto_service_.get()), pid),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
void SetPidsInitialized() {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&PerfettoService::SetActiveServicePidsInitialized,
base::Unretained(perfetto_service_.get())),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
void RemovePid(base::ProcessId pid) {
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&PerfettoService::RemoveActiveServicePid,
base::Unretained(perfetto_service_.get()), pid),
wait_for_call.QuitClosure());
wait_for_call.Run();
}
bool IsTracingEnabled() const {
bool tracing_enabled;
base::RunLoop wait_for_call;
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&ThreadedPerfettoService::GetTracingEnabledOnSequence,
base::Unretained(this), &tracing_enabled),
wait_for_call.QuitClosure());
wait_for_call.Run();
return tracing_enabled;
}
void GetTracingEnabledOnSequence(bool* tracing_enabled) const {
*tracing_enabled = tracing_enabled_;
}
void ClearConsumer() {
base::RunLoop wait_loop;
task_runner_->PostTaskAndReply(
FROM_HERE, base::BindLambdaForTesting([&]() { consumer_.reset(); }),
wait_loop.QuitClosure());
wait_loop.Run();
}
PerfettoService* perfetto_service() const { return perfetto_service_.get(); }
private:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
std::unique_ptr<PerfettoService> perfetto_service_;
std::unique_ptr<ConsumerHost> consumer_;
std::unique_ptr<MockProducer> producer_;
std::unique_ptr<mojo::Receiver<mojom::TracingSessionClient>> receiver_;
std::unique_ptr<mojo::Remote<tracing::mojom::TracingSessionHost>>
tracing_session_host_;
bool tracing_enabled_ = false;
};
class TracingConsumerTest : public testing::Test,
public mojo::DataPipeDrainer::Client {
public:
void SetUp() override {
threaded_service_ = std::make_unique<ThreadedPerfettoService>();
matching_packet_count_ = 0;
total_bytes_received_ = 0;
}
void TearDown() override {
threaded_service_.reset();
}
void OnDataAvailable(base::span<const uint8_t> data) override {
total_bytes_received_ += data.size();
base::Extend(received_data_, data);
}
void OnDataComplete() override {
if (expect_json_data_) {
std::string output(reinterpret_cast<const char*>(received_data_.data()),
received_data_.size());
if (output.find(packet_testing_str_) != std::string::npos) {
matching_packet_count_++;
}
} else {
auto proto = std::make_unique<perfetto::protos::Trace>();
EXPECT_TRUE(
proto->ParseFromArray(received_data_.data(), received_data_.size()));
for (int i = 0; i < proto->packet_size(); ++i) {
if (proto->packet(i).for_testing().str() == packet_testing_str_) {
matching_packet_count_++;
}
}
}
if (on_data_complete_) {
std::move(on_data_complete_).Run();
}
}
void ExpectPackets(const std::string& testing_str,
base::OnceClosure on_data_complete) {
on_data_complete_ = std::move(on_data_complete);
packet_testing_str_ = testing_str;
matching_packet_count_ = 0;
}
void ReadBuffers() {
MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions),
MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
MojoResult rv = mojo::CreateDataPipe(&options, producer, consumer);
ASSERT_EQ(MOJO_RESULT_OK, rv);
threaded_service_->ReadBuffers(std::move(producer), base::OnceClosure());
drainer_ =
std::make_unique<mojo::DataPipeDrainer>(this, std::move(consumer));
}
void DisableTracingAndEmitJson(base::OnceClosure write_callback,
bool enable_privacy_filtering = false) {
expect_json_data_ = true;
MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions),
MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
MojoResult rv = mojo::CreateDataPipe(&options, producer, consumer);
ASSERT_EQ(MOJO_RESULT_OK, rv);
threaded_service_->DisableTracingAndEmitJson(std::move(producer),
std::move(write_callback),
enable_privacy_filtering);
drainer_ =
std::make_unique<mojo::DataPipeDrainer>(this, std::move(consumer));
}
perfetto::TraceConfig GetDefaultTraceConfig(
const std::string& data_source_name,
perfetto::protos::gen::ChromeConfig::ClientPriority priority =
perfetto::protos::gen::ChromeConfig::UNKNOWN) {
perfetto::TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(32 * 1024);
auto* trace_event_config =
trace_config.add_data_sources()->mutable_config();
trace_event_config->set_name(data_source_name);
trace_event_config->set_target_buffer(0);
return trace_config;
}
void EnableTracingWithDataSourceName(const std::string& data_source_name,
bool enable_privacy_filtering = false,
bool convert_to_legacy_json = false) {
perfetto::TraceConfig config = GetDefaultTraceConfig(data_source_name);
if (enable_privacy_filtering) {
for (auto& source : *config.mutable_data_sources()) {
source.mutable_config()
->mutable_chrome_config()
->set_privacy_filtering_enabled(true);
}
}
if (convert_to_legacy_json) {
for (auto& source : *config.mutable_data_sources()) {
source.mutable_config()
->mutable_chrome_config()
->set_convert_to_legacy_json(true);
}
}
threaded_service_->EnableTracingWithConfig(config);
}
bool IsTracingEnabled() {
task_environment_.RunUntilIdle();
return threaded_service_->IsTracingEnabled();
}
size_t matching_packet_count() const { return matching_packet_count_; }
size_t total_bytes_received() const { return total_bytes_received_; }
ThreadedPerfettoService* threaded_perfetto_service() const {
return threaded_service_.get();
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::IO};
TracedProcessForTesting traced_process_{
base::SingleThreadTaskRunner::GetCurrentDefault()};
std::unique_ptr<ThreadedPerfettoService> threaded_service_;
base::OnceClosure on_data_complete_;
std::unique_ptr<mojo::DataPipeDrainer> drainer_;
std::vector<uint8_t> received_data_;
std::string packet_testing_str_;
size_t matching_packet_count_ = 0;
size_t total_bytes_received_ = 0;
bool expect_json_data_ = false;
};
TEST_F(TracingConsumerTest, EnableAndDisableTracing) {
EnableTracingWithDataSourceName(kDataSourceName);
base::RunLoop no_more_data;
ExpectPackets(kPerfettoTestString, no_more_data.QuitClosure());
threaded_perfetto_service()->DisableTracing();
ReadBuffers();
no_more_data.Run();
EXPECT_EQ(0u, matching_packet_count());
}
TEST_F(TracingConsumerTest, ReceiveTestPackets) {
EnableTracingWithDataSourceName(kDataSourceName);
threaded_perfetto_service()->CreateProducer();
auto writer = threaded_perfetto_service()->CreateTraceWriter(kDataSourceName);
MockProducer::WritePackets(*writer, 10);
base::RunLoop no_more_data;
ExpectPackets(kPerfettoTestString, no_more_data.QuitClosure());
threaded_perfetto_service()->DisableTracing();
ReadBuffers();
no_more_data.Run();
EXPECT_EQ(10u, matching_packet_count());
}
TEST_F(TracingConsumerTest, DeleteConsumerWhenReceiving) {
EnableTracingWithDataSourceName(kDataSourceName);
threaded_perfetto_service()->CreateProducer();
auto writer = threaded_perfetto_service()->CreateTraceWriter(kDataSourceName);
MockProducer::WritePackets(*writer, 100u);
base::RunLoop no_more_data;
ExpectPackets(kPerfettoTestString, no_more_data.QuitClosure());
threaded_perfetto_service()->DisableTracing();
ReadBuffers();
threaded_perfetto_service()->ClearConsumer();
no_more_data.Run();
}
TEST_F(TracingConsumerTest, FlushProducers) {
EnableTracingWithDataSourceName(kDataSourceName);
threaded_perfetto_service()->CreateProducer();
auto writer = threaded_perfetto_service()->CreateTraceWriter(kDataSourceName);
base::RunLoop wait_for_packets;
ExpectPackets(kPerfettoTestString, wait_for_packets.QuitClosure());
MockProducer::WritePackets(*writer, 10);
base::RunLoop wait_for_flush;
threaded_perfetto_service()->Flush(*writer, wait_for_flush.QuitClosure());
wait_for_flush.Run();
ReadBuffers();
wait_for_packets.Run();
EXPECT_EQ(10u, matching_packet_count());
}
TEST_F(TracingConsumerTest, LargeDataSize) {
EnableTracingWithDataSourceName(kDataSourceName);
threaded_perfetto_service()->CreateProducer();
auto writer = threaded_perfetto_service()->CreateTraceWriter(kDataSourceName);
MockProducer::WritePacketBigly(*writer);
base::RunLoop no_more_data;
ExpectPackets(kPerfettoTestString, no_more_data.QuitClosure());
threaded_perfetto_service()->DisableTracing();
ReadBuffers();
no_more_data.Run();
EXPECT_GE(total_bytes_received(), kLargeMessageSize);
}
TEST_F(TracingConsumerTest, NotifiesOnTracingEnabled) {
threaded_perfetto_service()->SetPidsInitialized();
EnableTracingWithDataSourceName(kDataSourceName);
EXPECT_TRUE(IsTracingEnabled());
}
TEST_F(TracingConsumerTest, NotifiesOnTracingEnabledWaitsForProducer) {
threaded_perfetto_service()->ExpectPid(kProducerPid);
threaded_perfetto_service()->SetPidsInitialized();
EnableTracingWithDataSourceName(kDataSourceName);
EXPECT_FALSE(IsTracingEnabled());
threaded_perfetto_service()->CreateProducer();
threaded_perfetto_service()->RegisterDataSource(kDataSourceName);
EXPECT_TRUE(IsTracingEnabled());
}
TEST_F(TracingConsumerTest, NotifiesOnTracingEnabledWaitsForFilteredProducer) {
threaded_perfetto_service()->ExpectPid(kProducerPid);
threaded_perfetto_service()->SetPidsInitialized();
auto config = GetDefaultTraceConfig(kDataSourceName);
*config.mutable_data_sources()->front().add_producer_name_filter() =
base::StrCat({mojom::kPerfettoProducerNamePrefix,
base::NumberToString(kProducerPid)});
threaded_perfetto_service()->EnableTracingWithConfig(config);
EXPECT_FALSE(IsTracingEnabled());
threaded_perfetto_service()->CreateProducer();
threaded_perfetto_service()->RegisterDataSource(kDataSourceName);
EXPECT_TRUE(IsTracingEnabled());
}
TEST_F(TracingConsumerTest,
NotifiesOnTracingEnabledDoesNotWaitForUnfilteredProducer) {
threaded_perfetto_service()->ExpectPid(kProducerPid);
threaded_perfetto_service()->SetPidsInitialized();
auto config = GetDefaultTraceConfig(kDataSourceName);
*config.mutable_data_sources()->front().add_producer_name_filter() =
base::StrCat({mojom::kPerfettoProducerNamePrefix,
base::NumberToString(kProducerPid + 1)});
threaded_perfetto_service()->EnableTracingWithConfig(config);
EXPECT_TRUE(IsTracingEnabled());
}
TEST_F(TracingConsumerTest,
NotifiesOnTracingEnabledWaitsForProducerAndInitializedPids) {
threaded_perfetto_service()->ExpectPid(kProducerPid);
EnableTracingWithDataSourceName(kDataSourceName);
EXPECT_FALSE(IsTracingEnabled());
threaded_perfetto_service()->CreateProducer();
threaded_perfetto_service()->RegisterDataSource(kDataSourceName);
EXPECT_FALSE(IsTracingEnabled());
threaded_perfetto_service()->SetPidsInitialized();
EXPECT_TRUE(IsTracingEnabled());
}
TEST_F(TracingConsumerTest, PrivacyFilterConfig) {
EnableTracingWithDataSourceName(kDataSourceName,
true,
false);
threaded_perfetto_service()->CreateProducer();
auto config =
threaded_perfetto_service()->GetDataSourceConfig(kDataSourceName);
EXPECT_TRUE(config.chrome_config().privacy_filtering_enabled());
base::trace_event::TraceConfig base_config(
config.chrome_config().trace_config());
EXPECT_FALSE(base_config.IsArgumentFilterEnabled());
}
TEST_F(TracingConsumerTest, NoPrivacyFilterWithJsonConversion) {
EnableTracingWithDataSourceName(kDataSourceName,
false,
true);
threaded_perfetto_service()->CreateProducer();
auto config =
threaded_perfetto_service()->GetDataSourceConfig(kDataSourceName);
EXPECT_FALSE(config.chrome_config().privacy_filtering_enabled());
base::trace_event::TraceConfig base_config(
config.chrome_config().trace_config());
EXPECT_FALSE(base_config.IsArgumentFilterEnabled());
}
TEST_F(TracingConsumerTest, PrivacyFilterConfigInJson) {
EnableTracingWithDataSourceName(kDataSourceName,
true,
true);
threaded_perfetto_service()->CreateProducer();
auto config =
threaded_perfetto_service()->GetDataSourceConfig(kDataSourceName);
EXPECT_FALSE(config.chrome_config().privacy_filtering_enabled());
base::trace_event::TraceConfig base_config(
config.chrome_config().trace_config());
EXPECT_TRUE(base_config.IsArgumentFilterEnabled());
base::RunLoop no_more_data;
ExpectPackets("\"trace_processor_stats\":\"__stripped__\"",
no_more_data.QuitClosure());
base::RunLoop write_done;
DisableTracingAndEmitJson(write_done.QuitClosure(),
true);
no_more_data.Run();
write_done.Run();
EXPECT_EQ(1u, matching_packet_count());
}
}