mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[VM] Bare instructions - Part 3: Add support for building a PC -> Code mapping table
This CL adds a [ReversePcLookupCache] which, based on a list of code objects, builds up a binary searchable table for mapping PCs to Code objects (where all the metadata is available, like stackmaps, ...). This CL also adds stack walking support for "bare instruction" frames. In a later part we will start emitting the sorted list of code objects (via the new field in the object store) under a flag. Issue https://github.com/dart-lang/sdk/issues/33274 Change-Id: I3c06c12bc0fb266dc1bd843a4a11b5208773151d Reviewed-on: https://dart-review.googlesource.com/c/85746 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
parent
7a968ae283
commit
2440a32461
|
@ -36,6 +36,7 @@
|
|||
#include "vm/port.h"
|
||||
#include "vm/profiler.h"
|
||||
#include "vm/reusable_handles.h"
|
||||
#include "vm/reverse_pc_lookup_cache.h"
|
||||
#include "vm/service.h"
|
||||
#include "vm/service_event.h"
|
||||
#include "vm/service_isolate.h"
|
||||
|
@ -946,7 +947,8 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags)
|
|||
handler_info_cache_(),
|
||||
catch_entry_moves_cache_(),
|
||||
embedder_entry_points_(NULL),
|
||||
obfuscation_map_(NULL) {
|
||||
obfuscation_map_(NULL),
|
||||
reverse_pc_lookup_cache_(nullptr) {
|
||||
FlagsCopyFrom(api_flags);
|
||||
SetErrorsFatal(true);
|
||||
set_compilation_allowed(true);
|
||||
|
@ -974,6 +976,9 @@ Isolate::~Isolate() {
|
|||
// RELEASE_ASSERT(reload_context_ == NULL);
|
||||
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
delete reverse_pc_lookup_cache_;
|
||||
reverse_pc_lookup_cache_ = nullptr;
|
||||
|
||||
delete background_compiler_;
|
||||
background_compiler_ = NULL;
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ class RawInteger;
|
|||
class RawFloat32x4;
|
||||
class RawInt32x4;
|
||||
class RawUserTag;
|
||||
class ReversePcLookupCache;
|
||||
class SafepointHandler;
|
||||
class SampleBuffer;
|
||||
class SendPort;
|
||||
|
@ -704,6 +705,17 @@ class Isolate : public BaseIsolate {
|
|||
void set_obfuscation_map(const char** map) { obfuscation_map_ = map; }
|
||||
const char** obfuscation_map() const { return obfuscation_map_; }
|
||||
|
||||
// Returns the pc -> code lookup cache object for this isolate.
|
||||
ReversePcLookupCache* reverse_pc_lookup_cache() const {
|
||||
return reverse_pc_lookup_cache_;
|
||||
}
|
||||
|
||||
// Sets the pc -> code lookup cache object for this isolate.
|
||||
void set_reverse_pc_lookup_cache(ReversePcLookupCache* table) {
|
||||
ASSERT(reverse_pc_lookup_cache_ == nullptr);
|
||||
reverse_pc_lookup_cache_ = table;
|
||||
}
|
||||
|
||||
// Isolate-specific flag handling.
|
||||
static void FlagsInitialize(Dart_IsolateFlags* api_flags);
|
||||
void FlagsCopyTo(Dart_IsolateFlags* api_flags) const;
|
||||
|
@ -1006,6 +1018,8 @@ class Isolate : public BaseIsolate {
|
|||
Dart_QualifiedFunctionName* embedder_entry_points_;
|
||||
const char** obfuscation_map_;
|
||||
|
||||
ReversePcLookupCache* reverse_pc_lookup_cache_;
|
||||
|
||||
static Dart_IsolateCreateCallback create_callback_;
|
||||
static Dart_IsolateShutdownCallback shutdown_callback_;
|
||||
static Dart_IsolateCleanupCallback cleanup_callback_;
|
||||
|
|
|
@ -7947,6 +7947,8 @@ class Array : public Instance {
|
|||
return memcmp(a->ptr()->data(), b->ptr()->data(), kWordSize * length) == 0;
|
||||
}
|
||||
|
||||
static RawObject** DataOf(RawArray* array) { return array->ptr()->data(); }
|
||||
|
||||
RawObject* At(intptr_t index) const { return *ObjectAddr(index); }
|
||||
void SetAt(intptr_t index, const Object& value) const {
|
||||
// TODO(iposva): Add storing NoSafepointScope.
|
||||
|
|
|
@ -124,6 +124,7 @@ class ObjectPointerVisitor;
|
|||
RW(Array, unique_dynamic_targets) \
|
||||
RW(GrowableObjectArray, megamorphic_cache_table) \
|
||||
RW(Code, build_method_extractor_code) \
|
||||
RW(Array, code_order_table) \
|
||||
R_(Code, megamorphic_miss_code) \
|
||||
R_(Function, megamorphic_miss_function) \
|
||||
RW(Array, obfuscation_map) \
|
||||
|
|
56
runtime/vm/reverse_pc_lookup_cache.cc
Normal file
56
runtime/vm/reverse_pc_lookup_cache.cc
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2018, 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/reverse_pc_lookup_cache.h"
|
||||
|
||||
#include "vm/isolate.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
static uword BeginPcFromCode(const RawCode* code) {
|
||||
auto instr = Code::InstructionsOf(code);
|
||||
return Instructions::PayloadStart(instr);
|
||||
}
|
||||
|
||||
static uword EndPcFromCode(const RawCode* code) {
|
||||
auto instr = Code::InstructionsOf(code);
|
||||
return Instructions::PayloadStart(instr) + Instructions::Size(instr);
|
||||
}
|
||||
|
||||
void ReversePcLookupCache::BuildAndAttachToIsolate(Isolate* isolate) {
|
||||
auto object_store = isolate->object_store();
|
||||
auto& array = Array::Handle(object_store->code_order_table());
|
||||
if (!array.IsNull()) {
|
||||
const intptr_t length = array.Length();
|
||||
{
|
||||
NoSafepointScope no_safepoint_scope;
|
||||
|
||||
const uword begin =
|
||||
BeginPcFromCode(reinterpret_cast<RawCode*>(array.At(0)));
|
||||
const uword end =
|
||||
EndPcFromCode(reinterpret_cast<RawCode*>(array.At(length - 1)));
|
||||
|
||||
auto pc_array = new uint32_t[length];
|
||||
for (intptr_t i = 0; i < length; i++) {
|
||||
const auto end_pc =
|
||||
EndPcFromCode(reinterpret_cast<RawCode*>(array.At(i)));
|
||||
pc_array[i] = end_pc - begin;
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
for (intptr_t i = 1; i < length; i++) {
|
||||
ASSERT(pc_array[i - 1] <= pc_array[i]);
|
||||
}
|
||||
#endif // defined(DEBUG)
|
||||
auto cache =
|
||||
new ReversePcLookupCache(isolate, pc_array, length, begin, end);
|
||||
isolate->set_reverse_pc_lookup_cache(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
} // namespace dart
|
126
runtime/vm/reverse_pc_lookup_cache.h
Normal file
126
runtime/vm/reverse_pc_lookup_cache.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
#ifndef RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_
|
||||
#define RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_
|
||||
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/object_store.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class Isolate;
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
// A cache for looking up a Code object based on pc (currently the cache is
|
||||
// implemented as a binary-searchable uint32 array)
|
||||
//
|
||||
// If an AOT snapshot was created with --use_bare_instructions the isolate's
|
||||
// object store will contain a `code_order_table` - which is a sorted array
|
||||
// of [Code] objects. The order is based on addresses of the code's
|
||||
// instructions in memory.
|
||||
//
|
||||
// For a binary search we would need to touch O(log(array-size)) array entries,
|
||||
// code objects and instruction objects.
|
||||
//
|
||||
// To avoid this we make another uint32 array which is initialized from the end
|
||||
// PCs of the instructions (relative to the start pc of the first instruction
|
||||
// object).
|
||||
//
|
||||
// We have the following invariants:
|
||||
//
|
||||
// BeginPcFromCode(code_array[0]) <= pc_array[0]
|
||||
// pc_array[i] == EndPcFromCode(code_array[i])
|
||||
// pc_array[i] <= pc_array[i+1]
|
||||
//
|
||||
// The lookup will then do a binary search in pc_array. The index can then be
|
||||
// used in the `code_order_table` of the object store.
|
||||
//
|
||||
// WARNING: This class cannot do memory allocation or handle allocation!
|
||||
class ReversePcLookupCache {
|
||||
public:
|
||||
ReversePcLookupCache(Isolate* isolate,
|
||||
uint32_t* pc_array,
|
||||
intptr_t length,
|
||||
uword first_absolute_pc,
|
||||
uword last_absolute_pc)
|
||||
: isolate_(isolate),
|
||||
pc_array_(pc_array),
|
||||
length_(length),
|
||||
first_absolute_pc_(first_absolute_pc),
|
||||
last_absolute_pc_(last_absolute_pc) {}
|
||||
~ReversePcLookupCache() { delete[] pc_array_; }
|
||||
|
||||
// Builds a [ReversePcLookupCache] and attaches it to the isolate (if
|
||||
// `code_order_table` is non-`null`).
|
||||
static void BuildAndAttachToIsolate(Isolate* isolate);
|
||||
|
||||
// Returns `true` if the given [pc] contains can be mapped to a [Code] object
|
||||
// using this cache.
|
||||
inline bool Contains(uword pc) {
|
||||
return first_absolute_pc_ <= pc && pc <= last_absolute_pc_;
|
||||
}
|
||||
|
||||
// Looks up the [Code] object from a given [pc].
|
||||
inline RawCode* Lookup(uword pc) {
|
||||
NoSafepointScope no_safepoint_scope;
|
||||
|
||||
intptr_t left = 0;
|
||||
intptr_t right = length_ - 1;
|
||||
|
||||
ASSERT(first_absolute_pc_ <= pc && pc < last_absolute_pc_);
|
||||
uint32_t pc_offset = static_cast<uint32_t>(pc - first_absolute_pc_);
|
||||
|
||||
while (left < right) {
|
||||
intptr_t middle = left + (right - left) / 2;
|
||||
|
||||
uword middle_pc = pc_array_[middle];
|
||||
if (middle_pc < pc_offset) {
|
||||
left = middle + 1;
|
||||
} else {
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
auto code_array = isolate_->object_store()->code_order_table();
|
||||
auto raw_code = reinterpret_cast<RawCode*>(Array::DataOf(code_array)[left]);
|
||||
|
||||
#if defined(DEBUG)
|
||||
ASSERT(raw_code->GetClassIdMayBeSmi() == kCodeCid);
|
||||
ASSERT(Code::ContainsInstructionAt(raw_code, pc));
|
||||
#endif
|
||||
|
||||
return raw_code;
|
||||
}
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
uint32_t* pc_array_;
|
||||
intptr_t length_;
|
||||
uword first_absolute_pc_;
|
||||
uword last_absolute_pc_;
|
||||
};
|
||||
|
||||
#else // defined(DART_PRECOMPILED_RUNTIME
|
||||
|
||||
class ReversePcLookupCache {
|
||||
public:
|
||||
ReversePcLookupCache() {}
|
||||
~ReversePcLookupCache() {}
|
||||
|
||||
static void BuildAndAttachToIsolate(Isolate* isolate) {}
|
||||
|
||||
inline bool Contains(uword pc) { return false; }
|
||||
|
||||
inline RawCode* Lookup(uword pc) { UNREACHABLE(); }
|
||||
};
|
||||
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_
|
|
@ -15,6 +15,7 @@
|
|||
#include "vm/parser.h"
|
||||
#include "vm/raw_object.h"
|
||||
#include "vm/reusable_handles.h"
|
||||
#include "vm/reverse_pc_lookup_cache.h"
|
||||
#include "vm/scopes.h"
|
||||
#include "vm/stub_code.h"
|
||||
#include "vm/visitor.h"
|
||||
|
@ -65,16 +66,100 @@ void FrameLayout::Init() {
|
|||
runtime_frame_layout = default_frame_layout;
|
||||
}
|
||||
|
||||
Isolate* StackFrame::IsolateOfBareInstructionsFrame() const {
|
||||
auto isolate = this->isolate();
|
||||
|
||||
if (isolate->object_store()->code_order_table() != Object::null()) {
|
||||
auto rct = isolate->reverse_pc_lookup_cache();
|
||||
if (rct->Contains(pc())) return isolate;
|
||||
}
|
||||
|
||||
isolate = Dart::vm_isolate();
|
||||
if (isolate->object_store()->code_order_table() != Object::null()) {
|
||||
auto rct = isolate->reverse_pc_lookup_cache();
|
||||
if (rct->Contains(pc())) return isolate;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool StackFrame::IsBareInstructionsDartFrame() const {
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
if (auto isolate = IsolateOfBareInstructionsFrame()) {
|
||||
Code code;
|
||||
auto rct = isolate->reverse_pc_lookup_cache();
|
||||
code = rct->Lookup(pc());
|
||||
|
||||
// All stub codes have a `null` owner except for the megamorphic miss
|
||||
// stub. So if it's neither of those, we are know it must be a
|
||||
// precompiled dart frame.
|
||||
RawObject* owner = code.owner();
|
||||
if (owner != Object::null()) {
|
||||
if (code.raw() ==
|
||||
Isolate::Current()->object_store()->megamorphic_miss_code()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StackFrame::IsBareInstructionsStubFrame() const {
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
if (auto isolate = IsolateOfBareInstructionsFrame()) {
|
||||
Code code;
|
||||
auto rct = isolate->reverse_pc_lookup_cache();
|
||||
code = rct->Lookup(pc());
|
||||
|
||||
// All stub codes have a `null` owner except for the megamorphic miss stub.
|
||||
// So if it's either of those, we are know it must be a precompiled stub
|
||||
// frame.
|
||||
RawObject* owner = code.owner();
|
||||
if (owner == Object::null()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code.raw() ==
|
||||
Isolate::Current()->object_store()->megamorphic_miss_code()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StackFrame::IsDartFrame(bool validate) const {
|
||||
ASSERT(!validate || IsValid());
|
||||
|
||||
if (IsEntryFrame() || IsExitFrame()) return false;
|
||||
|
||||
// Even though the megamorphic miss stub is a stub, we consider it as a
|
||||
// dart frame for all practical purposes.
|
||||
const bool is_megamorphic_miss_stub = Code::ContainsInstructionAt(
|
||||
thread_->isolate()->object_store()->megamorphic_miss_code(), pc_);
|
||||
|
||||
if (is_megamorphic_miss_stub) return true;
|
||||
|
||||
return !IsStubFrame();
|
||||
}
|
||||
|
||||
bool StackFrame::IsStubFrame() const {
|
||||
if (is_interpreted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsBareInstructionsStubFrame()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ASSERT(!(IsEntryFrame() || IsExitFrame()));
|
||||
#if !defined(HOST_OS_WINDOWS) && !defined(HOST_OS_FUCHSIA)
|
||||
// On Windows and Fuchsia, the profiler calls this from a separate thread
|
||||
// where Thread::Current() is NULL, so we cannot create a NoSafepointScope.
|
||||
NoSafepointScope no_safepoint;
|
||||
#endif
|
||||
|
||||
RawCode* code = GetCodeObject();
|
||||
ASSERT(code != Object::null());
|
||||
const intptr_t cid = code->ptr()->owner_->GetClassId();
|
||||
|
@ -172,21 +257,27 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|||
// be able to reuse the handle based code and avoid having to add
|
||||
// helper functions to the raw object interface.
|
||||
NoSafepointScope no_safepoint;
|
||||
RawObject* pc_marker = *(reinterpret_cast<RawObject**>(
|
||||
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
|
||||
: runtime_frame_layout.code_from_fp) *
|
||||
kWordSize)));
|
||||
// May forward raw code. Note we don't just visit the pc marker slot first
|
||||
// because the visitor's forwarding might not be idempotent.
|
||||
visitor->VisitPointer(&pc_marker);
|
||||
Code code;
|
||||
if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) {
|
||||
code ^= pc_marker;
|
||||
|
||||
if (auto isolate = IsolateOfBareInstructionsFrame()) {
|
||||
code = isolate->reverse_pc_lookup_cache()->Lookup(pc());
|
||||
} else {
|
||||
ASSERT(pc_marker == Object::null() ||
|
||||
(is_interpreted() && (!pc_marker->IsHeapObject() ||
|
||||
(pc_marker->GetClassId() == kBytecodeCid))));
|
||||
RawObject* pc_marker = *(reinterpret_cast<RawObject**>(
|
||||
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
|
||||
: runtime_frame_layout.code_from_fp) *
|
||||
kWordSize)));
|
||||
// May forward raw code. Note we don't just visit the pc marker slot first
|
||||
// because the visitor's forwarding might not be idempotent.
|
||||
visitor->VisitPointer(&pc_marker);
|
||||
if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) {
|
||||
code ^= pc_marker;
|
||||
} else {
|
||||
ASSERT(pc_marker == Object::null() ||
|
||||
(is_interpreted() && (!pc_marker->IsHeapObject() ||
|
||||
(pc_marker->GetClassId() == kBytecodeCid))));
|
||||
}
|
||||
}
|
||||
|
||||
if (!code.IsNull()) {
|
||||
// Optimized frames have a stack map. We need to visit the frame based
|
||||
// on the stack map.
|
||||
|
@ -328,6 +419,10 @@ RawCode* StackFrame::LookupDartCode() const {
|
|||
// where Thread::Current() is NULL, so we cannot create a NoSafepointScope.
|
||||
NoSafepointScope no_safepoint;
|
||||
#endif
|
||||
if (auto isolate = IsolateOfBareInstructionsFrame()) {
|
||||
return isolate->reverse_pc_lookup_cache()->Lookup(pc());
|
||||
}
|
||||
|
||||
RawCode* code = GetCodeObject();
|
||||
if ((code != Code::null()) &&
|
||||
(code->ptr()->owner_->GetClassId() == kFunctionCid)) {
|
||||
|
@ -338,11 +433,15 @@ RawCode* StackFrame::LookupDartCode() const {
|
|||
|
||||
RawCode* StackFrame::GetCodeObject() const {
|
||||
ASSERT(!is_interpreted());
|
||||
RawObject* pc_marker = *(reinterpret_cast<RawObject**>(
|
||||
fp() + runtime_frame_layout.code_from_fp * kWordSize));
|
||||
ASSERT((pc_marker == Object::null()) ||
|
||||
(pc_marker->GetClassId() == kCodeCid));
|
||||
return reinterpret_cast<RawCode*>(pc_marker);
|
||||
if (auto isolate = IsolateOfBareInstructionsFrame()) {
|
||||
return isolate->reverse_pc_lookup_cache()->Lookup(pc());
|
||||
} else {
|
||||
RawObject* pc_marker = *(reinterpret_cast<RawObject**>(
|
||||
fp() + runtime_frame_layout.code_from_fp * kWordSize));
|
||||
ASSERT((pc_marker == Object::null()) ||
|
||||
(pc_marker->GetClassId() == kCodeCid));
|
||||
return reinterpret_cast<RawCode*>(pc_marker);
|
||||
}
|
||||
}
|
||||
|
||||
RawBytecode* StackFrame::LookupDartBytecode() const {
|
||||
|
|
|
@ -152,14 +152,24 @@ class StackFrame : public ValueObject {
|
|||
// Check validity of a frame, used for assertion purposes.
|
||||
virtual bool IsValid() const;
|
||||
|
||||
// Returns the isolate containing the bare instructions of the current frame.
|
||||
//
|
||||
// If the frame does not belong to a bare instructions snapshot, it will
|
||||
// return nullptr.
|
||||
Isolate* IsolateOfBareInstructionsFrame() const;
|
||||
|
||||
// Returns true iff the current frame is a bare instructions dart frame.
|
||||
bool IsBareInstructionsDartFrame() const;
|
||||
|
||||
// Returns true iff the current frame is a bare instructions stub frame.
|
||||
bool IsBareInstructionsStubFrame() const;
|
||||
|
||||
// Frame type.
|
||||
virtual bool IsDartFrame(bool validate = true) const {
|
||||
ASSERT(!validate || IsValid());
|
||||
return !(IsEntryFrame() || IsExitFrame() || IsStubFrame());
|
||||
}
|
||||
virtual bool IsDartFrame(bool validate = true) const;
|
||||
virtual bool IsStubFrame() const;
|
||||
virtual bool IsEntryFrame() const { return false; }
|
||||
virtual bool IsExitFrame() const { return false; }
|
||||
|
||||
virtual bool is_interpreted() const { return is_interpreted_; }
|
||||
|
||||
RawFunction* LookupDartFunction() const;
|
||||
|
@ -180,7 +190,9 @@ class StackFrame : public ValueObject {
|
|||
|
||||
// Name of the frame, used for generic frame printing functionality.
|
||||
virtual const char* GetName() const {
|
||||
return IsStubFrame() ? "stub" : "dart";
|
||||
if (IsBareInstructionsStubFrame()) return "bare-stub";
|
||||
if (IsStubFrame()) return "stub";
|
||||
return IsBareInstructionsDartFrame() ? "bare-dart" : "dart";
|
||||
}
|
||||
|
||||
Isolate* isolate() const { return thread_->isolate(); }
|
||||
|
|
|
@ -239,6 +239,8 @@ vm_sources = [
|
|||
"resolver.cc",
|
||||
"resolver.h",
|
||||
"reusable_handles.h",
|
||||
"reverse_pc_lookup_cache.cc",
|
||||
"reverse_pc_lookup_cache.h",
|
||||
"ring_buffer.h",
|
||||
"runtime_entry.cc",
|
||||
"runtime_entry.h",
|
||||
|
|
Loading…
Reference in a new issue