Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

V8 CPU profiler: source level support #50

Merged
merged 1 commit into from
Sep 4, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/v8-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ typedef uint32_t SnapshotObjectId;
*/
class V8_EXPORT CpuProfileNode {
public:
struct LineTick {
/** The 1-based number of the source line where the function originates. */
int line;

/** The count of samples associated with the source line. */
unsigned int hit_count;
};

/** Returns function name (empty string for anonymous functions.) */
Handle<String> GetFunctionName() const;

Expand All @@ -43,6 +51,18 @@ class V8_EXPORT CpuProfileNode {
*/
int GetColumnNumber() const;

/**
* Returns the number of the function's source lines that collect the samples.
*/
unsigned int GetHitLineCount() const;

/** Returns the set of source lines that collect the samples.
* The caller allocates buffer and responsible for releasing it.
* True if all available entries are copied, otherwise false.
* The function copies nothing if buffer is not large enough.
*/
bool GetLineTicks(LineTick* entries, unsigned int length) const;

/** Returns bailout reason for the function
* if the optimization was disabled for it.
*/
Expand Down
13 changes: 13 additions & 0 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7037,6 +7037,19 @@ int CpuProfileNode::GetColumnNumber() const {
}


unsigned int CpuProfileNode::GetHitLineCount() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
return node->GetHitLineCount();
}


bool CpuProfileNode::GetLineTicks(LineTick* entries,
unsigned int length) const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
return node->GetLineTicks(entries, length);
}


const char* CpuProfileNode::GetBailoutReason() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
return node->entry()->bailout_reason();
Expand Down
30 changes: 24 additions & 6 deletions src/cpu-profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,38 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
Script* script = NULL;
JITLineInfoTable* line_table = NULL;
if (shared->script()->IsScript()) {
DCHECK(Script::cast(shared->script()));
script = Script::cast(shared->script());
line_table = new JITLineInfoTable();
for (RelocIterator it(code); !it.done(); it.next()) {
RelocInfo::Mode mode = it.rinfo()->rmode();
if (RelocInfo::IsPosition(mode)) {
int position = static_cast<int>(it.rinfo()->data());
if (position >= 0) {
int pc_offset = static_cast<int>(it.rinfo()->pc() - code->address());
int line_number = script->GetLineNumber(position) + 1;
line_table->SetPosition(pc_offset, line_number);
}
}
}
}
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetFunctionName(shared->DebugName()),
CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
column);
column, line_table);
if (info) {
rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
}
DCHECK(Script::cast(shared->script()));
Script* script = Script::cast(shared->script());
rec->entry->set_script_id(script->id()->value());
rec->size = code->ExecutableSize();
rec->shared = shared->address();
if (script) {
rec->entry->set_script_id(script->id()->value());
}
rec->entry->set_bailout_reason(
GetBailoutReason(shared->DisableOptimizationReason()));
rec->size = code->ExecutableSize();
rec->shared = shared->address();
processor_->Enqueue(evt_rec);
}

Expand Down
1 change: 1 addition & 0 deletions src/full-codegen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ void FullCodeGenerator::SetExpressionPosition(Expression* expr) {
void FullCodeGenerator::SetSourcePosition(int pos) {
if (pos != RelocInfo::kNoPosition) {
masm_->positions_recorder()->RecordPosition(pos);
masm_->positions_recorder()->WriteRecordedPositions();
}
}

Expand Down
14 changes: 11 additions & 3 deletions src/profile-generator-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
const char* name_prefix,
const char* resource_name,
int line_number,
int column_number)
int column_number,
JITLineInfoTable* line_info)
: tag_(tag),
builtin_id_(Builtins::builtin_count),
name_prefix_(name_prefix),
Expand All @@ -26,7 +27,8 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
shared_id_(0),
script_id_(v8::UnboundScript::kNoScriptId),
no_frame_ranges_(NULL),
bailout_reason_(kEmptyBailoutReason) { }
bailout_reason_(kEmptyBailoutReason),
line_info_(line_info) { }


bool CodeEntry::is_js_function_tag(Logger::LogEventsAndTags tag) {
Expand All @@ -39,12 +41,18 @@ bool CodeEntry::is_js_function_tag(Logger::LogEventsAndTags tag) {
}


static bool LineTickMatch(void* a, void* b) {
return a == b;
}


ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry)
: tree_(tree),
entry_(entry),
self_ticks_(0),
children_(CodeEntriesMatch),
id_(tree->next_node_id()) { }
id_(tree->next_node_id()),
line_ticks_(LineTickMatch) { }

} } // namespace v8::internal

Expand Down
109 changes: 96 additions & 13 deletions src/profile-generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const char* const CodeEntry::kEmptyBailoutReason = "";

CodeEntry::~CodeEntry() {
delete no_frame_ranges_;
delete line_info_;
}


Expand Down Expand Up @@ -181,6 +182,14 @@ void CodeEntry::SetBuiltinId(Builtins::Name id) {
}


int CodeEntry::GetSourceLine(int pc_offset) const {
if (line_info_ && !line_info_->Empty()) {
return line_info_->GetSourceLineNumber(pc_offset);
}
return v8::CpuProfileNode::kNoLineNumberInfo;
}


ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
HashMap::Entry* map_entry =
children_.Lookup(entry, CodeEntryHash(entry), false);
Expand All @@ -202,6 +211,41 @@ ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
}


