diff --git a/common/settings.h b/common/settings.h index d9df7e707b172..eb07585a33fb0 100644 --- a/common/settings.h +++ b/common/settings.h @@ -146,6 +146,7 @@ struct Settings { std::optional> trace_skia_allowlist; bool trace_startup = false; bool trace_systrace = false; + std::string trace_to_file; bool enable_timeline_event_handler = true; bool dump_skp_on_shader_compilation = false; bool cache_sksl = false; diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index fb1349214349a..48715c5cc2eec 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -99,6 +99,12 @@ static const char* kDartSystraceTraceBufferArgs[] = { "--systrace_timeline", }; +static std::string DartFileRecorderArgs(const std::string& path) { + std::ostringstream oss; + oss << "--timeline_recorder=perfettofile:" << path; + return oss.str(); +} + FML_ALLOW_UNUSED_TYPE static const char* kDartDefaultTraceStreamsArgs[]{ "--timeline_streams=Dart,Embedder,GC", @@ -390,6 +396,14 @@ DartVM::DartVM(const std::shared_ptr& vm_data, fml::size(kDartSystraceTraceStreamsArgs)); } + std::string file_recorder_args; + if (!settings_.trace_to_file.empty()) { + file_recorder_args = DartFileRecorderArgs(settings_.trace_to_file); + args.push_back(file_recorder_args.c_str()); + PushBackAll(&args, kDartSystraceTraceStreamsArgs, + fml::size(kDartSystraceTraceStreamsArgs)); + } + if (settings_.trace_startup) { PushBackAll(&args, kDartStartupTraceStreamsArgs, fml::size(kDartStartupTraceStreamsArgs)); diff --git a/shell/common/switches.cc b/shell/common/switches.cc index e0d4ae3d5826e..f9cbb548cc0f5 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -354,6 +354,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.trace_systrace = command_line.HasOption(FlagForSwitch(Switch::TraceSystrace)); + command_line.GetOptionValue(FlagForSwitch(Switch::TraceToFile), + &settings.trace_to_file); + settings.skia_deterministic_rendering_on_cpu = command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering)); diff --git a/shell/common/switches.h b/shell/common/switches.h index c8a75f778664c..7dd04fc7e12e4 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -197,6 +197,11 @@ DEF_SWITCH( "Trace to the system tracer (instead of the timeline) on platforms where " "such a tracer is available. Currently only supported on Android and " "Fuchsia.") +DEF_SWITCH(TraceToFile, + "trace-to-file", + "Write the timeline trace to a file at the specified path. The file " + "will be in Perfetto's proto format; it will be possible to load " + "the file into Perfetto's trace viewer.") DEF_SWITCH(UseTestFonts, "use-test-fonts", "Running tests that layout and measure text will not yield " diff --git a/shell/common/switches_unittests.cc b/shell/common/switches_unittests.cc index 0307b9d62bf91..617c0a891cfa7 100644 --- a/shell/common/switches_unittests.cc +++ b/shell/common/switches_unittests.cc @@ -51,6 +51,14 @@ TEST(SwitchesTest, SkiaTraceAllowlistFlag) { #endif } +TEST(SwitchesTest, TraceToFile) { + fml::CommandLine command_line = fml::CommandLineFromInitializerList( + {"command", "--trace-to-file=trace.binpb"}); + EXPECT_TRUE(command_line.HasOption("trace-to-file")); + Settings settings = SettingsFromCommandLine(command_line); + EXPECT_EQ(settings.trace_to_file, "trace.binpb"); +} + TEST(SwitchesTest, RouteParsedFlag) { fml::CommandLine command_line = fml::CommandLineFromInitializerList({"command", "--route=/animation"}); diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index bcc54b1e9786f..b57e20c061dfa 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -314,6 +314,9 @@ private static String[] getArgsFromIntent(Intent intent) { if (intent.getBooleanExtra("trace-systrace", false)) { args.add("--trace-systrace"); } + if (intent.hasExtra("trace-to-file")) { + args.add("--trace-to-file=" + intent.getStringExtra("trace-to-file")); + } if (intent.getBooleanExtra("dump-skp-on-shader-compilation", false)) { args.add("--dump-skp-on-shader-compilation"); } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java index e97eb66bfce05..b3775217a86fe 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java @@ -42,6 +42,8 @@ public class FlutterShellArgs { public static final String ARG_TRACE_SKIA_ALLOWLIST = "--trace-skia-allowlist="; public static final String ARG_KEY_TRACE_SYSTRACE = "trace-systrace"; public static final String ARG_TRACE_SYSTRACE = "--trace-systrace"; + public static final String ARG_KEY_TRACE_TO_FILE = "trace-to-file"; + public static final String ARG_TRACE_TO_FILE = "--trace-to-file"; public static final String ARG_KEY_ENABLE_IMPELLER = "enable-impeller"; public static final String ARG_ENABLE_IMPELLER = "--enable-impeller"; public static final String ARG_KEY_ENABLE_VULKAN_VALIDATION = "enable-vulkan-validation"; @@ -120,6 +122,9 @@ public static FlutterShellArgs fromIntent(@NonNull Intent intent) { if (intent.getBooleanExtra(ARG_KEY_TRACE_SYSTRACE, false)) { args.add(ARG_TRACE_SYSTRACE); } + if (intent.hasExtra(ARG_KEY_TRACE_TO_FILE)) { + args.add(ARG_TRACE_TO_FILE + "=" + intent.getStringExtra(ARG_KEY_TRACE_TO_FILE)); + } if (intent.getBooleanExtra(ARG_KEY_ENABLE_IMPELLER, false)) { args.add(ARG_ENABLE_IMPELLER); }