Commit 0823dbae authored by Alexander Timin's avatar Alexander Timin Committed by Chromium LUCI CQ

[tracing] Align --trace-startup and --enable-tracing.

Use StartupTracingController both for --trace-startup (primary use case:
browser startup perf profiling) and --enable-tracing (primary use case:
browser test tracing).

Due to historical reasons both flags are kept, but now their behaviour
is mostly the same. If both are present, --trace-startup takes
precedence.

Differences:
- `trace-startup` finishes after 5 seconds by default, while `enable-tracing`
  continues until the browser is closed. (--trace-startup-duration overrides
  both).
- `trace-startup-file` always treats the path as absolute, while if
  `enable-tracing-output` ends with /, it uses the provided value as the
  directory and generates the base name based on the current runtime
  metadata.

R=skyostil@chromium.org,eseckler@chromium.org
BUG=1157954

Change-Id: Ia76316aa8927b431784e2fb94c8e42e4e2c0b479
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2589834Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Commit-Queue: Alexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839992}
parent 4e761f87
...@@ -88,11 +88,11 @@ TraceStartupConfig::TraceStartupConfig() { ...@@ -88,11 +88,11 @@ TraceStartupConfig::TraceStartupConfig() {
DCHECK(IsEnabled()); DCHECK(IsEnabled());
DCHECK(!IsTracingStartupForDuration()); DCHECK(!IsTracingStartupForDuration());
DCHECK_EQ(SessionOwner::kBackgroundTracing, session_owner_); DCHECK_EQ(SessionOwner::kBackgroundTracing, session_owner_);
CHECK(!ShouldTraceToResultFile()); CHECK(GetResultFile().empty());
} else if (EnableFromATrace()) { } else if (EnableFromATrace()) {
DCHECK(IsEnabled()); DCHECK(IsEnabled());
DCHECK_EQ(SessionOwner::kSystemTracing, session_owner_); DCHECK_EQ(SessionOwner::kSystemTracing, session_owner_);
CHECK(!ShouldTraceToResultFile()); CHECK(GetResultFile().empty());
} }
} }
...@@ -126,20 +126,11 @@ TraceStartupConfig::OutputFormat TraceStartupConfig::GetOutputFormat() const { ...@@ -126,20 +126,11 @@ TraceStartupConfig::OutputFormat TraceStartupConfig::GetOutputFormat() const {
return output_format_; return output_format_;
} }
bool TraceStartupConfig::ShouldTraceToResultFile() const {
return IsEnabled() && should_trace_to_result_file_;
}
base::FilePath TraceStartupConfig::GetResultFile() const { base::FilePath TraceStartupConfig::GetResultFile() const {
DCHECK(IsEnabled()); DCHECK(IsEnabled());
DCHECK(ShouldTraceToResultFile());
return result_file_; return result_file_;
} }
void TraceStartupConfig::OnTraceToResultFileFinished() {
finished_writing_to_file_ = true;
}
void TraceStartupConfig::SetBackgroundStartupTracingEnabled(bool enabled) { void TraceStartupConfig::SetBackgroundStartupTracingEnabled(bool enabled) {
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
base::android::SetBackgroundStartupTracingFlag(enabled); base::android::SetBackgroundStartupTracingFlag(enabled);
...@@ -172,25 +163,41 @@ bool TraceStartupConfig::EnableFromCommandLine() { ...@@ -172,25 +163,41 @@ bool TraceStartupConfig::EnableFromCommandLine() {
<< "=" << startup_duration_str << " defaulting to 5 (secs)"; << "=" << startup_duration_str << " defaulting to 5 (secs)";
startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds; startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds;
} }
} else if (command_line->HasSwitch(switches::kEnableTracing)) {
// For --enable-tracing, tracing should last until browser shutdown.
startup_duration_in_seconds_ = 0;
} }
if (command_line->HasSwitch(switches::kTraceStartupFormat)) {
if (command_line->GetSwitchValueASCII(switches::kTraceStartupFormat) == if (command_line->GetSwitchValueASCII(switches::kTraceStartupFormat) ==
"proto") { "proto") {
// Default is "json". // Default is "json".
output_format_ = OutputFormat::kProto; output_format_ = OutputFormat::kProto;
} }
} else if (command_line->GetSwitchValueASCII(
switches::kEnableTracingFormat) == "proto") {
output_format_ = OutputFormat::kProto;
}
if (!command_line->HasSwitch(switches::kTraceStartup)) if (!command_line->HasSwitch(switches::kTraceStartup) &&
!command_line->HasSwitch(switches::kEnableTracing)) {
return false; return false;
}
std::string categories;
if (command_line->HasSwitch(switches::kTraceStartup)) {
categories = command_line->GetSwitchValueASCII(switches::kTraceStartup);
} else {
categories = command_line->GetSwitchValueASCII(switches::kEnableTracing);
}
trace_config_ = base::trace_event::TraceConfig( trace_config_ = base::trace_event::TraceConfig(
command_line->GetSwitchValueASCII(switches::kTraceStartup), categories,
command_line->GetSwitchValueASCII(switches::kTraceStartupRecordMode)); command_line->GetSwitchValueASCII(switches::kTraceStartupRecordMode));
result_file_ = command_line->GetSwitchValuePath(switches::kTraceStartupFile); result_file_ = command_line->GetSwitchValuePath(switches::kTraceStartupFile);
is_enabled_ = true; is_enabled_ = true;
should_trace_to_result_file_ = true;
return true; return true;
} }
...@@ -225,7 +232,6 @@ bool TraceStartupConfig::EnableFromConfigFile() { ...@@ -225,7 +232,6 @@ bool TraceStartupConfig::EnableFromConfigFile() {
if (trace_config_file.empty()) { if (trace_config_file.empty()) {
is_enabled_ = true; is_enabled_ = true;
should_trace_to_result_file_ = true;
DLOG(WARNING) << "Use default trace config."; DLOG(WARNING) << "Use default trace config.";
return true; return true;
} }
...@@ -245,7 +251,6 @@ bool TraceStartupConfig::EnableFromConfigFile() { ...@@ -245,7 +251,6 @@ bool TraceStartupConfig::EnableFromConfigFile() {
is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content); is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content);
if (!is_enabled_) if (!is_enabled_)
DLOG(WARNING) << "Cannot parse the trace config file correctly."; DLOG(WARNING) << "Cannot parse the trace config file correctly.";
should_trace_to_result_file_ = is_enabled_;
return is_enabled_; return is_enabled_;
} }
...@@ -267,7 +272,6 @@ bool TraceStartupConfig::EnableFromBackgroundTracing() { ...@@ -267,7 +272,6 @@ bool TraceStartupConfig::EnableFromBackgroundTracing() {
is_enabled_ = true; is_enabled_ = true;
session_owner_ = SessionOwner::kBackgroundTracing; session_owner_ = SessionOwner::kBackgroundTracing;
should_trace_to_result_file_ = false;
// Set startup duration to 0 since background tracing config will configure // Set startup duration to 0 since background tracing config will configure
// the durations later. // the durations later.
startup_duration_in_seconds_ = 0; startup_duration_in_seconds_ = 0;
......
...@@ -123,21 +123,12 @@ class TRACING_EXPORT TraceStartupConfig { ...@@ -123,21 +123,12 @@ class TRACING_EXPORT TraceStartupConfig {
base::trace_event::TraceConfig GetTraceConfig() const; base::trace_event::TraceConfig GetTraceConfig() const;
int GetStartupDuration() const; int GetStartupDuration() const;
// Returns true while startup tracing is not finished, if trace should be // Returns the name of the file to write the trace result into.
// saved to result file.
bool ShouldTraceToResultFile() const;
base::FilePath GetResultFile() const; base::FilePath GetResultFile() const;
void OnTraceToResultFileFinished();
// Set the background tracing config in preferences for the next session. // Set the background tracing config in preferences for the next session.
void SetBackgroundStartupTracingEnabled(bool enabled); void SetBackgroundStartupTracingEnabled(bool enabled);
// Returns when the startup tracing is finished and written to file, false on
// all other cases.
bool finished_writing_to_file_for_testing() const {
return finished_writing_to_file_;
}
SessionOwner GetSessionOwner() const; SessionOwner GetSessionOwner() const;
OutputFormat GetOutputFormat() const; OutputFormat GetOutputFormat() const;
...@@ -170,9 +161,7 @@ class TRACING_EXPORT TraceStartupConfig { ...@@ -170,9 +161,7 @@ class TRACING_EXPORT TraceStartupConfig {
bool enable_background_tracing_for_testing_ = false; bool enable_background_tracing_for_testing_ = false;
base::trace_event::TraceConfig trace_config_; base::trace_event::TraceConfig trace_config_;
int startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds; int startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds;
bool should_trace_to_result_file_ = false;
base::FilePath result_file_; base::FilePath result_file_;
bool finished_writing_to_file_ = false;
SessionOwner session_owner_ = SessionOwner::kTracingController; SessionOwner session_owner_ = SessionOwner::kTracingController;
bool session_adopted_ = false; bool session_adopted_ = false;
OutputFormat output_format_ = OutputFormat::kLegacyJSON; OutputFormat output_format_ = OutputFormat::kLegacyJSON;
......
...@@ -18,14 +18,25 @@ const char kTraceConfigFile[] = "trace-config-file"; ...@@ -18,14 +18,25 @@ const char kTraceConfigFile[] = "trace-config-file";
// specify the specific trace categories to include (e.g. // specify the specific trace categories to include (e.g.
// --trace-startup=base,net) otherwise, all events are recorded. Setting this // --trace-startup=base,net) otherwise, all events are recorded. Setting this
// flag results in the first call to BeginTracing() to receive all trace events // flag results in the first call to BeginTracing() to receive all trace events
// since startup. In Chrome, you may find --trace-startup-file and // since startup.
//
// Historically, --trace-startup was used for browser startup profiling and
// --enable-tracing was used for browsertest tracing. Now they are share the
// same implementation, but both are still supported to avoid disrupting
// existing workflows. The only difference between them is the default duration
// (5 seconds for trace-startup, unlimited for enable-tracing). If both are
// specified, 'trace-startup' takes precedence.
//
// In Chrome, you may find --trace-startup-file and
// --trace-startup-duration to control the auto-saving of the trace (not // --trace-startup-duration to control the auto-saving of the trace (not
// supported in the base-only TraceLog component). // supported in the base-only TraceLog component).
const char kTraceStartup[] = "trace-startup"; const char kTraceStartup[] = "trace-startup";
const char kEnableTracing[] = "enable-tracing";
// Sets the time in seconds until startup tracing ends. If omitted a default of // Sets the time in seconds until startup tracing ends. If omitted:
// 5 seconds is used. Has no effect without --trace-startup, or if // - if --trace-startup is specified, a default of 5 seconds is used.
// --startup-trace-file=none was supplied. // - if --enable-tracing is specified, tracing lasts until the browser is
// closed. Has no effect otherwise.
const char kTraceStartupDuration[] = "trace-startup-duration"; const char kTraceStartupDuration[] = "trace-startup-duration";
// If supplied, sets the file which startup tracing will be stored into, if // If supplied, sets the file which startup tracing will be stored into, if
...@@ -37,6 +48,15 @@ const char kTraceStartupDuration[] = "trace-startup-duration"; ...@@ -37,6 +48,15 @@ const char kTraceStartupDuration[] = "trace-startup-duration";
// all events since startup. // all events since startup.
const char kTraceStartupFile[] = "trace-startup-file"; const char kTraceStartupFile[] = "trace-startup-file";
// Similar to the flag above, with the following differences:
// - A more detailed basename will be generated.
// - If the value is empty or ends with path separator, the provided directory
// will be used (with empty standing for current directory) and a detailed
// basename file will be generated.
//
// It is ignored if --trace-startup-file is specified.
const char kEnableTracingOutput[] = "enable-tracing-output";
// Sets the output format for the trace, valid values are "json" and "proto". // Sets the output format for the trace, valid values are "json" and "proto".
// If not set, the current default is "json". // If not set, the current default is "json".
// "proto", unlike json, supports writing the trace into the output file // "proto", unlike json, supports writing the trace into the output file
...@@ -44,6 +64,7 @@ const char kTraceStartupFile[] = "trace-startup-file"; ...@@ -44,6 +64,7 @@ const char kTraceStartupFile[] = "trace-startup-file";
// unexpectedly terminates. // unexpectedly terminates.
// Ignored if "trace-startup-owner" is not "controller". // Ignored if "trace-startup-owner" is not "controller".
const char kTraceStartupFormat[] = "trace-startup-format"; const char kTraceStartupFormat[] = "trace-startup-format";
const char kEnableTracingFormat[] = "enable-tracing-format";
// If supplied, sets the tracing record mode and options; otherwise, the default // If supplied, sets the tracing record mode and options; otherwise, the default
// "record-until-full" mode will be used. // "record-until-full" mode will be used.
......
...@@ -12,10 +12,13 @@ namespace switches { ...@@ -12,10 +12,13 @@ namespace switches {
TRACING_EXPORT extern const char kEnableBackgroundTracing[]; TRACING_EXPORT extern const char kEnableBackgroundTracing[];
TRACING_EXPORT extern const char kTraceConfigFile[]; TRACING_EXPORT extern const char kTraceConfigFile[];
TRACING_EXPORT extern const char kTraceStartup[]; TRACING_EXPORT extern const char kTraceStartup[];
TRACING_EXPORT extern const char kEnableTracing[];
TRACING_EXPORT extern const char kTraceStartupDuration[]; TRACING_EXPORT extern const char kTraceStartupDuration[];
TRACING_EXPORT extern const char kTraceStartupFile[]; TRACING_EXPORT extern const char kTraceStartupFile[];
TRACING_EXPORT extern const char kEnableTracingOutput[];
TRACING_EXPORT extern const char kTraceStartupRecordMode[]; TRACING_EXPORT extern const char kTraceStartupRecordMode[];
TRACING_EXPORT extern const char kTraceStartupFormat[]; TRACING_EXPORT extern const char kTraceStartupFormat[];
TRACING_EXPORT extern const char kEnableTracingFormat[];
TRACING_EXPORT extern const char kTraceStartupOwner[]; TRACING_EXPORT extern const char kTraceStartupOwner[];
TRACING_EXPORT extern const char kTraceStartupEnablePrivacyFiltering[]; TRACING_EXPORT extern const char kTraceStartupEnablePrivacyFiltering[];
TRACING_EXPORT extern const char kPerfettoDisableInterning[]; TRACING_EXPORT extern const char kPerfettoDisableInterning[];
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "components/tracing/common/trace_startup_config.h" #include "components/tracing/common/trace_startup_config.h"
#include "components/tracing/common/tracing_switches.h" #include "components/tracing/common/tracing_switches.h"
#include "content/browser/tracing/startup_tracing_controller.h"
#include "content/browser/tracing/tracing_controller_impl.h" #include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
...@@ -42,52 +43,6 @@ void WaitForCondition(base::RepeatingCallback<bool()> condition, ...@@ -42,52 +43,6 @@ void WaitForCondition(base::RepeatingCallback<bool()> condition,
} // namespace } // namespace
class CommandlineStartupTracingTest : public ContentBrowserTest {
public:
CommandlineStartupTracingTest() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
base::CreateTemporaryFile(&temp_file_path_);
command_line->AppendSwitch(switches::kTraceStartup);
command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "3");
command_line->AppendSwitchASCII(switches::kTraceStartupFile,
temp_file_path_.AsUTF8Unsafe());
}
protected:
base::FilePath temp_file_path_;
private:
DISALLOW_COPY_AND_ASSIGN(CommandlineStartupTracingTest);
};
// Failing on Android/Win ASAN, Linux TSAN. crbug.com/1041392
#if (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
(defined(OS_WIN) && defined(ADDRESS_SANITIZER)) || \
((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER))
#define MAYBE_TestStartupTracing DISABLED_TestStartupTracing
#else
#define MAYBE_TestStartupTracing TestStartupTracing
#endif
IN_PROC_BROWSER_TEST_F(CommandlineStartupTracingTest,
MAYBE_TestStartupTracing) {
EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
WaitForCondition(base::BindRepeating([]() {
return tracing::TraceStartupConfig::GetInstance()
->finished_writing_to_file_for_testing();
}),
"finish file write");
std::string trace;
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::ReadFileToString(temp_file_path_, &trace));
EXPECT_TRUE(base::JSONReader::Read(trace));
EXPECT_TRUE(trace.find("StartupTracingController::Start") !=
std::string::npos);
}
#undef MAYBE_TestStartupTracing
class StartupTracingInProcessTest : public ContentBrowserTest { class StartupTracingInProcessTest : public ContentBrowserTest {
public: public:
StartupTracingInProcessTest() { StartupTracingInProcessTest() {
...@@ -154,4 +109,210 @@ IN_PROC_BROWSER_TEST_F(StartupTracingInProcessTest, TestFilledStartupBuffer) { ...@@ -154,4 +109,210 @@ IN_PROC_BROWSER_TEST_F(StartupTracingInProcessTest, TestFilledStartupBuffer) {
wait_for_stop.Run(); wait_for_stop.Run();
} }
namespace {
enum class FinishType {
kWaitForTimeout,
kStopExplicitly,
};
std::ostream& operator<<(std::ostream& o, FinishType type) {
switch (type) {
case FinishType::kStopExplicitly:
o << "Stop";
return o;
case FinishType::kWaitForTimeout:
o << "Wait";
return o;
}
}
enum class OutputType {
kProto,
kJSON,
};
std::ostream& operator<<(std::ostream& o, OutputType type) {
switch (type) {
case OutputType::kJSON:
o << "json";
return o;
case OutputType::kProto:
o << "proto";
return o;
}
}
enum class OutputLocation {
// Write trace to a given file.
kGivenFile,
// Write trace into a given directory (basename will be set to trace1 before
// starting).
kDirectoryWithDefaultBasename,
// Write trace into a given directory (basename will be set to trace1 before
// starting, and updated to trace2 before calling Stop()).
kDirectoryWithBasenameUpdatedBeforeStop,
};
std::ostream& operator<<(std::ostream& o, OutputLocation type) {
switch (type) {
case OutputLocation::kGivenFile:
o << "file";
return o;
case OutputLocation::kDirectoryWithDefaultBasename:
o << "dir/trace1";
return o;
case OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop:
o << "dir/trace2";
return o;
}
}
} // namespace
class StartupTracingTest
: public ContentBrowserTest,
public testing::WithParamInterface<
std::tuple<FinishType, OutputType, OutputLocation>> {
public:
StartupTracingTest() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kTraceStartup);
if (GetFinishType() == FinishType::kWaitForTimeout) {
command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "3");
} else {
command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "0");
}
command_line->AppendSwitchASCII(switches::kTraceStartupFormat,
GetOutputTypeAsString());
if (GetOutputLocation() == OutputLocation::kGivenFile) {
base::CreateTemporaryFile(&temp_file_path_);
} else {
base::CreateNewTempDirectory(base::FilePath::StringType(),
&temp_file_path_);
temp_file_path_ = temp_file_path_.AsEndingWithSeparator();
}
command_line->AppendSwitchASCII(switches::kEnableTracingOutput,
temp_file_path_.AsUTF8Unsafe());
if (GetOutputLocation() != OutputLocation::kGivenFile) {
// --enable-tracing-format switch should be initialised before
// calling SetDefaultBasenameForTest, which forces the creation of
// TraceStartupConfig, which queries the command line flags and
// stores the snapshot.
StartupTracingController::GetInstance().SetDefaultBasenameForTest(
"trace1",
StartupTracingController::ExtensionType::kAppendAppropriate);
}
}
FinishType GetFinishType() { return std::get<0>(GetParam()); }
OutputType GetOutputType() { return std::get<1>(GetParam()); }
std::string GetOutputTypeAsString() {
switch (GetOutputType()) {
case OutputType::kJSON:
return "json";
case OutputType::kProto:
return "proto";
}
}
OutputLocation GetOutputLocation() { return std::get<2>(GetParam()); }
base::FilePath GetExpectedPath() {
std::string filename;
switch (GetOutputLocation()) {
case OutputLocation::kGivenFile:
return temp_file_path_;
case OutputLocation::kDirectoryWithDefaultBasename:
filename = "trace1";
break;
case OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop:
filename = "trace2";
break;
}
// Renames are not supported together with timeouts.
if (GetFinishType() == FinishType::kWaitForTimeout)
filename = "trace1";
return temp_file_path_.AppendASCII(filename + "." +
GetOutputTypeAsString());
}
void CheckOutput(base::FilePath path) {
std::string trace;
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::ReadFileToString(path, &trace))
<< "Failed to read file " << path;
if (GetOutputType() == OutputType::kJSON) {
EXPECT_TRUE(base::JSONReader::Read(trace));
}
// Both proto and json should have the trace event name recorded somewhere
// as a substring.
EXPECT_TRUE(trace.find("StartupTracingController::Start") !=
std::string::npos);
}
void Wait() {
if (GetFinishType() == FinishType::kWaitForTimeout) {
WaitForCondition(base::BindRepeating([]() {
return StartupTracingController::GetInstance()
.is_finished_for_testing();
}),
"finish file write");
} else {
StartupTracingController::GetInstance().WaitUntilStopped();
}
}
protected:
base::FilePath temp_file_path_;
private:
DISALLOW_COPY_AND_ASSIGN(StartupTracingTest);
};
INSTANTIATE_TEST_SUITE_P(
All,
StartupTracingTest,
testing::Combine(
testing::Values(FinishType::kStopExplicitly,
FinishType::kWaitForTimeout),
testing::Values(OutputType::kJSON, OutputType::kProto),
testing::Values(
OutputLocation::kGivenFile,
OutputLocation::kDirectoryWithDefaultBasename,
OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop)));
// Failing on Android/Win ASAN, Linux TSAN. crbug.com/1041392
#if (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
(defined(OS_WIN) && defined(ADDRESS_SANITIZER)) || \
((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER))
#define MAYBE_TestEnableTracing DISABLED_TestStartupTracing
#else
#define MAYBE_TestEnableTracing TestStartupTracing
#endif
IN_PROC_BROWSER_TEST_P(StartupTracingTest, TestEnableTracing) {
EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
if (GetOutputLocation() ==
OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop) {
StartupTracingController::GetInstance().SetDefaultBasenameForTest(
"trace2", StartupTracingController::ExtensionType::kAppendAppropriate);
}
Wait();
CheckOutput(GetExpectedPath());
}
} // namespace content } // namespace content
...@@ -6,13 +6,14 @@ ...@@ -6,13 +6,14 @@
#define CONTENT_BROWSER_TRACING_STARTUP_TRACING_CONTROLLER_H_ #define CONTENT_BROWSER_TRACING_STARTUP_TRACING_CONTROLLER_H_
#include "base/threading/sequence_bound.h" #include "base/threading/sequence_bound.h"
#include "content/common/content_export.h"
namespace content { namespace content {
// Class responsible for starting and stopping startup tracing as configured by // Class responsible for starting and stopping startup tracing as configured by
// StartupTracingConfig. All interactions with it are limited to UI thread, but // StartupTracingConfig. All interactions with it are limited to UI thread, but
// the actual logic lives on a background ThreadPool sequence. // the actual logic lives on a background ThreadPool sequence.
class StartupTracingController { class CONTENT_EXPORT StartupTracingController {
public: public:
StartupTracingController(); StartupTracingController();
~StartupTracingController(); ~StartupTracingController();
...@@ -22,16 +23,55 @@ class StartupTracingController { ...@@ -22,16 +23,55 @@ class StartupTracingController {
void StartIfNeeded(); void StartIfNeeded();
void WaitUntilStopped(); void WaitUntilStopped();
// By default, a trace is written into a temporary file which then is renamed,
// however this can lead to data loss when the browser process crashes.
// Embedders can disable this (especially if a name provided to
// SetDefaultBasename makes it clear that the trace is incomplete and final
// name will be provided via SetDefaultBasename call before calling Stop).
enum class TempFilePolicy {
kUseTemporaryFile,
kWriteDirectly,
};
void SetUsingTemporaryFile(TempFilePolicy temp_file_policy);
// Set default basename for the trace output file to allow //content embedders
// to customise it using some metadata (like test names).
//
// If --enable-trace-output is a directory (default value, empty, designated
// "current directory"), then the startup trace will be written in a file with
// the given basename in this directory. Depending on the |extension_type|,
// an appropriate extension (.json or .proto) will be added.
//
// Note that embedders can call it even after tracing has started and Perfetto
// started streaming the trace into it — in that case,
// StartupTracingController will rename the file after finishing. However,
// this is guaranteed to work only when tracing lasts until Stop() (not with
// duration-based tracing).
enum class ExtensionType {
kAppendAppropriate,
kNone,
};
void SetDefaultBasename(std::string basename, ExtensionType extension_type);
// As the test harness calls SetDefaultBasename, expose ForTest() version for
// the tests checking the StartupTracingController logic itself.
void SetDefaultBasenameForTest(std::string basename,
ExtensionType extension_type);
bool is_finished_for_testing() const { return state_ == State::kStopped; }
private: private:
void Stop(base::OnceClosure on_finished_callback); void Stop(base::OnceClosure on_finished_callback);
void OnStoppedOnUIThread(); void OnStoppedOnUIThread();
base::FilePath GetOutputPath();
enum class State { enum class State {
kNotEnabled,
kRunning, kRunning,
kNotRunning, kStopped,
}; };
State state_ = State::kNotRunning; State state_ = State::kNotEnabled;
// All actual interactions with the tracing service and the process of writing // All actual interactions with the tracing service and the process of writing
// files happens on a background thread. // files happens on a background thread.
...@@ -40,6 +80,11 @@ class StartupTracingController { ...@@ -40,6 +80,11 @@ class StartupTracingController {
base::OnceClosure on_tracing_finished_; base::OnceClosure on_tracing_finished_;
base::FilePath output_file_; base::FilePath output_file_;
std::string default_basename_;
bool basename_for_test_set_ = false;
TempFilePolicy temp_file_policy_ = TempFilePolicy::kUseTemporaryFile;
}; };
} // namespace content } // namespace content
......
...@@ -402,14 +402,6 @@ const char kEnableStrictPowerfulFeatureRestrictions[] = ...@@ -402,14 +402,6 @@ const char kEnableStrictPowerfulFeatureRestrictions[] =
// Enabled threaded compositing for web tests. // Enabled threaded compositing for web tests.
const char kEnableThreadedCompositing[] = "enable-threaded-compositing"; const char kEnableThreadedCompositing[] = "enable-threaded-compositing";
// Enable tracing during the execution of browser tests.
const char kEnableTracing[] = "enable-tracing";
// The filename to write the output of the test tracing to. If it is empty
// or it ends in a directory separator then an auto-generated filename will be
// appended.
const char kEnableTracingOutput[] = "enable-tracing-output";
// Enable screen capturing support for MediaStream API. // Enable screen capturing support for MediaStream API.
const char kEnableUserMediaScreenCapturing[] = const char kEnableUserMediaScreenCapturing[] =
"enable-usermedia-screen-capturing"; "enable-usermedia-screen-capturing";
......
...@@ -123,8 +123,6 @@ CONTENT_EXPORT extern const char kEnableSpatialNavigation[]; ...@@ -123,8 +123,6 @@ CONTENT_EXPORT extern const char kEnableSpatialNavigation[];
CONTENT_EXPORT extern const char kEnableStrictMixedContentChecking[]; CONTENT_EXPORT extern const char kEnableStrictMixedContentChecking[];
CONTENT_EXPORT extern const char kEnableStrictPowerfulFeatureRestrictions[]; CONTENT_EXPORT extern const char kEnableStrictPowerfulFeatureRestrictions[];
CONTENT_EXPORT extern const char kEnableThreadedCompositing[]; CONTENT_EXPORT extern const char kEnableThreadedCompositing[];
CONTENT_EXPORT extern const char kEnableTracing[];
CONTENT_EXPORT extern const char kEnableTracingOutput[];
CONTENT_EXPORT extern const char kEnableUserMediaScreenCapturing[]; CONTENT_EXPORT extern const char kEnableUserMediaScreenCapturing[];
CONTENT_EXPORT extern const char kEnableUseZoomForDSF[]; CONTENT_EXPORT extern const char kEnableUseZoomForDSF[];
CONTENT_EXPORT extern const char kEnableViewport[]; CONTENT_EXPORT extern const char kEnableViewport[];
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "content/browser/startup_helper.h" #include "content/browser/startup_helper.h"
#include "content/browser/storage_partition_impl.h" #include "content/browser/storage_partition_impl.h"
#include "content/browser/tracing/memory_instrumentation_util.h" #include "content/browser/tracing/memory_instrumentation_util.h"
#include "content/browser/tracing/startup_tracing_controller.h"
#include "content/browser/tracing/tracing_controller_impl.h" #include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/app/content_main.h" #include "content/public/app/content_main.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
...@@ -166,10 +167,47 @@ void RunTaskOnRendererThread(base::OnceClosure task, ...@@ -166,10 +167,47 @@ void RunTaskOnRendererThread(base::OnceClosure task,
GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(quit_task)); GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(quit_task));
} }
void TraceStopTracingComplete(base::OnceClosure quit, enum class TraceBasenameType {
const base::FilePath& file_path) { kWithoutTestStatus,
LOG(ERROR) << "Tracing written to: " << file_path.value(); kWithTestStatus,
std::move(quit).Run(); };
std::string GetDefaultTraceBasename(TraceBasenameType type) {
std::string test_suite_name = ::testing::UnitTest::GetInstance()
->current_test_info()
->test_suite_name();
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
// Parameterised tests might have slashes in their full name — replace them
// before using it as a file name to avoid trying to write to an incorrect
// location.
base::ReplaceChars(test_suite_name, "/", "_", &test_suite_name);
base::ReplaceChars(test_name, "/", "_", &test_name);
// Add random number to the trace file to distinguish traces from different
// test runs. We don't use timestamp here to avoid collisions with parallel
// runs of the same test. Browser test runner runs one test per browser
// process instantiation, so saving the seed here is appopriate.
// GetDefaultTraceBasename() is going to be called twice:
// - for the first time, before the test starts to get the name of the file to
// stream the results (to avoid losing them if test crashes).
// - the second time, if test execution finishes normally, to calculate the
// resulting name of the file, including test result.
static std::string random_seed =
base::NumberToString(base::RandInt(1e7, 1e8 - 1));
std::string status;
if (type == TraceBasenameType::kWithTestStatus) {
status = ::testing::UnitTest::GetInstance()
->current_test_info()
->result()
->Passed()
? "OK"
: "FAIL";
} else {
// In order to be able to stream the test to the file,
status = "NOT_FINISHED";
}
return "trace_test_" + test_suite_name + "_" + test_name + "_" + random_seed +
"_" + status;
} }
// See SetInitialWebContents comment for more information. // See SetInitialWebContents comment for more information.
...@@ -483,6 +521,16 @@ void BrowserTestBase::SetUp() { ...@@ -483,6 +521,16 @@ void BrowserTestBase::SetUp() {
std::make_unique<CreatedMainPartsClosure>(base::BindOnce( std::make_unique<CreatedMainPartsClosure>(base::BindOnce(
&BrowserTestBase::CreatedBrowserMainParts, base::Unretained(this))); &BrowserTestBase::CreatedBrowserMainParts, base::Unretained(this)));
// If tracing is enabled, customise the output filename based on the name of
// the test.
StartupTracingController::GetInstance().SetDefaultBasename(
GetDefaultTraceBasename(TraceBasenameType::kWithoutTestStatus),
StartupTracingController::ExtensionType::kAppendAppropriate);
// Write to the provided file directly to recover at least some data when the
// test crashes or times out.
StartupTracingController::GetInstance().SetUsingTemporaryFile(
StartupTracingController::TempFilePolicy::kWriteDirectly);
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// For all other platforms, we call ContentMain for browser tests which goes // For all other platforms, we call ContentMain for browser tests which goes
// through the normal browser initialization paths. For Android, we must set // through the normal browser initialization paths. For Android, we must set
...@@ -670,36 +718,6 @@ void BrowserTestBase::WaitUntilJavaIsReady(base::OnceClosure quit_closure) { ...@@ -670,36 +718,6 @@ void BrowserTestBase::WaitUntilJavaIsReady(base::OnceClosure quit_closure) {
} }
#endif #endif
namespace {
std::string GetDefaultTraceFilename() {
std::string test_suite_name = ::testing::UnitTest::GetInstance()
->current_test_info()
->test_suite_name();
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
// Parameterised tests might have slashes in their full name — replace them
// before using it as a file name to avoid trying to write to an incorrect
// location.
base::ReplaceChars(test_suite_name, "/", "_", &test_suite_name);
base::ReplaceChars(test_name, "/", "_", &test_name);
// Add random number to the trace file to distinguish traces from different
// test runs.
// We don't use timestamp here to avoid collisions with parallel runs of the
// same test.
std::string random_seed = base::NumberToString(base::RandInt(1e7, 1e8 - 1));
std::string status = ::testing::UnitTest::GetInstance()
->current_test_info()
->result()
->Passed()
? "OK"
: "FAIL";
return "trace_test_" + test_suite_name + "_" + test_name + "_" + random_seed +
"_" + status + ".json";
}
} // namespace
void BrowserTestBase::ProxyRunTestOnMainThreadLoop() { void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
// All FeatureList overrides should have been registered prior to browser test // All FeatureList overrides should have been registered prior to browser test
...@@ -728,17 +746,6 @@ void BrowserTestBase::ProxyRunTestOnMainThreadLoop() { ...@@ -728,17 +746,6 @@ void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
signal(SIGTERM, DumpStackTraceSignalHandler); signal(SIGTERM, DumpStackTraceSignalHandler);
#endif // defined(OS_POSIX) #endif // defined(OS_POSIX)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableTracing)) {
base::trace_event::TraceConfig trace_config(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kEnableTracing),
base::trace_event::RECORD_CONTINUOUSLY);
TracingController::GetInstance()->StartTracing(
trace_config,
TracingController::StartTracingDoneCallback());
}
{ {
// This can be called from a posted task. Allow nested tasks here, because // This can be called from a posted task. Allow nested tasks here, because
// otherwise the test body will have to do it in order to use RunLoop for // otherwise the test body will have to do it in order to use RunLoop for
...@@ -782,31 +789,23 @@ void BrowserTestBase::ProxyRunTestOnMainThreadLoop() { ...@@ -782,31 +789,23 @@ void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
TearDownOnMainThread(); TearDownOnMainThread();
} }
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableTracing)) {
base::FilePath trace_file =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kEnableTracingOutput);
// If |trace_file| ends in a directory separator or is empty use a generated
// name in that directory (empty means current directory).
if (trace_file.empty() || trace_file.EndsWithSeparator())
trace_file = trace_file.AppendASCII(GetDefaultTraceFilename());
// Wait for tracing to collect results from the renderers.
base::RunLoop run_loop;
TracingController::GetInstance()->StopTracing(
TracingControllerImpl::CreateFileEndpoint(
trace_file, base::BindOnce(&TraceStopTracingComplete,
run_loop.QuitClosure(), trace_file)));
run_loop.Run();
}
PostRunTestOnMainThread(); PostRunTestOnMainThread();
// Sometimes tests initialize a storage partition and the initialization // Sometimes tests initialize a storage partition and the initialization
// schedules some tasks which need to be executed before finishing tests. // schedules some tasks which need to be executed before finishing tests.
// Run these tasks. // Run these tasks.
content::RunAllPendingInMessageLoop(); content::RunAllPendingInMessageLoop();
// Update the trace output filename to include the test result.
StartupTracingController::GetInstance().SetDefaultBasename(
GetDefaultTraceBasename(TraceBasenameType::kWithTestStatus),
StartupTracingController::ExtensionType::kAppendAppropriate);
#if defined(OS_ANDROID)
// On Android, browser main runner is not shut down, so stop trace recording
// here.
StartupTracingController::GetInstance().WaitUntilStopped();
#endif
} }
void BrowserTestBase::SetAllowNetworkAccessToHostResolutions() { void BrowserTestBase::SetAllowNetworkAccessToHostResolutions() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment