blob: ecbe887bd27931250b927ebd26395febffe47da6 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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/trace_startup_config.h"
#include <algorithm>
#include "base/at_exit.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/test/test_proto_loader.h"
#include "base/threading/thread_restrictions.h"
#include "components/tracing/common/tracing_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
#include "third_party/perfetto/protos/perfetto/config/data_source_config.gen.h"
#include "third_party/perfetto/protos/perfetto/config/trace_config.gen.h"
namespace tracing {
namespace {
perfetto::protos::gen::TraceConfig ParseTracingConfigFromText(
const std::string& proto_text) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::TestProtoLoader config_loader(
base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT)
.Append(FILE_PATH_LITERAL(
"third_party/perfetto/protos/perfetto/config/config.descriptor")),
"perfetto.protos.TraceConfig");
std::string serialized_message;
config_loader.ParseFromText(proto_text, serialized_message);
perfetto::protos::gen::TraceConfig destination;
destination.ParseFromString(serialized_message);
return destination;
}
const char kTraceConfig[] =
"{"
"\"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\"],"
"\"record_mode\":\"record-continuously\""
"}";
constexpr const char kPerfettoConfig[] = R"pb(
duration_ms: 10000
buffers: { size_kb: 4 fill_policy: RING_BUFFER }
data_sources: {
config: {
name: "track_event"
track_event_config: {
disabled_categories: [ "excluded" ]
enabled_categories: [ "included" ]
}
}
}
)pb";
std::string GetTraceConfigFileContent(std::string trace_config,
std::string startup_duration,
std::string result_file) {
std::string content = "{";
if (!trace_config.empty()) {
content += "\"trace_config\":" + trace_config;
}
if (!startup_duration.empty()) {
if (content != "{") {
content += ",";
}
content += "\"startup_duration\":" + startup_duration;
}
if (!result_file.empty()) {
if (content != "{") {
content += ",";
}
content += "\"result_file\":\"" + result_file + "\"";
}
content += "}";
return content;
}
} // namespace
class TraceStartupConfigTest : public ::testing::Test {
protected:
void Initialize() {
startup_config_ = base::WrapUnique(new TraceStartupConfig());
}
std::unique_ptr<TraceStartupConfig> startup_config_;
};
TEST_F(TraceStartupConfigTest, TraceStartupEnabled) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup);
Initialize();
EXPECT_TRUE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, TraceStartupConfigNotEnabled) {
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, TraceStartupConfigEnabledWithoutPath) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kTraceConfigFile);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_LE(2, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(
base::trace_event::TraceConfig().ToPerfettoTrackEventConfigRaw(false),
config.data_sources()[0].config().track_event_config_raw());
EXPECT_EQ(5000U, config.duration_ms());
EXPECT_TRUE(startup_config_->GetResultFile().empty());
}
TEST_F(TraceStartupConfigTest, TraceStartupConfigEnabledWithInvalidPath) {
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile,
base::FilePath(FILE_PATH_LITERAL("invalid-trace-config-file-path")));
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, ValidJsonContent) {
std::string content =
GetTraceConfigFileContent(kTraceConfig, "10", "trace_result_file.log");
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_LE(2, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(base::trace_event::TraceConfig(kTraceConfig)
.ToPerfettoTrackEventConfigRaw(false),
config.data_sources()[0].config().track_event_config_raw());
EXPECT_EQ(10000U, config.duration_ms());
EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("trace_result_file.log")),
startup_config_->GetResultFile());
}
TEST_F(TraceStartupConfigTest, ValidProtoContent) {
std::string config_string =
ParseTracingConfigFromText(kPerfettoConfig).SerializeAsString();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath config_file =
temp_dir.GetPath().Append(FILE_PATH_LITERAL("config.pb"));
ASSERT_TRUE(base::WriteFile(config_file, config_string));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTracePerfettoConfigFile, config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_EQ(1, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(10000U, config.duration_ms());
}
TEST_F(TraceStartupConfigTest, ValidBase64Content) {
std::string config_string =
ParseTracingConfigFromText(kPerfettoConfig).SerializeAsString();
std::string serialized_config = base::Base64Encode(config_string);
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath config_file =
temp_dir.GetPath().Append(FILE_PATH_LITERAL("config.txt"));
ASSERT_TRUE(base::WriteFile(config_file, serialized_config));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTracePerfettoConfigFile, config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_EQ(1, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(10000U, config.duration_ms());
}
TEST_F(TraceStartupConfigTest, ValidContentWithOnlyTraceConfig) {
std::string content = GetTraceConfigFileContent(kTraceConfig, "", "");
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_LE(2, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(base::trace_event::TraceConfig(kTraceConfig)
.ToPerfettoTrackEventConfigRaw(false),
config.data_sources()[0].config().track_event_config_raw());
EXPECT_FALSE(config.has_duration_ms());
EXPECT_TRUE(startup_config_->GetResultFile().empty());
}
TEST_F(TraceStartupConfigTest, ContentWithAbsoluteResultFilePath) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath result_file_path =
temp_dir.GetPath().Append(FILE_PATH_LITERAL("trace_result_file.log"));
ASSERT_TRUE(result_file_path.IsAbsolute());
std::string result_file_path_str = result_file_path.AsUTF8Unsafe();
auto it = std::ranges::find(result_file_path_str, '\\');
while (it != result_file_path_str.end()) {
auto it2 = result_file_path_str.insert(it, '\\');
it = std::find(it2 + 2, result_file_path_str.end(), '\\');
}
std::string content =
GetTraceConfigFileContent(kTraceConfig, "10", result_file_path_str);
base::FilePath trace_config_file;
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
EXPECT_EQ(result_file_path, startup_config_->GetResultFile());
}
TEST_F(TraceStartupConfigTest, ContentWithNegtiveDuration) {
std::string content = GetTraceConfigFileContent(kTraceConfig, "-1", "");
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
ASSERT_TRUE(startup_config_->IsEnabled());
auto config = startup_config_->GetPerfettoConfig();
ASSERT_LE(2, config.data_sources_size());
EXPECT_EQ("track_event", config.data_sources()[0].config().name());
EXPECT_EQ(base::trace_event::TraceConfig(kTraceConfig)
.ToPerfettoTrackEventConfigRaw(false),
config.data_sources()[0].config().track_event_config_raw());
EXPECT_FALSE(config.has_duration_ms());
EXPECT_TRUE(startup_config_->GetResultFile().empty());
}
TEST_F(TraceStartupConfigTest, ContentWithoutTraceConfig) {
std::string content =
GetTraceConfigFileContent("", "10", "trace_result_file.log");
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, InvalidJsonContent) {
std::string content = "invalid trace config file content";
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, InvalidProtoContent) {
std::string content = "invalid trace config file content";
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
ASSERT_TRUE(base::WriteFile(trace_config_file, content));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTracePerfettoConfigFile, trace_config_file);
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, EmptyContent) {
base::FilePath trace_config_file;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
switches::kTraceConfigFile, trace_config_file);
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, TraceStartupDisabledSystemOwner) {
// Set the owner to 'system' is not sufficient to setup startup tracing.
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kTraceStartupOwner, "system");
Initialize();
EXPECT_FALSE(startup_config_->IsEnabled());
}
TEST_F(TraceStartupConfigTest, TraceStartupEnabledSystemOwner) {
// With owner and --trace-startup TraceStartupConfig should be enabled with
// the owner being the system.
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kTraceStartupOwner, "system");
base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup);
Initialize();
EXPECT_TRUE(startup_config_->IsEnabled());
EXPECT_EQ(TraceStartupConfig::SessionOwner::kSystemTracing,
startup_config_->GetSessionOwner());
}
} // namespace tracing