[vm] Account for bytecode in profile processing and Observatory.

Change-Id: I220eca1f4a3aadaa271ead4de698c28e4a7b653a
Reviewed-on: https://dart-review.googlesource.com/c/84447
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: RĂ©gis Crelier <regis@google.com>
diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc
index 1ed1327..04a3e89 100644
--- a/runtime/bin/vmservice_impl.cc
+++ b/runtime/bin/vmservice_impl.cc
@@ -86,6 +86,18 @@
   return NULL;
 }
 
+const uint8_t* VmServiceIONativeSymbol(Dart_NativeFunction nf) {
+  intptr_t n =
+      sizeof(_VmServiceIONativeEntries) / sizeof(_VmServiceIONativeEntries[0]);
+  for (intptr_t i = 0; i < n; i++) {
+    VmServiceIONativeEntry entry = _VmServiceIONativeEntries[i];
+    if (reinterpret_cast<Dart_NativeFunction>(entry.function) == nf) {
+      return reinterpret_cast<const uint8_t*>(entry.name);
+    }
+  }
+  return NULL;
+}
+
 const char* VmService::error_msg_ = NULL;
 char VmService::server_uri_[kServerUriStringBufferSize];
 
@@ -93,7 +105,8 @@
   Dart_Handle url = DartUtils::NewString(kVMServiceIOLibraryUri);
   Dart_Handle library = Dart_LookupLibrary(url);
   if (!Dart_IsError(library)) {
-    Dart_SetNativeResolver(library, VmServiceIONativeResolver, NULL);
+    Dart_SetNativeResolver(library, VmServiceIONativeResolver,
+                           VmServiceIONativeSymbol);
   }
 }
 
@@ -120,7 +133,8 @@
   SHUTDOWN_ON_ERROR(library);
   result = Dart_SetRootLibrary(library);
   SHUTDOWN_ON_ERROR(library);
-  result = Dart_SetNativeResolver(library, VmServiceIONativeResolver, NULL);
+  result = Dart_SetNativeResolver(library, VmServiceIONativeResolver,
+                                  VmServiceIONativeSymbol);
   SHUTDOWN_ON_ERROR(result);
 
   // Make runnable.
diff --git a/runtime/observatory/lib/src/elements/function_view.dart b/runtime/observatory/lib/src/elements/function_view.dart
index 57d775a..3c32189 100644
--- a/runtime/observatory/lib/src/elements/function_view.dart
+++ b/runtime/observatory/lib/src/elements/function_view.dart
@@ -288,6 +288,21 @@
             ]
         ]);
     }
+    if (_function.bytecode != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'bytecode',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new CodeRefElement(_isolate, _function.bytecode,
+                  queue: _r.queue),
+            ]
+        ]);
+    }
     members.add(new DivElement()
       ..classes = ['memberItem']
       ..text = ' ');
diff --git a/runtime/observatory/lib/src/models/objects/function.dart b/runtime/observatory/lib/src/models/objects/function.dart
index e619f5e..541dcad 100644
--- a/runtime/observatory/lib/src/models/objects/function.dart
+++ b/runtime/observatory/lib/src/models/objects/function.dart
@@ -87,6 +87,9 @@
   CodeRef get unoptimizedCode;
 
   /// [optional]
+  CodeRef get bytecode;
+
+  /// [optional]
   FieldRef get field;
   int get usageCounter;
   InstanceRef get icDataArray;
diff --git a/runtime/observatory/lib/src/repositories/heap_snapshot.dart b/runtime/observatory/lib/src/repositories/heap_snapshot.dart
index cea60e2..198af19 100644
--- a/runtime/observatory/lib/src/repositories/heap_snapshot.dart
+++ b/runtime/observatory/lib/src/repositories/heap_snapshot.dart
@@ -90,12 +90,13 @@
   }
 
   void reuse() {
-    _onProgress =
+    final onProgress =
         new StreamController<HeapSnapshotLoadingProgressEvent>.broadcast();
-    (() async {
-      _triggerOnProgress();
-      _onProgress.close();
-    }());
+    Timer.run(() {
+      onProgress.add(new HeapSnapshotLoadingProgressEvent(this));
+      onProgress.close();
+    });
+    _onProgress = onProgress;
   }
 }
 
