Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |