| Index: runtime/vm/profiler.cc
|
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
|
| index 9eb125e0ed3d05c0beca8b121758f3219cb13ee6..48f4648a0a9627e32c8b17f22093b42a3e8be15b 100644
|
| --- a/runtime/vm/profiler.cc
|
| +++ b/runtime/vm/profiler.cc
|
| @@ -162,6 +162,31 @@ void Profiler::EndExecution(Isolate* isolate) {
|
| }
|
|
|
|
|
| +class ScopeStopwatch : public ValueObject {
|
| + public:
|
| + explicit ScopeStopwatch(const char* name) : name_(name) {
|
| + start_ = FLAG_trace_profiled_isolates ? OS::GetCurrentTimeMillis() : 0;
|
| + }
|
| +
|
| + intptr_t GetElapsed() const {
|
| + intptr_t end = OS::GetCurrentTimeMillis();
|
| + ASSERT(end >= start_);
|
| + return end - start_;
|
| + }
|
| +
|
| + ~ScopeStopwatch() {
|
| + if (FLAG_trace_profiled_isolates) {
|
| + intptr_t elapsed = GetElapsed();
|
| + OS::Print("%s took %" Pd " millis.\n", name_, elapsed);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + const char* name_;
|
| + intptr_t start_;
|
| +};
|
| +
|
| +
|
| struct AddressEntry {
|
| uword pc;
|
| intptr_t exclusive_ticks;
|
| @@ -183,16 +208,20 @@ struct CallEntry {
|
|
|
| typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end);
|
|
|
| -// A region of code. Each region is a kind of code (Dart, Collected, or Native).
|
| +// 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,
|
| - kCollectedCode,
|
| - kNativeCode
|
| + 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) :
|
| + CodeRegion(Kind kind, uword start, uword end, int64_t timestamp) :
|
| kind_(kind),
|
| start_(start),
|
| end_(end),
|
| @@ -200,14 +229,14 @@ class CodeRegion : public ZoneAllocated {
|
| exclusive_ticks_(0),
|
| inclusive_tick_serial_(0),
|
| name_(NULL),
|
| + compile_timestamp_(timestamp),
|
| + creation_serial_(0),
|
| address_table_(new ZoneGrowableArray<AddressEntry>()),
|
| callers_table_(new ZoneGrowableArray<CallEntry>()),
|
| callees_table_(new ZoneGrowableArray<CallEntry>()) {
|
| ASSERT(start_ < end_);
|
| }
|
|
|
| - ~CodeRegion() {
|
| - }
|
|
|
| uword start() const { return start_; }
|
| void set_start(uword start) {
|
| @@ -241,6 +270,15 @@ class CodeRegion : public ZoneAllocated {
|
| contains(other->end() - 1);
|
| }
|
|
|
| + intptr_t creation_serial() const { return creation_serial_; }
|
| + void set_creation_serial(intptr_t serial) {
|
| + creation_serial_ = serial;
|
| + }
|
| + 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;
|
| @@ -272,17 +310,25 @@ class CodeRegion : public ZoneAllocated {
|
| return "Collected";
|
| case kNativeCode:
|
| return "Native";
|
| + case kReusedCode:
|
| + return "Overwritten";
|
| + case kTagCode:
|
| + return "Tag";
|
| }
|
| UNREACHABLE();
|
| return NULL;
|
| }
|
|
|
| void DebugPrint() const {
|
| - printf("%s [%" Px ", %" Px ") %s\n", KindToCString(kind_), start(), end(),
|
| - name_);
|
| + OS::Print("%s [%" Px ", %" Px ") %"Pd" %"Pd64"\n",
|
| + KindToCString(kind_),
|
| + start(),
|
| + end(),
|
| + creation_serial_,
|
| + compile_timestamp_);
|
| }
|
|
|
| - void AddTickAtAddress(uword pc, bool exclusive, intptr_t serial) {
|
| + 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)) {
|
| @@ -293,35 +339,11 @@ class CodeRegion : public ZoneAllocated {
|
| if (exclusive) {
|
| exclusive_ticks_++;
|
| } else {
|
| + inclusive_ticks_++;
|
| // Mark the last serial we ticked the inclusive count.
|
| inclusive_tick_serial_ = serial;
|
| - inclusive_ticks_++;
|
| - }
|
| - // Tick the address entry.
|
| - const intptr_t length = address_table_->length();
|
| - intptr_t i = 0;
|
| - for (; i < length; i++) {
|
| - AddressEntry& entry = (*address_table_)[i];
|
| - if (entry.pc == pc) {
|
| - entry.tick(exclusive);
|
| - return;
|
| - }
|
| - if (entry.pc > pc) {
|
| - break;
|
| - }
|
| - }
|
| - 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);
|
| }
|
| + TickAddress(pc, exclusive);
|
| }
|
|
|
| void AddCaller(intptr_t index) {
|
| @@ -341,12 +363,12 @@ class CodeRegion : public ZoneAllocated {
|
| obj.AddProperty("user_name", name());
|
| obj.AddPropertyF("start", "%" Px "", start());
|
| obj.AddPropertyF("end", "%" Px "", end());
|
| - obj.AddPropertyF("id", "code/native/%" Px "", start());
|
| + obj.AddPropertyF("id", "code/native-%" Px "", start());
|
| {
|
| // Generate a fake function entry.
|
| JSONObject func(&obj, "function");
|
| func.AddProperty("type", "@Function");
|
| - func.AddPropertyF("id", "native/functions/%" Pd "", start());
|
| + func.AddPropertyF("id", "functions/native-%" Px "", start());
|
| func.AddProperty("name", name());
|
| func.AddProperty("user_name", name());
|
| func.AddProperty("kind", "Native");
|
| @@ -362,27 +384,48 @@ class CodeRegion : public ZoneAllocated {
|
| obj.AddProperty("user_name", name());
|
| obj.AddPropertyF("start", "%" Px "", start());
|
| obj.AddPropertyF("end", "%" Px "", end());
|
| - obj.AddPropertyF("id", "code/collected/%" Px "", start());
|
| + obj.AddPropertyF("id", "code/collected-%" Px "", start());
|
| {
|
| // Generate a fake function entry.
|
| JSONObject func(&obj, "function");
|
| func.AddProperty("type", "@Function");
|
| - func.AddPropertyF("id", "collected/functions/%" Pd "", start());
|
| + obj.AddPropertyF("id", "functions/collected-%" Px "", start());
|
| func.AddProperty("name", name());
|
| func.AddProperty("user_name", name());
|
| func.AddProperty("kind", "Collected");
|
| }
|
| }
|
|
|
| + void PrintOverwrittenCode(JSONObject* profile_code_obj) {
|
| + ASSERT(kind() == kReusedCode);
|
| + JSONObject obj(profile_code_obj, "code");
|
| + obj.AddProperty("type", "@Code");
|
| + obj.AddProperty("kind", "Reused");
|
| + obj.AddProperty("name", name());
|
| + obj.AddProperty("user_name", name());
|
| + obj.AddPropertyF("start", "%" Px "", start());
|
| + obj.AddPropertyF("end", "%" Px "", end());
|
| + obj.AddPropertyF("id", "code/reused-%" Px "", start());
|
| + {
|
| + // Generate a fake function entry.
|
| + JSONObject func(&obj, "function");
|
| + func.AddProperty("type", "@Function");
|
| + obj.AddPropertyF("id", "functions/reused-%" Px "", start());
|
| + func.AddProperty("name", name());
|
| + func.AddProperty("user_name", name());
|
| + func.AddProperty("kind", "Reused");
|
| + }
|
| + }
|
| +
|
| void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) {
|
| JSONObject obj(events);
|
| - obj.AddProperty("type", "ProfileCode");
|
| + obj.AddProperty("type", "CodeRegion");
|
| obj.AddProperty("kind", KindToCString(kind()));
|
| obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks());
|
| obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks());
|
| if (kind() == kDartCode) {
|
| // Look up code in Dart heap.
|
| - Code& code = Code::Handle();
|
| + Code& code = Code::Handle(isolate);
|
| code ^= Code::LookupCode(start());
|
| if (code.IsNull()) {
|
| // Code is a stub in the Vm isolate.
|
| @@ -393,14 +436,20 @@ class CodeRegion : public ZoneAllocated {
|
| } else if (kind() == kCollectedCode) {
|
| if (name() == NULL) {
|
| // Lazily set generated name.
|
| - GenerateAndSetSymbolName("Collected");
|
| + GenerateAndSetSymbolName("[Collected]");
|
| }
|
| PrintCollectedCode(&obj);
|
| + } else if (kind() == kReusedCode) {
|
| + if (name() == NULL) {
|
| + // Lazily set generated name.
|
| + GenerateAndSetSymbolName("[Reused]");
|
| + }
|
| + PrintOverwrittenCode(&obj);
|
| } else {
|
| ASSERT(kind() == kNativeCode);
|
| if (name() == NULL) {
|
| // Lazily set generated name.
|
| - GenerateAndSetSymbolName("Native");
|
| + GenerateAndSetSymbolName("[Native]");
|
| }
|
| PrintNativeCode(&obj);
|
| }
|
| @@ -432,6 +481,36 @@ class CodeRegion : public ZoneAllocated {
|
| }
|
|
|
| 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 AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index) {
|
| const intptr_t length = table->length();
|
| intptr_t i = 0;
|
| @@ -463,77 +542,72 @@ class CodeRegion : public ZoneAllocated {
|
| SetName(buff);
|
| }
|
|
|
| - Kind kind_;
|
| + // 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_;
|
| + // Serial number at which this CodeRegion was created.
|
| + intptr_t creation_serial_;
|
| ZoneGrowableArray<AddressEntry>* address_table_;
|
| ZoneGrowableArray<CallEntry>* callers_table_;
|
| ZoneGrowableArray<CallEntry>* callees_table_;
|
| DISALLOW_COPY_AND_ASSIGN(CodeRegion);
|
| };
|
|
|
| -
|
| -class ScopeStopwatch : public ValueObject {
|
| +// A sorted table of CodeRegions. Does not allow for overlap.
|
| +class CodeRegionTable : public ValueObject {
|
| public:
|
| - explicit ScopeStopwatch(const char* name) : name_(name) {
|
| - start_ = OS::GetCurrentTimeMillis();
|
| - }
|
| -
|
| - intptr_t GetElapsed() const {
|
| - intptr_t end = OS::GetCurrentTimeMillis();
|
| - ASSERT(end >= start_);
|
| - return end - start_;
|
| - }
|
| -
|
| - ~ScopeStopwatch() {
|
| - if (FLAG_trace_profiled_isolates) {
|
| - intptr_t elapsed = GetElapsed();
|
| - OS::Print("%s took %" Pd " millis.\n", name_, elapsed);
|
| - }
|
| - }
|
| -
|
| - private:
|
| - const char* name_;
|
| - intptr_t start_;
|
| -};
|
| -
|
| + enum TickResult {
|
| + kTicked = 0, // CodeRegion found and ticked.
|
| + kNotFound = -1, // No CodeRegion found.
|
| + kNewerCode = -2, // CodeRegion found but it was compiled after sample.
|
| + };
|
|
|
| -// All code regions. Code region tables are built on demand when a profile
|
| -// is requested (through the service or on isolate shutdown).
|
| -class ProfilerCodeRegionTable : public ValueObject {
|
| - public:
|
| - explicit ProfilerCodeRegionTable(Isolate* isolate) :
|
| - heap_(isolate->heap()),
|
| + CodeRegionTable() :
|
| code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) {
|
| }
|
|
|
| - ~ProfilerCodeRegionTable() {
|
| - }
|
| -
|
| - void AddTick(uword pc, bool exclusive, intptr_t serial) {
|
| + // 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) {
|
| - CodeRegion* code_region = CreateCodeRegion(pc);
|
| - ASSERT(code_region != NULL);
|
| - index = InsertCodeRegion(code_region);
|
| + // Not found.
|
| + return kNotFound;
|
| }
|
| - ASSERT(index >= 0);
|
| ASSERT(index < code_region_table_->length());
|
| -
|
| - // Update code object counters.
|
| - (*code_region_table_)[index]->AddTickAtAddress(pc, exclusive, serial);
|
| + CodeRegion* region = At(index);
|
| + if (region->compile_timestamp() > timestamp) {
|
| + // Compiled after tick.
|
| + return kNewerCode;
|
| + }
|
| + region->Tick(pc, exclusive, serial);
|
| + return kTicked;
|
| }
|
|
|
| + // Table length.
|
| intptr_t Length() const { return code_region_table_->length(); }
|
|
|
| - CodeRegion* At(intptr_t idx) {
|
| - return (*code_region_table_)[idx];
|
| + // Get the CodeRegion at index.
|
| + CodeRegion* At(intptr_t index) const {
|
| + return (*code_region_table_)[index];
|
| }
|
|
|
| + // Find the table index to the CodeRegion containing pc.
|
| + // Returns < 0 if not found.
|
| intptr_t FindIndex(uword pc) const {
|
| intptr_t index = FindRegionIndex(pc, &CompareLowerBound);
|
| const CodeRegion* code_region = NULL;
|
| @@ -541,90 +615,17 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| // Not present.
|
| return -1;
|
| }
|
| - code_region = (*code_region_table_)[index];
|
| + code_region = At(index);
|
| if (code_region->contains(pc)) {
|
| // Found at index.
|
| return index;
|
| }
|
| - return -1;
|
| - }
|
| -
|
| -#if defined(DEBUG)
|
| - void Verify() {
|
| - VerifyOrder();
|
| - VerifyOverlap();
|
| - }
|
| -#endif
|
| -
|
| - private:
|
| - intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const {
|
| - ASSERT(comparator != NULL);
|
| - intptr_t count = code_region_table_->length();
|
| - intptr_t first = 0;
|
| - while (count > 0) {
|
| - intptr_t it = first;
|
| - intptr_t step = count / 2;
|
| - it += step;
|
| - const CodeRegion* code_region = (*code_region_table_)[it];
|
| - if (comparator(pc, code_region->start(), code_region->end())) {
|
| - first = ++it;
|
| - count -= (step + 1);
|
| - } else {
|
| - count = step;
|
| - }
|
| - }
|
| - return first;
|
| - }
|
| -
|
| - static bool CompareUpperBound(uword pc, uword start, uword end) {
|
| - return pc >= end;
|
| - }
|
| -
|
| - static bool CompareLowerBound(uword pc, uword start, uword end) {
|
| - return end <= pc;
|
| - }
|
| -
|
| - CodeRegion* CreateCodeRegion(uword pc) {
|
| - Code& code = Code::Handle();
|
| - code ^= Code::LookupCode(pc);
|
| - if (!code.IsNull()) {
|
| - return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
|
| - code.EntryPoint() + code.Size());
|
| - }
|
| - code ^= Code::LookupCodeInVmIsolate(pc);
|
| - if (!code.IsNull()) {
|
| - return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
|
| - code.EntryPoint() + code.Size());
|
| - }
|
| - if (heap_->CodeContains(pc)) {
|
| - const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment();
|
| - const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1);
|
| - return new CodeRegion(CodeRegion::kCollectedCode, pc,
|
| - (pc & kDartCodeAlignmentMask) + kDartCodeAlignment);
|
| - }
|
| - uintptr_t native_start = 0;
|
| - char* native_name = NativeSymbolResolver::LookupSymbolName(pc,
|
| - &native_start);
|
| - if (native_name == NULL) {
|
| - return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1);
|
| - }
|
| - ASSERT(pc >= native_start);
|
| - CodeRegion* code_region =
|
| - new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1);
|
| - code_region->SetName(native_name);
|
| - free(native_name);
|
| - return code_region;
|
| - }
|
| -
|
| - void HandleOverlap(CodeRegion* region, CodeRegion* code_region,
|
| - uword start, uword end) {
|
| - // We should never see overlapping Dart code regions.
|
| - ASSERT(region->kind() != CodeRegion::kDartCode);
|
| - // When code regions overlap, they should be of the same kind.
|
| - ASSERT(region->kind() == code_region->kind());
|
| - region->AdjustExtent(start, end);
|
| + return -2;
|
| }
|
|
|
| + // 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();
|
| @@ -636,11 +637,12 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| // 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);
|
| + // TODO(johnmccutchan): Simplify below logic.
|
| if ((lo == length) && (hi == length)) {
|
| lo = length - 1;
|
| }
|
| if (lo == length) {
|
| - CodeRegion* region = (*code_region_table_)[hi];
|
| + CodeRegion* region = At(hi);
|
| if (region->overlaps(code_region)) {
|
| HandleOverlap(region, code_region, start, end);
|
| return hi;
|
| @@ -648,7 +650,7 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| code_region_table_->Add(code_region);
|
| return length;
|
| } else if (hi == length) {
|
| - CodeRegion* region = (*code_region_table_)[lo];
|
| + CodeRegion* region = At(lo);
|
| if (region->overlaps(code_region)) {
|
| HandleOverlap(region, code_region, start, end);
|
| return lo;
|
| @@ -656,7 +658,7 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| code_region_table_->Add(code_region);
|
| return length;
|
| } else if (lo == hi) {
|
| - CodeRegion* region = (*code_region_table_)[lo];
|
| + CodeRegion* region = At(lo);
|
| if (region->overlaps(code_region)) {
|
| HandleOverlap(region, code_region, start, end);
|
| return lo;
|
| @@ -664,12 +666,12 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| code_region_table_->InsertAt(lo, code_region);
|
| return lo;
|
| } else {
|
| - CodeRegion* region = (*code_region_table_)[lo];
|
| + CodeRegion* region = At(lo);
|
| if (region->overlaps(code_region)) {
|
| HandleOverlap(region, code_region, start, end);
|
| return lo;
|
| }
|
| - region = (*code_region_table_)[hi];
|
| + region = At(hi);
|
| if (region->overlaps(code_region)) {
|
| HandleOverlap(region, code_region, start, end);
|
| return hi;
|
| @@ -681,6 +683,58 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| }
|
|
|
| #if defined(DEBUG)
|
| + void Verify() {
|
| + VerifyOrder();
|
| + VerifyOverlap();
|
| + }
|
| +#endif
|
| +
|
| + 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 {
|
| + ASSERT(comparator != NULL);
|
| + intptr_t count = code_region_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())) {
|
| + first = ++it;
|
| + count -= (step + 1);
|
| + } else {
|
| + count = step;
|
| + }
|
| + }
|
| + return first;
|
| + }
|
| +
|
| + static bool CompareUpperBound(uword pc, uword start, uword end) {
|
| + return pc >= end;
|
| + }
|
| +
|
| + static bool CompareLowerBound(uword pc, uword start, uword end) {
|
| + return end <= pc;
|
| + }
|
| +
|
| + void HandleOverlap(CodeRegion* region, CodeRegion* code_region,
|
| + uword start, uword end) {
|
| + // We should never see overlapping Dart code regions.
|
| + ASSERT(region->kind() != CodeRegion::kDartCode);
|
| + // When code regions overlap, they should be of the same kind.
|
| + ASSERT(region->kind() == code_region->kind());
|
| + region->AdjustExtent(start, end);
|
| + }
|
| +
|
| +#if defined(DEBUG)
|
| void VerifyOrder() {
|
| const intptr_t length = code_region_table_->length();
|
| if (length == 0) {
|
| @@ -709,7 +763,6 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| }
|
| #endif
|
|
|
| - Heap* heap_;
|
| ZoneGrowableArray<CodeRegion*>* code_region_table_;
|
| };
|
|
|
| @@ -717,11 +770,20 @@ class ProfilerCodeRegionTable : public ValueObject {
|
| class CodeRegionTableBuilder : public SampleVisitor {
|
| public:
|
| CodeRegionTableBuilder(Isolate* isolate,
|
| - ProfilerCodeRegionTable* code_region_table)
|
| - : SampleVisitor(isolate), code_region_table_(code_region_table) {
|
| + CodeRegionTable* live_code_table,
|
| + CodeRegionTable* dead_code_table)
|
| + : SampleVisitor(isolate),
|
| + live_code_table_(live_code_table),
|
| + dead_code_table_(dead_code_table),
|
| + isolate_(isolate),
|
| + vm_isolate_(Dart::vm_isolate()) {
|
| + ASSERT(live_code_table_ != NULL);
|
| + ASSERT(dead_code_table_ != NULL);
|
| frames_ = 0;
|
| min_time_ = kMaxInt64;
|
| max_time_ = 0;
|
| + ASSERT(isolate_ != NULL);
|
| + ASSERT(vm_isolate_ != NULL);
|
| }
|
|
|
| void VisitSample(Sample* sample) {
|
| @@ -732,44 +794,148 @@ class CodeRegionTableBuilder : public SampleVisitor {
|
| if (timestamp < min_time_) {
|
| min_time_ = timestamp;
|
| }
|
| - // Give the bottom frame an exclusive tick.
|
| - code_region_table_->AddTick(sample->At(0), true, -1);
|
| - // Give all frames (including the bottom) an inclusive tick.
|
| + // Exclusive tick for bottom frame.
|
| + Tick(sample->At(0), true, timestamp);
|
| + // Inclusive tick for all frames.
|
| for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
|
| if (sample->At(i) == 0) {
|
| break;
|
| }
|
| frames_++;
|
| - code_region_table_->AddTick(sample->At(i), false, visited());
|
| + Tick(sample->At(i), false, timestamp);
|
| }
|
| }
|
|
|
| intptr_t frames() const { return frames_; }
|
| +
|
| intptr_t TimeDeltaMicros() const {
|
| return static_cast<intptr_t>(max_time_ - min_time_);
|
| }
|
| int64_t max_time() const { return max_time_; }
|
|
|
| private:
|
| + void Tick(uword pc, bool exclusive, int64_t timestamp) {
|
| + CodeRegionTable::TickResult r;
|
| + intptr_t serial = exclusive ? -1 : visited();
|
| + 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);
|
| + region->set_creation_serial(visited());
|
| + 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);
|
| + intptr_t index = dead_code_table_->InsertCodeRegion(region);
|
| + region->set_creation_serial(visited());
|
| + 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()) {
|
| + return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
|
| + code.EntryPoint() + code.Size(),
|
| + code.compile_timestamp());
|
| + }
|
| + return new CodeRegion(CodeRegion::kCollectedCode, pc,
|
| + (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
|
| + 0);
|
| + }
|
| + // 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());
|
| + }
|
| + return new CodeRegion(CodeRegion::kCollectedCode, pc,
|
| + (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
|
| + 0);
|
| + }
|
| + // 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);
|
| + }
|
| + ASSERT(pc >= native_start);
|
| + CodeRegion* code_region =
|
| + new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0);
|
| + code_region->SetName(native_name);
|
| + free(native_name);
|
| + return code_region;
|
| + }
|
| +
|
| intptr_t frames_;
|
| int64_t min_time_;
|
| int64_t max_time_;
|
| - ProfilerCodeRegionTable* code_region_table_;
|
| + CodeRegionTable* live_code_table_;
|
| + CodeRegionTable* dead_code_table_;
|
| + Isolate* isolate_;
|
| + Isolate* vm_isolate_;
|
| };
|
|
|
|
|
| class CodeRegionTableCallersBuilder : public SampleVisitor {
|
| public:
|
| CodeRegionTableCallersBuilder(Isolate* isolate,
|
| - ProfilerCodeRegionTable* code_region_table)
|
| - : SampleVisitor(isolate), code_region_table_(code_region_table) {
|
| - ASSERT(code_region_table_ != NULL);
|
| + CodeRegionTable* live_code_table,
|
| + CodeRegionTable* dead_code_table)
|
| + : SampleVisitor(isolate),
|
| + live_code_table_(live_code_table),
|
| + dead_code_table_(dead_code_table) {
|
| + ASSERT(live_code_table_ != NULL);
|
| + ASSERT(dead_code_table_ != NULL);
|
| + dead_code_table_offset_ = live_code_table_->Length();
|
| }
|
|
|
| void VisitSample(Sample* sample) {
|
| - intptr_t current_index = code_region_table_->FindIndex(sample->At(0));
|
| - ASSERT(current_index != -1);
|
| - CodeRegion* current = code_region_table_->At(current_index);
|
| + int64_t timestamp = sample->timestamp();
|
| + intptr_t current_index = FindFinalIndex(sample->At(0), timestamp);
|
| + ASSERT(current_index >= 0);
|
| + CodeRegion* current = At(current_index);
|
| intptr_t caller_index = -1;
|
| CodeRegion* caller = NULL;
|
| intptr_t callee_index = -1;
|
| @@ -778,9 +944,9 @@ class CodeRegionTableCallersBuilder : public SampleVisitor {
|
| if (sample->At(i) == 0) {
|
| break;
|
| }
|
| - caller_index = code_region_table_->FindIndex(sample->At(i));
|
| - ASSERT(caller_index != -1);
|
| - caller = code_region_table_->At(caller_index);
|
| + caller_index = FindFinalIndex(sample->At(i), timestamp);
|
| + ASSERT(caller_index >= 0);
|
| + caller = At(caller_index);
|
| current->AddCaller(caller_index);
|
| if (callee != NULL) {
|
| current->AddCallee(callee_index);
|
| @@ -794,7 +960,36 @@ class CodeRegionTableCallersBuilder : public SampleVisitor {
|
| }
|
|
|
| private:
|
| - ProfilerCodeRegionTable* code_region_table_;
|
| + intptr_t FindFinalIndex(uword pc, int64_t timestamp) const {
|
| + intptr_t index = live_code_table_->FindIndex(pc);
|
| + ASSERT(index >= 0);
|
| + CodeRegion* region = live_code_table_->At(index);
|
| + ASSERT(region->contains(pc));
|
| + if (region->compile_timestamp() > timestamp) {
|
| + // Overwritten code, find in dead code table.
|
| + index = dead_code_table_->FindIndex(pc);
|
| + ASSERT(index >= 0);
|
| + region = dead_code_table_->At(index);
|
| + ASSERT(region->contains(pc));
|
| + ASSERT(region->compile_timestamp() <= timestamp);
|
| + return index + dead_code_table_offset_;
|
| + }
|
| + ASSERT(region->compile_timestamp() <= timestamp);
|
| + return index;
|
| + }
|
| +
|
| + CodeRegion* At(intptr_t final_index) {
|
| + ASSERT(final_index >= 0);
|
| + if (final_index < dead_code_table_offset_) {
|
| + return live_code_table_->At(final_index);
|
| + } else {
|
| + return dead_code_table_->At(final_index - dead_code_table_offset_);
|
| + }
|
| + }
|
| +
|
| + CodeRegionTable* live_code_table_;
|
| + CodeRegionTable* dead_code_table_;
|
| + intptr_t dead_code_table_offset_;
|
| };
|
|
|
| void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream,
|
| @@ -815,28 +1010,43 @@ void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream,
|
| {
|
| StackZone zone(isolate);
|
| {
|
| - // Build code region table.
|
| - ProfilerCodeRegionTable code_region_table(isolate);
|
| - CodeRegionTableBuilder builder(isolate, &code_region_table);
|
| - CodeRegionTableCallersBuilder build_callers(isolate, &code_region_table);
|
| + // Live code holds Dart, Native, and Collected CodeRegions.
|
| + CodeRegionTable live_code_table;
|
| + // Dead code holds Overwritten CodeRegions.
|
| + CodeRegionTable dead_code_table;
|
| + CodeRegionTableBuilder builder(isolate,
|
| + &live_code_table,
|
| + &dead_code_table);
|
| {
|
| + // Build CodeRegion tables.
|
| ScopeStopwatch sw("CodeTableBuild");
|
| sample_buffer->VisitSamples(&builder);
|
| }
|
| + intptr_t samples = builder.visited();
|
| + intptr_t frames = builder.frames();
|
| + if (FLAG_trace_profiled_isolates) {
|
| + intptr_t total_live_code_objects = live_code_table.Length();
|
| + intptr_t total_dead_code_objects = dead_code_table.Length();
|
| + OS::Print("Processed %" Pd " frames\n", frames);
|
| + OS::Print("CodeTables: live=%" Pd " dead=%" Pd "\n",
|
| + total_live_code_objects,
|
| + total_dead_code_objects);
|
| + }
|
| #if defined(DEBUG)
|
| - code_region_table.Verify();
|
| + live_code_table.Verify();
|
| + dead_code_table.Verify();
|
| + if (FLAG_trace_profiled_isolates) {
|
| + OS::Print("CodeRegionTables verified to be ordered and not overlap.\n");
|
| + }
|
| #endif
|
| + CodeRegionTableCallersBuilder build_callers(isolate,
|
| + &live_code_table,
|
| + &dead_code_table);
|
| {
|
| + // Build CodeRegion callers.
|
| ScopeStopwatch sw("CodeTableCallersBuild");
|
| sample_buffer->VisitSamples(&build_callers);
|
| }
|
| - // Number of samples we processed.
|
| - intptr_t samples = builder.visited();
|
| - intptr_t frames = builder.frames();
|
| - if (FLAG_trace_profiled_isolates) {
|
| - OS::Print("%" Pd " frames produced %" Pd " code objects.\n",
|
| - frames, code_region_table.Length());
|
| - }
|
| {
|
| ScopeStopwatch sw("CodeTableStream");
|
| // Serialize to JSON.
|
| @@ -846,8 +1056,13 @@ void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream,
|
| obj.AddProperty("samples", samples);
|
| obj.AddProperty("time_delta_micros", builder.TimeDeltaMicros());
|
| JSONArray codes(&obj, "codes");
|
| - for (intptr_t i = 0; i < code_region_table.Length(); i++) {
|
| - CodeRegion* region = code_region_table.At(i);
|
| + for (intptr_t i = 0; i < live_code_table.Length(); i++) {
|
| + CodeRegion* region = live_code_table.At(i);
|
| + ASSERT(region != NULL);
|
| + region->PrintToJSONArray(isolate, &codes, full);
|
| + }
|
| + for (intptr_t i = 0; i < dead_code_table.Length(); i++) {
|
| + CodeRegion* region = dead_code_table.At(i);
|
| ASSERT(region != NULL);
|
| region->PrintToJSONArray(isolate, &codes, full);
|
| }
|
|
|