void ProfileNode::IncrementLineTicks(int src_line) {
if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return;
// Increment a hit counter of a certain source line.
// Add a new source line if not found.
HashMap::Entry* e =
line_ticks_.Lookup(reinterpret_cast<void*>(src_line), src_line, true);
DCHECK(e);
e->value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(e->value) + 1);
}


bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
unsigned int length) const {
if (entries == NULL || length == 0) return false;

unsigned line_count = line_ticks_.occupancy();

if (line_count == 0) return false;
if (length < line_count) return false;

v8::CpuProfileNode::LineTick* entry = entries;

for (HashMap::Entry* p = line_ticks_.Start();
p != NULL;
p = line_ticks_.Next(p), entry++) {
entry->line =
static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->key));
entry->hit_count =
static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->value));
}

return true;
}


void ProfileNode::Print(int indent) {
base::OS::Print("%5u %*s %s%s %d #%d %s", self_ticks_, indent, "",
entry_->name_prefix(), entry_->name(), entry_->script_id(),
Expand Down Expand Up @@ -242,7 +286,8 @@ ProfileTree::~ProfileTree() {
}


ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
int src_line) {
ProfileNode* node = root_;
for (CodeEntry** entry = path.start() + path.length() - 1;
entry != path.start() - 1;
Expand All @@ -252,11 +297,15 @@ ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
}
}
node->IncrementSelfTicks();
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
}
return node;
}


void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path,
int src_line) {
ProfileNode* node = root_;
for (CodeEntry** entry = path.start();
entry != path.start() + path.length();
Expand All @@ -266,6 +315,9 @@ void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
}
}
node->IncrementSelfTicks();
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
}
}


Expand Down Expand Up @@ -327,8 +379,9 @@ CpuProfile::CpuProfile(const char* title, bool record_samples)


void CpuProfile::AddPath(base::TimeTicks timestamp,
const Vector<CodeEntry*>& path) {
ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path);
const Vector<CodeEntry*>& path,
int src_line) {
ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line);
if (record_samples_) {
timestamps_.Add(timestamp);
samples_.Add(top_frame_node);
Expand Down Expand Up @@ -517,13 +570,15 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {


void CpuProfilesCollection::AddPathToCurrentProfiles(
base::TimeTicks timestamp, const Vector<CodeEntry*>& path) {
base::TimeTicks timestamp,
const Vector<CodeEntry*>& path,
int src_line) {
// As starting / stopping profiles is rare relatively to this
// method, we don't bother minimizing the duration of lock holding,
// e.g. copying contents of the list to a local vector.
current_profiles_semaphore_.Wait();
for (int i = 0; i < current_profiles_.length(); ++i) {
current_profiles_[i]->AddPath(timestamp, path);
current_profiles_[i]->AddPath(timestamp, path, src_line);
}
current_profiles_semaphore_.Signal();
}
Expand All @@ -535,13 +590,15 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(
const char* name_prefix,
const char* resource_name,
int line_number,
int column_number) {
int column_number,
JITLineInfoTable* line_info) {
CodeEntry* code_entry = new CodeEntry(tag,
name,
name_prefix,
resource_name,
line_number,
column_number);
column_number,
line_info);
code_entries_.Add(code_entry);
return code_entry;
}
Expand Down Expand Up @@ -579,6 +636,14 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// entries vector with NULL values.
CodeEntry** entry = entries.start();
memset(entry, 0, entries.length() * sizeof(*entry));

// The ProfileNode knows nothing about all versions of generated code for
// the same JS function. The line number information associated with
// the latest version of generated code is used to find a source line number
// for a JS function. Then, the detected source line is passed to
// ProfileNode to accumulate the samples.
int src_line = v8::CpuProfileNode::kNoLineNumberInfo;

if (sample.pc != NULL) {
if (sample.has_external_callback && sample.state == EXTERNAL &&
sample.top_frame_type == StackFrame::EXIT) {
Expand All @@ -595,10 +660,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// frame. Check for this case and just skip such samples.
if (pc_entry) {
List<OffsetRange>* ranges = pc_entry->no_frame_ranges();
Code* code = Code::cast(HeapObject::FromAddress(start));
int pc_offset = static_cast<int>(sample.pc - code->instruction_start());
src_line = pc_entry->GetSourceLine(pc_offset);
if (ranges) {
Code* code = Code::cast(HeapObject::FromAddress(start));
int pc_offset = static_cast<int>(
sample.pc - code->instruction_start());
for (int i = 0; i < ranges->length(); i++) {
OffsetRange& range = ranges->at(i);
if (range.from <= pc_offset && pc_offset < range.to) {
Expand All @@ -622,11 +687,29 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
}
}

bool src_line_not_found = src_line == v8::CpuProfileNode::kNoLineNumberInfo;

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);
Address start = NULL;
*entry = code_map_.FindEntry(*stack_pos, &start);

// Skip unresolved frames (e.g. internal frame) and get source line of
// the JS caller.
if (src_line_not_found && *entry) {
Code* code = Code::cast(HeapObject::FromAddress(start));
int pc_offset =
static_cast<int>(*stack_pos - code->instruction_start());
src_line = (*entry)->GetSourceLine(pc_offset);
if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
src_line = (*entry)->line_number();
}
src_line_not_found = false;
}

entry++;
}
}

Expand All @@ -644,7 +727,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
}
}

profiles_->AddPathToCurrentProfiles(sample.timestamp, entries);
profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line);
}


Expand Down
Loading