本文主要对比Flutter编辑engine中两种runtime mode(debug release)的区别
首先使用gn构建两种模式的engine
./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm64
./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm64
两个命令背后的真实动作
//debug
gn gen --check --ide=xcode out/ios_debug_arm --args=skia_enable_pdf=false enable_lto=true full_dart_sdk=false use_clang_static_analyzer=false flutter_enable_skshaper=false skia_use_expat=false enable_bitcode=false skia_use_fontconfig=false skia_use_dng_sdk=false skia_enable_flutter_defines=true use_goma=false dart_custom_version_for_pub="flutter" embedder_for_target=false is_official_build=true android_full_debug=false is_clang=true skia_use_sfntly=false dart_target_arch="arm" skia_gl_standard="gles" skia_use_wuffs=true flutter_use_fontconfig=false dart_component_kind="static_library" flutter_runtime_mode="debug" goma_dir="None" target_os="ios" skia_use_x11=false enable_coverage=false target_cpu="arm" dart_runtime_mode="develop" dart_lib_export_symbols=false is_debug=false use_ios_simulator=false
ninja -C out/ios_debug_arm -t compdb cc cxx objc objcxx asm
//release
gn gen --check --ide=xcode out/ios_release_arm --args=skia_enable_pdf=false enable_lto=true full_dart_sdk=false use_clang_static_analyzer=false flutter_enable_skshaper=false skia_use_expat=false enable_bitcode=false skia_use_fontconfig=false skia_use_dng_sdk=false skia_enable_flutter_defines=true use_goma=false dart_custom_version_for_pub="flutter" embedder_for_target=false is_official_build=true android_full_debug=false is_clang=true skia_use_sfntly=false dart_target_arch="arm" skia_gl_standard="gles" skia_use_wuffs=true flutter_use_fontconfig=false dart_component_kind="static_library" flutter_runtime_mode="release" goma_dir="None" target_os="ios" skia_use_x11=false enable_coverage=false target_cpu="arm" dart_runtime_mode="release" dart_lib_export_symbols=false is_debug=false use_ios_simulator=false
ninja -C out/ios_release_arm -t compdb cc cxx objc objcxx asm
可以看出两条命令的不同之处在于
//release
flutter_runtime_mode="release"
dart_runtime_mode="release"
//debug
flutter_runtime_mode="debug"
dart_runtime_mode="develop"
接下来分析compile_commands.json
忽略掉ios_debug ios_release
忽略掉-DFLUTTER_RUNTIME_MODE=1 -DFLUTTER_RUNTIME_MODE=3
忽略掉-DPRODUCT
剩下的不同之处
debug编译test_font.test_font_data.o.d 时多了个-DEMBED_TEST_FONT_DATA=1
release独有
{
"directory": "/Users/kila/Desktop/Workspace/engine/src/out/ios_release",
"command": "../../buildtools/mac-x64/clang/bin/clang++ -MD -MF obj/third_party/dart/runtime/bin/dart.observatory_assets_empty.o.d -DNO_TCMALLOC -DMEMORY_TOOL_REPLACES_ALLOCATOR -DMEMORY_SANITIZER_INITIAL_SIZE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -DTARGET_ARCH_ARM64 -DNDEBUG -DTARGET_OS_MACOS -DTARGET_OS_MACOS_IOS -DPRODUCT -I../../third_party/dart/runtime -I../../third_party -I../.. -Igen -I../../third_party/dart/runtime/include -I../../third_party/dart/runtime -I../../third_party/dart/runtime/include -I../../third_party/boringssl/src/include -I../../third_party/zlib -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.1.sdk -miphoneos-version-min=8.0 -flto -fno-strict-aliasing -arch arm64 -fcolor-diagnostics -Wall -Wextra -Wendif-labels -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wunguarded-availability -fvisibility=hidden -stdlib=libc++ -Wheader-hygiene -Wstring-conversion -Wthread-safety -Os -fno-ident -fdata-sections -ffunction-sections -g2 -Werror -Wall -Wextra -Wno-unused-parameter -Wno-unused-private-field -Wnon-virtual-dtor -Wvla -Wno-conversion-null -Woverloaded-virtual -Wno-comments -g3 -ggdb3 -fno-rtti -fno-exceptions -Wimplicit-fallthrough -O3 -fvisibility-inlines-hidden -std=c++17 -fno-rtti -fno-exceptions -c ../../third_party/dart/runtime/bin/observatory_assets_empty.cc -o obj/third_party/dart/runtime/bin/dart.observatory_assets_empty.o",
"file": "../../third_party/dart/runtime/bin/observatory_assets_empty.cc"
},
{
"directory": "/Users/kila/Desktop/Workspace/engine/src/out/ios_release",
"command": "../../buildtools/mac-x64/clang/bin/clang++ -MD -MF obj/third_party/dart/runtime/bin/dart_precompiled_runtime.observatory_assets_empty.o.d -DNO_TCMALLOC -DMEMORY_TOOL_REPLACES_ALLOCATOR -DMEMORY_SANITIZER_INITIAL_SIZE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -DTARGET_ARCH_ARM64 -DNDEBUG -DTARGET_OS_MACOS -DTARGET_OS_MACOS_IOS -DPRODUCT -DDART_PRECOMPILED_RUNTIME -DEXCLUDE_CFE_AND_KERNEL_PLATFORM -I../../third_party/dart/runtime -I../../third_party -I../.. -Igen -I../../third_party/dart/runtime/include -I../../third_party/dart/runtime -I../../third_party/dart/runtime/include -I../../third_party/boringssl/src/include -I../../third_party/zlib -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.1.sdk -miphoneos-version-min=8.0 -flto -fno-strict-aliasing -arch arm64 -fcolor-diagnostics -Wall -Wextra -Wendif-labels -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wunguarded-availability -fvisibility=hidden -stdlib=libc++ -Wheader-hygiene -Wstring-conversion -Wthread-safety -Os -fno-ident -fdata-sections -ffunction-sections -g2 -Werror -Wall -Wextra -Wno-unused-parameter -Wno-unused-private-field -Wnon-virtual-dtor -Wvla -Wno-conversion-null -Woverloaded-virtual -Wno-comments -g3 -ggdb3 -fno-rtti -fno-exceptions -Wimplicit-fallthrough -O3 -fvisibility-inlines-hidden -std=c++17 -fno-rtti -fno-exceptions -c ../../third_party/dart/runtime/bin/observatory_assets_empty.cc -o obj/third_party/dart/runtime/bin/dart_precompiled_runtime.observatory_assets_empty.o",
"file": "../../third_party/dart/runtime/bin/observatory_assets_empty.cc"
},
debug独有
{
"directory": "/Users/kila/Desktop/Workspace/engine/src/out/ios_debug",
"command": "../../buildtools/mac-x64/clang/bin/clang++ -MD -MF obj/out/ios_debug/gen/third_party/dart/runtime/observatory/embedded_observatory_archive.embedded_archive_observatory.o.d -DNO_TCMALLOC -DMEMORY_TOOL_REPLACES_ALLOCATOR -DMEMORY_SANITIZER_INITIAL_SIZE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -I../.. -Igen -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.1.sdk -miphoneos-version-min=8.0 -flto -fno-strict-aliasing -arch arm64 -fcolor-diagnostics -Wall -Wextra -Wendif-labels -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wunguarded-availability -fvisibility=hidden -stdlib=libc++ -Wheader-hygiene -Wstring-conversion -Wthread-safety -Os -fno-ident -fdata-sections -ffunction-sections -g2 -fvisibility-inlines-hidden -std=c++17 -fno-rtti -fno-exceptions -c gen/third_party/dart/runtime/observatory/embedded_archive_observatory.cc -o obj/out/ios_debug/gen/third_party/dart/runtime/observatory/embedded_observatory_archive.embedded_archive_observatory.o",
"file": "gen/third_party/dart/runtime/observatory/embedded_archive_observatory.cc"
},
{
"directory": "/Users/kila/Desktop/Workspace/engine/src/out/ios_debug",
"command": "../../buildtools/mac-x64/clang/bin/clang++ -MD -MF obj/out/ios_debug/gen/third_party/dart/runtime/observatory/standalone_observatory_archive.standalone_archive_observatory.o.d -DNO_TCMALLOC -DMEMORY_TOOL_REPLACES_ALLOCATOR -DMEMORY_SANITIZER_INITIAL_SIZE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -I../.. -Igen -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.1.sdk -miphoneos-version-min=8.0 -flto -fno-strict-aliasing -arch arm64 -fcolor-diagnostics -Wall -Wextra -Wendif-labels -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wunguarded-availability -fvisibility=hidden -stdlib=libc++ -Wheader-hygiene -Wstring-conversion -Wthread-safety -Os -fno-ident -fdata-sections -ffunction-sections -g2 -fvisibility-inlines-hidden -std=c++17 -fno-rtti -fno-exceptions -c gen/third_party/dart/runtime/observatory/standalone_archive_observatory.cc -o obj/out/ios_debug/gen/third_party/dart/runtime/observatory/standalone_observatory_archive.standalone_archive_observatory.o",
"file": "gen/third_party/dart/runtime/observatory/standalone_archive_observatory.cc"
},
dart_runtime_mode 涉及的模块
third_party/dart/runtime/vm/BUILD.gn
is_product_flag = dart_runtime_mode == "release"
allow_causal_async_stacks = !is_product_flag
args += [
"-Ddart.vm.product=$is_product_flag",
"-Ddart.developer.causal_async_stacks=$allow_causal_async_stacks",
"-Ddart.isVM=true",
]
third_party/dart/runtime/runtime_args.gni
# TODO(rmacnak): dart_runtime_mode no longer selects whether libdart is build
# for JIT or AOT, since libdart waw split into libdart_jit and
# libdart_precompiled_runtime. We should remove this flag and just set
# dart_debug/dart_product.
dart_runtime_mode = "develop"
third_party/dart/runtime/BUILD.gn
# Adds PRODUCT define if Flutter has specified "release" for dart_runtime_mode
config("dart_maybe_product_config") {
defines = []
if (dart_runtime_mode != "develop" && dart_runtime_mode != "profile" &&
dart_runtime_mode != "release") {
print("Invalid |dart_runtime_mode|")
assert(false)
}
if (dart_runtime_mode == "release") {
if (dart_debug) {
print("Debug and release mode are mutually exclusive.")
}
assert(!dart_debug)
defines += [ "PRODUCT" ]
}
}
third_party/dart/runtime/bin/BUILD.gn
dart_executable("dart") {
extra_deps = [
"..:libdart_jit",
"../platform:libdart_platform_jit",
":dart_snapshot_cc",
]
if (dart_runtime_mode != "release") {
extra_deps += [ "../observatory:standalone_observatory_archive" ]
}
extra_sources = [
"builtin.cc",
"dfe.cc",
"dfe.h",
"gzip.cc",
"gzip.h",
"loader.cc",
"loader.h",
"main.cc",
]
if (dart_runtime_mode == "release") {
extra_sources += [ "observatory_assets_empty.cc" ]
}
if (!exclude_kernel_service) {
extra_deps += [ ":dart_kernel_platform_cc" ]
}
}
dart_executable("dart_precompiled_runtime") {
extra_configs = [ "..:dart_precompiled_runtime_config" ]
extra_deps = [
"..:libdart_precompiled_runtime",
"../platform:libdart_platform_precompiled_runtime",
]
if (dart_runtime_mode != "release") {
extra_deps += [ "../observatory:standalone_observatory_archive" ]
}
extra_sources = [
"builtin.cc",
"gzip.cc",
"gzip.h",
"loader.cc",
"loader.h",
"main.cc",
"snapshot_empty.cc",
]
if (dart_runtime_mode == "release") {
extra_sources += [ "observatory_assets_empty.cc" ]
}
}
flutter_runtime_mode涉及模块
flutter/common/config.gni
declare_args() {
# The runtime mode ("debug", "profile", or "release")
flutter_runtime_mode = "debug"
# Whether to use the Skia text shaper module
flutter_enable_skshaper = false
# A copy of the enable_bitcode flag from build/toolchain/clang.gni.
# This needs to be mirrored here because build/toolchain/clang.gni does
# not exist in the Fuchsia source tree.
flutter_enable_bitcode = false
}
feature_defines_list = [
"FLUTTER_RUNTIME_MODE_DEBUG=1",
"FLUTTER_RUNTIME_MODE_PROFILE=2",
"FLUTTER_RUNTIME_MODE_RELEASE=3",
]
if (flutter_runtime_mode == "debug") {
feature_defines_list += [ "FLUTTER_RUNTIME_MODE=1" ]
} else if (flutter_runtime_mode == "profile") {
feature_defines_list += [ "FLUTTER_RUNTIME_MODE=2" ]
} else if (flutter_runtime_mode == "release") {
feature_defines_list += [ "FLUTTER_RUNTIME_MODE=3" ]
} else {
feature_defines_list += [ "FLUTTER_RUNTIME_MODE=0" ]
}
flutter/flow/raster_cache.cc
void RasterCache::TraceStatsToTimeline() const {
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
size_t layer_cache_count = 0;
size_t layer_cache_bytes = 0;
size_t picture_cache_count = 0;
size_t picture_cache_bytes = 0;
for (const auto& item : layer_cache_) {
const auto dimensions = item.second.image.image_dimensions();
layer_cache_count++;
layer_cache_bytes += dimensions.width() * dimensions.height() * 4;
}
for (const auto& item : picture_cache_) {
const auto dimensions = item.second.image.image_dimensions();
picture_cache_count++;
picture_cache_bytes += dimensions.width() * dimensions.height() * 4;
}
FML_TRACE_COUNTER("flutter", "RasterCache",
reinterpret_cast<int64_t>(this), //
"LayerCount", layer_cache_count, //
"LayerMBytes", layer_cache_bytes * 1e-6, //
"PictureCount", picture_cache_count, //
"PictureMBytes", picture_cache_bytes * 1e-6 //
);
#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
}
flutter/lib/snapshot/BUILD.gn
if (is_debug && flutter_runtime_mode != "profile" &&
flutter_runtime_mode != "release") {
args += [ "--enable_asserts" ]
}
flutter/runtime/BUILD.gn(需要改动)
source_set("test_font") {
sources = [
"test_font_data.cc",
"test_font_data.h",
]
deps = [
"//third_party/skia",
]
public_configs = [ "$flutter_root:config" ]
defines = []
if (flutter_runtime_mode == "debug" || current_toolchain == host_toolchain) {
# Though the test font data is small, we dont want to add to the binary size
# on the device (in profile and release modes). We only add the same on the
# host test shells and the debug device shell.
defines += [ "EMBED_TEST_FONT_DATA=1" ]
}
}
# Picks the libdart implementation based on the Flutter runtime mode.
group("libdart") {
public_deps = []
if (!(is_fuchsia && using_fuchsia_sdk)) {
if (flutter_runtime_mode == "profile" ||
flutter_runtime_mode == "release") {
public_deps +=
[ "//third_party/dart/runtime:libdart_precompiled_runtime" ]
} else {
public_deps += [
"$flutter_root/lib/snapshot",
"//third_party/dart/runtime:libdart_jit",
]
}
}
}
flutter/runtime/dart_snapshot.cc
#define DART_SNAPSHOT_STATIC_LINK \
(OS_WIN || (OS_ANDROID && FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG))
#if !DART_SNAPSHOT_STATIC_LINK
static std::unique_ptr<const fml::Mapping> GetFileMapping(
const std::string& path,
bool executable) {
if (executable) {
return fml::FileMapping::CreateReadExecute(path);
} else {
return fml::FileMapping::CreateReadOnly(path);
}
}
// The first party embedders don't yet use the stable embedder API and depend on
// the engine figuring out the locations of the various heap and instructions
// buffers. Consequently, the engine had baked in opinions about where these
// buffers would reside and how they would be packaged (examples, in an external
// dylib, in the same dylib, at a path, at a path relative to and FD, etc..). As
// the needs of the platforms changed, the lack of an API meant that the engine
// had to be patched to look for new fields in the settings object. This grew
// untenable and with the addition of the new Fuchsia embedder and the generic C
// embedder API, embedders could specify the mapping directly. Once everyone
// moves to the embedder API, this method can effectively be reduced to just
// invoking the embedder_mapping_callback directly.
static std::shared_ptr<const fml::Mapping> SearchMapping(
MappingCallback embedder_mapping_callback,
const std::string& file_path,
const std::vector<std::string>& native_library_path,
const char* native_library_symbol_name,
bool is_executable) {
// Ask the embedder. There is no fallback as we expect the embedders (via
// their embedding APIs) to just specify the mappings directly.
if (embedder_mapping_callback) {
return embedder_mapping_callback();
}
// Attempt to open file at path specified.
if (file_path.size() > 0) {
if (auto file_mapping = GetFileMapping(file_path, is_executable)) {
return file_mapping;
}
}
// Look in application specified native library if specified.
for (const std::string& path : native_library_path) {
auto native_library = fml::NativeLibrary::Create(path.c_str());
auto symbol_mapping = std::make_unique<const fml::SymbolMapping>(
native_library, native_library_symbol_name);
if (symbol_mapping->GetMapping() != nullptr) {
return symbol_mapping;
}
}
// Look inside the currently loaded process.
{
auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess();
auto symbol_mapping = std::make_unique<const fml::SymbolMapping>(
loaded_process, native_library_symbol_name);
if (symbol_mapping->GetMapping() != nullptr) {
return symbol_mapping;
}
}
return nullptr;
}
#endif // !DART_SNAPSHOT_STATIC_LINK
static std::shared_ptr<const fml::Mapping> ResolveVMData(
const Settings& settings) {
#if DART_SNAPSHOT_STATIC_LINK
return std::make_unique<fml::NonOwnedMapping>(kDartVmSnapshotData, 0);
#else // DART_SNAPSHOT_STATIC_LINK
return SearchMapping(
settings.vm_snapshot_data, // embedder_mapping_callback
settings.vm_snapshot_data_path, // file_path
settings.application_library_path, // native_library_path
DartSnapshot::kVMDataSymbol, // native_library_symbol_name
false // is_executable
);
#endif // DART_SNAPSHOT_STATIC_LINK
}
static std::shared_ptr<const fml::Mapping> ResolveVMInstructions(
const Settings& settings) {
#if DART_SNAPSHOT_STATIC_LINK
return std::make_unique<fml::NonOwnedMapping>(kDartVmSnapshotInstructions, 0);
#else // DART_SNAPSHOT_STATIC_LINK
return SearchMapping(
settings.vm_snapshot_instr, // embedder_mapping_callback
settings.vm_snapshot_instr_path, // file_path
settings.application_library_path, // native_library_path
DartSnapshot::kVMInstructionsSymbol, // native_library_symbol_name
true // is_executable
);
#endif // DART_SNAPSHOT_STATIC_LINK
}
static std::shared_ptr<const fml::Mapping> ResolveIsolateData(
const Settings& settings) {
#if DART_SNAPSHOT_STATIC_LINK
return std::make_unique<fml::NonOwnedMapping>(kDartIsolateSnapshotData, 0);
#else // DART_SNAPSHOT_STATIC_LINK
return SearchMapping(
settings.isolate_snapshot_data, // embedder_mapping_callback
settings.isolate_snapshot_data_path, // file_path
settings.application_library_path, // native_library_path
DartSnapshot::kIsolateDataSymbol, // native_library_symbol_name
false // is_executable
);
#endif // DART_SNAPSHOT_STATIC_LINK
}
static std::shared_ptr<const fml::Mapping> ResolveIsolateInstructions(
const Settings& settings) {
#if DART_SNAPSHOT_STATIC_LINK
return std::make_unique<fml::NonOwnedMapping>(
kDartIsolateSnapshotInstructions, 0);
#else // DART_SNAPSHOT_STATIC_LINK
return SearchMapping(
settings.isolate_snapshot_instr, // embedder_mapping_callback
settings.isolate_snapshot_instr_path, // file_path
settings.application_library_path, // native_library_path
DartSnapshot::kIsolateInstructionsSymbol, // native_library_symbol_name
true // is_executable
);
#endif // DART_SNAPSHOT_STATIC_LINK
}
flutter/runtime/dart_vm.cc
namespace dart {
namespace observatory {
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE)
// These two symbols are defined in |observatory_archive.cc| which is generated
// by the |//third_party/dart/runtime/observatory:archive_observatory| rule.
// Both of these symbols will be part of the data segment and therefore are read
// only.
extern unsigned int observatory_assets_archive_len;
extern const uint8_t* observatory_assets_archive;
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE !=
// FLUTTER_RUNTIME_MODE_RELEASE)
} // namespace observatory
} // namespace dart
Dart_Handle GetVMServiceAssetsArchiveCallback() {
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE)
return nullptr;
#elif OS_FUCHSIA
fml::UniqueFD fd = fml::OpenFile("pkg/data/observatory.tar", false,
fml::FilePermission::kRead);
fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kRead});
if (mapping.GetSize() == 0 || mapping.GetMapping() == nullptr) {
FML_LOG(ERROR) << "Fail to load Observatory archive";
return nullptr;
}
return tonic::DartConverter<tonic::Uint8List>::ToDart(mapping.GetMapping(),
mapping.GetSize());
#else
return tonic::DartConverter<tonic::Uint8List>::ToDart(
::dart::observatory::observatory_assets_archive,
::dart::observatory::observatory_assets_archive_len);
#endif
}
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
#if !OS_IOS || TARGET_OS_SIMULATOR
// Debug mode uses the JIT, disable code page write protection to avoid
// memory page protection changes before and after every compilation.
PushBackAll(&args, kDartWriteProtectCodeArgs,
fml::size(kDartWriteProtectCodeArgs));
#else
EnsureDebuggedIOS(settings_);
#if TARGET_CPU_ARM
// Tell Dart in JIT mode to not use integer division on armv7
// Ideally, this would be detected at runtime by Dart.
// TODO(dnfield): Remove this code
// https://github.com/dart-lang/sdk/issues/24743
PushBackAll(&args, kDartDisableIntegerDivisionArgs,
fml::size(kDartDisableIntegerDivisionArgs));
#endif // TARGET_CPU_ARM
#endif // !OS_IOS || TARGET_OS_SIMULATOR
#endif // (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
flutter/runtime/ptrace_ios.cc
#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
// These headers should only be needed in debug mode.
#include <sys/sysctl.h>
#include <sys/types.h>
#define PT_TRACE_ME 0
#define PT_SIGEXC 12
extern "C" int ptrace(int request, pid_t pid, caddr_t addr, int data);
static bool DebuggedIOS(const flutter::Settings& vm_settings) {
// Only the Flutter CLI passes "--enable-checked-mode". Therefore, if the flag
// is present, we have been launched by "ios-deploy" via "debugserver".
//
// We choose this flag because it is always passed to launch debug builds.
if (vm_settings.enable_checked_mode) {
return true;
}
// Use "sysctl()" to check if we're currently being debugged (e.g. by Xcode).
// We could also check "getppid() != 1" (launchd), but this is more direct.
const pid_t self = getpid();
int mib[5] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, self, 0};
auto proc = std::make_unique<struct kinfo_proc>();
size_t proc_size = sizeof(struct kinfo_proc);
if (sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) {
FML_LOG(ERROR) << "Could not execute sysctl() to get current process info: "
<< strerror(errno);
return false;
}
return proc->kp_proc.p_flag & P_TRACED;
}
void EnsureDebuggedIOS(const flutter::Settings& vm_settings) {
if (DebuggedIOS(vm_settings)) {
return;
}
if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) {
FML_LOG(ERROR) << "Could not call ptrace(PT_TRACE_ME): " << strerror(errno);
// No use trying PT_SIGEXC -- it's only needed if PT_TRACE_ME succeeds.
return;
}
if (ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) {
FML_LOG(ERROR) << "Could not call ptrace(PT_SIGEXC): " << strerror(errno);
}
// The previous operation causes this process to not be reaped after it
// terminates (even if PT_SIGEXC fails). Issue a warning to the console every
// (approximiately) maxproc/10 leaks. See the links above for an explanation
// of this issue.
size_t maxproc = 0;
size_t maxproc_size = sizeof(size_t);
const int sysctl_result =
sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, nullptr, 0);
if (sysctl_result < 0) {
FML_LOG(ERROR)
<< "Could not execute sysctl() to determine process count limit: "
<< strerror(errno);
}
const char* warning =
"Launching a debug-mode app from the home screen may cause problems.\n"
"Please compile a profile-/release-build, launch your app via \"flutter "
"run\", or see https://github.com/flutter/flutter/wiki/"
"PID-leak-in-iOS-debug-builds-launched-from-home-screen for details.";
if (vm_settings.verbose_logging // used for testing and also informative
|| sysctl_result < 0 // could not determine maximum process count
|| maxproc / 10 == 0 // avoid division (%) by 0
|| getpid() % (maxproc / 10) == 0) // warning every ~maxproc/10 leaks
{
FML_LOG(ERROR) << warning;
}
}
#endif // OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
ptrace_ios.h
#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
// Ensure that the current process is or was ptrace()-d at some point in its
// life. Can only be used within debug builds for iOS.
void EnsureDebuggedIOS(const flutter::Settings& vm_settings);
#endif
flutter/shell/common/shell_unittests.cc
TEST_F(ShellTest, BlacklistedDartVMFlag) {
// Run this test in a thread-safe manner, otherwise gtest will complain.
::testing::FLAGS_gtest_death_test_style = "threadsafe";
const std::vector<fml::CommandLine::Option> options = {
fml::CommandLine::Option("dart-flags", "--verify_after_gc")};
fml::CommandLine command_line("", options, std::vector<std::string>());
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
// Upon encountering a non-whitelisted Dart flag the process terminates.
const char* expected =
"Encountered blacklisted Dart VM flag: --verify_after_gc";
ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected);
#else
flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
EXPECT_EQ(settings.dart_flags.size(), 0u);
#endif
}
TEST_F(ShellTest, WhitelistedDartVMFlag) {
const std::vector<fml::CommandLine::Option> options = {
fml::CommandLine::Option("dart-flags",
"--max_profile_depth 1,--random_seed 42")};
fml::CommandLine command_line("", options, std::vector<std::string>());
flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
EXPECT_EQ(settings.dart_flags.size(), 2u);
EXPECT_EQ(settings.dart_flags[0], "--max_profile_depth 1");
EXPECT_EQ(settings.dart_flags[1], "--random_seed 42");
#else
EXPECT_EQ(settings.dart_flags.size(), 0u);
#endif
}
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE
TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) {
#else
TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) {
#endif
fml::TimePoint start = fml::TimePoint::Now();
auto settings = CreateSettingsForFixture();
std::unique_ptr<Shell> shell = CreateShell(settings);
// Create the surface needed by rasterizer
PlatformViewNotifyCreated(shell.get());
auto configuration = RunConfiguration::InferFromSettings(settings);
ASSERT_TRUE(configuration.IsValid());
configuration.SetEntrypoint("reportTimingsMain");
// Wait for 2 reports: the first one is the immediate callback of the first
// frame; the second one will exercise the batching logic.
fml::CountDownLatch reportLatch(2);
std::vector<int64_t> timestamps;
auto nativeTimingCallback = [&reportLatch,
×tamps](Dart_NativeArguments args) {
Dart_Handle exception = nullptr;
timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
args, 0, exception);
reportLatch.CountDown();
};
AddNativeCallback("NativeReportTimingsCallback",
CREATE_NATIVE_ENTRY(nativeTimingCallback));
RunEngine(shell.get(), std::move(configuration));
PumpOneFrame(shell.get());
PumpOneFrame(shell.get());
reportLatch.Wait();
shell.reset();
fml::TimePoint finish = fml::TimePoint::Now();
fml::TimeDelta ellapsed = finish - start;
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE
// Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to
// make it not too flaky.
ASSERT_TRUE(ellapsed >= fml::TimeDelta::FromMilliseconds(800));
#else
// Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to
// make it not too flaky.
ASSERT_TRUE(ellapsed <= fml::TimeDelta::FromMilliseconds(500));
#endif
}
flutter/shell/common/shell.cc
// In tests using iPhone 6S with profile mode, sending a batch of 1 frame or a
// batch of 100 frames have roughly the same cost of less than 0.1ms. Sending
// a batch of 500 frames costs about 0.2ms. The 1 second threshold usually
// kicks in before we reaching the following 100 frames threshold. The 100
// threshold here is mainly for unit tests (so we don't have to write a
// 1-second unit test), and make sure that our vector won't grow too big with
// future 120fps, 240fps, or 1000fps displays.
//
// In the profile/debug mode, the timings are used by development tools which
// require a latency of no more than 100ms. Hence we lower that 1-second
// threshold to 100ms because performance overhead isn't that critical in
// those cases.
if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) {
first_frame_rasterized_ = true;
ReportTimings();
} else if (!frame_timings_report_scheduled_) {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE
constexpr int kBatchTimeInMilliseconds = 1000;
#else
constexpr int kBatchTimeInMilliseconds = 100;
#endif
flutter/shell/common/switches.cc
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
// List of common and safe VM flags to allow to be passed directly to the VM.
// clang-format off
static const std::string gDartFlagsWhitelist[] = {
"--max_profile_depth",
"--profile_period",
"--random_seed",
"--enable_mirrors",
};
// clang-format on
#endif
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
static bool IsWhitelistedDartVMFlag(const std::string& flag) {
for (uint32_t i = 0; i < fml::size(gDartFlagsWhitelist); ++i) {
const std::string& allowed = gDartFlagsWhitelist[i];
// Check that the prefix of the flag matches one of the whitelisted flags.
// We don't need to worry about cases like "--safe --sneaky_dangerous" as
// the VM will discard these as a single unrecognized flag.
if (std::equal(allowed.begin(), allowed.end(), flag.begin())) {
return true;
}
}
return false;
}
#endif
#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
std::string all_dart_flags;
if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
&all_dart_flags)) {
std::stringstream stream(all_dart_flags);
std::string flag;
// Assume that individual flags are comma separated.
while (std::getline(stream, flag, ',')) {
if (!IsWhitelistedDartVMFlag(flag)) {
FML_LOG(FATAL) << "Encountered blacklisted Dart VM flag: " << flag;
}
settings.dart_flags.push_back(flag);
}
}
settings.trace_skia =
command_line.HasOption(FlagForSwitch(Switch::TraceSkia));
settings.trace_systrace =
command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
#endif
flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
FLUTTER_EXPORT
#endif
@interface FlutterBinaryMessengerRelay : NSObject <FlutterBinaryMessenger>
@property(nonatomic, assign) NSObject<FlutterBinaryMessenger>* parent;
- (instancetype)initWithParent:(NSObject<FlutterBinaryMessenger>*)parent;
@end
flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm可能需要改动
extern "C" {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Used for debugging dart:* sources.
extern const uint8_t kPlatformStrongDill[];
extern const intptr_t kPlatformStrongDillSize;
#endif
}
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); };
};
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE
@implementation FlutterObservatoryPublisher {
}
#else
@interface FlutterObservatoryPublisher () <NSNetServiceDelegate>
@end
@implementation FlutterObservatoryPublisher {
fml::scoped_nsobject<NSURL> _url;
#if TARGET_IPHONE_SIMULATOR
DNSServiceRef _dnsServiceRef;
#else // TARGET_IPHONE_SIMULATOR
fml::scoped_nsobject<NSNetService> _netService;
#endif // TARGET_IPHONE_SIMULATOR
flutter::DartServiceIsolate::CallbackHandle _callbackHandle;
std::unique_ptr<fml::WeakPtrFactory<FlutterObservatoryPublisher>> _weakFactory;
}
- (NSURL*)url {
return _url.get();
}
- (instancetype)init {
self = [super init];
NSAssert(self, @"Super must not return null on init.");
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterObservatoryPublisher>>(self);
fml::MessageLoop::EnsureInitializedForCurrentThread();
_callbackHandle = flutter::DartServiceIsolate::AddServerStatusCallback(
[weak = _weakFactory->GetWeakPtr(),
runner = fml::MessageLoop::GetCurrent().GetTaskRunner()](const std::string& uri) {
runner->PostTask([weak, uri]() {
if (weak) {
[weak.get() publishServiceProtocolPort:std::move(uri)];
}
});
});
return self;
}
- (void)stopService {
#if TARGET_IPHONE_SIMULATOR
if (_dnsServiceRef) {
DNSServiceRefDeallocate(_dnsServiceRef);
_dnsServiceRef = NULL;
}
#else // TARGET_IPHONE_SIMULATOR
[_netService.get() stop];
#endif // TARGET_IPHONE_SIMULATOR
}
- (void)dealloc {
[self stopService];
flutter::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle));
[super dealloc];
}
- (void)publishServiceProtocolPort:(std::string)uri {
[self stopService];
if (uri.empty()) {
return;
}
// uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port
// number.
_url.reset([[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]]);
NSString* serviceName =
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
// Check to see if there's an authentication code. If there is, we'll provide
// it as a txt record so flutter tools can establish a connection.
auto path = std::string{[[_url path] UTF8String]};
if (!path.empty()) {
// Remove leading "/"
path = path.substr(1);
}
NSData* pathData = [[[NSData alloc] initWithBytes:path.c_str() length:path.length()] autorelease];
NSDictionary* txtDict = @{
@"authCode" : pathData,
};
NSData* txtData = [NSNetService dataFromTXTRecordDictionary:txtDict];
#if TARGET_IPHONE_SIMULATOR
DNSServiceFlags flags = kDNSServiceFlagsDefault;
uint32_t interfaceIndex = if_nametoindex("lo0");
const char* registrationType = "_dartobservatory._tcp";
const char* domain = "local."; // default domain
uint16_t port = [[_url port] intValue];
int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, [serviceName UTF8String],
registrationType, domain, NULL, htons(port), txtData.length,
txtData.bytes, registrationCallback, NULL);
if (err != 0) {
FML_LOG(ERROR) << "Failed to register observatory port with mDNS.";
} else {
DNSServiceSetDispatchQueue(_dnsServiceRef, dispatch_get_main_queue());
}
#else // TARGET_IPHONE_SIMULATOR
NSNetService* netServiceTmp = [[NSNetService alloc] initWithDomain:@"local."
type:@"_dartobservatory._tcp."
name:serviceName
port:[[_url port] intValue]];
[netServiceTmp setTXTRecordData:txtData];
_netService.reset(netServiceTmp);
[_netService.get() setDelegate:self];
[_netService.get() publish];
#endif // TARGET_IPHONE_SIMULATOR
}
- (void)netServiceDidPublish:(NSNetService*)sender {
FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!";
}
- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict {
FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your "
"network settings and relaunch the application.";
}
#if TARGET_IPHONE_SIMULATOR
static void DNSSD_API registrationCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char* name,
const char* regType,
const char* domain,
void* context) {
if (errorCode == kDNSServiceErr_NoError) {
FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!";
} else {
FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your "
"network settings and relaunch the application.";
}
}
#endif // TARGET_IPHONE_SIMULATOR
#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE !=
// FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE
flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
- (void)handleDidEnterBackground:(NSNotification*)notification {
UIApplication* application = [UIApplication sharedApplication];
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// The following keeps the Flutter session alive when the device screen locks
// in debug mode. It allows continued use of features like hot reload and
// taking screenshots once the device unlocks again.
//
// Note the name is not an identifier and multiple instances can exist.
_debugBackgroundTask = [application
beginBackgroundTaskWithName:@"Flutter debug task"
expirationHandler:^{
[application endBackgroundTask:_debugBackgroundTask];
FML_LOG(WARNING)
<< "\nThe OS has terminated the Flutter debug connection for being "
"inactive in the background for too long.\n\n"
"There are no errors with your Flutter application.\n\n"
"To reconnect, launch your application again via 'flutter run'";
}];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
if (!delegate) {
continue;
}
if ([delegate respondsToSelector:@selector(applicationDidEnterBackground:)]) {
[delegate applicationDidEnterBackground:application];
}
}
}
- (void)handleWillEnterForeground:(NSNotification*)notification {
UIApplication* application = [UIApplication sharedApplication];
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
[application endBackgroundTask:_debugBackgroundTask];
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
if (!delegate) {
continue;
}
if ([delegate respondsToSelector:@selector(applicationWillEnterForeground:)]) {
[delegate applicationWillEnterForeground:application];
}
}
}
flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (void)viewDidLayoutSubviews {
CGSize viewSize = self.view.bounds.size;
CGFloat scale = [UIScreen mainScreen].scale;
// First time since creation that the dimensions of its view is known.
bool firstViewBoundsUpdate = !_viewportMetrics.physical_width;
_viewportMetrics.device_pixel_ratio = scale;
_viewportMetrics.physical_width = viewSize.width * scale;
_viewportMetrics.physical_height = viewSize.height * scale;
[self updateViewportPadding];
[self updateViewportMetrics];
// This must run after updateViewportMetrics so that the surface creation tasks are queued after
// the viewport metrics update tasks.
if (firstViewBoundsUpdate) {
[self surfaceUpdated:YES];
flutter::Shell& shell = [_engine.get() shell];
fml::TimeDelta waitTime =
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
fml::TimeDelta::FromMilliseconds(200);
#else
fml::TimeDelta::FromMilliseconds(100);
#endif
if (shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded) {
FML_LOG(INFO) << "Timeout waiting for the first frame to render. This may happen in "
<< "unoptimized builds. If this is a release build, you should load a less "
<< "complex frame to avoid the timeout.";
}
}
}
flutter/shell/platform/embedder/embedder.cc可能需要改动
extern "C" {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Used for debugging dart:* sources.
extern const uint8_t kPlatformStrongDill[];
extern const intptr_t kPlatformStrongDillSize;
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
}
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
flutter/testing/testing.gni
is_aot_test =
flutter_runtime_mode == "profile" || flutter_runtime_mode == "release"
if (is_aot_test) {
deps += [ "$flutter_root/lib/snapshot:strong_platform" ]
}
if (flutter_runtime_mode == "release") {
args += [ "-Ddart.vm.product=true" ]
}
if (is_aot_test) {
args += [
"--aot",
# type flow analysis
"--tfa",
]
}
snapshot_deps = [ ":$dart_snapshot_kernel_target_name" ]
if (is_aot_test) {
dart_snapshot_aot_target_name = "dart_snapshot_aot_$target_name"
dart_snapshot_aot(dart_snapshot_aot_target_name) {
dart_kernel = dart_snapshot_kernel_path
deps = [
":$dart_snapshot_kernel_target_name",
]
}
snapshot_deps += [ ":$dart_snapshot_aot_target_name" ]
}
文件对比报告
https://gist.github.com/Kila2/5e6b59947309a44154e9c904a18e92e4