diff --git a/runtime/observatory/lib/src/repositories/sample_profile.dart b/runtime/observatory/lib/src/repositories/sample_profile.dart
index 3dace5a..27d03c5 100644
--- a/runtime/observatory/lib/src/repositories/sample_profile.dart
+++ b/runtime/observatory/lib/src/repositories/sample_profile.dart
@@ -115,12 +115,13 @@
   }
 
   void reuse() {
-    _onProgress =
+    final onProgress =
         new StreamController<SampleProfileLoadingProgressEvent>.broadcast();
-    (() async {
-      _triggerOnProgress();
-      _onProgress.close();
-    }());
+    Timer.run(() {
+      onProgress.add(new SampleProfileLoadingProgressEvent(this));
+      onProgress.close();
+    });
+    _onProgress = onProgress;
   }
 }
 
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index f296309..7ebc69c 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -3135,6 +3135,7 @@
   SourceLocation location;
   Code code;
   Code unoptimizedCode;
+  Code bytecode;
   bool isOptimizable;
   bool isInlinable;
   bool hasIntrinsic;
@@ -3193,6 +3194,7 @@
     isInlinable = map['_inlinable'];
     isRecognized = map['_recognized'];
     unoptimizedCode = map['_unoptimizedCode'];
+    bytecode = map['_bytecode'];
     deoptimizations = map['_deoptimizations'];
     usageCounter = map['_usageCounter'];
     icDataArray = map['_icDataArray'];
diff --git a/runtime/observatory/tests/observatory_ui/mocks/objects/function.dart b/runtime/observatory/tests/observatory_ui/mocks/objects/function.dart
index bbe2c28..00f3ca5 100644
--- a/runtime/observatory/tests/observatory_ui/mocks/objects/function.dart
+++ b/runtime/observatory/tests/observatory_ui/mocks/objects/function.dart
@@ -33,6 +33,7 @@
   final M.SourceLocation location;
   final M.CodeRef code;
   final M.CodeRef unoptimizedCode;
+  final M.CodeRef bytecode;
   final M.FieldRef field;
   final int usageCounter;
   final M.InstanceRef icDataArray;
@@ -55,6 +56,7 @@
     this.location,
     this.code,
     this.unoptimizedCode,
+    this.bytecode,
     this.field,
     this.usageCounter: 0,
     this.icDataArray: const InstanceRefMock(),
diff --git a/runtime/observatory/tests/service/get_object_rpc_test.dart b/runtime/observatory/tests/service/get_object_rpc_test.dart
index 15ed8df..3f0ff6c 100644
--- a/runtime/observatory/tests/service/get_object_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_object_rpc_test.dart
@@ -952,8 +952,8 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getObject', params);
     expect(result['type'], equals('Code'));
-    expect(result['name'], equals('_DummyClass.dummyFunction'));
-    expect(result['_vmName'], equals('dummyFunction'));
+    expect(result['name'], endsWith('_DummyClass.dummyFunction'));
+    expect(result['_vmName'], endsWith('dummyFunction'));
     expect(result['kind'], equals('Dart'));
     expect(result['_optimized'], new isInstanceOf<bool>());
     expect(result['function']['type'], equals('@Function'));
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 50b3846..f4edb7f 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -708,10 +708,9 @@
   {
     InterpreterSetjmpBuffer buffer(this);
     if (!setjmp(buffer.buffer_)) {
-      thread->set_vm_tag(reinterpret_cast<uword>(entrypoint));
       result = entrypoint(code, argdesc_, call_base, thread);
-      thread->set_vm_tag(VMTag::kDartInterpretedTagId);
       thread->set_top_exit_frame_info(0);
+      ASSERT(thread->vm_tag() == VMTag::kDartInterpretedTagId);
       ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
     } else {
       return false;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 4d20ed2..cd878c1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15103,7 +15103,7 @@
     // Regular stub.
     const char* name = StubCode::NameOfStub(EntryPoint());
     if (name == NULL) {
-      return zone->PrintToString("[this stub]");  // Not yet recorded.
+      return zone->PrintToString("[unknown stub]");  // Not yet recorded.
     }
     return zone->PrintToString("[Stub] %s", name);
   } else if (obj.IsClass()) {
@@ -15114,10 +15114,10 @@
   } else {
     ASSERT(obj.IsFunction());
     // Dart function.
-    const char* opt = is_optimized() ? "*" : "";
+    const char* opt = is_optimized() ? "[Optimized]" : "[Unoptimized]";
     const char* function_name =
         String::Handle(zone, Function::Cast(obj).UserVisibleName()).ToCString();
-    return zone->PrintToString("%s%s", opt, function_name);
+    return zone->PrintToString("%s %s", opt, function_name);
   }
 }
 
