[cpu-profiler] Only store deopt inline frames for functions that need it

We store deopt inline frames for all functions when we receive the code
creation event. We only ever use this information for code which is
deoptimized. Given that we receive code deopt events, we can just store
this information when the code is deoptimized.

At the time of the code deopt event, we also know the associated
deopt_id. That means we don't need to store a map of deopt_ids to
vectors of frames, because we will only ever access the frames for the
deopt_id that is already set.

This means we store way less data, particularly for long-running
processes which see fewer deopts. This saves 10MiB peak memory on the
node server example.

Bug: v8:7719
Change-Id: If6cf5ec413848e4c9f3c1e2106366ae2adae6fb1
Reviewed-on: https://chromium-review.googlesource.com/1050289
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Alexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53330}
This commit is contained in:
Peter Marshall 2018-05-24 13:09:16 +02:00 committed by Commit Bot
parent 170418b212
commit 0bfcbdd472
6 changed files with 47 additions and 40 deletions

View File

@ -35,7 +35,11 @@ void CodeDisableOptEventRecord::UpdateCodeMap(CodeMap* code_map) {
void CodeDeoptEventRecord::UpdateCodeMap(CodeMap* code_map) {
CodeEntry* entry = code_map->FindEntry(start);
if (entry != nullptr) entry->set_deopt_info(deopt_reason, deopt_id);
if (entry == nullptr) return;
std::vector<CpuProfileDeoptFrame> frames_vector(
deopt_frames, deopt_frames + deopt_frame_count);
entry->set_deopt_info(deopt_reason, deopt_id, std::move(frames_vector));
delete[] deopt_frames;
}

View File

@ -86,6 +86,8 @@ class CodeDeoptEventRecord : public CodeEventRecord {
int deopt_id;
Address pc;
int fp_to_sp_delta;
CpuProfileDeoptFrame* deopt_frames;
int deopt_frame_count;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};

View File

@ -132,15 +132,14 @@ const std::vector<std::unique_ptr<CodeEntry>>* CodeEntry::GetInlineStack(
return it != rare_data_->inline_locations_.end() ? &it->second : nullptr;
}
void CodeEntry::AddDeoptInlinedFrames(
int deopt_id, std::vector<CpuProfileDeoptFrame> inlined_frames) {
EnsureRareData()->deopt_inlined_frames_.insert(
std::make_pair(deopt_id, std::move(inlined_frames)));
}
bool CodeEntry::HasDeoptInlinedFramesFor(int deopt_id) const {
return rare_data_ && rare_data_->deopt_inlined_frames_.find(deopt_id) !=
rare_data_->deopt_inlined_frames_.end();
void CodeEntry::set_deopt_info(
const char* deopt_reason, int deopt_id,
std::vector<CpuProfileDeoptFrame> inlined_frames) {
DCHECK(!has_deopt_info());
RareData* rare_data = EnsureRareData();
rare_data->deopt_reason_ = deopt_reason;
rare_data->deopt_id_ = deopt_id;
rare_data->deopt_inlined_frames_ = std::move(inlined_frames);
}
void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
@ -159,12 +158,11 @@ CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
CpuProfileDeoptInfo info;
info.deopt_reason = rare_data_->deopt_reason_;
DCHECK_NE(kNoDeoptimizationId, rare_data_->deopt_id_);
if (rare_data_->deopt_inlined_frames_.find(rare_data_->deopt_id_) ==
rare_data_->deopt_inlined_frames_.end()) {
if (rare_data_->deopt_inlined_frames_.empty()) {
info.stack.push_back(CpuProfileDeoptFrame(
{script_id_, static_cast<size_t>(std::max(0, position()))}));
} else {
info.stack = rare_data_->deopt_inlined_frames_[rare_data_->deopt_id_];
info.stack = rare_data_->deopt_inlined_frames_;
}
return info;
}

View File

@ -74,12 +74,9 @@ class CodeEntry {
return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason;
}
void set_deopt_info(const char* deopt_reason, int deopt_id) {
DCHECK(!has_deopt_info());
RareData* rare_data = EnsureRareData();
rare_data->deopt_reason_ = deopt_reason;
rare_data->deopt_id_ = deopt_id;
}
void set_deopt_info(const char* deopt_reason, int deopt_id,
std::vector<CpuProfileDeoptFrame> inlined_frames);
CpuProfileDeoptInfo GetDeoptInfo();
bool has_deopt_info() const {
return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
@ -110,9 +107,6 @@ class CodeEntry {
const std::vector<std::unique_ptr<CodeEntry>>* GetInlineStack(
int pc_offset) const;
void AddDeoptInlinedFrames(int deopt_id, std::vector<CpuProfileDeoptFrame>);
bool HasDeoptInlinedFramesFor(int deopt_id) const;
Address instruction_start() const { return instruction_start_; }
CodeEventListener::LogEventsAndTags tag() const {
return TagField::decode(bit_field_);
@ -146,8 +140,7 @@ class CodeEntry {
int deopt_id_ = kNoDeoptimizationId;
std::unordered_map<int, std::vector<std::unique_ptr<CodeEntry>>>
inline_locations_;
std::unordered_map<int, std::vector<CpuProfileDeoptFrame>>
deopt_inlined_frames_;
std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_;
};
RareData* EnsureRareData();

View File

@ -106,7 +106,6 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
GetName(InferScriptName(script_name, shared)), line, column,
std::move(line_table), abstract_code->InstructionStart());
RecordInliningInfo(rec->entry, abstract_code);
RecordDeoptInlinedFrames(rec->entry, abstract_code);
rec->entry->FillFunctionInfo(shared);
rec->size = abstract_code->ExecutableSize();
DispatchCodeEvent(evt_rec);
@ -153,6 +152,10 @@ void ProfilerListener::CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
rec->deopt_id = info.deopt_id;
rec->pc = pc;
rec->fp_to_sp_delta = fp_to_sp_delta;
// When a function is deoptimized, we store the deoptimized frame information
// for the use of GetDeoptInfos().
AttachDeoptInlinedFrames(code, rec);
DispatchCodeEvent(evt_rec);
}
@ -250,16 +253,18 @@ void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
}
}
void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
AbstractCode* abstract_code) {
if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
Handle<Code> code(abstract_code->GetCode());
void ProfilerListener::AttachDeoptInlinedFrames(Code* code,
CodeDeoptEventRecord* rec) {
int deopt_id = rec->deopt_id;
SourcePosition last_position = SourcePosition::Unknown();
int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
for (RelocIterator it(*code, mask); !it.done(); it.next()) {
rec->deopt_frames = nullptr;
rec->deopt_frame_count = 0;
for (RelocIterator it(code, mask); !it.done(); it.next()) {
RelocInfo* info = it.rinfo();
if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
int script_offset = static_cast<int>(info->data());
@ -270,25 +275,29 @@ void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
continue;
}
if (info->rmode() == RelocInfo::DEOPT_ID) {
int deopt_id = static_cast<int>(info->data());
if (deopt_id != static_cast<int>(info->data())) continue;
DCHECK(last_position.IsKnown());
std::vector<CpuProfileDeoptFrame> inlined_frames;
// SourcePosition::InliningStack allocates a handle for the SFI of each
// frame. These don't escape this function, but quickly add up. This
// scope limits their lifetime.
HandleScope scope(isolate_);
for (SourcePositionInfo& pos_info : last_position.InliningStack(code)) {
std::vector<SourcePositionInfo> stack =
last_position.InliningStack(handle(code));
CpuProfileDeoptFrame* deopt_frames =
new CpuProfileDeoptFrame[stack.size()];
int deopt_frame_count = 0;
for (SourcePositionInfo& pos_info : stack) {
if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
if (pos_info.script.is_null()) continue;
int script_id = pos_info.script->id();
size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
inlined_frames.push_back(CpuProfileDeoptFrame({script_id, offset}));
}
if (!inlined_frames.empty() &&
!entry->HasDeoptInlinedFramesFor(deopt_id)) {
entry->AddDeoptInlinedFrames(deopt_id, std::move(inlined_frames));
deopt_frames[deopt_frame_count++] = {script_id, offset};
}
rec->deopt_frames = deopt_frames;
rec->deopt_frame_count = deopt_frame_count;
break;
}
}
}

View File

@ -15,6 +15,7 @@ namespace v8 {
namespace internal {
class CodeEventsContainer;
class CodeDeoptEventRecord;
class CodeEventObserver {
public:
@ -76,7 +77,7 @@ class ProfilerListener : public CodeEventListener {
private:
void RecordInliningInfo(CodeEntry* entry, AbstractCode* abstract_code);
void RecordDeoptInlinedFrames(CodeEntry* entry, AbstractCode* abstract_code);
void AttachDeoptInlinedFrames(Code* code, CodeDeoptEventRecord* rec);
Name* InferScriptName(Name* name, SharedFunctionInfo* info);
V8_INLINE void DispatchCodeEvent(const CodeEventsContainer& evt_rec) {
observer_->CodeEventHandler(evt_rec);