Skip samples where top function's stack frame is not setup properly
authoryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 14 May 2013 22:51:33 +0000 (22:51 +0000)
committeryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 14 May 2013 22:51:33 +0000 (22:51 +0000)
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

19 files changed:
src/arm/full-codegen-arm.cc
src/arm/lithium-codegen-arm.cc
src/compiler.cc
src/compiler.h
src/cpu-profiler.cc
src/cpu-profiler.h
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/log.cc
src/log.h
src/mips/full-codegen-mips.cc
src/mips/lithium-codegen-mips.cc
src/profile-generator-inl.h
src/profile-generator.cc
src/profile-generator.h
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
test/cctest/cctest.status
test/cctest/test-cpu-profiler.cc

index da2d3fd..5b6ce4c 100644 (file)
@@ -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
index 3c24a2a..2b0f611 100644 (file)
@@ -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());
+  }
 }
 
 
index 76fb726..5045758 100644 (file)
@@ -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()));
     }
   }
index 00074c8..dbb513c 100644 (file)
@@ -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_;
index 51d2942..c30d4d4 100644 (file)
@@ -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);
 }
 
 
index da7ea6d..2f8479f 100644 (file)
@@ -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);
index e5a999e..1bc72ec 100644 (file)
@@ -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());
   }
 }
 
index 33cfd01..b45c248 100644 (file)
@@ -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());
+  }
 }
 
 
index e52d0f3..00fa432 100644 (file)
@@ -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));
   }
 }
 
index 8db13df..531f1de 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -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();
index 872af86..aa5aa80 100644 (file)
@@ -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
index bf6052a..ba7dc10 100644 (file)
@@ -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());
+  }
 }
 
 
index 64dabf6..d6e8a37 100644 (file)
@@ -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) {
 }
 
 
index b1b163b..1506615 100644 (file)
@@ -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
index 761291e..37cc57d 100644 (file)
@@ -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();
index a296c07..22c6fae 100644 (file)
@@ -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());
   }
 }
 
index bc7e6a1..a2a6151 100644 (file)
@@ -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());
+  }
 }
 
 
index e5523b2..4a217e2 100644 (file)
@@ -81,6 +81,14 @@ 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 ]
 
 # Tests crash as there is no /tmp directory in Android.
index d73be18..56aa9a7 100644 (file)
@@ -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();
+}