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 4248a64..2517c5f 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 4d5559e..86f9f67 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 6d46740..f61e343 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 a9d89a2..5c70057 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 160072d..2534e1e 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 23c356b..c847b84 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 2bbdeaa..f63d672 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 a7cb7a2..525dea2 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 a2ac820..1e949a2 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 af7c0bd..6b41577 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;
 }