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:
parent
b79b985988
commit
ce8701b247
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user