Version 2.11.0-245.0.dev

Merge commit '37abbb2e3d146f83f4baf47dc5d83f1b6179e767' into 'dev'
diff --git a/.gitignore b/.gitignore
index 0ad331e..d7864e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,3 +103,11 @@
 /generated/
 /crash_logs/
 /build/config/gclient_args.gni
+/pkg/front_end/testcases/old_dills/
+/logs.json
+/results.json
+/async_lazy_debug.so
+/dwarf.so
+/dwarf_obfuscate.so
+/il_tmp.txt
+
diff --git a/build/mac/find_sdk.py b/build/mac/find_sdk.py
index a53ba28..01d0673 100755
--- a/build/mac/find_sdk.py
+++ b/build/mac/find_sdk.py
@@ -107,7 +107,7 @@
     else:
         sdk_dir = os.path.join(out.rstrip(), 'SDKs')
     sdks = [
-        re.findall('^MacOSX(10\.\d+)\.sdk$', s) for s in os.listdir(sdk_dir)
+        re.findall('^MacOSX(1[01]\.\d+)\.sdk$', s) for s in os.listdir(sdk_dir)
     ]
     sdks = [s[0] for s in sdks if s]  # [['10.5'], ['10.6']] => ['10.5', '10.6']
     sdks = [
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
index 52e5a2f..cc183b9 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
@@ -77,15 +77,15 @@
 
   MigrationInfo(this.units, this.unitMap, this.pathContext, this.includedRoot);
 
+  /// The path of the Dart logo displayed in the toolbar.
+  String get dartLogoPath => PreviewSite.dartLogoPath;
+
   /// The path to the highlight.pack.js script, relative to [unitInfo].
   String get highlightJsPath => PreviewSite.highlightJsPath;
 
   /// The path to the highlight.pack.js stylesheet, relative to [unitInfo].
   String get highlightStylePath => PreviewSite.highlightCssPath;
 
-  /// The path of the Dart logo displayed in the toolbar.
-  String get dartLogoPath => PreviewSite.dartLogoPath;
-
   /// The path of the Material icons font.
   String get materialIconsPath => PreviewSite.materialIconsPath;
 
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index 774a398..f0c2d76 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -51,7 +51,7 @@
   ObjectStore* object_store = thread->isolate()->object_store();
   Zone* zone = thread->zone();
   Class& cls = Class::Handle(zone, object_store->closure_class());
-  ClassFinalizer::LoadClassMembers(cls);
+  cls.EnsureIsFinalized(thread);
 
   // Make sure _Closure fields are not marked as unboxing candidates
   // as they are accessed with plain loads.
@@ -88,7 +88,7 @@
 
   // Eagerly compile Bool class, bool constants are used from within compiler.
   cls = object_store->bool_class();
-  ClassFinalizer::LoadClassMembers(cls);
+  cls.EnsureIsFinalized(thread);
 }
 
 static ErrorPtr BootstrapFromKernel(Thread* thread,
diff --git a/runtime/vm/class_finalizer_test.cc b/runtime/vm/class_finalizer_test.cc
index 29a708a..7f91581 100644
--- a/runtime/vm/class_finalizer_test.cc
+++ b/runtime/vm/class_finalizer_test.cc
@@ -10,13 +10,14 @@
 namespace dart {
 
 static ClassPtr CreateTestClass(const char* name) {
-  const String& class_name =
-      String::Handle(Symbols::New(Thread::Current(), name));
+  Thread* thread = Thread::Current();
+  const String& class_name = String::Handle(Symbols::New(thread, name));
   const Script& script = Script::Handle();
   const Class& cls = Class::Handle(Class::New(
       Library::Handle(), class_name, script, TokenPosition::kNoSource));
   cls.set_interfaces(Object::empty_array());
   cls.set_is_declaration_loaded();
+  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
   cls.SetFunctions(Object::empty_array());
   cls.SetFields(Object::empty_array());
   return cls.raw();
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 62c6dd0..41933d8 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1724,6 +1724,7 @@
     }
   };
 
+  SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
   auto& dispatchers_array = Array::Handle(Z);
   auto& name = String::Handle(Z);
   auto& desc = Array::Handle(Z);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 2ddd261..ef3a137 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -2472,7 +2472,11 @@
     functions_->SetAt(function_index_++, function);
   }
 
