Index: runtime/vm/profiler_service.cc |
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc |
index 188d7cd13218ad95f1ac0307cf94906fcb44923a..a75f8a20f41799c9c68c1ef545a95b202305318d 100644 |
--- a/runtime/vm/profiler_service.cc |
+++ b/runtime/vm/profiler_service.cc |
@@ -19,12 +19,6 @@ DECLARE_FLAG(int, profile_period); |
DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); |
-// Forward declarations. |
-class CodeRegion; |
-class ProfileFunction; |
-class ProfileFunctionTable; |
- |
- |
class DeoptimizedCodeSet : public ZoneAllocated { |
public: |
explicit DeoptimizedCodeSet(Isolate* isolate) |
@@ -92,156 +86,351 @@ class DeoptimizedCodeSet : public ZoneAllocated { |
const GrowableObjectArray& current_; |
}; |
-class ProfileFunction : public ZoneAllocated { |
- public: |
- enum Kind { |
- kDartFunction, // Dart function. |
- kNativeFunction, // Synthetic function for Native (C/C++). |
- kTagFunction, // Synthetic function for a VM or User tag. |
- kStubFunction, // Synthetic function for stub code. |
- kUnkownFunction, // A singleton function for unknown objects. |
- }; |
- ProfileFunction(Kind kind, |
+ |
+ProfileFunction::ProfileFunction(Kind kind, |
const char* name, |
const Function& function, |
const intptr_t table_index) |
- : kind_(kind), |
- name_(name), |
- function_(Function::ZoneHandle(function.raw())), |
- table_index_(table_index), |
- code_objects_(new ZoneGrowableArray<intptr_t>()), |
- exclusive_ticks_(0), |
- inclusive_ticks_(0), |
- inclusive_tick_serial_(0) { |
- ASSERT((kind_ != kDartFunction) || !function_.IsNull()); |
- ASSERT((kind_ != kDartFunction) || (table_index_ >= 0)); |
- ASSERT(code_objects_->length() == 0); |
- } |
+ : kind_(kind), |
+ name_(name), |
+ function_(Function::ZoneHandle(function.raw())), |
+ table_index_(table_index), |
+ profile_codes_(0), |
+ exclusive_ticks_(0), |
+ inclusive_ticks_(0) { |
+ ASSERT((kind_ != kDartFunction) || !function_.IsNull()); |
+ ASSERT((kind_ != kDartFunction) || (table_index_ >= 0)); |
+ ASSERT(profile_codes_.length() == 0); |
+} |
- const char* name() const { |
- ASSERT(name_ != NULL); |
- return name_; |
- } |
- RawFunction* function() const { |
- return function_.raw(); |
+void ProfileFunction::Tick(bool exclusive, intptr_t inclusive_serial) { |
+ if (exclusive) { |
+ exclusive_ticks_++; |
+ } else { |
+ if (inclusive_serial_ == inclusive_serial) { |
+ // Already ticket. |
+ return; |
+ } |
+ inclusive_serial_ = inclusive_serial; |
+ inclusive_ticks_++; |
} |
+} |
- intptr_t index() const { |
- return table_index_; |
- } |
- Kind kind() const { |
- return kind_; |
+const char* ProfileFunction::KindToCString(Kind kind) { |
+ switch (kind) { |
+ case kDartFunction: |
+ return "Dart"; |
+ case kNativeFunction: |
+ return "Native"; |
+ case kTagFunction: |
+ return "Tag"; |
+ case kStubFunction: |
+ return "Stub"; |
+ case kUnknownFunction: |
+ return "Collected"; |
+ default: |
+ UNIMPLEMENTED(); |
+ return ""; |
} |
+} |
+ |
+ |
+void ProfileFunction::PrintToJSONObject(JSONObject* func) { |
+ func->AddProperty("type", "@Function"); |
+ func->AddProperty("name", name()); |
+ func->AddProperty("_kind", KindToCString(kind())); |
+} |
+ |
- const char* KindToCString(Kind kind) { |
- switch (kind) { |
- case kDartFunction: |
- return "Dart"; |
- case kNativeFunction: |
- return "Native"; |
- case kTagFunction: |
- return "Tag"; |
- case kStubFunction: |
- return "Stub"; |
- case kUnkownFunction: |
- return "Collected"; |
- default: |
- UNIMPLEMENTED(); |
- return ""; |
+void ProfileFunction::PrintToJSONArray(JSONArray* functions) { |
+ JSONObject obj(functions); |
+ obj.AddProperty("kind", KindToCString(kind())); |
+ obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
+ obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
+ if (kind() == kDartFunction) { |
+ ASSERT(!function_.IsNull()); |
+ obj.AddProperty("function", function_); |
+ } else { |
+ JSONObject func(&obj, "function"); |
+ PrintToJSONObject(&func); |
+ } |
+ { |
+ JSONArray codes(&obj, "codes"); |
+ for (intptr_t i = 0; i < profile_codes_.length(); i++) { |
+ intptr_t code_index = profile_codes_[i]; |
+ codes.AddValue(code_index); |
} |
} |
+} |
+ |
- void Dump() { |
- const char* n = (name_ == NULL) ? "<NULL>" : name_; |
- const char* fn = ""; |
- if (!function_.IsNull()) { |
- fn = function_.ToQualifiedCString(); |
+void ProfileFunction::AddProfileCode(intptr_t code_table_index) { |
+ for (intptr_t i = 0; i < profile_codes_.length(); i++) { |
+ if (profile_codes_[i] == code_table_index) { |
+ return; |
} |
- OS::Print("%s %s [%s]", KindToCString(kind()), n, fn); |
} |
+ profile_codes_.Add(code_table_index); |
+} |
- void AddCodeObjectIndex(intptr_t index) { |
- for (intptr_t i = 0; i < code_objects_->length(); i++) { |
- if ((*code_objects_)[i] == index) { |
- return; |
- } |
- } |
- code_objects_->Add(index); |
+ |
+ProfileCodeAddress::ProfileCodeAddress(uword pc) |
+ : pc_(pc), |
+ exclusive_ticks_(0), |
+ inclusive_ticks_(0) { |
+} |
+ |
+ |
+void ProfileCodeAddress::Tick(bool exclusive) { |
+ if (exclusive) { |
+ exclusive_ticks_++; |
+ } else { |
+ inclusive_ticks_++; |
} |
+} |
+ |
+ |
+ProfileCode::ProfileCode(Kind kind, |
+ uword start, |
+ uword end, |
+ int64_t timestamp, |
+ const Code& code) |
+ : kind_(kind), |
+ start_(start), |
+ end_(end), |
+ exclusive_ticks_(0), |
+ inclusive_ticks_(0), |
+ inclusive_serial_(-1), |
+ code_(code), |
+ name_(NULL), |
+ compile_timestamp_(0), |
+ function_(NULL), |
+ code_table_index_(-1), |
+ address_ticks_(0) { |
+} |
+ |
- intptr_t inclusive_ticks() const { |
- return inclusive_ticks_; |
+void ProfileCode::AdjustExtent(uword start, uword end) { |
+ if (start < start_) { |
+ start_ = start; |
} |
- void inc_inclusive_ticks() { |
- inclusive_ticks_++; |
+ if (end > end_) { |
+ end_ = end; |
+ } |
+ ASSERT(start_ < end_); |
+} |
+ |
+ |
+bool ProfileCode::Overlaps(const ProfileCode* other) const { |
+ ASSERT(other != NULL); |
+ return other->Contains(start_) || |
+ other->Contains(end_ - 1) || |
+ Contains(other->start()) || |
+ Contains(other->end() - 1); |
+} |
+ |
+ |
+bool ProfileCode::IsOptimizedDart() const { |
+ return !code_.IsNull() && code_.is_optimized(); |
+} |
+ |
+ |
+void ProfileCode::SetName(const char* name) { |
+ if (name == NULL) { |
+ name_ = NULL; |
} |
- intptr_t exclusive_ticks() const { |
- return exclusive_ticks_; |
+ intptr_t len = strlen(name); |
+ name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); |
+ strncpy(const_cast<char*>(name_), name, len); |
+ const_cast<char*>(name_)[len] = '\0'; |
+} |
+ |
+ |
+void ProfileCode::GenerateAndSetSymbolName(const char* prefix) { |
+ const intptr_t kBuffSize = 512; |
+ char buff[kBuffSize]; |
+ OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
+ prefix, start(), end()); |
+ SetName(buff); |
+} |
+ |
+ |
+void ProfileCode::Tick(uword pc, bool exclusive, intptr_t serial) { |
+ if (exclusive) { |
+ exclusive_ticks_++; |
+ } else { |
+ if (inclusive_serial_ == serial) { |
+ // Already ticked for this sample. |
+ return; |
+ } |
+ inclusive_serial_ = serial; |
+ inclusive_ticks_++; |
} |
+ TickAddress(pc, exclusive); |
+} |
+ |
- void Tick(bool exclusive, intptr_t serial) { |
- // Assert that exclusive ticks are never passed a valid serial number. |
- ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); |
- if (!exclusive && (inclusive_tick_serial_ == serial)) { |
- // We've already given this object an inclusive tick for this sample. |
+void ProfileCode::TickAddress(uword pc, bool exclusive) { |
+ const intptr_t length = address_ticks_.length(); |
+ |
+ intptr_t i = 0; |
+ for (; i < length; i++) { |
+ ProfileCodeAddress& entry = address_ticks_[i]; |
+ if (entry.pc() == pc) { |
+ // Tick the address entry. |
+ entry.Tick(exclusive); |
return; |
} |
- if (exclusive) { |
- exclusive_ticks_++; |
- } else { |
- inclusive_ticks_++; |
- // Mark the last serial we ticked the inclusive count. |
- inclusive_tick_serial_ = serial; |
+ if (entry.pc() > pc) { |
+ break; |
} |
} |
- void PrintToJSONObject(JSONObject* func) { |
- func->AddProperty("type", "@Function"); |
- func->AddProperty("name", name()); |
- func->AddProperty("_kind", KindToCString(kind())); |
+ // New address, add entry. |
+ ProfileCodeAddress entry(pc); |
+ |
+ entry.Tick(exclusive); |
+ |
+ if (i < length) { |
+ // Insert at i. |
+ address_ticks_.InsertAt(i, entry); |
+ } else { |
+ // Add to end. |
+ address_ticks_.Add(entry); |
} |
+} |
- void PrintToJSONArray(JSONArray* functions) { |
- JSONObject obj(functions); |
- obj.AddProperty("kind", KindToCString(kind())); |
- obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
- obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
- if (kind() == kDartFunction) { |
- ASSERT(!function_.IsNull()); |
- obj.AddProperty("function", function_); |
- } else { |
- JSONObject func(&obj, "function"); |
- PrintToJSONObject(&func); |
- } |
- { |
- JSONArray codes(&obj, "codes"); |
- for (intptr_t i = 0; i < code_objects_->length(); i++) { |
- intptr_t code_index = (*code_objects_)[i]; |
- codes.AddValue(code_index); |
- } |
- } |
+ |
+void ProfileCode::PrintNativeCode(JSONObject* profile_code_obj) { |
+ ASSERT(kind() == kNativeCode); |
+ JSONObject obj(profile_code_obj, "code"); |
+ obj.AddProperty("type", "@Code"); |
+ obj.AddProperty("kind", "Native"); |
+ obj.AddProperty("name", name()); |
+ obj.AddProperty("_optimized", false); |
+ obj.AddPropertyF("start", "%" Px "", start()); |
+ obj.AddPropertyF("end", "%" Px "", end()); |
+ { |
+ // Generate a fake function entry. |
+ JSONObject func(&obj, "function"); |
+ ASSERT(function_ != NULL); |
+ function_->PrintToJSONObject(&func); |
} |
+} |
- private: |
- const Kind kind_; |
- const char* name_; |
- const Function& function_; |
- const intptr_t table_index_; |
- ZoneGrowableArray<intptr_t>* code_objects_; |
- intptr_t exclusive_ticks_; |
- intptr_t inclusive_ticks_; |
- intptr_t inclusive_tick_serial_; |
-}; |
+ |
+void ProfileCode::PrintCollectedCode(JSONObject* profile_code_obj) { |
+ ASSERT(kind() == kCollectedCode); |
+ JSONObject obj(profile_code_obj, "code"); |
+ obj.AddProperty("type", "@Code"); |
+ obj.AddProperty("kind", "Collected"); |
+ obj.AddProperty("name", name()); |
+ obj.AddProperty("_optimized", false); |
+ obj.AddPropertyF("start", "%" Px "", start()); |
+ obj.AddPropertyF("end", "%" Px "", end()); |
+ { |
+ // Generate a fake function entry. |
+ JSONObject func(&obj, "function"); |
+ ASSERT(function_ != NULL); |
+ function_->PrintToJSONObject(&func); |
+ } |
+} |
+ |
+ |
+void ProfileCode::PrintOverwrittenCode(JSONObject* profile_code_obj) { |
+ ASSERT(kind() == kReusedCode); |
+ JSONObject obj(profile_code_obj, "code"); |
+ obj.AddProperty("type", "@Code"); |
+ obj.AddProperty("kind", "Collected"); |
+ obj.AddProperty("name", name()); |
+ obj.AddProperty("_optimized", false); |
+ obj.AddPropertyF("start", "%" Px "", start()); |
+ obj.AddPropertyF("end", "%" Px "", end()); |
+ { |
+ // Generate a fake function entry. |
+ JSONObject func(&obj, "function"); |
+ ASSERT(function_ != NULL); |
+ function_->PrintToJSONObject(&func); |
+ } |
+} |
+ |
+ |
+void ProfileCode::PrintTagCode(JSONObject* profile_code_obj) { |
+ ASSERT(kind() == kTagCode); |
+ JSONObject obj(profile_code_obj, "code"); |
+ obj.AddProperty("type", "@Code"); |
+ obj.AddProperty("kind", "Tag"); |
+ obj.AddProperty("name", name()); |
+ obj.AddPropertyF("start", "%" Px "", start()); |
+ obj.AddPropertyF("end", "%" Px "", end()); |
+ obj.AddProperty("_optimized", false); |
+ { |
+ // Generate a fake function entry. |
+ JSONObject func(&obj, "function"); |
+ ASSERT(function_ != NULL); |
+ function_->PrintToJSONObject(&func); |
+ } |
+} |
+ |
+ |
+const char* ProfileCode::KindToCString(Kind kind) { |
+ switch (kind) { |
+ case kDartCode: |
+ return "Dart"; |
+ case kCollectedCode: |
+ return "Collected"; |
+ case kNativeCode: |
+ return "Native"; |
+ case kReusedCode: |
+ return "Overwritten"; |
+ case kTagCode: |
+ return "Tag"; |
+ } |
+ UNREACHABLE(); |
+ return NULL; |
+} |
+ |
+ |
+void ProfileCode::PrintToJSONArray(JSONArray* codes) { |
+ JSONObject obj(codes); |
+ obj.AddProperty("kind", ProfileCode::KindToCString(kind())); |
+ obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
+ obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
+ if (kind() == kDartCode) { |
+ ASSERT(!code_.IsNull()); |
+ obj.AddProperty("code", code_); |
+ } else if (kind() == kCollectedCode) { |
+ PrintCollectedCode(&obj); |
+ } else if (kind() == kReusedCode) { |
+ PrintOverwrittenCode(&obj); |
+ } else if (kind() == kTagCode) { |
+ PrintTagCode(&obj); |
+ } else { |
+ ASSERT(kind() == kNativeCode); |
+ PrintNativeCode(&obj); |
+ } |
+ { |
+ JSONArray ticks(&obj, "ticks"); |
+ for (intptr_t i = 0; i < address_ticks_.length(); i++) { |
+ const ProfileCodeAddress& entry = address_ticks_[i]; |
+ ticks.AddValueF("%" Px "", entry.pc()); |
+ ticks.AddValueF("%" Pd "", entry.exclusive_ticks()); |
+ ticks.AddValueF("%" Pd "", entry.inclusive_ticks()); |
+ } |
+ } |
+} |
-class ProfileFunctionTable : public ValueObject { |
+class ProfileFunctionTable : public ZoneAllocated { |
public: |
ProfileFunctionTable() |
: null_function_(Function::ZoneHandle()), |
- table_(new ZoneGrowableArray<ProfileFunction*>()), |
+ table_(8), |
unknown_function_(NULL) { |
+ unknown_function_ = Add(ProfileFunction::kUnknownFunction, |
+ "<unknown Dart function>"); |
} |
ProfileFunction* LookupOrAdd(const Function& function) { |
@@ -255,8 +444,8 @@ class ProfileFunctionTable : public ValueObject { |
intptr_t LookupIndex(const Function& function) { |
ASSERT(!function.IsNull()); |
- for (intptr_t i = 0; i < table_->length(); i++) { |
- ProfileFunction* profile_function = (*table_)[i]; |
+ for (intptr_t i = 0; i < table_.length(); i++) { |
+ ProfileFunction* profile_function = table_[i]; |
if (profile_function->function() == function.raw()) { |
return i; |
} |
@@ -265,11 +454,6 @@ class ProfileFunctionTable : public ValueObject { |
} |
ProfileFunction* GetUnknown() { |
- if (unknown_function_ == NULL) { |
- // Construct. |
- unknown_function_ = Add(ProfileFunction::kUnkownFunction, |
- "<unknown Dart function>"); |
- } |
ASSERT(unknown_function_ != NULL); |
return unknown_function_; |
} |
@@ -292,14 +476,14 @@ class ProfileFunctionTable : public ValueObject { |
return Add(ProfileFunction::kStubFunction, name); |
} |
- intptr_t Length() const { |
- return table_->length(); |
+ intptr_t length() const { |
+ return table_.length(); |
} |
ProfileFunction* At(intptr_t i) const { |
ASSERT(i >= 0); |
- ASSERT(i < Length()); |
- return (*table_)[i]; |
+ ASSERT(i < length()); |
+ return table_[i]; |
} |
private: |
@@ -310,8 +494,8 @@ class ProfileFunctionTable : public ValueObject { |
new ProfileFunction(kind, |
name, |
null_function_, |
- table_->length()); |
- table_->Add(profile_function); |
+ table_.length()); |
+ table_.Add(profile_function); |
return profile_function; |
} |
@@ -321,8 +505,8 @@ class ProfileFunctionTable : public ValueObject { |
new ProfileFunction(ProfileFunction::kDartFunction, |
NULL, |
function, |
- table_->length()); |
- table_->Add(profile_function); |
+ table_.length()); |
+ table_.Add(profile_function); |
return profile_function; |
} |
@@ -332,559 +516,189 @@ class ProfileFunctionTable : public ValueObject { |
if (index == -1) { |
return NULL; |
} |
- return (*table_)[index]; |
+ return table_[index]; |
} |
const Function& null_function_; |
- ZoneGrowableArray<ProfileFunction*>* table_; |
- |
+ ZoneGrowableArray<ProfileFunction*> table_; |
ProfileFunction* unknown_function_; |
}; |
-struct AddressEntry { |
- uword pc; |
- intptr_t exclusive_ticks; |
- intptr_t inclusive_ticks; |
- |
- void tick(bool exclusive) { |
- if (exclusive) { |
- exclusive_ticks++; |
+ProfileFunction* ProfileCode::SetFunctionAndName(ProfileFunctionTable* table) { |
+ ASSERT(function_ == NULL); |
+ |
+ ProfileFunction* function = NULL; |
+ if ((kind() == kReusedCode) || (kind() == kCollectedCode)) { |
+ if (name() == NULL) { |
+ // Lazily set generated name. |
+ GenerateAndSetSymbolName("[Collected]"); |
+ } |
+ // Map these to a canonical unknown function. |
+ function = table->GetUnknown(); |
+ } else if (kind() == kDartCode) { |
+ ASSERT(!code_.IsNull()); |
+ const Object& obj = Object::Handle(code_.owner()); |
+ if (obj.IsFunction()) { |
+ const String& user_name = String::Handle(code_.PrettyName()); |
+ function = table->LookupOrAdd(Function::Cast(obj)); |
+ SetName(user_name.ToCString()); |
} else { |
- inclusive_ticks++; |
- } |
- } |
-}; |
- |
-typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); |
- |
-// A contiguous address region that holds code. Each CodeRegion has a "kind" |
-// which describes the type of code contained inside the region. Each |
-// region covers the following interval: [start, end). |
-class CodeRegion : public ZoneAllocated { |
- public: |
- enum Kind { |
- kDartCode, // Live Dart code. |
- kCollectedCode, // Dead Dart code. |
- kNativeCode, // Native code. |
- kReusedCode, // Dead Dart code that has been reused by new kDartCode. |
- kTagCode, // A special kind of code representing a tag. |
- }; |
- |
- CodeRegion(Kind kind, |
- uword start, |
- uword end, |
- int64_t timestamp, |
- const Code& code) |
- : kind_(kind), |
- start_(start), |
- end_(end), |
- inclusive_ticks_(0), |
- exclusive_ticks_(0), |
- inclusive_tick_serial_(0), |
- name_(NULL), |
- compile_timestamp_(timestamp), |
- code_(Code::ZoneHandle(code.raw())), |
- profile_function_(NULL), |
- code_table_index_(-1) { |
- ASSERT(start_ < end_); |
- // Ensure all kDartCode have a valid code_ object. |
- ASSERT((kind != kDartCode) || (!code_.IsNull())); |
- } |
- |
- uword start() const { return start_; } |
- void set_start(uword start) { |
- start_ = start; |
- } |
- |
- uword end() const { return end_; } |
- void set_end(uword end) { |
- end_ = end; |
- } |
- |
- void AdjustExtent(uword start, uword end) { |
- if (start < start_) { |
- start_ = start; |
- } |
- if (end > end_) { |
- end_ = end; |
- } |
- ASSERT(start_ < end_); |
- } |
- |
- bool contains(uword pc) const { |
- return (pc >= start_) && (pc < end_); |
- } |
- |
- bool overlaps(const CodeRegion* other) const { |
- ASSERT(other != NULL); |
- return other->contains(start_) || |
- other->contains(end_ - 1) || |
- contains(other->start()) || |
- contains(other->end() - 1); |
- } |
- |
- int64_t compile_timestamp() const { return compile_timestamp_; } |
- void set_compile_timestamp(int64_t timestamp) { |
- compile_timestamp_ = timestamp; |
- } |
- |
- intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
- void set_inclusive_ticks(intptr_t inclusive_ticks) { |
- inclusive_ticks_ = inclusive_ticks; |
- } |
- void inc_inclusive_ticks() { |
- inclusive_ticks_++; |
- } |
- |
- intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
- void set_exclusive_ticks(intptr_t exclusive_ticks) { |
- exclusive_ticks_ = exclusive_ticks; |
- } |
- |
- const char* name() const { return name_; } |
- void SetName(const char* name) { |
- if (name == NULL) { |
- name_ = NULL; |
- } |
- intptr_t len = strlen(name); |
- name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); |
- strncpy(const_cast<char*>(name_), name, len); |
- const_cast<char*>(name_)[len] = '\0'; |
- } |
- |
- bool IsOptimizedDart() const { |
- return !code_.IsNull() && code_.is_optimized(); |
- } |
- |
- RawCode* code() const { |
- return code_.raw(); |
- } |
- |
- ProfileFunction* SetFunctionAndName(ProfileFunctionTable* table) { |
- ASSERT(profile_function_ == NULL); |
- |
- ProfileFunction* function = NULL; |
- if ((kind() == kReusedCode) || (kind() == kCollectedCode)) { |
- if (name() == NULL) { |
- // Lazily set generated name. |
- GenerateAndSetSymbolName("[Collected]"); |
- } |
- // Map these to a canonical unknown function. |
- function = table->GetUnknown(); |
- } else if (kind() == kDartCode) { |
- ASSERT(!code_.IsNull()); |
- const Object& obj = Object::Handle(code_.owner()); |
- if (obj.IsFunction()) { |
- const String& user_name = String::Handle(code_.PrettyName()); |
- function = table->LookupOrAdd(Function::Cast(obj)); |
- SetName(user_name.ToCString()); |
+ // A stub. |
+ const String& user_name = String::Handle(code_.PrettyName()); |
+ function = table->AddStub(start(), user_name.ToCString()); |
+ SetName(user_name.ToCString()); |
+ } |
+ } else if (kind() == kNativeCode) { |
+ if (name() == NULL) { |
+ // Lazily set generated name. |
+ GenerateAndSetSymbolName("[Native]"); |
+ } |
+ function = table->AddNative(start(), name()); |
+ } else if (kind() == kTagCode) { |
+ if (name() == NULL) { |
+ if (UserTags::IsUserTag(start())) { |
+ const char* tag_name = UserTags::TagName(start()); |
+ ASSERT(tag_name != NULL); |
+ SetName(tag_name); |
+ } else if (VMTag::IsVMTag(start()) || |
+ VMTag::IsRuntimeEntryTag(start()) || |
+ VMTag::IsNativeEntryTag(start())) { |
+ const char* tag_name = VMTag::TagName(start()); |
+ ASSERT(tag_name != NULL); |
+ SetName(tag_name); |
} else { |
- // A stub. |
- const String& user_name = String::Handle(code_.PrettyName()); |
- function = table->AddStub(start(), user_name.ToCString()); |
- SetName(user_name.ToCString()); |
- } |
- } else if (kind() == kNativeCode) { |
- if (name() == NULL) { |
- // Lazily set generated name. |
- GenerateAndSetSymbolName("[Native]"); |
- } |
- function = table->AddNative(start(), name()); |
- } else if (kind() == kTagCode) { |
- if (name() == NULL) { |
- if (UserTags::IsUserTag(start())) { |
- const char* tag_name = UserTags::TagName(start()); |
- ASSERT(tag_name != NULL); |
- SetName(tag_name); |
- } else if (VMTag::IsVMTag(start()) || |
- VMTag::IsRuntimeEntryTag(start()) || |
- VMTag::IsNativeEntryTag(start())) { |
- const char* tag_name = VMTag::TagName(start()); |
- ASSERT(tag_name != NULL); |
- SetName(tag_name); |
+ if (start() == VMTag::kRootTagId) { |
+ SetName("Root"); |
} else { |
- if (start() == VMTag::kRootTagId) { |
- SetName("Root"); |
- } else { |
- ASSERT(start() == VMTag::kTruncatedTagId); |
- SetName("[Truncated]"); |
- } |
+ ASSERT(start() == VMTag::kTruncatedTagId); |
+ SetName("[Truncated]"); |
} |
} |
- function = table->AddTag(start(), name()); |
- } else { |
- UNREACHABLE(); |
- } |
- ASSERT(function != NULL); |
- // Register this CodeRegion with this function. |
- function->AddCodeObjectIndex(code_table_index()); |
- profile_function_ = function; |
- return profile_function_; |
- } |
- |
- ProfileFunction* function() const { |
- ASSERT(profile_function_ != NULL); |
- return profile_function_; |
- } |
- |
- void set_code_table_index(intptr_t code_table_index) { |
- ASSERT(code_table_index_ == -1); |
- ASSERT(code_table_index != -1); |
- code_table_index_ = code_table_index; |
- } |
- intptr_t code_table_index() const { |
- ASSERT(code_table_index_ != -1); |
- return code_table_index_; |
- } |
- |
- Kind kind() const { return kind_; } |
- |
- static const char* KindToCString(Kind kind) { |
- switch (kind) { |
- case kDartCode: |
- return "Dart"; |
- case kCollectedCode: |
- return "Collected"; |
- case kNativeCode: |
- return "Native"; |
- case kReusedCode: |
- return "Overwritten"; |
- case kTagCode: |
- return "Tag"; |
} |
+ function = table->AddTag(start(), name()); |
+ } else { |
UNREACHABLE(); |
- return NULL; |
- } |
- |
- void DebugPrint() const { |
- OS::Print("%s [%" Px ", %" Px ") %" Pd64 "\n", |
- KindToCString(kind_), |
- start(), |
- end(), |
- compile_timestamp_); |
} |
+ ASSERT(function != NULL); |
- void Tick(uword pc, bool exclusive, intptr_t serial) { |
- // Assert that exclusive ticks are never passed a valid serial number. |
- ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); |
- if (!exclusive && (inclusive_tick_serial_ == serial)) { |
- // We've already given this code object an inclusive tick for this sample. |
- return; |
- } |
- // Tick the code object. |
- if (exclusive) { |
- exclusive_ticks_++; |
- } else { |
- inclusive_ticks_++; |
- // Mark the last serial we ticked the inclusive count. |
- inclusive_tick_serial_ = serial; |
- } |
- TickAddress(pc, exclusive); |
- } |
+ function->AddProfileCode(code_table_index()); |
- void PrintNativeCode(JSONObject* profile_code_obj) { |
- ASSERT(kind() == kNativeCode); |
- JSONObject obj(profile_code_obj, "code"); |
- obj.AddProperty("type", "@Code"); |
- obj.AddProperty("kind", "Native"); |
- obj.AddProperty("name", name()); |
- obj.AddProperty("_optimized", false); |
- obj.AddPropertyF("start", "%" Px "", start()); |
- obj.AddPropertyF("end", "%" Px "", end()); |
- { |
- // Generate a fake function entry. |
- JSONObject func(&obj, "function"); |
- profile_function_->PrintToJSONObject(&func); |
- } |
- } |
- |
- void PrintCollectedCode(JSONObject* profile_code_obj) { |
- ASSERT(kind() == kCollectedCode); |
- JSONObject obj(profile_code_obj, "code"); |
- obj.AddProperty("type", "@Code"); |
- obj.AddProperty("kind", "Collected"); |
- obj.AddProperty("name", name()); |
- obj.AddProperty("_optimized", false); |
- obj.AddPropertyF("start", "%" Px "", start()); |
- obj.AddPropertyF("end", "%" Px "", end()); |
- { |
- // Generate a fake function entry. |
- JSONObject func(&obj, "function"); |
- profile_function_->PrintToJSONObject(&func); |
- } |
- } |
- |
- void PrintOverwrittenCode(JSONObject* profile_code_obj) { |
- ASSERT(kind() == kReusedCode); |
- JSONObject obj(profile_code_obj, "code"); |
- obj.AddProperty("type", "@Code"); |
- obj.AddProperty("kind", "Collected"); |
- obj.AddProperty("name", name()); |
- obj.AddProperty("_optimized", false); |
- obj.AddPropertyF("start", "%" Px "", start()); |
- obj.AddPropertyF("end", "%" Px "", end()); |
- { |
- // Generate a fake function entry. |
- JSONObject func(&obj, "function"); |
- ASSERT(profile_function_ != NULL); |
- profile_function_->PrintToJSONObject(&func); |
- } |
- } |
- |
- void PrintTagCode(JSONObject* profile_code_obj) { |
- ASSERT(kind() == kTagCode); |
- JSONObject obj(profile_code_obj, "code"); |
- obj.AddProperty("type", "@Code"); |
- obj.AddProperty("kind", "Tag"); |
- obj.AddProperty("name", name()); |
- obj.AddPropertyF("start", "%" Px "", start()); |
- obj.AddPropertyF("end", "%" Px "", end()); |
- obj.AddProperty("_optimized", false); |
- { |
- // Generate a fake function entry. |
- JSONObject func(&obj, "function"); |
- ASSERT(profile_function_ != NULL); |
- profile_function_->PrintToJSONObject(&func); |
- } |
- } |
- |
- void PrintToJSONArray(JSONArray* codes) { |
- JSONObject obj(codes); |
- obj.AddProperty("kind", KindToCString(kind())); |
- obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
- obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
- if (kind() == kDartCode) { |
- ASSERT(!code_.IsNull()); |
- obj.AddProperty("code", code_); |
- } else if (kind() == kCollectedCode) { |
- PrintCollectedCode(&obj); |
- } else if (kind() == kReusedCode) { |
- PrintOverwrittenCode(&obj); |
- } else if (kind() == kTagCode) { |
- PrintTagCode(&obj); |
- } else { |
- ASSERT(kind() == kNativeCode); |
- PrintNativeCode(&obj); |
- } |
- { |
- JSONArray ticks(&obj, "ticks"); |
- for (intptr_t i = 0; i < address_table_.length(); i++) { |
- const AddressEntry& entry = address_table_[i]; |
- ticks.AddValueF("%" Px "", entry.pc); |
- ticks.AddValueF("%" Pd "", entry.exclusive_ticks); |
- ticks.AddValueF("%" Pd "", entry.inclusive_ticks); |
- } |
- } |
- } |
+ function_ = function; |
+ return function_; |
+} |
- private: |
- void TickAddress(uword pc, bool exclusive) { |
- const intptr_t length = address_table_.length(); |
- intptr_t i = 0; |
- for (; i < length; i++) { |
- AddressEntry& entry = address_table_[i]; |
- if (entry.pc == pc) { |
- // Tick the address entry. |
- entry.tick(exclusive); |
- return; |
- } |
- if (entry.pc > pc) { |
- break; |
- } |
- } |
- // New address, add entry. |
- AddressEntry entry; |
- entry.pc = pc; |
- entry.exclusive_ticks = 0; |
- entry.inclusive_ticks = 0; |
- entry.tick(exclusive); |
- if (i < length) { |
- // Insert at i. |
- address_table_.InsertAt(i, entry); |
- } else { |
- // Add to end. |
- address_table_.Add(entry); |
- } |
- } |
- |
- void GenerateAndSetSymbolName(const char* prefix) { |
- const intptr_t kBuffSize = 512; |
- char buff[kBuffSize]; |
- OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
- prefix, start(), end()); |
- SetName(buff); |
- } |
- |
- // CodeRegion kind. |
- const Kind kind_; |
- // CodeRegion start address. |
- uword start_; |
- // CodeRegion end address. |
- uword end_; |
- // Inclusive ticks. |
- intptr_t inclusive_ticks_; |
- // Exclusive ticks. |
- intptr_t exclusive_ticks_; |
- // Inclusive tick serial number, ensures that each CodeRegion is only given |
- // a single inclusive tick per sample. |
- intptr_t inclusive_tick_serial_; |
- // Name of code region. |
- const char* name_; |
- // The compilation timestamp associated with this code region. |
- int64_t compile_timestamp_; |
- // Dart code object (may be null). |
- const Code& code_; |
- // Pointer to ProfileFunction. |
- ProfileFunction* profile_function_; |
- // Final code table index. |
- intptr_t code_table_index_; |
- ZoneGrowableArray<AddressEntry> address_table_; |
- DISALLOW_COPY_AND_ASSIGN(CodeRegion); |
-}; |
+typedef bool (*RangeCompare)(uword pc, uword region_start, uword region_end); |
-// A sorted table of CodeRegions. Does not allow for overlap. |
-class CodeRegionTable : public ValueObject { |
+class ProfileCodeTable : public ZoneAllocated { |
public: |
- enum TickResult { |
- kTicked = 0, // CodeRegion found and ticked. |
- kNotFound = -1, // No CodeRegion found. |
- kNewerCode = -2, // CodeRegion found but it was compiled after sample. |
- }; |
- |
- CodeRegionTable() : |
- code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { |
- } |
- |
- // Ticks the CodeRegion containing pc if it is alive at timestamp. |
- TickResult Tick(uword pc, |
- bool exclusive, |
- intptr_t serial, |
- int64_t timestamp) { |
- intptr_t index = FindIndex(pc); |
- if (index < 0) { |
- // Not found. |
- return kNotFound; |
- } |
- ASSERT(index < code_region_table_->length()); |
- CodeRegion* region = At(index); |
- if (region->compile_timestamp() > timestamp) { |
- // Compiled after tick. |
- return kNewerCode; |
- } |
- region->Tick(pc, exclusive, serial); |
- return kTicked; |
+ ProfileCodeTable() |
+ : table_(8) { |
} |
- // Table length. |
- intptr_t Length() const { return code_region_table_->length(); } |
+ intptr_t length() const { return table_.length(); } |
- // Get the CodeRegion at index. |
- CodeRegion* At(intptr_t index) const { |
- return (*code_region_table_)[index]; |
+ ProfileCode* At(intptr_t index) const { |
+ ASSERT(index >= 0); |
+ ASSERT(index < length()); |
+ return table_[index]; |
} |
- // Find the table index to the CodeRegion containing pc. |
+ // Find the table index to the ProfileCode containing pc. |
// Returns < 0 if not found. |
- intptr_t FindIndex(uword pc) const { |
- intptr_t index = FindRegionIndex(pc, &CompareLowerBound); |
- const CodeRegion* code_region = NULL; |
- if (index == code_region_table_->length()) { |
+ intptr_t FindCodeIndexForPC(uword pc) const { |
+ intptr_t index = FindCodeIndex(pc, &CompareLowerBound); |
+ if (index == length()) { |
+ // Not present. |
+ return -1; |
+ } |
+ const ProfileCode* code = At(index); |
+ if (!code->Contains(pc)) { |
// Not present. |
return -1; |
} |
- code_region = At(index); |
- if (code_region->contains(pc)) { |
- // Found at index. |
- return index; |
+ // Found at index. |
+ return index; |
+ } |
+ |
+ ProfileCode* FindCodeForPC(uword pc) const { |
+ intptr_t index = FindCodeIndexForPC(pc); |
+ if (index < 0) { |
+ return NULL; |
} |
- return -2; |
+ return At(index); |
} |
- // Insert code_region into the table. Returns the table index where the |
- // CodeRegion was inserted. Will merge with an overlapping CodeRegion if |
- // one is present. |
- intptr_t InsertCodeRegion(CodeRegion* code_region) { |
- const uword start = code_region->start(); |
- const uword end = code_region->end(); |
- const intptr_t length = code_region_table_->length(); |
+ // Insert |new_code| into the table. Returns the table index where |new_code| |
+ // was inserted. Will merge with an overlapping ProfileCode if one is present. |
+ intptr_t InsertCode(ProfileCode* new_code) { |
+ const uword start = new_code->start(); |
+ const uword end = new_code->end(); |
+ const intptr_t length = table_.length(); |
if (length == 0) { |
- code_region_table_->Add(code_region); |
+ table_.Add(new_code); |
return length; |
} |
- // Determine the correct place to insert or merge code_region into table. |
- intptr_t lo = FindRegionIndex(start, &CompareLowerBound); |
- intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); |
+ // Determine the correct place to insert or merge |new_code| into table. |
+ intptr_t lo = FindCodeIndex(start, &CompareLowerBound); |
+ intptr_t hi = FindCodeIndex(end - 1, &CompareUpperBound); |
// TODO(johnmccutchan): Simplify below logic. |
if ((lo == length) && (hi == length)) { |
lo = length - 1; |
} |
if (lo == length) { |
- CodeRegion* region = At(hi); |
- if (region->overlaps(code_region)) { |
- HandleOverlap(region, code_region, start, end); |
+ ProfileCode* code = At(hi); |
+ if (code->Overlaps(new_code)) { |
+ HandleOverlap(code, new_code, start, end); |
return hi; |
} |
- code_region_table_->Add(code_region); |
+ table_.Add(new_code); |
return length; |
} else if (hi == length) { |
- CodeRegion* region = At(lo); |
- if (region->overlaps(code_region)) { |
- HandleOverlap(region, code_region, start, end); |
+ ProfileCode* code = At(lo); |
+ if (code->Overlaps(new_code)) { |
+ HandleOverlap(code, new_code, start, end); |
return lo; |
} |
- code_region_table_->Add(code_region); |
+ table_.Add(new_code); |
return length; |
} else if (lo == hi) { |
- CodeRegion* region = At(lo); |
- if (region->overlaps(code_region)) { |
- HandleOverlap(region, code_region, start, end); |
+ ProfileCode* code = At(lo); |
+ if (code->Overlaps(new_code)) { |
+ HandleOverlap(code, new_code, start, end); |
return lo; |
} |
- code_region_table_->InsertAt(lo, code_region); |
+ table_.InsertAt(lo, new_code); |
return lo; |
} else { |
- CodeRegion* region = At(lo); |
- if (region->overlaps(code_region)) { |
- HandleOverlap(region, code_region, start, end); |
+ ProfileCode* code = At(lo); |
+ if (code->Overlaps(new_code)) { |
+ HandleOverlap(code, new_code, start, end); |
return lo; |
} |
- region = At(hi); |
- if (region->overlaps(code_region)) { |
- HandleOverlap(region, code_region, start, end); |
+ code = At(hi); |
+ if (code->Overlaps(new_code)) { |
+ HandleOverlap(code, new_code, start, end); |
return hi; |
} |
- code_region_table_->InsertAt(hi, code_region); |
+ table_.InsertAt(hi, new_code); |
return hi; |
} |
UNREACHABLE(); |
} |
- void Verify() { |
- VerifyOrder(); |
- VerifyOverlap(); |
- } |
- |
- void DebugPrint() { |
- OS::Print("Dumping CodeRegionTable:\n"); |
- for (intptr_t i = 0; i < code_region_table_->length(); i++) { |
- CodeRegion* region = At(i); |
- region->DebugPrint(); |
- } |
- } |
- |
private: |
- intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { |
+ intptr_t FindCodeIndex(uword pc, RangeCompare comparator) const { |
ASSERT(comparator != NULL); |
- intptr_t count = code_region_table_->length(); |
+ intptr_t count = table_.length(); |
intptr_t first = 0; |
while (count > 0) { |
intptr_t it = first; |
intptr_t step = count / 2; |
it += step; |
- const CodeRegion* code_region = At(it); |
- if (comparator(pc, code_region->start(), code_region->end())) { |
+ const ProfileCode* code = At(it); |
+ if (comparator(pc, code->start(), code->end())) { |
first = ++it; |
count -= (step + 1); |
} else { |
@@ -902,387 +716,126 @@ class CodeRegionTable : public ValueObject { |
return end <= pc; |
} |
- void HandleOverlap(CodeRegion* region, CodeRegion* code_region, |
+ void HandleOverlap(ProfileCode* existing, ProfileCode* code, |
uword start, uword end) { |
// We should never see overlapping Dart code regions. |
- ASSERT(region->kind() != CodeRegion::kDartCode); |
+ ASSERT(existing->kind() != ProfileCode::kDartCode); |
// We should never see overlapping Tag code regions. |
- ASSERT(region->kind() != CodeRegion::kTagCode); |
+ ASSERT(existing->kind() != ProfileCode::kTagCode); |
// When code regions overlap, they should be of the same kind. |
- ASSERT(region->kind() == code_region->kind()); |
- region->AdjustExtent(start, end); |
+ ASSERT(existing->kind() == code->kind()); |
+ existing->AdjustExtent(start, end); |
} |
void VerifyOrder() { |
- const intptr_t length = code_region_table_->length(); |
+ const intptr_t length = table_.length(); |
if (length == 0) { |
return; |
} |
- uword last = (*code_region_table_)[0]->end(); |
+ uword last = table_[0]->end(); |
for (intptr_t i = 1; i < length; i++) { |
- CodeRegion* a = (*code_region_table_)[i]; |
+ ProfileCode* a = table_[i]; |
ASSERT(last <= a->start()); |
last = a->end(); |
} |
} |
void VerifyOverlap() { |
- const intptr_t length = code_region_table_->length(); |
+ const intptr_t length = table_.length(); |
for (intptr_t i = 0; i < length; i++) { |
- CodeRegion* a = (*code_region_table_)[i]; |
+ ProfileCode* a = table_[i]; |
for (intptr_t j = i+1; j < length; j++) { |
- CodeRegion* b = (*code_region_table_)[j]; |
- ASSERT(!a->contains(b->start()) && |
- !a->contains(b->end() - 1) && |
- !b->contains(a->start()) && |
- !b->contains(a->end() - 1)); |
+ ProfileCode* b = table_[j]; |
+ ASSERT(!a->Contains(b->start()) && |
+ !a->Contains(b->end() - 1) && |
+ !b->Contains(a->start()) && |
+ !b->Contains(a->end() - 1)); |
} |
} |
} |
- ZoneGrowableArray<CodeRegion*>* code_region_table_; |
+ ZoneGrowableArray<ProfileCode*> table_; |
}; |
-class CodeRegionTableBuilder { |
- public: |
- CodeRegionTableBuilder(Isolate* isolate, |
- CodeRegionTable* live_code_table, |
- CodeRegionTable* dead_code_table, |
- CodeRegionTable* tag_code_table, |
- DeoptimizedCodeSet* deoptimized_code) |
- : live_code_table_(live_code_table), |
- dead_code_table_(dead_code_table), |
- tag_code_table_(tag_code_table), |
- isolate_(isolate), |
- vm_isolate_(Dart::vm_isolate()), |
- null_code_(Code::ZoneHandle()), |
- deoptimized_code_(deoptimized_code) { |
- ASSERT(live_code_table_ != NULL); |
- ASSERT(dead_code_table_ != NULL); |
- ASSERT(tag_code_table_ != NULL); |
- ASSERT(isolate_ != NULL); |
- ASSERT(vm_isolate_ != NULL); |
- ASSERT(null_code_.IsNull()); |
- frames_ = 0; |
- min_time_ = kMaxInt64; |
- max_time_ = 0; |
- } |
+ProfileTrieNode::ProfileTrieNode(intptr_t table_index) |
+ : table_index_(table_index), |
+ count_(0), |
+ children_(0) { |
+ ASSERT(table_index_ >= 0); |
+} |
- void Build(ProcessedSampleBuffer* buffer) { |
- for (intptr_t i = 0; i < buffer->length(); i++) { |
- ProcessedSample* sample = buffer->At(i); |
- VisitSample(i, sample); |
- } |
- } |
- intptr_t frames() const { return frames_; } |
+ProfileTrieNode::~ProfileTrieNode() { |
+} |
- intptr_t TimeDeltaMicros() const { |
- return static_cast<intptr_t>(max_time_ - min_time_); |
- } |
- int64_t max_time() const { return max_time_; } |
- private: |
- void VisitSample(intptr_t serial, ProcessedSample* sample) { |
- int64_t timestamp = sample->timestamp(); |
- if (timestamp > max_time_) { |
- max_time_ = timestamp; |
- } |
- if (timestamp < min_time_) { |
- min_time_ = timestamp; |
- } |
+void ProfileTrieNode::SortChildren() { |
+ children_.Sort(ProfileTrieNodeCompare); |
+ // Recurse. |
+ for (intptr_t i = 0; i < children_.length(); i++) { |
+ children_[i]->SortChildren(); |
+ } |
+} |
- // Make sure VM tag is created. |
- if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
- CreateTag(VMTag::kNativeTagId); |
- } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
- CreateTag(VMTag::kRuntimeTagId); |
- } |
- CreateTag(sample->vm_tag()); |
- // Make sure user tag is created. |
- CreateUserTag(sample->user_tag()); |
+class ProfileCodeTrieNode : public ProfileTrieNode { |
+ public: |
+ explicit ProfileCodeTrieNode(intptr_t table_index) |
+ : ProfileTrieNode(table_index) { |
+ } |
- // Exclusive tick for top frame if the first frame was executing. |
- if (!sample->first_frame_executing()) { |
- Tick(sample->At(0), true, serial, timestamp); |
+ void PrintToJSONArray(JSONArray* array) const { |
+ ASSERT(array != NULL); |
+ // Write CodeRegion index. |
+ array->AddValue(table_index()); |
+ // Write count. |
+ array->AddValue(count()); |
+ // Write number of children. |
+ intptr_t child_count = NumChildren(); |
+ array->AddValue(child_count); |
+ // Recurse. |
+ for (intptr_t i = 0; i < child_count; i++) { |
+ children_[i]->PrintToJSONArray(array); |
} |
+ } |
- // Inclusive tick for all frames. |
- for (intptr_t i = 0; i < sample->length(); i++) { |
- ASSERT(sample->At(i) != 0); |
- frames_++; |
- Tick(sample->At(i), false, serial, timestamp); |
+ ProfileCodeTrieNode* GetChild(intptr_t child_table_index) { |
+ const intptr_t length = NumChildren(); |
+ intptr_t i = 0; |
+ while (i < length) { |
+ ProfileCodeTrieNode* child = |
+ reinterpret_cast<ProfileCodeTrieNode*>(children_[i]); |
+ if (child->table_index() == child_table_index) { |
+ return child; |
+ } |
+ if (child->table_index() > child_table_index) { |
+ break; |
+ } |
+ i++; |
+ } |
+ ProfileCodeTrieNode* child = new ProfileCodeTrieNode(child_table_index); |
+ if (i < length) { |
+ // Insert at i. |
+ children_.InsertAt(i, reinterpret_cast<ProfileTrieNode*>(child)); |
+ } else { |
+ // Add to end. |
+ children_.Add(reinterpret_cast<ProfileTrieNode*>(child)); |
} |
+ return child; |
} |
+}; |
- void CreateTag(uword tag) { |
- intptr_t index = tag_code_table_->FindIndex(tag); |
- if (index >= 0) { |
- // Already created. |
- return; |
- } |
- CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, |
- tag, |
- tag + 1, |
- 0, |
- null_code_); |
- index = tag_code_table_->InsertCodeRegion(region); |
- ASSERT(index >= 0); |
+ |
+class ProfileFunctionTrieNodeCode { |
+ public: |
+ explicit ProfileFunctionTrieNodeCode(intptr_t index) |
+ : code_index_(index), |
+ ticks_(0) { |
} |
- void CreateUserTag(uword tag) { |
- if (tag == 0) { |
- // None set. |
- return; |
- } |
- return CreateTag(tag); |
- } |
- |
- void Tick(uword pc, bool exclusive, intptr_t serial, int64_t timestamp) { |
- CodeRegionTable::TickResult r; |
- if (exclusive) { |
- // Exclusive ticks do not have an associated serial. |
- serial = -1; |
- } |
- |
- r = live_code_table_->Tick(pc, exclusive, serial, timestamp); |
- if (r == CodeRegionTable::kTicked) { |
- // Live code found and ticked. |
- return; |
- } |
- |
- if (r == CodeRegionTable::kNewerCode) { |
- // Code has been overwritten by newer code. |
- // Update shadow table of dead code regions. |
- r = dead_code_table_->Tick(pc, exclusive, serial, timestamp); |
- ASSERT(r != CodeRegionTable::kNewerCode); |
- if (r == CodeRegionTable::kTicked) { |
- // Dead code found and ticked. |
- return; |
- } |
- ASSERT(r == CodeRegionTable::kNotFound); |
- CreateAndTickDeadCodeRegion(pc, exclusive, serial); |
- return; |
- } |
- |
- // Create new live CodeRegion. |
- ASSERT(r == CodeRegionTable::kNotFound); |
- CodeRegion* region = CreateCodeRegion(pc); |
- intptr_t index = live_code_table_->InsertCodeRegion(region); |
- ASSERT(index >= 0); |
- region = live_code_table_->At(index); |
- if (region->compile_timestamp() <= timestamp) { |
- region->Tick(pc, exclusive, serial); |
- return; |
- } |
- |
- // We have created a new code region but it's for a CodeRegion |
- // compiled after the sample. |
- ASSERT(region->kind() == CodeRegion::kDartCode); |
- CreateAndTickDeadCodeRegion(pc, exclusive, serial); |
- } |
- |
- void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { |
- // Need to create dead code. |
- CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, |
- pc, |
- pc + 1, |
- 0, |
- null_code_); |
- intptr_t index = dead_code_table_->InsertCodeRegion(region); |
- ASSERT(index >= 0); |
- dead_code_table_->At(index)->Tick(pc, exclusive, serial); |
- } |
- |
- CodeRegion* CreateCodeRegion(uword pc) { |
- const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
- const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
- Code& code = Code::Handle(isolate_); |
- |
- // Check current isolate for pc. |
- if (isolate_->heap()->CodeContains(pc)) { |
- code ^= Code::LookupCode(pc); |
- if (!code.IsNull()) { |
- deoptimized_code_->Add(code); |
- return new CodeRegion(CodeRegion::kDartCode, |
- code.EntryPoint(), |
- code.EntryPoint() + code.Size(), |
- code.compile_timestamp(), |
- code); |
- } |
- return new CodeRegion(CodeRegion::kCollectedCode, |
- pc, |
- (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
- 0, |
- code); |
- } |
- |
- // Check VM isolate for pc. |
- if (vm_isolate_->heap()->CodeContains(pc)) { |
- code ^= Code::LookupCodeInVmIsolate(pc); |
- if (!code.IsNull()) { |
- return new CodeRegion(CodeRegion::kDartCode, |
- code.EntryPoint(), |
- code.EntryPoint() + code.Size(), |
- code.compile_timestamp(), |
- code); |
- } |
- return new CodeRegion(CodeRegion::kCollectedCode, |
- pc, |
- (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
- 0, |
- code); |
- } |
- |
- // Check NativeSymbolResolver for pc. |
- uintptr_t native_start = 0; |
- char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
- &native_start); |
- if (native_name == NULL) { |
- // No native name found. |
- return new CodeRegion(CodeRegion::kNativeCode, |
- pc, |
- pc + 1, |
- 0, |
- code); |
- } |
- ASSERT(pc >= native_start); |
- CodeRegion* code_region = |
- new CodeRegion(CodeRegion::kNativeCode, |
- native_start, |
- pc + 1, |
- 0, |
- code); |
- code_region->SetName(native_name); |
- free(native_name); |
- return code_region; |
- } |
- |
- intptr_t frames_; |
- int64_t min_time_; |
- int64_t max_time_; |
- CodeRegionTable* live_code_table_; |
- CodeRegionTable* dead_code_table_; |
- CodeRegionTable* tag_code_table_; |
- Isolate* isolate_; |
- Isolate* vm_isolate_; |
- const Code& null_code_; |
- DeoptimizedCodeSet* deoptimized_code_; |
-}; |
- |
- |
-class CodeRegionFunctionMapper : public ValueObject { |
- public: |
- CodeRegionFunctionMapper(Isolate* isolate, |
- CodeRegionTable* live_code_table, |
- CodeRegionTable* dead_code_table, |
- CodeRegionTable* tag_code_table, |
- ProfileFunctionTable* function_table) |
- : isolate_(isolate), |
- live_code_table_(live_code_table), |
- dead_code_table_(dead_code_table), |
- tag_code_table_(tag_code_table), |
- function_table_(function_table) { |
- ASSERT(isolate_ != NULL); |
- ASSERT(live_code_table_ != NULL); |
- ASSERT(dead_code_table_ != NULL); |
- ASSERT(tag_code_table_ != NULL); |
- dead_code_table_offset_ = live_code_table_->Length(); |
- tag_code_table_offset_ = dead_code_table_offset_ + |
- dead_code_table_->Length(); |
- |
- const Code& null_code = Code::ZoneHandle(); |
- |
- // Create the truncated tag. |
- intptr_t truncated_index = |
- tag_code_table_->FindIndex(VMTag::kTruncatedTagId); |
- ASSERT(truncated_index < 0); |
- CodeRegion* truncated = |
- new CodeRegion(CodeRegion::kTagCode, |
- VMTag::kTruncatedTagId, |
- VMTag::kTruncatedTagId + 1, |
- 0, |
- null_code); |
- truncated_index = tag_code_table_->InsertCodeRegion(truncated); |
- ASSERT(truncated_index >= 0); |
- |
- // Create the root tag. |
- intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); |
- ASSERT(root_index < 0); |
- CodeRegion* root = new CodeRegion(CodeRegion::kTagCode, |
- VMTag::kRootTagId, |
- VMTag::kRootTagId + 1, |
- 0, |
- null_code); |
- root_index = tag_code_table_->InsertCodeRegion(root); |
- ASSERT(root_index >= 0); |
- } |
- |
- void Map() { |
- // Calculate final indexes in code table for each CodeRegion. |
- for (intptr_t i = 0; i < live_code_table_->Length(); i++) { |
- const intptr_t index = i; |
- CodeRegion* region = live_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->set_code_table_index(index); |
- } |
- |
- for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { |
- const intptr_t index = dead_code_table_offset_ + i; |
- CodeRegion* region = dead_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->set_code_table_index(index); |
- } |
- |
- for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { |
- const intptr_t index = tag_code_table_offset_ + i; |
- CodeRegion* region = tag_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->set_code_table_index(index); |
- } |
- |
- // Associate a ProfileFunction with each CodeRegion. |
- for (intptr_t i = 0; i < live_code_table_->Length(); i++) { |
- CodeRegion* region = live_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->SetFunctionAndName(function_table_); |
- } |
- |
- for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { |
- CodeRegion* region = dead_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->SetFunctionAndName(function_table_); |
- } |
- |
- for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { |
- CodeRegion* region = tag_code_table_->At(i); |
- ASSERT(region != NULL); |
- region->SetFunctionAndName(function_table_); |
- } |
- } |
- |
- private: |
- Isolate* isolate_; |
- CodeRegionTable* live_code_table_; |
- CodeRegionTable* dead_code_table_; |
- CodeRegionTable* tag_code_table_; |
- ProfileFunctionTable* function_table_; |
- intptr_t dead_code_table_offset_; |
- intptr_t tag_code_table_offset_; |
-}; |
- |
- |
-class ProfileFunctionTrieNodeCode { |
- public: |
- explicit ProfileFunctionTrieNodeCode(intptr_t index) |
- : code_index_(index), |
- ticks_(0) { |
- } |
- |
- intptr_t index() const { |
- return code_index_; |
+ intptr_t index() const { |
+ return code_index_; |
} |
void Tick() { |
@@ -1299,56 +852,65 @@ class ProfileFunctionTrieNodeCode { |
}; |
-class ProfileFunctionTrieNode : public ZoneAllocated { |
+class ProfileFunctionTrieNode : public ProfileTrieNode { |
public: |
- explicit ProfileFunctionTrieNode(intptr_t profile_function_table_index) |
- : profile_function_table_index_(profile_function_table_index), |
- count_(0), |
- code_objects_(new ZoneGrowableArray<ProfileFunctionTrieNodeCode>()) { |
+ explicit ProfileFunctionTrieNode(intptr_t table_index) |
+ : ProfileTrieNode(table_index), |
+ code_objects_(1) { |
} |
- void Tick() { |
- count_++; |
- } |
- |
- intptr_t count() const { |
- return count_; |
- } |
- |
- intptr_t profile_function_table_index() const { |
- return profile_function_table_index_; |
+ void PrintToJSONArray(JSONArray* array) const { |
+ ASSERT(array != NULL); |
+ // Write CodeRegion index. |
+ array->AddValue(table_index()); |
+ // Write count. |
+ array->AddValue(count()); |
+ // Write number of code objects. |
+ intptr_t code_count = code_objects_.length(); |
+ array->AddValue(code_count); |
+ // Write each code object index and ticks. |
+ for (intptr_t i = 0; i < code_count; i++) { |
+ array->AddValue(code_objects_[i].index()); |
+ array->AddValue(code_objects_[i].ticks()); |
+ } |
+ // Write number of children. |
+ intptr_t child_count = children_.length(); |
+ array->AddValue(child_count); |
+ // Recurse. |
+ for (intptr_t i = 0; i < child_count; i++) { |
+ children_[i]->PrintToJSONArray(array); |
+ } |
} |
- |
- ProfileFunctionTrieNode* GetChild(intptr_t child_index) { |
- const intptr_t length = children_.length(); |
+ ProfileFunctionTrieNode* GetChild(intptr_t child_table_index) { |
+ const intptr_t length = NumChildren(); |
intptr_t i = 0; |
while (i < length) { |
- ProfileFunctionTrieNode* child = children_[i]; |
- if (child->profile_function_table_index() == child_index) { |
+ ProfileFunctionTrieNode* child = |
+ reinterpret_cast<ProfileFunctionTrieNode*>(children_[i]); |
+ if (child->table_index() == child_table_index) { |
return child; |
} |
- if (child->profile_function_table_index() > child_index) { |
+ if (child->table_index() > child_table_index) { |
break; |
} |
i++; |
} |
- // Add new ProfileFunctionTrieNode, sorted by index. |
ProfileFunctionTrieNode* child = |
- new ProfileFunctionTrieNode(child_index); |
+ new ProfileFunctionTrieNode(child_table_index); |
if (i < length) { |
// Insert at i. |
- children_.InsertAt(i, child); |
+ children_.InsertAt(i, reinterpret_cast<ProfileTrieNode*>(child)); |
} else { |
// Add to end. |
- children_.Add(child); |
+ children_.Add(reinterpret_cast<ProfileTrieNode*>(child)); |
} |
return child; |
} |
void AddCodeObjectIndex(intptr_t index) { |
- for (intptr_t i = 0; i < code_objects_->length(); i++) { |
- ProfileFunctionTrieNodeCode& code_object = (*code_objects_)[i]; |
+ for (intptr_t i = 0; i < code_objects_.length(); i++) { |
+ ProfileFunctionTrieNodeCode& code_object = code_objects_[i]; |
if (code_object.index() == index) { |
code_object.Tick(); |
return; |
@@ -1356,414 +918,361 @@ class ProfileFunctionTrieNode : public ZoneAllocated { |
} |
ProfileFunctionTrieNodeCode code_object(index); |
code_object.Tick(); |
- code_objects_->Add(code_object); |
+ code_objects_.Add(code_object); |
} |
- // This should only be called after the trie is completely built. |
- void SortByCount() { |
- code_objects_->Sort(ProfileFunctionTrieNodeCodeCompare); |
- children_.Sort(ProfileFunctionTrieNodeCompare); |
- intptr_t child_count = children_.length(); |
- // Recurse. |
- for (intptr_t i = 0; i < child_count; i++) { |
- children_[i]->SortByCount(); |
- } |
+ private: |
+ ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; |
+}; |
+ |
+ |
+class ProfileBuilder : public ValueObject { |
+ public: |
+ ProfileBuilder(Isolate* isolate, |
+ SampleFilter* filter, |
+ Profile::TagOrder tag_order, |
+ Profile* profile) |
+ : isolate_(isolate), |
+ vm_isolate_(Dart::vm_isolate()), |
+ filter_(filter), |
+ tag_order_(tag_order), |
+ profile_(profile), |
+ deoptimized_code_(new DeoptimizedCodeSet(isolate)), |
+ null_code_(Code::ZoneHandle()), |
+ null_function_(Function::ZoneHandle()), |
+ tick_functions_(false), |
+ samples_(NULL) { |
+ ASSERT(profile_ != NULL); |
} |
- void PrintToJSONArray(JSONArray* array) const { |
- ASSERT(array != NULL); |
- // Write CodeRegion index. |
- array->AddValue(profile_function_table_index_); |
- // Write count. |
- array->AddValue(count_); |
- // Write number of code objects. |
- intptr_t code_count = code_objects_->length(); |
- array->AddValue(code_count); |
- // Write each code object index and ticks. |
- for (intptr_t i = 0; i < code_count; i++) { |
- array->AddValue((*code_objects_)[i].index()); |
- array->AddValue((*code_objects_)[i].ticks()); |
- } |
- // Write number of children. |
- intptr_t child_count = children_.length(); |
- array->AddValue(child_count); |
- // Recurse. |
- for (intptr_t i = 0; i < child_count; i++) { |
- children_[i]->PrintToJSONArray(array); |
- } |
+ void Build() { |
+ ScopeTimer sw("ProfileBuilder::Build", FLAG_trace_profiler); |
+ FilterSamples(); |
+ |
+ Setup(); |
+ BuildCodeTable(); |
+ FinalizeCodeIndexes(); |
+ BuildFunctionTable(); |
+ |
+ BuildCodeTrie(Profile::kExclusiveCode); |
+ BuildCodeTrie(Profile::kInclusiveCode); |
+ |
+ BuildFunctionTrie(Profile::kExclusiveFunction); |
+ BuildFunctionTrie(Profile::kInclusiveFunction); |
} |
private: |
- static int ProfileFunctionTrieNodeCodeCompare( |
- const ProfileFunctionTrieNodeCode* a, |
- const ProfileFunctionTrieNodeCode* b) { |
- ASSERT(a != NULL); |
- ASSERT(b != NULL); |
- return b->ticks() - a->ticks(); |
+ static bool IsInclusiveTrie(Profile::TrieKind kind) { |
+ return (kind == Profile::kInclusiveFunction) || |
+ (kind == Profile::kInclusiveCode); |
+ } |
+ |
+ void Setup() { |
+ profile_->live_code_ = new ProfileCodeTable(); |
+ profile_->dead_code_ = new ProfileCodeTable(); |
+ profile_->tag_code_ = new ProfileCodeTable(); |
+ profile_->functions_ = new ProfileFunctionTable(); |
+ // Register some synthetic tags. |
+ RegisterProfileCodeTag(VMTag::kRootTagId); |
+ RegisterProfileCodeTag(VMTag::kTruncatedTagId); |
+ } |
+ |
+ void FilterSamples() { |
+ ScopeTimer sw("ProfileBuilder::FilterSamples", FLAG_trace_profiler); |
+ MutexLocker profiler_data_lock(isolate_->profiler_data_mutex()); |
+ IsolateProfilerData* profiler_data = isolate_->profiler_data(); |
+ if (profiler_data == NULL) { |
+ return; |
+ } |
+ SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
+ if (sample_buffer == NULL) { |
+ return; |
+ } |
+ samples_ = sample_buffer->BuildProcessedSampleBuffer(filter_); |
+ profile_->sample_count_ = samples_->length(); |
} |
- static int ProfileFunctionTrieNodeCompare(ProfileFunctionTrieNode* const* a, |
- ProfileFunctionTrieNode* const* b) { |
- ASSERT(a != NULL); |
- ASSERT(b != NULL); |
- return (*b)->count() - (*a)->count(); |
+ void UpdateMinMaxTimes(int64_t timestamp) { |
+ profile_->min_time_ = |
+ timestamp < profile_->min_time_ ? timestamp : profile_->min_time_; |
+ profile_->max_time_ = |
+ timestamp > profile_->max_time_ ? timestamp : profile_->max_time_; |
} |
- const intptr_t profile_function_table_index_; |
- intptr_t count_; |
- ZoneGrowableArray<ProfileFunctionTrieNode*> children_; |
- ZoneGrowableArray<ProfileFunctionTrieNodeCode>* code_objects_; |
-}; |
+ void BuildCodeTable() { |
+ ScopeTimer sw("ProfileBuilder::BuildCodeTable", FLAG_trace_profiler); |
+ for (intptr_t i = 0; i < samples_->length(); i++) { |
+ ProcessedSample* sample = samples_->At(i); |
+ const int64_t timestamp = sample->timestamp(); |
+ // This is our first pass over the sample buffer, use this as an |
+ // opportunity to determine the min and max time ranges of this profile. |
+ UpdateMinMaxTimes(timestamp); |
-class TrieBuilder : public ValueObject { |
- public: |
- TrieBuilder(CodeRegionTable* live_code_table, |
- CodeRegionTable* dead_code_table, |
- CodeRegionTable* tag_code_table) |
- : live_code_table_(live_code_table), |
- dead_code_table_(dead_code_table), |
- tag_code_table_(tag_code_table) { |
- ASSERT(live_code_table_ != NULL); |
- ASSERT(dead_code_table_ != NULL); |
- ASSERT(tag_code_table_ != NULL); |
- } |
- |
- ProfilerService::TagOrder tag_order() const { |
- return tag_order_; |
+ // Make sure VM tag exists. |
+ if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
+ RegisterProfileCodeTag(VMTag::kNativeTagId); |
+ } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
+ RegisterProfileCodeTag(VMTag::kRuntimeTagId); |
+ } |
+ RegisterProfileCodeTag(sample->vm_tag()); |
+ // Make sure user tag exists. |
+ RegisterProfileCodeTag(sample->user_tag()); |
+ |
+ // Make sure that a ProfileCode objects exist for all pcs in the sample |
+ // and tick each one. |
+ for (intptr_t i = 0; i < sample->length(); i++) { |
+ const uword pc = sample->At(i); |
+ ASSERT(pc != 0); |
+ ProfileCode* code = RegisterProfileCode(pc, timestamp); |
+ ASSERT(code != NULL); |
+ code->Tick(pc, (i == 0), i); |
+ } |
+ } |
} |
- void set_tag_order(ProfilerService::TagOrder tag_order) { |
- tag_order_ = tag_order; |
- } |
+ void FinalizeCodeIndexes() { |
+ ScopeTimer sw("ProfileBuilder::FinalizeCodeIndexes", FLAG_trace_profiler); |
+ ProfileCodeTable* live_table = profile_->live_code_; |
+ ProfileCodeTable* dead_table = profile_->dead_code_; |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ const intptr_t dead_code_index_offset = live_table->length(); |
+ const intptr_t tag_code_index_offset = |
+ dead_table->length() + dead_code_index_offset; |
- protected: |
- intptr_t FindTagIndex(uword tag) const { |
- if (tag == 0) { |
- UNREACHABLE(); |
- return -1; |
+ profile_->dead_code_index_offset_ = dead_code_index_offset; |
+ profile_->tag_code_index_offset_ = tag_code_index_offset; |
+ |
+ for (intptr_t i = 0; i < live_table->length(); i++) { |
+ const intptr_t index = i; |
+ ProfileCode* code = live_table->At(i); |
+ ASSERT(code != NULL); |
+ code->set_code_table_index(index); |
} |
- intptr_t index = tag_code_table_->FindIndex(tag); |
- if (index < 0) { |
- UNREACHABLE(); |
- return -1; |
+ |
+ for (intptr_t i = 0; i < dead_table->length(); i++) { |
+ const intptr_t index = dead_code_index_offset + i; |
+ ProfileCode* code = dead_table->At(i); |
+ ASSERT(code != NULL); |
+ code->set_code_table_index(index); |
} |
- ASSERT(index >= 0); |
- CodeRegion* region = tag_code_table_->At(index); |
- ASSERT(region->contains(tag)); |
- return region->code_table_index(); |
- } |
- intptr_t FindDeadIndex(uword pc, int64_t timestamp) const { |
- intptr_t index = dead_code_table_->FindIndex(pc); |
- if (index < 0) { |
- OS::Print("%" Px " cannot be found\n", pc); |
- return -1; |
+ for (intptr_t i = 0; i < tag_table->length(); i++) { |
+ const intptr_t index = tag_code_index_offset + i; |
+ ProfileCode* code = tag_table->At(i); |
+ ASSERT(code != NULL); |
+ code->set_code_table_index(index); |
} |
- CodeRegion* region = dead_code_table_->At(index); |
- ASSERT(region->contains(pc)); |
- ASSERT(region->compile_timestamp() <= timestamp); |
- return region->code_table_index(); |
} |
- intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { |
- intptr_t index = live_code_table_->FindIndex(pc); |
- if (index < 0) { |
- // Try dead code table. |
- return FindDeadIndex(pc, timestamp); |
+ void BuildFunctionTable() { |
+ ScopeTimer sw("ProfileBuilder::BuildFunctionTable", FLAG_trace_profiler); |
+ ProfileCodeTable* live_table = profile_->live_code_; |
+ ProfileCodeTable* dead_table = profile_->dead_code_; |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ ProfileFunctionTable* function_table = profile_->functions_; |
+ for (intptr_t i = 0; i < live_table->length(); i++) { |
+ ProfileCode* code = live_table->At(i); |
+ ASSERT(code != NULL); |
+ code->SetFunctionAndName(function_table); |
} |
- CodeRegion* region = live_code_table_->At(index); |
- ASSERT(region->contains(pc)); |
- if (region->compile_timestamp() > timestamp) { |
- // Overwritten code, find in dead code table. |
- return FindDeadIndex(pc, timestamp); |
+ |
+ for (intptr_t i = 0; i < dead_table->length(); i++) { |
+ ProfileCode* code = dead_table->At(i); |
+ ASSERT(code != NULL); |
+ code->SetFunctionAndName(function_table); |
} |
- ASSERT(region->compile_timestamp() <= timestamp); |
- return region->code_table_index(); |
- } |
- bool vm_tags_emitted() const { |
- return (tag_order_ == ProfilerService::kUserVM) || |
- (tag_order_ == ProfilerService::kVMUser) || |
- (tag_order_ == ProfilerService::kVM); |
+ for (intptr_t i = 0; i < tag_table->length(); i++) { |
+ ProfileCode* code = tag_table->At(i); |
+ ASSERT(code != NULL); |
+ code->SetFunctionAndName(function_table); |
+ } |
} |
- CodeRegion* FindCodeObject(uword pc, int64_t timestamp) const { |
- intptr_t index = live_code_table_->FindIndex(pc); |
- if (index < 0) { |
- return NULL; |
- } |
- CodeRegion* region = live_code_table_->At(index); |
- ASSERT(region->contains(pc)); |
- if (region->compile_timestamp() > timestamp) { |
- // Overwritten code, find in dead code table. |
- index = dead_code_table_->FindIndex(pc); |
- if (index < 0) { |
- return NULL; |
- } |
- region = dead_code_table_->At(index); |
- ASSERT(region->contains(pc)); |
- ASSERT(region->compile_timestamp() <= timestamp); |
- return region; |
+ void BuildCodeTrie(Profile::TrieKind kind) { |
+ ProfileCodeTrieNode* root = |
+ new ProfileCodeTrieNode(GetProfileCodeTagIndex(VMTag::kRootTagId)); |
+ if (IsInclusiveTrie(kind)) { |
+ BuildInclusiveCodeTrie(root); |
+ } else { |
+ BuildExclusiveCodeTrie(root); |
} |
- ASSERT(region->compile_timestamp() <= timestamp); |
- return region; |
+ root->SortChildren(); |
+ profile_->roots_[static_cast<intptr_t>(kind)] = root; |
} |
- CodeRegionTable* live_code_table_; |
- CodeRegionTable* dead_code_table_; |
- CodeRegionTable* tag_code_table_; |
- ProfilerService::TagOrder tag_order_; |
-}; |
+ void BuildInclusiveCodeTrie(ProfileCodeTrieNode* root) { |
+ ScopeTimer sw("ProfileBuilder::BuildInclusiveCodeTrie", |
+ FLAG_trace_profiler); |
+ for (intptr_t i = 0; i < samples_->length(); i++) { |
+ ProcessedSample* sample = samples_->At(i); |
+ // Tick the root. |
+ ProfileCodeTrieNode* current = root; |
+ current->Tick(); |
-class ProfileFunctionTrieBuilder : public TrieBuilder { |
- public: |
- ProfileFunctionTrieBuilder(CodeRegionTable* live_code_table, |
- CodeRegionTable* dead_code_table, |
- CodeRegionTable* tag_code_table, |
- ProfileFunctionTable* function_table) |
- : TrieBuilder(live_code_table, dead_code_table, tag_code_table), |
- function_table_(function_table), |
- inclusive_tree_(false) { |
- ASSERT(function_table_ != NULL); |
- set_tag_order(ProfilerService::kUserVM); |
- |
- // Verify that the truncated tag exists. |
- ASSERT(tag_code_table_->FindIndex(VMTag::kTruncatedTagId) >= 0); |
- |
- // Verify that the root tag exists. |
- intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); |
- ASSERT(root_index >= 0); |
- |
- // Setup root. |
- CodeRegion* region = tag_code_table_->At(root_index); |
- ASSERT(region != NULL); |
- ProfileFunction* function = region->function(); |
- ASSERT(function != NULL); |
- |
- exclusive_root_ = new ProfileFunctionTrieNode(function->index()); |
- inclusive_root_ = new ProfileFunctionTrieNode(function->index()); |
- } |
+ // VM & User tags. |
+ current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
- void VisitSample(intptr_t sample_idx, ProcessedSample* sample) { |
- inclusive_tree_ = false; |
- ProcessSampleExclusive(sample_idx, sample); |
- inclusive_tree_ = true; |
- ProcessSampleInclusive(sample_idx, sample); |
- } |
+ // Truncated tag. |
+ if (sample->truncated()) { |
+ current = AppendTruncatedTag(current); |
+ } |
- void Build(ProcessedSampleBuffer* buffer) { |
- for (intptr_t i = 0; i < buffer->length(); i++) { |
- ProcessedSample* sample = buffer->At(i); |
- VisitSample(i, sample); |
+ // Walk the sampled PCs. |
+ for (intptr_t j = sample->length() - 1; j >= 0; j--) { |
+ ASSERT(sample->At(j) != 0); |
+ intptr_t index = |
+ GetProfileCodeIndex(sample->At(j), sample->timestamp()); |
+ ASSERT(index >= 0); |
+ current = current->GetChild(index); |
+ current->Tick(); |
+ } |
} |
} |
- ProfileFunctionTrieNode* exclusive_root() const { |
- return exclusive_root_; |
- } |
+ void BuildExclusiveCodeTrie(ProfileCodeTrieNode* root) { |
+ ScopeTimer sw("ProfileBuilder::BuildExclusiveCodeTrie", |
+ FLAG_trace_profiler); |
+ for (intptr_t i = 0; i < samples_->length(); i++) { |
+ ProcessedSample* sample = samples_->At(i); |
- ProfileFunctionTrieNode* inclusive_root() const { |
- return inclusive_root_; |
- } |
+ // Tick the root. |
+ ProfileCodeTrieNode* current = root; |
+ current->Tick(); |
- ProfilerService::TagOrder tag_order() const { |
- return tag_order_; |
- } |
+ // VM & User tags. |
+ current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
+ |
+ // Walk the sampled PCs. |
+ for (intptr_t j = 0; j < sample->length(); j++) { |
+ ASSERT(sample->At(j) != 0); |
+ intptr_t index = |
+ GetProfileCodeIndex(sample->At(j), sample->timestamp()); |
+ ASSERT(index >= 0); |
+ current = current->GetChild(index); |
+ |
+ if (j == 0) { |
+ // Executing PC. |
+ if (!sample->first_frame_executing() || vm_tags_emitted()) { |
+ // Only tick if this isn't an exit frame or VM tags are emitted. |
+ current->Tick(); |
+ } |
+ } else { |
+ // Caller PCs. |
+ current->Tick(); |
+ } |
- bool vm_tags_emitted() const { |
- return (tag_order_ == ProfilerService::kUserVM) || |
- (tag_order_ == ProfilerService::kVMUser) || |
- (tag_order_ == ProfilerService::kVM); |
- } |
+ current->Tick(); |
+ } |
- void set_tag_order(ProfilerService::TagOrder tag_order) { |
- tag_order_ = tag_order; |
+ // Truncated tag. |
+ if (sample->truncated()) { |
+ current = AppendTruncatedTag(current); |
+ } |
+ } |
} |
- private: |
- void ProcessSampleInclusive(intptr_t sample_idx, ProcessedSample* sample) { |
- // Give the root a tick. |
- inclusive_root_->Tick(); |
- ProfileFunctionTrieNode* current = inclusive_root_; |
- current = AppendTags(sample, current); |
- if (sample->truncated()) { |
- InclusiveTickTruncatedTag(); |
- current = AppendTruncatedTag(current); |
- } |
- // Walk the sampled PCs. |
- for (intptr_t i = sample->length() - 1; i >= 0; i--) { |
- ASSERT(sample->At(i) != 0); |
- current = ProcessPC(sample->At(i), |
- sample->timestamp(), |
- current, |
- sample_idx, |
- (i == 0), |
- !sample->first_frame_executing() && (i == 0)); |
- } |
- } |
- |
- void ProcessSampleExclusive(intptr_t sample_idx, ProcessedSample* sample) { |
- // Give the root a tick. |
- exclusive_root_->Tick(); |
- ProfileFunctionTrieNode* current = exclusive_root_; |
- current = AppendTags(sample, current); |
- // Walk the sampled PCs. |
- for (intptr_t i = 0; i < sample->length(); i++) { |
- ASSERT(sample->At(i) != 0); |
- current = ProcessPC(sample->At(i), |
- sample->timestamp(), |
- current, |
- sample_idx, |
- (i == 0), |
- !sample->first_frame_executing() && (i == 0)); |
- } |
- if (sample->truncated()) { |
- current = AppendTruncatedTag(current); |
- } |
- } |
- |
- ProfileFunctionTrieNode* AppendUserTag(ProcessedSample* sample, |
- ProfileFunctionTrieNode* current) { |
- intptr_t user_tag_index = FindTagFunctionIndex(sample->user_tag()); |
- if (user_tag_index >= 0) { |
- current = current->GetChild(user_tag_index); |
- // Give the tag a tick. |
- current->Tick(); |
+ void BuildFunctionTrie(Profile::TrieKind kind) { |
+ ProfileFunctionTrieNode* root = |
+ new ProfileFunctionTrieNode( |
+ GetProfileFunctionTagIndex(VMTag::kRootTagId)); |
+ // We tick the functions while building the trie, but, we don't want to do |
+ // it for both tries, just one. |
+ tick_functions_ = IsInclusiveTrie(kind); |
+ if (IsInclusiveTrie(kind)) { |
+ BuildInclusiveFunctionTrie(root); |
+ } else { |
+ BuildExclusiveFunctionTrie(root); |
} |
- return current; |
+ root->SortChildren(); |
+ profile_->roots_[static_cast<intptr_t>(kind)] = root; |
} |
+ void BuildInclusiveFunctionTrie(ProfileFunctionTrieNode* root) { |
+ ScopeTimer sw("ProfileBuilder::BuildInclusiveFunctionTrie", |
+ FLAG_trace_profiler); |
+ for (intptr_t i = 0; i < samples_->length(); i++) { |
+ ProcessedSample* sample = samples_->At(i); |
- ProfileFunctionTrieNode* AppendTruncatedTag( |
- ProfileFunctionTrieNode* current) { |
- intptr_t truncated_tag_index = FindTagFunctionIndex(VMTag::kTruncatedTagId); |
- ASSERT(truncated_tag_index >= 0); |
- current = current->GetChild(truncated_tag_index); |
- current->Tick(); |
- return current; |
- } |
+ // Tick the root. |
+ ProfileFunctionTrieNode* current = root; |
+ current->Tick(); |
- void InclusiveTickTruncatedTag() { |
- intptr_t index = tag_code_table_->FindIndex(VMTag::kTruncatedTagId); |
- CodeRegion* region = tag_code_table_->At(index); |
- ProfileFunction* function = region->function(); |
- function->inc_inclusive_ticks(); |
- } |
+ // VM & User tags. |
+ current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
- ProfileFunctionTrieNode* AppendVMTag(ProcessedSample* sample, |
- ProfileFunctionTrieNode* current) { |
- if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
- // Insert a dummy kNativeTagId node. |
- intptr_t tag_index = FindTagFunctionIndex(VMTag::kNativeTagId); |
- current = current->GetChild(tag_index); |
- // Give the tag a tick. |
- current->Tick(); |
- } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
- // Insert a dummy kRuntimeTagId node. |
- intptr_t tag_index = FindTagFunctionIndex(VMTag::kRuntimeTagId); |
- current = current->GetChild(tag_index); |
- // Give the tag a tick. |
- current->Tick(); |
- } else { |
- intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); |
- current = current->GetChild(tag_index); |
- // Give the tag a tick. |
- current->Tick(); |
- } |
- return current; |
- } |
+ // Truncated tag. |
+ if (sample->truncated()) { |
+ current = AppendTruncatedTag(current); |
+ InclusiveTickTruncatedTag(); |
+ } |
- ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
- ProcessedSample* sample, ProfileFunctionTrieNode* current) { |
- // Only Native and Runtime entries have a second VM tag. |
- if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && |
- !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
- return current; |
+ // Walk the sampled PCs. |
+ for (intptr_t j = sample->length() - 1; j >= 0; j--) { |
+ ASSERT(sample->At(j) != 0); |
+ current = ProcessFunctionPC( |
+ sample->At(j), |
+ sample->timestamp(), |
+ current, |
+ i, |
+ (j == 0), |
+ sample->first_frame_executing(), |
+ true); |
+ } |
} |
- intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); |
- current = current->GetChild(tag_index); |
- // Give the tag a tick. |
- current->Tick(); |
- return current; |
} |
- ProfileFunctionTrieNode* AppendVMTags(ProcessedSample* sample, |
- ProfileFunctionTrieNode* current) { |
- current = AppendVMTag(sample, current); |
- current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); |
- return current; |
- } |
+ void BuildExclusiveFunctionTrie(ProfileFunctionTrieNode* root) { |
+ ScopeTimer sw("ProfileBuilder::BuildExclusiveFunctionTrie", |
+ FLAG_trace_profiler); |
+ for (intptr_t i = 0; i < samples_->length(); i++) { |
+ ProcessedSample* sample = samples_->At(i); |
- ProfileFunctionTrieNode* AppendTags(ProcessedSample* sample, |
- ProfileFunctionTrieNode* current) { |
- // None. |
- if (tag_order() == ProfilerService::kNoTags) { |
- return current; |
- } |
- // User first. |
- if ((tag_order() == ProfilerService::kUserVM) || |
- (tag_order() == ProfilerService::kUser)) { |
- current = AppendUserTag(sample, current); |
- // Only user. |
- if (tag_order() == ProfilerService::kUser) { |
- return current; |
+ // Tick the root. |
+ ProfileFunctionTrieNode* current = root; |
+ current->Tick(); |
+ |
+ // VM & User tags. |
+ current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
+ |
+ // Walk the sampled PCs. |
+ for (intptr_t j = 0; j < sample->length(); j++) { |
+ ASSERT(sample->At(j) != 0); |
+ current = ProcessFunctionPC( |
+ sample->At(j), |
+ sample->timestamp(), |
+ current, |
+ i, |
+ (j == 0), |
+ sample->first_frame_executing(), |
+ false); |
} |
- return AppendVMTags(sample, current); |
- } |
- // VM first. |
- ASSERT((tag_order() == ProfilerService::kVMUser) || |
- (tag_order() == ProfilerService::kVM)); |
- current = AppendVMTags(sample, current); |
- // Only VM. |
- if (tag_order() == ProfilerService::kVM) { |
- return current; |
- } |
- return AppendUserTag(sample, current); |
- } |
- intptr_t FindTagFunctionIndex(uword tag) const { |
- if (tag == 0) { |
- UNREACHABLE(); |
- return -1; |
- } |
- intptr_t index = tag_code_table_->FindIndex(tag); |
- if (index < 0) { |
- UNREACHABLE(); |
- return -1; |
+ // Truncated tag. |
+ if (sample->truncated()) { |
+ current = AppendTruncatedTag(current); |
+ } |
} |
- ASSERT(index >= 0); |
- CodeRegion* region = tag_code_table_->At(index); |
- ASSERT(region->contains(tag)); |
- ProfileFunction* function = region->function(); |
- ASSERT(function != NULL); |
- return function->index(); |
- } |
- |
- void Dump(ProfileFunctionTrieNode* current) { |
- int current_index = current->profile_function_table_index(); |
- ProfileFunction* function = function_table_->At(current_index); |
- function->Dump(); |
- OS::Print("\n"); |
} |
- ProfileFunctionTrieNode* ProcessPC(uword pc, |
- int64_t timestamp, |
- ProfileFunctionTrieNode* current, |
- intptr_t inclusive_serial, |
- bool top_frame, |
- bool exit_frame) { |
- CodeRegion* region = FindCodeObject(pc, timestamp); |
- if (region == NULL) { |
- return current; |
- } |
- const char* region_name = region->name(); |
- if (region_name == NULL) { |
- region_name = ""; |
- } |
- intptr_t code_index = region->code_table_index(); |
- const Code& code = Code::ZoneHandle(region->code()); |
+ ProfileFunctionTrieNode* ProcessFunctionPC( |
+ uword pc, |
+ int64_t timestamp, |
+ ProfileFunctionTrieNode* current, |
+ intptr_t inclusive_serial, |
+ bool top_frame, |
+ bool top_frame_executing, |
+ bool inclusive_tree) { |
+ ProfileCode* profile_code = GetProfileCode(pc, timestamp); |
+ ASSERT(profile_code != NULL); |
+ const char* code_name = profile_code->name(); |
+ if (code_name == NULL) { |
+ code_name = ""; |
+ } |
+ intptr_t code_index = profile_code->code_table_index(); |
+ const Code& code = Code::ZoneHandle(profile_code->code()); |
GrowableArray<Function*> inlined_functions; |
if (!code.IsNull()) { |
intptr_t offset = pc - code.EntryPoint(); |
@@ -1771,18 +1280,18 @@ class ProfileFunctionTrieBuilder : public TrieBuilder { |
} |
if (code.IsNull() || (inlined_functions.length() == 0)) { |
// No inlined functions. |
- ProfileFunction* function = region->function(); |
+ ProfileFunction* function = profile_code->function(); |
ASSERT(function != NULL); |
current = ProcessFunction(function, |
current, |
inclusive_serial, |
top_frame, |
- exit_frame, |
+ top_frame_executing, |
code_index); |
return current; |
} |
- if (inclusive_tree_) { |
+ if (inclusive_tree) { |
for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { |
Function* inlined_function = inlined_functions[i]; |
ASSERT(inlined_function != NULL); |
@@ -1791,7 +1300,7 @@ class ProfileFunctionTrieBuilder : public TrieBuilder { |
current, |
inclusive_serial, |
top_frame, |
- exit_frame, |
+ top_frame_executing, |
code_index); |
top_frame = false; |
} |
@@ -1804,7 +1313,7 @@ class ProfileFunctionTrieBuilder : public TrieBuilder { |
current, |
inclusive_serial, |
top_frame, |
- exit_frame, |
+ top_frame_executing, |
code_index); |
top_frame = false; |
} |
@@ -1818,16 +1327,16 @@ class ProfileFunctionTrieBuilder : public TrieBuilder { |
ProfileFunctionTrieNode* current, |
intptr_t inclusive_serial, |
bool top_frame, |
- bool exit_frame, |
+ bool top_frame_executing, |
intptr_t code_index) { |
- ProfileFunction* function = |
- function_table_->LookupOrAdd(*inlined_function); |
+ ProfileFunctionTable* function_table = profile_->functions_; |
+ ProfileFunction* function = function_table->LookupOrAdd(*inlined_function); |
ASSERT(function != NULL); |
return ProcessFunction(function, |
current, |
inclusive_serial, |
top_frame, |
- exit_frame, |
+ top_frame_executing, |
code_index); |
} |
@@ -1835,254 +1344,171 @@ class ProfileFunctionTrieBuilder : public TrieBuilder { |
ProfileFunctionTrieNode* current, |
intptr_t inclusive_serial, |
bool top_frame, |
- bool exit_frame, |
- intptr_t code_index) { |
- const bool exclusive = top_frame && !exit_frame; |
- if (!inclusive_tree_) { |
- // We process functions for the inclusive and exclusive trees. |
- // Only tick the function for the exclusive tree. |
- function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
- } |
- function->AddCodeObjectIndex(code_index); |
- current = current->GetChild(function->index()); |
- current->AddCodeObjectIndex(code_index); |
- if (top_frame) { |
- if (!exit_frame || vm_tags_emitted()) { |
- // Only tick if this isn't an exit frame or VM tags are emitted. |
- current->Tick(); |
- } |
- } else { |
- current->Tick(); |
- } |
- return current; |
- } |
- |
- ProfileFunctionTrieNode* exclusive_root_; |
- ProfileFunctionTrieNode* inclusive_root_; |
- ProfileFunctionTable* function_table_; |
- bool inclusive_tree_; |
-}; |
- |
- |
-class CodeRegionTrieNode : public ZoneAllocated { |
- public: |
- explicit CodeRegionTrieNode(intptr_t code_region_index) |
- : code_region_index_(code_region_index), |
- count_(0), |
- children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { |
- } |
- |
- void Tick() { |
- ASSERT(code_region_index_ >= 0); |
- count_++; |
- } |
- |
- intptr_t count() const { |
- ASSERT(code_region_index_ >= 0); |
- return count_; |
- } |
- |
- intptr_t code_region_index() const { |
- return code_region_index_; |
- } |
- |
- ZoneGrowableArray<CodeRegionTrieNode*>& children() const { |
- return *children_; |
- } |
- |
- CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { |
- const intptr_t length = children_->length(); |
- intptr_t i = 0; |
- while (i < length) { |
- CodeRegionTrieNode* child = (*children_)[i]; |
- if (child->code_region_index() == child_code_region_index) { |
- return child; |
- } |
- if (child->code_region_index() > child_code_region_index) { |
- break; |
- } |
- i++; |
- } |
- // Add new CodeRegion, sorted by CodeRegionTable index. |
- CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index); |
- if (i < length) { |
- // Insert at i. |
- children_->InsertAt(i, child); |
- } else { |
- // Add to end. |
- children_->Add(child); |
- } |
- return child; |
- } |
- |
- // This should only be called after the trie is completely built. |
- void SortByCount() { |
- children_->Sort(CodeRegionTrieNodeCompare); |
- ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); |
- intptr_t child_count = kids.length(); |
- // Recurse. |
- for (intptr_t i = 0; i < child_count; i++) { |
- kids[i]->SortByCount(); |
- } |
- } |
- |
- void PrintToJSONArray(JSONArray* array) const { |
- ASSERT(array != NULL); |
- // Write CodeRegion index. |
- array->AddValue(code_region_index_); |
- // Write count. |
- array->AddValue(count_); |
- // Write number of children. |
- ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); |
- intptr_t child_count = kids.length(); |
- array->AddValue(child_count); |
- // Recurse. |
- for (intptr_t i = 0; i < child_count; i++) { |
- kids[i]->PrintToJSONArray(array); |
+ bool top_frame_executing, |
+ intptr_t code_index) { |
+ const bool exclusive = top_frame && top_frame_executing; |
+ if (tick_functions_) { |
+ function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
+ } |
+ function->AddProfileCode(code_index); |
+ current = current->GetChild(function->table_index()); |
+ current->AddCodeObjectIndex(code_index); |
+ if (top_frame) { |
+ if (top_frame_executing || vm_tags_emitted()) { |
+ // Only tick if this function is using CPU time or VM tags are emitted. |
+ current->Tick(); |
+ } |
+ } else { |
+ current->Tick(); |
} |
+ return current; |
} |
- private: |
- static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, |
- CodeRegionTrieNode* const* b) { |
- ASSERT(a != NULL); |
- ASSERT(b != NULL); |
- return (*b)->count() - (*a)->count(); |
+ // Tick the truncated tag's inclusive tick count. |
+ void InclusiveTickTruncatedTag() { |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ intptr_t index = tag_table->FindCodeIndexForPC(VMTag::kTruncatedTagId); |
+ ASSERT(index >= 0); |
+ ProfileCode* code = tag_table->At(index); |
+ code->IncInclusiveTicks(); |
+ ASSERT(code != NULL); |
+ ProfileFunction* function = code->function(); |
+ function->IncInclusiveTicks(); |
} |
- const intptr_t code_region_index_; |
- intptr_t count_; |
- ZoneGrowableArray<CodeRegionTrieNode*>* children_; |
-}; |
- |
- |
-class CodeRegionTrieBuilder : public TrieBuilder { |
- public: |
- CodeRegionTrieBuilder(Isolate* isolate, |
- CodeRegionTable* live_code_table, |
- CodeRegionTable* dead_code_table, |
- CodeRegionTable* tag_code_table) |
- : TrieBuilder(live_code_table, dead_code_table, tag_code_table) { |
- set_tag_order(ProfilerService::kUserVM); |
- // Verify that the truncated tag exists. |
- ASSERT(tag_code_table_->FindIndex(VMTag::kTruncatedTagId) >= 0); |
+ // Tag append functions are overloaded for |ProfileCodeTrieNode| and |
+ // |ProfileFunctionTrieNode| types. |
- // Verify that the root tag exists. |
- intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); |
- ASSERT(root_index >= 0); |
- CodeRegion* region = tag_code_table_->At(root_index); |
- ASSERT(region != NULL); |
+ // ProfileCodeTrieNode |
+ ProfileCodeTrieNode* AppendUserTag(uword user_tag, |
+ ProfileCodeTrieNode* current) { |
+ intptr_t user_tag_index = GetProfileCodeTagIndex(user_tag); |
+ if (user_tag_index >= 0) { |
+ current = current->GetChild(user_tag_index); |
+ current->Tick(); |
+ } |
+ return current; |
+ } |
- exclusive_root_ = new CodeRegionTrieNode(region->code_table_index()); |
- inclusive_root_ = new CodeRegionTrieNode(region->code_table_index()); |
+ ProfileCodeTrieNode* AppendTruncatedTag(ProfileCodeTrieNode* current) { |
+ intptr_t truncated_tag_index = |
+ GetProfileCodeTagIndex(VMTag::kTruncatedTagId); |
+ ASSERT(truncated_tag_index >= 0); |
+ current = current->GetChild(truncated_tag_index); |
+ current->Tick(); |
+ return current; |
} |
- void Build(ProcessedSampleBuffer* buffer) { |
- for (intptr_t i = 0; i < buffer->length(); i++) { |
- ProcessedSample* sample = buffer->At(i); |
- VisitSample(sample); |
+ ProfileCodeTrieNode* AppendVMTag(uword vm_tag, |
+ ProfileCodeTrieNode* current) { |
+ if (VMTag::IsNativeEntryTag(vm_tag)) { |
+ // Insert a dummy kNativeTagId node. |
+ intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kNativeTagId); |
+ current = current->GetChild(tag_index); |
+ // Give the tag a tick. |
+ current->Tick(); |
+ } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
+ // Insert a dummy kRuntimeTagId node. |
+ intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kRuntimeTagId); |
+ current = current->GetChild(tag_index); |
+ // Give the tag a tick. |
+ current->Tick(); |
+ } else { |
+ intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); |
+ current = current->GetChild(tag_index); |
+ // Give the tag a tick. |
+ current->Tick(); |
} |
+ return current; |
} |
- CodeRegionTrieNode* inclusive_root() const { |
- return inclusive_root_; |
+ ProfileCodeTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
+ uword vm_tag, ProfileCodeTrieNode* current) { |
+ // Only Native and Runtime entries have a second VM tag. |
+ if (!VMTag::IsNativeEntryTag(vm_tag) && |
+ !VMTag::IsRuntimeEntryTag(vm_tag)) { |
+ return current; |
+ } |
+ intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); |
+ current = current->GetChild(tag_index); |
+ // Give the tag a tick. |
+ current->Tick(); |
+ return current; |
} |
- CodeRegionTrieNode* exclusive_root() const { |
- return exclusive_root_; |
+ ProfileCodeTrieNode* AppendVMTags(uword vm_tag, |
+ ProfileCodeTrieNode* current) { |
+ current = AppendVMTag(vm_tag, current); |
+ current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); |
+ return current; |
} |
- private: |
- void VisitSample(ProcessedSample* sample) { |
- ProcessSampleExclusive(sample); |
- ProcessSampleInclusive(sample); |
- } |
- |
- void ProcessSampleInclusive(ProcessedSample* sample) { |
- // Give the root a tick. |
- inclusive_root_->Tick(); |
- CodeRegionTrieNode* current = inclusive_root_; |
- current = AppendTags(sample, current); |
- if (sample->truncated()) { |
- current = AppendTruncatedTag(current); |
- } |
- // Walk the sampled PCs. |
- for (intptr_t i = sample->length() - 1; i >= 0; i--) { |
- ASSERT(sample->At(i) != 0); |
- intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
- if (index < 0) { |
- continue; |
- } |
- current = current->GetChild(index); |
- current->Tick(); |
+ ProfileCodeTrieNode* AppendTags(uword vm_tag, |
+ uword user_tag, |
+ ProfileCodeTrieNode* current) { |
+ // None. |
+ if (tag_order() == Profile::kNoTags) { |
+ return current; |
} |
- } |
- |
- void ProcessSampleExclusive(ProcessedSample* sample) { |
- // Give the root a tick. |
- exclusive_root_->Tick(); |
- CodeRegionTrieNode* current = exclusive_root_; |
- current = AppendTags(sample, current); |
- // Walk the sampled PCs. |
- for (intptr_t i = 0; i < sample->length(); i++) { |
- ASSERT(sample->At(i) != 0); |
- intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
- if (index < 0) { |
- continue; |
- } |
- current = current->GetChild(index); |
- if (i == 0) { |
- // Executing PC. |
- if (!sample->first_frame_executing() || vm_tags_emitted()) { |
- // Only tick if this isn't an exit frame or VM tags are emitted. |
- current->Tick(); |
- } |
- } else { |
- // Caller PCs. |
- current->Tick(); |
+ // User first. |
+ if ((tag_order() == Profile::kUserVM) || |
+ (tag_order() == Profile::kUser)) { |
+ current = AppendUserTag(user_tag, current); |
+ // Only user. |
+ if (tag_order() == Profile::kUser) { |
+ return current; |
} |
+ return AppendVMTags(vm_tag, current); |
} |
- if (sample->truncated()) { |
- current = AppendTruncatedTag(current); |
+ // VM first. |
+ ASSERT((tag_order() == Profile::kVMUser) || |
+ (tag_order() == Profile::kVM)); |
+ current = AppendVMTags(vm_tag, current); |
+ // Only VM. |
+ if (tag_order() == Profile::kVM) { |
+ return current; |
} |
+ return AppendUserTag(user_tag, current); |
} |
- CodeRegionTrieNode* AppendUserTag(ProcessedSample* sample, |
- CodeRegionTrieNode* current) { |
- intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
+ // ProfileFunctionTrieNode |
+ ProfileFunctionTrieNode* AppendUserTag(uword user_tag, |
+ ProfileFunctionTrieNode* current) { |
+ intptr_t user_tag_index = GetProfileFunctionTagIndex(user_tag); |
if (user_tag_index >= 0) { |
current = current->GetChild(user_tag_index); |
- // Give the tag a tick. |
current->Tick(); |
} |
return current; |
} |
- CodeRegionTrieNode* AppendTruncatedTag(CodeRegionTrieNode* current) { |
- intptr_t truncated_tag_index = FindTagIndex(VMTag::kTruncatedTagId); |
+ ProfileFunctionTrieNode* AppendTruncatedTag( |
+ ProfileFunctionTrieNode* current) { |
+ intptr_t truncated_tag_index = |
+ GetProfileFunctionTagIndex(VMTag::kTruncatedTagId); |
ASSERT(truncated_tag_index >= 0); |
current = current->GetChild(truncated_tag_index); |
current->Tick(); |
return current; |
} |
- CodeRegionTrieNode* AppendVMTag(ProcessedSample* sample, |
- CodeRegionTrieNode* current) { |
- if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
+ ProfileFunctionTrieNode* AppendVMTag(uword vm_tag, |
+ ProfileFunctionTrieNode* current) { |
+ if (VMTag::IsNativeEntryTag(vm_tag)) { |
// Insert a dummy kNativeTagId node. |
- intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); |
+ intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kNativeTagId); |
current = current->GetChild(tag_index); |
// Give the tag a tick. |
current->Tick(); |
- } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
+ } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
// Insert a dummy kRuntimeTagId node. |
- intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); |
+ intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kRuntimeTagId); |
current = current->GetChild(tag_index); |
// Give the tag a tick. |
current->Tick(); |
} else { |
- intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
+ intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
current = current->GetChild(tag_index); |
// Give the tag a tick. |
current->Tick(); |
@@ -2090,59 +1516,384 @@ class CodeRegionTrieBuilder : public TrieBuilder { |
return current; |
} |
- CodeRegionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
- ProcessedSample* sample, CodeRegionTrieNode* current) { |
+ ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
+ uword vm_tag, ProfileFunctionTrieNode* current) { |
// Only Native and Runtime entries have a second VM tag. |
- if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && |
- !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
+ if (!VMTag::IsNativeEntryTag(vm_tag) && |
+ !VMTag::IsRuntimeEntryTag(vm_tag)) { |
return current; |
} |
- intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
+ intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
current = current->GetChild(tag_index); |
// Give the tag a tick. |
current->Tick(); |
return current; |
} |
- CodeRegionTrieNode* AppendVMTags(ProcessedSample* sample, |
- CodeRegionTrieNode* current) { |
- current = AppendVMTag(sample, current); |
- current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); |
+ ProfileFunctionTrieNode* AppendVMTags(uword vm_tag, |
+ ProfileFunctionTrieNode* current) { |
+ current = AppendVMTag(vm_tag, current); |
+ current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); |
return current; |
} |
- CodeRegionTrieNode* AppendTags(ProcessedSample* sample, |
- CodeRegionTrieNode* current) { |
+ ProfileFunctionTrieNode* AppendTags(uword vm_tag, |
+ uword user_tag, |
+ ProfileFunctionTrieNode* current) { |
// None. |
- if (tag_order() == ProfilerService::kNoTags) { |
+ if (tag_order() == Profile::kNoTags) { |
return current; |
} |
// User first. |
- if ((tag_order() == ProfilerService::kUserVM) || |
- (tag_order() == ProfilerService::kUser)) { |
- current = AppendUserTag(sample, current); |
+ if ((tag_order() == Profile::kUserVM) || |
+ (tag_order() == Profile::kUser)) { |
+ current = AppendUserTag(user_tag, current); |
// Only user. |
- if (tag_order() == ProfilerService::kUser) { |
+ if (tag_order() == Profile::kUser) { |
return current; |
} |
- return AppendVMTags(sample, current); |
+ return AppendVMTags(vm_tag, current); |
} |
// VM first. |
- ASSERT((tag_order() == ProfilerService::kVMUser) || |
- (tag_order() == ProfilerService::kVM)); |
- current = AppendVMTags(sample, current); |
+ ASSERT((tag_order() == Profile::kVMUser) || |
+ (tag_order() == Profile::kVM)); |
+ current = AppendVMTags(vm_tag, current); |
// Only VM. |
- if (tag_order() == ProfilerService::kVM) { |
+ if (tag_order() == Profile::kVM) { |
return current; |
} |
- return AppendUserTag(sample, current); |
+ return AppendUserTag(user_tag, current); |
+ } |
+ |
+ intptr_t GetProfileCodeTagIndex(uword tag) { |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ intptr_t index = tag_table->FindCodeIndexForPC(tag); |
+ ASSERT(index >= 0); |
+ ProfileCode* code = tag_table->At(index); |
+ ASSERT(code != NULL); |
+ return code->code_table_index(); |
+ } |
+ |
+ intptr_t GetProfileFunctionTagIndex(uword tag) { |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ intptr_t index = tag_table->FindCodeIndexForPC(tag); |
+ ASSERT(index >= 0); |
+ ProfileCode* code = tag_table->At(index); |
+ ASSERT(code != NULL); |
+ ProfileFunction* function = code->function(); |
+ ASSERT(function != NULL); |
+ return function->table_index(); |
+ } |
+ |
+ intptr_t GetProfileCodeIndex(uword pc, int64_t timestamp) { |
+ return GetProfileCode(pc, timestamp)->code_table_index(); |
+ } |
+ |
+ ProfileCode* GetProfileCode(uword pc, int64_t timestamp) { |
+ ProfileCodeTable* live_table = profile_->live_code_; |
+ ProfileCodeTable* dead_table = profile_->dead_code_; |
+ |
+ intptr_t index = live_table->FindCodeIndexForPC(pc); |
+ ProfileCode* code = NULL; |
+ if (index < 0) { |
+ index = dead_table->FindCodeIndexForPC(pc); |
+ ASSERT(index >= 0); |
+ code = dead_table->At(index); |
+ } else { |
+ code = live_table->At(index); |
+ ASSERT(code != NULL); |
+ if (code->compile_timestamp() > timestamp) { |
+ // Code is newer than sample. Fall back to dead code table. |
+ index = dead_table->FindCodeIndexForPC(pc); |
+ ASSERT(index >= 0); |
+ code = dead_table->At(index); |
+ } |
+ } |
+ |
+ ASSERT(code != NULL); |
+ ASSERT(code->Contains(pc)); |
+ ASSERT(code->compile_timestamp() <= timestamp); |
+ return code; |
+ } |
+ |
+ void RegisterProfileCodeTag(uword tag) { |
+ if (tag == 0) { |
+ // No tag. |
+ return; |
+ } |
+ ProfileCodeTable* tag_table = profile_->tag_code_; |
+ intptr_t index = tag_table->FindCodeIndexForPC(tag); |
+ if (index >= 0) { |
+ // Already created. |
+ return; |
+ } |
+ ProfileCode* code = new ProfileCode(ProfileCode::kTagCode, |
+ tag, |
+ tag + 1, |
+ 0, |
+ null_code_); |
+ index = tag_table->InsertCode(code); |
+ ASSERT(index >= 0); |
+ } |
+ |
+ ProfileCode* CreateProfileCodeReused(uword pc) { |
+ ProfileCode* code = new ProfileCode(ProfileCode::kReusedCode, |
+ pc, |
+ pc + 1, |
+ 0, |
+ null_code_); |
+ return code; |
+ } |
+ |
+ ProfileCode* CreateProfileCode(uword pc) { |
+ const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
+ const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
+ Code& code = Code::Handle(isolate_); |
+ |
+ // Check current isolate for pc. |
+ if (isolate_->heap()->CodeContains(pc)) { |
+ code ^= Code::LookupCode(pc); |
+ if (!code.IsNull()) { |
+ deoptimized_code_->Add(code); |
+ return new ProfileCode(ProfileCode::kDartCode, |
+ code.EntryPoint(), |
+ code.EntryPoint() + code.Size(), |
+ code.compile_timestamp(), |
+ code); |
+ } |
+ return new ProfileCode(ProfileCode::kCollectedCode, |
+ pc, |
+ (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
+ 0, |
+ code); |
+ } |
+ |
+ // Check VM isolate for pc. |
+ if (vm_isolate_->heap()->CodeContains(pc)) { |
+ code ^= Code::LookupCodeInVmIsolate(pc); |
+ if (!code.IsNull()) { |
+ return new ProfileCode(ProfileCode::kDartCode, |
+ code.EntryPoint(), |
+ code.EntryPoint() + code.Size(), |
+ code.compile_timestamp(), |
+ code); |
+ } |
+ return new ProfileCode(ProfileCode::kCollectedCode, |
+ pc, |
+ (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
+ 0, |
+ code); |
+ } |
+ |
+ // Check NativeSymbolResolver for pc. |
+ uintptr_t native_start = 0; |
+ char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
+ &native_start); |
+ if (native_name == NULL) { |
+ // No native name found. |
+ return new ProfileCode(ProfileCode::kNativeCode, |
+ pc, |
+ pc + 1, |
+ 0, |
+ code); |
+ } |
+ ASSERT(pc >= native_start); |
+ ProfileCode* profile_code = |
+ new ProfileCode(ProfileCode::kNativeCode, |
+ native_start, |
+ pc + 1, |
+ 0, |
+ code); |
+ profile_code->SetName(native_name); |
+ free(native_name); |
+ return profile_code; |
+ } |
+ |
+ ProfileCode* RegisterProfileCode(uword pc, int64_t timestamp) { |
+ ProfileCodeTable* live_table = profile_->live_code_; |
+ ProfileCodeTable* dead_table = profile_->dead_code_; |
+ |
+ ProfileCode* code = live_table->FindCodeForPC(pc); |
+ if (code == NULL) { |
+ // Code not found. |
+ intptr_t index = live_table->InsertCode(CreateProfileCode(pc)); |
+ ASSERT(index >= 0); |
+ code = live_table->At(index); |
+ if (code->compile_timestamp() <= timestamp) { |
+ // Code was compiled before sample was taken. |
+ return code; |
+ } |
+ // Code was compiled after the sample was taken. Insert code object into |
+ // the dead code table. |
+ index = dead_table->InsertCode(CreateProfileCodeReused(pc)); |
+ ASSERT(index >= 0); |
+ return dead_table->At(index); |
+ } |
+ // Existing code found. |
+ if (code->compile_timestamp() <= timestamp) { |
+ // Code was compiled before sample was taken. |
+ return code; |
+ } |
+ // Code was compiled after the sample was taken. Check if we have an entry |
+ // in the dead code table. |
+ code = dead_table->FindCodeForPC(pc); |
+ if (code != NULL) { |
+ return code; |
+ } |
+ // Create a new dead code entry. |
+ intptr_t index = dead_table->InsertCode(CreateProfileCodeReused(pc)); |
+ ASSERT(index >= 0); |
+ return dead_table->At(index); |
+ } |
+ |
+ Profile::TagOrder tag_order() const { |
+ return tag_order_; |
} |
- CodeRegionTrieNode* exclusive_root_; |
- CodeRegionTrieNode* inclusive_root_; |
+ bool vm_tags_emitted() const { |
+ return (tag_order_ == Profile::kUserVM) || |
+ (tag_order_ == Profile::kVMUser) || |
+ (tag_order_ == Profile::kVM); |
+ } |
+ |
+ Isolate* isolate_; |
+ Isolate* vm_isolate_; |
+ SampleFilter* filter_; |
+ Profile::TagOrder tag_order_; |
+ Profile* profile_; |
+ DeoptimizedCodeSet* deoptimized_code_; |
+ const Code& null_code_; |
+ const Function& null_function_; |
+ bool tick_functions_; |
+ |
+ ProcessedSampleBuffer* samples_; |
}; |
+Profile::Profile(Isolate* isolate) |
+ : isolate_(isolate), |
+ live_code_(NULL), |
+ dead_code_(NULL), |
+ tag_code_(NULL), |
+ functions_(NULL), |
+ dead_code_index_offset_(-1), |
+ tag_code_index_offset_(-1), |
+ min_time_(kMaxInt64), |
+ max_time_(0) { |
+ ASSERT(isolate_ != NULL); |
+ for (intptr_t i = 0; i < kNumTrieKinds; i++) { |
+ roots_[i] = NULL; |
+ } |
+} |
+ |
+ |
+void Profile::Build(SampleFilter* filter, TagOrder tag_order) { |
+ ProfileBuilder builder(isolate_, filter, tag_order, this); |
+ builder.Build(); |
+} |
+ |
+ |
+ProfileFunction* Profile::GetFunction(intptr_t index) { |
+ ASSERT(functions_ != NULL); |
+ return functions_->At(index); |
+} |
+ |
+ |
+ProfileCode* Profile::GetCode(intptr_t index) { |
+ ASSERT(live_code_ != NULL); |
+ ASSERT(dead_code_ != NULL); |
+ ASSERT(tag_code_ != NULL); |
+ ASSERT(dead_code_index_offset_ >= 0); |
+ ASSERT(tag_code_index_offset_ >= 0); |
+ |
+ // Code indexes span three arrays. |
+ // 0 ... |live_code| |
+ // |live_code| ... |dead_code| |
+ // |dead_code| ... |tag_code| |
+ |
+ if (index < dead_code_index_offset_) { |
+ return live_code_->At(index); |
+ } |
+ |
+ if (index < tag_code_index_offset_) { |
+ index -= dead_code_index_offset_; |
+ return dead_code_->At(index); |
+ } |
+ |
+ index -= tag_code_index_offset_; |
+ return tag_code_->At(index); |
+} |
+ |
+ |
+ProfileTrieNode* Profile::GetTrieRoot(TrieKind trie_kind) { |
+ return roots_[static_cast<intptr_t>(trie_kind)]; |
+} |
+ |
+ |
+void Profile::PrintJSON(JSONStream* stream) { |
+ ScopeTimer sw("Profile::PrintJSON", FLAG_trace_profiler); |
+ JSONObject obj(stream); |
+ obj.AddProperty("type", "_CpuProfile"); |
+ obj.AddProperty("samplePeriod", |
+ static_cast<intptr_t>(FLAG_profile_period)); |
+ obj.AddProperty("stackDepth", |
+ static_cast<intptr_t>(FLAG_profile_depth)); |
+ obj.AddProperty("sampleCount", sample_count()); |
+ obj.AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan())); |
+ { |
+ JSONArray codes(&obj, "codes"); |
+ for (intptr_t i = 0; i < live_code_->length(); i++) { |
+ ProfileCode* code = live_code_->At(i); |
+ ASSERT(code != NULL); |
+ code->PrintToJSONArray(&codes); |
+ } |
+ for (intptr_t i = 0; i < dead_code_->length(); i++) { |
+ ProfileCode* code = dead_code_->At(i); |
+ ASSERT(code != NULL); |
+ code->PrintToJSONArray(&codes); |
+ } |
+ for (intptr_t i = 0; i < tag_code_->length(); i++) { |
+ ProfileCode* code = tag_code_->At(i); |
+ ASSERT(code != NULL); |
+ code->PrintToJSONArray(&codes); |
+ } |
+ } |
+ |
+ { |
+ JSONArray functions(&obj, "functions"); |
+ for (intptr_t i = 0; i < functions_->length(); i++) { |
+ ProfileFunction* function = functions_->At(i); |
+ ASSERT(function != NULL); |
+ function->PrintToJSONArray(&functions); |
+ } |
+ } |
+ { |
+ JSONArray code_trie(&obj, "exclusiveCodeTrie"); |
+ ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveCode)]; |
+ ASSERT(root != NULL); |
+ root->PrintToJSONArray(&code_trie); |
+ } |
+ { |
+ JSONArray code_trie(&obj, "inclusiveCodeTrie"); |
+ ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveCode)]; |
+ ASSERT(root != NULL); |
+ root->PrintToJSONArray(&code_trie); |
+ } |
+ { |
+ JSONArray function_trie(&obj, "exclusiveFunctionTrie"); |
+ ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveFunction)]; |
+ ASSERT(root != NULL); |
+ root->PrintToJSONArray(&function_trie); |
+ } |
+ { |
+ JSONArray function_trie(&obj, "inclusiveFunctionTrie"); |
+ ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveFunction)]; |
+ ASSERT(root != NULL); |
+ root->PrintToJSONArray(&function_trie); |
+ } |
+} |
+ |
+ |
class NoAllocationSampleFilter : public SampleFilter { |
public: |
explicit NoAllocationSampleFilter(Isolate* isolate) |
@@ -2155,189 +1906,30 @@ class NoAllocationSampleFilter : public SampleFilter { |
}; |
-void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
+void ProfilerService::PrintJSON(JSONStream* stream, |
+ Profile::TagOrder tag_order) { |
Isolate* isolate = Isolate::Current(); |
// Disable profile interrupts while processing the buffer. |
Profiler::EndExecution(isolate); |
- MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
- IsolateProfilerData* profiler_data = isolate->profiler_data(); |
- if (profiler_data == NULL) { |
- stream->PrintError(kFeatureDisabled, NULL); |
- return; |
+ |
+ { |
+ MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
+ IsolateProfilerData* profiler_data = isolate->profiler_data(); |
+ if (profiler_data == NULL) { |
+ stream->PrintError(kFeatureDisabled, NULL); |
+ return; |
+ } |
} |
- SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
- ASSERT(sample_buffer != NULL); |
- ScopeTimer sw("ProfilerService::PrintJSON", FLAG_trace_profiler); |
+ |
{ |
StackZone zone(isolate); |
HANDLESCOPE(isolate); |
- |
- ProcessedSampleBuffer* processed_samples = NULL; |
- { |
- ScopeTimer sw("BuildProcessedSampleBuffer", FLAG_trace_profiler); |
- NoAllocationSampleFilter filter(isolate); |
- processed_samples = sample_buffer->BuildProcessedSampleBuffer(&filter); |
- } |
- |
- { |
- // Live code holds Dart, Native, and Collected CodeRegions. |
- CodeRegionTable live_code_table; |
- // Dead code holds Overwritten CodeRegions. |
- CodeRegionTable dead_code_table; |
- // Tag code holds Tag CodeRegions. |
- CodeRegionTable tag_code_table; |
- // Table holding all ProfileFunctions. |
- ProfileFunctionTable function_table; |
- // Set of deoptimized code still referenced by the profiler. |
- DeoptimizedCodeSet* deoptimized_code = new DeoptimizedCodeSet(isolate); |
- |
- // Build CodeRegion tables. |
- CodeRegionTableBuilder builder(isolate, |
- &live_code_table, |
- &dead_code_table, |
- &tag_code_table, |
- deoptimized_code); |
- { |
- ScopeTimer sw("CodeRegionTableBuilder::Build", FLAG_trace_profiler); |
- builder.Build(processed_samples); |
- } |
- intptr_t samples = processed_samples->length(); |
- intptr_t frames = builder.frames(); |
- if (FLAG_trace_profiler) { |
- intptr_t total_live_code_objects = live_code_table.Length(); |
- intptr_t total_dead_code_objects = dead_code_table.Length(); |
- intptr_t total_tag_code_objects = tag_code_table.Length(); |
- OS::Print( |
- "Processed %" Pd " samples with %" Pd " frames\n", samples, frames); |
- OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", |
- total_live_code_objects, |
- total_dead_code_objects, |
- total_tag_code_objects); |
- } |
- |
- if (FLAG_trace_profiler) { |
- ScopeTimer sw("CodeRegionTableVerify", FLAG_trace_profiler); |
- live_code_table.Verify(); |
- dead_code_table.Verify(); |
- tag_code_table.Verify(); |
- } |
- |
- { |
- ScopeTimer st("CodeRegionFunctionMapping", FLAG_trace_profiler); |
- CodeRegionFunctionMapper mapper(isolate, &live_code_table, |
- &dead_code_table, |
- &tag_code_table, |
- &function_table); |
- mapper.Map(); |
- } |
- if (FLAG_trace_profiler) { |
- intptr_t total_functions = function_table.Length(); |
- OS::Print("FunctionTable: size=%" Pd "\n", total_functions); |
- } |
- CodeRegionTrieBuilder code_trie_builder(isolate, |
- &live_code_table, |
- &dead_code_table, |
- &tag_code_table); |
- code_trie_builder.set_tag_order(tag_order); |
- { |
- // Build CodeRegion trie. |
- ScopeTimer sw("CodeRegionTrieBuilder::Build", FLAG_trace_profiler); |
- code_trie_builder.Build(processed_samples); |
- code_trie_builder.exclusive_root()->SortByCount(); |
- code_trie_builder.inclusive_root()->SortByCount(); |
- } |
- if (FLAG_trace_profiler) { |
- OS::Print("Code Trie Root Count: E: %" Pd " I: %" Pd "\n", |
- code_trie_builder.exclusive_root()->count(), |
- code_trie_builder.inclusive_root()->count()); |
- } |
- ProfileFunctionTrieBuilder function_trie_builder(&live_code_table, |
- &dead_code_table, |
- &tag_code_table, |
- &function_table); |
- function_trie_builder.set_tag_order(tag_order); |
- { |
- // Build ProfileFunction trie. |
- ScopeTimer sw("ProfileFunctionTrieBuilder::Build", |
- FLAG_trace_profiler); |
- function_trie_builder.Build(processed_samples); |
- function_trie_builder.exclusive_root()->SortByCount(); |
- function_trie_builder.inclusive_root()->SortByCount(); |
- } |
- if (FLAG_trace_profiler) { |
- OS::Print("Function Trie Root Count: E: %" Pd " I: %" Pd "\n", |
- function_trie_builder.exclusive_root()->count(), |
- function_trie_builder.inclusive_root()->count()); |
- } |
- { |
- ScopeTimer sw("CpuProfileJSONStream", FLAG_trace_profiler); |
- // Serialize to JSON. |
- JSONObject obj(stream); |
- obj.AddProperty("type", "_CpuProfile"); |
- obj.AddProperty("sampleCount", samples); |
- obj.AddProperty("samplePeriod", |
- static_cast<intptr_t>(FLAG_profile_period)); |
- obj.AddProperty("stackDepth", |
- static_cast<intptr_t>(FLAG_profile_depth)); |
- obj.AddProperty("timeSpan", |
- MicrosecondsToSeconds(builder.TimeDeltaMicros())); |
- { |
- JSONArray code_trie(&obj, "exclusiveCodeTrie"); |
- CodeRegionTrieNode* root = code_trie_builder.exclusive_root(); |
- ASSERT(root != NULL); |
- root->PrintToJSONArray(&code_trie); |
- } |
- { |
- JSONArray code_trie(&obj, "inclusiveCodeTrie"); |
- CodeRegionTrieNode* root = code_trie_builder.inclusive_root(); |
- ASSERT(root != NULL); |
- root->PrintToJSONArray(&code_trie); |
- } |
- { |
- JSONArray function_trie(&obj, "exclusiveFunctionTrie"); |
- ProfileFunctionTrieNode* root = |
- function_trie_builder.exclusive_root(); |
- ASSERT(root != NULL); |
- root->PrintToJSONArray(&function_trie); |
- } |
- { |
- JSONArray function_trie(&obj, "inclusiveFunctionTrie"); |
- ProfileFunctionTrieNode* root = |
- function_trie_builder.inclusive_root(); |
- ASSERT(root != NULL); |
- root->PrintToJSONArray(&function_trie); |
- } |
- { |
- JSONArray codes(&obj, "codes"); |
- for (intptr_t i = 0; i < live_code_table.Length(); i++) { |
- CodeRegion* region = live_code_table.At(i); |
- ASSERT(region != NULL); |
- region->PrintToJSONArray(&codes); |
- } |
- for (intptr_t i = 0; i < dead_code_table.Length(); i++) { |
- CodeRegion* region = dead_code_table.At(i); |
- ASSERT(region != NULL); |
- region->PrintToJSONArray(&codes); |
- } |
- for (intptr_t i = 0; i < tag_code_table.Length(); i++) { |
- CodeRegion* region = tag_code_table.At(i); |
- ASSERT(region != NULL); |
- region->PrintToJSONArray(&codes); |
- } |
- } |
- { |
- JSONArray functions(&obj, "functions"); |
- for (intptr_t i = 0; i < function_table.Length(); i++) { |
- ProfileFunction* function = function_table.At(i); |
- ASSERT(function != NULL); |
- function->PrintToJSONArray(&functions); |
- } |
- } |
- } |
- // Update the isolates set of dead code. |
- deoptimized_code->UpdateIsolate(isolate); |
- } |
+ Profile profile(isolate); |
+ NoAllocationSampleFilter filter(isolate); |
+ profile.Build(&filter, tag_order); |
+ profile.PrintJSON(stream); |
} |
+ |
// Enable profile interrupts. |
Profiler::BeginExecution(isolate); |
} |