@@ -15125,11 +15125,11 @@
   Zone* zone = Thread::Current()->zone();
   const Object& obj = Object::Handle(zone, owner());
   if (obj.IsFunction()) {
-    const char* opt = is_optimized() ? "*" : "";
+    const char* opt = is_optimized() ? "[Optimized]" : "[Unoptimized]";
     const char* function_name =
         String::Handle(zone, Function::Cast(obj).QualifiedScrubbedName())
             .ToCString();
-    return zone->PrintToString("%s%s", opt, function_name);
+    return zone->PrintToString("%s %s", opt, function_name);
   }
   return Name();
 }
@@ -15304,12 +15304,6 @@
   return instr.LengthInBytes();
 }
 
-bool Bytecode::ContainsInstructionAt(uword addr) const {
-  const ExternalTypedData& instr = ExternalTypedData::Handle(instructions());
-  const uword offset = addr - reinterpret_cast<uword>(instr.DataAddr(0));
-  return offset < static_cast<uword>(instr.LengthInBytes());
-}
-
 void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
 #if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -15389,7 +15383,7 @@
   ASSERT(!fun.IsNull());
   const char* function_name =
       String::Handle(zone, fun.UserVisibleName()).ToCString();
-  return zone->PrintToString("%s", function_name);
+  return zone->PrintToString("[Bytecode] %s", function_name);
 }
 
 const char* Bytecode::QualifiedName() const {
@@ -15398,7 +15392,23 @@
   ASSERT(!fun.IsNull());
   const char* function_name =
       String::Handle(zone, fun.QualifiedScrubbedName()).ToCString();
-  return zone->PrintToString("%s", function_name);
+  return zone->PrintToString("[Bytecode] %s", function_name);
+}
+
+bool Bytecode::SlowFindRawBytecodeVisitor::FindObject(
+    RawObject* raw_obj) const {
+  return RawBytecode::ContainsPC(raw_obj, pc_);
+}
+
+RawBytecode* Bytecode::FindCode(uword pc) {
+  Thread* thread = Thread::Current();
+  HeapIterationScope heap_iteration_scope(thread);
+  SlowFindRawBytecodeVisitor visitor(pc);
+  RawObject* needle = thread->heap()->FindOldObject(&visitor);
+  if (needle != Bytecode::null()) {
+    return static_cast<RawBytecode*>(needle);
+  }
+  return Bytecode::null();
 }
 
 RawContext* Context::New(intptr_t num_variables, Heap::Space space) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5ac93eb..cfa12c8 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5302,7 +5302,10 @@
   intptr_t Size() const;
 
   RawObjectPool* object_pool() const { return raw_ptr()->object_pool_; }
