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);
!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());
}
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);
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());
}
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);
}
DEOPT_MESSAGES_LIST(DEOPT_MESSAGES_CONSTANTS) kLastDeoptReason
};
#undef DEOPT_MESSAGES_CONSTANTS
-
static const char* GetDeoptReason(DeoptReason deopt_reason);
struct DeoptInfo {
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;
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;
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);
!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());
}
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);
!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());
}
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);
!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());
}
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);
!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());
}
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) {}
#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"
const char* const CodeEntry::kEmptyNamePrefix = "";
const char* const CodeEntry::kEmptyResourceName = "";
const char* const CodeEntry::kEmptyBailoutReason = "";
+const char* const CodeEntry::kNoDeoptReason = "";
CodeEntry::~CodeEntry() {
}
+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);
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);
+ return node;
}
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) &&
+ bailout_reason != CodeEntry::kEmptyBailoutReason) {
+ 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)) {
ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
int src_line) {
ProfileNode* node = root_;
+ CodeEntry* last_entry = NULL;
for (CodeEntry** entry = path.start() + path.length() - 1;
entry != path.start() - 1;
--entry) {
if (*entry != NULL) {
node = node->FindOrAddChild(*entry);
+ last_entry = *entry;
}
}
+ if (last_entry && last_entry->has_deopt_info()) {
+ node->CollectDeoptInfo(last_entry);
+ }
node->IncrementSelfTicks();
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
void set_bailout_reason(const char* bailout_reason) {
bailout_reason_ = bailout_reason;
}
- void set_deopt_reason(const char* deopt_reason) {
+ const char* bailout_reason() const { return bailout_reason_; }
+
+ void set_deopt_info(const char* deopt_reason, int location) {
+ 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_location_; }
+ void clear_deopt_info() {
+ deopt_reason_ = kNoDeoptReason;
+ deopt_location_ = 0;
}
- void set_deopt_location(int location) { deopt_location_ = location; }
- const char* bailout_reason() const { return bailout_reason_; }
static inline bool is_js_function_tag(Logger::LogEventsAndTags tag);
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> {};
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);
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);
unsigned id_;
HashMap line_ticks_;
+ List<DeoptInfo> deopt_infos_;
DISALLOW_COPY_AND_ASSIGN(ProfileNode);
};
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);
!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());
}
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);
!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());
}
#include "include/v8-profiler.h"
#include "src/base/platform/platform.h"
#include "src/cpu-profiler-inl.h"
+#include "src/deoptimizer.h"
#include "src/smart-pointers.h"
#include "src/utils.h"
#include "test/cctest/cctest.h"
static const char* collect_deopt_events_test_source =
- "function opt_function(value) {\n"
- " return value / 10;\n"
+ "function opt_function(left, right, depth) {\n"
+ " if (depth) return opt_function(left, right, depth - 1);\n"
+ "\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, 1);\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"
+ "test(10, 10);\n"
+ "\n"
+ "test(undefined, 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(10, 0);\n"
"\n"
"stopProfiling();\n"
"\n";
i::CpuProfile* iprofile = iprofiler->GetProfile(0);
iprofile->Print();
v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
- const char* branch[] = {"", "test", "opt_function"};
+ const char* branch[] = {"", "test", "opt_function", "opt_function"};
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());
+ CHECK_EQ(i::Deoptimizer::GetDeoptReason(i::Deoptimizer::kNotAHeapNumber),
+ iopt_function->deopt_infos()[0].deopt_reason);
+ CHECK_EQ(i::Deoptimizer::GetDeoptReason(i::Deoptimizer::kDivisionByZero),
+ iopt_function->deopt_infos()[1].deopt_reason);
iprofiler->DeleteProfile(iprofile);
}