CPUProfiler: Push deopt reason further to ProfileNode.

1) create beefy RelocInfo table when cpu profiler is active, so if a function
was optimized when profiler was active RelocInfo would get separate DeoptInfo
for the each deopt case.

2) push DeoptInfo from CodeEntry to ProfileNode.
When deopt happens we put the info collected on #1 into CodeEntry and record stack sample.
On the sampling thread we grab the deopt data and append it to the corresponding ProfileNode deopts list.

Sample profile dump.
[Top down]:
    0  (root) 0 #1
    1     29 #2
    5      test 29 #3
    3        opt_function 29 #4
                 deopted at 52 with reason 'not a heap number'
                 deopted at 71 with reason 'division by zero'

BUG=452067
LOG=n

Review URL: https://codereview.chromium.org/919953002

Cr-Commit-Position: refs/heads/master@{#26615}
This commit is contained in:
loislo 2015-02-12 05:24:59 -08:00 committed by Commit bot
parent b79b985988
commit ce8701b247
22 changed files with 102 additions and 50 deletions

View File

@ -3392,7 +3392,7 @@ void Assembler::RecordComment(const char* msg) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -907,7 +907,8 @@ void LCodeGen::DeoptimizeIf(Condition condition, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -3081,7 +3081,7 @@ void Assembler::RecordComment(const char* msg) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -1071,7 +1071,8 @@ void LCodeGen::DeoptimizeBranch(
entry, deopt_info, bailout_type, !frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry->IsEquivalentTo(*jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -38,10 +38,7 @@ void CodeDisableOptEventRecord::UpdateCodeMap(CodeMap* code_map) {
void CodeDeoptEventRecord::UpdateCodeMap(CodeMap* code_map) {
CodeEntry* entry = code_map->FindEntry(start);
if (entry != NULL) {
entry->set_deopt_reason(deopt_reason);
entry->set_deopt_location(raw_position);
}
if (entry != NULL) entry->set_deopt_info(deopt_reason, raw_position);
}

View File

@ -188,14 +188,6 @@ class Deoptimizer : public Malloced {
DeoptInfo(int r, const char* m, DeoptReason d)
: raw_position(r), mnemonic(m), deopt_reason(d) {}
bool operator==(const DeoptInfo& other) const {
return raw_position == other.raw_position &&
CStringEquals(mnemonic, other.mnemonic) &&
deopt_reason == other.deopt_reason;
}
bool operator!=(const DeoptInfo& other) const { return !(*this == other); }
int raw_position;
const char* mnemonic;
DeoptReason deopt_reason;
@ -214,8 +206,7 @@ class Deoptimizer : public Malloced {
bool IsEquivalentTo(const JumpTableEntry& other) const {
return address == other.address && bailout_type == other.bailout_type &&
needs_frame == other.needs_frame &&
(!FLAG_trace_deopt || deopt_info == other.deopt_info);
needs_frame == other.needs_frame;
}
Label label;

View File

@ -2652,7 +2652,7 @@ void Assembler::RecordComment(const char* msg, bool force) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -872,7 +872,8 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -2355,7 +2355,7 @@ void Assembler::RecordComment(const char* msg) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -870,7 +870,8 @@ void LCodeGen::DeoptimizeIf(Condition condition, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -2582,7 +2582,7 @@ void Assembler::RecordComment(const char* msg) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -820,7 +820,8 @@ void LCodeGen::DeoptimizeIf(Condition condition, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -2196,7 +2196,7 @@ void Assembler::RecordComment(const char* msg) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -825,7 +825,8 @@ void LCodeGen::DeoptimizeIf(Condition cond, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -25,7 +25,7 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag, const char* name,
script_id_(v8::UnboundScript::kNoScriptId),
no_frame_ranges_(NULL),
bailout_reason_(kEmptyBailoutReason),
deopt_reason_(kEmptyBailoutReason),
deopt_reason_(kNoDeoptReason),
deopt_location_(0),
line_info_(line_info),
instruction_start_(instruction_start) {}

View File

@ -8,6 +8,7 @@
#include "src/compiler.h"
#include "src/debug.h"
#include "src/deoptimizer.h"
#include "src/global-handles.h"
#include "src/sampler.h"
#include "src/scopeinfo.h"
@ -158,7 +159,9 @@ int JITLineInfoTable::GetSourceLineNumber(int pc_offset) const {
const char* const CodeEntry::kEmptyNamePrefix = "";
const char* const CodeEntry::kEmptyResourceName = "";
const char* const CodeEntry::kEmptyBailoutReason = "";
const char* const CodeEntry::kEmptyBailoutReason =
GetBailoutReason(BailoutReason::kNoReason);
const char* const CodeEntry::kNoDeoptReason = "";
CodeEntry::~CodeEntry() {
@ -212,6 +215,12 @@ int CodeEntry::GetSourceLine(int pc_offset) const {
}
void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
deopt_infos_.Add(DeoptInfo(entry->deopt_reason(), entry->deopt_location()));
entry->clear_deopt_info();
}
ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
HashMap::Entry* map_entry =
children_.Lookup(entry, CodeEntryHash(entry), false);
@ -223,13 +232,15 @@ ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
HashMap::Entry* map_entry =
children_.Lookup(entry, CodeEntryHash(entry), true);
if (map_entry->value == NULL) {
ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value);
if (node == NULL) {
// New node added.
ProfileNode* new_node = new ProfileNode(tree_, entry);
map_entry->value = new_node;
children_list_.Add(new_node);
node = new ProfileNode(tree_, entry);
map_entry->value = node;
children_list_.Add(node);
}
return reinterpret_cast<ProfileNode*>(map_entry->value);
if (entry->has_deopt_info()) node->CollectDeoptInfo(entry);
return node;
}
@ -268,12 +279,21 @@ bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
void ProfileNode::Print(int indent) {
base::OS::Print("%5u %*s %s%s %d #%d %s", self_ticks_, indent, "",
base::OS::Print("%5u %*s %s%s %d #%d", self_ticks_, indent, "",
entry_->name_prefix(), entry_->name(), entry_->script_id(),
id(), entry_->bailout_reason());
id());
if (entry_->resource_name()[0] != '\0')
base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
base::OS::Print("\n");
for (auto info : deopt_infos_) {
base::OS::Print("%*s deopted at %d with reason '%s'\n", indent + 10, "",
info.deopt_location, info.deopt_reason);
}
const char* bailout_reason = entry_->bailout_reason();
if (bailout_reason != GetBailoutReason(BailoutReason::kNoReason)) {
base::OS::Print("%*s bailed out due to '%s'\n", indent + 10, "",
bailout_reason);
}
for (HashMap::Entry* p = children_.Start();
p != NULL;
p = children_.Next(p)) {

View File

@ -90,12 +90,22 @@ class CodeEntry {
void set_bailout_reason(const char* bailout_reason) {
bailout_reason_ = bailout_reason;
}
void set_deopt_reason(const char* deopt_reason) {
deopt_reason_ = deopt_reason;
}
void set_deopt_location(int location) { deopt_location_ = location; }
const char* bailout_reason() const { return bailout_reason_; }
void set_deopt_info(const char* deopt_reason, int location) {
DCHECK(deopt_reason_ == kNoDeoptReason);
DCHECK(!deopt_location_);
deopt_reason_ = deopt_reason;
deopt_location_ = location;
}
const char* deopt_reason() const { return deopt_reason_; }
int deopt_location() const { return deopt_location_; }
bool has_deopt_info() const { return deopt_reason_ != kNoDeoptReason; }
void clear_deopt_info() {
deopt_reason_ = kNoDeoptReason;
deopt_location_ = 0;
}
static inline bool is_js_function_tag(Logger::LogEventsAndTags tag);
List<OffsetRange>* no_frame_ranges() const { return no_frame_ranges_; }
@ -118,6 +128,7 @@ class CodeEntry {
static const char* const kEmptyNamePrefix;
static const char* const kEmptyResourceName;
static const char* const kEmptyBailoutReason;
static const char* const kNoDeoptReason;
private:
class TagField : public BitField<Logger::LogEventsAndTags, 0, 8> {};
@ -146,6 +157,17 @@ class CodeEntry {
class ProfileTree;
class ProfileNode {
private:
struct DeoptInfo {
DeoptInfo(const char* deopt_reason, int deopt_location)
: deopt_reason(deopt_reason), deopt_location(deopt_location) {}
DeoptInfo(const DeoptInfo& info)
: deopt_reason(info.deopt_reason),
deopt_location(info.deopt_location) {}
const char* deopt_reason;
int deopt_location;
};
public:
inline ProfileNode(ProfileTree* tree, CodeEntry* entry);
@ -162,6 +184,8 @@ class ProfileNode {
unsigned int GetHitLineCount() const { return line_ticks_.occupancy(); }
bool GetLineTicks(v8::CpuProfileNode::LineTick* entries,
unsigned int length) const;
void CollectDeoptInfo(CodeEntry* entry);
const List<DeoptInfo>& deopt_infos() const { return deopt_infos_; }
void Print(int indent);
@ -186,6 +210,7 @@ class ProfileNode {
unsigned id_;
HashMap line_ticks_;
List<DeoptInfo> deopt_infos_;
DISALLOW_COPY_AND_ASSIGN(ProfileNode);
};

View File

@ -3451,7 +3451,7 @@ void Assembler::RecordComment(const char* msg, bool force) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -782,7 +782,8 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -1921,7 +1921,7 @@ void Assembler::RecordComment(const char* msg, bool force) {
void Assembler::RecordDeoptReason(const int reason, const int raw_position) {
if (FLAG_trace_deopt) {
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling()) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, raw_position);
RecordRelocInfo(RelocInfo::DEOPT_REASON, reason);

View File

@ -1154,7 +1154,8 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr,
!frame_is_built_);
// We often have several deopts to the same entry, reuse the last
// jump entry if this is the case.
if (jump_table_.is_empty() ||
if (FLAG_trace_deopt || isolate()->cpu_profiler()->is_profiling() ||
jump_table_.is_empty() ||
!table_entry.IsEquivalentTo(jump_table_.last())) {
jump_table_.Add(table_entry, zone());
}

View File

@ -1720,23 +1720,31 @@ TEST(DontStopOnFinishedProfileDelete) {
static const char* collect_deopt_events_test_source =
"function opt_function(value) {\n"
" return value / 10;\n"
"function opt_function(left, right) {\n"
" var k = left / 10;\n"
" var r = 10 / right;\n"
" return k + r;"
"}\n"
"\n"
"function test(value) {\n"
" return opt_function(value);\n"
"function test(left, right) {\n"
" return opt_function(left, right);\n"
"}\n"
"\n"
"startProfiling();\n"
"\n"
"for (var i = 0; i < 10; ++i) test(10);\n"
"test(10, 10);\n"
"\n"
"%OptimizeFunctionOnNextCall(opt_function)\n"
"\n"
"for (var i = 0; i < 10; ++i) test(10);\n"
"test(10, 10);\n"
"\n"
"for (var i = 0; i < 10; ++i) test(undefined);\n"
"test(undefined, 10);\n"
"\n"
"%OptimizeFunctionOnNextCall(opt_function)\n"
"\n"
"test(10, 10);\n"
"\n"
"test(10, 0);\n"
"\n"
"stopProfiling();\n"
"\n";
@ -1760,5 +1768,8 @@ TEST(CollectDeoptEvents) {
const v8::CpuProfileNode* opt_function = GetSimpleBranch(
env->GetIsolate(), profile->GetTopDownRoot(), branch, arraysize(branch));
CHECK(opt_function);
const i::ProfileNode* iopt_function =
reinterpret_cast<const i::ProfileNode*>(opt_function);
CHECK_EQ(2, iopt_function->deopt_infos().length());
iprofiler->DeleteProfile(iprofile);
}