Provide more functions to CPU profiler (fix issue 858).

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
parent a26a9b74
......@@ -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();
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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.
......
......@@ -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));
......
......@@ -83,7 +83,6 @@ PropertyDetails PropertyDetails::AsDeleted() {
}
#define SMI_ACCESSORS(holder, name, offset) \
int holder::name() { \
Object* value = READ_FIELD(this, offset); \
......
......@@ -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));
}
}
}
......
......@@ -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();
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment