Index: runtime/vm/profiler_service.cc |
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc |
index 3de17c8009fd8fc55b28c77842ad24dd365761b1..474fa0c5938fc311a16f7c0ccc1960d7d0b82ea4 100644 |
--- a/runtime/vm/profiler_service.cc |
+++ b/runtime/vm/profiler_service.cc |
@@ -5,6 +5,7 @@ |
#include "vm/profiler_service.h" |
#include "vm/growable_array.h" |
+#include "vm/log.h" |
#include "vm/native_symbol.h" |
#include "vm/object.h" |
#include "vm/os.h" |
@@ -89,6 +90,23 @@ class DeoptimizedCodeSet : public ZoneAllocated { |
}; |
+ProfileFunctionSourcePosition::ProfileFunctionSourcePosition( |
+ TokenPosition token_pos) |
+ : token_pos_(token_pos), |
+ exclusive_ticks_(0), |
+ inclusive_ticks_(0) { |
+} |
+ |
+ |
+void ProfileFunctionSourcePosition::Tick(bool exclusive) { |
+ if (exclusive) { |
+ exclusive_ticks_++; |
+ } else { |
+ inclusive_ticks_++; |
+ } |
+} |
+ |
+ |
ProfileFunction::ProfileFunction(Kind kind, |
const char* name, |
const Function& function, |
@@ -98,6 +116,7 @@ ProfileFunction::ProfileFunction(Kind kind, |
function_(Function::ZoneHandle(function.raw())), |
table_index_(table_index), |
profile_codes_(0), |
+ source_position_ticks_(0), |
exclusive_ticks_(0), |
inclusive_ticks_(0), |
inclusive_serial_(-1) { |
@@ -117,9 +136,13 @@ const char* ProfileFunction::Name() const { |
return func_name.ToCString(); |
} |
-void ProfileFunction::Tick(bool exclusive, intptr_t inclusive_serial) { |
+ |
+void ProfileFunction::Tick(bool exclusive, |
+ intptr_t inclusive_serial, |
+ TokenPosition token_position) { |
if (exclusive) { |
exclusive_ticks_++; |
+ TickSourcePosition(token_position, exclusive); |
} |
// Fall through and tick inclusive count too. |
if (inclusive_serial_ == inclusive_serial) { |
@@ -128,6 +151,24 @@ void ProfileFunction::Tick(bool exclusive, intptr_t inclusive_serial) { |
} |
inclusive_serial_ = inclusive_serial; |
inclusive_ticks_++; |
+ TickSourcePosition(token_position, false); |
+} |
+ |
+ |
+void ProfileFunction::TickSourcePosition(TokenPosition token_position, |
+ bool exclusive) { |
+ for (intptr_t i = 0; i < source_position_ticks_.length(); i++) { |
+ ProfileFunctionSourcePosition& position = source_position_ticks_[i]; |
+ if (position.token_pos() == token_position) { |
+ // Found existing position, tick it. |
+ position.Tick(exclusive); |
+ return; |
+ } |
+ } |
+ // Add new one. |
+ ProfileFunctionSourcePosition pfsp(token_position); |
+ pfsp.Tick(exclusive); |
+ source_position_ticks_.Add(pfsp); |
} |
@@ -189,6 +230,18 @@ void ProfileFunction::AddProfileCode(intptr_t code_table_index) { |
} |
+bool ProfileFunction::GetSinglePosition(ProfileFunctionSourcePosition* pfsp) { |
+ if (pfsp == NULL) { |
+ return false; |
+ } |
+ if (source_position_ticks_.length() != 1) { |
+ return false; |
+ } |
+ *pfsp = source_position_ticks_[0]; |
+ return true; |
+} |
+ |
+ |
ProfileCodeAddress::ProfileCodeAddress(uword pc) |
: pc_(pc), |
exclusive_ticks_(0), |
@@ -1388,14 +1441,40 @@ class ProfileBuilder : public ValueObject { |
ASSERT(profile_code != NULL); |
const Code& code = Code::ZoneHandle(profile_code->code()); |
GrowableArray<Function*> inlined_functions; |
+ GrowableArray<TokenPosition> inlined_token_positions; |
+ TokenPosition token_position = TokenPosition::kNoSource; |
if (!code.IsNull()) { |
intptr_t offset = pc - code.EntryPoint(); |
if (frame_index != 0) { |
// The PC of frames below the top frame is a call's return address, |
// which can belong to a different inlining interval than the call. |
offset--; |
+ } else if (sample->IsAllocationSample()) { |
+ // Allocation samples skip the top frame, so the top frame's pc is |
+ // also a call's return address. |
+ offset--; |
+ } |
+ code.GetInlinedFunctionsAt(offset, |
+ &inlined_functions, |
+ &inlined_token_positions); |
+ token_position = code.GetTokenPositionAt(offset); |
+ if (inlined_functions.length() > 0) { |
+ // The inlined token position table does not include the token position |
+ // of the final call. Insert it at the beginning because the table. |
+ // is reversed. |
+ inlined_token_positions.InsertAt(0, token_position); |
+ } |
+ ASSERT(inlined_functions.length() <= inlined_token_positions.length()); |
+ if (FLAG_trace_profiler) { |
+ for (intptr_t i = 0; i < inlined_functions.length(); i++) { |
+ const String& name = |
+ String::Handle(inlined_functions[i]->QualifiedScrubbedName()); |
+ THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", |
+ i, |
+ name.ToCString(), |
+ inlined_token_positions[i].ToCString()); |
+ } |
} |
- code.GetInlinedFunctionsAt(offset, &inlined_functions); |
} |
if (code.IsNull() || (inlined_functions.length() == 0)) { |
// No inlined functions. |
@@ -1407,6 +1486,7 @@ class ProfileBuilder : public ValueObject { |
sample, |
frame_index, |
function, |
+ token_position, |
code_index); |
if (!inclusive_tree_) { |
current = AppendKind(code, current); |
@@ -1421,6 +1501,7 @@ class ProfileBuilder : public ValueObject { |
Function* inlined_function = inlined_functions[i]; |
ASSERT(inlined_function != NULL); |
ASSERT(!inlined_function->IsNull()); |
+ TokenPosition inlined_token_position = inlined_token_positions[i]; |
const bool inliner = i == (inlined_functions.length() - 1); |
if (inliner) { |
current = AppendKind(code, current); |
@@ -1430,6 +1511,7 @@ class ProfileBuilder : public ValueObject { |
sample, |
frame_index, |
inlined_function, |
+ inlined_token_position, |
code_index); |
if (inliner) { |
current = AppendKind(kInlineStart, current); |
@@ -1443,6 +1525,7 @@ class ProfileBuilder : public ValueObject { |
Function* inlined_function = inlined_functions[i]; |
ASSERT(inlined_function != NULL); |
ASSERT(!inlined_function->IsNull()); |
+ TokenPosition inlined_token_position = inlined_token_positions[i]; |
const bool inliner = i == (inlined_functions.length() - 1); |
if (inliner) { |
current = AppendKind(kInlineStart, current); |
@@ -1452,6 +1535,7 @@ class ProfileBuilder : public ValueObject { |
sample, |
frame_index + i, |
inlined_function, |
+ inlined_token_position, |
code_index); |
if (inliner) { |
current = AppendKind(code, current); |
@@ -1468,6 +1552,7 @@ class ProfileBuilder : public ValueObject { |
ProcessedSample* sample, |
intptr_t frame_index, |
Function* inlined_function, |
+ TokenPosition inlined_token_position, |
intptr_t code_index) { |
ProfileFunctionTable* function_table = profile_->functions_; |
ProfileFunction* function = function_table->LookupOrAdd(*inlined_function); |
@@ -1477,6 +1562,7 @@ class ProfileBuilder : public ValueObject { |
sample, |
frame_index, |
function, |
+ inlined_token_position, |
code_index); |
} |
@@ -1494,9 +1580,18 @@ class ProfileBuilder : public ValueObject { |
ProcessedSample* sample, |
intptr_t frame_index, |
ProfileFunction* function, |
+ TokenPosition token_position, |
intptr_t code_index) { |
+ if (FLAG_trace_profiler) { |
+ THR_Print("S[%" Pd "]F[%" Pd "] %s %s\n", |
+ sample_index, |
+ frame_index, |
+ function->Name(), token_position.ToCString()); |
+ } |
if (tick_functions_) { |
- function->Tick(IsExecutingFrame(sample, frame_index), sample_index); |
+ function->Tick(IsExecutingFrame(sample, frame_index), |
+ sample_index, |
+ token_position); |
} |
function->AddProfileCode(code_index); |
current = current->GetChild(function->table_index()); |
@@ -2244,6 +2339,50 @@ intptr_t ProfileTrieWalker::CurrentExclusiveTicks() { |
} |
+const char* ProfileTrieWalker::CurrentToken() { |
+ if (current_ == NULL) { |
+ return NULL; |
+ } |
+ if (code_trie_) { |
+ return NULL; |
+ } |
+ ProfileFunction* func = profile_->GetFunction(current_->table_index()); |
+ const Function& function = Function::Handle(func->function()); |
+ if (function.IsNull()) { |
+ // No function. |
+ return NULL; |
+ } |
+ const Script& script = Script::Handle(function.script()); |
+ if (script.IsNull()) { |
+ // No script. |
+ return NULL; |
+ } |
+ const TokenStream& token_stream = TokenStream::Handle(script.tokens()); |
+ if (token_stream.IsNull()) { |
+ // No token position. |
+ return NULL; |
+ } |
+ ProfileFunctionSourcePosition pfsp(TokenPosition::kNoSource); |
+ if (!func->GetSinglePosition(&pfsp)) { |
+ // Not exactly one source position. |
+ return NULL; |
+ } |
+ TokenPosition token_pos = pfsp.token_pos(); |
+ if (!token_pos.IsReal() && !token_pos.IsSynthetic()) { |
+ // Not a location in a script. |
+ return NULL; |
+ } |
+ if (token_pos.IsSynthetic()) { |
+ token_pos = token_pos.FromSynthetic(); |
+ } |
+ TokenStream::Iterator iterator(token_stream, token_pos); |
+ const String& str = String::Handle(iterator.CurrentLiteral()); |
+ if (str.IsNull()) { |
+ return NULL; |
+ } |
+ return str.ToCString(); |
+} |
+ |
bool ProfileTrieWalker::Down() { |
if ((current_ == NULL) || (current_->NumChildren() == 0)) { |
return false; |