// Adjust FP to point to saved FP.
__ add(fp, sp, Operand(2 * kPointerSize));
}
+ info->AddNoFrameRange(0, masm_->pc_offset());
{ Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots();
PredictableCodeSizeScope predictable(masm_, -1);
__ RecordJSReturn();
masm_->mov(sp, fp);
+ int no_frame_start = masm_->pc_offset();
masm_->ldm(ia_w, sp, fp.bit() | lr.bit());
masm_->add(sp, sp, Operand(sp_delta));
masm_->Jump(lr);
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
#ifdef DEBUG
count++;
}
}
+ int no_frame_start = -1;
if (NeedsEagerFrame()) {
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
+ no_frame_start = masm_->pc_offset();
}
if (instr->has_constant_parameter_count()) {
int parameter_count = ToInteger32(instr->constant_parameter_count());
}
__ Jump(lr);
+
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
}
code_stub_ = NULL;
prologue_offset_ = kPrologueOffsetNotSet;
opt_count_ = shared_info().is_null() ? 0 : shared_info()->opt_count();
+ no_frame_ranges_ = isolate->cpu_profiler()->is_profiling()
+ ? new List<OffsetRange>(2) : NULL;
if (mode == STUB) {
mode_ = STUB;
return;
CompilationInfo::~CompilationInfo() {
delete deferred_handles_;
+ delete no_frame_ranges_;
}
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(),
*result,
+ info,
String::cast(script->name())));
GDBJIT(AddCode(Handle<String>(String::cast(script->name())),
script,
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(),
*result,
+ info,
isolate->heap()->empty_string()));
GDBJIT(AddCode(Handle<String>(), script, info->code(), info));
}
// reset this bit when lazy compiling the code again.
if (shared->optimization_disabled()) code->set_optimizable(false);
+ if (shared->code() == *code) {
+ // Do not send compilation event for the same code twice.
+ return;
+ }
Compiler::RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared);
}
CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code,
*shared,
+ info,
String::cast(script->name()),
line_num));
} else {
CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code,
*shared,
+ info,
shared->DebugName()));
}
}
ONLY_SINGLE_FUNCTION_LITERAL // Only a single FunctionLiteral expression.
};
+struct OffsetRange {
+ OffsetRange(int from, int to) : from(from), to(to) {}
+ int from;
+ int to;
+};
+
// CompilationInfo encapsulates some information known at compile time. It
// is constructed based on the resources available at compile-time.
class CompilationInfo {
prologue_offset_ = prologue_offset;
}
+ // Adds offset range [from, to) where fp register does not point
+ // to the current frame base. Used in CPU profiler to detect stack
+ // samples where top frame is not set up.
+ inline void AddNoFrameRange(int from, int to) {
+ if (no_frame_ranges_) no_frame_ranges_->Add(OffsetRange(from, to));
+ }
+
+ List<OffsetRange>* ReleaseNoFrameRanges() {
+ List<OffsetRange>* result = no_frame_ranges_;
+ no_frame_ranges_ = NULL;
+ return result;
+ }
+
+
private:
Isolate* isolate_;
int prologue_offset_;
+ List<OffsetRange>* no_frame_ranges_;
+
// A copy of shared_info()->opt_count() to avoid handle deref
// during graph optimization.
int opt_count_;
#include "cpu-profiler-inl.h"
+#include "compiler.h"
#include "frames-inl.h"
#include "hashmap.h"
#include "log-inl.h"
int line_number,
Address start,
unsigned size,
- Address shared) {
+ Address shared,
+ CompilationInfo* info) {
if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->order = ++enqueue_order_;
rec->start = start;
rec->entry = profiles_->NewCodeEntry(tag, name, resource_name, line_number);
+ if (info) {
+ rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
+ }
rec->size = size;
rec->shared = shared;
events_buffer_.Enqueue(evt_rec);
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize(),
+ NULL,
NULL);
}
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
Name* name) {
processor_->CodeCreateEvent(
tag,
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize(),
- shared->address());
+ shared->address(),
+ info);
}
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
String* source, int line) {
processor_->CodeCreateEvent(
tag,
line,
code->address(),
code->ExecutableSize(),
- shared->address());
+ shared->address(),
+ info);
}
// Forward declarations.
class CodeEntry;
class CodeMap;
+class CompilationInfo;
class CpuProfile;
class CpuProfilesCollection;
class ProfileGenerator;
Name* name,
String* resource_name, int line_number,
Address start, unsigned size,
- Address shared);
+ Address shared,
+ CompilationInfo* info);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name,
Address start, unsigned size);
Code* code, Name* name);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
- SharedFunctionInfo* shared,
- Name* name);
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
String* source, int line);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, int args_count);
__ mov(ebp, esp);
__ push(esi); // Callee's context.
__ push(edi); // Callee's JS Function.
+ info->AddNoFrameRange(0, masm_->pc_offset());
{ Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots();
// Do not use the leave instruction here because it is too short to
// patch with the code required by the debugger.
__ mov(esp, ebp);
+ int no_frame_start = masm_->pc_offset();
__ pop(ebp);
int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
ASSERT(Assembler::kJSReturnSequenceLength <=
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
frame_is_built_ = true;
__ push(ebp); // Caller's frame pointer.
__ mov(ebp, esp);
+ info()->AddNoFrameRange(0, masm_->pc_offset());
__ push(esi); // Callee's context.
if (info()->IsStub()) {
__ push(Immediate(Smi::FromInt(StackFrame::STUB)));
__ mov(edx, Operand(ebp,
JavaScriptFrameConstants::kDynamicAlignmentStateOffset));
}
+ int no_frame_start = -1;
if (NeedsEagerFrame()) {
__ mov(esp, ebp);
__ pop(ebp);
+ no_frame_start = masm_->pc_offset();
}
if (dynamic_frame_alignment_) {
Label no_padding;
}
EmitReturn(instr, false);
+ if (no_frame_start != -1) {
+ info()->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
}
void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
Name* name) {
if (!is_logging_code_events()) return;
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
Name* source, int line) {
if (!is_logging_code_events()) return;
if (FLAG_ll_prof || Serializer::enabled() || code_event_handler_ != NULL) {
PROFILE(isolate_,
CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code, *shared,
+ *code, *shared, NULL,
*script_name, line_num + 1));
} else {
// Can't distinguish eval and script here, so always use Script.
PROFILE(isolate_,
CodeCreateEvent(
Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
- *code, *shared, *script_name));
+ *code, *shared, NULL, *script_name));
}
} else {
PROFILE(isolate_,
CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code, *shared, *func_name));
+ *code, *shared, NULL, *func_name));
}
} else if (shared->IsApiFunction()) {
// API function.
} else {
PROFILE(isolate_,
CodeCreateEvent(
- Logger::LAZY_COMPILE_TAG, *code, *shared, *func_name));
+ Logger::LAZY_COMPILE_TAG, *code, *shared, NULL, *func_name));
}
}
class Isolate;
class PositionsRecorder;
class CpuProfiler;
+class CompilationInfo;
#undef LOG
#define LOG(isolate, Call) \
void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
Name* name);
void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
+ CompilationInfo* info,
Name* source, int line);
void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count);
void CodeMovingGCEvent();
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
// Adjust fp to point to caller's fp.
__ Addu(fp, sp, Operand(2 * kPointerSize));
+ info->AddNoFrameRange(0, masm_->pc_offset());
{ Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots();
CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
__ RecordJSReturn();
masm_->mov(sp, fp);
+ int no_frame_start = masm_->pc_offset();
masm_->MultiPop(static_cast<RegList>(fp.bit() | ra.bit()));
masm_->Addu(sp, sp, Operand(sp_delta));
masm_->Jump(ra);
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
#ifdef DEBUG
__ Addu(fp, sp, Operand(2 * kPointerSize));
}
frame_is_built_ = true;
+ info_->AddNoFrameRange(0, masm_->pc_offset());
}
// Reserve space for the stack slots needed by the code.
count++;
}
}
+ int no_frame_start = -1;
if (NeedsEagerFrame()) {
__ mov(sp, fp);
__ Pop(ra, fp);
+ no_frame_start = masm_->pc_offset();
}
if (instr->has_constant_parameter_count()) {
int parameter_count = ToInteger32(instr->constant_parameter_count());
}
__ Jump(ra);
+
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
}
resource_name_(resource_name),
line_number_(line_number),
shared_id_(0),
- security_token_id_(security_token_id) {
+ security_token_id_(security_token_id),
+ no_frame_ranges_(NULL) {
}
#include "profile-generator-inl.h"
+#include "compiler.h"
#include "global-handles.h"
#include "scopeinfo.h"
#include "unicode.h"
const char* const CodeEntry::kEmptyNamePrefix = "";
+CodeEntry::~CodeEntry() {
+ delete no_frame_ranges_;
+}
+
+
void CodeEntry::CopyData(const CodeEntry& source) {
tag_ = source.tag_;
name_prefix_ = source.name_prefix_;
}
-CodeEntry* CodeMap::FindEntry(Address addr) {
+CodeEntry* CodeMap::FindEntry(Address addr, Address* start) {
CodeTree::Locator locator;
if (tree_.FindGreatestLessThan(addr, &locator)) {
// locator.key() <= addr. Need to check that addr is within entry.
const CodeEntryInfo& entry = locator.value();
- if (addr < (locator.key() + entry.size))
+ if (addr < (locator.key() + entry.size)) {
+ if (start) {
+ *start = locator.key();
+ }
return entry.entry;
+ }
}
return NULL;
}
CodeEntry** entry = entries.start();
memset(entry, 0, entries.length() * sizeof(*entry));
if (sample.pc != NULL) {
- *entry++ = code_map_.FindEntry(sample.pc);
+ Address start;
+ CodeEntry* pc_entry = code_map_.FindEntry(sample.pc, &start);
+ // If pc is in the function code before it set up stack frame or after the
+ // frame was destroyed SafeStackTraceFrameIterator incorrectly thinks that
+ // ebp contains return address of the current function and skips caller's
+ // frame. Check for this case and just skip such samples.
+ if (pc_entry) {
+ List<OffsetRange>* ranges = pc_entry->no_frame_ranges();
+ if (ranges) {
+ Code* code = Code::cast(HeapObject::FromAddress(start));
+ int pc_offset = sample.pc - code->instruction_start();
+ for (int i = 0; i < ranges->length(); i++) {
+ OffsetRange& range = ranges->at(i);
+ if (range.from <= pc_offset && pc_offset < range.to) {
+ return;
+ }
+ }
+ }
+ }
+ *entry++ = pc_entry;
if (sample.has_external_callback) {
// Don't use PC when in external callback code, as it can point
namespace v8 {
namespace internal {
+struct OffsetRange;
+
class TokenEnumerator {
public:
TokenEnumerator();
const char* resource_name,
int line_number,
int security_token_id));
+ ~CodeEntry();
INLINE(bool is_js_function() const) { return is_js_function_tag(tag_); }
INLINE(const char* name_prefix() const) { return name_prefix_; }
INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag));
+ List<OffsetRange>* no_frame_ranges() const { return no_frame_ranges_; }
+ void set_no_frame_ranges(List<OffsetRange>* ranges) {
+ no_frame_ranges_ = ranges;
+ }
+
void CopyData(const CodeEntry& source);
uint32_t GetCallUid() const;
bool IsSameAs(CodeEntry* entry) const;
int line_number_;
int shared_id_;
int security_token_id_;
+ List<OffsetRange>* no_frame_ranges_;
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
};
CodeMap() : next_shared_id_(1) { }
void AddCode(Address addr, CodeEntry* entry, unsigned size);
void MoveCode(Address from, Address to);
- CodeEntry* FindEntry(Address addr);
+ CodeEntry* FindEntry(Address addr, Address* start = NULL);
int GetSharedId(Address addr);
void Print();
__ movq(rbp, rsp);
__ push(rsi); // Callee's context.
__ push(rdi); // Callee's JS Function.
+ info->AddNoFrameRange(0, masm_->pc_offset());
{ Comment cmnt(masm_, "[ Allocate locals");
int locals_count = info->scope()->num_stack_slots();
// patch with the code required by the debugger.
__ movq(rsp, rbp);
__ pop(rbp);
+ int no_frame_start = masm_->pc_offset();
int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
__ Ret(arguments_bytes, rcx);
ASSERT(Assembler::kJSReturnSequenceLength <=
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
#endif
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
} else {
__ push(rdi); // Callee's JS function.
}
+ info()->AddNoFrameRange(0, masm_->pc_offset());
}
// Reserve space for the stack slots needed by the code.
count++;
}
}
+ int no_frame_start = -1;
if (NeedsEagerFrame()) {
__ movq(rsp, rbp);
__ pop(rbp);
+ no_frame_start = masm_->pc_offset();
}
if (instr->has_constant_parameter_count()) {
__ Ret((ToInteger32(instr->constant_parameter_count()) + 1) * kPointerSize,
__ addq(rsp, reg);
__ jmp(return_addr_reg);
}
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
}
test-serialize/DeserializeFromSecondSerialization: SKIP
##############################################################################
+[ $arch == arm || $arch == mipsel ]
+
+# BUG(2628): Signal may come when pc is close to frame enter/exit code and on
+# simulator the stack frame is not set up when it is expected to be for the pc
+# value.
+test-cpu-profiler/SampleWhenFrameIsNotSetup: SKIP
+
+##############################################################################
[ $arch == android_arm || $arch == android_ia32 ]
# Tests crash as there is no /tmp directory in Android.
0,
ToAddress(0x1000),
0x100,
- ToAddress(0x10000));
+ ToAddress(0x10000),
+ NULL);
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
"bbb",
ToAddress(0x1200),
cpu_profiler->DeleteAllCpuProfiles();
}
+
+
+
+static const char* cpu_profiler_test_source2 = "function loop() {}\n"
+"function delay() { loop(); }\n"
+"function start(count) {\n"
+" var k = 0;\n"
+" do {\n"
+" delay();\n"
+" } while (++k < count*100*1000);\n"
+"}\n";
+
+// Check that the profile tree doesn't contain unexpecte traces:
+// - 'loop' can be called only by 'delay'
+// - 'delay' may be called only by 'start'
+// The profile will look like the following:
+//
+// [Top down]:
+// 135 0 (root) [-1] #1
+// 121 72 start [-1] #3
+// 49 33 delay [-1] #4
+// 16 16 loop [-1] #5
+// 14 14 (program) [-1] #2
+TEST(SampleWhenFrameIsNotSetup) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ v8::Script::Compile(v8::String::New(cpu_profiler_test_source2))->Run();
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("start")));
+
+ v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
+ v8::Local<v8::String> profile_name = v8::String::New("my_profile");
+
+ cpu_profiler->StartCpuProfiling(profile_name);
+ int32_t repeat_count = 100;
+#if defined(USE_SIMULATOR)
+ // Simulators are much slower.
+ repeat_count = 1;
+#endif
+ v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
+ function->Call(env->Global(), ARRAY_SIZE(args), args);
+ const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
+
+ CHECK_NE(NULL, profile);
+ // Dump collected profile to have a better diagnostic in case of failure.
+ reinterpret_cast<i::CpuProfile*>(
+ const_cast<v8::CpuProfile*>(profile))->Print();
+
+ const v8::CpuProfileNode* root = profile->GetTopDownRoot();
+
+ ScopedVector<v8::Handle<v8::String> > names(3);
+ names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
+ names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
+ names[2] = v8::String::New("start");
+ CheckChildrenNames(root, names);
+
+ const v8::CpuProfileNode* startNode = FindChild(root, "start");
+ if (startNode->GetChildrenCount() > 0) {
+ CHECK_EQ(1, startNode->GetChildrenCount());
+ const v8::CpuProfileNode* delayNode = FindChild(startNode, "delay");
+ if (delayNode->GetChildrenCount() > 0) {
+ CHECK_EQ(1, delayNode->GetChildrenCount());
+ FindChild(delayNode, "loop");
+ }
+ }
+
+ cpu_profiler->DeleteAllCpuProfiles();
+}