Provide more functions to CPU profiler (fix issue 858).
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 24 Sep 2010 11:45:12 +0000 (11:45 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 24 Sep 2010 11:45:12 +0000 (11:45 +0000)
The cause for missing functions is that some of them are created
from compiled code (see FastNewClosureStub), and thus not get
registered in profiler's code map.

My solution is to hook on GC visitor to provide JS functions
addresses to profiler, only if it is enabled.

BUG=858
TEST=

Review URL: http://codereview.chromium.org/3417019

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5523 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/cpu-profiler.cc
src/cpu-profiler.h
src/heap.cc
src/log.cc
src/log.h
src/mark-compact.cc
src/objects-inl.h
src/profile-generator.cc
src/profile-generator.h
src/stub-cache.cc

index 4248a64338f81228fbfdb9eb0fb41fa097748c5a..2517c5f4a277ebc7a5707788600319564731f076 100644 (file)
@@ -32,6 +32,7 @@
 #ifdef ENABLE_LOGGING_AND_PROFILING
 
 #include "frames-inl.h"
+#include "hashmap.h"
 #include "log-inl.h"
 
 #include "../include/v8-profiler.h"
@@ -50,7 +51,13 @@ ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
       ticks_buffer_(sizeof(TickSampleEventRecord),
                     kTickSamplesBufferChunkSize,
                     kTickSamplesBufferChunksCount),
-      enqueue_order_(0) {
+      enqueue_order_(0),
+      known_functions_(new HashMap(AddressesMatch)) {
+}
+
+
+ProfilerEventsProcessor::~ProfilerEventsProcessor() {
+  delete known_functions_;
 }
 
 
@@ -152,16 +159,32 @@ void ProfilerEventsProcessor::FunctionCreateEvent(Address alias,
   rec->entry = generator_->NewCodeEntry(security_token_id);
   rec->code_start = start;
   events_buffer_.Enqueue(evt_rec);
+
+  known_functions_->Lookup(alias, AddressHash(alias), true);
 }
 
 
 void ProfilerEventsProcessor::FunctionMoveEvent(Address from, Address to) {
   CodeMoveEvent(from, to);
+
+  if (IsKnownFunction(from)) {
+    known_functions_->Remove(from, AddressHash(from));
+    known_functions_->Lookup(to, AddressHash(to), true);
+  }
 }
 
 
 void ProfilerEventsProcessor::FunctionDeleteEvent(Address from) {
   CodeDeleteEvent(from);
+
+  known_functions_->Remove(from, AddressHash(from));
+}
+
+
+bool ProfilerEventsProcessor::IsKnownFunction(Address start) {
+  HashMap::Entry* entry =
+      known_functions_->Lookup(start, AddressHash(start), false);
+  return entry != NULL;
 }
 
 
@@ -403,6 +426,40 @@ void CpuProfiler::FunctionCreateEvent(JSFunction* function) {
 }
 
 
+void CpuProfiler::FunctionCreateEventFromMove(JSFunction* function,
+                                              HeapObject* source) {
+  // This function is called from GC iterators (during Scavenge,
+  // MC, and MS), so marking bits can be set on objects. That's
+  // why unchecked accessors are used here.
+  
+  // The same function can be reported several times.
+  if (function->unchecked_code() == Builtins::builtin(Builtins::LazyCompile)
+      || singleton_->processor_->IsKnownFunction(function->address())) return;
+
+  int security_token_id = TokenEnumerator::kNoSecurityToken;
+  // In debug mode, assertions may fail for contexts,
+  // and we can live without security tokens in debug mode.
+#ifndef DEBUG
+  if (function->unchecked_context()->IsContext()) {
+    security_token_id = singleton_->token_enumerator_->GetTokenId(
+        function->context()->global_context()->security_token());
+  }
+  // Security token may not be moved yet.
+  if (security_token_id == TokenEnumerator::kNoSecurityToken) {
+    JSFunction* old_function = reinterpret_cast<JSFunction*>(source);
+    if (old_function->unchecked_context()->IsContext()) {
+      security_token_id = singleton_->token_enumerator_->GetTokenId(
+          old_function->context()->global_context()->security_token());
+    }
+  }
+#endif
+  singleton_->processor_->FunctionCreateEvent(
+      function->address(),
+      function->unchecked_code()->address(),
+      security_token_id);
+}
+
+
 void CpuProfiler::FunctionMoveEvent(Address from, Address to) {
   singleton_->processor_->FunctionMoveEvent(from, to);
 }
@@ -473,7 +530,12 @@ void CpuProfiler::StartProcessorIfNotStarted() {
     processor_->Start();
     // Enumerate stuff we already have in the heap.
     if (Heap::HasBeenSetup()) {
-      Logger::LogCodeObjects();
+      if (!FLAG_prof_browser_mode) {
+        bool saved_log_code_flag = FLAG_log_code;
+        FLAG_log_code = true;
+        Logger::LogCodeObjects();
+        FLAG_log_code = saved_log_code_flag;
+      }
       Logger::LogCompiledFunctions();
       Logger::LogFunctionObjects();
       Logger::LogAccessorCallbacks();
index 4d5559e4febdc9de02a7ca105e007347a6686170..86f9f6711f9f21713130f24fcdea3e61149112ef 100644 (file)
@@ -41,6 +41,7 @@ class CodeEntry;
 class CodeMap;
 class CpuProfile;
 class CpuProfilesCollection;
+class HashMap;
 class ProfileGenerator;
 class TokenEnumerator;
 
@@ -132,7 +133,7 @@ class TickSampleEventRecord BASE_EMBEDDED {
 class ProfilerEventsProcessor : public Thread {
  public:
   explicit ProfilerEventsProcessor(ProfileGenerator* generator);
-  virtual ~ProfilerEventsProcessor() { }
+  virtual ~ProfilerEventsProcessor();
 
   // Thread control.
   virtual void Run();
@@ -163,6 +164,7 @@ class ProfilerEventsProcessor : public Thread {
                              Address start, unsigned size);
   // Puts current stack into tick sample events buffer.
   void AddCurrentStack();
+  bool IsKnownFunction(Address start);
 
   // Tick sample events are filled directly in the buffer of the circular
   // queue (because the structure is of fixed width, but usually not all
@@ -183,6 +185,13 @@ class ProfilerEventsProcessor : public Thread {
   bool ProcessTicks(unsigned dequeue_order);
 
   INLINE(static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag));
+  INLINE(static bool AddressesMatch(void* key1, void* key2)) {
+    return key1 == key2;
+  }
+  INLINE(static uint32_t AddressHash(Address addr)) {
+    return ComputeIntegerHash(
+        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr)));
+  }
 
   ProfileGenerator* generator_;
   bool running_;
@@ -190,6 +199,9 @@ class ProfilerEventsProcessor : public Thread {
   SamplingCircularQueue ticks_buffer_;
   UnboundQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
   unsigned enqueue_order_;
+
+  // Used from the VM thread.
+  HashMap* known_functions_;
 };
 
 } }  // namespace v8::internal
@@ -242,6 +254,10 @@ class CpuProfiler {
   static void CodeMoveEvent(Address from, Address to);
   static void CodeDeleteEvent(Address from);
   static void FunctionCreateEvent(JSFunction* function);
+  // Reports function creation in case we had missed it (e.g.
+  // if it was created from compiled code).
+  static void FunctionCreateEventFromMove(JSFunction* function,
+                                          HeapObject* source);
   static void FunctionMoveEvent(Address from, Address to);
   static void FunctionDeleteEvent(Address from);
   static void GetterCallbackEvent(String* name, Address entry_point);
index 6d46740b2ef7b9955261c42978f31dcd215ad3af..f61e343f4ff821318f97f380092dd06fc7d9c55c 100644 (file)
@@ -1218,7 +1218,14 @@ class ScavengingVisitor : public StaticVisitorBase {
     RecordCopiedObject(target);
 #endif
     HEAP_PROFILE(ObjectMoveEvent(source->address(), target->address()));
-
+#if defined(ENABLE_LOGGING_AND_PROFILING)
+    if (Logger::is_logging() || CpuProfiler::is_profiling()) {
+      if (target->IsJSFunction()) {
+        PROFILE(FunctionMoveEvent(source->address(), target->address()));
+        PROFILE(FunctionCreateEventFromMove(JSFunction::cast(target), source));
+      }
+    }
+#endif
     return target;
   }
 
index a9d89a20ea49fd848dcdc3edd43fff07a7aa8010..5c700576c8042c640d5f9f425ad0101cc42176e0 100644 (file)
@@ -871,14 +871,17 @@ void Logger::SnapshotPositionEvent(Address addr, int pos) {
 
 void Logger::FunctionCreateEvent(JSFunction* function) {
 #ifdef ENABLE_LOGGING_AND_PROFILING
+  // This function can be called from GC iterators (during Scavenge,
+  // MC, and MS), so marking bits can be set on objects. That's
+  // why unchecked accessors are used here.
   static Address prev_code = NULL;
   if (!Log::IsEnabled() || !FLAG_log_code) return;
   LogMessageBuilder msg;
   msg.Append("%s,", log_events_[FUNCTION_CREATION_EVENT]);
   msg.AppendAddress(function->address());
   msg.Append(',');
-  msg.AppendAddress(function->code()->address(), prev_code);
-  prev_code = function->code()->address();
+  msg.AppendAddress(function->unchecked_code()->address(), prev_code);
+  prev_code = function->unchecked_code()->address();
   if (FLAG_compress_log) {
     ASSERT(compression_helper_ != NULL);
     if (!compression_helper_->HandleMessage(&msg)) return;
@@ -889,6 +892,16 @@ void Logger::FunctionCreateEvent(JSFunction* function) {
 }
 
 
+void Logger::FunctionCreateEventFromMove(JSFunction* function,
+                                         HeapObject*) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+  if (function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile)) {
+    FunctionCreateEvent(function);
+  }
+#endif
+}
+
+
 void Logger::FunctionMoveEvent(Address from, Address to) {
 #ifdef ENABLE_LOGGING_AND_PROFILING
   MoveEventInternal(FUNCTION_MOVE_EVENT, from, to);
index 160072dec572c5fa6d782734be377850bdd8fc43..2534e1e8d1ad2a074e15660ade02fb789bfd86f7 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -216,6 +216,8 @@ class Logger {
   static void CodeDeleteEvent(Address from);
   // Emits a function object create event.
   static void FunctionCreateEvent(JSFunction* function);
+  static void FunctionCreateEventFromMove(JSFunction* function,
+                                          HeapObject*);
   // Emits a function move event.
   static void FunctionMoveEvent(Address from, Address to);
   // Emits a function delete event.
index 23c356b7e377572bd792e2a1327f78ea96db0528..c847b8424c72282187c23b31856bdd908d9a4cc8 100644 (file)
@@ -2520,6 +2520,7 @@ int MarkCompactCollector::RelocateOldNonCodeObject(HeapObject* obj,
   HeapObject* copied_to = HeapObject::FromAddress(new_addr);
   if (copied_to->IsJSFunction()) {
     PROFILE(FunctionMoveEvent(old_addr, new_addr));
+    PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to), obj));
   }
   HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
 
@@ -2612,6 +2613,7 @@ int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
   HeapObject* copied_to = HeapObject::FromAddress(new_addr);
   if (copied_to->IsJSFunction()) {
     PROFILE(FunctionMoveEvent(old_addr, new_addr));
+    PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to), obj));
   }
   HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
 
index 2bbdeaa2050159b15baef8c050313fab5f4e9502..f63d6725ec2981afdf13b294c8a7de8a76d51f31 100644 (file)
@@ -83,7 +83,6 @@ PropertyDetails PropertyDetails::AsDeleted() {
   }
 
 
-
 #define SMI_ACCESSORS(holder, name, offset)             \
   int holder::name() {                                  \
     Object* value = READ_FIELD(this, offset);           \
index a7cb7a238ca165335be8bc6bf0f7ba9791191fd3..525dea2fbaa0cf71a010c316e6b58309250ad866 100644 (file)
@@ -134,10 +134,13 @@ void CodeEntry::CopyData(const CodeEntry& source) {
 
 uint32_t CodeEntry::GetCallUid() const {
   uint32_t hash = ComputeIntegerHash(tag_);
-  hash ^= static_cast<int32_t>(reinterpret_cast<intptr_t>(name_prefix_));
-  hash ^= static_cast<int32_t>(reinterpret_cast<intptr_t>(name_));
-  hash ^= static_cast<int32_t>(reinterpret_cast<intptr_t>(resource_name_));
-  hash ^= static_cast<int32_t>(line_number_);
+  hash ^= ComputeIntegerHash(
+      static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)));
+  hash ^= ComputeIntegerHash(
+      static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
+  hash ^= ComputeIntegerHash(
+      static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
+  hash ^= ComputeIntegerHash(line_number_);
   return hash;
 }
 
@@ -442,9 +445,10 @@ void CodeMap::AddAlias(Address start, CodeEntry* entry, Address code_start) {
   CodeTree::Locator locator;
   if (tree_.Find(code_start, &locator)) {
     const CodeEntryInfo& code_info = locator.value();
-    entry->CopyData(*code_info.entry);
-    tree_.Insert(start, &locator);
-    locator.set_value(CodeEntryInfo(entry, code_info.size));
+    if (tree_.Insert(start, &locator)) {
+      entry->CopyData(*code_info.entry);
+      locator.set_value(CodeEntryInfo(entry, code_info.size));
+    }
   }
 }
 
index a2ac82019b0b72e20e9e426a84c7bf5859735792..1e949a2cfb1106bff670df9a66331f47de390fe4 100644 (file)
@@ -745,7 +745,8 @@ class HeapObjectsMap {
   }
 
   static uint32_t AddressHash(Address addr) {
-    return static_cast<int32_t>(reinterpret_cast<intptr_t>(addr));
+    return ComputeIntegerHash(
+        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr)));
   }
 
   bool initial_fill_mode_;
@@ -888,7 +889,8 @@ class HeapEntriesMap {
   };
 
   uint32_t Hash(HeapObject* object) {
-    return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
+    return ComputeIntegerHash(
+        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object)));
   }
   static bool HeapObjectsMatch(void* key1, void* key2) { return key1 == key2; }
 
