Chromium Code Reviews| Index: runtime/vm/profiler_service.cc |
| diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc |
| index a6457d6cd40ad1bd1186539ef5be1c87bf14d677..628a21e938215d46b990d042bee0d75c3a2e9855 100644 |
| --- a/runtime/vm/profiler_service.cc |
| +++ b/runtime/vm/profiler_service.cc |
| @@ -15,125 +15,362 @@ |
| namespace dart { |
| DECLARE_FLAG(int, profile_depth); |
| -DECLARE_FLAG(bool, trace_profiler); |
| DECLARE_FLAG(int, profile_period); |
| -struct AddressEntry { |
| - uword pc; |
| - intptr_t exclusive_ticks; |
| - intptr_t inclusive_ticks; |
| +DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); |
| - void tick(bool exclusive) { |
| - if (exclusive) { |
| - exclusive_ticks++; |
| - } else { |
| - inclusive_ticks++; |
| - } |
| +// Forward declarations. |
| +class CodeRegion; |
| +class ProfileFunction; |
| +class ProfileFunctionTable; |
| + |
| + |
| +class DeoptimizedCodeSet : public ZoneAllocated { |
| + public: |
| + explicit DeoptimizedCodeSet(Isolate* isolate) |
| + : previous_( |
| + GrowableObjectArray::ZoneHandle(isolate->deoptimized_code_array())), |
| + current_(GrowableObjectArray::ZoneHandle( |
| + previous_.IsNull() ? GrowableObjectArray::null() : |
| + GrowableObjectArray::New())) { |
| } |
| -}; |
| + void Add(const Code& code) { |
| + if (current_.IsNull()) { |
| + return; |
| + } |
| + if (!Contained(code, previous_) || Contained(code, current_)) { |
| + return; |
| + } |
| + current_.Add(code); |
| + } |
| -struct CallEntry { |
| - intptr_t code_table_index; |
| - intptr_t count; |
| -}; |
| + void UpdateIsolate(Isolate* isolate) { |
| + intptr_t length_before = previous_.Length(); |
|
rmacnak
2015/02/24 20:52:43
vm/cc/Service_Profile is hitting an assert here be
Cutch
2015/02/24 20:54:37
Done.
|
| + intptr_t length_after = current_.Length(); |
| + intptr_t size_before = SizeOf(previous_); |
| + intptr_t size_after = SizeOf(current_); |
| + if ((size_before > 0) && FLAG_trace_profiler) { |
| + OS::Print("Updating isolate deoptimized code array: " |
| + "%" Pd " -> %" Pd " [%" Pd " -> %" Pd "]\n", |
| + size_before, size_after, length_before, length_after); |
| + } |
| + isolate->set_deoptimized_code_array(current_); |
| + } |
| + private: |
| + bool Contained(const Code& code, const GrowableObjectArray& array) { |
| + if (array.IsNull() || code.IsNull()) { |
| + return false; |
| + } |
| + NoGCScope no_gc_scope; |
| + for (intptr_t i = 0; array.Length(); i++) { |
| + if (code.raw() == array.At(i)) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| -typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); |
| + intptr_t SizeOf(const GrowableObjectArray& array) { |
| + if (array.IsNull()) { |
| + return 0; |
| + } |
| + Code& code = Code::ZoneHandle(); |
| + intptr_t size = 0; |
| + for (intptr_t i = 0; i < array.Length(); i++) { |
| + code ^= array.At(i); |
| + ASSERT(!code.IsNull()); |
| + size += code.Size(); |
| + } |
| + return size; |
| + } |
| + // Array holding code that is being kept around only for the profiler. |
| + const GrowableObjectArray& previous_; |
| + // Array holding code that should continue to be kept around for the profiler. |
| + const GrowableObjectArray& current_; |
| +}; |
| -class CodeRegionTrieNode : public ZoneAllocated { |
| +class ProfileFunction : public ZoneAllocated { |
| public: |
| - explicit CodeRegionTrieNode(intptr_t code_region_index) |
| - : code_region_index_(code_region_index), |
| - count_(0), |
| - children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { |
| + 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, |
| + 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); |
| } |
| - void Tick() { |
| - ASSERT(code_region_index_ >= 0); |
| - count_++; |
| + const char* name() const { |
| + ASSERT(name_ != NULL); |
| + return name_; |
| } |
| - intptr_t count() const { |
| - ASSERT(code_region_index_ >= 0); |
| - return count_; |
| + RawFunction* function() const { |
| + return function_.raw(); |
| } |
| - intptr_t code_region_index() const { |
| - return code_region_index_; |
| + intptr_t index() const { |
| + return table_index_; |
| } |
| - ZoneGrowableArray<CodeRegionTrieNode*>& children() const { |
| - return *children_; |
| + Kind kind() const { |
| + return kind_; |
| } |
| - 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; |
| + 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 Dump() { |
| + const char* n = (name_ == NULL) ? "<NULL>" : name_; |
| + const char* fn = ""; |
| + if (!function_.IsNull()) { |
| + fn = function_.ToQualifiedCString(); |
| + } |
| + OS::Print("%s %s [%s]", KindToCString(kind()), n, fn); |
| + } |
| + |
| + void AddCodeObjectIndex(intptr_t index) { |
| + for (intptr_t i = 0; i < code_objects_->length(); i++) { |
| + if ((*code_objects_)[i] == index) { |
| + return; |
| } |
| - 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); |
| + code_objects_->Add(index); |
| + } |
| + |
| + intptr_t inclusive_ticks() const { |
| + return inclusive_ticks_; |
| + } |
| + |
| + intptr_t exclusive_ticks() const { |
| + return exclusive_ticks_; |
| + } |
| + |
| + 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. |
| + return; |
| + } |
| + if (exclusive) { |
| + exclusive_ticks_++; |
| } else { |
| - // Add to end. |
| - children_->Add(child); |
| + inclusive_ticks_++; |
| + // Mark the last serial we ticked the inclusive count. |
| + inclusive_tick_serial_ = serial; |
| } |
| - return child; |
| } |
| - // Sort this's children and (recursively) all descendants by count. |
| - // 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 PrintToJSONObject(JSONObject* func) { |
| + if (kind() == kNativeFunction) { |
| + func->AddProperty("type", "@Function"); |
| + func->AddProperty("name", name()); |
| + func->AddProperty("kind", "Native"); |
| + } else if (kind() == kTagFunction) { |
| + func->AddProperty("type", "@Function"); |
| + func->AddProperty("kind", "Tag"); |
| + func->AddProperty("name", name()); |
| + } else if (kind() == kUnkownFunction) { |
| + func->AddProperty("type", "@Function"); |
| + func->AddProperty("name", name()); |
| + func->AddProperty("kind", "Collected"); |
| + } else if (kind() == kStubFunction) { |
| + func->AddProperty("type", "@Function"); |
| + func->AddProperty("name", name()); |
| + func->AddProperty("kind", "Stub"); |
| + } else { |
| + UNREACHABLE(); |
| } |
| } |
| - 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); |
| + 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); |
| + } |
| } |
| } |
| private: |
| - static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, |
| - CodeRegionTrieNode* const* b) { |
| - ASSERT(a != NULL); |
| - ASSERT(b != NULL); |
| - return (*b)->count() - (*a)->count(); |
| + 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_; |
| +}; |
| + |
| + |
| +class ProfileFunctionTable : public ValueObject { |
| + public: |
| + ProfileFunctionTable() |
| + : null_function_(Function::ZoneHandle()), |
| + table_(new ZoneGrowableArray<ProfileFunction*>()), |
| + unknown_function_(NULL) { |
| } |
| - const intptr_t code_region_index_; |
| - intptr_t count_; |
| - ZoneGrowableArray<CodeRegionTrieNode*>* children_; |
| + ProfileFunction* LookupOrAdd(const Function& function) { |
| + ASSERT(!function.IsNull()); |
| + ProfileFunction* profile_function = Lookup(function); |
| + if (profile_function != NULL) { |
| + return profile_function; |
| + } |
| + return Add(function); |
| + } |
| + |
| + intptr_t LookupIndex(const Function& function) { |
| + ASSERT(!function.IsNull()); |
| + for (intptr_t i = 0; i < table_->length(); i++) { |
| + ProfileFunction* profile_function = (*table_)[i]; |
| + if (profile_function->function() == function.raw()) { |
| + return i; |
| + } |
| + } |
| + return -1; |
| + } |
| + |
| + ProfileFunction* GetUnknown() { |
| + if (unknown_function_ == NULL) { |
| + // Construct. |
| + unknown_function_ = Add(ProfileFunction::kUnkownFunction, |
| + "<unknown Dart function>"); |
| + } |
| + ASSERT(unknown_function_ != NULL); |
| + return unknown_function_; |
| + } |
| + |
| + // No protection against being called more than once for the same tag_id. |
| + ProfileFunction* AddTag(uword tag_id, const char* name) { |
| + // TODO(johnmccutchan): Canonicalize ProfileFunctions for tags. |
| + return Add(ProfileFunction::kTagFunction, name); |
| + } |
| + |
| + // No protection against being called more than once for the same native |
| + // address. |
| + ProfileFunction* AddNative(uword start_address, const char* name) { |
| + // TODO(johnmccutchan): Canonicalize ProfileFunctions for natives. |
| + return Add(ProfileFunction::kNativeFunction, name); |
| + } |
| + |
| + // No protection against being called more tha once for the same stub. |
| + ProfileFunction* AddStub(uword start_address, const char* name) { |
| + return Add(ProfileFunction::kStubFunction, name); |
| + } |
| + |
| + intptr_t Length() const { |
| + return table_->length(); |
| + } |
| + |
| + ProfileFunction* At(intptr_t i) const { |
| + ASSERT(i >= 0); |
| + ASSERT(i < Length()); |
| + return (*table_)[i]; |
| + } |
| + |
| + private: |
| + ProfileFunction* Add(ProfileFunction::Kind kind, const char* name) { |
| + ASSERT(kind != ProfileFunction::kDartFunction); |
| + ASSERT(name != NULL); |
| + ProfileFunction* profile_function = |
| + new ProfileFunction(kind, |
| + name, |
| + null_function_, |
| + table_->length()); |
| + table_->Add(profile_function); |
| + return profile_function; |
| + } |
| + |
| + ProfileFunction* Add(const Function& function) { |
| + ASSERT(Lookup(function) == NULL); |
| + ProfileFunction* profile_function = |
| + new ProfileFunction(ProfileFunction::kDartFunction, |
| + NULL, |
| + function, |
| + table_->length()); |
| + table_->Add(profile_function); |
| + return profile_function; |
| + } |
| + |
| + ProfileFunction* Lookup(const Function& function) { |
| + ASSERT(!function.IsNull()); |
| + intptr_t index = LookupIndex(function); |
| + if (index == -1) { |
| + return NULL; |
| + } |
| + return (*table_)[index]; |
| + } |
| + |
| + const Function& null_function_; |
| + 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++; |
| + } 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 |
| @@ -148,7 +385,11 @@ class CodeRegion : public ZoneAllocated { |
| kTagCode, // A special kind of code representing a tag. |
| }; |
| - CodeRegion(Kind kind, uword start, uword end, int64_t timestamp) |
| + CodeRegion(Kind kind, |
| + uword start, |
| + uword end, |
| + int64_t timestamp, |
| + const Code& code) |
| : kind_(kind), |
| start_(start), |
| end_(end), |
| @@ -158,13 +399,14 @@ class CodeRegion : public ZoneAllocated { |
| name_(NULL), |
| compile_timestamp_(timestamp), |
| creation_serial_(0), |
| - address_table_(new ZoneGrowableArray<AddressEntry>()), |
| - callers_table_(new ZoneGrowableArray<CallEntry>()), |
| - callees_table_(new ZoneGrowableArray<CallEntry>()) { |
| + 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; |
| @@ -227,6 +469,87 @@ class CodeRegion : public ZoneAllocated { |
| 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()); |
| + } 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); |
| + } else { |
| + ASSERT(start() == 0); |
| + SetName("root"); |
| + } |
| + } |
| + 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) { |
| @@ -273,14 +596,6 @@ class CodeRegion : public ZoneAllocated { |
| TickAddress(pc, exclusive); |
| } |
| - void AddCaller(intptr_t index, intptr_t count) { |
| - AddCallEntry(callers_table_, index, count); |
| - } |
| - |
| - void AddCallee(intptr_t index, intptr_t count) { |
| - AddCallEntry(callees_table_, index, count); |
| - } |
| - |
| void PrintNativeCode(JSONObject* profile_code_obj) { |
| ASSERT(kind() == kNativeCode); |
| JSONObject obj(profile_code_obj, "code"); |
| @@ -289,14 +604,10 @@ class CodeRegion : public ZoneAllocated { |
| obj.AddProperty("name", name()); |
| obj.AddPropertyF("start", "%" Px "", start()); |
| obj.AddPropertyF("end", "%" Px "", end()); |
| - obj.AddPropertyF("id", "code/native-%" Px "", start()); |
| { |
| // Generate a fake function entry. |
| JSONObject func(&obj, "function"); |
| - func.AddProperty("type", "@Function"); |
| - func.AddPropertyF("id", "functions/native-%" Px "", start()); |
| - func.AddProperty("name", name()); |
| - func.AddProperty("kind", "Native"); |
| + profile_function_->PrintToJSONObject(&func); |
| } |
| } |
| @@ -308,14 +619,10 @@ class CodeRegion : public ZoneAllocated { |
| obj.AddProperty("name", name()); |
| obj.AddPropertyF("start", "%" Px "", start()); |
| obj.AddPropertyF("end", "%" Px "", end()); |
| - obj.AddPropertyF("id", "code/collected-%" Px "", start()); |
| { |
| // Generate a fake function entry. |
| JSONObject func(&obj, "function"); |
| - func.AddProperty("type", "@Function"); |
| - obj.AddPropertyF("id", "functions/collected-%" Px "", start()); |
| - func.AddProperty("name", name()); |
| - func.AddProperty("kind", "Collected"); |
| + profile_function_->PrintToJSONObject(&func); |
| } |
| } |
| @@ -327,122 +634,65 @@ class CodeRegion : public ZoneAllocated { |
| obj.AddProperty("name", name()); |
| obj.AddPropertyF("start", "%" Px "", start()); |
| obj.AddPropertyF("end", "%" Px "", end()); |
| - obj.AddPropertyF("id", "code/reused-%" Px "", start()); |
| { |
| // Generate a fake function entry. |
| JSONObject func(&obj, "function"); |
| - func.AddProperty("type", "@Function"); |
| - obj.AddPropertyF("id", "functions/reused-%" Px "", start()); |
| - func.AddProperty("name", name()); |
| - func.AddProperty("kind", "Reused"); |
| + ASSERT(profile_function_ != NULL); |
| + profile_function_->PrintToJSONObject(&func); |
| } |
| } |
| - void PrintTagCode(JSONObject* profile_code_obj) { |
| + void PrintTagCode(JSONObject* profile_code_obj) { |
| ASSERT(kind() == kTagCode); |
| JSONObject obj(profile_code_obj, "code"); |
| obj.AddProperty("type", "@Code"); |
| obj.AddProperty("kind", "Tag"); |
| - obj.AddPropertyF("id", "code/tag-%" Px "", start()); |
| obj.AddProperty("name", name()); |
| obj.AddPropertyF("start", "%" Px "", start()); |
| obj.AddPropertyF("end", "%" Px "", end()); |
| { |
| // Generate a fake function entry. |
| JSONObject func(&obj, "function"); |
| - func.AddProperty("type", "@Function"); |
| - func.AddProperty("kind", "Tag"); |
| - obj.AddPropertyF("id", "functions/tag-%" Px "", start()); |
| - func.AddProperty("name", name()); |
| + ASSERT(profile_function_ != NULL); |
| + profile_function_->PrintToJSONObject(&func); |
| } |
| } |
| - void PrintToJSONArray(Isolate* isolate, JSONArray* events) { |
| - JSONObject obj(events); |
| + void PrintToJSONArray(JSONArray* codes) { |
| + JSONObject obj(codes); |
| obj.AddProperty("kind", KindToCString(kind())); |
| - obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); |
| - obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); |
| + obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
| + obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
| if (kind() == kDartCode) { |
| - // Look up code in Dart heap. |
| - Code& code = Code::Handle(isolate); |
| - code ^= Code::LookupCode(start()); |
| - if (code.IsNull()) { |
| - // Code is a stub in the Vm isolate. |
| - code ^= Code::LookupCodeInVmIsolate(start()); |
| - } |
| - ASSERT(!code.IsNull()); |
| - obj.AddProperty("code", code); |
| + ASSERT(!code_.IsNull()); |
| + obj.AddProperty("code", code_); |
| } else if (kind() == kCollectedCode) { |
| - if (name() == NULL) { |
| - // Lazily set generated name. |
| - GenerateAndSetSymbolName("[Collected]"); |
| - } |
| PrintCollectedCode(&obj); |
| } else if (kind() == kReusedCode) { |
| - if (name() == NULL) { |
| - // Lazily set generated name. |
| - GenerateAndSetSymbolName("[Reused]"); |
| - } |
| PrintOverwrittenCode(&obj); |
| } 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 { |
| - ASSERT(start() == 0); |
| - SetName("root"); |
| - } |
| - } |
| PrintTagCode(&obj); |
| } else { |
| ASSERT(kind() == kNativeCode); |
| - if (name() == NULL) { |
| - // Lazily set generated name. |
| - GenerateAndSetSymbolName("[Native]"); |
| - } |
| PrintNativeCode(&obj); |
| } |
| { |
| JSONArray ticks(&obj, "ticks"); |
| - for (intptr_t i = 0; i < address_table_->length(); i++) { |
| - const AddressEntry& entry = (*address_table_)[i]; |
| + 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); |
| } |
| } |
| - { |
| - JSONArray callers(&obj, "callers"); |
| - for (intptr_t i = 0; i < callers_table_->length(); i++) { |
| - const CallEntry& entry = (*callers_table_)[i]; |
| - callers.AddValueF("%" Pd "", entry.code_table_index); |
| - callers.AddValueF("%" Pd "", entry.count); |
| - } |
| - } |
| - { |
| - JSONArray callees(&obj, "callees"); |
| - for (intptr_t i = 0; i < callees_table_->length(); i++) { |
| - const CallEntry& entry = (*callees_table_)[i]; |
| - callees.AddValueF("%" Pd "", entry.code_table_index); |
| - callees.AddValueF("%" Pd "", entry.count); |
| - } |
| - } |
| } |
| private: |
| void TickAddress(uword pc, bool exclusive) { |
| - const intptr_t length = address_table_->length(); |
| + const intptr_t length = address_table_.length(); |
| intptr_t i = 0; |
| for (; i < length; i++) { |
| - AddressEntry& entry = (*address_table_)[i]; |
| + AddressEntry& entry = address_table_[i]; |
| if (entry.pc == pc) { |
| // Tick the address entry. |
| entry.tick(exclusive); |
| @@ -460,35 +710,10 @@ class CodeRegion : public ZoneAllocated { |
| entry.tick(exclusive); |
| if (i < length) { |
| // Insert at i. |
| - address_table_->InsertAt(i, entry); |
| + address_table_.InsertAt(i, entry); |
| } else { |
| // Add to end. |
| - address_table_->Add(entry); |
| - } |
| - } |
| - |
| - |
| - void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index, |
| - intptr_t count) { |
| - const intptr_t length = table->length(); |
| - intptr_t i = 0; |
| - for (; i < length; i++) { |
| - CallEntry& entry = (*table)[i]; |
| - if (entry.code_table_index == index) { |
| - entry.count += count; |
| - return; |
| - } |
| - if (entry.code_table_index > index) { |
| - break; |
| - } |
| - } |
| - CallEntry entry; |
| - entry.code_table_index = index; |
| - entry.count = count; |
| - if (i < length) { |
| - table->InsertAt(i, entry); |
| - } else { |
| - table->Add(entry); |
| + address_table_.Add(entry); |
| } |
| } |
| @@ -519,9 +744,13 @@ class CodeRegion : public ZoneAllocated { |
| int64_t compile_timestamp_; |
| // Serial number at which this CodeRegion was created. |
| intptr_t creation_serial_; |
| - ZoneGrowableArray<AddressEntry>* address_table_; |
| - ZoneGrowableArray<CallEntry>* callers_table_; |
| - ZoneGrowableArray<CallEntry>* callees_table_; |
| + // 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); |
| }; |
| @@ -641,12 +870,10 @@ class CodeRegionTable : public ValueObject { |
| UNREACHABLE(); |
| } |
| -#if defined(DEBUG) |
| void Verify() { |
| VerifyOrder(); |
| VerifyOverlap(); |
| } |
| -#endif |
| void DebugPrint() { |
| OS::Print("Dumping CodeRegionTable:\n"); |
| @@ -695,7 +922,6 @@ class CodeRegionTable : public ValueObject { |
| region->AdjustExtent(start, end); |
| } |
| -#if defined(DEBUG) |
| void VerifyOrder() { |
| const intptr_t length = code_region_table_->length(); |
| if (length == 0) { |
| @@ -722,124 +948,35 @@ class CodeRegionTable : public ValueObject { |
| } |
| } |
| } |
| -#endif |
| ZoneGrowableArray<CodeRegion*>* code_region_table_; |
| }; |
| -class FixTopFrameVisitor : public SampleVisitor { |
| - public: |
| - explicit FixTopFrameVisitor(Isolate* isolate) |
| - : SampleVisitor(isolate), |
| - vm_isolate_(Dart::vm_isolate()) { |
| - } |
| - |
| - void VisitSample(Sample* sample) { |
| - if (sample->processed()) { |
| - // Already processed. |
| - return; |
| - } |
| - REUSABLE_CODE_HANDLESCOPE(isolate()); |
| - // Mark that we've processed this sample. |
| - sample->set_processed(true); |
| - // Lookup code object for leaf frame. |
| - Code& code = reused_code_handle.Handle(); |
| - code = FindCodeForPC(sample->At(0)); |
| - sample->set_leaf_frame_is_dart(!code.IsNull()); |
| - if (sample->pc_marker() == 0) { |
| - // No pc marker. Nothing to do. |
| - return; |
| - } |
| - if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) { |
| - // Code compiled after sample. Ignore. |
| - return; |
| - } |
| - if (sample->leaf_frame_is_dart()) { |
| - CheckForMissingDartFrame(code, sample); |
| - } |
| - } |
| - |
| - private: |
| - void CheckForMissingDartFrame(const Code& code, Sample* sample) const { |
| - // Some stubs (and intrinsics) do not push a frame onto the stack leaving |
| - // the frame pointer in the caller. |
| - // |
| - // PC -> STUB |
| - // FP -> DART3 <-+ |
| - // DART2 <-| <- TOP FRAME RETURN ADDRESS. |
| - // DART1 <-| |
| - // ..... |
| - // |
| - // In this case, traversing the linked stack frames will not collect a PC |
| - // inside DART3. The stack will incorrectly be: STUB, DART2, DART1. |
| - // In Dart code, after pushing the FP onto the stack, an IP in the current |
| - // function is pushed onto the stack as well. This stack slot is called |
| - // the PC marker. We can use the PC marker to insert DART3 into the stack |
| - // so that it will correctly be: STUB, DART3, DART2, DART1. Note the |
| - // inserted PC may not accurately reflect the true return address from STUB. |
| - ASSERT(!code.IsNull()); |
| - if (sample->sp() == sample->fp()) { |
| - // Haven't pushed pc marker yet. |
| - return; |
| - } |
| - uword pc_marker = sample->pc_marker(); |
| - if (code.ContainsInstructionAt(pc_marker)) { |
| - // PC marker is in the same code as pc, no missing frame. |
| - return; |
| - } |
| - if (!ContainedInDartCodeHeaps(pc_marker)) { |
| - // Not a valid PC marker. |
| - return; |
| - } |
| - sample->InsertCallerForTopFrame(pc_marker); |
| - } |
| - |
| - bool ContainedInDartCodeHeaps(uword pc) const { |
| - return isolate()->heap()->CodeContains(pc) || |
| - vm_isolate()->heap()->CodeContains(pc); |
| - } |
| - |
| - Isolate* vm_isolate() const { |
| - return vm_isolate_; |
| - } |
| - |
| - RawCode* FindCodeForPC(uword pc) const { |
| - // Check current isolate for pc. |
| - if (isolate()->heap()->CodeContains(pc)) { |
| - return Code::LookupCode(pc); |
| - } |
| - // Check VM isolate for pc. |
| - if (vm_isolate()->heap()->CodeContains(pc)) { |
| - return Code::LookupCodeInVmIsolate(pc); |
| - } |
| - return Code::null(); |
| - } |
| - |
| - Isolate* vm_isolate_; |
| -}; |
| - |
| - |
| class CodeRegionTableBuilder : public SampleVisitor { |
| public: |
| CodeRegionTableBuilder(Isolate* isolate, |
| CodeRegionTable* live_code_table, |
| CodeRegionTable* dead_code_table, |
| - CodeRegionTable* tag_code_table) |
| + CodeRegionTable* tag_code_table, |
| + DeoptimizedCodeSet* deoptimized_code) |
| : SampleVisitor(isolate), |
| 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()) { |
| + 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; |
| - ASSERT(isolate_ != NULL); |
| - ASSERT(vm_isolate_ != NULL); |
| } |
| void VisitSample(Sample* sample) { |
| @@ -859,7 +996,7 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| CreateTag(sample->vm_tag()); |
| // Make sure user tag is created. |
| CreateUserTag(sample->user_tag()); |
| - // Exclusive tick for bottom frame if we aren't sampled from an exit frame. |
| + // Exclusive tick for top frame if we aren't sampled from an exit frame. |
| if (!sample->exit_frame_sample()) { |
| Tick(sample->At(0), true, timestamp); |
| } |
| @@ -890,7 +1027,8 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, |
| tag, |
| tag + 1, |
| - 0); |
| + 0, |
| + null_code_); |
| index = tag_code_table_->InsertCodeRegion(region); |
| ASSERT(index >= 0); |
| region->set_creation_serial(visited()); |
| @@ -901,18 +1039,7 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| // None set. |
| return; |
| } |
| - intptr_t index = tag_code_table_->FindIndex(tag); |
| - if (index >= 0) { |
| - // Already created. |
| - return; |
| - } |
| - CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, |
| - tag, |
| - tag + 1, |
| - 0); |
| - index = tag_code_table_->InsertCodeRegion(region); |
| - ASSERT(index >= 0); |
| - region->set_creation_serial(visited()); |
| + return CreateTag(tag); |
| } |
| void Tick(uword pc, bool exclusive, int64_t timestamp) { |
| @@ -958,7 +1085,8 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, |
| pc, |
| pc + 1, |
| - 0); |
| + 0, |
| + null_code_); |
| intptr_t index = dead_code_table_->InsertCodeRegion(region); |
| region->set_creation_serial(visited()); |
| ASSERT(index >= 0); |
| @@ -973,25 +1101,34 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| if (isolate_->heap()->CodeContains(pc)) { |
| code ^= Code::LookupCode(pc); |
| if (!code.IsNull()) { |
| - return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| + deoptimized_code_->Add(code); |
| + return new CodeRegion(CodeRegion::kDartCode, |
| + code.EntryPoint(), |
| code.EntryPoint() + code.Size(), |
| - code.compile_timestamp()); |
| + code.compile_timestamp(), |
| + code); |
| } |
| - return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| + return new CodeRegion(CodeRegion::kCollectedCode, |
| + pc, |
| (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| - 0); |
| + 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(), |
| + return new CodeRegion(CodeRegion::kDartCode, |
| + code.EntryPoint(), |
| code.EntryPoint() + code.Size(), |
| - code.compile_timestamp()); |
| + code.compile_timestamp(), |
| + code); |
| } |
| - return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| + return new CodeRegion(CodeRegion::kCollectedCode, |
| + pc, |
| (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| - 0); |
| + 0, |
| + code); |
| } |
| // Check NativeSymbolResolver for pc. |
| uintptr_t native_start = 0; |
| @@ -999,11 +1136,19 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| &native_start); |
| if (native_name == NULL) { |
| // No native name found. |
| - return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0); |
| + 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); |
| + new CodeRegion(CodeRegion::kNativeCode, |
| + native_start, |
| + pc + 1, |
| + 0, |
| + code); |
| code_region->SetName(native_name); |
| free(native_name); |
| return code_region; |
| @@ -1017,19 +1162,24 @@ class CodeRegionTableBuilder : public SampleVisitor { |
| CodeRegionTable* tag_code_table_; |
| Isolate* isolate_; |
| Isolate* vm_isolate_; |
| + const Code& null_code_; |
| + DeoptimizedCodeSet* deoptimized_code_; |
| }; |
| -class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| +class CodeRegionFunctionMapper : public ValueObject { |
| public: |
| - CodeRegionExclusiveTrieBuilder(Isolate* isolate, |
| - CodeRegionTable* live_code_table, |
| - CodeRegionTable* dead_code_table, |
| - CodeRegionTable* tag_code_table) |
| - : SampleVisitor(isolate), |
| + 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) { |
| + 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); |
| @@ -1039,32 +1189,261 @@ class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| intptr_t root_index = tag_code_table_->FindIndex(0); |
| // Verify that the "0" tag does not exist. |
| ASSERT(root_index < 0); |
| - // Insert the dummy tag CodeRegion that is used for the Trie root. |
| - CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0); |
| + // Insert the dummy tag CodeRegion as the root. |
| + const Code& null_code = Code::ZoneHandle(); |
| + CodeRegion* region = |
| + new CodeRegion(CodeRegion::kTagCode, 0, 1, 0, null_code); |
| root_index = tag_code_table_->InsertCodeRegion(region); |
| ASSERT(root_index >= 0); |
| region->set_creation_serial(0); |
| - root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index); |
| + } |
| + |
| + 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_; |
| + } |
| + |
| + void Tick() { |
| + ticks_++; |
| + } |
| + |
| + intptr_t ticks() const { |
| + return ticks_; |
| + } |
| + |
| + private: |
| + intptr_t code_index_; |
| + intptr_t ticks_; |
| +}; |
| + |
| + |
| +class ProfileFunctionTrieNode : public ZoneAllocated { |
| + public: |
| + explicit ProfileFunctionTrieNode(intptr_t profile_function_table_index) |
| + : profile_function_table_index_(profile_function_table_index), |
| + count_(0), |
| + code_objects_(new ZoneGrowableArray<ProfileFunctionTrieNodeCode>()) { |
| + } |
| + |
| + void Tick() { |
| + count_++; |
| + } |
| + |
| + intptr_t count() const { |
| + return count_; |
| + } |
| + |
| + intptr_t profile_function_table_index() const { |
| + return profile_function_table_index_; |
| + } |
| + |
| + |
| + ProfileFunctionTrieNode* GetChild(intptr_t child_index) { |
| + const intptr_t length = children_.length(); |
| + intptr_t i = 0; |
| + while (i < length) { |
| + ProfileFunctionTrieNode* child = children_[i]; |
| + if (child->profile_function_table_index() == child_index) { |
| + return child; |
| + } |
| + if (child->profile_function_table_index() > child_index) { |
| + break; |
| + } |
| + i++; |
| + } |
| + // Add new ProfileFunctionTrieNode, sorted by index. |
| + ProfileFunctionTrieNode* child = |
| + new ProfileFunctionTrieNode(child_index); |
| + if (i < length) { |
| + // Insert at i. |
| + children_.InsertAt(i, child); |
| + } else { |
| + // Add to end. |
| + children_.Add(child); |
| + } |
| + return child; |
| + } |
| + |
| + void AddCodeObjectIndex(intptr_t index) { |
| + 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; |
| + } |
| + } |
| + ProfileFunctionTrieNodeCode code_object(index); |
| + code_object.Tick(); |
| + 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(); |
| + } |
| + } |
| + |
| + 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); |
| + } |
| + } |
| + |
| + private: |
| + static int ProfileFunctionTrieNodeCodeCompare( |
| + const ProfileFunctionTrieNodeCode* a, |
| + const ProfileFunctionTrieNodeCode* b) { |
| + ASSERT(a != NULL); |
| + ASSERT(b != NULL); |
| + return b->ticks() - a->ticks(); |
| + } |
| + |
| + static int ProfileFunctionTrieNodeCompare(ProfileFunctionTrieNode* const* a, |
| + ProfileFunctionTrieNode* const* b) { |
| + ASSERT(a != NULL); |
| + ASSERT(b != NULL); |
| + return (*b)->count() - (*a)->count(); |
| + } |
| + |
| + const intptr_t profile_function_table_index_; |
| + intptr_t count_; |
| + ZoneGrowableArray<ProfileFunctionTrieNode*> children_; |
| + ZoneGrowableArray<ProfileFunctionTrieNodeCode>* code_objects_; |
| +}; |
| + |
| + |
| +class ProfileFunctionExclusiveTrieBuilder : public SampleVisitor { |
| + public: |
| + ProfileFunctionExclusiveTrieBuilder(Isolate* isolate, |
| + CodeRegionTable* live_code_table, |
| + CodeRegionTable* dead_code_table, |
| + CodeRegionTable* tag_code_table, |
| + ProfileFunctionTable* function_table) |
| + : SampleVisitor(isolate), |
| + live_code_table_(live_code_table), |
| + dead_code_table_(dead_code_table), |
| + tag_code_table_(tag_code_table), |
| + function_table_(function_table), |
| + trace_(false), |
| + trace_code_filter_(NULL) { |
| + ASSERT(live_code_table_ != NULL); |
| + ASSERT(dead_code_table_ != NULL); |
| + ASSERT(tag_code_table_ != NULL); |
| + ASSERT(function_table_ != NULL); |
| set_tag_order(ProfilerService::kUserVM); |
| + |
| + intptr_t root_index = tag_code_table_->FindIndex(0); |
| + // Verify that the "0" tag does exist. |
| + ASSERT(root_index >= 0); |
| + CodeRegion* region = tag_code_table_->At(root_index); |
| + ASSERT(region != NULL); |
| + |
| + ProfileFunction* function = region->function(); |
| + root_ = new ProfileFunctionTrieNode(function->index()); |
| } |
| void VisitSample(Sample* sample) { |
| // Give the root a tick. |
| root_->Tick(); |
| - CodeRegionTrieNode* current = root_; |
| + ProfileFunctionTrieNode* current = root_; |
| current = ProcessTags(sample, current); |
| // Walk the sampled PCs. |
| for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| if (sample->At(i) == 0) { |
| break; |
| } |
| - intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
| - current = current->GetChild(index); |
| - current->Tick(); |
| + // If we aren't sampled out of an exit frame and this is the top |
| + // frame. |
| + bool exclusive_tick = (i == 0) && !sample->exit_frame_sample(); |
| + current = ProcessPC(sample->At(i), sample->timestamp(), current, |
| + visited(), exclusive_tick, |
| + sample->missing_frame_inserted()); |
| } |
| } |
| - CodeRegionTrieNode* root() const { |
| + ProfileFunctionTrieNode* root() const { |
| return root_; |
| } |
| @@ -1077,8 +1456,8 @@ class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| } |
| private: |
| - CodeRegionTrieNode* ProcessUserTags(Sample* sample, |
| - CodeRegionTrieNode* current) { |
| + ProfileFunctionTrieNode* ProcessUserTags(Sample* sample, |
| + ProfileFunctionTrieNode* current) { |
| intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
| if (user_tag_index >= 0) { |
| current = current->GetChild(user_tag_index); |
| @@ -1088,8 +1467,8 @@ class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| return current; |
| } |
| - CodeRegionTrieNode* ProcessVMTags(Sample* sample, |
| - CodeRegionTrieNode* current) { |
| + ProfileFunctionTrieNode* ProcessVMTags(Sample* sample, |
| + ProfileFunctionTrieNode* current) { |
| if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| // Insert a dummy kNativeTagId node. |
| intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); |
| @@ -1110,7 +1489,8 @@ class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| return current; |
| } |
| - CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { |
| + ProfileFunctionTrieNode* ProcessTags(Sample* sample, |
| + ProfileFunctionTrieNode* current) { |
| // None. |
| if (tag_order() == ProfilerService::kNoTags) { |
| return current; |
| @@ -1138,104 +1518,389 @@ class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| intptr_t FindTagIndex(uword tag) const { |
| if (tag == 0) { |
| + UNREACHABLE(); |
| return -1; |
| } |
| intptr_t index = tag_code_table_->FindIndex(tag); |
| - if (index <= 0) { |
| + if (index < 0) { |
| + UNREACHABLE(); |
| return -1; |
| } |
| ASSERT(index >= 0); |
| - ASSERT((tag_code_table_->At(index))->contains(tag)); |
| - return tag_code_table_offset_ + index; |
| + 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 exclusive, |
| + bool missing_frame_inserted) { |
| + 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()); |
| + GrowableArray<Function*> inlined_functions; |
| + if (!code.IsNull()) { |
| + intptr_t offset = pc - code.EntryPoint(); |
| + code.GetInlinedFunctionsAt(offset, &inlined_functions); |
| + } |
| + if (code.IsNull() || (inlined_functions.length() == 0)) { |
| + // No inlined functions. |
| + ProfileFunction* function = region->function(); |
| + ASSERT(function != NULL); |
| + if (trace_) { |
| + OS::Print("[%" Px "] X - %s (%s)\n", |
| + pc, function->name(), region_name); |
| + } |
| + function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
| + current = current->GetChild(function->index()); |
| + current->AddCodeObjectIndex(code_index); |
| + current->Tick(); |
| + if ((trace_code_filter_ != NULL) && |
| + (strstr(region_name, trace_code_filter_) != NULL)) { |
| + trace_ = true; |
| + OS::Print("Tracing from: %" Px " [%s] ", pc, |
| + missing_frame_inserted ? "INSERTED" : ""); |
| + Dump(current); |
| + } |
| + return current; |
| + } |
| + |
| + |
| + for (intptr_t i = 0; i < inlined_functions.length(); i++) { |
| + Function* inlined_function = inlined_functions[i]; |
| + ASSERT(inlined_function != NULL); |
| + ASSERT(!inlined_function->IsNull()); |
| + const char* inline_name = inlined_function->ToQualifiedCString(); |
| + if (trace_) { |
| + OS::Print("[%" Px "] %" Pd " - %s (%s)\n", |
| + pc, i, inline_name, region_name); |
| + } |
| + ProfileFunction* function = |
| + function_table_->LookupOrAdd(*inlined_function); |
| + ASSERT(function != NULL); |
| + function->AddCodeObjectIndex(code_index); |
| + function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
| + exclusive = false; |
| + current = current->GetChild(function->index()); |
| + current->AddCodeObjectIndex(code_index); |
| + current->Tick(); |
| + if ((trace_code_filter_ != NULL) && |
| + (strstr(region_name, trace_code_filter_) != NULL)) { |
| + trace_ = true; |
| + OS::Print("Tracing from: %" Px " [%s] ", |
| + pc, missing_frame_inserted ? "INSERTED" : ""); |
| + Dump(current); |
| + } |
| + } |
| + return current; |
| } |
| - intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { |
| + CodeRegion* FindCodeObject(uword pc, int64_t timestamp) const { |
| intptr_t index = live_code_table_->FindIndex(pc); |
| - ASSERT(index >= 0); |
| + 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); |
| - ASSERT(index >= 0); |
| + if (index < 0) { |
| + return NULL; |
| + } |
| region = dead_code_table_->At(index); |
| ASSERT(region->contains(pc)); |
| ASSERT(region->compile_timestamp() <= timestamp); |
| - return index + dead_code_table_offset_; |
| + return region; |
| } |
| ASSERT(region->compile_timestamp() <= timestamp); |
| - return index; |
| + return region; |
| } |
| ProfilerService::TagOrder tag_order_; |
| - CodeRegionTrieNode* root_; |
| + ProfileFunctionTrieNode* root_; |
| CodeRegionTable* live_code_table_; |
| CodeRegionTable* dead_code_table_; |
| CodeRegionTable* tag_code_table_; |
| - intptr_t dead_code_table_offset_; |
| - intptr_t tag_code_table_offset_; |
| + ProfileFunctionTable* function_table_; |
| + bool trace_; |
| + const char* trace_code_filter_; |
| +}; |
| + |
| + |
| +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); |
| + } |
| + } |
| + |
| + private: |
| + static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, |
| + CodeRegionTrieNode* const* b) { |
| + ASSERT(a != NULL); |
| + ASSERT(b != NULL); |
| + return (*b)->count() - (*a)->count(); |
| + } |
| + |
| + const intptr_t code_region_index_; |
| + intptr_t count_; |
| + ZoneGrowableArray<CodeRegionTrieNode*>* children_; |
| }; |
| -class CodeRegionTableCallersBuilder { |
| +class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| public: |
| - CodeRegionTableCallersBuilder(CodeRegionTrieNode* exclusive_root, |
| - CodeRegionTable* live_code_table, |
| - CodeRegionTable* dead_code_table, |
| - CodeRegionTable* tag_code_table) |
| - : exclusive_root_(exclusive_root), |
| + CodeRegionExclusiveTrieBuilder(Isolate* isolate, |
| + CodeRegionTable* live_code_table, |
| + CodeRegionTable* dead_code_table, |
| + CodeRegionTable* tag_code_table) |
| + : SampleVisitor(isolate), |
| live_code_table_(live_code_table), |
| dead_code_table_(dead_code_table), |
| tag_code_table_(tag_code_table) { |
| - ASSERT(exclusive_root_ != 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(); |
| + set_tag_order(ProfilerService::kUserVM); |
| + |
| + intptr_t root_index = tag_code_table_->FindIndex(0); |
| + // Verify that the "0" (root) tag does exist. |
| + ASSERT(root_index >= 0); |
| + CodeRegion* region = tag_code_table_->At(root_index); |
| + ASSERT(region != NULL); |
| + root_ = new CodeRegionTrieNode(region->code_table_index()); |
| } |
| - void Build() { |
| - ProcessNode(exclusive_root_); |
| + void VisitSample(Sample* sample) { |
| + // Give the root a tick. |
| + root_->Tick(); |
| + CodeRegionTrieNode* current = root_; |
| + current = ProcessTags(sample, current); |
| + // Walk the sampled PCs. |
| + for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| + if (sample->At(i) == 0) { |
| + break; |
| + } |
| + intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
| + if (index < 0) { |
| + continue; |
| + } |
| + current = current->GetChild(index); |
| + current->Tick(); |
| + } |
| + } |
| + |
| + CodeRegionTrieNode* root() const { |
| + return root_; |
| + } |
| + |
| + ProfilerService::TagOrder tag_order() const { |
| + return tag_order_; |
| + } |
| + |
| + void set_tag_order(ProfilerService::TagOrder tag_order) { |
| + tag_order_ = tag_order; |
| } |
| private: |
| - void ProcessNode(CodeRegionTrieNode* parent) { |
| - const ZoneGrowableArray<CodeRegionTrieNode*>& children = parent->children(); |
| - intptr_t parent_index = parent->code_region_index(); |
| - ASSERT(parent_index >= 0); |
| - CodeRegion* parent_region = At(parent_index); |
| - ASSERT(parent_region != NULL); |
| - for (intptr_t i = 0; i < children.length(); i++) { |
| - CodeRegionTrieNode* node = children[i]; |
| - ProcessNode(node); |
| - intptr_t index = node->code_region_index(); |
| - ASSERT(index >= 0); |
| - CodeRegion* region = At(index); |
| - ASSERT(region != NULL); |
| - region->AddCallee(parent_index, node->count()); |
| - parent_region->AddCaller(index, node->count()); |
| + CodeRegionTrieNode* ProcessUserTags(Sample* sample, |
| + CodeRegionTrieNode* current) { |
| + intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
| + if (user_tag_index >= 0) { |
| + current = current->GetChild(user_tag_index); |
| + // Give the tag a tick. |
| + current->Tick(); |
| } |
| + return current; |
| } |
| - CodeRegion* At(intptr_t final_index) { |
| - ASSERT(final_index >= 0); |
| - if (final_index < dead_code_table_offset_) { |
| - return live_code_table_->At(final_index); |
| - } else if (final_index < tag_code_table_offset_) { |
| - return dead_code_table_->At(final_index - dead_code_table_offset_); |
| - } else { |
| - return tag_code_table_->At(final_index - tag_code_table_offset_); |
| + CodeRegionTrieNode* ProcessVMTags(Sample* sample, |
| + CodeRegionTrieNode* current) { |
| + if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| + // Insert a dummy kNativeTagId node. |
| + intptr_t tag_index = FindTagIndex(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 = FindTagIndex(VMTag::kRuntimeTagId); |
| + current = current->GetChild(tag_index); |
| + // Give the tag a tick. |
| + current->Tick(); |
| + } |
| + intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
| + current = current->GetChild(tag_index); |
| + // Give the tag a tick. |
| + current->Tick(); |
| + return current; |
| + } |
| + |
| + CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { |
| + // None. |
| + if (tag_order() == ProfilerService::kNoTags) { |
| + return current; |
| + } |
| + // User first. |
| + if ((tag_order() == ProfilerService::kUserVM) || |
| + (tag_order() == ProfilerService::kUser)) { |
| + current = ProcessUserTags(sample, current); |
| + // Only user. |
| + if (tag_order() == ProfilerService::kUser) { |
| + return current; |
| + } |
| + return ProcessVMTags(sample, current); |
| + } |
| + // VM first. |
| + ASSERT((tag_order() == ProfilerService::kVMUser) || |
| + (tag_order() == ProfilerService::kVM)); |
| + current = ProcessVMTags(sample, current); |
| + // Only VM. |
| + if (tag_order() == ProfilerService::kVM) { |
| + return current; |
| + } |
| + return ProcessUserTags(sample, current); |
| + } |
| + |
| + intptr_t FindTagIndex(uword tag) const { |
| + if (tag == 0) { |
| + UNREACHABLE(); |
| + return -1; |
| + } |
| + intptr_t index = tag_code_table_->FindIndex(tag); |
| + if (index < 0) { |
| + UNREACHABLE(); |
| + return -1; |
| + } |
| + 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; |
| } |
| + CodeRegion* region = dead_code_table_->At(index); |
| + ASSERT(region->contains(pc)); |
| + ASSERT(region->compile_timestamp() <= timestamp); |
| + return region->code_table_index(); |
| } |
| - CodeRegionTrieNode* exclusive_root_; |
| + 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); |
| + } |
| + 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); |
| + } |
| + ASSERT(region->compile_timestamp() <= timestamp); |
| + return region->code_table_index(); |
| + } |
| + |
| + ProfilerService::TagOrder tag_order_; |
| + CodeRegionTrieNode* root_; |
| CodeRegionTable* live_code_table_; |
| CodeRegionTable* dead_code_table_; |
| CodeRegionTable* tag_code_table_; |
| - intptr_t dead_code_table_offset_; |
| - intptr_t tag_code_table_offset_; |
| }; |
| @@ -1253,8 +1918,10 @@ void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
| } |
| SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| ASSERT(sample_buffer != NULL); |
| + ScopeTimer sw("ProfilerService::PrintJSON", FLAG_trace_profiler); |
| { |
| StackZone zone(isolate); |
| + HANDLESCOPE(isolate); |
| { |
| // Live code holds Dart, Native, and Collected CodeRegions. |
| CodeRegionTable live_code_table; |
| @@ -1262,19 +1929,25 @@ void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
| 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); |
| + |
| + { |
| + ScopeTimer sw("PreprocessSamples", FLAG_trace_profiler); |
| + // Preprocess samples. |
| + PreprocessVisitor preprocessor(isolate); |
| + sample_buffer->VisitSamples(&preprocessor); |
| + } |
| + |
| + // Build CodeRegion tables. |
| CodeRegionTableBuilder builder(isolate, |
| &live_code_table, |
| &dead_code_table, |
| - &tag_code_table); |
| + &tag_code_table, |
| + deoptimized_code); |
| { |
| - ScopeTimer sw("FixTopFrame", FLAG_trace_profiler); |
| - // Preprocess samples and fix the caller when the top PC is in a |
| - // stub or intrinsic without a frame. |
| - FixTopFrameVisitor fixTopFrame(isolate); |
| - sample_buffer->VisitSamples(&fixTopFrame); |
| - } |
| - { |
| - // Build CodeRegion tables. |
| ScopeTimer sw("CodeRegionTableBuilder", FLAG_trace_profiler); |
| sample_buffer->VisitSamples(&builder); |
| } |
| @@ -1290,72 +1963,130 @@ void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
| total_dead_code_objects, |
| total_tag_code_objects); |
| } |
| -#if defined(DEBUG) |
| - live_code_table.Verify(); |
| - dead_code_table.Verify(); |
| - tag_code_table.Verify(); |
| + |
| + 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) { |
| - OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); |
| + intptr_t total_functions = function_table.Length(); |
| + OS::Print("FunctionTable: size=%" Pd "\n", total_functions); |
| } |
| -#endif |
| - CodeRegionExclusiveTrieBuilder build_trie(isolate, |
| - &live_code_table, |
| - &dead_code_table, |
| - &tag_code_table); |
| - build_trie.set_tag_order(tag_order); |
| + CodeRegionExclusiveTrieBuilder 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("CodeRegionExclusiveTrieBuilder", FLAG_trace_profiler); |
| - sample_buffer->VisitSamples(&build_trie); |
| - build_trie.root()->SortByCount(); |
| + sample_buffer->VisitSamples(&code_trie_builder); |
| + code_trie_builder.root()->SortByCount(); |
| } |
| - CodeRegionTableCallersBuilder build_callers(build_trie.root(), |
| - &live_code_table, |
| - &dead_code_table, |
| - &tag_code_table); |
| + ProfileFunctionExclusiveTrieBuilder |
| + function_trie_builder(isolate, |
| + &live_code_table, |
| + &dead_code_table, |
| + &tag_code_table, |
| + &function_table); |
| + function_trie_builder.set_tag_order(tag_order); |
| { |
| - // Build CodeRegion callers. |
| - ScopeTimer sw("CodeRegionTableCallersBuilder", FLAG_trace_profiler); |
| - build_callers.Build(); |
| + // Build ProfileFunction trie. |
| + ScopeTimer sw("ProfileFunctionExclusiveTrieBuilder", |
| + FLAG_trace_profiler); |
| + sample_buffer->VisitSamples(&function_trie_builder); |
| + function_trie_builder.root()->SortByCount(); |
| } |
| { |
| ScopeTimer sw("CodeTableStream", FLAG_trace_profiler); |
| // Serialize to JSON. |
| JSONObject obj(stream); |
| - obj.AddProperty("type", "CpuProfile"); |
| - obj.AddProperty("id", "profile"); |
| - obj.AddProperty("samples", samples); |
| - obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth)); |
| - obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period)); |
| + 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 exclusive_trie(&obj, "exclusive_trie"); |
| - CodeRegionTrieNode* root = build_trie.root(); |
| + JSONArray exclusive_trie(&obj, "exclusiveCodeTrie"); |
| + CodeRegionTrieNode* root = code_trie_builder.root(); |
| ASSERT(root != NULL); |
| root->PrintToJSONArray(&exclusive_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(isolate, &codes); |
| + { |
| + JSONArray function_trie(&obj, "exclusiveFunctionTrie"); |
| + ProfileFunctionTrieNode* root = function_trie_builder.root(); |
| + ASSERT(root != NULL); |
| + root->PrintToJSONArray(&function_trie); |
| } |
| - for (intptr_t i = 0; i < dead_code_table.Length(); i++) { |
| - CodeRegion* region = dead_code_table.At(i); |
| - ASSERT(region != NULL); |
| - region->PrintToJSONArray(isolate, &codes); |
| + { |
| + 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); |
| + } |
| } |
| - for (intptr_t i = 0; i < tag_code_table.Length(); i++) { |
| - CodeRegion* region = tag_code_table.At(i); |
| - ASSERT(region != NULL); |
| - region->PrintToJSONArray(isolate, &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); |
| } |
| } |
| // Enable profile interrupts. |
| Profiler::BeginExecution(isolate); |
| } |
| + |
| +void ProfilerService::ClearSamples() { |
| + 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) { |
| + return; |
| + } |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + ASSERT(sample_buffer != NULL); |
| + |
| + ClearProfileVisitor clear_profile(isolate); |
| + sample_buffer->VisitSamples(&clear_profile); |
| + |
| + // Enable profile interrupts. |
| + Profiler::BeginExecution(isolate); |
| +} |
| + |
| } // namespace dart |