Fix CPU profiling for Crankshaft.

The main issue was due to multiple recompilations of functions.  Now
code objects are grouped by function using SFI object address.
JSFunction objects are no longer tracked, instead we track SFI object
moves. To pick a correct code version, we now sample return addresses
instead of JSFunction addresses.

tools/{linux|mac|windows}-tickprocessor scripts differentiate
between code optimization states for the same function
(using * and ~ prefixes introduced earlier).

DevTools CPU profiler treats all variants of function code as
a single function.

ll_prof treats each optimized variant as a separate entry, because
it can disassemble each one of them.

tickprocessor.py not updated -- it is deprecated and will be removed.

BUG=v8/1087,b/3178160
TEST=all existing tests pass, including Chromium layout tests

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 08377c84
......@@ -261,10 +261,8 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
Handle<SharedFunctionInfo> shared = info->shared_info();
shared->EnableDeoptimizationSupport(*unoptimized.code());
// The existing unoptimized code was replaced with the new one.
Compiler::RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG,
Handle<String>(shared->DebugName()),
shared->start_position(),
&unoptimized);
Compiler::RecordFunctionCompilation(
Logger::LAZY_COMPILE_TAG, &unoptimized, shared);
}
}
......@@ -414,13 +412,25 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
return Handle<SharedFunctionInfo>::null();
}
// Allocate function.
ASSERT(!info->code().is_null());
Handle<SharedFunctionInfo> result =
Factory::NewSharedFunctionInfo(
lit->name(),
lit->materialized_literal_count(),
info->code(),
SerializedScopeInfo::Create(info->scope()));
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
Compiler::SetFunctionInfo(result, lit, true, script);
if (script->name()->IsString()) {
PROFILE(CodeCreateEvent(
info->is_eval()
? Logger::EVAL_TAG
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(),
*result,
String::cast(script->name())));
GDBJIT(AddCode(Handle<String>(String::cast(script->name())),
script,
......@@ -431,21 +441,11 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
? Logger::EVAL_TAG
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(),
""));
*result,
Heap::empty_string()));
GDBJIT(AddCode(Handle<String>(), script, info->code()));
}
// Allocate function.
Handle<SharedFunctionInfo> result =
Factory::NewSharedFunctionInfo(
lit->name(),
lit->materialized_literal_count(),
info->code(),
SerializedScopeInfo::Create(info->scope()));
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
Compiler::SetFunctionInfo(result, lit, true, script);
// Hint to the runtime system used when allocating space for initial
// property space by setting the expected number of properties for
// the instances of the function.
......@@ -612,10 +612,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
ASSERT(!info->code().is_null());
Handle<Code> code = info->code();
Handle<JSFunction> function = info->closure();
RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG,
Handle<String>(shared->DebugName()),
shared->start_position(),
info);
RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared);
if (info->IsOptimizing()) {
function->ReplaceCode(*code);
......@@ -723,10 +720,6 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
ASSERT(!info.code().is_null());
// Function compilation complete.
RecordFunctionCompilation(Logger::FUNCTION_TAG,
literal->debug_name(),
literal->start_position(),
&info);
scope_info = SerializedScopeInfo::Create(info.scope());
}
......@@ -737,6 +730,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
info.code(),
scope_info);
SetFunctionInfo(result, literal, false, script);
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
result->set_allows_lazy_compilation(allow_lazy);
// Set the expected number of properties for instances and return
......@@ -775,28 +769,31 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
Handle<String> name,
int start_position,
CompilationInfo* info) {
CompilationInfo* info,
Handle<SharedFunctionInfo> shared) {
// SharedFunctionInfo is passed separately, because if CompilationInfo
// was created using Script object, it will not have it.
// Log the code generation. If source information is available include
// script name and line number. Check explicitly whether logging is
// enabled as finding the line number is not free.
if (Logger::is_logging() ||
CpuProfiler::is_profiling()) {
if (Logger::is_logging() || CpuProfiler::is_profiling()) {
Handle<Script> script = info->script();
Handle<Code> code = info->code();
if (*code == Builtins::builtin(Builtins::LazyCompile)) return;
if (script->name()->IsString()) {
int line_num = GetScriptLineNumber(script, start_position) + 1;
int line_num = GetScriptLineNumber(script, shared->start_position()) + 1;
USE(line_num);
PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code,
*name,
*shared,
String::cast(script->name()),
line_num));
} else {
PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code,
*name));
*shared,
shared->DebugName()));
}
}
......
......@@ -265,9 +265,8 @@ class Compiler : public AllStatic {
#endif
static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
Handle<String> name,
int start_position,
CompilationInfo* info);
CompilationInfo* info,
Handle<SharedFunctionInfo> shared);
};
......
......@@ -41,6 +41,9 @@ namespace internal {
void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->AddCode(start, entry, size);
if (sfi_address != NULL) {
entry->set_shared_id(code_map->GetSFITag(sfi_address));
}
}
......@@ -54,8 +57,8 @@ void CodeDeleteEventRecord::UpdateCodeMap(CodeMap* code_map) {
}
void CodeAliasEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->AddAlias(start, entry, code_start);
void SFIMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->MoveCode(from, to);
}
......
......@@ -53,13 +53,7 @@ ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
ticks_buffer_(sizeof(TickSampleEventRecord),
kTickSamplesBufferChunkSize,
kTickSamplesBufferChunksCount),
enqueue_order_(0),
known_functions_(new HashMap(AddressesMatch)) {
}
ProfilerEventsProcessor::~ProfilerEventsProcessor() {
delete known_functions_;
enqueue_order_(0) {
}
......@@ -75,6 +69,7 @@ void ProfilerEventsProcessor::CallbackCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, prefix, name);
rec->size = 1;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
......@@ -84,7 +79,8 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
String* resource_name,
int line_number,
Address start,
unsigned size) {
unsigned size,
Address sfi_address) {
if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
......@@ -93,6 +89,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
rec->size = size;
rec->sfi_address = sfi_address;
events_buffer_.Enqueue(evt_rec);
}
......@@ -109,6 +106,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name);
rec->size = size;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
......@@ -125,6 +123,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, args_count);
rec->size = size;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
......@@ -150,57 +149,14 @@ void ProfilerEventsProcessor::CodeDeleteEvent(Address from) {
}
void ProfilerEventsProcessor::FunctionCreateEvent(Address alias,
Address start,
int security_token_id) {
void ProfilerEventsProcessor::SFIMoveEvent(Address from, Address to) {
CodeEventsContainer evt_rec;
CodeAliasEventRecord* rec = &evt_rec.CodeAliasEventRecord_;
rec->type = CodeEventRecord::CODE_ALIAS;
SFIMoveEventRecord* rec = &evt_rec.SFIMoveEventRecord_;
rec->type = CodeEventRecord::SFI_MOVE;
rec->order = ++enqueue_order_;
rec->start = alias;
rec->entry = generator_->NewCodeEntry(security_token_id);
rec->code_start = start;
rec->from = from;
rec->to = to;
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;
}
void ProfilerEventsProcessor::ProcessMovedFunctions() {
for (int i = 0; i < moved_functions_.length(); ++i) {
JSFunction* function = moved_functions_[i];
CpuProfiler::FunctionCreateEvent(function);
}
moved_functions_.Clear();
}
void ProfilerEventsProcessor::RememberMovedFunction(JSFunction* function) {
moved_functions_.Add(function);
}
......@@ -227,13 +183,12 @@ void ProfilerEventsProcessor::AddCurrentStack() {
TickSample* sample = &record.sample;
sample->state = Top::current_vm_state();
sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
sample->tos = NULL;
sample->frames_count = 0;
for (StackTraceFrameIterator it;
!it.done() && sample->frames_count < TickSample::kMaxFramesCount;
it.Advance()) {
JavaScriptFrame* frame = it.frame();
sample->stack[sample->frames_count++] =
reinterpret_cast<Address>(frame->function());
sample->stack[sample->frames_count++] = it.frame()->pc();
}
record.order = enqueue_order_;
ticks_from_vm_buffer_.Enqueue(record);
......@@ -393,20 +348,38 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Heap::empty_string(),
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize());
code->ExecutableSize(),
NULL);
}
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name,
String* source, int line) {
Code* code,
SharedFunctionInfo* shared,
String* name) {
singleton_->processor_->CodeCreateEvent(
tag,
name,
Heap::empty_string(),
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize(),
shared->address());
}
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* source, int line) {
singleton_->processor_->CodeCreateEvent(
tag,
shared->DebugName(),
source,
line,
code->address(),
code->ExecutableSize());
code->ExecutableSize(),
shared->address());
}
......@@ -430,44 +403,8 @@ void CpuProfiler::CodeDeleteEvent(Address from) {
}
void CpuProfiler::FunctionCreateEvent(JSFunction* function) {
int security_token_id = TokenEnumerator::kNoSecurityToken;
if (function->unchecked_context()->IsContext()) {
security_token_id = singleton_->token_enumerator_->GetTokenId(
function->context()->global_context()->security_token());
}
singleton_->processor_->FunctionCreateEvent(
function->address(),
function->shared()->code()->address(),
security_token_id);
}
void CpuProfiler::ProcessMovedFunctions() {
singleton_->processor_->ProcessMovedFunctions();
}
void CpuProfiler::FunctionCreateEventFromMove(JSFunction* function) {
// 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;
singleton_->processor_->RememberMovedFunction(function);
}
void CpuProfiler::FunctionMoveEvent(Address from, Address to) {
singleton_->processor_->FunctionMoveEvent(from, to);
}
void CpuProfiler::FunctionDeleteEvent(Address from) {
singleton_->processor_->FunctionDeleteEvent(from);
void CpuProfiler::SFIMoveEvent(Address from, Address to) {
singleton_->processor_->SFIMoveEvent(from, to);
}
......@@ -539,7 +476,6 @@ void CpuProfiler::StartProcessorIfNotStarted() {
FLAG_log_code = saved_log_code_flag;
}
Logger::LogCompiledFunctions();
Logger::LogFunctionObjects();
Logger::LogAccessorCallbacks();
}
// Enable stack sampling.
......
......@@ -50,7 +50,7 @@ class TokenEnumerator;
V(CODE_CREATION, CodeCreateEventRecord) \
V(CODE_MOVE, CodeMoveEventRecord) \
V(CODE_DELETE, CodeDeleteEventRecord) \
V(CODE_ALIAS, CodeAliasEventRecord)
V(SFI_MOVE, SFIMoveEventRecord)
class CodeEventRecord {
......@@ -73,6 +73,7 @@ class CodeCreateEventRecord : public CodeEventRecord {
Address start;
CodeEntry* entry;
unsigned size;
Address sfi_address;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
......@@ -95,11 +96,10 @@ class CodeDeleteEventRecord : public CodeEventRecord {
};
class CodeAliasEventRecord : public CodeEventRecord {
class SFIMoveEventRecord : public CodeEventRecord {
public:
Address start;
CodeEntry* entry;
Address code_start;
Address from;
Address to;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
......@@ -134,7 +134,7 @@ class TickSampleEventRecord BASE_EMBEDDED {
class ProfilerEventsProcessor : public Thread {
public:
explicit ProfilerEventsProcessor(ProfileGenerator* generator);
virtual ~ProfilerEventsProcessor();
virtual ~ProfilerEventsProcessor() {}
// Thread control.
virtual void Run();
......@@ -148,7 +148,8 @@ class ProfilerEventsProcessor : public Thread {
void CodeCreateEvent(Logger::LogEventsAndTags tag,
String* name,
String* resource_name, int line_number,
Address start, unsigned size);
Address start, unsigned size,
Address sfi_address);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name,
Address start, unsigned size);
......@@ -157,17 +158,12 @@ class ProfilerEventsProcessor : public Thread {
Address start, unsigned size);
void CodeMoveEvent(Address from, Address to);
void CodeDeleteEvent(Address from);
void FunctionCreateEvent(Address alias, Address start, int security_token_id);
void FunctionMoveEvent(Address from, Address to);
void FunctionDeleteEvent(Address from);
void SFIMoveEvent(Address from, Address to);
void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag,
const char* prefix, String* name,
Address start, unsigned size);
// Puts current stack into tick sample events buffer.
void AddCurrentStack();
bool IsKnownFunction(Address start);
void ProcessMovedFunctions();
void RememberMovedFunction(JSFunction* function);
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
......@@ -188,13 +184,6 @@ 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_;
......@@ -202,10 +191,6 @@ class ProfilerEventsProcessor : public Thread {
SamplingCircularQueue ticks_buffer_;
UnboundQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
unsigned enqueue_order_;
// Used from the VM thread.
HashMap* known_functions_;
List<JSFunction*> moved_functions_;
};
} } // namespace v8::internal
......@@ -251,23 +236,22 @@ class CpuProfiler {
static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name);
static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name,
Code* code,
SharedFunctionInfo *shared,
String* name);
static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo *shared,
String* source, int line);
static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, int args_count);
static void CodeMovingGCEvent() {}
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);
static void FunctionMoveEvent(Address from, Address to);
static void FunctionDeleteEvent(Address from);
static void GetterCallbackEvent(String* name, Address entry_point);
static void RegExpCodeCreateEvent(Code* code, String* source);
static void ProcessMovedFunctions();
static void SetterCallbackEvent(String* name, Address entry_point);
static void SFIMoveEvent(Address from, Address to);
static INLINE(bool is_profiling()) {
return NoBarrier_Load(&is_profiling_);
......
......@@ -847,9 +847,6 @@ static bool CompileLazyFunction(Handle<JSFunction> function,
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
if (result && function->is_compiled()) {
PROFILE(FunctionCreateEvent(*function));
}
return result;
}
......@@ -869,9 +866,7 @@ bool CompileLazyInLoop(Handle<JSFunction> function,
bool CompileOptimized(Handle<JSFunction> function, int osr_ast_id) {
CompilationInfo info(function);
info.SetOptimizing(osr_ast_id);
bool result = CompileLazyHelper(&info, KEEP_EXCEPTION);
if (result) PROFILE(FunctionCreateEvent(*function));
return result;
return CompileLazyHelper(&info, KEEP_EXCEPTION);
}
......
......@@ -515,7 +515,6 @@ bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (FLAG_log_gc) HeapProfiler::WriteSample();
if (CpuProfiler::is_profiling()) CpuProfiler::ProcessMovedFunctions();
#endif
return next_gc_likely_to_collect_more;
......@@ -1350,9 +1349,8 @@ class ScavengingVisitor : public StaticVisitorBase {
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)));
if (target->IsSharedFunctionInfo()) {
PROFILE(SFIMoveEvent(source->address(), target->address()));
}
}
#endif
......
......@@ -4099,10 +4099,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
if (!FullCodeGenerator::MakeCode(&inner_info)) return false;
shared->EnableDeoptimizationSupport(*inner_info.code());
Compiler::RecordFunctionCompilation(
Logger::FUNCTION_TAG,
Handle<String>(shared->DebugName()),
shared->start_position(),
&inner_info);
Logger::FUNCTION_TAG, &inner_info, shared);
}
// Save the pending call context and type feedback oracle. Set up new ones
......
......@@ -300,6 +300,8 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
Append("\\,");
} else if (c == '\\') {
Append("\\\\");
} else if (c == '\"') {
Append("\"\"");
} else {
Append("%lc", c);
}
......
This diff is collapsed.
......@@ -91,9 +91,7 @@ class LogMessageBuilder;
V(CODE_MOVE_EVENT, "code-move") \
V(CODE_DELETE_EVENT, "code-delete") \
V(CODE_MOVING_GC, "code-moving-gc") \
V(FUNCTION_CREATION_EVENT, "function-creation") \
V(FUNCTION_MOVE_EVENT, "function-move") \
V(FUNCTION_DELETE_EVENT, "function-delete") \
V(SFI_MOVE_EVENT, "sfi-move") \
V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
V(TICK_EVENT, "tick") \
V(REPEAT_META_EVENT, "repeat") \
......@@ -205,8 +203,15 @@ class Logger {
// Emits a code create event.
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code, const char* source);
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name);
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name,
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code, String* name);
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* name);
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* source, int line);
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count);
static void CodeMovingGCEvent();
......@@ -216,13 +221,8 @@ class Logger {
static void CodeMoveEvent(Address from, Address to);
// Emits a code delete event.
static void CodeDeleteEvent(Address from);
// Emits a function object create event.
static void FunctionCreateEvent(JSFunction* function);
static void FunctionCreateEventFromMove(JSFunction* function);
// Emits a function move event.
static void FunctionMoveEvent(Address from, Address to);
// Emits a function delete event.
static void FunctionDeleteEvent(Address from);
static void SFIMoveEvent(Address from, Address to);
static void SnapshotPositionEvent(Address addr, int pos);
......@@ -273,8 +273,6 @@ class Logger {
// Logs all compiled functions found in the heap.
static void LogCompiledFunctions();
// Logs all compiled JSFunction objects found in the heap.
static void LogFunctionObjects();
// Logs all accessor callbacks found in the heap.
static void LogAccessorCallbacks();
// Used for logging stubs found in the snapshot.
......
......@@ -2819,9 +2819,8 @@ int MarkCompactCollector::RelocateOldNonCodeObject(HeapObject* obj,
ASSERT(!HeapObject::FromAddress(new_addr)->IsCode());
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsJSFunction()) {
PROFILE(FunctionMoveEvent(old_addr, new_addr));
PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to)));
if (copied_to->IsSharedFunctionInfo()) {
PROFILE(SFIMoveEvent(old_addr, new_addr));
}
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
......@@ -2912,9 +2911,8 @@ int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
#endif
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsJSFunction()) {
PROFILE(FunctionMoveEvent(old_addr, new_addr));
PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to)));
if (copied_to->IsSharedFunctionInfo()) {
PROFILE(SFIMoveEvent(old_addr, new_addr));
}
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
......@@ -2931,8 +2929,6 @@ void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (obj->IsCode()) {
PROFILE(CodeDeleteEvent(obj->address()));
} else if (obj->IsJSFunction()) {
PROFILE(FunctionDeleteEvent(obj->address()));
}
#endif
}
......
......@@ -567,13 +567,13 @@ class TickSample {
pc(NULL),
sp(NULL),
fp(NULL),
function(NULL),
tos(NULL),
frames_count(0) {}
StateTag state; // The state of the VM.
Address pc; // Instruction pointer.
Address sp; // Stack pointer.
Address fp; // Frame pointer.
Address function; // The last called JS function.
Address pc; // Instruction pointer.
Address sp; // Stack pointer.
Address fp; // Frame pointer.
Address tos; // Top stack value (*sp).
static const int kMaxFramesCount = 64;
Address stack[kMaxFramesCount]; // Call stack.
int frames_count; // Number of captured frames.
......
......@@ -45,16 +45,6 @@ const char* StringsStorage::GetFunctionName(const char* name) {
}
CodeEntry::CodeEntry(int security_token_id)
: tag_(Logger::FUNCTION_TAG),
name_prefix_(kEmptyNamePrefix),
name_(""),
resource_name_(""),
line_number_(0),
security_token_id_(security_token_id) {
}
CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
const char* name_prefix,
const char* name,
......@@ -66,6 +56,7 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
name_(name),
resource_name_(resource_name),
line_number_(line_number),
shared_id_(0),
security_token_id_(security_token_id) {
}
......
......@@ -156,13 +156,18 @@ void CodeEntry::CopyData(const CodeEntry& source) {
uint32_t CodeEntry::GetCallUid() const {
uint32_t hash = ComputeIntegerHash(tag_);
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_);
if (shared_id_ != 0) {
hash ^= ComputeIntegerHash(
static_cast<uint32_t>(shared_id_));
} else {
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;
}
......@@ -170,10 +175,12 @@ uint32_t CodeEntry::GetCallUid() const {
bool CodeEntry::IsSameAs(CodeEntry* entry) const {
return this == entry
|| (tag_ == entry->tag_
&& name_prefix_ == entry->name_prefix_
&& name_ == entry->name_
&& resource_name_ == entry->resource_name_
&& line_number_ == entry->line_number_);
&& shared_id_ == entry->shared_id_
&& (shared_id_ != 0
|| (name_prefix_ == entry->name_prefix_
&& name_ == entry->name_
&& resource_name_ == entry->resource_name_
&& line_number_ == entry->line_number_)));
}
......@@ -458,23 +465,12 @@ void CpuProfile::Print() {
}
CodeEntry* const CodeMap::kSfiCodeEntry = NULL;
const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
CodeMap::CodeEntryInfo(NULL, 0);
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();
if (tree_.Insert(start, &locator)) {
entry->CopyData(*code_info.entry);
locator.set_value(CodeEntryInfo(entry, code_info.size));
}
}
}
CodeEntry* CodeMap::FindEntry(Address addr) {
CodeTree::Locator locator;
if (tree_.FindGreatestLessThan(addr, &locator)) {
......@@ -487,6 +483,22 @@ CodeEntry* CodeMap::FindEntry(Address addr) {
}
int CodeMap::GetSFITag(Address addr) {
CodeTree::Locator locator;
// For SFI entries, 'size' field is used to store their IDs.
if (tree_.Find(addr, &locator)) {
const CodeEntryInfo& entry = locator.value();
ASSERT(entry.entry == kSfiCodeEntry);
return entry.size;
} else {
tree_.Insert(addr, &locator);
int tag = next_sfi_tag_++;
locator.set_value(CodeEntryInfo(kSfiCodeEntry, tag));
return tag;
}
}
void CodeMap::CodeTreePrinter::Call(
const Address& key, const CodeMap::CodeEntryInfo& value) {
OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
......@@ -715,13 +727,6 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
}
CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) {
CodeEntry* entry = new CodeEntry(security_token_id);
code_entries_.Add(entry);
return entry;
}
void CpuProfilesCollection::AddPathToCurrentProfiles(
const Vector<CodeEntry*>& path) {
// As starting / stopping profiles is rare relatively to this
......@@ -784,19 +789,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
if (sample.pc != NULL) {
*entry++ = code_map_.FindEntry(sample.pc);
if (sample.function != NULL) {
*entry = code_map_.FindEntry(sample.function);
if (sample.tos != NULL) {
*entry = code_map_.FindEntry(sample.tos);
if (*entry != NULL && !(*entry)->is_js_function()) {
*entry = NULL;
} else {
CodeEntry* pc_entry = *entries.start();
if (pc_entry == NULL) {
*entry = NULL;
} else if (pc_entry->is_js_function()) {
// Use function entry in favor of pc entry, as function
// entry has security token.
*entries.start() = NULL;
}
}
entry++;
}
......
......@@ -88,7 +88,6 @@ class StringsStorage {
class CodeEntry {
public:
explicit INLINE(CodeEntry(int security_token_id));
// CodeEntry doesn't own name strings, just references them.
INLINE(CodeEntry(Logger::LogEventsAndTags tag,
const char* name_prefix,
......@@ -103,6 +102,8 @@ class CodeEntry {
INLINE(const char* name() const) { return name_; }
INLINE(const char* resource_name() const) { return resource_name_; }
INLINE(int line_number() const) { return line_number_; }
INLINE(int shared_id() const) { return shared_id_; }
INLINE(void set_shared_id(int shared_id)) { shared_id_ = shared_id; }
INLINE(int security_token_id() const) { return security_token_id_; }
INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag));
......@@ -119,6 +120,7 @@ class CodeEntry {
const char* name_;
const char* resource_name_;
int line_number_;
int shared_id_;
int security_token_id_;
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
......@@ -234,12 +236,12 @@ class CpuProfile {
class CodeMap {
public:
CodeMap() { }
CodeMap() : next_sfi_tag_(1) { }
INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size));
INLINE(void MoveCode(Address from, Address to));
INLINE(void DeleteCode(Address addr));
void AddAlias(Address start, CodeEntry* entry, Address code_start);
CodeEntry* FindEntry(Address addr);
int GetSFITag(Address addr);
void Print();
......@@ -267,7 +269,11 @@ class CodeMap {
void Call(const Address& key, const CodeEntryInfo& value);
};
// Fake CodeEntry pointer to distinguish SFI entries.
static CodeEntry* const kSfiCodeEntry;
CodeTree tree_;
int next_sfi_tag_;
DISALLOW_COPY_AND_ASSIGN(CodeMap);
};
......
......@@ -50,7 +50,7 @@ static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
i::Address frame3 = NULL) {
i::TickSample* sample = proc->TickSampleEvent();
sample->pc = frame1;
sample->function = frame1;
sample->tos = frame1;
sample->frames_count = 0;
if (frame2 != NULL) {
sample->stack[0] = frame2;
......@@ -103,7 +103,8 @@ TEST(CodeEvents) {
i::Heap::empty_string(),
0,
ToAddress(0x1000),
0x100);
0x100,
ToAddress(0x10000));
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
"bbb",
ToAddress(0x1200),
......@@ -116,8 +117,6 @@ TEST(CodeEvents) {
processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500));
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10);
processor.CodeDeleteEvent(ToAddress(0x1600));
processor.FunctionCreateEvent(ToAddress(0x1700), ToAddress(0x1000),
TokenEnumerator::kNoSecurityToken);
// Enqueue a tick event to enable code events processing.
EnqueueTickSampleEvent(&processor, ToAddress(0x1000));
......@@ -139,9 +138,6 @@ TEST(CodeEvents) {
CHECK_NE(NULL, entry4);
CHECK_EQ("ddd", entry4->name());
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1600)));
CodeEntry* entry5 = generator.code_map()->FindEntry(ToAddress(0x1700));
CHECK_NE(NULL, entry5);
CHECK_EQ(aaa_str, entry5->name());
}
......
......@@ -33,6 +33,7 @@
#include "v8.h"
#include "api.h"
#include "codegen.h"
#include "log.h"
#include "top.h"
......@@ -200,16 +201,16 @@ static void InitializeVM() {
}
static void CheckJSFunctionAtAddress(const char* func_name, Address addr) {
CHECK(i::Heap::Contains(addr));
i::Object* obj = i::HeapObject::FromAddress(addr);
CHECK(obj->IsJSFunction());
CHECK(JSFunction::cast(obj)->shared()->name()->IsString());
i::SmartPointer<char> found_name =
i::String::cast(
JSFunction::cast(
obj)->shared()->name())->ToCString();
CHECK_EQ(func_name, *found_name);
static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
i::Code* code = function->code();
return code->contains(addr);
}
static bool IsAddressWithinFuncCode(const char* func_name, Address addr) {
v8::Local<v8::Value> func = env->Global()->Get(v8_str(func_name));
CHECK(func->IsFunction());
JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
return IsAddressWithinFuncCode(js_func, addr);
}
......@@ -309,8 +310,8 @@ TEST(CFromJSStackTrace) {
// Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
CHECK_GT(sample.frames_count, base + 1);
CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[base + 0]);
CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 1]);
CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0]));
CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1]));
}
......@@ -351,9 +352,6 @@ TEST(PureJSStackTrace) {
// DoTraceHideCEntryFPAddress(EBP) [native]
// StackTracer::Trace
//
// The last JS function called. It is only visible through
// sample.function, as its return address is above captured EBP value.
CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function);
// The VM state tracking keeps track of external callbacks and puts
// them at the top of the sample stack.
......@@ -363,8 +361,8 @@ TEST(PureJSStackTrace) {
// Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
CHECK_GT(sample.frames_count, base + 1);
CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 0]);
CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[base + 1]);
CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0]));
CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1]));
}
......
......@@ -1053,10 +1053,10 @@ static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
// Skip size.
ref_s = strchr(ref_s, ',') + 1;
new_s = strchr(new_s, ',') + 1;
int ref_len = StrChrLen(ref_s, '\n');
int new_len = StrChrLen(new_s, '\n');
// If reference is anonymous (""), it's OK to have anything in new.
if (ref_len == 2) return true;
CHECK_EQ('"', ref_s[0]);
CHECK_EQ('"', new_s[0]);
int ref_len = StrChrLen(ref_s + 1, '\"');
int new_len = StrChrLen(new_s + 1, '\"');
// A special case for ErrorPrototype. Haven't yet figured out why they
// are different.
const char* error_prototype = "\"ErrorPrototype";
......@@ -1074,21 +1074,6 @@ static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
return true;
}
}
// Code objects can change their optimizability: code object may start
// as optimizable, but later be discovered to be actually not optimizable.
// Alas, we don't record this info as of now, so we allow cases when
// ref is thought to be optimizable while traverse finds it to be
// not optimizable.
if (ref_s[1] == '~') { // Code object used to be optimizable
if (new_s[1] == ' ') { // ...but later was set unoptimizable.
CHECK_EQ('"', ref_s[0]);
CHECK_EQ('"', new_s[0]);
ref_s += 2; // Cut the leading quote and the marker
ref_len -= 2;
new_s += 1; // Cut the leading quote only.
new_len -= 1;
}
}
return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
}
......
......@@ -600,13 +600,13 @@ TEST(RecordTickSample) {
// -> ccc -> aaa - sample3
TickSample sample1;
sample1.pc = ToAddress(0x1600);
sample1.function = ToAddress(0x1500);
sample1.tos = ToAddress(0x1500);
sample1.stack[0] = ToAddress(0x1510);
sample1.frames_count = 1;
generator.RecordTickSample(sample1);
TickSample sample2;
sample2.pc = ToAddress(0x1925);
sample2.function = ToAddress(0x1900);
sample2.tos = ToAddress(0x1900);
sample2.stack[0] = ToAddress(0x1780);
sample2.stack[1] = ToAddress(0x10000); // non-existent.
sample2.stack[2] = ToAddress(0x1620);
......@@ -614,7 +614,7 @@ TEST(RecordTickSample) {
generator.RecordTickSample(sample2);
TickSample sample3;
sample3.pc = ToAddress(0x1510);
sample3.function = ToAddress(0x1500);
sample3.tos = ToAddress(0x1500);
sample3.stack[0] = ToAddress(0x1910);
sample3.stack[1] = ToAddress(0x1610);
sample3.frames_count = 2;
......
......@@ -3,11 +3,9 @@ shared-library,"/lib32/libm-2.7.so",0xf7db6000,0xf7dd9000
shared-library,"ffffe000-fffff000",0xffffe000,0xfffff000
profiler,"begin",1
code-creation,Stub,0x424260,348,"CompareStub_GE"
code-creation,LazyCompile,0x2a8100,18535,"DrawQube 3d-cube.js:188"
function-creation,0x2d11b8,0x2a8100
code-creation,LazyCompile,0x480100,3908,"DrawLine 3d-cube.js:17"
function-creation,0x2d0f7c,0x480100
tick,0x424284,0xbfffeea0,0x2d0f7c,0,0x2aaaa5
tick,0x42429f,0xbfffed88,0x2d0f7c,0,0x2aacb4
code-creation,LazyCompile,0x2a8100,18535,"DrawQube 3d-cube.js:188",0xf43abcac,
code-creation,LazyCompile,0x480100,3908,"DrawLine 3d-cube.js:17",0xf43abc50,
tick,0x424284,0xbfffeea0,0x480600,0,0x2aaaa5
tick,0x42429f,0xbfffed88,0x480600,0,0x2aacb4
tick,0x48063d,0xbfffec7c,0x2d0f7c,0,0x2aaec6
profiler,"end"
......@@ -311,7 +311,7 @@ class CodeLogReader(object):
r"code-info,([^,]+),(\d+)")
_CODE_CREATE_RE = re.compile(
r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"(.*)\"(?:,(\d+))?")
r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"(.*)\"(?:,(0x[a-f0-9]+),([~*])?)?(?:,(\d+))?")
_CODE_MOVE_RE = re.compile(
r"code-move,(0x[a-f0-9]+),(0x[a-f0-9]+)")
......@@ -358,12 +358,18 @@ class CodeLogReader(object):
name = self.address_to_snapshot_name[start_address]
origin = JS_SNAPSHOT_ORIGIN
else:
name = "%s:%s" % (match.group(1), match.group(4))
tag = match.group(1)
optimization_status = match.group(6)
func_name = match.group(4)
if optimization_status:
name = "%s:%s%s" % (tag, optimization_status, func_name)
else:
name = "%s:%s" % (tag, func_name)
origin = JS_ORIGIN
if self.is_snapshot:
origin_offset = 0
else:
origin_offset = int(match.group(5))
origin_offset = int(match.group(7))
code = Code(name, start_address, end_address, origin, origin_offset)
conficting_code = self.code_map.Find(start_address)
if conficting_code:
......
......@@ -38,11 +38,6 @@ function Profile() {
this.bottomUpTree_ = new CallTree();
};
/**
* Version of profiler log.
*/
Profile.VERSION = 2;
/**
* Returns whether a function with the specified name must be skipped.
......@@ -68,6 +63,18 @@ Profile.Operation = {
};
/**
* Enum for code state regarding its dynamic optimization.
*
* @enum {number}
*/
Profile.CodeState = {
COMPILED: 0,
OPTIMIZABLE: 1,
OPTIMIZED: 2
};
/**
* Called whenever the specified operation has failed finding a function
* containing the specified address. Should be overriden by subclasses.
......@@ -134,17 +141,30 @@ Profile.prototype.addCode = function(
/**
* Creates an alias entry for a code entry.
* Registers dynamic (JIT-compiled) code entry.
*
* @param {number} aliasAddr Alias address.
* @param {number} addr Code entry address.
*/
Profile.prototype.addCodeAlias = function(
aliasAddr, addr) {
var entry = this.codeMap_.findDynamicEntryByStartAddress(addr);
if (entry) {
this.codeMap_.addCode(aliasAddr, entry);
* @param {string} type Code entry type.
* @param {string} name Code entry name.
* @param {number} start Starting address.
* @param {number} size Code entry size.
* @param {number} funcAddr Shared function object address.
* @param {Profile.CodeState} state Optimization state.
*/
Profile.prototype.addFuncCode = function(
type, name, start, size, funcAddr, state) {
// As code and functions are in the same address space,
// it is safe to put them in a single code map.
var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
if (!func) {
func = new Profile.FunctionEntry(name);
this.codeMap_.addCode(funcAddr, func);
} else if (func.name !== name) {
// Function object has been overwritten with a new one.
func.name = name;
}
var entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
this.codeMap_.addCode(start, entry);
return entry;
};
......@@ -183,25 +203,13 @@ Profile.prototype.deleteCode = function(start) {
* @param {number} from Current code entry address.
* @param {number} to New code entry address.
*/
Profile.prototype.safeMoveDynamicCode = function(from, to) {
Profile.prototype.moveFunc = function(from, to) {
if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
this.codeMap_.moveCode(from, to);
}
};
/**
* Reports about deletion of a dynamic code entry.
*
* @param {number} start Starting address.
*/
Profile.prototype.safeDeleteDynamicCode = function(start) {
if (this.codeMap_.findDynamicEntryByStartAddress(start)) {
this.codeMap_.deleteCode(start);
}
};
/**
* Retrieves a code entry by an address.
*
......@@ -383,14 +391,7 @@ Profile.DynamicCodeEntry = function(size, type, name) {
* Returns node name.
*/
Profile.DynamicCodeEntry.prototype.getName = function() {
var name = this.name;
if (name.length == 0) {
name = '<anonymous>';
} else if (name.charAt(0) == ' ') {
// An anonymous function with location: " aaa.js:10".
name = '<anonymous>' + name;
}
return this.type + ': ' + name;
return this.type + ': ' + this.name;
};
......@@ -403,9 +404,73 @@ Profile.DynamicCodeEntry.prototype.getRawName = function() {
Profile.DynamicCodeEntry.prototype.isJSFunction = function() {
return this.type == "Function" ||
this.type == "LazyCompile" ||
this.type == "Script";
return false;
};
/**
* Creates a dynamic code entry.
*
* @param {number} size Code size.
* @param {string} type Code type.
* @param {Profile.FunctionEntry} func Shared function entry.
* @param {Profile.CodeState} state Code optimization state.
* @constructor
*/
Profile.DynamicFuncCodeEntry = function(size, type, func, state) {
CodeMap.CodeEntry.call(this, size);
this.type = type;
this.func = func;
this.state = state;
};
Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"];
/**
* Returns node name.
*/
Profile.DynamicFuncCodeEntry.prototype.getName = function() {
var name = this.func.getName();
return this.type + ': ' + Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state] + name;
};
/**
* Returns raw node name (without type decoration).
*/
Profile.DynamicFuncCodeEntry.prototype.getRawName = function() {
return this.func.getName();
};
Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() {
return true;
};
/**
* Creates a shared function object entry.
*
* @param {string} name Function name.
* @constructor
*/
Profile.FunctionEntry = function(name) {
CodeMap.CodeEntry.call(this, 0, name);
};
/**
* Returns node name.
*/
Profile.FunctionEntry.prototype.getName = function() {
var name = this.name;
if (name.length == 0) {
name = '<anonymous>';
} else if (name.charAt(0) == ' ') {
// An anonymous function with location: " aaa.js:10".
name = '<anonymous>' + name;
}
return name;
};
......
......@@ -57,10 +57,23 @@ function readFile(fileName) {
}
/**
* Parser for dynamic code optimization state.
*/
function parseState(s) {
switch (s) {
case "": return Profile.CodeState.COMPILED;
case "~": return Profile.CodeState.OPTIMIZABLE;
case "*": return Profile.CodeState.OPTIMIZED;
}
throw new Error("unknown code state: " + s);
}
function SnapshotLogProcessor() {
LogReader.call(this, {
'code-creation': {
parsers: [null, parseInt, parseInt, null],
parsers: [null, parseInt, parseInt, null, 'var-args'],
processor: this.processCodeCreation },
'code-move': { parsers: [parseInt, parseInt],
processor: this.processCodeMove },
......@@ -69,6 +82,7 @@ function SnapshotLogProcessor() {
'function-creation': null,
'function-move': null,
'function-delete': null,
'sfi-move': null,
'snapshot-pos': { parsers: [parseInt, parseInt],
processor: this.processSnapshotPosition }});
......@@ -93,8 +107,14 @@ inherits(SnapshotLogProcessor, LogReader);
SnapshotLogProcessor.prototype.processCodeCreation = function(
type, start, size, name) {
var entry = this.profile_.addCode(type, name, start, size);
type, start, size, name, maybe_func) {
if (maybe_func.length) {
var funcAddr = parseInt(maybe_func[0]);
var state = parseState(maybe_func[1]);
this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
} else {
this.profile_.addCode(type, name, start, size);
}
};
......@@ -131,18 +151,14 @@ function TickProcessor(
'shared-library': { parsers: [null, parseInt, parseInt],
processor: this.processSharedLibrary },
'code-creation': {
parsers: [null, parseInt, parseInt, null],
parsers: [null, parseInt, parseInt, null, 'var-args'],
processor: this.processCodeCreation },
'code-move': { parsers: [parseInt, parseInt],
processor: this.processCodeMove },
'code-delete': { parsers: [parseInt],
processor: this.processCodeDelete },
'function-creation': { parsers: [parseInt, parseInt],
processor: this.processFunctionCreation },
'function-move': { parsers: [parseInt, parseInt],
'sfi-move': { parsers: [parseInt, parseInt],
processor: this.processFunctionMove },
'function-delete': { parsers: [parseInt],
processor: this.processFunctionDelete },
'snapshot-pos': { parsers: [parseInt, parseInt],
processor: this.processSnapshotPosition },
'tick': { parsers: [parseInt, parseInt, parseInt, parseInt, 'var-args'],
......@@ -155,6 +171,9 @@ function TickProcessor(
processor: this.processJSProducer },
// Ignored events.
'profiler': null,
'function-creation': null,
'function-move': null,
'function-delete': null,
'heap-sample-stats': null,
'heap-sample-item': null,
'heap-js-cons-item': null,
......@@ -285,9 +304,15 @@ TickProcessor.prototype.processSharedLibrary = function(
TickProcessor.prototype.processCodeCreation = function(
type, start, size, name) {
type, start, size, name, maybe_func) {
name = this.deserializedEntriesNames_[start] || name;
var entry = this.profile_.addCode(type, name, start, size);
if (maybe_func.length) {
var funcAddr = parseInt(maybe_func[0]);
var state = parseState(maybe_func[1]);
this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
} else {
this.profile_.addCode(type, name, start, size);
}
};
......@@ -301,19 +326,8 @@ TickProcessor.prototype.processCodeDelete = function(start) {
};
TickProcessor.prototype.processFunctionCreation = function(
functionAddr, codeAddr) {
this.profile_.addCodeAlias(functionAddr, codeAddr);
};
TickProcessor.prototype.processFunctionMove = function(from, to) {
this.profile_.safeMoveDynamicCode(from, to);
};
TickProcessor.prototype.processFunctionDelete = function(start) {
this.profile_.safeDeleteDynamicCode(start);
this.profile_.moveFunc(from, to);
};
......@@ -330,7 +344,7 @@ TickProcessor.prototype.includeTick = function(vmState) {
};
TickProcessor.prototype.processTick = function(pc, sp, func, vmState, stack) {
TickProcessor.prototype.processTick = function(pc, sp, tos, vmState, stack) {
this.ticks_.total++;
if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
if (!this.includeTick(vmState)) {
......@@ -338,19 +352,14 @@ TickProcessor.prototype.processTick = function(pc, sp, func, vmState, stack) {
return;
}
if (func) {
var funcEntry = this.profile_.findEntry(func);
if (tos) {
var funcEntry = this.profile_.findEntry(tos);
if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
func = 0;
} else {
var currEntry = this.profile_.findEntry(pc);
if (!currEntry || !currEntry.isJSFunction || currEntry.isJSFunction()) {
func = 0;
}
tos = 0;
}
}
this.profile_.recordTick(this.processStack(pc, func, stack));
this.profile_.recordTick(this.processStack(pc, tos, stack));
};
......
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