Commit 34c37396 authored by Andrew Comminos's avatar Andrew Comminos Committed by Commit Bot

[cpu-profiler] Track code object deletion using WeakCodeRegistry

Propagates CodeDeleteEvents to the CPU profiler based on finalizers
registered in a WeakCodeRegistry, which tracks heap objects for weakly
owned CodeEntries.

Bug: v8:11054
Change-Id: I4c1f7885e982241724ca9f284f864da008ce9d75
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2751606Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Andrew Comminos <acomminos@fb.com>
Cr-Commit-Position: refs/heads/master@{#73585}
parent 7a7e48a1
...@@ -2903,6 +2903,7 @@ v8_header_set("v8_internal_headers") { ...@@ -2903,6 +2903,7 @@ v8_header_set("v8_internal_headers") {
"src/profiler/symbolizer.h", "src/profiler/symbolizer.h",
"src/profiler/tick-sample.h", "src/profiler/tick-sample.h",
"src/profiler/tracing-cpu-profiler.h", "src/profiler/tracing-cpu-profiler.h",
"src/profiler/weak-code-registry.h",
"src/regexp/experimental/experimental-bytecode.h", "src/regexp/experimental/experimental-bytecode.h",
"src/regexp/experimental/experimental-compiler.h", "src/regexp/experimental/experimental-compiler.h",
"src/regexp/experimental/experimental-interpreter.h", "src/regexp/experimental/experimental-interpreter.h",
...@@ -3846,6 +3847,7 @@ v8_source_set("v8_base_without_compiler") { ...@@ -3846,6 +3847,7 @@ v8_source_set("v8_base_without_compiler") {
"src/profiler/symbolizer.cc", "src/profiler/symbolizer.cc",
"src/profiler/tick-sample.cc", "src/profiler/tick-sample.cc",
"src/profiler/tracing-cpu-profiler.cc", "src/profiler/tracing-cpu-profiler.cc",
"src/profiler/weak-code-registry.cc",
"src/regexp/experimental/experimental-bytecode.cc", "src/regexp/experimental/experimental-bytecode.cc",
"src/regexp/experimental/experimental-compiler.cc", "src/regexp/experimental/experimental-compiler.cc",
"src/regexp/experimental/experimental-interpreter.cc", "src/regexp/experimental/experimental-interpreter.cc",
......
...@@ -2020,6 +2020,9 @@ void MarkCompactCollector::MarkLiveObjects() { ...@@ -2020,6 +2020,9 @@ void MarkCompactCollector::MarkLiveObjects() {
DCHECK(local_marking_worklists()->IsEmpty()); DCHECK(local_marking_worklists()->IsEmpty());
} }
// We depend on IterateWeakRootsForPhantomHandles being called before
// ClearOldBytecodeCandidates in order to identify flushed bytecode in the
// CPU profiler.
{ {
heap()->isolate()->global_handles()->IterateWeakRootsForPhantomHandles( heap()->isolate()->global_handles()->IterateWeakRootsForPhantomHandles(
&IsUnmarkedHeapObject); &IsUnmarkedHeapObject);
...@@ -2083,6 +2086,8 @@ void MarkCompactCollector::ClearNonLiveReferences() { ...@@ -2083,6 +2086,8 @@ void MarkCompactCollector::ClearNonLiveReferences() {
ClearJSWeakRefs(); ClearJSWeakRefs();
} }
PROFILE(heap()->isolate(), WeakCodeClearEvent());
MarkDependentCodeForDeoptimization(); MarkDependentCodeForDeoptimization();
DCHECK(weak_objects_.transition_arrays.IsEmpty()); DCHECK(weak_objects_.transition_arrays.IsEmpty());
...@@ -2206,8 +2211,6 @@ void MarkCompactCollector::FlushBytecodeFromSFI( ...@@ -2206,8 +2211,6 @@ void MarkCompactCollector::FlushBytecodeFromSFI(
// performing the unusual task of decompiling. // performing the unusual task of decompiling.
shared_info.set_function_data(uncompiled_data, kReleaseStore); shared_info.set_function_data(uncompiled_data, kReleaseStore);
DCHECK(!shared_info.is_compiled()); DCHECK(!shared_info.is_compiled());
PROFILE(heap()->isolate(), BytecodeFlushEvent(compiled_data_start));
} }
void MarkCompactCollector::ClearOldBytecodeCandidates() { void MarkCompactCollector::ClearOldBytecodeCandidates() {
......
...@@ -37,8 +37,7 @@ using WasmName = Vector<const char>; ...@@ -37,8 +37,7 @@ using WasmName = Vector<const char>;
V(CODE_MOVING_GC, code-moving-gc) \ V(CODE_MOVING_GC, code-moving-gc) \
V(SHARED_FUNC_MOVE_EVENT, sfi-move) \ V(SHARED_FUNC_MOVE_EVENT, sfi-move) \
V(SNAPSHOT_CODE_NAME_EVENT, snapshot-code-name) \ V(SNAPSHOT_CODE_NAME_EVENT, snapshot-code-name) \
V(TICK_EVENT, tick) \ V(TICK_EVENT, tick)
V(BYTECODE_FLUSH_EVENT, bytecode-flush)
// clang-format on // clang-format on
#define TAGS_LIST(V) \ #define TAGS_LIST(V) \
...@@ -110,8 +109,9 @@ class CodeEventListener { ...@@ -110,8 +109,9 @@ class CodeEventListener {
virtual void CodeDependencyChangeEvent(Handle<Code> code, virtual void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> shared, Handle<SharedFunctionInfo> shared,
const char* reason) = 0; const char* reason) = 0;
// Invoked during GC. No allocation allowed. // Called during GC shortly after any weak references to code objects are
virtual void BytecodeFlushEvent(Address compiled_data_start) = 0; // cleared.
virtual void WeakCodeClearEvent() = 0;
virtual bool is_listening_to_code_events() { return false; } virtual bool is_listening_to_code_events() { return false; }
}; };
...@@ -240,10 +240,9 @@ class CodeEventDispatcher : public CodeEventListener { ...@@ -240,10 +240,9 @@ class CodeEventDispatcher : public CodeEventListener {
listener->CodeDependencyChangeEvent(code, sfi, reason); listener->CodeDependencyChangeEvent(code, sfi, reason);
}); });
} }
void BytecodeFlushEvent(Address compiled_data_start) override { void WeakCodeClearEvent() override {
DispatchEventToListeners([=](CodeEventListener* listener) { DispatchEventToListeners(
listener->BytecodeFlushEvent(compiled_data_start); [=](CodeEventListener* listener) { listener->WeakCodeClearEvent(); });
});
} }
private: private:
......
...@@ -216,7 +216,7 @@ class Logger : public CodeEventListener { ...@@ -216,7 +216,7 @@ class Logger : public CodeEventListener {
void CodeDependencyChangeEvent(Handle<Code> code, void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> sfi, Handle<SharedFunctionInfo> sfi,
const char* reason) override; const char* reason) override;
void BytecodeFlushEvent(Address compiled_data_start) override {} void WeakCodeClearEvent() override {}
void ProcessDeoptEvent(Handle<Code> code, SourcePosition position, void ProcessDeoptEvent(Handle<Code> code, SourcePosition position,
const char* kind, const char* reason); const char* kind, const char* reason);
...@@ -416,7 +416,7 @@ class V8_EXPORT_PRIVATE CodeEventLogger : public CodeEventListener { ...@@ -416,7 +416,7 @@ class V8_EXPORT_PRIVATE CodeEventLogger : public CodeEventListener {
void CodeDependencyChangeEvent(Handle<Code> code, void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> sfi, Handle<SharedFunctionInfo> sfi,
const char* reason) override {} const char* reason) override {}
void BytecodeFlushEvent(Address compiled_data_start) override {} void WeakCodeClearEvent() override {}
protected: protected:
Isolate* isolate_; Isolate* isolate_;
...@@ -484,7 +484,7 @@ class ExternalCodeEventListener : public CodeEventListener { ...@@ -484,7 +484,7 @@ class ExternalCodeEventListener : public CodeEventListener {
void CodeDependencyChangeEvent(Handle<Code> code, void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> sfi, Handle<SharedFunctionInfo> sfi,
const char* reason) override {} const char* reason) override {}
void BytecodeFlushEvent(Address compiled_data_start) override {} void WeakCodeClearEvent() override {}
void StartListening(v8::CodeEventHandler* code_event_handler); void StartListening(v8::CodeEventHandler* code_event_handler);
void StopListening(); void StopListening();
......
...@@ -68,8 +68,9 @@ TickSample* SamplingEventsProcessor::StartTickSample() { ...@@ -68,8 +68,9 @@ TickSample* SamplingEventsProcessor::StartTickSample() {
return &evt->sample; return &evt->sample;
} }
void BytecodeFlushEventRecord::UpdateCodeMap(CodeMap* code_map) { void CodeDeleteEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->ClearCodesInRange(instruction_start, instruction_start + 1); bool removed = code_map->RemoveCode(entry);
CHECK(removed);
} }
void SamplingEventsProcessor::FinishTickSample() { void SamplingEventsProcessor::FinishTickSample() {
......
...@@ -201,7 +201,7 @@ void ProfilerEventsProcessor::CodeEventHandler( ...@@ -201,7 +201,7 @@ void ProfilerEventsProcessor::CodeEventHandler(
case CodeEventRecord::CODE_CREATION: case CodeEventRecord::CODE_CREATION:
case CodeEventRecord::CODE_MOVE: case CodeEventRecord::CODE_MOVE:
case CodeEventRecord::CODE_DISABLE_OPT: case CodeEventRecord::CODE_DISABLE_OPT:
case CodeEventRecord::BYTECODE_FLUSH: case CodeEventRecord::CODE_DELETE:
Enqueue(evt_rec); Enqueue(evt_rec);
break; break;
case CodeEventRecord::CODE_DEOPT: { case CodeEventRecord::CODE_DEOPT: {
...@@ -328,12 +328,16 @@ void* SamplingEventsProcessor::operator new(size_t size) { ...@@ -328,12 +328,16 @@ void* SamplingEventsProcessor::operator new(size_t size) {
void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); } void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
ProfilerCodeObserver::ProfilerCodeObserver(Isolate* isolate) ProfilerCodeObserver::ProfilerCodeObserver(Isolate* isolate)
: isolate_(isolate), code_map_(strings_), processor_(nullptr) { : isolate_(isolate),
code_map_(strings_),
weak_code_registry_(isolate),
processor_(nullptr) {
CreateEntriesForRuntimeCallStats(); CreateEntriesForRuntimeCallStats();
LogBuiltins(); LogBuiltins();
} }
void ProfilerCodeObserver::ClearCodeMap() { void ProfilerCodeObserver::ClearCodeMap() {
weak_code_registry_.Clear();
code_map_.Clear(); code_map_.Clear();
// We don't currently expect any references to refcounted strings to be // We don't currently expect any references to refcounted strings to be
// maintained with zero profiles after the code map is cleared. // maintained with zero profiles after the code map is cleared.
...@@ -513,9 +517,9 @@ void CpuProfiler::EnableLogging() { ...@@ -513,9 +517,9 @@ void CpuProfiler::EnableLogging() {
if (profiling_scope_) return; if (profiling_scope_) return;
if (!profiler_listener_) { if (!profiler_listener_) {
profiler_listener_.reset( profiler_listener_.reset(new ProfilerListener(
new ProfilerListener(isolate_, code_observer_.get(), isolate_, code_observer_.get(), *code_observer_->strings(),
*code_observer_->strings(), naming_mode_)); *code_observer_->weak_code_registry(), naming_mode_));
} }
profiling_scope_.reset( profiling_scope_.reset(
new ProfilingScope(isolate_, profiler_listener_.get())); new ProfilingScope(isolate_, profiler_listener_.get()));
......
...@@ -35,7 +35,7 @@ class Symbolizer; ...@@ -35,7 +35,7 @@ class Symbolizer;
V(CODE_DISABLE_OPT, CodeDisableOptEventRecord) \ V(CODE_DISABLE_OPT, CodeDisableOptEventRecord) \
V(CODE_DEOPT, CodeDeoptEventRecord) \ V(CODE_DEOPT, CodeDeoptEventRecord) \
V(REPORT_BUILTIN, ReportBuiltinEventRecord) \ V(REPORT_BUILTIN, ReportBuiltinEventRecord) \
V(BYTECODE_FLUSH, BytecodeFlushEventRecord) V(CODE_DELETE, CodeDeleteEventRecord)
class CodeEventRecord { class CodeEventRecord {
public: public:
...@@ -112,9 +112,9 @@ class TickSampleEventRecord { ...@@ -112,9 +112,9 @@ class TickSampleEventRecord {
TickSample sample; TickSample sample;
}; };
class BytecodeFlushEventRecord : public CodeEventRecord { class CodeDeleteEventRecord : public CodeEventRecord {
public: public:
Address instruction_start; CodeEntry* entry;
V8_INLINE void UpdateCodeMap(CodeMap* code_map); V8_INLINE void UpdateCodeMap(CodeMap* code_map);
}; };
...@@ -255,6 +255,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver { ...@@ -255,6 +255,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
void CodeEventHandler(const CodeEventsContainer& evt_rec) override; void CodeEventHandler(const CodeEventsContainer& evt_rec) override;
CodeMap* code_map() { return &code_map_; } CodeMap* code_map() { return &code_map_; }
StringsStorage* strings() { return &strings_; } StringsStorage* strings() { return &strings_; }
WeakCodeRegistry* weak_code_registry() { return &weak_code_registry_; }
void ClearCodeMap(); void ClearCodeMap();
...@@ -279,6 +280,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver { ...@@ -279,6 +280,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
Isolate* const isolate_; Isolate* const isolate_;
StringsStorage strings_; StringsStorage strings_;
CodeMap code_map_; CodeMap code_map_;
WeakCodeRegistry weak_code_registry_;
ProfilerEventsProcessor* processor_; ProfilerEventsProcessor* processor_;
}; };
......
...@@ -334,7 +334,6 @@ CpuProfileNode::SourceType ProfileNode::source_type() const { ...@@ -334,7 +334,6 @@ CpuProfileNode::SourceType ProfileNode::source_type() const {
case CodeEventListener::SHARED_FUNC_MOVE_EVENT: case CodeEventListener::SHARED_FUNC_MOVE_EVENT:
case CodeEventListener::SNAPSHOT_CODE_NAME_EVENT: case CodeEventListener::SNAPSHOT_CODE_NAME_EVENT:
case CodeEventListener::TICK_EVENT: case CodeEventListener::TICK_EVENT:
case CodeEventListener::BYTECODE_FLUSH_EVENT:
case CodeEventListener::NUMBER_OF_LOG_EVENTS: case CodeEventListener::NUMBER_OF_LOG_EVENTS:
return CpuProfileNode::kInternal; return CpuProfileNode::kInternal;
} }
...@@ -755,8 +754,24 @@ void CodeMap::Clear() { ...@@ -755,8 +754,24 @@ void CodeMap::Clear() {
} }
void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) { void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
ClearCodesInRange(addr, addr + size);
code_map_.emplace(addr, CodeEntryMapInfo{entry, size}); code_map_.emplace(addr, CodeEntryMapInfo{entry, size});
entry->set_instruction_start(addr);
}
bool CodeMap::RemoveCode(CodeEntry* entry) {
auto range = code_map_.equal_range(entry->instruction_start());
for (auto i = range.first; i != range.second; ++i) {
if (i->second.entry == entry) {
if (!entry->used()) {
DeleteCodeEntry(entry);
} else {
used_entries_.push_back(entry);
}
code_map_.erase(i);
return true;
}
}
return false;
} }
void CodeMap::ClearCodesInRange(Address start, Address end) { void CodeMap::ClearCodesInRange(Address start, Address end) {
...@@ -777,6 +792,9 @@ void CodeMap::ClearCodesInRange(Address start, Address end) { ...@@ -777,6 +792,9 @@ void CodeMap::ClearCodesInRange(Address start, Address end) {
} }
CodeEntry* CodeMap::FindEntry(Address addr, Address* out_instruction_start) { CodeEntry* CodeMap::FindEntry(Address addr, Address* out_instruction_start) {
// Note that an address may correspond to multiple CodeEntry objects. An
// arbitrary selection is made (as per multimap spec) in the event of a
// collision.
auto it = code_map_.upper_bound(addr); auto it = code_map_.upper_bound(addr);
if (it == code_map_.begin()) return nullptr; if (it == code_map_.begin()) return nullptr;
--it; --it;
...@@ -790,13 +808,25 @@ CodeEntry* CodeMap::FindEntry(Address addr, Address* out_instruction_start) { ...@@ -790,13 +808,25 @@ CodeEntry* CodeMap::FindEntry(Address addr, Address* out_instruction_start) {
void CodeMap::MoveCode(Address from, Address to) { void CodeMap::MoveCode(Address from, Address to) {
if (from == to) return; if (from == to) return;
auto it = code_map_.find(from);
if (it == code_map_.end()) return; auto range = code_map_.equal_range(from);
CodeEntryMapInfo info = it->second; // Instead of iterating until |range.second|, iterate the number of elements.
code_map_.erase(it); // This is because the |range.second| may no longer be the element past the
DCHECK(from + info.size <= to || to + info.size <= from); // end of the equal elements range after insertions.
ClearCodesInRange(to, to + info.size); size_t distance = std::distance(range.first, range.second);
code_map_.emplace(to, info); auto it = range.first;
while (distance--) {
CodeEntryMapInfo& info = it->second;
DCHECK(info.entry);
DCHECK_EQ(info.entry->instruction_start(), from);
info.entry->set_instruction_start(to);
DCHECK(from + info.size <= to || to + info.size <= from);
code_map_.emplace(to, info);
it++;
}
code_map_.erase(range.first, it);
} }
void CodeMap::DeleteCodeEntry(CodeEntry* entry) { void CodeMap::DeleteCodeEntry(CodeEntry* entry) {
......
...@@ -72,6 +72,11 @@ class CodeEntry { ...@@ -72,6 +72,11 @@ class CodeEntry {
CodeType code_type = CodeType::JS); CodeType code_type = CodeType::JS);
CodeEntry(const CodeEntry&) = delete; CodeEntry(const CodeEntry&) = delete;
CodeEntry& operator=(const CodeEntry&) = delete; CodeEntry& operator=(const CodeEntry&) = delete;
~CodeEntry() {
// No alive handles should be associated with the CodeEntry at time of
// destruction.
DCHECK(!heap_object_location_);
}
const char* name() const { return name_; } const char* name() const { return name_; }
const char* resource_name() const { return resource_name_; } const char* resource_name() const { return resource_name_; }
...@@ -116,6 +121,13 @@ class CodeEntry { ...@@ -116,6 +121,13 @@ class CodeEntry {
} }
} }
// Returns the start address of the instruction segment represented by this
// CodeEntry. Used as a key in the containing CodeMap.
Address instruction_start() const { return instruction_start_; }
void set_instruction_start(Address address) { instruction_start_ = address; }
Address** heap_object_location_address() { return &heap_object_location_; }
void FillFunctionInfo(SharedFunctionInfo shared); void FillFunctionInfo(SharedFunctionInfo shared);
void SetBuiltinId(Builtins::Name id); void SetBuiltinId(Builtins::Name id);
...@@ -214,6 +226,8 @@ class CodeEntry { ...@@ -214,6 +226,8 @@ class CodeEntry {
int position_; int position_;
std::unique_ptr<SourcePositionTable> line_info_; std::unique_ptr<SourcePositionTable> line_info_;
std::unique_ptr<RareData> rare_data_; std::unique_ptr<RareData> rare_data_;
Address instruction_start_ = kNullAddress;
Address* heap_object_location_ = nullptr;
}; };
struct CodeEntryAndLineNumber { struct CodeEntryAndLineNumber {
...@@ -420,9 +434,13 @@ class V8_EXPORT_PRIVATE CodeMap { ...@@ -420,9 +434,13 @@ class V8_EXPORT_PRIVATE CodeMap {
void AddCode(Address addr, CodeEntry* entry, unsigned size); void AddCode(Address addr, CodeEntry* entry, unsigned size);
void MoveCode(Address from, Address to); void MoveCode(Address from, Address to);
// Attempts to remove the given CodeEntry from the CodeMap.
// Returns true iff the entry was found and removed.
bool RemoveCode(CodeEntry*);
void ClearCodesInRange(Address start, Address end); void ClearCodesInRange(Address start, Address end);
CodeEntry* FindEntry(Address addr, Address* out_instruction_start = nullptr); CodeEntry* FindEntry(Address addr, Address* out_instruction_start = nullptr);
void Print(); void Print();
size_t size() const { return code_map_.size(); }
void Clear(); void Clear();
...@@ -434,7 +452,7 @@ class V8_EXPORT_PRIVATE CodeMap { ...@@ -434,7 +452,7 @@ class V8_EXPORT_PRIVATE CodeMap {
void DeleteCodeEntry(CodeEntry*); void DeleteCodeEntry(CodeEntry*);
std::map<Address, CodeEntryMapInfo> code_map_; std::multimap<Address, CodeEntryMapInfo> code_map_;
std::deque<CodeEntry*> used_entries_; // Entries that are no longer in the std::deque<CodeEntry*> used_entries_; // Entries that are no longer in the
// map, but used by a profile. // map, but used by a profile.
StringsStorage& function_and_resource_names_; StringsStorage& function_and_resource_names_;
......
...@@ -30,10 +30,12 @@ namespace internal { ...@@ -30,10 +30,12 @@ namespace internal {
ProfilerListener::ProfilerListener(Isolate* isolate, ProfilerListener::ProfilerListener(Isolate* isolate,
CodeEventObserver* observer, CodeEventObserver* observer,
StringsStorage& function_and_resource_names, StringsStorage& function_and_resource_names,
WeakCodeRegistry& weak_code_registry,
CpuProfilingNamingMode naming_mode) CpuProfilingNamingMode naming_mode)
: isolate_(isolate), : isolate_(isolate),
observer_(observer), observer_(observer),
function_and_resource_names_(function_and_resource_names), function_and_resource_names_(function_and_resource_names),
weak_code_registry_(weak_code_registry),
naming_mode_(naming_mode) {} naming_mode_(naming_mode) {}
ProfilerListener::~ProfilerListener() = default; ProfilerListener::~ProfilerListener() = default;
...@@ -48,6 +50,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag, ...@@ -48,6 +50,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag,
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, nullptr); CpuProfileNode::kNoColumnNumberInfo, nullptr);
rec->instruction_size = code->InstructionSize(); rec->instruction_size = code->InstructionSize();
weak_code_registry_.Track(rec->entry, code);
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
...@@ -61,6 +64,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag, ...@@ -61,6 +64,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag,
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, nullptr); CpuProfileNode::kNoColumnNumberInfo, nullptr);
rec->instruction_size = code->InstructionSize(); rec->instruction_size = code->InstructionSize();
weak_code_registry_.Track(rec->entry, code);
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
...@@ -78,6 +82,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag, ...@@ -78,6 +82,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag,
DCHECK(!code->IsCode()); DCHECK(!code->IsCode());
rec->entry->FillFunctionInfo(*shared); rec->entry->FillFunctionInfo(*shared);
rec->instruction_size = code->InstructionSize(); rec->instruction_size = code->InstructionSize();
weak_code_registry_.Track(rec->entry, code);
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
...@@ -220,6 +225,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag, ...@@ -220,6 +225,7 @@ void ProfilerListener::CodeCreateEvent(LogEventsAndTags tag,
rec->entry->FillFunctionInfo(*shared); rec->entry->FillFunctionInfo(*shared);
rec->instruction_size = abstract_code->InstructionSize(); rec->instruction_size = abstract_code->InstructionSize();
weak_code_registry_.Track(rec->entry, abstract_code);
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
...@@ -283,6 +289,7 @@ void ProfilerListener::RegExpCodeCreateEvent(Handle<AbstractCode> code, ...@@ -283,6 +289,7 @@ void ProfilerListener::RegExpCodeCreateEvent(Handle<AbstractCode> code,
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo, CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, nullptr); CpuProfileNode::kNoColumnNumberInfo, nullptr);
rec->instruction_size = code->InstructionSize(); rec->instruction_size = code->InstructionSize();
weak_code_registry_.Track(rec->entry, code);
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
...@@ -324,14 +331,16 @@ void ProfilerListener::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind, ...@@ -324,14 +331,16 @@ void ProfilerListener::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind,
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
void ProfilerListener::BytecodeFlushEvent(Address compiled_data_start) { void ProfilerListener::WeakCodeClearEvent() { weak_code_registry_.Sweep(this); }
CodeEventsContainer evt_rec(CodeEventRecord::BYTECODE_FLUSH);
BytecodeFlushEventRecord* rec = &evt_rec.BytecodeFlushEventRecord_;
rec->instruction_start = compiled_data_start + BytecodeArray::kHeaderSize;
void ProfilerListener::OnHeapObjectDeletion(CodeEntry* entry) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DELETE);
evt_rec.CodeDeleteEventRecord_.entry = entry;
DispatchCodeEvent(evt_rec); DispatchCodeEvent(evt_rec);
} }
void ProfilerListener::CodeSweepEvent() { weak_code_registry_.Sweep(this); }
const char* ProfilerListener::GetName(Vector<const char> name) { const char* ProfilerListener::GetName(Vector<const char> name) {
// TODO(all): Change {StringsStorage} to accept non-null-terminated strings. // TODO(all): Change {StringsStorage} to accept non-null-terminated strings.
OwnedVector<char> null_terminated = OwnedVector<char>::New(name.size() + 1); OwnedVector<char> null_terminated = OwnedVector<char>::New(name.size() + 1);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "include/v8-profiler.h" #include "include/v8-profiler.h"
#include "src/logging/code-events.h" #include "src/logging/code-events.h"
#include "src/profiler/profile-generator.h" #include "src/profiler/profile-generator.h"
#include "src/profiler/weak-code-registry.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -24,10 +25,12 @@ class CodeEventObserver { ...@@ -24,10 +25,12 @@ class CodeEventObserver {
virtual ~CodeEventObserver() = default; virtual ~CodeEventObserver() = default;
}; };
class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener { class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener,
public WeakCodeRegistry::Listener {
public: public:
ProfilerListener(Isolate*, CodeEventObserver*, ProfilerListener(Isolate*, CodeEventObserver*,
StringsStorage& function_and_resource_names, StringsStorage& function_and_resource_names,
WeakCodeRegistry& weak_code_registry,
CpuProfilingNamingMode mode = kDebugNaming); CpuProfilingNamingMode mode = kDebugNaming);
~ProfilerListener() override; ~ProfilerListener() override;
ProfilerListener(const ProfilerListener&) = delete; ProfilerListener(const ProfilerListener&) = delete;
...@@ -64,7 +67,12 @@ class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener { ...@@ -64,7 +67,12 @@ class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener {
void CodeDependencyChangeEvent(Handle<Code> code, void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> sfi, Handle<SharedFunctionInfo> sfi,
const char* reason) override {} const char* reason) override {}
void BytecodeFlushEvent(Address compiled_data_start) override; void WeakCodeClearEvent() override;
void OnHeapObjectDeletion(CodeEntry*) override;
// Invoked after a mark-sweep cycle.
void CodeSweepEvent();
const char* GetName(Name name) { const char* GetName(Name name) {
return function_and_resource_names_.GetName(name); return function_and_resource_names_.GetName(name);
...@@ -94,6 +102,7 @@ class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener { ...@@ -94,6 +102,7 @@ class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener {
Isolate* isolate_; Isolate* isolate_;
CodeEventObserver* observer_; CodeEventObserver* observer_;
StringsStorage& function_and_resource_names_; StringsStorage& function_and_resource_names_;
WeakCodeRegistry& weak_code_registry_;
const CpuProfilingNamingMode naming_mode_; const CpuProfilingNamingMode naming_mode_;
}; };
......
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/profiler/weak-code-registry.h"
#include "src/handles/global-handles.h"
#include "src/objects/instance-type-inl.h"
namespace v8 {
namespace internal {
namespace {
void Untrack(CodeEntry* entry) {
if (Address** heap_object_location_address =
entry->heap_object_location_address()) {
GlobalHandles::Destroy(*heap_object_location_address);
*heap_object_location_address = nullptr;
}
}
} // namespace
void WeakCodeRegistry::Track(CodeEntry* entry, Handle<AbstractCode> code) {
DCHECK(!*entry->heap_object_location_address());
DisallowGarbageCollection no_gc;
Handle<AbstractCode> handle = isolate_->global_handles()->Create(*code);
Address** heap_object_location_address =
entry->heap_object_location_address();
*heap_object_location_address = handle.location();
GlobalHandles::MakeWeak(heap_object_location_address);
entries_.push_back(entry);
}
void WeakCodeRegistry::Sweep(WeakCodeRegistry::Listener* listener) {
std::vector<CodeEntry*> alive_entries;
for (CodeEntry* entry : entries_) {
// Mark the CodeEntry as being deleted on the heap if the heap object
// location was nulled, indicating the object was freed.
if (!*entry->heap_object_location_address()) {
if (listener) {
listener->OnHeapObjectDeletion(entry);
}
} else {
alive_entries.push_back(entry);
}
}
entries_ = std::move(alive_entries);
}
void WeakCodeRegistry::Clear() {
for (CodeEntry* entry : entries_) {
Untrack(entry);
}
entries_.clear();
}
} // namespace internal
} // namespace v8
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_PROFILER_WEAK_CODE_REGISTRY_H_
#define V8_PROFILER_WEAK_CODE_REGISTRY_H_
#include <vector>
#include "src/execution/isolate.h"
#include "src/objects/objects.h"
#include "src/profiler/profile-generator.h"
namespace v8 {
namespace internal {
class V8_EXPORT_PRIVATE WeakCodeRegistry {
public:
struct Listener {
virtual void OnHeapObjectDeletion(CodeEntry* entry) = 0;
};
explicit WeakCodeRegistry(Isolate* isolate) : isolate_(isolate) {}
~WeakCodeRegistry() { Clear(); }
void Track(CodeEntry* entry, Handle<AbstractCode> code);
// Removes all dead code objects from the registry, invoking the provided
// listener for each new CodeEntry that is no longer referenced on the heap
// (if set).
void Sweep(Listener* listener);
// Removes all heap object tracking from stored CodeEntries.
void Clear();
private:
Isolate* const isolate_;
// Invariant: Entries will always be removed here before the CodeMap is
// destroyed. CodeEntries should not be freed while their heap objects exist.
std::vector<CodeEntry*> entries_;
};
} // namespace internal
} // namespace v8
#endif // V8_PROFILER_WEAK_CODE_REGISTRY_H_
...@@ -1265,7 +1265,7 @@ RUNTIME_FUNCTION(Runtime_EnableCodeLoggingForTesting) { ...@@ -1265,7 +1265,7 @@ RUNTIME_FUNCTION(Runtime_EnableCodeLoggingForTesting) {
void CodeDependencyChangeEvent(Handle<Code> code, void CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> shared, Handle<SharedFunctionInfo> shared,
const char* reason) final {} const char* reason) final {}
void BytecodeFlushEvent(Address compiled_data_start) final {} void WeakCodeClearEvent() final {}
bool is_listening_to_code_events() final { return true; } bool is_listening_to_code_events() final { return true; }
}; };
......
...@@ -178,7 +178,8 @@ TEST(CodeEvents) { ...@@ -178,7 +178,8 @@ TEST(CodeEvents) {
v8::base::TimeDelta::FromMicroseconds(100), true); v8::base::TimeDelta::FromMicroseconds(100), true);
CHECK(processor->Start()); CHECK(processor->Start());
ProfilerListener profiler_listener(isolate, processor, ProfilerListener profiler_listener(isolate, processor,
*code_observer.strings()); *code_observer.strings(),
*code_observer.weak_code_registry());
isolate->logger()->AddCodeEventListener(&profiler_listener); isolate->logger()->AddCodeEventListener(&profiler_listener);
// Enqueue code creation events. // Enqueue code creation events.
...@@ -243,7 +244,8 @@ TEST(TickEvents) { ...@@ -243,7 +244,8 @@ TEST(TickEvents) {
profiles->StartProfiling(""); profiles->StartProfiling("");
CHECK(processor->Start()); CHECK(processor->Start());
ProfilerListener profiler_listener(isolate, processor, ProfilerListener profiler_listener(isolate, processor,
*code_observer->strings()); *code_observer->strings(),
*code_observer->weak_code_registry());
isolate->logger()->AddCodeEventListener(&profiler_listener); isolate->logger()->AddCodeEventListener(&profiler_listener);
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb"); profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
...@@ -404,7 +406,8 @@ TEST(Issue1398) { ...@@ -404,7 +406,8 @@ TEST(Issue1398) {
profiles->StartProfiling(""); profiles->StartProfiling("");
CHECK(processor->Start()); CHECK(processor->Start());
ProfilerListener profiler_listener(isolate, processor, ProfilerListener profiler_listener(isolate, processor,
*code_observer->strings()); *code_observer->strings(),
*code_observer->weak_code_registry());
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb"); profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
...@@ -1272,7 +1275,8 @@ static void TickLines(bool optimize) { ...@@ -1272,7 +1275,8 @@ static void TickLines(bool optimize) {
isolate->logger()->LogCompiledFunctions(); isolate->logger()->LogCompiledFunctions();
CHECK(processor->Start()); CHECK(processor->Start());
ProfilerListener profiler_listener(isolate, processor, ProfilerListener profiler_listener(isolate, processor,
*code_observer->strings()); *code_observer->strings(),
*code_observer->weak_code_registry());
// Enqueue code creation events. // Enqueue code creation events.
i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name); i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
...@@ -4155,6 +4159,38 @@ TEST(BytecodeFlushEventsEagerLogging) { ...@@ -4155,6 +4159,38 @@ TEST(BytecodeFlushEventsEagerLogging) {
} }
} }
// Ensure that unused code entries are removed after GC with eager logging.
TEST(ClearUnusedWithEagerLogging) {
ManualGCScope manual_gc;
TestSetup test_setup;
LocalContext env;
i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate);
CpuProfiler profiler(isolate, kDebugNaming, kEagerLogging);
CodeMap* code_map = profiler.code_map_for_test();
size_t initial_size = code_map->size();
{
// Create and run a new script and function, generating 2 code objects.
i::HandleScope inner_scope(isolate);
CompileRun(
"function some_func() {}"
"some_func();");
CHECK_GT(code_map->size(), initial_size);
}
// Perform a few GCs, ensuring that the executed code's bytecode is flushed.
const int kAgingThreshold = 8;
for (int i = 0; i < kAgingThreshold; i++) {
CcTest::CollectAllGarbage();
}
// Verify that the CodeMap's size is unchanged post-GC.
CHECK_EQ(code_map->size(), initial_size);
}
} // namespace test_cpu_profiler } // namespace test_cpu_profiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -1202,103 +1202,3 @@ UNINITIALIZED_TEST(BuiltinsNotLoggedAsLazyCompile) { ...@@ -1202,103 +1202,3 @@ UNINITIALIZED_TEST(BuiltinsNotLoggedAsLazyCompile) {
} }
isolate->Dispose(); isolate->Dispose();
} }
TEST(BytecodeFlushEvents) {
SETUP_FLAGS();
#ifndef V8_LITE_MODE
i::FLAG_opt = false;
i::FLAG_always_opt = false;
i::FLAG_optimize_for_size = false;
#endif // V8_LITE_MODE
i::FLAG_flush_bytecode = true;
i::FLAG_allow_natives_syntax = true;
ManualGCScope manual_gc_scope;
v8::Isolate* isolate = CcTest::isolate();
i::Isolate* i_isolate = CcTest::i_isolate();
i::Factory* factory = i_isolate->factory();
struct FakeCodeEventLogger : public i::CodeEventLogger {
explicit FakeCodeEventLogger(i::Isolate* isolate)
: CodeEventLogger(isolate) {}
void CodeMoveEvent(i::AbstractCode from, i::AbstractCode to) override {}
void CodeDisableOptEvent(i::Handle<i::AbstractCode> code,
i::Handle<i::SharedFunctionInfo> shared) override {
}
void BytecodeFlushEvent(Address compiled_data_start) override {
// We only expect a single flush.
CHECK_EQ(flushed_compiled_data_start, i::kNullAddress);
flushed_compiled_data_start = compiled_data_start;
}
void LogRecordedBuffer(i::Handle<i::AbstractCode> code,
i::MaybeHandle<i::SharedFunctionInfo> maybe_shared,
const char* name, int length) override {}
#if V8_ENABLE_WEBASSEMBLY
void LogRecordedBuffer(const i::wasm::WasmCode* code, const char* name,
int length) override {}
#endif // V8_ENABLE_WEBASSEMBLY
i::Address flushed_compiled_data_start = i::kNullAddress;
};
FakeCodeEventLogger code_event_logger(i_isolate);
{
ScopedLoggerInitializer logger(isolate);
logger.logger()->AddCodeEventListener(&code_event_logger);
const char* source =
"function foo() {"
" var x = 42;"
" var y = 42;"
" var z = x + y;"
"};"
"foo()";
i::Handle<i::String> foo_name = factory->InternalizeUtf8String("foo");
// This compile will add the code to the compilation cache.
{
v8::HandleScope scope(isolate);
CompileRun(source);
}
// Check function is compiled.
i::Handle<i::Object> func_value =
i::Object::GetProperty(i_isolate, i_isolate->global_object(), foo_name)
.ToHandleChecked();
CHECK(func_value->IsJSFunction());
i::Handle<i::JSFunction> function =
i::Handle<i::JSFunction>::cast(func_value);
CHECK(function->shared().is_compiled());
// The code will survive at least two GCs.
CcTest::CollectAllGarbage();
CcTest::CollectAllGarbage();
CHECK(function->shared().is_compiled());
CHECK_EQ(code_event_logger.flushed_compiled_data_start, i::kNullAddress);
// Get the start address of the compiled data before flushing.
i::HeapObject compiled_data =
function->shared().GetBytecodeArray(i_isolate);
i::Address compiled_data_start = compiled_data.address();
// Simulate several GCs that use full marking.
const int kAgingThreshold = 6;
for (int i = 0; i < kAgingThreshold; i++) {
CcTest::CollectAllGarbage();
}
// foo should no longer be in the compilation cache
CHECK(!function->shared().is_compiled());
CHECK(!function->is_compiled());
// Verify that foo() was in fact flushed.
CHECK_EQ(code_event_logger.flushed_compiled_data_start,
compiled_data_start);
}
}
...@@ -352,10 +352,6 @@ TEST(CodeMapMoveAndDeleteCode) { ...@@ -352,10 +352,6 @@ TEST(CodeMapMoveAndDeleteCode) {
code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb. code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb.
CHECK(!code_map.FindEntry(ToAddress(0x1500))); CHECK(!code_map.FindEntry(ToAddress(0x1500)));
CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1700))); CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1700)));
CodeEntry* entry3 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
code_map.AddCode(ToAddress(0x1750), entry3, 0x100);
CHECK(!code_map.FindEntry(ToAddress(0x1700)));
CHECK_EQ(entry3, code_map.FindEntry(ToAddress(0x1750)));
} }
TEST(CodeMapClear) { TEST(CodeMapClear) {
...@@ -962,6 +958,63 @@ TEST(NodeSourceTypes) { ...@@ -962,6 +958,63 @@ TEST(NodeSourceTypes) {
CHECK_EQ(unresolved_node->source_type(), v8::CpuProfileNode::kUnresolved); CHECK_EQ(unresolved_node->source_type(), v8::CpuProfileNode::kUnresolved);
} }
TEST(CodeMapRemoveCode) {
StringsStorage strings;
CodeMap code_map(strings);
CodeEntry* entry = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
code_map.AddCode(ToAddress(0x1000), entry, 0x100);
CHECK(code_map.RemoveCode(entry));
CHECK(!code_map.FindEntry(ToAddress(0x1000)));
// Test that when two entries share the same address, we remove only the
// entry that we desired to.
CodeEntry* colliding_entry1 =
new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
CodeEntry* colliding_entry2 =
new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
code_map.AddCode(ToAddress(0x1000), colliding_entry1, 0x100);
code_map.AddCode(ToAddress(0x1000), colliding_entry2, 0x100);
CHECK(code_map.RemoveCode(colliding_entry1));
CHECK_EQ(code_map.FindEntry(ToAddress(0x1000)), colliding_entry2);
CHECK(code_map.RemoveCode(colliding_entry2));
CHECK(!code_map.FindEntry(ToAddress(0x1000)));
}
TEST(CodeMapMoveOverlappingCode) {
StringsStorage strings;
CodeMap code_map(strings);
CodeEntry* colliding_entry1 =
new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
CodeEntry* colliding_entry2 =
new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "bbb");
CodeEntry* after_entry =
new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
code_map.AddCode(ToAddress(0x1400), colliding_entry1, 0x200);
code_map.AddCode(ToAddress(0x1400), colliding_entry2, 0x200);
code_map.AddCode(ToAddress(0x1800), after_entry, 0x200);
CHECK_EQ(colliding_entry1->instruction_start(), ToAddress(0x1400));
CHECK_EQ(colliding_entry2->instruction_start(), ToAddress(0x1400));
CHECK_EQ(after_entry->instruction_start(), ToAddress(0x1800));
CHECK(code_map.FindEntry(ToAddress(0x1400)));
CHECK_EQ(code_map.FindEntry(ToAddress(0x1800)), after_entry);
code_map.MoveCode(ToAddress(0x1400), ToAddress(0x1600));
CHECK(!code_map.FindEntry(ToAddress(0x1400)));
CHECK(code_map.FindEntry(ToAddress(0x1600)));
CHECK_EQ(code_map.FindEntry(ToAddress(0x1800)), after_entry);
CHECK_EQ(colliding_entry1->instruction_start(), ToAddress(0x1600));
CHECK_EQ(colliding_entry2->instruction_start(), ToAddress(0x1600));
CHECK_EQ(after_entry->instruction_start(), ToAddress(0x1800));
}
} // namespace test_profile_generator } // namespace test_profile_generator
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment