blob: 05a45ebd23be267dbed6c25bb07de4fa2f05ba38 [file]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/protocol/tracing_handler.h"
#include <memory>
#include "base/json/json_reader.h"
#include "base/trace_event/trace_config.h"
#include "base/values.h"
#include "content/browser/devtools/devtools_traceable_screenshot.h"
#include "services/tracing/public/cpp/perfetto/perfetto_data_source_names.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
namespace content {
namespace protocol {
namespace {
const char kCustomTraceConfigString[] =
"{"
"\"enable_argument_filter\":true,"
"\"enable_package_name_filter\":false,"
"\"enable_systrace\":true,"
"\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
"\"included_categories\":[\"included\","
"\"inc_pattern*\","
"\"disabled-by-default-cc\","
"\"disabled-by-default-memory-infra\"],"
"\"memory_dump_config\":{"
"\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
"\"triggers\":["
"{"
"\"min_time_between_dumps_ms\":50,"
"\"mode\":\"light\","
"\"type\":\"periodic_interval\""
"},"
"{"
"\"min_time_between_dumps_ms\":1000,"
"\"mode\":\"detailed\","
"\"type\":\"periodic_interval\""
"}"
"]"
"},"
"\"record_mode\":\"record-continuously\","
"\"trace_buffer_size_in_kb\":262144"
"}";
const char kCustomTraceConfigStringDevToolsStyle[] =
"{"
"\"enableArgumentFilter\":true,"
"\"enableSystrace\":true,"
"\"excludedCategories\":[\"excluded\",\"exc_pattern*\"],"
"\"includedCategories\":[\"included\","
"\"inc_pattern*\","
"\"disabled-by-default-cc\","
"\"disabled-by-default-memory-infra\"],"
"\"memoryDumpConfig\":{"
"\"allowedDumpModes\":[\"background\",\"light\",\"detailed\"],"
"\"triggers\":["
"{"
"\"minTimeBetweenDumpsMs\":50,"
"\"mode\":\"light\","
"\"type\":\"periodic_interval\""
"},"
"{"
"\"minTimeBetweenDumpsMs\":1000,"
"\"mode\":\"detailed\","
"\"type\":\"periodic_interval\""
"}"
"]"
"},"
"\"recordMode\":\"recordContinuously\","
"\"traceBufferSizeInKb\":262144"
"}";
} // namespace
class TracingHandlerTest : public testing::Test {
public:
void SetUp() override {
tracing_handler_ =
std::make_unique<TracingHandler>(nullptr, nullptr, nullptr);
}
void TearDown() override { tracing_handler_.reset(); }
std::string GetValidTraceFragment(const std::string& trace_fragment) {
const std::string valid_trace_fragment =
tracing_handler_->UpdateTraceDataBuffer(trace_fragment);
return valid_trace_fragment.substr(
tracing_handler_->trace_data_buffer_state_.offset);
}
private:
std::unique_ptr<TracingHandler> tracing_handler_;
};
TEST_F(TracingHandlerTest, GetTraceConfigFromDevToolsConfig) {
base::Value devtools_config =
base::JSONReader::Read(kCustomTraceConfigStringDevToolsStyle,
base::JSON_PARSE_CHROMIUM_EXTENSIONS)
.value();
base::trace_event::TraceConfig trace_config =
TracingHandler::GetTraceConfigFromDevToolsConfig(devtools_config);
EXPECT_STREQ(kCustomTraceConfigString, trace_config.ToString().c_str());
}
TEST_F(TracingHandlerTest, SimpleGetValidTraceFragment) {
// No prefix is valid.
EXPECT_EQ("", GetValidTraceFragment("{pid: 1, "));
// The longest valid prefix of "{pid: 1, args: {}}, {pid: 2" is
// "{pid: 1, args: {}}".
EXPECT_EQ("{pid: 1, args: {}}", GetValidTraceFragment("args: {}}, {pid: 2"));
EXPECT_EQ("{pid: 2}, {pid: 3}", GetValidTraceFragment("}, {pid: 3}"));
}
TEST_F(TracingHandlerTest, GetValidTraceFragmentBreakBeforeComma) {
EXPECT_EQ("{pid: 1}", GetValidTraceFragment("{pid: 1}"));
// The comma should be ignored.
EXPECT_EQ("{pid: 2}", GetValidTraceFragment(",{pid: 2}"));
}
TEST_F(TracingHandlerTest, ComplexGetValidTraceFragment) {
const std::string chunk1 = "{\"pid\":1,\"args\":{\"key\":\"}\"},\"tid\":1}";
const std::string chunk2 =
"{\"pid\":2,\"args\":{\"key\":{\"key\":\"\\\"t}\"},\"key2\":2},\"tid\":"
"2}";
const std::string trace_data = chunk1 + "," + chunk2;
EXPECT_EQ("", GetValidTraceFragment(trace_data.substr(0, chunk1.size() - 1)));
EXPECT_EQ(chunk1, GetValidTraceFragment(trace_data.substr(
chunk1.size() - 1, trace_data.size() - chunk1.size())));
EXPECT_EQ(chunk2,
GetValidTraceFragment(trace_data.substr(trace_data.size() - 1, 1)));
}
TEST_F(TracingHandlerTest, ProcessFilterClearsRegex) {
perfetto::TraceConfig trace_config;
auto* data_source = trace_config.add_data_sources();
auto* config = data_source->mutable_config();
config->set_name("track_event");
data_source->add_producer_name_regex_filter(".*");
data_source->add_producer_name_filter("old_filter");
auto* data_source2 = trace_config.add_data_sources();
auto* config2 = data_source2->mutable_config();
config2->set_name("org.chromium.foo");
data_source2->add_producer_name_regex_filter(".*");
auto* data_source3 = trace_config.add_data_sources();
auto* config3 = data_source3->mutable_config();
config3->set_name("other_source");
data_source3->add_producer_name_regex_filter(".*");
std::unordered_set<base::ProcessId> pids = {1234};
TracingHandler::AddPidsToProcessFilter(pids, trace_config);
ASSERT_EQ(3, trace_config.data_sources_size());
// Chrome track_event data source: regex cleared, pid added
EXPECT_EQ("track_event", trace_config.data_sources()[0].config().name());
EXPECT_EQ(0, trace_config.data_sources()[0].producer_name_regex_filter_size());
ASSERT_EQ(1, trace_config.data_sources()[0].producer_name_filter_size());
EXPECT_EQ(std::string(tracing::kPerfettoProducerNamePrefix) + "1234",
trace_config.data_sources()[0].producer_name_filter()[0]);
// Chrome data source: regex cleared, pid added
EXPECT_EQ("org.chromium.foo", trace_config.data_sources()[1].config().name());
EXPECT_EQ(0, trace_config.data_sources()[1].producer_name_regex_filter_size());
ASSERT_EQ(1, trace_config.data_sources()[1].producer_name_filter_size());
EXPECT_EQ(std::string(tracing::kPerfettoProducerNamePrefix) + "1234",
trace_config.data_sources()[1].producer_name_filter()[0]);
// Other data source: regex retained, pid not added
EXPECT_EQ("other_source", trace_config.data_sources()[2].config().name());
ASSERT_EQ(1, trace_config.data_sources()[2].producer_name_regex_filter_size());
EXPECT_EQ(".*", trace_config.data_sources()[2].producer_name_regex_filter()[0]);
EXPECT_EQ(0, trace_config.data_sources()[2].producer_name_filter_size());
}
TEST_F(TracingHandlerTest, ProcessFilterAppendsPids) {
perfetto::TraceConfig trace_config;
auto* data_source = trace_config.add_data_sources();
auto* config = data_source->mutable_config();
config->set_name("track_event");
// Initial PIDs
std::unordered_set<base::ProcessId> pids1 = {1234, 5678};
TracingHandler::AddPidsToProcessFilter(pids1, trace_config);
ASSERT_EQ(1, trace_config.data_sources_size());
EXPECT_EQ(2, trace_config.data_sources()[0].producer_name_filter_size());
// Append new PID
std::unordered_set<base::ProcessId> pids2 = {9012};
TracingHandler::AddPidsToProcessFilter(pids2, trace_config);
ASSERT_EQ(1, trace_config.data_sources_size());
// Should have 3 PIDs now
EXPECT_EQ(3, trace_config.data_sources()[0].producer_name_filter_size());
EXPECT_THAT(trace_config.data_sources()[0].producer_name_filter(),
testing::Contains(
std::string(tracing::kPerfettoProducerNamePrefix) + "1234"));
EXPECT_THAT(trace_config.data_sources()[0].producer_name_filter(),
testing::Contains(
std::string(tracing::kPerfettoProducerNamePrefix) + "5678"));
EXPECT_THAT(trace_config.data_sources()[0].producer_name_filter(),
testing::Contains(
std::string(tracing::kPerfettoProducerNamePrefix) + "9012"));
}
TEST_F(TracingHandlerTest, ResolveScreenshotParams) {
constexpr int kDefaultSize = 500;
constexpr int kDefaultCount =
DevToolsTraceableScreenshot::kDefaultMaximumNumberOfScreenshots;
// Defaults are applied when no values are supplied.
{
gfx::Size size;
int count = 0;
EXPECT_TRUE(TracingHandler::ResolveScreenshotParams(
std::nullopt, std::nullopt, &size, &count)
.IsSuccess());
EXPECT_EQ(gfx::Size(kDefaultSize, kDefaultSize), size);
EXPECT_EQ(kDefaultCount, count);
}
// Explicit equivalent of the defaults is accepted.
{
gfx::Size size;
int count = 0;
EXPECT_TRUE(TracingHandler::ResolveScreenshotParams(
kDefaultSize, kDefaultCount, &size, &count)
.IsSuccess());
EXPECT_EQ(gfx::Size(kDefaultSize, kDefaultSize), size);
EXPECT_EQ(kDefaultCount, count);
}
// Trading resolution for count within the memory budget is accepted
// (e.g. 250x250 / 1800 = same total budget as 500x500 / 450).
{
gfx::Size size;
int count = 0;
EXPECT_TRUE(
TracingHandler::ResolveScreenshotParams(250, 1800, &size, &count)
.IsSuccess());
EXPECT_EQ(gfx::Size(250, 250), size);
EXPECT_EQ(1800, count);
}
// Non-positive values are rejected.
{
gfx::Size size;
int count = 0;
EXPECT_FALSE(
TracingHandler::ResolveScreenshotParams(0, kDefaultCount, &size, &count)
.IsSuccess());
EXPECT_FALSE(
TracingHandler::ResolveScreenshotParams(kDefaultSize, 0, &size, &count)
.IsSuccess());
EXPECT_FALSE(TracingHandler::ResolveScreenshotParams(-1, kDefaultCount,
&size, &count)
.IsSuccess());
}
// Combinations that exceed the per-session memory budget are rejected.
{
gfx::Size size;
int count = 0;
// 500x500 * 4 * 451 exceeds the 500x500 * 4 * 450 budget by one frame.
EXPECT_FALSE(TracingHandler::ResolveScreenshotParams(
kDefaultSize, kDefaultCount + 1, &size, &count)
.IsSuccess());
}
}
} // namespace protocol
} // namespace content