void Isolate::GetStackSample(const RegisterState& state, void** frames,
size_t frames_limit, SampleInfo* sample_info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
- i::TickSample::GetStackSample(isolate, state, frames, frames_limit,
- sample_info);
+ i::TickSample::GetStackSample(isolate, state, i::TickSample::kSkipCEntryFrame,
+ frames, frames_limit, sample_info);
}
regs.fp = frame->fp();
regs.pc = frame->pc();
}
- record.sample.Init(isolate, regs);
+ record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame);
ticks_from_vm_buffer_.Enqueue(record);
}
// Save the frame pointer and the context in top.
ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate());
ExternalReference context_address(Isolate::kContextAddress, isolate());
+ ExternalReference c_function_address(Isolate::kCFunctionAddress, isolate());
mov(Operand::StaticVariable(c_entry_fp_address), ebp);
mov(Operand::StaticVariable(context_address), esi);
+ mov(Operand::StaticVariable(c_function_address), ebx);
}
#define FOR_EACH_ISOLATE_ADDRESS_NAME(C) \
C(Handler, handler) \
C(CEntryFP, c_entry_fp) \
+ C(CFunction, c_function) \
C(Context, context) \
C(PendingException, pending_exception) \
C(ExternalCaughtException, external_caught_exception) \
// Stack.
Address c_entry_fp_; // the frame pointer of the top c entry frame
Address handler_; // try-blocks are chained through the stack
+ Address c_function_; // C function that was called at c entry.
// Throwing an exception may cause a Promise rejection. For this purpose
// we keep track of a stack of nested promises and the corresponding
return thread->c_entry_fp_;
}
static Address handler(ThreadLocalTop* thread) { return thread->handler_; }
+ Address c_function() { return thread_local_top_.c_function_; }
inline Address* c_entry_fp_address() {
return &thread_local_top_.c_entry_fp_;
}
inline Address* handler_address() { return &thread_local_top_.handler_; }
+ inline Address* c_function_address() {
+ return &thread_local_top_.c_function_;
+ }
// Bottom JS entry.
Address js_entry_sp() {
// StackTracer implementation
//
DISABLE_ASAN void TickSample::Init(Isolate* isolate,
- const v8::RegisterState& regs) {
+ const v8::RegisterState& regs,
+ RecordCEntryFrame record_c_entry_frame) {
timestamp = base::TimeTicks::HighResolutionNow();
pc = reinterpret_cast<Address>(regs.pc);
state = isolate->current_vm_state();
top_frame_type = it.top_frame_type();
SampleInfo info;
- GetStackSample(isolate, regs, reinterpret_cast<void**>(&stack[0]),
- kMaxFramesCount, &info);
+ GetStackSample(isolate, regs, record_c_entry_frame,
+ reinterpret_cast<void**>(&stack[0]), kMaxFramesCount, &info);
frames_count = static_cast<unsigned>(info.frames_count);
}
void TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
+ RecordCEntryFrame record_c_entry_frame,
void** frames, size_t frames_limit,
v8::SampleInfo* sample_info) {
sample_info->frames_count = 0;
SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
reinterpret_cast<Address>(regs.sp), js_entry_sp);
size_t i = 0;
+ if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() &&
+ it.top_frame_type() == StackFrame::EXIT) {
+ frames[i++] = isolate->c_function();
+ }
while (!it.done() && i < frames_limit) {
frames[i++] = it.frame()->pc();
it.Advance();
TickSample* sample = isolate_->cpu_profiler()->StartTickSample();
TickSample sample_obj;
if (sample == NULL) sample = &sample_obj;
- sample->Init(isolate_, state);
+ sample->Init(isolate_, state, TickSample::kIncludeCEntryFrame);
if (is_counting_samples_) {
if (sample->state == JS || sample->state == EXTERNAL) {
++js_and_external_sample_count_;
// TickSample captures the information collected for each sample.
struct TickSample {
+ // Internal profiling (with --prof + tools/$OS-tick-processor) wants to
+ // include the runtime function we're calling. Externally exposed tick
+ // samples don't care.
+ enum RecordCEntryFrame { kIncludeCEntryFrame, kSkipCEntryFrame };
+
TickSample()
: state(OTHER),
pc(NULL),
frames_count(0),
has_external_callback(false),
top_frame_type(StackFrame::NONE) {}
- void Init(Isolate* isolate, const v8::RegisterState& state);
+ void Init(Isolate* isolate, const v8::RegisterState& state,
+ RecordCEntryFrame record_c_entry_frame);
static void GetStackSample(Isolate* isolate, const v8::RegisterState& state,
+ RecordCEntryFrame record_c_entry_frame,
void** frames, size_t frames_limit,
v8::SampleInfo* sample_info);
StateTag state; // The state of the VM.
Store(ExternalReference(Isolate::kCEntryFPAddress, isolate()), rbp);
Store(ExternalReference(Isolate::kContextAddress, isolate()), rsi);
+ Store(ExternalReference(Isolate::kCFunctionAddress, isolate()), rbx);
}
// sp is only used to define stack high bound
regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240;
- trace_env.sample->Init(CcTest::i_isolate(), regs);
+ trace_env.sample->Init(CcTest::i_isolate(), regs,
+ TickSample::kSkipCEntryFrame);
}
4 30.8% Shared libraries
2 15.4% Unaccounted
+ [C++ entry points]:
+ ticks cpp total name
+ 2 40.0% 15.4% v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+ 1 20.0% 7.7% v8::internal::JSObject::LookupOwnRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+ 1 20.0% 7.7% v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+ 1 20.0% 7.7% exp
+
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
0 0.0% 0.0% GC
0 0.0% Shared libraries
+ [C++ entry points]:
+ ticks cpp total name
+
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
0 0.0% 0.0% GC
0 0.0% Shared libraries
+ [C++ entry points]:
+ ticks cpp total name
+
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
0 0.0% 0.0% GC
4 36.4% Shared libraries
+ [C++ entry points]:
+ ticks cpp total name
+ 2 40.0% 18.2% v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+ 1 20.0% 9.1% v8::internal::JSObject::LookupOwnRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+ 1 20.0% 9.1% v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+ 1 20.0% 9.1% exp
+
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
4 30.8% Shared libraries
2 15.4% Unaccounted
+ [C++ entry points]:
+ ticks cpp total name
+ 2 40.0% 15.4% v8::internal::Runtime_Math_exp(v8::internal::Arguments)
+ 1 20.0% 7.7% v8::internal::JSObject::LookupOwnRealNamedProperty(v8::internal::String*, v8::internal::LookupResult*)
+ 1 20.0% 7.7% v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::String*)
+ 1 20.0% 7.7% exp
+
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
*
* @param {number} size Code entry size in bytes.
* @param {string} opt_name Code entry name.
+ * @param {string} opt_type Code entry type, e.g. SHARED_LIB, CPP.
* @constructor
*/
-CodeMap.CodeEntry = function(size, opt_name) {
+CodeMap.CodeEntry = function(size, opt_name, opt_type) {
this.size = size;
this.name = opt_name || '';
+ this.type = opt_type || '';
this.nameUpdated_ = false;
};
// Filter out possible 'overflow' string.
} else if (firstChar != 'o') {
fullStack.push(parseInt(frame, 16));
+ } else {
+ print("dropping: " + frame);
}
}
return fullStack;
this.codeMap_ = new CodeMap();
this.topDownTree_ = new CallTree();
this.bottomUpTree_ = new CallTree();
+ this.c_entries_ = {};
};
Profile.prototype.addLibrary = function(
name, startAddr, endAddr) {
var entry = new CodeMap.CodeEntry(
- endAddr - startAddr, name);
+ endAddr - startAddr, name, 'SHARED_LIB');
this.codeMap_.addLibrary(startAddr, entry);
return entry;
};
Profile.prototype.addStaticCode = function(
name, startAddr, endAddr) {
var entry = new CodeMap.CodeEntry(
- endAddr - startAddr, name);
+ endAddr - startAddr, name, 'CPP');
this.codeMap_.addStaticCode(startAddr, entry);
return entry;
};
*/
Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
var result = [];
+ var last_seen_c_function = '';
+ var look_for_first_c_function = false;
for (var i = 0; i < stack.length; ++i) {
var entry = this.codeMap_.findEntry(stack[i]);
if (entry) {
var name = entry.getName();
+ if (i == 0 && (entry.type == 'CPP' || entry.type == 'SHARED_LIB')) {
+ look_for_first_c_function = true;
+ }
+ if (look_for_first_c_function) {
+ if (entry.type == 'CPP') {
+ last_seen_c_function = name;
+ } else if (i > 0 && last_seen_c_function != '') {
+ if (this.c_entries_[last_seen_c_function] === undefined) {
+ this.c_entries_[last_seen_c_function] = 0;
+ }
+ this.c_entries_[last_seen_c_function]++;
+ look_for_first_c_function = false; // Found it, we're done.
+ }
+ }
if (!this.skipThisFunction(name)) {
result.push(name);
}
};
+Profile.CEntryNode = function(name, ticks) {
+ this.name = name;
+ this.ticks = ticks;
+}
+
+
+Profile.prototype.getCEntryProfile = function() {
+ var result = [new Profile.CEntryNode("TOTAL", 0)];
+ var total_ticks = 0;
+ for (var f in this.c_entries_) {
+ var ticks = this.c_entries_[f];
+ total_ticks += ticks;
+ result.push(new Profile.CEntryNode(f, ticks));
+ }
+ result[0].ticks = total_ticks; // Sorting will keep this at index 0.
+ result.sort(function(n1, n2) {
+ return n2.ticks - n1.ticks || (n2.name < n1.name ? -1 : 1)
+ });
+ return result;
+}
+
+
/**
* Cleans up function entries that are not referenced by code entries.
*/
* @constructor
*/
Profile.DynamicCodeEntry = function(size, type, name) {
- CodeMap.CodeEntry.call(this, size, name);
- this.type = type;
+ CodeMap.CodeEntry.call(this, size, name, type);
};
* @constructor
*/
Profile.DynamicFuncCodeEntry = function(size, type, func, state) {
- CodeMap.CodeEntry.call(this, size);
- this.type = type;
+ CodeMap.CodeEntry.call(this, size, '', type);
this.func = func;
this.state = state;
};
this.ticks_.total, null);
}
+ print('\n [C++ entry points]:');
+ print(' ticks cpp total name');
+ var c_entry_functions = this.profile_.getCEntryProfile();
+ var total_c_entry = c_entry_functions[0].ticks;
+ for (var i = 1; i < c_entry_functions.length; i++) {
+ c = c_entry_functions[i];
+ this.printLine(c.name, c.ticks, total_c_entry, totalTicks);
+ }
+
this.printHeavyProfHeader();
var heavyProfile = this.profile_.getBottomUpProfile();
var heavyView = this.viewBuilder_.buildView(heavyProfile);