| Index: runtime/vm/profiler_service.cc
|
| diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
|
| index a6457d6cd40ad1bd1186539ef5be1c87bf14d677..bfd4912fccdb83dd1f484bf0f092eef7010bf2aa 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 size_before = SizeOf(previous_);
|
| + intptr_t size_after = SizeOf(current_);
|
| + if ((size_before > 0) && FLAG_trace_profiler) {
|
| + intptr_t length_before = previous_.Length();
|
| + intptr_t length_after = current_.Length();
|
| + 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
|
|
|