-  bool ContainsInstructionAt(uword addr) const;
+
+  bool ContainsInstructionAt(uword addr) const {
+    return RawBytecode::ContainsPC(raw(), addr);
+  }
 
   RawPcDescriptors* pc_descriptors() const {
     return raw_ptr()->pc_descriptors_;
@@ -5358,6 +5361,22 @@
   const char* Name() const;
   const char* QualifiedName() const;
 
+  class SlowFindRawBytecodeVisitor : public FindObjectVisitor {
+   public:
+    explicit SlowFindRawBytecodeVisitor(uword pc) : pc_(pc) {}
+    virtual ~SlowFindRawBytecodeVisitor() {}
+
+    // Check if object matches find condition.
+    virtual bool FindObject(RawObject* obj) const;
+
+   private:
+    const uword pc_;
+
+    DISALLOW_COPY_AND_ASSIGN(SlowFindRawBytecodeVisitor);
+  };
+
+  static RawBytecode* FindCode(uword pc);
+
  private:
   void set_object_pool(const ObjectPool& object_pool) const {
     StorePointer(&raw_ptr()->object_pool_, object_pool.raw());
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 964dc6d..3080fe7 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -315,7 +315,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
   Bytecode& bytecode = Bytecode::Handle(this->bytecode());
   if (!bytecode.IsNull()) {
-    jsobj.AddProperty("bytecode", bytecode);
+    jsobj.AddProperty("_bytecode", bytecode);
   }
 #endif  // !DART_PRECOMPILED_RUNTIME
   Array& ics = Array::Handle(ic_data_array());
@@ -794,6 +794,8 @@
 }
 
 void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
+  // N.B. This is polymorphic with Bytecode.
+
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Code", ref);
   jsobj.AddFixedServiceId("code/%" Px64 "-%" Px "", compile_timestamp(),
@@ -860,11 +862,21 @@
 }
 
 void Bytecode::PrintJSONImpl(JSONStream* stream, bool ref) const {
+  // N.B. This is polymorphic with Code.
+
   JSONObject jsobj(stream);
-  AddCommonObjectProperties(&jsobj, "Bytecode", ref);
+  AddCommonObjectProperties(&jsobj, "Code", ref);
+  int64_t compile_timestamp = 0;
+  jsobj.AddFixedServiceId("code/%" Px64 "-%" Px "", compile_timestamp,
+                          PayloadStart());
   const char* qualified_name = QualifiedName();
   const char* vm_name = Name();
   AddNameProperties(&jsobj, qualified_name, vm_name);
+
+  jsobj.AddProperty("kind", "Dart");
+  jsobj.AddProperty("_optimized", false);
+  jsobj.AddProperty("_intrinsic", false);
+  jsobj.AddProperty("_native", false);
   if (ref) {
     return;
   }
@@ -872,6 +884,7 @@
   jsobj.AddProperty("function", fun);
   jsobj.AddPropertyF("_startAddress", "%" Px "", PayloadStart());
   jsobj.AddPropertyF("_endAddress", "%" Px "", PayloadStart() + Size());
+  jsobj.AddProperty("_alive", true);
   const ObjectPool& obj_pool = ObjectPool::Handle(object_pool());
   jsobj.AddProperty("_objectPool", obj_pool);
   {
@@ -884,6 +897,9 @@
     JSONObject desc(&jsobj, "_descriptors");
     descriptors.PrintToJSONObject(&desc, false);
   }
+
+  { JSONArray inlined_functions(&jsobj, "_inlinedFunctions"); }
+  { JSONArray inline_intervals(&jsobj, "_inlinedIntervals"); }
 }
 
 void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index f5b9a86..fbb891a 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -1409,9 +1409,7 @@
                 &counters_);
 }
 
-CodeDescriptor::CodeDescriptor(const Code& code) : code_(code) {
-  ASSERT(!code_.IsNull());
-}
+CodeDescriptor::CodeDescriptor(const AbstractCode code) : code_(code) {}
 
 uword CodeDescriptor::Start() const {
   return code_.PayloadStart();
@@ -1438,15 +1436,10 @@
   ~CodeLookupTableBuilder() {}
 
   void VisitObject(RawObject* raw_obj) {
-    uint32_t tags = raw_obj->ptr()->tags_;
-    if (RawObject::ClassIdTag::decode(tags) == kCodeCid) {
-      RawCode* raw_code = reinterpret_cast<RawCode*>(raw_obj);
-      const Code& code = Code::Handle(raw_code);
-      ASSERT(!code.IsNull());
-      const Instructions& instructions =
-          Instructions::Handle(code.instructions());
-      ASSERT(!instructions.IsNull());
-      table_->Add(code);
+    if (raw_obj->IsCode()) {
+      table_->Add(Code::Handle(Code::RawCast(raw_obj)));
+    } else if (raw_obj->IsBytecode()) {
+      table_->Add(Bytecode::Handle(Bytecode::RawCast(raw_obj)));
     }
   }
 
@@ -1495,9 +1488,10 @@
 #endif
 }
 
-void CodeLookupTable::Add(const Code& code) {
+void CodeLookupTable::Add(const Object& code) {
   ASSERT(!code.IsNull());
-  CodeDescriptor* cd = new CodeDescriptor(code);
+  ASSERT(code.IsCode() || code.IsBytecode());
+  CodeDescriptor* cd = new CodeDescriptor(AbstractCode(code.raw()));
   code_objects_.Add(cd);
 }
 
@@ -1672,7 +1666,12 @@
                                                uword pc_marker,
                                                uword* stack_buffer) {
   ASSERT(cd != NULL);
-  const Code& code = Code::Handle(cd->code());
+  if (cd->code().IsBytecode()) {
+    // Bytecode frame build is atomic from the profiler's perspective: no
+    // missing frame.
+    return;
+  }
+  const Code& code = Code::Handle(Code::RawCast(cd->code().raw()));
   ASSERT(!code.IsNull());
   // Some stubs (and intrinsics) do not push a frame onto the stack leaving
   // the frame pointer in the caller.
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index 951abed..c6f868d 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -434,10 +434,83 @@
   }
 };
 
