Flutter engine runtime mode 研究

本文主要对比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,
                               &timestamps](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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容