-  cls.SetFunctions(*functions_);
+  {
+    Thread* thread = Thread::Current();
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(*functions_);
+  }
 
   if (cls.IsTopLevel()) {
     const Library& library = Library::Handle(Z, cls.library());
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index cd1a70c..8ad542d 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1919,7 +1919,8 @@
   // Iterate over all classes, deoptimize functions.
   // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
   const ClassTable& class_table = *isolate_->class_table();
-  Zone* zone = Thread::Current()->zone();
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
   CallSiteResetter resetter(zone);
   Class& cls = Class::Handle(zone);
   Array& functions = Array::Handle(zone);
@@ -1929,6 +1930,9 @@
 
   const intptr_t num_classes = class_table.NumCids();
   const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
+  // TODO(dartbug.com/36097): Need to stop other mutators running in same IG
+  // before deoptimizing the world.
+  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
   for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
     const classid_t cid =
         i < num_classes ? i : ClassTable::CidFromTopLevelIndex(i - num_classes);
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 5aa297b..3f7e002 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -374,6 +374,7 @@
           NOT_IN_PRODUCT("IsolateGroup::type_canonicalization_mutex_")),
       type_arguments_canonicalization_mutex_(NOT_IN_PRODUCT(
           "IsolateGroup::type_arguments_canonicalization_mutex_")),
