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);
}
}
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,
? 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.
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);
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());
}
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
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()));
}
}
#endif
static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
- Handle<String> name,
- int start_position,
- CompilationInfo* info);
+ CompilationInfo* info,
+ Handle<SharedFunctionInfo> shared);
};
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));
+ }
}
}
-void CodeAliasEventRecord::UpdateCodeMap(CodeMap* code_map) {
- code_map->AddAlias(start, entry, code_start);
+void SFIMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
+ code_map->MoveCode(from, to);
}
ticks_buffer_(sizeof(TickSampleEventRecord),
kTickSamplesBufferChunkSize,
kTickSamplesBufferChunksCount),
- enqueue_order_(0),
- known_functions_(new HashMap(AddressesMatch)) {
-}
-
-
-ProfilerEventsProcessor::~ProfilerEventsProcessor() {
- delete known_functions_;
+ enqueue_order_(0) {
}
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, prefix, name);
rec->size = 1;
+ rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
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_;
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);
}
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name);
rec->size = size;
+ rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, args_count);
rec->size = size;
+ rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec);
}
}
-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);
}
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);
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());
}
}
-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);
}
FLAG_log_code = saved_log_code_flag;
}
Logger::LogCompiledFunctions();
- Logger::LogFunctionObjects();
Logger::LogAccessorCallbacks();
}
// Enable stack sampling.
V(CODE_CREATION, CodeCreateEventRecord) \
V(CODE_MOVE, CodeMoveEventRecord) \
V(CODE_DELETE, CodeDeleteEventRecord) \
- V(CODE_ALIAS, CodeAliasEventRecord)
+ V(SFI_MOVE, SFIMoveEventRecord)
class CodeEventRecord {
Address start;
CodeEntry* entry;
unsigned size;
+ Address sfi_address;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
};
-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));
};
class ProfilerEventsProcessor : public Thread {
public:
explicit ProfilerEventsProcessor(ProfileGenerator* generator);
- virtual ~ProfilerEventsProcessor();
+ virtual ~ProfilerEventsProcessor() {}
// Thread control.
virtual void Run();
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);
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
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_;
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
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_);
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
- if (result && function->is_compiled()) {
- PROFILE(FunctionCreateEvent(*function));
- }
return result;
}
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);
}
#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;
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
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
Append("\\,");
} else if (c == '\\') {
Append("\\\\");
+ } else if (c == '\"') {
+ Append("\"\"");
} else {
Append("%lc", c);
}
// StackTracer implementation
//
void StackTracer::Trace(TickSample* sample) {
- sample->function = NULL;
+ sample->tos = NULL;
sample->frames_count = 0;
// Avoid collecting traces while doing GC.
return;
}
- const Address function_address =
- sample->fp + JavaScriptFrameConstants::kFunctionOffset;
- if (SafeStackFrameIterator::IsWithinBounds(sample->sp, js_entry_sp,
- function_address)) {
- Object* object = Memory::Object_at(function_address);
- if (object->IsHeapObject()) {
- sample->function = HeapObject::cast(object)->address();
- }
- }
+ // Sample potential return address value for frameless invocation of
+ // stubs (we'll figure out later, if this value makes sense).
+ sample->tos = Memory::Address_at(sample->sp);
int i = 0;
const Address callback = Top::external_callback();
SafeStackTraceFrameIterator it(sample->fp, sample->sp,
sample->sp, js_entry_sp);
while (!it.done() && i < TickSample::kMaxFramesCount) {
- Object* object = it.frame()->function_slot_object();
- if (object->IsHeapObject()) {
- sample->stack[i++] = HeapObject::cast(object)->address();
- }
+ sample->stack[i++] = it.frame()->pc();
it.Advance();
}
sample->frames_count = i;
}
-#ifdef ENABLE_LOGGING_AND_PROFILING
-static const char* ComputeMarker(Code* code) {
- switch (code->kind()) {
- case Code::FUNCTION: return code->optimizable() ? "~" : "";
- case Code::OPTIMIZED_FUNCTION: return "*";
- default: return "";
- }
-}
-#endif
-
-
void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
const char* comment) {
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
- msg.Append(",%d,\"%s", code->ExecutableSize(), ComputeMarker(code));
+ msg.Append(",%d,\"", code->ExecutableSize());
for (const char* p = comment; *p != '\0'; p++) {
if (*p == '"') {
msg.Append('\\');
}
-void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) {
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ String* name) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (name != NULL) {
+ SmartPointer<char> str =
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ CodeCreateEvent(tag, code, *str);
+ } else {
+ CodeCreateEvent(tag, code, "");
+ }
+#endif
+}
+
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+// ComputeMarker must only be used when SharedFunctionInfo is known.
+static const char* ComputeMarker(Code* code) {
+ switch (code->kind()) {
+ case Code::FUNCTION: return code->optimizable() ? "~" : "";
+ case Code::OPTIMIZED_FUNCTION: return "*";
+ default: return "";
+ }
+}
+#endif
+
+
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_code) return;
+ if (code == Builtins::builtin(Builtins::LazyCompile)) return;
LogMessageBuilder msg;
SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
- msg.Append(",%d,\"%s%s\"", code->ExecutableSize(), ComputeMarker(code), *str);
+ msg.Append(",%d,\"%s\",", code->ExecutableSize(), *str);
+ msg.AppendAddress(shared->address());
+ msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
}
+// Although, it is possible to extract source and line from
+// the SharedFunctionInfo object, we left it to caller
+// to leave logging functions free from heap allocations.
void Logger::CodeCreateEvent(LogEventsAndTags tag,
- Code* code, String* name,
+ Code* code,
+ SharedFunctionInfo* shared,
String* source, int line) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
- SmartPointer<char> str =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ SmartPointer<char> name =
+ shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
SmartPointer<char> sourcestr =
source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
- msg.Append(",%d,\"%s%s %s:%d\"",
+ msg.Append(",%d,\"%s %s:%d\",",
code->ExecutableSize(),
- ComputeMarker(code),
- *str,
+ *name,
*sourcestr,
line);
+ msg.AppendAddress(shared->address());
+ msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
}
-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.
- if (!Log::IsEnabled() || !FLAG_log_code) return;
- LogMessageBuilder msg;
- msg.Append("%s,", kLogEventsNames[FUNCTION_CREATION_EVENT]);
- msg.AppendAddress(function->address());
- msg.Append(',');
- msg.AppendAddress(function->unchecked_code()->address());
- msg.Append('\n');
- msg.WriteToLogFile();
-#endif
-}
-
-
-void Logger::FunctionCreateEventFromMove(JSFunction* function) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile)) {
- FunctionCreateEvent(function);
- }
-#endif
-}
-
-
-void Logger::FunctionMoveEvent(Address from, Address to) {
+void Logger::SFIMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- MoveEventInternal(FUNCTION_MOVE_EVENT, from, to);
-#endif
-}
-
-
-void Logger::FunctionDeleteEvent(Address from) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- DeleteEventInternal(FUNCTION_DELETE_EVENT, from);
+ MoveEventInternal(SFI_MOVE_EVENT, from, to);
#endif
}
msg.Append(',');
msg.AppendAddress(sample->sp);
msg.Append(',');
- msg.AppendAddress(sample->function);
+ msg.AppendAddress(sample->tos);
msg.Append(",%d", static_cast<int>(sample->state));
if (overflow) {
msg.Append(",overflow");
LOG(UncheckedStringEvent("profiler", "resume"));
FLAG_log_code = true;
LogCompiledFunctions();
- LogFunctionObjects();
LogAccessorCallbacks();
if (!FLAG_sliding_state_window && !ticker_->IsActive()) {
ticker_->Start();
// During iteration, there can be heap allocation due to
// GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) {
+ if (*code_objects[i] == Builtins::builtin(Builtins::LazyCompile)) continue;
Handle<SharedFunctionInfo> shared = sfis[i];
- Handle<String> name(String::cast(shared->name()));
- Handle<String> func_name(name->length() > 0 ?
- *name : shared->inferred_name());
+ Handle<String> func_name(shared->DebugName());
if (shared->script()->IsScript()) {
Handle<Script> script(Script::cast(shared->script()));
if (script->name()->IsString()) {
if (line_num > 0) {
PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code_objects[i], *func_name,
+ *code_objects[i], *shared,
*script_name, line_num + 1));
} else {
// Can't distinguish eval and script here, so always use Script.
PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
- *code_objects[i], *script_name));
+ *code_objects[i], *shared, *script_name));
}
} else {
PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code_objects[i], *func_name));
+ *code_objects[i], *shared, *func_name));
}
} else if (shared->IsApiFunction()) {
// API function.
}
} else {
PROFILE(CodeCreateEvent(
- Logger::LAZY_COMPILE_TAG, *code_objects[i], *func_name));
+ Logger::LAZY_COMPILE_TAG, *code_objects[i], *shared, *func_name));
}
}
}
-void Logger::LogFunctionObjects() {
- AssertNoAllocation no_alloc;
- HeapIterator iterator;
- for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
- if (!obj->IsJSFunction()) continue;
- JSFunction* jsf = JSFunction::cast(obj);
- if (!jsf->is_compiled()) continue;
- PROFILE(FunctionCreateEvent(jsf));
- }
-}
-
-
void Logger::LogAccessorCallbacks() {
AssertNoAllocation no_alloc;
HeapIterator iterator;
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") \
// 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();
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);
// 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.
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));
#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));
#ifdef ENABLE_LOGGING_AND_PROFILING
if (obj->IsCode()) {
PROFILE(CodeDeleteEvent(obj->address()));
- } else if (obj->IsJSFunction()) {
- PROFILE(FunctionDeleteEvent(obj->address()));
}
#endif
}
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.
}
-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,
name_(name),
resource_name_(resource_name),
line_number_(line_number),
+ shared_id_(0),
security_token_id_(security_token_id) {
}
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;
}
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_)));
}
}
+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)) {
}
+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());
}
-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
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++;
}
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,
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));
const char* name_;
const char* resource_name_;
int line_number_;
+ int shared_id_;
int security_token_id_;
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
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();
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);
};
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;
i::Heap::empty_string(),
0,
ToAddress(0x1000),
- 0x100);
+ 0x100,
+ ToAddress(0x10000));
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
"bbb",
ToAddress(0x1200),
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));
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());
}
#include "v8.h"
+#include "api.h"
#include "codegen.h"
#include "log.h"
#include "top.h"
}
-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);
}
// 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]));
}
// 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.
// 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]));
}
// 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";
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;
}
// -> 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);
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;
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"
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]+)")
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:
this.bottomUpTree_ = new CallTree();
};
-/**
- * Version of profiler log.
- */
-Profile.VERSION = 2;
-
/**
* Returns whether a function with the specified name must be skipped.
/**
+ * 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.
* See the Profile.Operation enum for the list of
/**
- * 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;
};
* @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.
*
* @param {number} addr Entry address.
* 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;
};
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;
};
}
+/**
+ * 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 },
'function-creation': null,
'function-move': null,
'function-delete': null,
+ 'sfi-move': null,
'snapshot-pos': { parsers: [parseInt, parseInt],
processor: this.processSnapshotPosition }});
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);
+ }
};
'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'],
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,
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);
+ }
};
};
-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);
};
};
-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)) {
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));
};