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