+      program_lock_(new SafepointRwLock()),
       active_mutators_monitor_(new Monitor()),
       max_active_mutators_(Scavenger::MaxMutatorThreadCount()) {
   const bool is_vm_isolate = Dart::VmIsolateNameEquals(source_->name);
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 51d50d91..68c72ed 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -435,6 +435,8 @@
   Mutex* initializer_functions_mutex() { return &initializer_functions_mutex_; }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  SafepointRwLock* program_lock() { return program_lock_.get(); }
+
   static inline IsolateGroup* Current() {
     Thread* thread = Thread::Current();
     return thread == nullptr ? nullptr : thread->isolate_group();
@@ -725,6 +727,11 @@
   Mutex initializer_functions_mutex_;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  // Ensures synchronized access to classes functions, fields and other
+  // program structure elements to accommodate concurrent modification done
+  // by multiple isolates and background compiler.
+  std::unique_ptr<SafepointRwLock> program_lock_;
+
   // Allow us to ensure the number of active mutators is limited by a maximum.
   std::unique_ptr<Monitor> active_mutators_monitor_;
   intptr_t active_mutators_ = 0;
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 567f4e1..7a7f4f6 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -734,6 +734,7 @@
 }
 
 ObjectPtr KernelLoader::LoadProgram(bool process_pending_classes) {
+  SafepointWriteRwLocker ml(thread_, thread_->isolate_group()->program_lock());
   ASSERT(kernel_program_info_.constants() == Array::null());
 
   if (!program_->is_single_program()) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b45e2f1..ba6f92c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1116,7 +1116,11 @@
   cls = type_arguments_class_;
   cls.set_interfaces(Object::empty_array());
   cls.SetFields(Object::empty_array());
-  cls.SetFunctions(Object::empty_array());
+  {
+    Thread* thread = Thread::Current();
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(Object::empty_array());
+  }
 
   cls = Class::New<Bool, RTN::Bool>(isolate);
   isolate->object_store()->set_bool_class(cls);
@@ -1654,6 +1658,7 @@
     // This will initialize isolate group object_store, shared by all isolates
     // running in the isolate group.
     ObjectStore* object_store = isolate->object_store();
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
 
     Class& cls = Class::Handle(zone);
     Type& type = Type::Handle(zone);
@@ -3000,7 +3005,11 @@
 typedef UnorderedHashSet<ClassFunctionsTraits> ClassFunctionsSet;
 
 void Class::SetFunctions(const Array& value) const {
-  ASSERT(Thread::Current()->IsMutatorThread());
+#if defined(DEBUG)
+  Thread* thread = Thread::Current();
+  ASSERT(thread->IsMutatorThread());
+  ASSERT(thread->isolate_group()->program_lock()->IsCurrentThreadWriter());
+#endif
   ASSERT(!value.IsNull());
   set_functions(value);
   const intptr_t len = value.Length();
@@ -3020,7 +3029,11 @@
 }
 
 void Class::AddFunction(const Function& function) const {
-  ASSERT(Thread::Current()->IsMutatorThread());
+#if defined(DEBUG)
+  Thread* thread = Thread::Current();
+  ASSERT(thread->IsMutatorThread());
+  ASSERT(thread->isolate_group()->program_lock()->IsCurrentThreadWriter());
+#endif
   const Array& arr = Array::Handle(functions());
   const Array& new_array =
       Array::Handle(Array::Grow(arr, arr.Length() + 1, Heap::kOld));
@@ -3616,13 +3629,19 @@
   const Function& closure_function =
       Function::Handle(ImplicitClosureFunction());
   const Class& owner = Class::Handle(closure_function.Owner());
-  if (owner.EnsureIsFinalized(Thread::Current()) != Error::null()) {
+  Thread* thread = Thread::Current();
+  if (owner.EnsureIsFinalized(thread) != Error::null()) {
     return Function::null();
   }
+  IsolateGroup* group = thread->isolate_group();
   Function& result =
       Function::Handle(owner.LookupDynamicFunctionUnsafe(getter_name));
   if (result.IsNull()) {
-    result = CreateMethodExtractor(getter_name);
+    SafepointWriteRwLocker ml(thread, group->program_lock());
+    result = owner.LookupDynamicFunctionUnsafe(getter_name);
+    if (result.IsNull()) {
+      result = CreateMethodExtractor(getter_name);
+    }
   }
   ASSERT(result.kind() == FunctionLayout::kMethodExtractor);
   return result.raw();
@@ -4288,6 +4307,10 @@
     Compiler::AbortBackgroundCompilation(DeoptId::kNone,
                                          "Class finalization while compiling");
   }
+  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+  if (is_finalized()) {
+    return Error::null();
+  }
   ASSERT(thread->IsMutatorThread());
   ASSERT(thread != NULL);
   const Error& error =
@@ -13126,7 +13149,8 @@
     const String& klass,
     const Array& arguments,
     const TypeArguments& type_arguments) {
-  Zone* zone = Thread::Current()->zone();
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
 #if defined(DART_PRECOMPILED_RUNTIME)
   const String& error_str = String::Handle(
       zone,
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index d46aae1..a9c2fcb 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -121,7 +121,10 @@
   functions.SetAt(5, function);
 
   // Setup the functions in the class.
-  cls.SetFunctions(functions);
+  {
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(functions);
+  }
 
   // The class can now be finalized.
   cls.Finalize();
@@ -200,7 +203,11 @@
     }
 
     cls.set_interfaces(Array::empty_array());
-    cls.SetFunctions(Array::empty_array());
+    {
+      SafepointWriteRwLocker ml(thread,
+                                thread->isolate_group()->program_lock());
+      cls.SetFunctions(Array::empty_array());
+    }
     cls.SetFields(fields);
     cls.Finalize();
 
@@ -2516,7 +2523,10 @@
       Function::New(parent_name, FunctionLayout::kRegularFunction, false, false,
                     false, false, false, cls, TokenPosition::kMinSource);
   functions.SetAt(0, parent);
-  cls.SetFunctions(functions);
+  {
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(functions);
+  }
 
   Function& function = Function::Handle();
   const String& function_name = String::Handle(Symbols::New(thread, "foo"));
@@ -3766,7 +3776,10 @@
       Function::New(parent_name, FunctionLayout::kRegularFunction, false, false,
                     false, false, false, cls, TokenPosition::kMinSource);
   functions.SetAt(0, parent);
-  cls.SetFunctions(functions);
+  {
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(functions);
+  }
 
   Function& function = Function::Handle();
   const String& function_name = String::Handle(Symbols::New(thread, "foo"));
@@ -3802,7 +3815,10 @@
       Function::New(parent_name, FunctionLayout::kRegularFunction, false, false,
                     false, false, false, cls, TokenPosition::kMinSource);
   functions.SetAt(0, parent);
-  cls.SetFunctions(functions);
+  {
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    cls.SetFunctions(functions);
+  }
   cls.Finalize();
 
   // Add invocation dispatcher.
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index 57b2051..cdf4fac 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -57,6 +57,7 @@
 
   const bool is_dyn_call = demangled.raw() != function_name.raw();
 
+  bool need_to_create_method_extractor = false;
   while (!cls.IsNull()) {
     if (is_dyn_call) {
       // Try to find a dyn:* forwarder & return it.
@@ -82,12 +83,8 @@
       function = lookup(cls, demangled_getter_name);
       if (!function.IsNull()) {
         if (allow_add && FLAG_lazy_dispatchers) {
-          // We were looking for the getter but found a method with the same
-          // name. Create a method extractor and return it.
-          // The extractor does not exist yet, so using GetMethodExtractor is
-          // not necessary here.
-          function = function.CreateMethodExtractor(demangled);
-          return function.raw();
+          need_to_create_method_extractor = true;
+          break;
         } else {
           return Function::null();
         }
@@ -95,6 +92,13 @@
     }
     cls = cls.SuperClass();
   }
+  if (need_to_create_method_extractor) {
+    // We were looking for the getter but found a method with the same
+    // name. Create a method extractor and return it.
+    // Use GetMethodExtractor instead of CreateMethodExtractor to ensure
+    // nobody created method extractor since we last checked under ReadRwLocker.
+    function = function.GetMethodExtractor(demangled);
+  }
   return function.raw();
 }
 
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 3435afe..601befe 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -109,27 +109,6 @@
 }
 #endif
 
-// Add function to a class and that class to the class dictionary so that
-// frame walking can be used.
-const Function& RegisterFakeFunction(const char* name, const Code& code) {
-  Thread* thread = Thread::Current();
-  const String& class_name = String::Handle(Symbols::New(thread, "ownerClass"));
-  const Script& script = Script::Handle();
-  const Library& lib = Library::Handle(Library::CoreLibrary());
-  const Class& owner_class = Class::Handle(
-      Class::New(lib, class_name, script, TokenPosition::kNoSource));
-  const String& function_name = String::ZoneHandle(Symbols::New(thread, name));
-  const Function& function = Function::ZoneHandle(Function::New(
-      function_name, FunctionLayout::kRegularFunction, true, false, false,
-      false, false, owner_class, TokenPosition::kMinSource));
-  const Array& functions = Array::Handle(Array::New(1));
-  functions.SetAt(0, function);
-  owner_class.SetFunctions(functions);
-  lib.AddClass(owner_class);
-  function.AttachCode(code);
-  return function;
-}
-
 DEFINE_RUNTIME_ENTRY(RangeError, 2) {
   const Instance& length = Instance::CheckedHandle(zone, arguments.ArgAt(0));
   const Instance& index = Instance::CheckedHandle(zone, arguments.ArgAt(1));
@@ -1776,10 +1755,10 @@
                            const String& name,
                            const Array& descriptor) {
   ASSERT(name.IsSymbol());
-
-  ArgumentsDescriptor args_desc(descriptor);
   Function& target_function = Function::Handle(zone);
+
   if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
+    ArgumentsDescriptor args_desc(descriptor);
     target_function = Resolver::ResolveDynamicForReceiverClass(receiver_class,
                                                                name, args_desc);
   }
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index a09361b..9427a30 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -16,6 +16,7 @@
 #include "vm/compiler/assembler/disassembler.h"
 #include "vm/constants.h"
 #include "vm/cpu.h"
+#include "vm/image_snapshot.h"
 #include "vm/native_arguments.h"
 #include "vm/os_thread.h"
 #include "vm/stack_frame.h"
@@ -95,7 +96,9 @@
 
   static TokenPosition GetApproximateTokenIndex(const Code& code, uword pc);
 
-  static void PrintDartFrame(uword pc,
+  static void PrintDartFrame(uword vm_instructions,
+                             uword isolate_instructions,
+                             uword pc,
                              uword fp,
                              uword sp,
                              const Function& function,
@@ -241,7 +244,29 @@
   return token_pos;
 }
 
-void SimulatorDebugger::PrintDartFrame(uword pc,
+#if defined(DART_PRECOMPILED_RUNTIME)
+static const char* ImageName(uword vm_instructions,
+                             uword isolate_instructions,
+                             uword pc,
+                             intptr_t* offset) {
+  const Image vm_image(vm_instructions);
+  const Image isolate_image(isolate_instructions);
+  if (vm_image.contains(pc)) {
+    *offset = pc - vm_instructions;
+    return kVmSnapshotInstructionsAsmSymbol;
+  } else if (isolate_image.contains(pc)) {
+    *offset = pc - isolate_instructions;
+    return kIsolateSnapshotInstructionsAsmSymbol;
+  } else {
+    *offset = 0;
+    return "<unknown>";
+  }
+}
+#endif
+
+void SimulatorDebugger::PrintDartFrame(uword vm_instructions,
+                                       uword isolate_instructions,
+                                       uword pc,
                                        uword fp,
                                        uword sp,
                                        const Function& function,
@@ -257,22 +282,42 @@
     script.GetTokenLocation(token_pos, &line, &column);
   }
   OS::PrintErr(
-      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")\n", pc,
+      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
       fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
       func_name.ToCString(), url.ToCString(), line, column);
+#if defined(DART_PRECOMPILED_RUNTIME)
+  intptr_t offset;
+  auto const symbol_name =
+      ImageName(vm_instructions, isolate_instructions, pc, &offset);
+  OS::PrintErr(" %s+0x%" Px "", symbol_name, offset);
+#endif
+  OS::PrintErr("\n");
 }
 
 void SimulatorDebugger::PrintBacktrace() {
-  StackFrameIterator frames(
-      sim_->get_register(FP), sim_->get_register(SP), sim_->get_pc(),
-      ValidationPolicy::kDontValidateFrames, Thread::Current(),
-      StackFrameIterator::kNoCrossThreadIteration);
+  auto const T = Thread::Current();
+  auto const Z = T->zone();
+#if defined(DART_PRECOMPILED_RUNTIME)
+  auto const vm_instructions = reinterpret_cast<uword>(
+      Dart::vm_isolate()->group()->source()->snapshot_instructions);
+  auto const isolate_instructions = reinterpret_cast<uword>(
+      T->isolate_group()->source()->snapshot_instructions);
+  OS::PrintErr("vm_instructions=0x%" Px ", isolate_instructions=0x%" Px "\n",
+               vm_instructions, isolate_instructions);
+#else
+  const uword vm_instructions = 0;
+  const uword isolate_instructions = 0;
+#endif
+  StackFrameIterator frames(sim_->get_register(FP), sim_->get_register(SP),
+                            sim_->get_pc(),
+                            ValidationPolicy::kDontValidateFrames, T,
+                            StackFrameIterator::kNoCrossThreadIteration);
   StackFrame* frame = frames.NextFrame();
   ASSERT(frame != NULL);
-  Function& function = Function::Handle();
-  Function& inlined_function = Function::Handle();
-  Code& code = Code::Handle();
-  Code& unoptimized_code = Code::Handle();
+  Function& function = Function::Handle(Z);
+  Function& inlined_function = Function::Handle(Z);
+  Code& code = Code::Handle(Z);
+  Code& unoptimized_code = Code::Handle(Z);
   while (frame != NULL) {
     if (frame->IsDartFrame()) {
       ASSERT(!frame->is_interpreted());  // Not yet supported.
@@ -291,24 +336,33 @@
           it.Advance();
           if (!it.Done()) {
             PrintDartFrame(
-                unoptimized_pc, frame->fp(), frame->sp(), inlined_function,
+                vm_instructions, isolate_instructions, unoptimized_pc,
+                frame->fp(), frame->sp(), inlined_function,
                 GetApproximateTokenIndex(unoptimized_code, unoptimized_pc),
                 true, true);
           }
         }
         // Print the optimized inlining frame below.
       }
-      PrintDartFrame(frame->pc(), frame->fp(), frame->sp(), function,
+      PrintDartFrame(vm_instructions, isolate_instructions, frame->pc(),
+                     frame->fp(), frame->sp(), function,
                      GetApproximateTokenIndex(code, frame->pc()),
                      code.is_optimized(), false);
     } else {
-      OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame\n",
+      OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame",
                    frame->pc(), frame->fp(), frame->sp(),
                    frame->IsEntryFrame()
                        ? "entry"
                        : frame->IsExitFrame()
                              ? "exit"
                              : frame->IsStubFrame() ? "stub" : "invalid");
+#if defined(DART_PRECOMPILED_RUNTIME)
+      intptr_t offset;
+      auto const symbol_name = ImageName(vm_instructions, isolate_instructions,
+                                         frame->pc(), &offset);
+      OS::PrintErr(" %s+0x%" Px "", symbol_name, offset);
+#endif
+      OS::PrintErr("\n");
     }
     frame = frames.NextFrame();
   }
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index 456ab1c..d7af78b 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -15,6 +15,7 @@
 
 #include "vm/compiler/assembler/disassembler.h"
 #include "vm/constants.h"
+#include "vm/image_snapshot.h"
 #include "vm/native_arguments.h"
 #include "vm/os_thread.h"
 #include "vm/stack_frame.h"
@@ -100,7 +101,9 @@
 
   static TokenPosition GetApproximateTokenIndex(const Code& code, uword pc);
 
-  static void PrintDartFrame(uword pc,
+  static void PrintDartFrame(uword vm_instructions,
+                             uword isolate_instructions,
+                             uword pc,
                              uword fp,
                              uword sp,
                              const Function& function,
@@ -269,7 +272,29 @@
   return token_pos;
 }
 
-void SimulatorDebugger::PrintDartFrame(uword pc,
+#if defined(DART_PRECOMPILED_RUNTIME)
+static const char* ImageName(uword vm_instructions,
+                             uword isolate_instructions,
+                             uword pc,
+                             intptr_t* offset) {
+  const Image vm_image(vm_instructions);
+  const Image isolate_image(isolate_instructions);
+  if (vm_image.contains(pc)) {
+    *offset = pc - vm_instructions;
+    return kVmSnapshotInstructionsAsmSymbol;
+  } else if (isolate_image.contains(pc)) {
+    *offset = pc - isolate_instructions;
+    return kIsolateSnapshotInstructionsAsmSymbol;
+  } else {
+    *offset = 0;
+    return "<unknown>";
+  }
+}
+#endif
+
+void SimulatorDebugger::PrintDartFrame(uword vm_instructions,
+                                       uword isolate_instructions,
+                                       uword pc,
                                        uword fp,
                                        uword sp,
                                        const Function& function,
@@ -285,22 +310,42 @@
     script.GetTokenLocation(token_pos, &line, &column);
   }
   OS::PrintErr(
-      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")\n", pc,
+      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
       fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
       func_name.ToCString(), url.ToCString(), line, column);
+#if defined(DART_PRECOMPILED_RUNTIME)
+  intptr_t offset;
+  auto const symbol_name =
+      ImageName(vm_instructions, isolate_instructions, pc, &offset);
+  OS::PrintErr(" %s+0x%" Px "", symbol_name, offset);
+#endif
+  OS::PrintErr("\n");
 }
 
 void SimulatorDebugger::PrintBacktrace() {
-  StackFrameIterator frames(
-      sim_->get_register(FP), sim_->get_register(SP), sim_->get_pc(),
-      ValidationPolicy::kDontValidateFrames, Thread::Current(),
-      StackFrameIterator::kNoCrossThreadIteration);
+  auto const T = Thread::Current();
+  auto const Z = T->zone();
+#if defined(DART_PRECOMPILED_RUNTIME)
+  auto const vm_instructions = reinterpret_cast<uword>(
+      Dart::vm_isolate()->group()->source()->snapshot_instructions);
+  auto const isolate_instructions = reinterpret_cast<uword>(
+      T->isolate_group()->source()->snapshot_instructions);
+  OS::PrintErr("vm_instructions=0x%" Px ", isolate_instructions=0x%" Px "\n",
+               vm_instructions, isolate_instructions);
+#else
+  const uword vm_instructions = 0;
+  const uword isolate_instructions = 0;
+#endif
+  StackFrameIterator frames(sim_->get_register(FP), sim_->get_register(SP),
+                            sim_->get_pc(),
+                            ValidationPolicy::kDontValidateFrames, T,
+                            StackFrameIterator::kNoCrossThreadIteration);
   StackFrame* frame = frames.NextFrame();
   ASSERT(frame != NULL);
-  Function& function = Function::Handle();
-  Function& inlined_function = Function::Handle();
-  Code& code = Code::Handle();
-  Code& unoptimized_code = Code::Handle();
+  Function& function = Function::Handle(Z);
+  Function& inlined_function = Function::Handle(Z);
+  Code& code = Code::Handle(Z);
+  Code& unoptimized_code = Code::Handle(Z);
   while (frame != NULL) {
     if (frame->IsDartFrame()) {
       ASSERT(!frame->is_interpreted());  // Not yet supported.
@@ -319,24 +364,33 @@
           it.Advance();
           if (!it.Done()) {
             PrintDartFrame(
-                unoptimized_pc, frame->fp(), frame->sp(), inlined_function,
+                vm_instructions, isolate_instructions, unoptimized_pc,
+                frame->fp(), frame->sp(), inlined_function,
                 GetApproximateTokenIndex(unoptimized_code, unoptimized_pc),
                 true, true);
           }
         }
         // Print the optimized inlining frame below.
       }
-      PrintDartFrame(frame->pc(), frame->fp(), frame->sp(), function,
+      PrintDartFrame(vm_instructions, isolate_instructions, frame->pc(),
+                     frame->fp(), frame->sp(), function,
                      GetApproximateTokenIndex(code, frame->pc()),
                      code.is_optimized(), false);
     } else {
-      OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame\n",
+      OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame",
                    frame->pc(), frame->fp(), frame->sp(),
                    frame->IsEntryFrame()
                        ? "entry"
                        : frame->IsExitFrame()
                              ? "exit"
                              : frame->IsStubFrame() ? "stub" : "invalid");
+#if defined(DART_PRECOMPILED_RUNTIME)
+      intptr_t offset;
+      auto const symbol_name = ImageName(vm_instructions, isolate_instructions,
+                                         frame->pc(), &offset);
+      OS::PrintErr(" %s+0x%" Px "", symbol_name, offset);
+#endif
+      OS::PrintErr("\n");
     }
     frame = frames.NextFrame();
   }
diff --git a/runtime/vm/stub_code_test.cc b/runtime/vm/stub_code_test.cc
new file mode 100644
index 0000000..4b8649b
--- /dev/null
+++ b/runtime/vm/stub_code_test.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/globals.h"
+#include "vm/object.h"
+#include "vm/symbols.h"
+
+namespace dart {
+
+// Add function to a class and that class to the class dictionary so that
+// frame walking can be used.
+const Function& RegisterFakeFunction(const char* name, const Code& code) {
+  Thread* thread = Thread::Current();
+  const String& class_name = String::Handle(Symbols::New(thread, "ownerClass"));
+  const Script& script = Script::Handle();
+  const Library& lib = Library::Handle(Library::CoreLibrary());
+  const Class& owner_class = Class::Handle(
+      Class::New(lib, class_name, script, TokenPosition::kNoSource));
+  const String& function_name = String::ZoneHandle(Symbols::New(thread, name));
+  const Function& function = Function::ZoneHandle(Function::New(
+      function_name, FunctionLayout::kRegularFunction, true, false, false,
+      false, false, owner_class, TokenPosition::kMinSource));
+  const Array& functions = Array::Handle(Array::New(1));
+  functions.SetAt(0, function);
+  {
+    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    owner_class.SetFunctions(functions);
+  }
+  lib.AddClass(owner_class);
+  function.AttachCode(code);
+  return function;
+}
+
+}  // namespace dart
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index f70a389..83569d7 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -436,6 +436,7 @@
   "snapshot_test.cc",
   "source_report_test.cc",
   "stack_frame_test.cc",
+  "stub_code_test.cc",
   "stub_code_arm64_test.cc",
   "stub_code_arm_test.cc",
   "stub_code_ia32_test.cc",
diff --git a/tools/VERSION b/tools/VERSION
index e5b1b55..6dfe295 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 11
 PATCH 0
-PRERELEASE 244
+PRERELEASE 245
 PRERELEASE_PATCH 0
\ No newline at end of file