Chromium Code Reviews| Index: runtime/vm/profiler.cc |
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
| index 9eb125e0ed3d05c0beca8b121758f3219cb13ee6..7d3809354df5d4ecc736ca359b2a075aa0b4e4bf 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_ = OS::GetCurrentTimeMillis(); |
|
siva
2014/03/13 17:12:38
This also should be done conditionally under the f
Cutch
2014/03/13 17:33:57
Done.
|
| + } |
| + |
| + 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. |
| + kOverwrittenCode, // Dead Dart code that has been overwritten by kDartCode. |
|
siva
2014/03/13 17:12:38
I would call this kReusedCode as it is a region th
Cutch
2014/03/13 17:33:57
Done.
|
| + 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 kOverwrittenCode: |
| + 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,21 +384,42 @@ 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() == kOverwrittenCode); |
| + JSONObject obj(profile_code_obj, "code"); |
| + obj.AddProperty("type", "@Code"); |
| + obj.AddProperty("kind", "Overwritten"); |
| + obj.AddProperty("name", name()); |
| + obj.AddProperty("user_name", name()); |
| + obj.AddPropertyF("start", "%" Px "", start()); |
| + obj.AddPropertyF("end", "%" Px "", end()); |
| + obj.AddPropertyF("id", "/code/overwritten-%" Px "", start()); |
| + { |
| + // Generate a fake function entry. |
| + JSONObject func(&obj, "function"); |
| + func.AddProperty("type", "@Function"); |
| + obj.AddPropertyF("id", "/functions/overwritten-%" Px "", start()); |
| + func.AddProperty("name", name()); |
| + func.AddProperty("user_name", name()); |
| + func.AddProperty("kind", "Overwritten"); |
| + } |
| + } |
| + |
| 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()); |
| @@ -396,6 +439,12 @@ class CodeRegion : public ZoneAllocated { |
| GenerateAndSetSymbolName("Collected"); |
| } |
| PrintCollectedCode(&obj); |
| + } else if (kind() == kOverwrittenCode) { |
| + if (name() == NULL) { |
| + // Lazily set generated name. |
| + GenerateAndSetSymbolName("Overwritten"); |
| + } |
| + PrintOverwrittenCode(&obj); |
| } else { |
| ASSERT(kind() == kNativeCode); |
| if (name() == NULL) { |
| @@ -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,73 @@ 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) : |
| + explicit CodeRegionTable(Isolate* isolate) : |
| heap_(isolate->heap()), |
| 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 +616,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 +638,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 +651,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 +659,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 +667,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 +684,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) { |
| @@ -717,13 +772,33 @@ 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), |
| + heap_(isolate->heap()) { |
|
siva
2014/03/13 17:12:38
Why not store the isolate as a field in this class
Cutch
2014/03/13 17:33:57
Done.
|
| + ASSERT(live_code_table_ != NULL); |
| + ASSERT(dead_code_table_ != NULL); |
| frames_ = 0; |
| min_time_ = kMaxInt64; |
| max_time_ = 0; |
| + vm_isolate_heap_ = Dart::vm_isolate()->heap(); |
| + ASSERT(vm_isolate_heap_ != NULL); |
| } |
| + /* |
| + code_region_table_->AddTick(sample->At(i), false, visited()); |
| + ASSERT(index >= 0); |
| + ASSERT(index < code_region_table_->length()); |
| + if (index < 0) { |
| + CodeRegion* code_region = CreateCodeRegion(pc); |
| + ASSERT(code_region != NULL); |
| + index = InsertCodeRegion(code_region); |
| + } |
| + ASSERT(index >= 0); |
| + */ |
|
siva
2014/03/13 17:12:38
Why do you want to keep this commented out code ar
Cutch
2014/03/13 17:33:57
Removed.
|
| + |
| void VisitSample(Sample* sample) { |
| int64_t timestamp = sample->timestamp(); |
| if (timestamp > max_time_) { |
| @@ -732,44 +807,139 @@ 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::kOverwrittenCode, |
| + 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) { |
| + Code& code = Code::Handle(); |
| + code ^= Code::LookupCode(pc); |
| + if (!code.IsNull()) { |
| + return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| + code.EntryPoint() + code.Size(), |
| + code.compile_timestamp()); |
| + } |
| + code ^= Code::LookupCodeInVmIsolate(pc); |
| + if (!code.IsNull()) { |
| + return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| + code.EntryPoint() + code.Size(), |
| + code.compile_timestamp()); |
| + } |
| + if (heap_->CodeContains(pc) || vm_isolate_heap_->CodeContains(pc)) { |
|
siva
2014/03/13 17:12:38
Wouldn't it be more efficient to do this check fir
Cutch
2014/03/13 17:33:57
Done.
|
| + const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
| + const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
| + return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| + (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| + 0); |
| + } |
| + 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, 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_; |
| + const Heap* heap_; |
| + const Heap* vm_isolate_heap_; |
| }; |
| 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 +948,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 +964,36 @@ class CodeRegionTableCallersBuilder : public SampleVisitor { |
| } |
| private: |
| - ProfilerCodeRegionTable* code_region_table_; |
| + intptr_t FindFinalIndex(uword pc, int64_t timestamp) { |
|
siva
2014/03/13 17:12:38
) const {
Cutch
2014/03/13 17:33:57
Done.
|
| + 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 +1014,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(isolate); |
| + // Dead code holds Overwritten CodeRegions. |
| + CodeRegionTable dead_code_table(isolate); |
| + 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 +1060,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); |
| } |