+class AbstractCode {
+ public:
+  explicit AbstractCode(RawObject* code) : code_(Object::Handle(code)) {
+    ASSERT(code_.IsNull() || code_.IsCode() || code_.IsBytecode());
+  }
+
+  RawObject* raw() const { return code_.raw(); }
+  const Object* handle() const { return &code_; }
+
+  uword PayloadStart() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).PayloadStart();
+    } else {
+      return Bytecode::Cast(code_).PayloadStart();
+    }
+  }
+
+  uword Size() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).Size();
+    } else {
+      return Bytecode::Cast(code_).Size();
+    }
+  }
+
+  int64_t compile_timestamp() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).compile_timestamp();
+    } else {
+      return 0;
+    }
+  }
+
+  const char* Name() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).Name();
+    } else {
+      return Bytecode::Cast(code_).Name();
+    }
+  }
+
+  const char* QualifiedName() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).QualifiedName();
+    } else {
+      return Bytecode::Cast(code_).QualifiedName();
+    }
+  }
+
+  RawObject* owner() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).owner();
+    } else {
+      return Bytecode::Cast(code_).function();
+    }
+  }
+
+  bool IsNull() const { return code_.IsNull(); }
+  bool IsCode() const { return code_.IsCode(); }
+  bool IsBytecode() const { return code_.IsBytecode(); }
+
+  bool is_optimized() const {
+    if (code_.IsCode()) {
+      return Code::Cast(code_).is_optimized();
+    } else {
+      return false;
+    }
+  }
+
+ private:
+  const Object& code_;
+};
+
 // A Code object descriptor.
 class CodeDescriptor : public ZoneAllocated {
  public:
-  explicit CodeDescriptor(const Code& code);
+  explicit CodeDescriptor(const AbstractCode code);
 
   uword Start() const;
 
@@ -445,7 +518,7 @@
 
   int64_t CompileTimestamp() const;
 
-  RawCode* code() const { return code_.raw(); }
+  const AbstractCode code() const { return code_; }
 
   const char* Name() const { return code_.Name(); }
 
@@ -471,7 +544,7 @@
   }
 
  private:
-  const Code& code_;
+  const AbstractCode code_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeDescriptor);
 };
@@ -492,7 +565,7 @@
  private:
   void Build(Thread* thread);
 
-  void Add(const Code& code);
+  void Add(const Object& code);
 
   // Code objects sorted by entry.
   ZoneGrowableArray<CodeDescriptor*> code_objects_;
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index a67a883..b2405f8 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -272,7 +272,7 @@
                          uword start,
                          uword end,
                          int64_t timestamp,
-                         const Code& code)
+                         const AbstractCode code)
     : kind_(kind),
       start_(start),
       end_(end),
