}
if (script->name()->IsString()) {
- PROFILE(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
- *code, String::cast(script->name())));
+ PROFILE(CodeCreateEvent(
+ is_eval ? Logger::EVAL_TAG :
+ Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *code, String::cast(script->name())));
OPROFILE(CreateNativeCodeRegion(String::cast(script->name()),
code->instruction_start(),
code->instruction_size()));
} else {
- PROFILE(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
- *code, ""));
+ PROFILE(CodeCreateEvent(
+ is_eval ? Logger::EVAL_TAG :
+ Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *code, ""));
OPROFILE(CreateNativeCodeRegion(is_eval ? "Eval" : "Script",
code->instruction_start(),
code->instruction_size()));
if (script->name()->IsString()) {
int line_num = GetScriptLineNumber(script, start_position) + 1;
USE(line_num);
- PROFILE(CodeCreateEvent(tag, *code, *func_name,
+ PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
+ *code, *func_name,
String::cast(script->name()), line_num));
OPROFILE(CreateNativeCodeRegion(*func_name,
String::cast(script->name()),
code->instruction_start(),
code->instruction_size()));
} else {
- PROFILE(CodeCreateEvent(tag, *code, *func_name));
+ PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
+ *code, *func_name));
OPROFILE(CreateNativeCodeRegion(*func_name,
code->instruction_start(),
code->instruction_size()));
return &evt->sample;
}
+
+bool ProfilerEventsProcessor::FilterOutCodeCreateEvent(
+ Logger::LogEventsAndTags tag) {
+ // In browser mode, leave only callbacks and non-native JS entries.
+ // We filter out regular expressions as currently we can't tell
+ // whether they origin from native scripts, so let's not confise people by
+ // showing them weird regexes they didn't wrote.
+ return FLAG_prof_browser_mode
+ && (tag != Logger::CALLBACK_TAG
+ && tag != Logger::FUNCTION_TAG
+ && tag != Logger::LAZY_COMPILE_TAG
+ && tag != Logger::SCRIPT_TAG);
+}
+
} } // namespace v8::internal
#endif // ENABLE_CPP_PROFILES_PROCESSOR
const char* prefix,
String* name,
Address start) {
+ if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
int line_number,
Address start,
unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
const char* name,
Address start,
unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
int args_count,
Address start,
unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
String* name,
Address start,
unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
class TickSampleEventRecord BASE_EMBEDDED {
public:
// In memory, the first machine word of a TickSampleEventRecord will be the
- // first entry of TickSample, that is -- a program counter field.
+ // first entry of TickSample, that is -- the VM state field.
// TickSample is put first, because 'order' can become equal to
- // SamplingCircularQueue::kClear, while program counter can't.
+ // SamplingCircularQueue::kClear, while VM state can't, see
+ // the definition of 'enum StateTag'.
TickSample sample;
unsigned order;
bool ProcessCodeEvent(unsigned* dequeue_order);
bool ProcessTicks(unsigned dequeue_order);
+ INLINE(static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag));
+
ProfileGenerator* generator_;
bool running_;
CircularQueue<CodeEventsContainer> events_buffer_;
static void SetterCallbackEvent(String* name, Address entry_point);
static INLINE(bool is_profiling()) {
- ASSERT(singleton_ != NULL);
- return singleton_->processor_ != NULL;
+ return singleton_ != NULL && singleton_->processor_ != NULL;
}
private:
V(EXTERNAL)
enum StateTag {
+#ifdef ENABLE_CPP_PROFILES_PROCESSOR
+ // This is to ensure that VM state field value of TickSample
+ // never gets equal to SamplingCircularQueue::kClear.
+ NULL_STATE = 0,
+#endif
#define DEF_STATE_TAG(name) name,
STATE_TAG_LIST(DEF_STATE_TAG)
#undef DEF_STATE_TAG
#define V8_LOG_INL_H_
#include "log.h"
+#include "cpu-profiler.h"
namespace v8 {
namespace internal {
}
}
-VMState::VMState(StateTag state) : disabled_(true), external_callback_(NULL) {
- if (!Logger::is_logging()) {
+VMState::VMState(StateTag state)
+ : disabled_(true),
+ state_(OTHER),
+ external_callback_(NULL) {
+ if (!Logger::is_logging() && !CpuProfiler::is_profiling()) {
return;
- }
+}
disabled_ = false;
#if !defined(ENABLE_HEAP_PROTECTION)
}
#endif
}
+
+Logger::LogEventsAndTags Logger::ToNativeByScript(Logger::LogEventsAndTags tag,
+ Script* script) {
+#ifdef ENABLE_CPP_PROFILES_PROCESSOR
+ if ((tag == FUNCTION_TAG || tag == LAZY_COMPILE_TAG || tag == SCRIPT_TAG)
+ && script->type()->value() == Script::TYPE_NATIVE) {
+ switch (tag) {
+ case FUNCTION_TAG: return NATIVE_FUNCTION_TAG;
+ case LAZY_COMPILE_TAG: return NATIVE_LAZY_COMPILE_TAG;
+ case SCRIPT_TAG: return NATIVE_SCRIPT_TAG;
+ default: return tag;
+ }
+ } else {
+ return tag;
+ }
+#else
+ return tag;
+#endif
+}
+
#endif
Handle<String> script_name(String::cast(script->name()));
int line_num = GetScriptLineNumber(script, shared->start_position());
if (line_num > 0) {
- PROFILE(CodeCreateEvent(Logger::LAZY_COMPILE_TAG,
- shared->code(), *func_name,
- *script_name, line_num + 1));
+ PROFILE(CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ shared->code(), *func_name,
+ *script_name, line_num + 1));
} else {
- // Can't distinguish enum and script here, so always use Script.
- PROFILE(CodeCreateEvent(Logger::SCRIPT_TAG,
- shared->code(), *script_name));
+ // Can't distinguish eval and script here, so always use Script.
+ PROFILE(CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ shared->code(), *script_name));
}
} else {
PROFILE(CodeCreateEvent(
- Logger::LAZY_COMPILE_TAG, shared->code(), *func_name));
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ shared->code(), *func_name));
}
} else if (shared->IsApiFunction()) {
// API function.
enum LogEventsAndTags {
LOG_EVENTS_AND_TAGS_LIST(DECLARE_ENUM)
NUMBER_OF_LOG_EVENTS
+#ifdef ENABLE_CPP_PROFILES_PROCESSOR
+ , NATIVE_FUNCTION_TAG
+ , NATIVE_LAZY_COMPILE_TAG
+ , NATIVE_SCRIPT_TAG
+#endif
};
#undef DECLARE_ENUM
// Used for logging stubs found in the snapshot.
static void LogCodeObjects();
+ // Converts tag to a corresponding NATIVE_... if the script is native.
+ INLINE(static LogEventsAndTags ToNativeByScript(LogEventsAndTags, Script*));
+
private:
// Profiler's sampling interval (in milliseconds).
if (active_sampler_ == NULL) return;
#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- if (Logger::state() == GC || !IsVmThread()) return;
-
- TickSample* sample = NULL;
+ TickSample* sample = CpuProfiler::TickSampleEvent();
+ if (sample == NULL) return;
+ sample->pc = NULL; // Impossible value if sampling succeeds.
+ sample->frames_count = 0;
#else
TickSample sample_obj;
TickSample* sample = &sample_obj;
+#endif
// We always sample the VM state.
sample->state = Logger::state();
-#endif
-
// If profiling, we extract the current pc and sp.
if (active_sampler_->IsProfiling()) {
// Extracting the sample from the context is extremely machine dependent.
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
mcontext_t& mcontext = ucontext->uc_mcontext;
-#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- sample = CpuProfiler::TickSampleEvent();
-#endif
- if (sample != NULL) {
#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
#elif V8_HOST_ARCH_ARM
// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
#else
- sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
- sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
- sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
+ sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
+ sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
+ sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
#endif
#elif V8_HOST_ARCH_MIPS
// Implement this on MIPS.
- UNIMPLEMENTED();
+ UNIMPLEMENTED();
#endif
- if (IsVmThread()) {
- active_sampler_->SampleStack(sample);
- }
+ if (IsVmThread()) {
+ active_sampler_->SampleStack(sample);
}
}
#ifndef ENABLE_CPP_PROFILES_PROCESSOR
// Loop until the sampler is disengaged, keeping the specified samling freq.
for ( ; sampler_->IsActive(); OS::Sleep(sampler_->interval_)) {
#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- if (Logger::state() == GC) continue;
-
- TickSample* sample = NULL;
+ TickSample* sample = CpuProfiler::TickSampleEvent();
+ if (sample == NULL) continue;
+ sample->pc = NULL; // Impossible value if sampling succeeds.
+ sample->frames_count = 0;
#else
TickSample sample_obj;
TickSample* sample = &sample_obj;
+#endif // ENABLE_CPP_PROFILES_PROCESSOR
// We always sample the VM state.
sample->state = Logger::state();
-#endif // ENABLE_CPP_PROFILES_PROCESSOR
-
// If profiling, we record the pc and sp of the profiled thread.
if (sampler_->IsProfiling()
&& KERN_SUCCESS == thread_suspend(profiled_thread_)) {
flavor,
reinterpret_cast<natural_t*>(&state),
&count) == KERN_SUCCESS) {
-#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- sample = CpuProfiler::TickSampleEvent();
-#endif
- if (sample != NULL) {
- sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
- sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
- sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
- sampler_->SampleStack(sample);
- }
+ sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
+ sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
+ sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
+ sampler_->SampleStack(sample);
}
thread_resume(profiled_thread_);
}
// Loop until the sampler is disengaged, keeping the specified samling freq.
for ( ; sampler_->IsActive(); Sleep(sampler_->interval_)) {
#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- if (Logger::state() == GC) continue;
-
- TickSample* sample = NULL;
+ TickSample* sample = CpuProfiler::TickSampleEvent();
+ if (sample == NULL) continue;
+ sample->pc = NULL; // Impossible value if sampling succeeds.
+ sample->frames_count = 0;
#else
TickSample sample_obj;
TickSample* sample = &sample_obj;
+#endif // ENABLE_CPP_PROFILES_PROCESSOR
// We always sample the VM state.
sample->state = Logger::state();
-#endif // ENABLE_CPP_PROFILES_PROCESSOR
-
// If profiling, we record the pc and sp of the profiled thread.
if (sampler_->IsProfiling()
&& SuspendThread(profiled_thread_) != (DWORD)-1) {
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(profiled_thread_, &context) != 0) {
-#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- sample = CpuProfiler::TickSampleEvent();
-#endif
- if (sample != NULL) {
#if V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(context.Rip);
- sample->sp = reinterpret_cast<Address>(context.Rsp);
- sample->fp = reinterpret_cast<Address>(context.Rbp);
+ sample->pc = reinterpret_cast<Address>(context.Rip);
+ sample->sp = reinterpret_cast<Address>(context.Rsp);
+ sample->fp = reinterpret_cast<Address>(context.Rbp);
#else
- sample->pc = reinterpret_cast<Address>(context.Eip);
- sample->sp = reinterpret_cast<Address>(context.Esp);
- sample->fp = reinterpret_cast<Address>(context.Ebp);
+ sample->pc = reinterpret_cast<Address>(context.Eip);
+ sample->sp = reinterpret_cast<Address>(context.Esp);
+ sample->fp = reinterpret_cast<Address>(context.Ebp);
#endif
- sampler_->SampleStack(sample);
- }
+ sampler_->SampleStack(sample);
}
ResumeThread(profiled_thread_);
}
class TickSample {
public:
TickSample()
- : pc(NULL),
+ : state(OTHER),
+ pc(NULL),
sp(NULL),
fp(NULL),
function(NULL),
- state(OTHER),
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.
- StateTag state; // The state of the VM.
static const int kMaxFramesCount = 64;
Address stack[kMaxFramesCount]; // Call stack.
int frames_count; // Number of captured frames.
}
-bool CodeEntry::is_js_function() const {
- return tag_ == Logger::FUNCTION_TAG
- || tag_ == Logger::LAZY_COMPILE_TAG
- || tag_ == Logger::SCRIPT_TAG;
+bool CodeEntry::is_js_function_tag(Logger::LogEventsAndTags tag) {
+ return tag == Logger::FUNCTION_TAG
+ || tag == Logger::LAZY_COMPILE_TAG
+ || tag == Logger::SCRIPT_TAG
+ || tag == Logger::NATIVE_FUNCTION_TAG
+ || tag == Logger::NATIVE_LAZY_COMPILE_TAG
+ || tag == Logger::NATIVE_SCRIPT_TAG;
}
return current_profiles_.length() == 1;
}
+
+const char* CpuProfilesCollection::GetFunctionName(String* name) {
+ return GetFunctionName(GetName(name));
+}
+
+
+const char* CpuProfilesCollection::GetFunctionName(const char* name) {
+ return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
+}
+
+
+CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
+ switch (tag) {
+ case GC:
+ return gc_entry_;
+ case JS:
+ case COMPILER:
+ // DOM events handlers are reported as OTHER / EXTERNAL entries.
+ // To avoid confusing people, let's put all these entries into
+ // one bucket.
+ case OTHER:
+ case EXTERNAL:
+ return program_entry_;
+ default: return NULL;
+ }
+}
+
} } // namespace v8::internal
#endif // ENABLE_CPP_PROFILES_PROCESSOR
const char* name) {
CodeEntry* entry = new CodeEntry(tag,
CodeEntry::kEmptyNamePrefix,
- name,
+ GetFunctionName(name),
"",
v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
}
-const char* CpuProfilesCollection::GetFunctionName(String* name) {
- const char* maybe_empty_name = GetName(name);
- return strlen(maybe_empty_name) > 0 ?
- maybe_empty_name : "(anonymous function)";
-}
-
-
const char* CpuProfilesCollection::GetName(String* name) {
if (name->IsString()) {
char* c_name =
}
+const char* ProfileGenerator::kAnonymousFunctionName = "(anonymous function)";
+const char* ProfileGenerator::kProgramEntryName = "(program)";
+const char* ProfileGenerator::kGarbageCollectorEntryName =
+ "(garbage collector)";
+
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
: profiles_(profiles),
program_entry_(
- profiles->NewCodeEntry(Logger::FUNCTION_TAG, "(program)")) {
+ profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
+ gc_entry_(
+ profiles->NewCodeEntry(Logger::BUILTIN_TAG,
+ kGarbageCollectorEntryName)) {
}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
- // Allocate space for stack frames + pc + function + (program).
+ // Allocate space for stack frames + pc + function + vm-state.
ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
+ // As actual number of decoded code entries may vary, initialize
+ // entries vector with NULL values.
CodeEntry** entry = entries.start();
- *entry++ = code_map_.FindEntry(sample.pc);
+ memset(entry, 0, entries.length() * sizeof(*entry));
+ if (sample.pc != NULL) {
+ *entry++ = code_map_.FindEntry(sample.pc);
- if (sample.function != NULL) {
- *entry = code_map_.FindEntry(sample.function);
- if (*entry != NULL && !(*entry)->is_js_function()) {
- *entry = NULL;
- } else {
- CodeEntry* pc_entry = *entries.start();
- if (pc_entry == NULL || pc_entry->is_js_function())
+ if (sample.function != NULL) {
+ *entry = code_map_.FindEntry(sample.function);
+ if (*entry != NULL && !(*entry)->is_js_function()) {
*entry = NULL;
+ } else {
+ CodeEntry* pc_entry = *entries.start();
+ if (pc_entry == NULL || pc_entry->is_js_function())
+ *entry = NULL;
+ }
+ entry++;
}
- entry++;
- } else {
- *entry++ = NULL;
- }
- for (const Address *stack_pos = sample.stack,
- *stack_end = stack_pos + sample.frames_count;
- stack_pos != stack_end;
- ++stack_pos) {
- *entry++ = code_map_.FindEntry(*stack_pos);
+ for (const Address *stack_pos = sample.stack,
+ *stack_end = stack_pos + sample.frames_count;
+ stack_pos != stack_end;
+ ++stack_pos) {
+ *entry++ = code_map_.FindEntry(*stack_pos);
+ }
}
- // WebKit CPU profiles visualization requires "(program)" to be the
- // topmost entry.
- *entry++ = FLAG_prof_browser_mode ? program_entry_ : NULL;
+ if (FLAG_prof_browser_mode) {
+ // Put VM state as the topmost entry.
+ *entry++ = EntryForVMState(sample.state);
+ }
profiles_->AddPathToCurrentProfiles(entries);
}
const char* resource_name,
int line_number));
- INLINE(bool is_js_function() const);
+ INLINE(bool is_js_function() const) { return is_js_function_tag(tag_); }
INLINE(const char* name_prefix() const) { return name_prefix_; }
INLINE(bool has_name_prefix() const) { return name_prefix_[0] != '\0'; }
INLINE(const char* name() const) { return name_; }
INLINE(int line_number() const) { return line_number_; }
INLINE(unsigned call_uid() const) { return call_uid_; }
+ INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag));
+
static const char* kEmptyNamePrefix;
private:
void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);
private:
- const char* GetFunctionName(String* name);
+ INLINE(const char* GetFunctionName(String* name));
+ INLINE(const char* GetFunctionName(const char* name));
const char* GetName(String* name);
const char* GetName(int args_count);
INLINE(CodeMap* code_map()) { return &code_map_; }
+ static const char* kAnonymousFunctionName;
+ static const char* kProgramEntryName;
+ static const char* kGarbageCollectorEntryName;
+
private:
+ INLINE(CodeEntry* EntryForVMState(StateTag tag));
+
CpuProfilesCollection* profiles_;
CodeMap code_map_;
CodeEntry* program_entry_;
+ CodeEntry* gc_entry_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};
i::Address frame2 = NULL,
i::Address frame3 = NULL) {
i::TickSample* sample = proc->TickSampleEvent();
+ sample->state = i::OTHER;
sample->pc = frame1;
sample->function = frame1;
sample->frames_count = 0;