Skip samples where top function's stack frame is not setup properly
Stack iterator takes return address based on the frame pointer (ebp) and detects JS frames based on value at fp + StandardFrameConstants::kMarkerOffset. So in order the iterator to work correctly this values should be already setup for the current function. Stack frame is constructed at the very beginning of JS function code and destroyed before return. If sample is taken before before the frame construction is completed or after it was destroyed the stack iterator will wrongly think that FP points at the current functions frame base and will skip callers frame. To avoid this we mark code ranges where stack frame doesn't exist and completely ignore such samples. This fixes cctest/test-cpu-profiler/CollectCpuProfile flakiness. BUG=v8:2628 R=jkummerow@chromium.org Review URL: https://codereview.chromium.org/14253015 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14670 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
8f602260d3
commit
69c2f54d32
@ -175,6 +175,7 @@ void FullCodeGenerator::Generate() {
|
||||
// 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();
|
||||
@ -438,9 +439,11 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
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
|
||||
|
@ -2898,9 +2898,11 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
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());
|
||||
@ -2916,6 +2918,10 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
__ Jump(lr);
|
||||
|
||||
if (no_frame_start != -1) {
|
||||
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -103,6 +103,8 @@ void CompilationInfo::Initialize(Isolate* isolate, Mode mode, Zone* zone) {
|
||||
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;
|
||||
@ -121,6 +123,7 @@ void CompilationInfo::Initialize(Isolate* isolate, Mode mode, Zone* zone) {
|
||||
|
||||
CompilationInfo::~CompilationInfo() {
|
||||
delete deferred_handles_;
|
||||
delete no_frame_ranges_;
|
||||
}
|
||||
|
||||
|
||||
@ -568,6 +571,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
|
||||
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
|
||||
*info->code(),
|
||||
*result,
|
||||
info,
|
||||
String::cast(script->name())));
|
||||
GDBJIT(AddCode(Handle<String>(String::cast(script->name())),
|
||||
script,
|
||||
@ -580,6 +584,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
|
||||
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
|
||||
*info->code(),
|
||||
*result,
|
||||
info,
|
||||
isolate->heap()->empty_string()));
|
||||
GDBJIT(AddCode(Handle<String>(), script, info->code(), info));
|
||||
}
|
||||
@ -807,6 +812,10 @@ static void InstallCodeCommon(CompilationInfo* 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);
|
||||
}
|
||||
|
||||
@ -1151,6 +1160,7 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
|
||||
CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
|
||||
*code,
|
||||
*shared,
|
||||
info,
|
||||
String::cast(script->name()),
|
||||
line_num));
|
||||
} else {
|
||||
@ -1158,6 +1168,7 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
|
||||
CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
|
||||
*code,
|
||||
*shared,
|
||||
info,
|
||||
shared->DebugName()));
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,12 @@ enum ParseRestriction {
|
||||
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 {
|
||||
@ -257,6 +263,20 @@ 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_;
|
||||
|
||||
@ -361,6 +381,8 @@ class CompilationInfo {
|
||||
|
||||
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_;
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "cpu-profiler-inl.h"
|
||||
|
||||
#include "compiler.h"
|
||||
#include "frames-inl.h"
|
||||
#include "hashmap.h"
|
||||
#include "log-inl.h"
|
||||
@ -80,7 +81,8 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
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_;
|
||||
@ -88,6 +90,9 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
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);
|
||||
@ -323,6 +328,7 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
v8::CpuProfileNode::kNoLineNumberInfo,
|
||||
code->address(),
|
||||
code->ExecutableSize(),
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@ -330,6 +336,7 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
Code* code,
|
||||
SharedFunctionInfo* shared,
|
||||
CompilationInfo* info,
|
||||
Name* name) {
|
||||
processor_->CodeCreateEvent(
|
||||
tag,
|
||||
@ -338,13 +345,15 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags 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,
|
||||
@ -353,7 +362,8 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
||||
line,
|
||||
code->address(),
|
||||
code->ExecutableSize(),
|
||||
shared->address());
|
||||
shared->address(),
|
||||
info);
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,6 +40,7 @@ namespace internal {
|
||||
// Forward declarations.
|
||||
class CodeEntry;
|
||||
class CodeMap;
|
||||
class CompilationInfo;
|
||||
class CpuProfile;
|
||||
class CpuProfilesCollection;
|
||||
class ProfileGenerator;
|
||||
@ -142,7 +143,8 @@ class ProfilerEventsProcessor : public Thread {
|
||||
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);
|
||||
@ -227,11 +229,13 @@ class CpuProfiler {
|
||||
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);
|
||||
|
@ -161,6 +161,7 @@ void FullCodeGenerator::Generate() {
|
||||
__ 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();
|
||||
@ -410,6 +411,7 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
// 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;
|
||||
@ -420,6 +422,7 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
ASSERT(Assembler::kJSReturnSequenceLength <=
|
||||
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
|
||||
#endif
|
||||
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,6 +210,7 @@ bool LCodeGen::GeneratePrologue() {
|
||||
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)));
|
||||
@ -2806,9 +2807,11 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
__ 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;
|
||||
@ -2820,6 +2823,9 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
EmitReturn(instr, false);
|
||||
if (no_frame_start != -1) {
|
||||
info()->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
10
src/log.cc
10
src/log.cc
@ -1016,6 +1016,7 @@ static const char* ComputeMarker(Code* code) {
|
||||
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) {
|
||||
@ -1093,6 +1094,7 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
||||
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) {
|
||||
@ -1735,20 +1737,20 @@ void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
|
||||
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.
|
||||
@ -1763,7 +1765,7 @@ void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
|
||||
} else {
|
||||
PROFILE(isolate_,
|
||||
CodeCreateEvent(
|
||||
Logger::LAZY_COMPILE_TAG, *code, *shared, *func_name));
|
||||
Logger::LAZY_COMPILE_TAG, *code, *shared, NULL, *func_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,7 @@ class Ticker;
|
||||
class Isolate;
|
||||
class PositionsRecorder;
|
||||
class CpuProfiler;
|
||||
class CompilationInfo;
|
||||
|
||||
#undef LOG
|
||||
#define LOG(isolate, Call) \
|
||||
@ -235,10 +236,12 @@ class Logger {
|
||||
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();
|
||||
|
@ -179,6 +179,7 @@ void FullCodeGenerator::Generate() {
|
||||
__ 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();
|
||||
@ -438,9 +439,11 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
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
|
||||
|
@ -179,6 +179,7 @@ bool LCodeGen::GeneratePrologue() {
|
||||
__ 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.
|
||||
@ -2511,9 +2512,11 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
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());
|
||||
@ -2530,6 +2533,10 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
}
|
||||
|
||||
__ Jump(ra);
|
||||
|
||||
if (no_frame_start != -1) {
|
||||
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,7 +55,8 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
|
||||
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) {
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "profile-generator-inl.h"
|
||||
|
||||
#include "compiler.h"
|
||||
#include "global-handles.h"
|
||||
#include "scopeinfo.h"
|
||||
#include "unicode.h"
|
||||
@ -186,6 +187,11 @@ size_t StringsStorage::GetUsedMemorySize() const {
|
||||
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_;
|
||||
@ -531,13 +537,17 @@ void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@ -898,7 +908,26 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
|
||||
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
|
||||
|
@ -35,6 +35,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
struct OffsetRange;
|
||||
|
||||
class TokenEnumerator {
|
||||
public:
|
||||
TokenEnumerator();
|
||||
@ -100,6 +102,7 @@ class CodeEntry {
|
||||
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_; }
|
||||
@ -112,6 +115,11 @@ class CodeEntry {
|
||||
|
||||
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;
|
||||
@ -126,6 +134,7 @@ class CodeEntry {
|
||||
int line_number_;
|
||||
int shared_id_;
|
||||
int security_token_id_;
|
||||
List<OffsetRange>* no_frame_ranges_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
|
||||
};
|
||||
@ -251,7 +260,7 @@ class CodeMap {
|
||||
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();
|
||||
|
@ -157,6 +157,7 @@ void FullCodeGenerator::Generate() {
|
||||
__ 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();
|
||||
@ -406,6 +407,7 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
// 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);
|
||||
@ -423,6 +425,7 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
ASSERT(Assembler::kJSReturnSequenceLength <=
|
||||
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
|
||||
#endif
|
||||
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,7 @@ bool LCodeGen::GeneratePrologue() {
|
||||
} else {
|
||||
__ push(rdi); // Callee's JS function.
|
||||
}
|
||||
info()->AddNoFrameRange(0, masm_->pc_offset());
|
||||
}
|
||||
|
||||
// Reserve space for the stack slots needed by the code.
|
||||
@ -2521,9 +2522,11 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
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,
|
||||
@ -2538,6 +2541,9 @@ void LCodeGen::DoReturn(LReturn* instr) {
|
||||
__ addq(rsp, reg);
|
||||
__ jmp(return_addr_reg);
|
||||
}
|
||||
if (no_frame_start != -1) {
|
||||
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,6 +80,14 @@ test-serialize/DeserializeFromSecondSerializationAndRunScript2: SKIP
|
||||
test-serialize/DeserializeAndRunScript2: SKIP
|
||||
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 ]
|
||||
|
||||
|
@ -122,7 +122,8 @@ TEST(CodeEvents) {
|
||||
0,
|
||||
ToAddress(0x1000),
|
||||
0x100,
|
||||
ToAddress(0x10000));
|
||||
ToAddress(0x10000),
|
||||
NULL);
|
||||
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
|
||||
"bbb",
|
||||
ToAddress(0x1200),
|
||||
@ -549,3 +550,72 @@ TEST(CollectCpuProfile) {
|
||||
|
||||
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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user