[tracing] Support perfetto config in command line
This CL support encoded perfertto config (base64 or proto) in command line. Change-Id: I37028be6bb4b689af4a23539bae5fc33c06f8110 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6505633 Reviewed-by: Mikhail Khokhlov <khokhlov@google.com> Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org> Cr-Commit-Position: refs/heads/main@{#1457177}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b7208a34ef
commit
31c16a61af
components/tracing/common
services/tracing
@ -18,10 +18,18 @@ namespace switches {
|
||||
// < {input txt config}.pbtxt > {output proto config}.pb
|
||||
const char kEnableBackgroundTracing[] = "enable-background-tracing";
|
||||
|
||||
// Causes TRACE_EVENT flags to be recorded from startup.
|
||||
// This flag will be ignored if --trace-startup or --trace-shutdown is provided.
|
||||
// Enables startup tracing by passing a file path containing the chrome Json
|
||||
// tracing config as an argument. This flag will be ignored if --trace-startup
|
||||
// or --trace-shutdown is provided.
|
||||
const char kTraceConfigFile[] = "trace-config-file";
|
||||
|
||||
// Enables startup tracing by passing a file path containing the perfetto config
|
||||
// as an argument. The config is a serialized or base64 encoded proto
|
||||
// `perfetto.protos.TraceConfig` defined in
|
||||
// third_party/perfetto/protos/perfetto/config/trace_config.proto. This flag
|
||||
// will be ignored if --trace-startup or --trace-shutdown is provided.
|
||||
const char kTracePerfettoConfigFile[] = "trace-perfetto-config-file";
|
||||
|
||||
// Causes TRACE_EVENT flags to be recorded from startup. Optionally, can
|
||||
// specify the specific trace categories to include (e.g.
|
||||
// --trace-startup=base,net) otherwise, all events are recorded. Setting this
|
||||
|
@ -11,6 +11,7 @@ namespace switches {
|
||||
|
||||
TRACING_EXPORT extern const char kEnableBackgroundTracing[];
|
||||
TRACING_EXPORT extern const char kTraceConfigFile[];
|
||||
TRACING_EXPORT extern const char kTracePerfettoConfigFile[];
|
||||
TRACING_EXPORT extern const char kTraceStartup[];
|
||||
TRACING_EXPORT extern const char kTraceConfigHandle[];
|
||||
TRACING_EXPORT extern const char kEnableTracing[];
|
||||
|
@ -4,6 +4,7 @@ include_rules = [
|
||||
"+third_party/perfetto/include",
|
||||
"+third_party/perfetto/protos/perfetto",
|
||||
"+third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h",
|
||||
"+third_party/snappy"
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
|
@ -136,6 +136,7 @@ target(tracing_lib_type, "cpp") {
|
||||
"//build:chromecast_buildflags",
|
||||
"//components/system_cpu:system_cpu",
|
||||
"//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
|
||||
"//third_party/snappy:snappy",
|
||||
]
|
||||
|
||||
if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/base64.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/json/json_reader.h"
|
||||
@ -25,6 +26,7 @@
|
||||
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
|
||||
#include "services/tracing/public/mojom/perfetto_service.mojom.h"
|
||||
#include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h"
|
||||
#include "third_party/snappy/src/snappy.h"
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "base/android/early_trace_event_binding.h"
|
||||
@ -120,7 +122,9 @@ TraceStartupConfig::TraceStartupConfig() {
|
||||
DCHECK(IsEnabled());
|
||||
} else if (EnableFromConfigHandle()) {
|
||||
DCHECK(IsEnabled());
|
||||
} else if (EnableFromConfigFile()) {
|
||||
} else if (EnableFromJsonConfigFile()) {
|
||||
DCHECK(IsEnabled());
|
||||
} else if (EnableFromPerfettoConfigFile()) {
|
||||
DCHECK(IsEnabled());
|
||||
} else if (EnableFromBackgroundTracing()) {
|
||||
DCHECK(IsEnabled());
|
||||
@ -283,7 +287,7 @@ bool TraceStartupConfig::EnableFromConfigHandle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraceStartupConfig::EnableFromConfigFile() {
|
||||
bool TraceStartupConfig::EnableFromJsonConfigFile() {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
base::FilePath trace_config_file(kAndroidTraceConfigFile);
|
||||
#else
|
||||
@ -318,11 +322,57 @@ bool TraceStartupConfig::EnableFromConfigFile() {
|
||||
DLOG(WARNING) << "Cannot read the trace config file correctly.";
|
||||
return false;
|
||||
}
|
||||
is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content);
|
||||
if (!is_enabled_) {
|
||||
auto config = ParseTraceJsonConfigFileContent(trace_config_file_content);
|
||||
if (!config) {
|
||||
DLOG(WARNING) << "Cannot parse the trace config file correctly.";
|
||||
return false;
|
||||
}
|
||||
return is_enabled_;
|
||||
perfetto_config_ = *config;
|
||||
is_enabled_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraceStartupConfig::EnableFromPerfettoConfigFile() {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (!command_line->HasSwitch(switches::kTracePerfettoConfigFile)) {
|
||||
return false;
|
||||
}
|
||||
base::FilePath config_file =
|
||||
command_line->GetSwitchValuePath(switches::kTracePerfettoConfigFile);
|
||||
|
||||
if (config_file.empty()) {
|
||||
DLOG(WARNING) << "--perfetto-config-file needs a config file path.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!base::PathExists(config_file)) {
|
||||
DLOG(WARNING) << "The perfetto config file does not exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string config_text;
|
||||
if (!base::ReadFileToString(config_file, &config_text)) {
|
||||
DLOG(WARNING) << "Cannot read the trace config file correctly.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<perfetto::TraceConfig> config;
|
||||
if (base::FilePath::CompareEqualIgnoreCase(config_file.Extension(),
|
||||
FILE_PATH_LITERAL(".pb"))) {
|
||||
config = ParseSerializedPerfettoConfig(base::as_byte_span(config_text));
|
||||
} else {
|
||||
config = ParseEncodedPerfettoConfig(config_text);
|
||||
}
|
||||
if (!config) {
|
||||
DLOG(WARNING) << "Failed to parse perfetto config file.";
|
||||
return false;
|
||||
}
|
||||
if (AdaptPerfettoConfigForChrome(&*config)) {
|
||||
DLOG(WARNING) << "Failed to adapt perfetto config file.";
|
||||
}
|
||||
perfetto_config_ = *config;
|
||||
is_enabled_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraceStartupConfig::EnableFromBackgroundTracing() {
|
||||
@ -352,28 +402,29 @@ bool TraceStartupConfig::EnableFromBackgroundTracing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraceStartupConfig::ParseTraceConfigFileContent(
|
||||
std::optional<perfetto::TraceConfig>
|
||||
TraceStartupConfig::ParseTraceJsonConfigFileContent(
|
||||
const std::string& content) {
|
||||
std::optional<base::Value::Dict> value = base::JSONReader::ReadDict(content);
|
||||
if (!value) {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto* trace_config_dict = value->FindDict(kTraceConfigParam);
|
||||
if (!trace_config_dict) {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto chrome_config =
|
||||
base::trace_event::TraceConfig(std::move(*trace_config_dict));
|
||||
perfetto_config_ = tracing::GetDefaultPerfettoConfig(
|
||||
perfetto::TraceConfig perfetto_config = tracing::GetDefaultPerfettoConfig(
|
||||
chrome_config, false, output_format_ != OutputFormat::kProto,
|
||||
perfetto::protos::gen::ChromeConfig::USER_INITIATED, "");
|
||||
|
||||
int startup_duration_in_seconds =
|
||||
value->FindInt(kStartupDurationParam).value_or(0);
|
||||
if (startup_duration_in_seconds > 0) {
|
||||
perfetto_config_.set_duration_ms(startup_duration_in_seconds * 1000);
|
||||
perfetto_config.set_duration_ms(startup_duration_in_seconds * 1000);
|
||||
}
|
||||
|
||||
if (auto* result_file = value->FindString(kResultFileParam)) {
|
||||
@ -386,7 +437,39 @@ bool TraceStartupConfig::ParseTraceConfigFileContent(
|
||||
"_chrometrace.log");
|
||||
}
|
||||
|
||||
return true;
|
||||
return perfetto_config;
|
||||
}
|
||||
|
||||
std::optional<perfetto::TraceConfig>
|
||||
TraceStartupConfig::ParseSerializedPerfettoConfig(
|
||||
const base::span<const uint8_t>& config_bytes) {
|
||||
perfetto::TraceConfig config;
|
||||
if (config_bytes.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (config.ParseFromArray(config_bytes.data(), config_bytes.size())) {
|
||||
return config;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<perfetto::TraceConfig>
|
||||
TraceStartupConfig::ParseEncodedPerfettoConfig(
|
||||
const std::string& config_string) {
|
||||
std::string serialized_config;
|
||||
if (!base::Base64Decode(config_string, &serialized_config,
|
||||
base::Base64DecodePolicy::kForgiving)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// `serialized_config` may optionally be compressed.
|
||||
std::string decompressed_config;
|
||||
if (!snappy::Uncompress(serialized_config.data(), serialized_config.size(),
|
||||
&decompressed_config)) {
|
||||
return ParseSerializedPerfettoConfig(base::as_byte_span(serialized_config));
|
||||
}
|
||||
|
||||
return ParseSerializedPerfettoConfig(base::as_byte_span(decompressed_config));
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define SERVICES_TRACING_PUBLIC_CPP_TRACE_STARTUP_CONFIG_H_
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/trace_event/trace_config.h"
|
||||
#include "build/build_config.h"
|
||||
@ -154,11 +155,17 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceStartupConfig {
|
||||
TraceStartupConfig();
|
||||
|
||||
bool EnableFromCommandLine();
|
||||
bool EnableFromConfigFile();
|
||||
bool EnableFromJsonConfigFile();
|
||||
bool EnableFromPerfettoConfigFile();
|
||||
bool EnableFromConfigHandle();
|
||||
bool EnableFromBackgroundTracing();
|
||||
|
||||
bool ParseTraceConfigFileContent(const std::string& content);
|
||||
std::optional<perfetto::TraceConfig> ParseTraceJsonConfigFileContent(
|
||||
const std::string& content);
|
||||
std::optional<perfetto::TraceConfig> ParseSerializedPerfettoConfig(
|
||||
const base::span<const uint8_t>& config_bytes);
|
||||
std::optional<perfetto::TraceConfig> ParseEncodedPerfettoConfig(
|
||||
const std::string& config_string);
|
||||
|
||||
bool is_enabled_ = false;
|
||||
perfetto::TraceConfig perfetto_config_;
|
||||
|
@ -7,19 +7,39 @@
|
||||
#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,"
|
||||
@ -32,6 +52,20 @@ const char kTraceConfig[] =
|
||||
"\"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) {
|
||||
@ -105,7 +139,7 @@ TEST_F(TraceStartupConfigTest, TraceStartupConfigEnabledWithInvalidPath) {
|
||||
EXPECT_FALSE(startup_config_->IsEnabled());
|
||||
}
|
||||
|
||||
TEST_F(TraceStartupConfigTest, ValidContent) {
|
||||
TEST_F(TraceStartupConfigTest, ValidJsonContent) {
|
||||
std::string content =
|
||||
GetTraceConfigFileContent(kTraceConfig, "10", "trace_result_file.log");
|
||||
|
||||
@ -132,6 +166,47 @@ TEST_F(TraceStartupConfigTest, ValidContent) {
|
||||
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, "", "");
|
||||
|
||||
@ -228,7 +303,7 @@ TEST_F(TraceStartupConfigTest, ContentWithoutTraceConfig) {
|
||||
EXPECT_FALSE(startup_config_->IsEnabled());
|
||||
}
|
||||
|
||||
TEST_F(TraceStartupConfigTest, InvalidContent) {
|
||||
TEST_F(TraceStartupConfigTest, InvalidJsonContent) {
|
||||
std::string content = "invalid trace config file content";
|
||||
|
||||
base::FilePath trace_config_file;
|
||||
@ -244,6 +319,22 @@ TEST_F(TraceStartupConfigTest, InvalidContent) {
|
||||
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;
|
||||
|
Reference in New Issue
Block a user