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 uword pc; |
| 204 uintptr_t ticks; | 153 intptr_t ticks; |
| 205 }; | 154 }; |
| 206 | 155 |
| 156 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); | |
| 207 | 157 |
| 208 // A region of code. Each region is a kind of code (Dart, Collected, or Native). | 158 // A region of code. Each region is a kind of code (Dart, Collected, or Native). |
| 209 class CodeRegion : public ZoneAllocated { | 159 class CodeRegion : public ZoneAllocated { |
| 210 public: | 160 public: |
| 211 enum Kind { | 161 enum Kind { |
| 212 kDartCode, | 162 kDartCode, |
| 213 kCollectedCode, | 163 kCollectedCode, |
| 214 kNativeCode | 164 kNativeCode |
| 215 }; | 165 }; |
| 216 | 166 |
| 217 CodeRegion(Kind kind, uintptr_t start, uintptr_t end) : | 167 CodeRegion(Kind kind, uword start, uword end) : |
| 218 kind_(kind), | 168 kind_(kind), |
| 219 start_(start), | 169 start_(start), |
| 220 end_(end), | 170 end_(end), |
| 221 inclusive_ticks_(0), | 171 inclusive_ticks_(0), |
| 222 exclusive_ticks_(0), | 172 exclusive_ticks_(0), |
| 223 name_(NULL), | 173 name_(NULL), |
| 224 address_table_(new ZoneGrowableArray<AddressEntry>()) { | 174 address_table_(new ZoneGrowableArray<AddressEntry>()) { |
| 175 ASSERT(start_ < end_); | |
| 225 } | 176 } |
| 226 | 177 |
| 227 ~CodeRegion() { | 178 ~CodeRegion() { |
| 228 } | 179 } |
| 229 | 180 |
| 230 uintptr_t start() const { return start_; } | 181 uword start() const { return start_; } |
| 231 void set_start(uintptr_t start) { | 182 void set_start(uword start) { |
| 232 start_ = start; | 183 start_ = start; |
| 233 } | 184 } |
| 234 | 185 |
| 235 uintptr_t end() const { return end_; } | 186 uword end() const { return end_; } |
| 236 void set_end(uintptr_t end) { | 187 void set_end(uword end) { |
| 237 end_ = end; | 188 end_ = end; |
| 238 } | 189 } |
| 239 | 190 |
| 240 void AdjustExtent(uintptr_t start, uintptr_t end) { | 191 void AdjustExtent(uword start, uword end) { |
| 241 if (start < start_) { | 192 if (start < start_) { |
| 242 start_ = start; | 193 start_ = start; |
| 243 } | 194 } |
| 244 if (end > end_) { | 195 if (end > end_) { |
| 245 end_ = end; | 196 end_ = end; |
| 246 } | 197 } |
| 198 ASSERT(start_ < end_); | |
| 247 } | 199 } |
| 248 | 200 |
| 249 bool contains(uintptr_t pc) const { | 201 bool contains(uword pc) const { |
| 250 return (pc >= start_) && (pc < end_); | 202 return (pc >= start_) && (pc < end_); |
| 251 } | 203 } |
| 252 | 204 |
| 205 bool overlaps(const CodeRegion* other) const { | |
| 206 ASSERT(other != NULL); | |
| 207 return other->contains(start_) || | |
| 208 other->contains(end_ - 1) || | |
| 209 contains(other->start()) || | |
| 210 contains(other->end() - 1); | |
| 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(uword 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; |
| 310 } | 270 } |
| 311 if (entry.pc > pc) { | 271 if (entry.pc > pc) { |
| 312 break; | 272 break; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 376 private: | 336 private: |
| 377 void GenerateAndSetSymbolName(const char* prefix) { | 337 void GenerateAndSetSymbolName(const char* prefix) { |
| 378 const intptr_t kBuffSize = 512; | 338 const intptr_t kBuffSize = 512; |
| 379 char buff[kBuffSize]; | 339 char buff[kBuffSize]; |
| 380 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | 340 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
| 381 prefix, start(), end()); | 341 prefix, start(), end()); |
| 382 SetName(buff); | 342 SetName(buff); |
| 383 } | 343 } |
| 384 | 344 |
| 385 Kind kind_; | 345 Kind kind_; |
| 386 uintptr_t start_; | 346 uword start_; |
| 387 uintptr_t end_; | 347 uword 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 : public ValueObject { | |
| 358 public: | |
| 359 explicit ScopeStopwatch(const char* name) : name_(name) { | |
| 360 start_ = OS::GetCurrentTimeMillis(); | |
| 361 } | |
| 362 | |
| 363 intptr_t GetElapsed() const { | |
| 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_; | |
| 379 }; | |
|
siva
2014/02/19 18:09:54
Can this be just another timer, i.e:
the code bel
| |
| 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() { |
| 407 } | 392 } |
| 408 | 393 |
| 409 void AddTick(uintptr_t pc, bool exclusive, bool tick_address) { | 394 void AddTick(uword pc, bool exclusive, bool tick_address) { |
| 410 intptr_t index = FindIndex(pc); | 395 intptr_t index = FindIndex(pc); |
| 411 if (index < 0) { | 396 if (index < 0) { |
| 412 CodeRegion* code_region = CreateCodeRegion(pc); | 397 CodeRegion* code_region = CreateCodeRegion(pc); |
| 413 ASSERT(code_region != NULL); | 398 ASSERT(code_region != NULL); |
| 414 index = InsertCodeRegion(code_region); | 399 index = InsertCodeRegion(code_region); |
| 415 } | 400 } |
| 416 ASSERT(index >= 0); | 401 ASSERT(index >= 0); |
| 417 ASSERT(index < code_region_table_->length()); | 402 ASSERT(index < code_region_table_->length()); |
| 418 (*code_region_table_)[index]->AddTick(exclusive); | 403 (*code_region_table_)[index]->AddTick(exclusive); |
| 419 if (tick_address) { | 404 if (tick_address) { |
| 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: |
| 431 intptr_t FindIndex(uintptr_t pc) { | 423 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) { |
| 432 const intptr_t length = code_region_table_->length(); | 424 ASSERT(comparator != NULL); |
| 433 for (intptr_t i = 0; i < length; i++) { | 425 intptr_t count = code_region_table_->length(); |
| 434 const CodeRegion* code_region = (*code_region_table_)[i]; | 426 intptr_t first = 0; |
| 435 if (code_region->contains(pc)) { | 427 while (count > 0) { |
| 436 return i; | 428 intptr_t it = first; |
| 429 intptr_t step = count / 2; | |
| 430 it += step; | |
| 431 const CodeRegion* code_region = (*code_region_table_)[it]; | |
| 432 if (comparator(pc, code_region->start(), code_region->end())) { | |
| 433 first = ++it; | |
| 434 count -= (step + 1); | |
| 435 } else { | |
| 436 count = step; | |
| 437 } | 437 } |
| 438 } | 438 } |
| 439 return first; | |
| 440 } | |
| 441 | |
| 442 static bool CompareUpperBound(uword pc, uword start, uword end) { | |
| 443 return pc >= end; | |
| 444 } | |
| 445 | |
| 446 static bool CompareLowerBound(uword pc, uword start, uword end) { | |
| 447 return end <= pc; | |
| 448 } | |
| 449 | |
| 450 intptr_t FindIndex(uword pc) { | |
| 451 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); | |
| 452 const CodeRegion* code_region = NULL; | |
| 453 if (index == code_region_table_->length()) { | |
| 454 // Not present. | |
| 455 return -1; | |
| 456 } | |
| 457 code_region = (*code_region_table_)[index]; | |
| 458 if (code_region->contains(pc)) { | |
| 459 // Found at index. | |
| 460 return index; | |
| 461 } | |
| 439 return -1; | 462 return -1; |
| 440 } | 463 } |
| 441 | 464 |
| 442 CodeRegion* CreateCodeRegion(uintptr_t pc) { | 465 CodeRegion* CreateCodeRegion(uword pc) { |
| 443 Code& code = Code::Handle(Code::LookupCode(pc)); | 466 Code& code = Code::Handle(Code::LookupCode(pc)); |
| 444 if (!code.IsNull()) { | 467 if (!code.IsNull()) { |
| 445 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | 468 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| 446 code.EntryPoint() + code.Size()); | 469 code.EntryPoint() + code.Size()); |
| 447 } | 470 } |
| 448 if (heap_->CodeContains(pc)) { | 471 if (heap_->CodeContains(pc)) { |
| 449 const intptr_t kDartCodeAlignment = 0x10; | 472 const intptr_t kDartCodeAlignment = 0x10; |
| 450 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | 473 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
| 451 return new CodeRegion(CodeRegion::kCollectedCode, | 474 return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| 452 (pc & kDartCodeAlignmentMask), | |
| 453 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment); | 475 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment); |
| 454 } | 476 } |
| 455 uintptr_t native_start = 0; | 477 uintptr_t native_start = 0; |
| 456 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | 478 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
| 457 &native_start); | 479 &native_start); |
| 458 if (native_name == NULL) { | 480 if (native_name == NULL) { |
| 459 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1); | 481 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1); |
| 460 } | 482 } |
| 461 ASSERT(pc >= native_start); | 483 ASSERT(pc >= native_start); |
| 462 CodeRegion* code_region = | 484 CodeRegion* code_region = |
| 463 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1); | 485 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1); |
| 464 code_region->SetName(native_name); | 486 code_region->SetName(native_name); |
| 465 free(native_name); | 487 free(native_name); |
| 466 return code_region; | 488 return code_region; |
| 467 } | 489 } |
| 468 | 490 |
| 491 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, | |
| 492 uword start, uword end) { | |
| 493 // We should never see overlapping Dart code regions. | |
| 494 ASSERT(region->kind() != CodeRegion::kDartCode); | |
| 495 // When code regions overlap, they should be of the same kind. | |
| 496 ASSERT(region->kind() == code_region->kind()); | |
| 497 region->AdjustExtent(start, end); | |
| 498 } | |
| 499 | |
| 469 intptr_t InsertCodeRegion(CodeRegion* code_region) { | 500 intptr_t InsertCodeRegion(CodeRegion* code_region) { |
| 501 const uword start = code_region->start(); | |
| 502 const uword end = code_region->end(); | |
| 470 const intptr_t length = code_region_table_->length(); | 503 const intptr_t length = code_region_table_->length(); |
| 471 const uintptr_t start = code_region->start(); | 504 if (length == 0) { |
| 472 const uintptr_t end = code_region->end(); | 505 code_region_table_->Add(code_region); |
| 473 intptr_t i = 0; | 506 return length; |
| 474 for (; i < length; i++) { | 507 } |
| 475 CodeRegion* region = (*code_region_table_)[i]; | 508 // Determine the correct place to insert or merge code_region into table. |
| 476 if (region->contains(start) || region->contains(end - 1)) { | 509 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); |
| 477 // We should only see overlapping native code regions. | 510 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); |
| 478 ASSERT(region->kind() == CodeRegion::kNativeCode); | 511 if ((lo == length) && (hi == length)) { |
| 479 // When code regions overlap, they should be of the same kind. | 512 lo = length - 1; |
| 480 ASSERT(region->kind() == code_region->kind()); | 513 } |
| 481 // Overlapping code region. | 514 if (lo == length) { |
| 482 region->AdjustExtent(start, end); | 515 CodeRegion* region = (*code_region_table_)[hi]; |
| 483 return i; | 516 if (region->overlaps(code_region)) { |
| 484 } else if (start >= region->end()) { | 517 HandleOverlap(region, code_region, start, end); |
| 485 // Insert here. | 518 return hi; |
| 486 break; | 519 } |
| 520 code_region_table_->Add(code_region); | |
| 521 return length; | |
| 522 } else if (hi == length) { | |
| 523 CodeRegion* region = (*code_region_table_)[lo]; | |
| 524 if (region->overlaps(code_region)) { | |
| 525 HandleOverlap(region, code_region, start, end); | |
| 526 return lo; | |
| 527 } | |
| 528 code_region_table_->Add(code_region); | |
| 529 return length; | |
| 530 } else if (lo == hi) { | |
| 531 CodeRegion* region = (*code_region_table_)[lo]; | |
| 532 if (region->overlaps(code_region)) { | |
| 533 HandleOverlap(region, code_region, start, end); | |
| 534 return lo; | |
| 535 } | |
| 536 code_region_table_->InsertAt(lo, code_region); | |
| 537 return lo; | |
| 538 } else { | |
| 539 CodeRegion* region = (*code_region_table_)[lo]; | |
| 540 if (region->overlaps(code_region)) { | |
| 541 HandleOverlap(region, code_region, start, end); | |
| 542 return lo; | |
| 543 } | |
| 544 region = (*code_region_table_)[hi]; | |
| 545 if (region->overlaps(code_region)) { | |
| 546 HandleOverlap(region, code_region, start, end); | |
| 547 return hi; | |
| 548 } | |
| 549 code_region_table_->InsertAt(hi, code_region); | |
| 550 return hi; | |
| 551 } | |
| 552 UNREACHABLE(); | |
| 553 } | |
| 554 | |
| 555 #if defined(DEBUG) | |
| 556 void VerifyOrder() { | |
| 557 const intptr_t length = code_region_table_->length(); | |
| 558 if (length == 0) { | |
| 559 return; | |
| 560 } | |
| 561 uword last = (*code_region_table_)[0]->end(); | |
| 562 for (intptr_t i = 1; i < length; i++) { | |
| 563 CodeRegion* a = (*code_region_table_)[i]; | |
| 564 ASSERT(last <= a->start()); | |
| 565 last = a->end(); | |
| 566 } | |
| 567 } | |
| 568 | |
| 569 void VerifyOverlap() { | |
| 570 const intptr_t length = code_region_table_->length(); | |
| 571 for (intptr_t i = 0; i < length; i++) { | |
| 572 CodeRegion* a = (*code_region_table_)[i]; | |
| 573 for (intptr_t j = i+1; j < length; j++) { | |
| 574 CodeRegion* b = (*code_region_table_)[j]; | |
| 575 ASSERT(!a->contains(b->start()) && | |
| 576 !a->contains(b->end() - 1) && | |
| 577 !b->contains(a->start()) && | |
| 578 !b->contains(a->end() - 1)); | |
| 487 } | 579 } |
| 488 } | 580 } |
| 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 } | 581 } |
| 582 #endif | |
| 496 | 583 |
| 497 Heap* heap_; | 584 Heap* heap_; |
| 498 ZoneGrowableArray<CodeRegion*>* code_region_table_; | 585 ZoneGrowableArray<CodeRegion*>* code_region_table_; |
| 499 }; | 586 }; |
| 500 | 587 |
| 501 | 588 |
| 589 class CodeRegionTableBuilder : public SampleVisitor { | |
| 590 public: | |
| 591 CodeRegionTableBuilder(Isolate* isolate, | |
| 592 ProfilerCodeRegionTable* code_region_table) | |
| 593 : SampleVisitor(isolate), code_region_table_(code_region_table) { | |
| 594 frames_ = 0; | |
| 595 } | |
| 596 | |
| 597 void VisitSample(Sample* sample) { | |
| 598 code_region_table_->AddTick(sample->At(0), true, false); | |
| 599 // Give all frames an inclusive tick and tick the address. | |
| 600 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | |
| 601 if (sample->At(i) == 0) { | |
| 602 break; | |
| 603 } | |
| 604 frames_++; | |
| 605 code_region_table_->AddTick(sample->At(i), false, true); | |
| 606 } | |
| 607 } | |
| 608 | |
| 609 intptr_t frames() const { return frames_; } | |
| 610 private: | |
| 611 intptr_t frames_; | |
| 612 ProfilerCodeRegionTable* code_region_table_; | |
| 613 }; | |
| 614 | |
| 615 | |
| 502 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, | 616 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, |
| 503 bool full) { | 617 bool full) { |
| 504 ASSERT(isolate == Isolate::Current()); | 618 ASSERT(isolate == Isolate::Current()); |
| 505 // Disable profile interrupts while processing the buffer. | 619 // Disable profile interrupts while processing the buffer. |
| 506 EndExecution(isolate); | 620 EndExecution(isolate); |
| 507 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 621 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 508 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 622 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 509 if (profiler_data == NULL) { | 623 if (profiler_data == NULL) { |
| 510 JSONObject error(stream); | 624 JSONObject error(stream); |
| 511 error.AddProperty("type", "Error"); | 625 error.AddProperty("type", "Error"); |
| 512 error.AddProperty("text", "Isolate does not have profiling enabled."); | 626 error.AddProperty("text", "Isolate does not have profiling enabled."); |
| 513 return; | 627 return; |
| 514 } | 628 } |
| 515 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 629 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 516 ASSERT(sample_buffer != NULL); | 630 ASSERT(sample_buffer != NULL); |
| 517 { | 631 { |
| 518 StackZone zone(isolate); | 632 StackZone zone(isolate); |
| 519 { | 633 { |
| 520 // Build code region table. | 634 // Build code region table. |
| 521 ProfilerCodeRegionTable code_region_table(isolate); | 635 ProfilerCodeRegionTable code_region_table(isolate); |
| 522 intptr_t samples = | 636 CodeRegionTableBuilder builder(isolate, &code_region_table); |
| 523 ProcessSamples(isolate, &code_region_table, sample_buffer); | |
| 524 { | 637 { |
| 638 ScopeStopwatch sw("CodeTableBuild"); | |
| 639 sample_buffer->VisitSamples(&builder); | |
| 640 } | |
| 641 #if defined(DEBUG) | |
| 642 code_region_table.Verify(); | |
| 643 #endif | |
| 644 // Number of samples we processed. | |
| 645 intptr_t samples = builder.visited(); | |
| 646 intptr_t frames = builder.frames(); | |
| 647 if (FLAG_trace_profiled_isolates) { | |
| 648 OS::Print("%" Pd " frames produced %" Pd " code objects.\n", | |
| 649 frames, code_region_table.Length()); | |
| 650 } | |
| 651 { | |
| 652 ScopeStopwatch sw("CodeTableStream"); | |
| 525 // Serialize to JSON. | 653 // Serialize to JSON. |
| 526 JSONObject obj(stream); | 654 JSONObject obj(stream); |
| 527 obj.AddProperty("type", "Profile"); | 655 obj.AddProperty("type", "Profile"); |
| 528 obj.AddProperty("samples", samples); | 656 obj.AddProperty("samples", samples); |
| 529 JSONArray codes(&obj, "codes"); | 657 JSONArray codes(&obj, "codes"); |
| 530 for (intptr_t i = 0; i < code_region_table.Length(); i++) { | 658 for (intptr_t i = 0; i < code_region_table.Length(); i++) { |
| 531 CodeRegion* region = code_region_table.At(i); | 659 CodeRegion* region = code_region_table.At(i); |
| 532 ASSERT(region != NULL); | 660 ASSERT(region != NULL); |
| 533 region->PrintToJSONArray(&codes, full); | 661 region->PrintToJSONArray(&codes, false); |
| 534 } | 662 } |
| 535 } | 663 } |
| 536 } | 664 } |
| 537 } | 665 } |
| 538 // Enable profile interrupts. | 666 // Enable profile interrupts. |
| 539 BeginExecution(isolate); | 667 BeginExecution(isolate); |
| 540 } | 668 } |
| 541 | 669 |
| 542 | 670 |
| 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) { | 671 void Profiler::WriteProfile(Isolate* isolate) { |
| 596 if (isolate == NULL) { | 672 if (isolate == NULL) { |
| 597 return; | 673 return; |
| 598 } | 674 } |
| 599 if (!FLAG_profile) { | 675 if (!FLAG_profile) { |
| 600 return; | 676 return; |
| 601 } | 677 } |
| 602 ASSERT(initialized_); | 678 ASSERT(initialized_); |
| 603 if (FLAG_profile_dir == NULL) { | 679 if (FLAG_profile_dir == NULL) { |
| 604 return; | 680 return; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 652 | 728 |
| 653 intptr_t Sample::instance_size_ = 0; | 729 intptr_t Sample::instance_size_ = 0; |
| 654 | 730 |
| 655 void Sample::InitOnce() { | 731 void Sample::InitOnce() { |
| 656 ASSERT(FLAG_profile_depth >= 1); | 732 ASSERT(FLAG_profile_depth >= 1); |
| 657 instance_size_ = | 733 instance_size_ = |
| 658 sizeof(Sample) + (sizeof(intptr_t) * FLAG_profile_depth); // NOLINT. | 734 sizeof(Sample) + (sizeof(intptr_t) * FLAG_profile_depth); // NOLINT. |
| 659 } | 735 } |
| 660 | 736 |
| 661 | 737 |
| 662 uintptr_t Sample::At(intptr_t i) const { | 738 uword Sample::At(intptr_t i) const { |
| 663 ASSERT(i >= 0); | 739 ASSERT(i >= 0); |
| 664 ASSERT(i < FLAG_profile_depth); | 740 ASSERT(i < FLAG_profile_depth); |
| 665 return pcs_[i]; | 741 return pcs_[i]; |
| 666 } | 742 } |
| 667 | 743 |
| 668 | 744 |
| 669 void Sample::SetAt(intptr_t i, uintptr_t pc) { | 745 void Sample::SetAt(intptr_t i, uword pc) { |
| 670 ASSERT(i >= 0); | 746 ASSERT(i >= 0); |
| 671 ASSERT(i < FLAG_profile_depth); | 747 ASSERT(i < FLAG_profile_depth); |
| 672 pcs_[i] = pc; | 748 pcs_[i] = pc; |
| 673 } | 749 } |
| 674 | 750 |
| 675 | 751 |
| 676 void Sample::Init(SampleType type, Isolate* isolate, int64_t timestamp, | 752 void Sample::Init(SampleType type, Isolate* isolate, int64_t timestamp, |
| 677 ThreadId tid) { | 753 ThreadId tid) { |
| 678 timestamp_ = timestamp; | 754 timestamp_ = timestamp; |
| 679 tid_ = tid; | 755 tid_ = tid; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 736 | 812 |
| 737 Sample* SampleBuffer::At(intptr_t idx) const { | 813 Sample* SampleBuffer::At(intptr_t idx) const { |
| 738 ASSERT(idx >= 0); | 814 ASSERT(idx >= 0); |
| 739 ASSERT(idx < capacity_); | 815 ASSERT(idx < capacity_); |
| 740 intptr_t offset = idx * Sample::instance_size(); | 816 intptr_t offset = idx * Sample::instance_size(); |
| 741 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_); | 817 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_); |
| 742 return reinterpret_cast<Sample*>(samples + offset); | 818 return reinterpret_cast<Sample*>(samples + offset); |
| 743 } | 819 } |
| 744 | 820 |
| 745 | 821 |
| 746 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, | 822 void SampleBuffer::VisitSamples(SampleVisitor* visitor) { |
| 747 uintptr_t stack_lower, | 823 ASSERT(visitor != NULL); |
| 748 uintptr_t stack_upper, | 824 Sample* sample = Sample::Allocate(); |
| 749 uintptr_t pc, | 825 const intptr_t length = capacity(); |
| 750 uintptr_t fp, | 826 for (intptr_t i = 0; i < length; i++) { |
| 751 uintptr_t sp) : | 827 CopySample(i, sample); |
| 752 sample_(sample), | 828 if (sample->isolate() != visitor->isolate()) { |
| 753 stack_lower_(stack_lower), | 829 // Another isolate. |
| 754 stack_upper_(stack_upper), | 830 continue; |
| 755 original_pc_(pc), | 831 } |
| 756 original_fp_(fp), | 832 if (sample->timestamp() == 0) { |
| 757 original_sp_(sp), | 833 // Empty. |
| 758 lower_bound_(stack_lower) { | 834 continue; |
| 759 ASSERT(sample_ != NULL); | 835 } |
| 836 if (sample->At(0) == 0) { | |
| 837 // No frames. | |
| 838 continue; | |
| 839 } | |
| 840 visitor->IncrementVisited(); | |
| 841 visitor->VisitSample(sample); | |
| 842 } | |
| 843 free(sample); | |
| 760 } | 844 } |
| 761 | 845 |
| 762 | 846 |
| 763 // Notes on stack frame walking: | 847 // Notes on stack frame walking: |
| 764 // | 848 // |
| 765 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames | 849 // 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. | 850 // 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 | 851 // 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 | 852 // recent GCC versions with optimizing enabled) the stack walking code may |
| 769 // fail (sometimes leading to a crash). | 853 // fail (sometimes leading to a crash). |
| 770 // | 854 // |
| 855 class ProfilerSampleStackWalker : public ValueObject { | |
| 856 public: | |
| 857 ProfilerSampleStackWalker(Sample* sample, | |
| 858 uword stack_lower, | |
| 859 uword stack_upper, | |
| 860 uword pc, | |
| 861 uword fp, | |
| 862 uword sp) | |
| 863 : sample_(sample), | |
| 864 stack_lower_(stack_lower), | |
| 865 stack_upper_(stack_upper), | |
| 866 original_pc_(pc), | |
| 867 original_fp_(fp), | |
| 868 original_sp_(sp), | |
| 869 lower_bound_(stack_lower) { | |
| 870 ASSERT(sample_ != NULL); | |
| 871 } | |
| 771 | 872 |
| 772 int ProfilerSampleStackWalker::walk() { | 873 int walk() { |
| 773 const intptr_t kMaxStep = 0x1000; // 4K. | 874 const intptr_t kMaxStep = 0x1000; // 4K. |
| 774 const bool kWalkStack = true; // Walk the stack. | 875 const bool kWalkStack = true; // Walk the stack. |
| 775 // Always store the exclusive PC. | 876 // Always store the exclusive PC. |
| 776 sample_->SetAt(0, original_pc_); | 877 sample_->SetAt(0, original_pc_); |
| 777 if (!kWalkStack) { | 878 if (!kWalkStack) { |
| 778 // Not walking the stack, only took exclusive sample. | 879 // Not walking the stack, only took exclusive sample. |
| 779 return 1; | 880 return 1; |
| 881 } | |
| 882 uword* pc = reinterpret_cast<uword*>(original_pc_); | |
| 883 uword* fp = reinterpret_cast<uword*>(original_fp_); | |
| 884 uword* previous_fp = fp; | |
| 885 if (original_sp_ > original_fp_) { | |
| 886 // Stack pointer should not be above frame pointer. | |
| 887 return 1; | |
| 888 } | |
| 889 intptr_t gap = original_fp_ - original_sp_; | |
| 890 if (gap >= kMaxStep) { | |
| 891 // Gap between frame pointer and stack pointer is | |
| 892 // too large. | |
| 893 return 1; | |
| 894 } | |
| 895 if (original_sp_ < lower_bound_) { | |
| 896 // The stack pointer gives us a better lower bound than | |
| 897 // the isolates stack limit. | |
| 898 lower_bound_ = original_sp_; | |
| 899 } | |
| 900 int i = 0; | |
| 901 for (; i < FLAG_profile_depth; i++) { | |
| 902 sample_->SetAt(i, reinterpret_cast<uword>(pc)); | |
| 903 if (!ValidFramePointer(fp)) { | |
| 904 return i + 1; | |
| 905 } | |
| 906 pc = CallerPC(fp); | |
| 907 previous_fp = fp; | |
| 908 fp = CallerFP(fp); | |
| 909 intptr_t step = fp - previous_fp; | |
| 910 if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) { | |
| 911 // Frame pointer step is too large. | |
| 912 // Frame pointer did not move to a higher address. | |
| 913 // Frame pointer is outside of isolate stack bounds. | |
| 914 return i + 1; | |
| 915 } | |
| 916 // Move the lower bound up. | |
| 917 lower_bound_ = reinterpret_cast<uword>(fp); | |
| 918 } | |
| 919 return i; | |
| 780 } | 920 } |
| 781 uword* pc = reinterpret_cast<uword*>(original_pc_); | 921 |
| 782 uword* fp = reinterpret_cast<uword*>(original_fp_); | 922 private: |
| 783 uword* previous_fp = fp; | 923 uword* CallerPC(uword* fp) const { |
| 784 if (original_sp_ > original_fp_) { | 924 ASSERT(fp != NULL); |
| 785 // Stack pointer should not be above frame pointer. | 925 return reinterpret_cast<uword*>(*(fp + kSavedCallerPcSlotFromFp)); |
| 786 return 1; | |
| 787 } | 926 } |
| 788 intptr_t gap = original_fp_ - original_sp_; | 927 |
| 789 if (gap >= kMaxStep) { | 928 uword* CallerFP(uword* fp) const { |
| 790 // Gap between frame pointer and stack pointer is | 929 ASSERT(fp != NULL); |
| 791 // too large. | 930 return reinterpret_cast<uword*>(*(fp + kSavedCallerFpSlotFromFp)); |
| 792 return 1; | |
| 793 } | 931 } |
| 794 if (original_sp_ < lower_bound_) { | 932 |
| 795 // The stack pointer gives us a better lower bound than | 933 bool ValidFramePointer(uword* fp) const { |
| 796 // the isolates stack limit. | 934 if (fp == NULL) { |
| 797 lower_bound_ = original_sp_; | 935 return false; |
| 936 } | |
| 937 uword cursor = reinterpret_cast<uword>(fp); | |
| 938 cursor += sizeof(fp); | |
| 939 bool r = cursor >= lower_bound_ && cursor < stack_upper_; | |
| 940 return r; | |
| 798 } | 941 } |
| 799 int i = 0; | 942 |
| 800 for (; i < FLAG_profile_depth; i++) { | 943 Sample* sample_; |
| 801 sample_->SetAt(i, reinterpret_cast<uintptr_t>(pc)); | 944 const uword stack_lower_; |
| 802 if (!ValidFramePointer(fp)) { | 945 const uword stack_upper_; |
| 803 return i + 1; | 946 const uword original_pc_; |
| 804 } | 947 const uword original_fp_; |
| 805 pc = CallerPC(fp); | 948 const uword original_sp_; |
| 806 previous_fp = fp; | 949 uword lower_bound_; |
| 807 fp = CallerFP(fp); | 950 }; |
| 808 intptr_t step = fp - previous_fp; | 951 |
| 809 if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) { | 952 void Profiler::RecordSampleInterruptCallback( |
| 810 // Frame pointer step is too large. | 953 const InterruptedThreadState& state, |
| 811 // Frame pointer did not move to a higher address. | 954 void* data) { |
| 812 // Frame pointer is outside of isolate stack bounds. | 955 Isolate* isolate = reinterpret_cast<Isolate*>(data); |
| 813 return i + 1; | 956 if (isolate == NULL) { |
| 814 } | 957 return; |
| 815 // Move the lower bound up. | |
| 816 lower_bound_ = reinterpret_cast<uintptr_t>(fp); | |
| 817 } | 958 } |
| 818 return i; | 959 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 960 if (profiler_data == NULL) { | |
| 961 return; | |
| 962 } | |
| 963 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
| 964 if (sample_buffer == NULL) { | |
| 965 return; | |
| 966 } | |
| 967 Sample* sample = sample_buffer->ReserveSample(); | |
| 968 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(), | |
| 969 state.tid); | |
| 970 uword stack_lower = 0; | |
| 971 uword stack_upper = 0; | |
| 972 isolate->GetStackBounds(&stack_lower, &stack_upper); | |
| 973 if ((stack_lower == 0) || (stack_upper == 0)) { | |
| 974 stack_lower = 0; | |
| 975 stack_upper = 0; | |
| 976 } | |
| 977 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, | |
| 978 state.pc, state.fp, state.sp); | |
| 979 stackWalker.walk(); | |
| 819 } | 980 } |
| 820 | 981 |
| 821 | 982 |
| 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 | 983 } // namespace dart |
| OLD | NEW |