Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Side by Side Diff: runtime/vm/profiler.cc

Issue 151143003: Improve CodeRegionTable build time by 30x (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« runtime/vm/profiler.h ('K') | « runtime/vm/profiler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "platform/utils.h" 5 #include "platform/utils.h"
6 6
7 #include "vm/allocation.h" 7 #include "vm/allocation.h"
8 #include "vm/atomic.h" 8 #include "vm/atomic.h"
9 #include "vm/code_patcher.h" 9 #include "vm/code_patcher.h"
10 #include "vm/isolate.h" 10 #include "vm/isolate.h"
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 return; 141 return;
142 } 142 }
143 if (!FLAG_profile) { 143 if (!FLAG_profile) {
144 return; 144 return;
145 } 145 }
146 ASSERT(initialized_); 146 ASSERT(initialized_);
147 ThreadInterrupter::Unregister(); 147 ThreadInterrupter::Unregister();
148 } 148 }
149 149
150 150
151 void Profiler::RecordTickInterruptCallback(const InterruptedThreadState& state,
152 void* data) {
153 Isolate* isolate = reinterpret_cast<Isolate*>(data);
154 if (isolate == NULL) {
155 return;
156 }
157 IsolateProfilerData* profiler_data = isolate->profiler_data();
158 if (profiler_data == NULL) {
159 return;
160 }
161 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
162 if (sample_buffer == NULL) {
163 return;
164 }
165 Sample* sample = sample_buffer->ReserveSample();
166 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(),
167 state.tid);
168 }
169
170
171 void Profiler::RecordSampleInterruptCallback(
172 const InterruptedThreadState& state,
173 void* data) {
174 Isolate* isolate = reinterpret_cast<Isolate*>(data);
175 if (isolate == NULL) {
176 return;
177 }
178 IsolateProfilerData* profiler_data = isolate->profiler_data();
179 if (profiler_data == NULL) {
180 return;
181 }
182 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
183 if (sample_buffer == NULL) {
184 return;
185 }
186 Sample* sample = sample_buffer->ReserveSample();
187 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(),
188 state.tid);
189 uintptr_t stack_lower = 0;
190 uintptr_t stack_upper = 0;
191 isolate->GetStackBounds(&stack_lower, &stack_upper);
192 if ((stack_lower == 0) || (stack_upper == 0)) {
193 stack_lower = 0;
194 stack_upper = 0;
195 }
196 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper,
197 state.pc, state.fp, state.sp);
198 stackWalker.walk();
199 }
200
201
202 struct AddressEntry { 151 struct AddressEntry {
203 uintptr_t pc; 152 uintptr_t pc;
204 uintptr_t ticks; 153 uintptr_t ticks;
205 }; 154 };
206 155
207 156
208 // A region of code. Each region is a kind of code (Dart, Collected, or Native). 157 // A region of code. Each region is a kind of code (Dart, Collected, or Native).
209 class CodeRegion : public ZoneAllocated { 158 class CodeRegion : public ZoneAllocated {
210 public: 159 public:
211 enum Kind { 160 enum Kind {
212 kDartCode, 161 kDartCode,
213 kCollectedCode, 162 kCollectedCode,
214 kNativeCode 163 kNativeCode
215 }; 164 };
216 165
217 CodeRegion(Kind kind, uintptr_t start, uintptr_t end) : 166 CodeRegion(Kind kind, uintptr_t start, uintptr_t end) :
218 kind_(kind), 167 kind_(kind),
219 start_(start), 168 start_(start),
220 end_(end), 169 end_(end),
221 inclusive_ticks_(0), 170 inclusive_ticks_(0),
222 exclusive_ticks_(0), 171 exclusive_ticks_(0),
223 name_(NULL), 172 name_(NULL),
224 address_table_(new ZoneGrowableArray<AddressEntry>()) { 173 address_table_(new ZoneGrowableArray<AddressEntry>()) {
174 ASSERT(start_ < end_);
225 } 175 }
226 176
227 ~CodeRegion() { 177 ~CodeRegion() {
228 } 178 }
229 179
230 uintptr_t start() const { return start_; } 180 uintptr_t start() const { return start_; }
231 void set_start(uintptr_t start) { 181 void set_start(uintptr_t start) {
232 start_ = start; 182 start_ = start;
233 } 183 }
234 184
235 uintptr_t end() const { return end_; } 185 uintptr_t end() const { return end_; }
236 void set_end(uintptr_t end) { 186 void set_end(uintptr_t end) {
237 end_ = end; 187 end_ = end;
238 } 188 }
239 189
240 void AdjustExtent(uintptr_t start, uintptr_t end) { 190 void AdjustExtent(uintptr_t start, uintptr_t end) {
241 if (start < start_) { 191 if (start < start_) {
242 start_ = start; 192 start_ = start;
243 } 193 }
244 if (end > end_) { 194 if (end > end_) {
245 end_ = end; 195 end_ = end;
246 } 196 }
197 ASSERT(start_ < end_);
247 } 198 }
248 199
249 bool contains(uintptr_t pc) const { 200 bool contains(uintptr_t pc) const {
250 return (pc >= start_) && (pc < end_); 201 return (pc >= start_) && (pc < end_);
251 } 202 }
252 203
204 bool overlaps(const CodeRegion* other) const {
205 ASSERT(other != NULL);
206 bool a = other->contains(start_);
207 bool b = other->contains(end_ - 1);
208 bool c = contains(other->start());
209 bool d = contains(other->end() - 1);
210 return a || b || c || d;
siva 2014/02/12 00:58:52 There is no short circuiting of calls to contains
Cutch 2014/02/13 23:13:19 Done.
211 }
212
253 intptr_t inclusive_ticks() const { return inclusive_ticks_; } 213 intptr_t inclusive_ticks() const { return inclusive_ticks_; }
254 void set_inclusive_ticks(intptr_t inclusive_ticks) { 214 void set_inclusive_ticks(intptr_t inclusive_ticks) {
255 inclusive_ticks_ = inclusive_ticks; 215 inclusive_ticks_ = inclusive_ticks;
256 } 216 }
257 217
258 intptr_t exclusive_ticks() const { return exclusive_ticks_; } 218 intptr_t exclusive_ticks() const { return exclusive_ticks_; }
259 void set_exclusive_ticks(intptr_t exclusive_ticks) { 219 void set_exclusive_ticks(intptr_t exclusive_ticks) {
260 exclusive_ticks_ = exclusive_ticks; 220 exclusive_ticks_ = exclusive_ticks;
261 } 221 }
262 222
(...skipping 24 matching lines...) Expand all
287 } 247 }
288 248
289 void AddTick(bool exclusive) { 249 void AddTick(bool exclusive) {
290 if (exclusive) { 250 if (exclusive) {
291 exclusive_ticks_++; 251 exclusive_ticks_++;
292 } else { 252 } else {
293 inclusive_ticks_++; 253 inclusive_ticks_++;
294 } 254 }
295 } 255 }
296 256
297 void DebugPrint() { 257 void DebugPrint() const {
298 printf("%s [%" Px ", %" Px ") %s\n", name_, start(), end(), 258 printf("%s [%" Px ", %" Px ") %s\n", KindToCString(kind_), start(), end(),
299 KindToCString(kind_)); 259 name_);
300 } 260 }
301 261
302 void AddTickAtAddress(uintptr_t pc) { 262 void AddTickAtAddress(uintptr_t pc) {
303 const intptr_t length = address_table_->length(); 263 const intptr_t length = address_table_->length();
304 intptr_t i = 0; 264 intptr_t i = 0;
305 for (; i < length; i++) { 265 for (; i < length; i++) {
306 AddressEntry& entry = (*address_table_)[i]; 266 AddressEntry& entry = (*address_table_)[i];
307 if (entry.pc == pc) { 267 if (entry.pc == pc) {
308 entry.ticks++; 268 entry.ticks++;
309 return; 269 return;
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 uintptr_t end_; 347 uintptr_t end_;
388 intptr_t inclusive_ticks_; 348 intptr_t inclusive_ticks_;
389 intptr_t exclusive_ticks_; 349 intptr_t exclusive_ticks_;
390 const char* name_; 350 const char* name_;
391 ZoneGrowableArray<AddressEntry>* address_table_; 351 ZoneGrowableArray<AddressEntry>* address_table_;
392 352
393 DISALLOW_COPY_AND_ASSIGN(CodeRegion); 353 DISALLOW_COPY_AND_ASSIGN(CodeRegion);
394 }; 354 };
395 355
396 356
357 class ScopeStopwatch {
358 public:
359 explicit ScopeStopwatch(const char* name) : name_(name) {
360 start_ = OS::GetCurrentTimeMillis();
361 }
362
363 intptr_t GetElapsed() {
siva 2014/02/12 00:58:52 const {
Cutch 2014/02/13 23:13:19 Done.
364 intptr_t end = OS::GetCurrentTimeMillis();
365 ASSERT(end >= start_);
366 return end - start_;
367 }
368
369 ~ScopeStopwatch() {
370 if (FLAG_trace_profiled_isolates) {
371 intptr_t elapsed = GetElapsed();
372 OS::Print("%s took %" Pd " millis.\n", name_, elapsed);
373 }
374 }
375
376 private:
377 const char* name_;
378 intptr_t start_;
siva 2014/02/12 00:58:52 DISALLOW stuff..... or make this a ValueObject. Pr
Cutch 2014/02/13 23:13:19 Done.
379 };
380
381
397 // All code regions. Code region tables are built on demand when a profile 382 // All code regions. Code region tables are built on demand when a profile
398 // is requested (through the service or on isolate shutdown). 383 // is requested (through the service or on isolate shutdown).
399 class ProfilerCodeRegionTable : public ValueObject { 384 class ProfilerCodeRegionTable : public ValueObject {
400 public: 385 public:
401 explicit ProfilerCodeRegionTable(Isolate* isolate) : 386 explicit ProfilerCodeRegionTable(Isolate* isolate) :
402 heap_(isolate->heap()), 387 heap_(isolate->heap()),
403 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { 388 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) {
404 } 389 }
405 390
406 ~ProfilerCodeRegionTable() { 391 ~ProfilerCodeRegionTable() {
(...skipping 13 matching lines...) Expand all
420 (*code_region_table_)[index]->AddTickAtAddress(pc); 405 (*code_region_table_)[index]->AddTickAtAddress(pc);
421 } 406 }
422 } 407 }
423 408
424 intptr_t Length() const { return code_region_table_->length(); } 409 intptr_t Length() const { return code_region_table_->length(); }
425 410
426 CodeRegion* At(intptr_t idx) { 411 CodeRegion* At(intptr_t idx) {
427 return (*code_region_table_)[idx]; 412 return (*code_region_table_)[idx];
428 } 413 }
429 414
415 #if defined(DEBUG)
416 void Verify() {
417 VerifyOrder();
418 VerifyOverlap();
419 }
420 #endif
421
430 private: 422 private:
423 intptr_t FindUpperBound(uintptr_t pc) {
siva 2014/02/12 00:58:52 we usually use uword for address types.
Cutch 2014/02/13 23:13:19 Done here and elsewhere.
424 intptr_t count = code_region_table_->length();
425 intptr_t first = 0;
426 while (count > 0) {
427 intptr_t it = first;
428 intptr_t step = count / 2;
429 it += step;
430 const CodeRegion* code_region = (*code_region_table_)[it];
431 if (pc >= code_region->end()) {
432 first = ++it;
433 count -= (step + 1);
434 } else {
435 count = step;
436 }
437 }
438 return first;
439 }
440
441
442 intptr_t FindLowerBound(uintptr_t pc) {
443 intptr_t count = code_region_table_->length();
444 intptr_t first = 0;
445 while (count > 0) {
446 intptr_t it = first;
447 intptr_t step = count / 2;
448 it += step;
449 const CodeRegion* code_region = (*code_region_table_)[it];
450 if (code_region->start() < pc) {
451 first = ++it;
452 count -= (step + 1);
453 } else {
454 count = step;
455 }
456 }
457 return first;
458 }
siva 2014/02/12 00:58:52 Would it make sense to combine FindUpperBound and
Cutch 2014/02/13 23:13:19 Done.
459
460
431 intptr_t FindIndex(uintptr_t pc) { 461 intptr_t FindIndex(uintptr_t pc) {
432 const intptr_t length = code_region_table_->length(); 462 intptr_t index = FindLowerBound(pc);
433 for (intptr_t i = 0; i < length; i++) { 463 const CodeRegion* code_region = NULL;
434 const CodeRegion* code_region = (*code_region_table_)[i]; 464 if (index > 0) {
465 // We may have overshot by 1. Check previous entry.
siva 2014/02/12 00:58:52 Not sure how this happens?
Cutch 2014/02/13 23:13:19 It doesn't anymore.
466 code_region = (*code_region_table_)[index - 1];
435 if (code_region->contains(pc)) { 467 if (code_region->contains(pc)) {
436 return i; 468 // Found at index - 1.
469 return index - 1;
437 } 470 }
438 } 471 }
472 if (index == code_region_table_->length()) {
473 // Not present.
474 return -1;
475 }
476 code_region = (*code_region_table_)[index];
477 if (code_region->contains(pc)) {
478 // Found at index.
479 return index;
480 }
439 return -1; 481 return -1;
440 } 482 }
441 483
442 CodeRegion* CreateCodeRegion(uintptr_t pc) { 484 CodeRegion* CreateCodeRegion(uintptr_t pc) {
443 Code& code = Code::Handle(Code::LookupCode(pc)); 485 Code& code = Code::Handle(Code::LookupCode(pc));
444 if (!code.IsNull()) { 486 if (!code.IsNull()) {
445 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), 487 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
446 code.EntryPoint() + code.Size()); 488 code.EntryPoint() + code.Size());
447 } 489 }
448 if (heap_->CodeContains(pc)) { 490 if (heap_->CodeContains(pc)) {
449 const intptr_t kDartCodeAlignment = 0x10; 491 const intptr_t kDartCodeAlignment = 0x10;
450 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); 492 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1);
451 return new CodeRegion(CodeRegion::kCollectedCode, 493 return new CodeRegion(CodeRegion::kCollectedCode, pc,
452 (pc & kDartCodeAlignmentMask),
453 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment); 494 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment);
454 } 495 }
455 uintptr_t native_start = 0; 496 uintptr_t native_start = 0;
456 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, 497 char* native_name = NativeSymbolResolver::LookupSymbolName(pc,
457 &native_start); 498 &native_start);
458 if (native_name == NULL) { 499 if (native_name == NULL) {
459 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1); 500 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1);
460 } 501 }
461 ASSERT(pc >= native_start); 502 ASSERT(pc >= native_start);
462 CodeRegion* code_region = 503 CodeRegion* code_region =
463 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1); 504 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1);
464 code_region->SetName(native_name); 505 code_region->SetName(native_name);
465 free(native_name); 506 free(native_name);
466 return code_region; 507 return code_region;
467 } 508 }
468 509
510 void HandleOverlap(CodeRegion* region, CodeRegion* code_region,
511 uintptr_t start, uintptr_t end) {
512 // We should never see overlapping Dart code regions.
513 ASSERT(region->kind() != CodeRegion::kDartCode);
514 // When code regions overlap, they should be of the same kind.
515 ASSERT(region->kind() == code_region->kind());
516 region->AdjustExtent(start, end);
517 }
518
469 intptr_t InsertCodeRegion(CodeRegion* code_region) { 519 intptr_t InsertCodeRegion(CodeRegion* code_region) {
470 const intptr_t length = code_region_table_->length();
471 const uintptr_t start = code_region->start(); 520 const uintptr_t start = code_region->start();
472 const uintptr_t end = code_region->end(); 521 const uintptr_t end = code_region->end();
473 intptr_t i = 0; 522 const intptr_t length = code_region_table_->length();
474 for (; i < length; i++) { 523 if (length == 0) {
475 CodeRegion* region = (*code_region_table_)[i]; 524 code_region_table_->Add(code_region);
476 if (region->contains(start) || region->contains(end - 1)) { 525 return length;
477 // We should only see overlapping native code regions. 526 }
478 ASSERT(region->kind() == CodeRegion::kNativeCode); 527 // Determine the correct place to insert or merge code_region into table.
479 // When code regions overlap, they should be of the same kind. 528 intptr_t lo = FindLowerBound(start);
480 ASSERT(region->kind() == code_region->kind()); 529 intptr_t hi = FindUpperBound(end - 1);
481 // Overlapping code region. 530 if ((lo == length) && (hi == length)) {
482 region->AdjustExtent(start, end); 531 lo = length - 1;
483 return i; 532 }
484 } else if (start >= region->end()) { 533 if (lo == length) {
485 // Insert here. 534 CodeRegion* region = (*code_region_table_)[hi];
486 break; 535 if (region->overlaps(code_region)) {
536 HandleOverlap(region, code_region, start, end);
537 return hi;
538 }
539 code_region_table_->Add(code_region);
540 return length;
541 } else if (hi == length) {
542 CodeRegion* region = (*code_region_table_)[lo];
543 if (region->overlaps(code_region)) {
544 HandleOverlap(region, code_region, start, end);
545 return lo;
546 }
547 code_region_table_->Add(code_region);
548 return length;
549 } else if (lo == hi) {
550 CodeRegion* region = (*code_region_table_)[lo];
551 if (region->overlaps(code_region)) {
552 HandleOverlap(region, code_region, start, end);
553 return lo;
554 }
555 code_region_table_->InsertAt(lo, code_region);
556 return lo;
557 } else {
558 CodeRegion* region = (*code_region_table_)[lo];
559 if (region->overlaps(code_region)) {
560 HandleOverlap(region, code_region, start, end);
561 return lo;
562 }
563 region = (*code_region_table_)[hi];
564 if (region->overlaps(code_region)) {
565 HandleOverlap(region, code_region, start, end);
566 return hi;
567 }
568 code_region_table_->InsertAt(hi, code_region);
569 return hi;
570 }
571 UNREACHABLE();
572 }
573
574 #if defined(DEBUG)
575 void VerifyOrder() {
576 const intptr_t length = code_region_table_->length();
577 if (length == 0) {
578 return;
579 }
580 uintptr_t last = (*code_region_table_)[0]->end();
581 for (intptr_t i = 1; i < length; i++) {
582 CodeRegion* a = (*code_region_table_)[i];
583 ASSERT(last <= a->start());
584 last = a->end();
585 }
586 }
587
588 void VerifyOverlap() {
589 const intptr_t length = code_region_table_->length();
590 for (intptr_t i = 0; i < length; i++) {
591 CodeRegion* a = (*code_region_table_)[i];
592 for (intptr_t j = i+1; j < length; j++) {
593 CodeRegion* b = (*code_region_table_)[j];
594 ASSERT(!a->contains(b->start()) &&
595 !a->contains(b->end() - 1) &&
596 !b->contains(a->start()) &&
597 !b->contains(a->end() - 1));
487 } 598 }
488 } 599 }
489 if (i != length) {
490 code_region_table_->InsertAt(i, code_region);
491 return i;
492 }
493 code_region_table_->Add(code_region);
494 return code_region_table_->length() - 1;
495 } 600 }
601 #endif
496 602
497 Heap* heap_; 603 Heap* heap_;
498 ZoneGrowableArray<CodeRegion*>* code_region_table_; 604 ZoneGrowableArray<CodeRegion*>* code_region_table_;
499 }; 605 };
500 606
501 607
608 class CodeRegionTableBuilder : public SampleVisitor {
609 public:
610 CodeRegionTableBuilder(Isolate* isolate,
611 ProfilerCodeRegionTable* code_region_table)
612 : SampleVisitor(isolate), code_region_table_(code_region_table) {
613 frames_ = 0;
614 }
615
616 void VisitSample(Sample* sample) {
617 code_region_table_->AddTick(sample->At(0), true, false);
618 // Give all frames an inclusive tick and tick the address.
619 for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
620 if (sample->At(i) == 0) {
621 break;
622 }
623 frames_++;
624 code_region_table_->AddTick(sample->At(i), false, true);
625 }
626 }
627
628 intptr_t frames() const { return frames_; }
629 private:
630 intptr_t frames_;
631 ProfilerCodeRegionTable* code_region_table_;
632 };
633
634
502 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, 635 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream,
503 bool full) { 636 bool full) {
504 ASSERT(isolate == Isolate::Current()); 637 ASSERT(isolate == Isolate::Current());
505 // Disable profile interrupts while processing the buffer. 638 // Disable profile interrupts while processing the buffer.
506 EndExecution(isolate); 639 EndExecution(isolate);
507 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); 640 MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
508 IsolateProfilerData* profiler_data = isolate->profiler_data(); 641 IsolateProfilerData* profiler_data = isolate->profiler_data();
509 if (profiler_data == NULL) { 642 if (profiler_data == NULL) {
510 JSONObject error(stream); 643 JSONObject error(stream);
511 error.AddProperty("type", "Error"); 644 error.AddProperty("type", "Error");
512 error.AddProperty("text", "Isolate does not have profiling enabled."); 645 error.AddProperty("text", "Isolate does not have profiling enabled.");
513 return; 646 return;
514 } 647 }
515 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); 648 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
516 ASSERT(sample_buffer != NULL); 649 ASSERT(sample_buffer != NULL);
517 { 650 {
518 StackZone zone(isolate); 651 StackZone zone(isolate);
519 { 652 {
520 // Build code region table. 653 // Build code region table.
521 ProfilerCodeRegionTable code_region_table(isolate); 654 ProfilerCodeRegionTable code_region_table(isolate);
522 intptr_t samples = 655 CodeRegionTableBuilder builder(isolate, &code_region_table);
523 ProcessSamples(isolate, &code_region_table, sample_buffer);
524 { 656 {
657 ScopeStopwatch sw("CodeTableBuild");
658 sample_buffer->VisitSamples(&builder);
659 }
660 #if defined(DEBUG)
661 code_region_table.Verify();
662 #endif
663 // Number of samples we processed.
664 intptr_t samples = builder.visited();
665 intptr_t frames = builder.frames();
666 if (FLAG_trace_profiled_isolates) {
667 OS::Print("%" Pd " frames produced %" Pd " code objects.\n",
668 frames, code_region_table.Length());
669 }
670 {
671 ScopeStopwatch sw("CodeTableStream");
525 // Serialize to JSON. 672 // Serialize to JSON.
526 JSONObject obj(stream); 673 JSONObject obj(stream);
527 obj.AddProperty("type", "Profile"); 674 obj.AddProperty("type", "Profile");
528 obj.AddProperty("samples", samples); 675 obj.AddProperty("samples", samples);
529 JSONArray codes(&obj, "codes"); 676 JSONArray codes(&obj, "codes");
530 for (intptr_t i = 0; i < code_region_table.Length(); i++) { 677 for (intptr_t i = 0; i < code_region_table.Length(); i++) {
531 CodeRegion* region = code_region_table.At(i); 678 CodeRegion* region = code_region_table.At(i);
532 ASSERT(region != NULL); 679 ASSERT(region != NULL);
533 region->PrintToJSONArray(&codes, full); 680 region->PrintToJSONArray(&codes, false);
534 } 681 }
535 } 682 }
536 } 683 }
537 } 684 }
538 // Enable profile interrupts. 685 // Enable profile interrupts.
539 BeginExecution(isolate); 686 BeginExecution(isolate);
540 } 687 }
541 688
542 689
543 intptr_t Profiler::ProcessSamples(Isolate* isolate,
544 ProfilerCodeRegionTable* code_region_table,
545 SampleBuffer* sample_buffer) {
546 int64_t start = OS::GetCurrentTimeMillis();
547 intptr_t samples = 0;
548 Sample* sample = Sample::Allocate();
549 for (intptr_t i = 0; i < sample_buffer->capacity(); i++) {
550 sample_buffer->CopySample(i, sample);
551 if (sample->isolate() != isolate) {
552 continue;
553 }
554 if (sample->timestamp() == 0) {
555 continue;
556 }
557 samples += ProcessSample(isolate, code_region_table, sample);
558 }
559 free(sample);
560 int64_t end = OS::GetCurrentTimeMillis();
561 if (FLAG_trace_profiled_isolates) {
562 int64_t delta = end - start;
563 OS::Print("Processed %" Pd " samples from %s in %" Pd64 " milliseconds.\n",
564 samples,
565 isolate->name(),
566 delta);
567 }
568 return samples;
569 }
570
571
572 intptr_t Profiler::ProcessSample(Isolate* isolate,
573 ProfilerCodeRegionTable* code_region_table,
574 Sample* sample) {
575 if (sample->type() != Sample::kIsolateSample) {
576 return 0;
577 }
578 if (sample->At(0) == 0) {
579 // No frames in this sample.
580 return 0;
581 }
582 // i points to the leaf (exclusive) PC sample. Do not tick the address.
583 code_region_table->AddTick(sample->At(0), true, false);
584 // Give all frames an inclusive tick and tick the address.
585 for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
586 if (sample->At(i) == 0) {
587 break;
588 }
589 code_region_table->AddTick(sample->At(i), false, true);
590 }
591 return 1;
592 }
593
594
595 void Profiler::WriteProfile(Isolate* isolate) { 690 void Profiler::WriteProfile(Isolate* isolate) {
596 if (isolate == NULL) { 691 if (isolate == NULL) {
597 return; 692 return;
598 } 693 }
599 if (!FLAG_profile) { 694 if (!FLAG_profile) {
600 return; 695 return;
601 } 696 }
602 ASSERT(initialized_); 697 ASSERT(initialized_);
603 if (FLAG_profile_dir == NULL) { 698 if (FLAG_profile_dir == NULL) {
604 return; 699 return;
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 dst->pcs_[i] = pcs_[i]; 790 dst->pcs_[i] = pcs_[i];
696 } 791 }
697 } 792 }
698 793
699 794
700 Sample* Sample::Allocate() { 795 Sample* Sample::Allocate() {
701 return reinterpret_cast<Sample*>(malloc(instance_size())); 796 return reinterpret_cast<Sample*>(malloc(instance_size()));
702 } 797 }
703 798
704 799
800 SampleVisitor::SampleVisitor(Isolate* isolate)
801 : isolate_(isolate), visited_(0) {
802 }
siva 2014/02/12 00:58:52 Why not move this to the header file itself, seems
Cutch 2014/02/13 23:13:19 Done.
803
804
705 SampleBuffer::SampleBuffer(intptr_t capacity) { 805 SampleBuffer::SampleBuffer(intptr_t capacity) {
706 capacity_ = capacity; 806 capacity_ = capacity;
707 samples_ = reinterpret_cast<Sample*>( 807 samples_ = reinterpret_cast<Sample*>(
708 calloc(capacity, Sample::instance_size())); 808 calloc(capacity, Sample::instance_size()));
709 cursor_ = 0; 809 cursor_ = 0;
710 } 810 }
711 811
712 812
713 SampleBuffer::~SampleBuffer() { 813 SampleBuffer::~SampleBuffer() {
714 if (samples_ != NULL) { 814 if (samples_ != NULL) {
(...skipping 21 matching lines...) Expand all
736 836
737 Sample* SampleBuffer::At(intptr_t idx) const { 837 Sample* SampleBuffer::At(intptr_t idx) const {
738 ASSERT(idx >= 0); 838 ASSERT(idx >= 0);
739 ASSERT(idx < capacity_); 839 ASSERT(idx < capacity_);
740 intptr_t offset = idx * Sample::instance_size(); 840 intptr_t offset = idx * Sample::instance_size();
741 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_); 841 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_);
742 return reinterpret_cast<Sample*>(samples + offset); 842 return reinterpret_cast<Sample*>(samples + offset);
743 } 843 }
744 844
745 845
746 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, 846 void SampleBuffer::VisitSamples(SampleVisitor* visitor) {
747 uintptr_t stack_lower, 847 ASSERT(visitor != NULL);
748 uintptr_t stack_upper, 848 Sample* sample = Sample::Allocate();
749 uintptr_t pc, 849 const intptr_t length = capacity();
750 uintptr_t fp, 850 for (intptr_t i = 0; i < length; i++) {
751 uintptr_t sp) : 851 CopySample(i, sample);
752 sample_(sample), 852 if (sample->isolate() != visitor->isolate()) {
753 stack_lower_(stack_lower), 853 // Another isolate.
754 stack_upper_(stack_upper), 854 continue;
755 original_pc_(pc), 855 }
756 original_fp_(fp), 856 if (sample->timestamp() == 0) {
757 original_sp_(sp), 857 // Empty.
758 lower_bound_(stack_lower) { 858 continue;
759 ASSERT(sample_ != NULL); 859 }
860 if (sample->At(0) == 0) {
861 // No frames.
862 continue;
863 }
864 visitor->IncrementVisited();
865 visitor->VisitSample(sample);
866 }
867 free(sample);
760 } 868 }
761 869
762 870
763 // Notes on stack frame walking: 871 // Notes on stack frame walking:
764 // 872 //
765 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames 873 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames
766 // The stack frame walking code uses the frame pointer to traverse the stack. 874 // The stack frame walking code uses the frame pointer to traverse the stack.
767 // If the VM is compiled without frame pointers (which is the default on 875 // If the VM is compiled without frame pointers (which is the default on
768 // recent GCC versions with optimizing enabled) the stack walking code may 876 // recent GCC versions with optimizing enabled) the stack walking code may
769 // fail (sometimes leading to a crash). 877 // fail (sometimes leading to a crash).
770 // 878 //
879 class ProfilerSampleStackWalker : public ValueObject {
880 public:
881 ProfilerSampleStackWalker(Sample* sample,
882 uintptr_t stack_lower,
883 uintptr_t stack_upper,
884 uintptr_t pc,
885 uintptr_t fp,
886 uintptr_t sp)
887 : sample_(sample),
888 stack_lower_(stack_lower),
889 stack_upper_(stack_upper),
890 original_pc_(pc),
891 original_fp_(fp),
892 original_sp_(sp),
893 lower_bound_(stack_lower) {
894 ASSERT(sample_ != NULL);
895 }
771 896
772 int ProfilerSampleStackWalker::walk() { 897 int walk() {
773 const intptr_t kMaxStep = 0x1000; // 4K. 898 const intptr_t kMaxStep = 0x1000; // 4K.
774 const bool kWalkStack = true; // Walk the stack. 899 const bool kWalkStack = true; // Walk the stack.
775 // Always store the exclusive PC. 900 // Always store the exclusive PC.
776 sample_->SetAt(0, original_pc_); 901 sample_->SetAt(0, original_pc_);
777 if (!kWalkStack) { 902 if (!kWalkStack) {
778 // Not walking the stack, only took exclusive sample. 903 // Not walking the stack, only took exclusive sample.
779 return 1; 904 return 1;
905 }
906 uword* pc = reinterpret_cast<uword*>(original_pc_);
907 uword* fp = reinterpret_cast<uword*>(original_fp_);
908 uword* previous_fp = fp;
909 if (original_sp_ > original_fp_) {
910 // Stack pointer should not be above frame pointer.
911 return 1;
912 }
913 intptr_t gap = original_fp_ - original_sp_;
914 if (gap >= kMaxStep) {
915 // Gap between frame pointer and stack pointer is
916 // too large.
917 return 1;
918 }
919 if (original_sp_ < lower_bound_) {
920 // The stack pointer gives us a better lower bound than
921 // the isolates stack limit.
922 lower_bound_ = original_sp_;
923 }
924 int i = 0;
925 for (; i < FLAG_profile_depth; i++) {
926 sample_->SetAt(i, reinterpret_cast<uintptr_t>(pc));
927 if (!ValidFramePointer(fp)) {
928 return i + 1;
929 }
930 pc = CallerPC(fp);
931 previous_fp = fp;
932 fp = CallerFP(fp);
933 intptr_t step = fp - previous_fp;
934 if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) {
935 // Frame pointer step is too large.
936 // Frame pointer did not move to a higher address.
937 // Frame pointer is outside of isolate stack bounds.
938 return i + 1;
939 }
940 // Move the lower bound up.
941 lower_bound_ = reinterpret_cast<uintptr_t>(fp);
942 }
943 return i;
780 } 944 }
781 uword* pc = reinterpret_cast<uword*>(original_pc_); 945
782 uword* fp = reinterpret_cast<uword*>(original_fp_); 946 private:
783 uword* previous_fp = fp; 947 uword* CallerPC(uword* fp) {
siva 2014/02/12 00:58:52 const {
Cutch 2014/02/13 23:13:19 Done.
784 if (original_sp_ > original_fp_) { 948 ASSERT(fp != NULL);
785 // Stack pointer should not be above frame pointer. 949 return reinterpret_cast<uword*>(*(fp + kSavedCallerPcSlotFromFp));
786 return 1;
787 } 950 }
788 intptr_t gap = original_fp_ - original_sp_; 951
789 if (gap >= kMaxStep) { 952 uword* CallerFP(uword* fp) {
siva 2014/02/12 00:58:52 const {
Cutch 2014/02/13 23:13:19 Done.
790 // Gap between frame pointer and stack pointer is 953 ASSERT(fp != NULL);
791 // too large. 954 return reinterpret_cast<uword*>(*(fp + kSavedCallerFpSlotFromFp));
792 return 1;
793 } 955 }
794 if (original_sp_ < lower_bound_) { 956
795 // The stack pointer gives us a better lower bound than 957 bool ValidFramePointer(uword* fp) {
siva 2014/02/12 00:58:52 const {
Cutch 2014/02/13 23:13:19 Done.
796 // the isolates stack limit. 958 if (fp == NULL) {
797 lower_bound_ = original_sp_; 959 return false;
960 }
961 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp);
962 cursor += sizeof(fp);
963 bool r = cursor >= lower_bound_ && cursor < stack_upper_;
964 return r;
798 } 965 }
799 int i = 0; 966
800 for (; i < FLAG_profile_depth; i++) { 967 Sample* sample_;
801 sample_->SetAt(i, reinterpret_cast<uintptr_t>(pc)); 968 const uintptr_t stack_lower_;
802 if (!ValidFramePointer(fp)) { 969 const uintptr_t stack_upper_;
803 return i + 1; 970 const uintptr_t original_pc_;
804 } 971 const uintptr_t original_fp_;
805 pc = CallerPC(fp); 972 const uintptr_t original_sp_;
806 previous_fp = fp; 973 uintptr_t lower_bound_;
807 fp = CallerFP(fp); 974 };
808 intptr_t step = fp - previous_fp; 975
809 if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) { 976 void Profiler::RecordSampleInterruptCallback(
810 // Frame pointer step is too large. 977 const InterruptedThreadState& state,
811 // Frame pointer did not move to a higher address. 978 void* data) {
812 // Frame pointer is outside of isolate stack bounds. 979 Isolate* isolate = reinterpret_cast<Isolate*>(data);
813 return i + 1; 980 if (isolate == NULL) {
814 } 981 return;
815 // Move the lower bound up.
816 lower_bound_ = reinterpret_cast<uintptr_t>(fp);
817 } 982 }
818 return i; 983 IsolateProfilerData* profiler_data = isolate->profiler_data();
984 if (profiler_data == NULL) {
985 return;
986 }
987 SampleBuffer* sample_buffer = profiler_data->sample_buffer();
988 if (sample_buffer == NULL) {
989 return;
990 }
991 Sample* sample = sample_buffer->ReserveSample();
992 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(),
993 state.tid);
994 uintptr_t stack_lower = 0;
995 uintptr_t stack_upper = 0;
996 isolate->GetStackBounds(&stack_lower, &stack_upper);
997 if ((stack_lower == 0) || (stack_upper == 0)) {
998 stack_lower = 0;
999 stack_upper = 0;
1000 }
1001 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper,
1002 state.pc, state.fp, state.sp);
1003 stackWalker.walk();
819 } 1004 }
820 1005
821 1006
822 uword* ProfilerSampleStackWalker::CallerPC(uword* fp) {
823 ASSERT(fp != NULL);
824 return reinterpret_cast<uword*>(*(fp + kSavedCallerPcSlotFromFp));
825 }
826
827
828 uword* ProfilerSampleStackWalker::CallerFP(uword* fp) {
829 ASSERT(fp != NULL);
830 return reinterpret_cast<uword*>(*(fp + kSavedCallerFpSlotFromFp));
831 }
832
833
834 bool ProfilerSampleStackWalker::ValidFramePointer(uword* fp) {
835 if (fp == NULL) {
836 return false;
837 }
838 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp);
839 cursor += sizeof(fp);
840 bool r = cursor >= lower_bound_ && cursor < stack_upper_;
841 return r;
842 }
843
844
845 } // namespace dart 1007 } // namespace dart
OLDNEW
« runtime/vm/profiler.h ('K') | « runtime/vm/profiler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698