@@ -482,7 +482,7 @@
   obj.AddProperty("exclusiveTicks", exclusive_ticks());
   if (kind() == kDartCode) {
     ASSERT(!code_.IsNull());
-    obj.AddProperty("code", code_);
+    obj.AddProperty("code", *code_.handle());
   } else if (kind() == kCollectedCode) {
     PrintCollectedCode(&obj);
   } else if (kind() == kReusedCode) {
@@ -1196,7 +1196,7 @@
         extra_tags_(extra_tags),
         profile_(profile),
         deoptimized_code_(new DeoptimizedCodeSet(thread->isolate())),
-        null_code_(Code::ZoneHandle()),
+        null_code_(Code::null()),
         null_function_(Function::ZoneHandle()),
         tick_functions_(false),
         inclusive_tree_(false),
@@ -1288,8 +1288,7 @@
     for (intptr_t i = 0; i < code_lookup_table.length(); i++) {
       const CodeDescriptor* descriptor = code_lookup_table.At(i);
       ASSERT(descriptor != NULL);
-      const Code& code = Code::Handle(descriptor->code());
-      ASSERT(!code.IsNull());
+      const AbstractCode code = descriptor->code();
       RegisterLiveProfileCode(new ProfileCode(
           ProfileCode::kDartCode, code.PayloadStart(),
           code.PayloadStart() + code.Size(), code.compile_timestamp(), code));
@@ -1426,7 +1425,6 @@
       }
 
       // Walk the sampled PCs.
-      Code& code = Code::Handle();
       for (intptr_t frame_index = sample->length() - 1; frame_index >= 0;
            frame_index--) {
         ASSERT(sample->At(frame_index) != 0);
@@ -1436,7 +1434,7 @@
         ProfileCode* profile_code =
             GetProfileCode(sample->At(frame_index), sample->timestamp());
         ASSERT(profile_code->code_table_index() == index);
-        code ^= profile_code->code();
+        const AbstractCode code = profile_code->code();
         current = AppendKind(code, current, sample);
         current = current->GetChild(index);
         current->Tick(sample, (frame_index == 0));
@@ -1469,7 +1467,6 @@
       }
 
       // Walk the sampled PCs.
-      Code& code = Code::Handle();
       for (intptr_t frame_index = 0; frame_index < sample->length();
            frame_index++) {
         ASSERT(sample->At(frame_index) != 0);
@@ -1479,7 +1476,7 @@
         ProfileCode* profile_code =
             GetProfileCode(sample->At(frame_index), sample->timestamp());
         ASSERT(profile_code->code_table_index() == index);
-        code ^= profile_code->code();
+        const AbstractCode code = profile_code->code();
         current = current->GetChild(index);
         if (ShouldTickNode(sample, frame_index)) {
           current->Tick(sample, (frame_index == 0));
@@ -1592,7 +1589,12 @@
     ASSERT(function != NULL);
     const intptr_t code_index = profile_code->code_table_index();
     ASSERT(profile_code != NULL);
-    const Code& code = Code::ZoneHandle(profile_code->code());
+    Code& code = Code::ZoneHandle();
+    if (profile_code->code().IsCode()) {
+      code ^= profile_code->code().raw();
+    } else {
+      // No inlining in bytecode.
+    }
     GrowableArray<const Function*>* inlined_functions = NULL;
     GrowableArray<TokenPosition>* inlined_token_positions = NULL;
     TokenPosition token_position = TokenPosition::kNoSource;
@@ -1846,7 +1848,7 @@
     return current;
   }
 
-  ProfileCodeTrieNode* AppendKind(const Code& code,
+  ProfileCodeTrieNode* AppendKind(const AbstractCode code,
                                   ProfileCodeTrieNode* current,
                                   ProcessedSample* sample) {
     if (code.IsNull()) {
@@ -2200,7 +2202,6 @@
     }
 
     // We haven't seen this pc yet.
-    Code& code = Code::Handle(thread_->zone());
 
     // Check NativeSymbolResolver for pc.
     uintptr_t native_start = 0;
@@ -2237,7 +2238,7 @@
 
     ASSERT(pc >= native_start);
     profile_code = new ProfileCode(ProfileCode::kNativeCode, native_start,
-                                   pc + 1, 0, code);
+                                   pc + 1, 0, null_code_);
     if (native_name != NULL) {
       profile_code->SetName(native_name);
       NativeSymbolResolver::FreeSymbolName(native_name);
@@ -2301,7 +2302,7 @@
   intptr_t extra_tags_;
   Profile* profile_;
   DeoptimizedCodeSet* deoptimized_code_;
-  const Code& null_code_;
+  const AbstractCode null_code_;
   const Function& null_function_;
   bool tick_functions_;
   bool inclusive_tree_;
diff --git a/runtime/vm/profiler_service.h b/runtime/vm/profiler_service.h
index ba1981a..6e7d660 100644
--- a/runtime/vm/profiler_service.h
+++ b/runtime/vm/profiler_service.h
@@ -10,6 +10,7 @@
 #include "vm/globals.h"
 #include "vm/growable_array.h"
 #include "vm/object.h"
+#include "vm/profiler.h"
 #include "vm/tags.h"
 #include "vm/thread_interrupter.h"
 #include "vm/token_position.h"
@@ -158,7 +159,7 @@
               uword start,
               uword end,
               int64_t timestamp,
-              const Code& code);
+              const AbstractCode code);
 
   Kind kind() const { return kind_; }
 
@@ -195,7 +196,7 @@
   void IncInclusiveTicks() { inclusive_ticks_++; }
 
   bool IsOptimizedDart() const;
-  RawCode* code() const { return code_.raw(); }
+  const AbstractCode code() const { return code_; }
 
   const char* name() const { return name_; }
   void SetName(const char* name);
@@ -228,7 +229,7 @@
   intptr_t inclusive_ticks_;
   intptr_t inclusive_serial_;
 
-  const Code& code_;
+  const AbstractCode code_;
   char* name_;
   int64_t compile_timestamp_;
   ProfileFunction* function_;
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index 9f4b0f6..1cb8236 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -282,18 +282,18 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT(!walker.Down());
 
     // Inclusive code: main -> B.boo.
     walker.Reset(Profile::kInclusiveCode);
     // Move down from the root.
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT(walker.Down());
@@ -618,18 +618,18 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT(!walker.Down());
 
     // Inclusive code: main -> B.boo.
     walker.Reset(Profile::kInclusiveCode);
     // Move down from the root.
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT(walker.Down());
@@ -747,11 +747,11 @@
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT_EQ(3, walker.CurrentExclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT_EQ(3, walker.CurrentNodeTickCount());
     EXPECT_EQ(3, walker.CurrentInclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT_EQ(3, walker.CurrentNodeTickCount());
     EXPECT_EQ(3, walker.CurrentInclusiveTicks());
     EXPECT_EQ(0, walker.CurrentExclusiveTicks());
@@ -761,12 +761,12 @@
     walker.Reset(Profile::kInclusiveCode);
     // Move down from the root.
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT_EQ(3, walker.CurrentNodeTickCount());
     EXPECT_EQ(3, walker.CurrentInclusiveTicks());
     EXPECT_EQ(0, walker.CurrentExclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT_EQ(3, walker.CurrentNodeTickCount());
     EXPECT_EQ(3, walker.CurrentInclusiveTicks());
     EXPECT(walker.Down());
@@ -923,11 +923,11 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("Double_add", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("_Double._add", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _Double._add", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("_Double.+", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _Double.+", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -990,9 +990,9 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] AllocateArray", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("new _List", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] new _List", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -1080,7 +1080,7 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] AllocateContext", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -1212,9 +1212,9 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("TypedData_Float32Array_new", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("new Float32List", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] new Float32List", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -1294,10 +1294,10 @@
     EXPECT_STREQ("String_concat", walker.CurrentName());
     EXPECT(walker.Down());
 #if 1
-    EXPECT_STREQ("_StringBase.+", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _StringBase.+", walker.CurrentName());
     EXPECT(walker.Down());
 #endif
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -1376,13 +1376,16 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("OneByteString_allocate", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("_OneByteString._allocate", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _OneByteString._allocate",
+                 walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("_OneByteString._concatAll", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _OneByteString._concatAll",
+                 walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("_StringBase._interpolate", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] _StringBase._interpolate",
+                 walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("foo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 
@@ -1493,12 +1496,12 @@
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT_EQ(50000, walker.CurrentExclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("*B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
     EXPECT_EQ(1, walker.SiblingCount());
     EXPECT_EQ(50000, walker.CurrentNodeTickCount());
     EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("mainA", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
     EXPECT_EQ(1, walker.SiblingCount());
     EXPECT_EQ(50000, walker.CurrentNodeTickCount());
     EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
@@ -1507,13 +1510,13 @@
     // We have two code objects: mainA and B.boo.
     walker.Reset(Profile::kInclusiveCode);
     EXPECT(walker.Down());
-    EXPECT_STREQ("mainA", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
     EXPECT_EQ(1, walker.SiblingCount());
     EXPECT_EQ(50000, walker.CurrentNodeTickCount());
     EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
     EXPECT_EQ(0, walker.CurrentExclusiveTicks());
     EXPECT(walker.Down());
-    EXPECT_STREQ("*B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
     EXPECT_EQ(1, walker.SiblingCount());
     EXPECT_EQ(50000, walker.CurrentNodeTickCount());
     EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
@@ -1613,11 +1616,11 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Unoptimized Code]", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("*B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Optimized Code]", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("mainA", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Unoptimized Code]", walker.CurrentName());
     EXPECT(!walker.Down());
@@ -1626,11 +1629,11 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Unoptimized Code]", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("mainA", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Optimized Code]", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("*B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
     EXPECT_STREQ("[Unoptimized Code]", walker.CurrentName());
     EXPECT(walker.Down());
@@ -1889,45 +1892,45 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("B.boo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("orange", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] orange", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("napkin", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] napkin", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("mayo", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] mayo", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("lemon", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] lemon", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("kindle", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] kindle", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("jeep", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] jeep", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("ice", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] ice", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("haystack", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] haystack", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("granola", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] granola", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("fred", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] fred", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("elephant", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] elephant", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("dog", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] dog", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("cantaloupe", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] cantaloupe", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("banana", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] banana", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("apple", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] apple", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("secondInit", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] secondInit", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("init", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] init", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("go", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] go", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("main", walker.CurrentName());
+    EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
     EXPECT(!walker.Down());
   }
 }
@@ -2718,7 +2721,7 @@
   EXPECT_EQ(table->FindCodeForPC(42), static_cast<ProfileCode*>(NULL));
 
   int64_t timestamp = 0;
-  Code& null_code = Code::Handle(Z);
+  const AbstractCode null_code(Code::null());
 
   ProfileCode* code1 = new (Z)
       ProfileCode(ProfileCode::kNativeCode, 50, 60, timestamp, null_code);
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 3ce3ced..d423f89 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -521,9 +521,8 @@
 }
 
 bool RawCode::ContainsPC(RawObject* raw_obj, uword pc) {
-  uint32_t tags = raw_obj->ptr()->tags_;
-  if (RawObject::ClassIdTag::decode(tags) == kCodeCid) {
-    RawCode* raw_code = reinterpret_cast<RawCode*>(raw_obj);
+  if (raw_obj->IsCode()) {
+    RawCode* raw_code = static_cast<RawCode*>(raw_obj);
     return RawInstructions::ContainsPC(raw_code->ptr()->instructions_, pc);
   }
   return false;
@@ -557,6 +556,17 @@
 #endif
 }
 
+bool RawBytecode::ContainsPC(RawObject* raw_obj, uword pc) {
+  if (raw_obj->IsBytecode()) {
+    RawBytecode* raw_bytecode = static_cast<RawBytecode*>(raw_obj);
+    RawExternalTypedData* bytes = raw_bytecode->ptr()->instructions_;
+    uword start = reinterpret_cast<uword>(bytes->ptr()->data_);
+    uword size = Smi::Value(bytes->ptr()->length_);
+    return (pc - start) < size;
+  }
+  return false;
+}
+
 intptr_t RawObjectPool::VisitObjectPoolPointers(RawObjectPool* raw_obj,
                                                 ObjectPointerVisitor* visitor) {
   const intptr_t length = raw_obj->ptr()->length_;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 3b4717d..3e35ec4 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1490,6 +1490,8 @@
 
   intptr_t source_positions_binary_offset_;
 
+  static bool ContainsPC(RawObject* raw_obj, uword pc);
+
   friend class Function;
   friend class StackFrame;
 };
@@ -2395,6 +2397,8 @@
   VISIT_TO(RawCompressed, length_)
 
   uint8_t* data_;
+
+  friend class RawBytecode;
 };
 
 // VM implementations of the basic types in the isolate.
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 1f5782a..7e2cb38 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -1864,6 +1864,10 @@
   if (!code.IsNull()) {
     return code.raw();
   }
+  Bytecode& bytecode = Bytecode::Handle(Bytecode::FindCode(pc));
+  if (!bytecode.IsNull()) {
+    return bytecode.raw();
+  }
 
   // Not found.
   return Object::sentinel().raw();