@@ -995,7 +997,8 @@ class HeapSnapshotJSONSerializer {
   }
 
   INLINE(static uint32_t ObjectHash(const void* key)) {
-    return static_cast<int32_t>(reinterpret_cast<intptr_t>(key));
+    return ComputeIntegerHash(
+        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)));
   }
 
   void EnumerateNodes();
index af7c0bd8d996d09c4228409e6ab4fb6376b1aede..6b41577ea860cde740974881832d9e82e54ae4d6 100644 (file)
@@ -1186,25 +1186,43 @@ void StubCompiler::LookupPostInterceptor(JSObject* holder,
 
 Object* LoadStubCompiler::GetCode(PropertyType type, String* name) {
   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, type);
-  return GetCodeWithFlags(flags, name);
+  Object* result = GetCodeWithFlags(flags, name);
+  if (!result->IsFailure()) {
+    PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(result), name));
+  }
+  return result;
 }
 
 
 Object* KeyedLoadStubCompiler::GetCode(PropertyType type, String* name) {
   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, type);
-  return GetCodeWithFlags(flags, name);
+  Object* result = GetCodeWithFlags(flags, name);
+  if (!result->IsFailure()) {
+    PROFILE(
+        CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(result), name));
+  }
+  return result;
 }
 
 
 Object* StoreStubCompiler::GetCode(PropertyType type, String* name) {
   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type);
-  return GetCodeWithFlags(flags, name);
+  Object* result = GetCodeWithFlags(flags, name);
+  if (!result->IsFailure()) {
+    PROFILE(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(result), name));
+  }
+  return result;
 }
 
 
 Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type);
-  return GetCodeWithFlags(flags, name);
+  Object* result = GetCodeWithFlags(flags, name);
+  if (!result->IsFailure()) {
+    PROFILE(
+        CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(result), name));
+  }
+  return result;
 }