| 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/address_sanitizer.h" | 5 #include "platform/address_sanitizer.h" |
| 6 #include "platform/memory_sanitizer.h" | 6 #include "platform/memory_sanitizer.h" |
| 7 #include "platform/utils.h" | 7 #include "platform/utils.h" |
| 8 | 8 |
| 9 #include "vm/allocation.h" | 9 #include "vm/allocation.h" |
| 10 #include "vm/atomic.h" | 10 #include "vm/atomic.h" |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 return; | 166 return; |
| 167 } | 167 } |
| 168 if (!FLAG_profile) { | 168 if (!FLAG_profile) { |
| 169 return; | 169 return; |
| 170 } | 170 } |
| 171 ASSERT(initialized_); | 171 ASSERT(initialized_); |
| 172 ThreadInterrupter::Unregister(); | 172 ThreadInterrupter::Unregister(); |
| 173 } | 173 } |
| 174 | 174 |
| 175 | 175 |
| 176 class ScopeStopwatch : public ValueObject { | |
| 177 public: | |
| 178 explicit ScopeStopwatch(const char* name) : name_(name) { | |
| 179 start_ = FLAG_trace_profiler ? OS::GetCurrentTimeMillis() : 0; | |
| 180 } | |
| 181 | |
| 182 int64_t GetElapsed() const { | |
| 183 int64_t end = OS::GetCurrentTimeMillis(); | |
| 184 ASSERT(end >= start_); | |
| 185 return end - start_; | |
| 186 } | |
| 187 | |
| 188 ~ScopeStopwatch() { | |
| 189 if (FLAG_trace_profiler) { | |
| 190 int64_t elapsed = GetElapsed(); | |
| 191 OS::Print("%s took %" Pd64 " millis.\n", name_, elapsed); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 private: | |
| 196 const char* name_; | |
| 197 int64_t start_; | |
| 198 }; | |
| 199 | |
| 200 | |
| 201 struct AddressEntry { | |
| 202 uword pc; | |
| 203 intptr_t exclusive_ticks; | |
| 204 intptr_t inclusive_ticks; | |
| 205 | |
| 206 void tick(bool exclusive) { | |
| 207 if (exclusive) { | |
| 208 exclusive_ticks++; | |
| 209 } else { | |
| 210 inclusive_ticks++; | |
| 211 } | |
| 212 } | |
| 213 }; | |
| 214 | |
| 215 | |
| 216 struct CallEntry { | |
| 217 intptr_t code_table_index; | |
| 218 intptr_t count; | |
| 219 }; | |
| 220 | |
| 221 | |
| 222 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); | |
| 223 | |
| 224 | |
| 225 class CodeRegionTrieNode : public ZoneAllocated { | |
| 226 public: | |
| 227 explicit CodeRegionTrieNode(intptr_t code_region_index) | |
| 228 : code_region_index_(code_region_index), | |
| 229 count_(0), | |
| 230 children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { | |
| 231 } | |
| 232 | |
| 233 void Tick() { | |
| 234 ASSERT(code_region_index_ >= 0); | |
| 235 count_++; | |
| 236 } | |
| 237 | |
| 238 intptr_t count() const { | |
| 239 ASSERT(code_region_index_ >= 0); | |
| 240 return count_; | |
| 241 } | |
| 242 | |
| 243 intptr_t code_region_index() const { | |
| 244 return code_region_index_; | |
| 245 } | |
| 246 | |
| 247 ZoneGrowableArray<CodeRegionTrieNode*>& children() const { | |
| 248 return *children_; | |
| 249 } | |
| 250 | |
| 251 CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { | |
| 252 const intptr_t length = children_->length(); | |
| 253 intptr_t i = 0; | |
| 254 while (i < length) { | |
| 255 CodeRegionTrieNode* child = (*children_)[i]; | |
| 256 if (child->code_region_index() == child_code_region_index) { | |
| 257 return child; | |
| 258 } | |
| 259 if (child->code_region_index() > child_code_region_index) { | |
| 260 break; | |
| 261 } | |
| 262 i++; | |
| 263 } | |
| 264 // Add new CodeRegion, sorted by CodeRegionTable index. | |
| 265 CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index); | |
| 266 if (i < length) { | |
| 267 // Insert at i. | |
| 268 children_->InsertAt(i, child); | |
| 269 } else { | |
| 270 // Add to end. | |
| 271 children_->Add(child); | |
| 272 } | |
| 273 return child; | |
| 274 } | |
| 275 | |
| 276 // Sort this's children and (recursively) all descendants by count. | |
| 277 // This should only be called after the trie is completely built. | |
| 278 void SortByCount() { | |
| 279 children_->Sort(CodeRegionTrieNodeCompare); | |
| 280 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
| 281 intptr_t child_count = kids.length(); | |
| 282 // Recurse. | |
| 283 for (intptr_t i = 0; i < child_count; i++) { | |
| 284 kids[i]->SortByCount(); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 void PrintToJSONArray(JSONArray* array) const { | |
| 289 ASSERT(array != NULL); | |
| 290 // Write CodeRegion index. | |
| 291 array->AddValue(code_region_index_); | |
| 292 // Write count. | |
| 293 array->AddValue(count_); | |
| 294 // Write number of children. | |
| 295 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
| 296 intptr_t child_count = kids.length(); | |
| 297 array->AddValue(child_count); | |
| 298 // Recurse. | |
| 299 for (intptr_t i = 0; i < child_count; i++) { | |
| 300 kids[i]->PrintToJSONArray(array); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 private: | |
| 305 static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, | |
| 306 CodeRegionTrieNode* const* b) { | |
| 307 ASSERT(a != NULL); | |
| 308 ASSERT(b != NULL); | |
| 309 return (*b)->count() - (*a)->count(); | |
| 310 } | |
| 311 | |
| 312 const intptr_t code_region_index_; | |
| 313 intptr_t count_; | |
| 314 ZoneGrowableArray<CodeRegionTrieNode*>* children_; | |
| 315 }; | |
| 316 | |
| 317 | |
| 318 // A contiguous address region that holds code. Each CodeRegion has a "kind" | |
| 319 // which describes the type of code contained inside the region. Each | |
| 320 // region covers the following interval: [start, end). | |
| 321 class CodeRegion : public ZoneAllocated { | |
| 322 public: | |
| 323 enum Kind { | |
| 324 kDartCode, // Live Dart code. | |
| 325 kCollectedCode, // Dead Dart code. | |
| 326 kNativeCode, // Native code. | |
| 327 kReusedCode, // Dead Dart code that has been reused by new kDartCode. | |
| 328 kTagCode, // A special kind of code representing a tag. | |
| 329 }; | |
| 330 | |
| 331 CodeRegion(Kind kind, uword start, uword end, int64_t timestamp) | |
| 332 : kind_(kind), | |
| 333 start_(start), | |
| 334 end_(end), | |
| 335 inclusive_ticks_(0), | |
| 336 exclusive_ticks_(0), | |
| 337 inclusive_tick_serial_(0), | |
| 338 name_(NULL), | |
| 339 compile_timestamp_(timestamp), | |
| 340 creation_serial_(0), | |
| 341 address_table_(new ZoneGrowableArray<AddressEntry>()), | |
| 342 callers_table_(new ZoneGrowableArray<CallEntry>()), | |
| 343 callees_table_(new ZoneGrowableArray<CallEntry>()) { | |
| 344 ASSERT(start_ < end_); | |
| 345 } | |
| 346 | |
| 347 | |
| 348 uword start() const { return start_; } | |
| 349 void set_start(uword start) { | |
| 350 start_ = start; | |
| 351 } | |
| 352 | |
| 353 uword end() const { return end_; } | |
| 354 void set_end(uword end) { | |
| 355 end_ = end; | |
| 356 } | |
| 357 | |
| 358 void AdjustExtent(uword start, uword end) { | |
| 359 if (start < start_) { | |
| 360 start_ = start; | |
| 361 } | |
| 362 if (end > end_) { | |
| 363 end_ = end; | |
| 364 } | |
| 365 ASSERT(start_ < end_); | |
| 366 } | |
| 367 | |
| 368 bool contains(uword pc) const { | |
| 369 return (pc >= start_) && (pc < end_); | |
| 370 } | |
| 371 | |
| 372 bool overlaps(const CodeRegion* other) const { | |
| 373 ASSERT(other != NULL); | |
| 374 return other->contains(start_) || | |
| 375 other->contains(end_ - 1) || | |
| 376 contains(other->start()) || | |
| 377 contains(other->end() - 1); | |
| 378 } | |
| 379 | |
| 380 intptr_t creation_serial() const { return creation_serial_; } | |
| 381 void set_creation_serial(intptr_t serial) { | |
| 382 creation_serial_ = serial; | |
| 383 } | |
| 384 int64_t compile_timestamp() const { return compile_timestamp_; } | |
| 385 void set_compile_timestamp(int64_t timestamp) { | |
| 386 compile_timestamp_ = timestamp; | |
| 387 } | |
| 388 | |
| 389 intptr_t inclusive_ticks() const { return inclusive_ticks_; } | |
| 390 void set_inclusive_ticks(intptr_t inclusive_ticks) { | |
| 391 inclusive_ticks_ = inclusive_ticks; | |
| 392 } | |
| 393 | |
| 394 intptr_t exclusive_ticks() const { return exclusive_ticks_; } | |
| 395 void set_exclusive_ticks(intptr_t exclusive_ticks) { | |
| 396 exclusive_ticks_ = exclusive_ticks; | |
| 397 } | |
| 398 | |
| 399 const char* name() const { return name_; } | |
| 400 void SetName(const char* name) { | |
| 401 if (name == NULL) { | |
| 402 name_ = NULL; | |
| 403 } | |
| 404 intptr_t len = strlen(name); | |
| 405 name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); | |
| 406 strncpy(const_cast<char*>(name_), name, len); | |
| 407 const_cast<char*>(name_)[len] = '\0'; | |
| 408 } | |
| 409 | |
| 410 Kind kind() const { return kind_; } | |
| 411 | |
| 412 static const char* KindToCString(Kind kind) { | |
| 413 switch (kind) { | |
| 414 case kDartCode: | |
| 415 return "Dart"; | |
| 416 case kCollectedCode: | |
| 417 return "Collected"; | |
| 418 case kNativeCode: | |
| 419 return "Native"; | |
| 420 case kReusedCode: | |
| 421 return "Overwritten"; | |
| 422 case kTagCode: | |
| 423 return "Tag"; | |
| 424 } | |
| 425 UNREACHABLE(); | |
| 426 return NULL; | |
| 427 } | |
| 428 | |
| 429 void DebugPrint() const { | |
| 430 OS::Print("%s [%" Px ", %" Px ") %" Pd " %" Pd64 "\n", | |
| 431 KindToCString(kind_), | |
| 432 start(), | |
| 433 end(), | |
| 434 creation_serial_, | |
| 435 compile_timestamp_); | |
| 436 } | |
| 437 | |
| 438 void Tick(uword pc, bool exclusive, intptr_t serial) { | |
| 439 // Assert that exclusive ticks are never passed a valid serial number. | |
| 440 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); | |
| 441 if (!exclusive && (inclusive_tick_serial_ == serial)) { | |
| 442 // We've already given this code object an inclusive tick for this sample. | |
| 443 return; | |
| 444 } | |
| 445 // Tick the code object. | |
| 446 if (exclusive) { | |
| 447 exclusive_ticks_++; | |
| 448 } else { | |
| 449 inclusive_ticks_++; | |
| 450 // Mark the last serial we ticked the inclusive count. | |
| 451 inclusive_tick_serial_ = serial; | |
| 452 } | |
| 453 TickAddress(pc, exclusive); | |
| 454 } | |
| 455 | |
| 456 void AddCaller(intptr_t index, intptr_t count) { | |
| 457 AddCallEntry(callers_table_, index, count); | |
| 458 } | |
| 459 | |
| 460 void AddCallee(intptr_t index, intptr_t count) { | |
| 461 AddCallEntry(callees_table_, index, count); | |
| 462 } | |
| 463 | |
| 464 void PrintNativeCode(JSONObject* profile_code_obj) { | |
| 465 ASSERT(kind() == kNativeCode); | |
| 466 JSONObject obj(profile_code_obj, "code"); | |
| 467 obj.AddProperty("type", "@Code"); | |
| 468 obj.AddProperty("kind", "Native"); | |
| 469 obj.AddProperty("name", name()); | |
| 470 obj.AddPropertyF("start", "%" Px "", start()); | |
| 471 obj.AddPropertyF("end", "%" Px "", end()); | |
| 472 obj.AddPropertyF("id", "code/native-%" Px "", start()); | |
| 473 { | |
| 474 // Generate a fake function entry. | |
| 475 JSONObject func(&obj, "function"); | |
| 476 func.AddProperty("type", "@Function"); | |
| 477 func.AddPropertyF("id", "functions/native-%" Px "", start()); | |
| 478 func.AddProperty("name", name()); | |
| 479 func.AddProperty("kind", "Native"); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 void PrintCollectedCode(JSONObject* profile_code_obj) { | |
| 484 ASSERT(kind() == kCollectedCode); | |
| 485 JSONObject obj(profile_code_obj, "code"); | |
| 486 obj.AddProperty("type", "@Code"); | |
| 487 obj.AddProperty("kind", "Collected"); | |
| 488 obj.AddProperty("name", name()); | |
| 489 obj.AddPropertyF("start", "%" Px "", start()); | |
| 490 obj.AddPropertyF("end", "%" Px "", end()); | |
| 491 obj.AddPropertyF("id", "code/collected-%" Px "", start()); | |
| 492 { | |
| 493 // Generate a fake function entry. | |
| 494 JSONObject func(&obj, "function"); | |
| 495 func.AddProperty("type", "@Function"); | |
| 496 obj.AddPropertyF("id", "functions/collected-%" Px "", start()); | |
| 497 func.AddProperty("name", name()); | |
| 498 func.AddProperty("kind", "Collected"); | |
| 499 } | |
| 500 } | |
| 501 | |
| 502 void PrintOverwrittenCode(JSONObject* profile_code_obj) { | |
| 503 ASSERT(kind() == kReusedCode); | |
| 504 JSONObject obj(profile_code_obj, "code"); | |
| 505 obj.AddProperty("type", "@Code"); | |
| 506 obj.AddProperty("kind", "Reused"); | |
| 507 obj.AddProperty("name", name()); | |
| 508 obj.AddPropertyF("start", "%" Px "", start()); | |
| 509 obj.AddPropertyF("end", "%" Px "", end()); | |
| 510 obj.AddPropertyF("id", "code/reused-%" Px "", start()); | |
| 511 { | |
| 512 // Generate a fake function entry. | |
| 513 JSONObject func(&obj, "function"); | |
| 514 func.AddProperty("type", "@Function"); | |
| 515 obj.AddPropertyF("id", "functions/reused-%" Px "", start()); | |
| 516 func.AddProperty("name", name()); | |
| 517 func.AddProperty("kind", "Reused"); | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 void PrintTagCode(JSONObject* profile_code_obj) { | |
| 522 ASSERT(kind() == kTagCode); | |
| 523 JSONObject obj(profile_code_obj, "code"); | |
| 524 obj.AddProperty("type", "@Code"); | |
| 525 obj.AddProperty("kind", "Tag"); | |
| 526 obj.AddPropertyF("id", "code/tag-%" Px "", start()); | |
| 527 obj.AddProperty("name", name()); | |
| 528 obj.AddPropertyF("start", "%" Px "", start()); | |
| 529 obj.AddPropertyF("end", "%" Px "", end()); | |
| 530 { | |
| 531 // Generate a fake function entry. | |
| 532 JSONObject func(&obj, "function"); | |
| 533 func.AddProperty("type", "@Function"); | |
| 534 func.AddProperty("kind", "Tag"); | |
| 535 obj.AddPropertyF("id", "functions/tag-%" Px "", start()); | |
| 536 func.AddProperty("name", name()); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) { | |
| 541 JSONObject obj(events); | |
| 542 obj.AddProperty("kind", KindToCString(kind())); | |
| 543 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); | |
| 544 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); | |
| 545 if (kind() == kDartCode) { | |
| 546 // Look up code in Dart heap. | |
| 547 Code& code = Code::Handle(isolate); | |
| 548 code ^= Code::LookupCode(start()); | |
| 549 if (code.IsNull()) { | |
| 550 // Code is a stub in the Vm isolate. | |
| 551 code ^= Code::LookupCodeInVmIsolate(start()); | |
| 552 } | |
| 553 ASSERT(!code.IsNull()); | |
| 554 obj.AddProperty("code", code, !full); | |
| 555 } else if (kind() == kCollectedCode) { | |
| 556 if (name() == NULL) { | |
| 557 // Lazily set generated name. | |
| 558 GenerateAndSetSymbolName("[Collected]"); | |
| 559 } | |
| 560 PrintCollectedCode(&obj); | |
| 561 } else if (kind() == kReusedCode) { | |
| 562 if (name() == NULL) { | |
| 563 // Lazily set generated name. | |
| 564 GenerateAndSetSymbolName("[Reused]"); | |
| 565 } | |
| 566 PrintOverwrittenCode(&obj); | |
| 567 } else if (kind() == kTagCode) { | |
| 568 if (name() == NULL) { | |
| 569 if (UserTags::IsUserTag(start())) { | |
| 570 const char* tag_name = UserTags::TagName(start()); | |
| 571 ASSERT(tag_name != NULL); | |
| 572 SetName(tag_name); | |
| 573 } else if (VMTag::IsVMTag(start()) || | |
| 574 VMTag::IsRuntimeEntryTag(start()) || | |
| 575 VMTag::IsNativeEntryTag(start())) { | |
| 576 const char* tag_name = VMTag::TagName(start()); | |
| 577 ASSERT(tag_name != NULL); | |
| 578 SetName(tag_name); | |
| 579 } else { | |
| 580 ASSERT(start() == 0); | |
| 581 SetName("root"); | |
| 582 } | |
| 583 } | |
| 584 PrintTagCode(&obj); | |
| 585 } else { | |
| 586 ASSERT(kind() == kNativeCode); | |
| 587 if (name() == NULL) { | |
| 588 // Lazily set generated name. | |
| 589 GenerateAndSetSymbolName("[Native]"); | |
| 590 } | |
| 591 PrintNativeCode(&obj); | |
| 592 } | |
| 593 { | |
| 594 JSONArray ticks(&obj, "ticks"); | |
| 595 for (intptr_t i = 0; i < address_table_->length(); i++) { | |
| 596 const AddressEntry& entry = (*address_table_)[i]; | |
| 597 ticks.AddValueF("%" Px "", entry.pc); | |
| 598 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); | |
| 599 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); | |
| 600 } | |
| 601 } | |
| 602 { | |
| 603 JSONArray callers(&obj, "callers"); | |
| 604 for (intptr_t i = 0; i < callers_table_->length(); i++) { | |
| 605 const CallEntry& entry = (*callers_table_)[i]; | |
| 606 callers.AddValueF("%" Pd "", entry.code_table_index); | |
| 607 callers.AddValueF("%" Pd "", entry.count); | |
| 608 } | |
| 609 } | |
| 610 { | |
| 611 JSONArray callees(&obj, "callees"); | |
| 612 for (intptr_t i = 0; i < callees_table_->length(); i++) { | |
| 613 const CallEntry& entry = (*callees_table_)[i]; | |
| 614 callees.AddValueF("%" Pd "", entry.code_table_index); | |
| 615 callees.AddValueF("%" Pd "", entry.count); | |
| 616 } | |
| 617 } | |
| 618 } | |
| 619 | |
| 620 private: | |
| 621 void TickAddress(uword pc, bool exclusive) { | |
| 622 const intptr_t length = address_table_->length(); | |
| 623 intptr_t i = 0; | |
| 624 for (; i < length; i++) { | |
| 625 AddressEntry& entry = (*address_table_)[i]; | |
| 626 if (entry.pc == pc) { | |
| 627 // Tick the address entry. | |
| 628 entry.tick(exclusive); | |
| 629 return; | |
| 630 } | |
| 631 if (entry.pc > pc) { | |
| 632 break; | |
| 633 } | |
| 634 } | |
| 635 // New address, add entry. | |
| 636 AddressEntry entry; | |
| 637 entry.pc = pc; | |
| 638 entry.exclusive_ticks = 0; | |
| 639 entry.inclusive_ticks = 0; | |
| 640 entry.tick(exclusive); | |
| 641 if (i < length) { | |
| 642 // Insert at i. | |
| 643 address_table_->InsertAt(i, entry); | |
| 644 } else { | |
| 645 // Add to end. | |
| 646 address_table_->Add(entry); | |
| 647 } | |
| 648 } | |
| 649 | |
| 650 | |
| 651 void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index, | |
| 652 intptr_t count) { | |
| 653 const intptr_t length = table->length(); | |
| 654 intptr_t i = 0; | |
| 655 for (; i < length; i++) { | |
| 656 CallEntry& entry = (*table)[i]; | |
| 657 if (entry.code_table_index == index) { | |
| 658 entry.count += count; | |
| 659 return; | |
| 660 } | |
| 661 if (entry.code_table_index > index) { | |
| 662 break; | |
| 663 } | |
| 664 } | |
| 665 CallEntry entry; | |
| 666 entry.code_table_index = index; | |
| 667 entry.count = count; | |
| 668 if (i < length) { | |
| 669 table->InsertAt(i, entry); | |
| 670 } else { | |
| 671 table->Add(entry); | |
| 672 } | |
| 673 } | |
| 674 | |
| 675 void GenerateAndSetSymbolName(const char* prefix) { | |
| 676 const intptr_t kBuffSize = 512; | |
| 677 char buff[kBuffSize]; | |
| 678 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | |
| 679 prefix, start(), end()); | |
| 680 SetName(buff); | |
| 681 } | |
| 682 | |
| 683 // CodeRegion kind. | |
| 684 const Kind kind_; | |
| 685 // CodeRegion start address. | |
| 686 uword start_; | |
| 687 // CodeRegion end address. | |
| 688 uword end_; | |
| 689 // Inclusive ticks. | |
| 690 intptr_t inclusive_ticks_; | |
| 691 // Exclusive ticks. | |
| 692 intptr_t exclusive_ticks_; | |
| 693 // Inclusive tick serial number, ensures that each CodeRegion is only given | |
| 694 // a single inclusive tick per sample. | |
| 695 intptr_t inclusive_tick_serial_; | |
| 696 // Name of code region. | |
| 697 const char* name_; | |
| 698 // The compilation timestamp associated with this code region. | |
| 699 int64_t compile_timestamp_; | |
| 700 // Serial number at which this CodeRegion was created. | |
| 701 intptr_t creation_serial_; | |
| 702 ZoneGrowableArray<AddressEntry>* address_table_; | |
| 703 ZoneGrowableArray<CallEntry>* callers_table_; | |
| 704 ZoneGrowableArray<CallEntry>* callees_table_; | |
| 705 DISALLOW_COPY_AND_ASSIGN(CodeRegion); | |
| 706 }; | |
| 707 | |
| 708 | |
| 709 // A sorted table of CodeRegions. Does not allow for overlap. | |
| 710 class CodeRegionTable : public ValueObject { | |
| 711 public: | |
| 712 enum TickResult { | |
| 713 kTicked = 0, // CodeRegion found and ticked. | |
| 714 kNotFound = -1, // No CodeRegion found. | |
| 715 kNewerCode = -2, // CodeRegion found but it was compiled after sample. | |
| 716 }; | |
| 717 | |
| 718 CodeRegionTable() : | |
| 719 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { | |
| 720 } | |
| 721 | |
| 722 // Ticks the CodeRegion containing pc if it is alive at timestamp. | |
| 723 TickResult Tick(uword pc, bool exclusive, intptr_t serial, | |
| 724 int64_t timestamp) { | |
| 725 intptr_t index = FindIndex(pc); | |
| 726 if (index < 0) { | |
| 727 // Not found. | |
| 728 return kNotFound; | |
| 729 } | |
| 730 ASSERT(index < code_region_table_->length()); | |
| 731 CodeRegion* region = At(index); | |
| 732 if (region->compile_timestamp() > timestamp) { | |
| 733 // Compiled after tick. | |
| 734 return kNewerCode; | |
| 735 } | |
| 736 region->Tick(pc, exclusive, serial); | |
| 737 return kTicked; | |
| 738 } | |
| 739 | |
| 740 // Table length. | |
| 741 intptr_t Length() const { return code_region_table_->length(); } | |
| 742 | |
| 743 // Get the CodeRegion at index. | |
| 744 CodeRegion* At(intptr_t index) const { | |
| 745 return (*code_region_table_)[index]; | |
| 746 } | |
| 747 | |
| 748 // Find the table index to the CodeRegion containing pc. | |
| 749 // Returns < 0 if not found. | |
| 750 intptr_t FindIndex(uword pc) const { | |
| 751 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); | |
| 752 const CodeRegion* code_region = NULL; | |
| 753 if (index == code_region_table_->length()) { | |
| 754 // Not present. | |
| 755 return -1; | |
| 756 } | |
| 757 code_region = At(index); | |
| 758 if (code_region->contains(pc)) { | |
| 759 // Found at index. | |
| 760 return index; | |
| 761 } | |
| 762 return -2; | |
| 763 } | |
| 764 | |
| 765 // Insert code_region into the table. Returns the table index where the | |
| 766 // CodeRegion was inserted. Will merge with an overlapping CodeRegion if | |
| 767 // one is present. | |
| 768 intptr_t InsertCodeRegion(CodeRegion* code_region) { | |
| 769 const uword start = code_region->start(); | |
| 770 const uword end = code_region->end(); | |
| 771 const intptr_t length = code_region_table_->length(); | |
| 772 if (length == 0) { | |
| 773 code_region_table_->Add(code_region); | |
| 774 return length; | |
| 775 } | |
| 776 // Determine the correct place to insert or merge code_region into table. | |
| 777 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); | |
| 778 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); | |
| 779 // TODO(johnmccutchan): Simplify below logic. | |
| 780 if ((lo == length) && (hi == length)) { | |
| 781 lo = length - 1; | |
| 782 } | |
| 783 if (lo == length) { | |
| 784 CodeRegion* region = At(hi); | |
| 785 if (region->overlaps(code_region)) { | |
| 786 HandleOverlap(region, code_region, start, end); | |
| 787 return hi; | |
| 788 } | |
| 789 code_region_table_->Add(code_region); | |
| 790 return length; | |
| 791 } else if (hi == length) { | |
| 792 CodeRegion* region = At(lo); | |
| 793 if (region->overlaps(code_region)) { | |
| 794 HandleOverlap(region, code_region, start, end); | |
| 795 return lo; | |
| 796 } | |
| 797 code_region_table_->Add(code_region); | |
| 798 return length; | |
| 799 } else if (lo == hi) { | |
| 800 CodeRegion* region = At(lo); | |
| 801 if (region->overlaps(code_region)) { | |
| 802 HandleOverlap(region, code_region, start, end); | |
| 803 return lo; | |
| 804 } | |
| 805 code_region_table_->InsertAt(lo, code_region); | |
| 806 return lo; | |
| 807 } else { | |
| 808 CodeRegion* region = At(lo); | |
| 809 if (region->overlaps(code_region)) { | |
| 810 HandleOverlap(region, code_region, start, end); | |
| 811 return lo; | |
| 812 } | |
| 813 region = At(hi); | |
| 814 if (region->overlaps(code_region)) { | |
| 815 HandleOverlap(region, code_region, start, end); | |
| 816 return hi; | |
| 817 } | |
| 818 code_region_table_->InsertAt(hi, code_region); | |
| 819 return hi; | |
| 820 } | |
| 821 UNREACHABLE(); | |
| 822 } | |
| 823 | |
| 824 #if defined(DEBUG) | |
| 825 void Verify() { | |
| 826 VerifyOrder(); | |
| 827 VerifyOverlap(); | |
| 828 } | |
| 829 #endif | |
| 830 | |
| 831 void DebugPrint() { | |
| 832 OS::Print("Dumping CodeRegionTable:\n"); | |
| 833 for (intptr_t i = 0; i < code_region_table_->length(); i++) { | |
| 834 CodeRegion* region = At(i); | |
| 835 region->DebugPrint(); | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 private: | |
| 840 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { | |
| 841 ASSERT(comparator != NULL); | |
| 842 intptr_t count = code_region_table_->length(); | |
| 843 intptr_t first = 0; | |
| 844 while (count > 0) { | |
| 845 intptr_t it = first; | |
| 846 intptr_t step = count / 2; | |
| 847 it += step; | |
| 848 const CodeRegion* code_region = At(it); | |
| 849 if (comparator(pc, code_region->start(), code_region->end())) { | |
| 850 first = ++it; | |
| 851 count -= (step + 1); | |
| 852 } else { | |
| 853 count = step; | |
| 854 } | |
| 855 } | |
| 856 return first; | |
| 857 } | |
| 858 | |
| 859 static bool CompareUpperBound(uword pc, uword start, uword end) { | |
| 860 return pc >= end; | |
| 861 } | |
| 862 | |
| 863 static bool CompareLowerBound(uword pc, uword start, uword end) { | |
| 864 return end <= pc; | |
| 865 } | |
| 866 | |
| 867 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, | |
| 868 uword start, uword end) { | |
| 869 // We should never see overlapping Dart code regions. | |
| 870 ASSERT(region->kind() != CodeRegion::kDartCode); | |
| 871 // We should never see overlapping Tag code regions. | |
| 872 ASSERT(region->kind() != CodeRegion::kTagCode); | |
| 873 // When code regions overlap, they should be of the same kind. | |
| 874 ASSERT(region->kind() == code_region->kind()); | |
| 875 region->AdjustExtent(start, end); | |
| 876 } | |
| 877 | |
| 878 #if defined(DEBUG) | |
| 879 void VerifyOrder() { | |
| 880 const intptr_t length = code_region_table_->length(); | |
| 881 if (length == 0) { | |
| 882 return; | |
| 883 } | |
| 884 uword last = (*code_region_table_)[0]->end(); | |
| 885 for (intptr_t i = 1; i < length; i++) { | |
| 886 CodeRegion* a = (*code_region_table_)[i]; | |
| 887 ASSERT(last <= a->start()); | |
| 888 last = a->end(); | |
| 889 } | |
| 890 } | |
| 891 | |
| 892 void VerifyOverlap() { | |
| 893 const intptr_t length = code_region_table_->length(); | |
| 894 for (intptr_t i = 0; i < length; i++) { | |
| 895 CodeRegion* a = (*code_region_table_)[i]; | |
| 896 for (intptr_t j = i+1; j < length; j++) { | |
| 897 CodeRegion* b = (*code_region_table_)[j]; | |
| 898 ASSERT(!a->contains(b->start()) && | |
| 899 !a->contains(b->end() - 1) && | |
| 900 !b->contains(a->start()) && | |
| 901 !b->contains(a->end() - 1)); | |
| 902 } | |
| 903 } | |
| 904 } | |
| 905 #endif | |
| 906 | |
| 907 ZoneGrowableArray<CodeRegion*>* code_region_table_; | |
| 908 }; | |
| 909 | |
| 910 | |
| 911 class FixTopFrameVisitor : public SampleVisitor { | |
| 912 public: | |
| 913 explicit FixTopFrameVisitor(Isolate* isolate) | |
| 914 : SampleVisitor(isolate), | |
| 915 vm_isolate_(Dart::vm_isolate()) { | |
| 916 } | |
| 917 | |
| 918 void VisitSample(Sample* sample) { | |
| 919 if (sample->processed()) { | |
| 920 // Already processed. | |
| 921 return; | |
| 922 } | |
| 923 REUSABLE_CODE_HANDLESCOPE(isolate()); | |
| 924 // Mark that we've processed this sample. | |
| 925 sample->set_processed(true); | |
| 926 // Lookup code object for leaf frame. | |
| 927 Code& code = reused_code_handle.Handle(); | |
| 928 code = FindCodeForPC(sample->At(0)); | |
| 929 sample->set_leaf_frame_is_dart(!code.IsNull()); | |
| 930 if (sample->pc_marker() == 0) { | |
| 931 // No pc marker. Nothing to do. | |
| 932 return; | |
| 933 } | |
| 934 if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) { | |
| 935 // Code compiled after sample. Ignore. | |
| 936 return; | |
| 937 } | |
| 938 if (sample->leaf_frame_is_dart()) { | |
| 939 CheckForMissingDartFrame(code, sample); | |
| 940 } | |
| 941 } | |
| 942 | |
| 943 private: | |
| 944 void CheckForMissingDartFrame(const Code& code, Sample* sample) const { | |
| 945 // Some stubs (and intrinsics) do not push a frame onto the stack leaving | |
| 946 // the frame pointer in the caller. | |
| 947 // | |
| 948 // PC -> STUB | |
| 949 // FP -> DART3 <-+ | |
| 950 // DART2 <-| <- TOP FRAME RETURN ADDRESS. | |
| 951 // DART1 <-| | |
| 952 // ..... | |
| 953 // | |
| 954 // In this case, traversing the linked stack frames will not collect a PC | |
| 955 // inside DART3. The stack will incorrectly be: STUB, DART2, DART1. | |
| 956 // In Dart code, after pushing the FP onto the stack, an IP in the current | |
| 957 // function is pushed onto the stack as well. This stack slot is called | |
| 958 // the PC marker. We can use the PC marker to insert DART3 into the stack | |
| 959 // so that it will correctly be: STUB, DART3, DART2, DART1. Note the | |
| 960 // inserted PC may not accurately reflect the true return address from STUB. | |
| 961 ASSERT(!code.IsNull()); | |
| 962 if (sample->sp() == sample->fp()) { | |
| 963 // Haven't pushed pc marker yet. | |
| 964 return; | |
| 965 } | |
| 966 uword pc_marker = sample->pc_marker(); | |
| 967 if (code.ContainsInstructionAt(pc_marker)) { | |
| 968 // PC marker is in the same code as pc, no missing frame. | |
| 969 return; | |
| 970 } | |
| 971 if (!ContainedInDartCodeHeaps(pc_marker)) { | |
| 972 // Not a valid PC marker. | |
| 973 return; | |
| 974 } | |
| 975 sample->InsertCallerForTopFrame(pc_marker); | |
| 976 } | |
| 977 | |
| 978 bool ContainedInDartCodeHeaps(uword pc) const { | |
| 979 return isolate()->heap()->CodeContains(pc) || | |
| 980 vm_isolate()->heap()->CodeContains(pc); | |
| 981 } | |
| 982 | |
| 983 Isolate* vm_isolate() const { | |
| 984 return vm_isolate_; | |
| 985 } | |
| 986 | |
| 987 RawCode* FindCodeForPC(uword pc) const { | |
| 988 // Check current isolate for pc. | |
| 989 if (isolate()->heap()->CodeContains(pc)) { | |
| 990 return Code::LookupCode(pc); | |
| 991 } | |
| 992 // Check VM isolate for pc. | |
| 993 if (vm_isolate()->heap()->CodeContains(pc)) { | |
| 994 return Code::LookupCodeInVmIsolate(pc); | |
| 995 } | |
| 996 return Code::null(); | |
| 997 } | |
| 998 | |
| 999 Isolate* vm_isolate_; | |
| 1000 }; | |
| 1001 | |
| 1002 | |
| 1003 class CodeRegionTableBuilder : public SampleVisitor { | |
| 1004 public: | |
| 1005 CodeRegionTableBuilder(Isolate* isolate, | |
| 1006 CodeRegionTable* live_code_table, | |
| 1007 CodeRegionTable* dead_code_table, | |
| 1008 CodeRegionTable* tag_code_table) | |
| 1009 : SampleVisitor(isolate), | |
| 1010 live_code_table_(live_code_table), | |
| 1011 dead_code_table_(dead_code_table), | |
| 1012 tag_code_table_(tag_code_table), | |
| 1013 isolate_(isolate), | |
| 1014 vm_isolate_(Dart::vm_isolate()) { | |
| 1015 ASSERT(live_code_table_ != NULL); | |
| 1016 ASSERT(dead_code_table_ != NULL); | |
| 1017 ASSERT(tag_code_table_ != NULL); | |
| 1018 frames_ = 0; | |
| 1019 min_time_ = kMaxInt64; | |
| 1020 max_time_ = 0; | |
| 1021 ASSERT(isolate_ != NULL); | |
| 1022 ASSERT(vm_isolate_ != NULL); | |
| 1023 } | |
| 1024 | |
| 1025 void VisitSample(Sample* sample) { | |
| 1026 int64_t timestamp = sample->timestamp(); | |
| 1027 if (timestamp > max_time_) { | |
| 1028 max_time_ = timestamp; | |
| 1029 } | |
| 1030 if (timestamp < min_time_) { | |
| 1031 min_time_ = timestamp; | |
| 1032 } | |
| 1033 // Make sure VM tag is created. | |
| 1034 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | |
| 1035 CreateTag(VMTag::kNativeTagId); | |
| 1036 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | |
| 1037 CreateTag(VMTag::kRuntimeTagId); | |
| 1038 } | |
| 1039 CreateTag(sample->vm_tag()); | |
| 1040 // Make sure user tag is created. | |
| 1041 CreateUserTag(sample->user_tag()); | |
| 1042 // Exclusive tick for bottom frame if we aren't sampled from an exit frame. | |
| 1043 if (!sample->exit_frame_sample()) { | |
| 1044 Tick(sample->At(0), true, timestamp); | |
| 1045 } | |
| 1046 // Inclusive tick for all frames. | |
| 1047 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | |
| 1048 if (sample->At(i) == 0) { | |
| 1049 break; | |
| 1050 } | |
| 1051 frames_++; | |
| 1052 Tick(sample->At(i), false, timestamp); | |
| 1053 } | |
| 1054 } | |
| 1055 | |
| 1056 intptr_t frames() const { return frames_; } | |
| 1057 | |
| 1058 intptr_t TimeDeltaMicros() const { | |
| 1059 return static_cast<intptr_t>(max_time_ - min_time_); | |
| 1060 } | |
| 1061 int64_t max_time() const { return max_time_; } | |
| 1062 | |
| 1063 private: | |
| 1064 void CreateTag(uword tag) { | |
| 1065 intptr_t index = tag_code_table_->FindIndex(tag); | |
| 1066 if (index >= 0) { | |
| 1067 // Already created. | |
| 1068 return; | |
| 1069 } | |
| 1070 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, | |
| 1071 tag, | |
| 1072 tag + 1, | |
| 1073 0); | |
| 1074 index = tag_code_table_->InsertCodeRegion(region); | |
| 1075 ASSERT(index >= 0); | |
| 1076 region->set_creation_serial(visited()); | |
| 1077 } | |
| 1078 | |
| 1079 void CreateUserTag(uword tag) { | |
| 1080 if (tag == 0) { | |
| 1081 // None set. | |
| 1082 return; | |
| 1083 } | |
| 1084 intptr_t index = tag_code_table_->FindIndex(tag); | |
| 1085 if (index >= 0) { | |
| 1086 // Already created. | |
| 1087 return; | |
| 1088 } | |
| 1089 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, | |
| 1090 tag, | |
| 1091 tag + 1, | |
| 1092 0); | |
| 1093 index = tag_code_table_->InsertCodeRegion(region); | |
| 1094 ASSERT(index >= 0); | |
| 1095 region->set_creation_serial(visited()); | |
| 1096 } | |
| 1097 | |
| 1098 void Tick(uword pc, bool exclusive, int64_t timestamp) { | |
| 1099 CodeRegionTable::TickResult r; | |
| 1100 intptr_t serial = exclusive ? -1 : visited(); | |
| 1101 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); | |
| 1102 if (r == CodeRegionTable::kTicked) { | |
| 1103 // Live code found and ticked. | |
| 1104 return; | |
| 1105 } | |
| 1106 if (r == CodeRegionTable::kNewerCode) { | |
| 1107 // Code has been overwritten by newer code. | |
| 1108 // Update shadow table of dead code regions. | |
| 1109 r = dead_code_table_->Tick(pc, exclusive, serial, timestamp); | |
| 1110 ASSERT(r != CodeRegionTable::kNewerCode); | |
| 1111 if (r == CodeRegionTable::kTicked) { | |
| 1112 // Dead code found and ticked. | |
| 1113 return; | |
| 1114 } | |
| 1115 ASSERT(r == CodeRegionTable::kNotFound); | |
| 1116 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
| 1117 return; | |
| 1118 } | |
| 1119 // Create new live CodeRegion. | |
| 1120 ASSERT(r == CodeRegionTable::kNotFound); | |
| 1121 CodeRegion* region = CreateCodeRegion(pc); | |
| 1122 region->set_creation_serial(visited()); | |
| 1123 intptr_t index = live_code_table_->InsertCodeRegion(region); | |
| 1124 ASSERT(index >= 0); | |
| 1125 region = live_code_table_->At(index); | |
| 1126 if (region->compile_timestamp() <= timestamp) { | |
| 1127 region->Tick(pc, exclusive, serial); | |
| 1128 return; | |
| 1129 } | |
| 1130 // We have created a new code region but it's for a CodeRegion | |
| 1131 // compiled after the sample. | |
| 1132 ASSERT(region->kind() == CodeRegion::kDartCode); | |
| 1133 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
| 1134 } | |
| 1135 | |
| 1136 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { | |
| 1137 // Need to create dead code. | |
| 1138 CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, | |
| 1139 pc, | |
| 1140 pc + 1, | |
| 1141 0); | |
| 1142 intptr_t index = dead_code_table_->InsertCodeRegion(region); | |
| 1143 region->set_creation_serial(visited()); | |
| 1144 ASSERT(index >= 0); | |
| 1145 dead_code_table_->At(index)->Tick(pc, exclusive, serial); | |
| 1146 } | |
| 1147 | |
| 1148 CodeRegion* CreateCodeRegion(uword pc) { | |
| 1149 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | |
| 1150 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | |
| 1151 Code& code = Code::Handle(isolate_); | |
| 1152 // Check current isolate for pc. | |
| 1153 if (isolate_->heap()->CodeContains(pc)) { | |
| 1154 code ^= Code::LookupCode(pc); | |
| 1155 if (!code.IsNull()) { | |
| 1156 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 1157 code.EntryPoint() + code.Size(), | |
| 1158 code.compile_timestamp()); | |
| 1159 } | |
| 1160 return new CodeRegion(CodeRegion::kCollectedCode, pc, | |
| 1161 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
| 1162 0); | |
| 1163 } | |
| 1164 // Check VM isolate for pc. | |
| 1165 if (vm_isolate_->heap()->CodeContains(pc)) { | |
| 1166 code ^= Code::LookupCodeInVmIsolate(pc); | |
| 1167 if (!code.IsNull()) { | |
| 1168 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 1169 code.EntryPoint() + code.Size(), | |
| 1170 code.compile_timestamp()); | |
| 1171 } | |
| 1172 return new CodeRegion(CodeRegion::kCollectedCode, pc, | |
| 1173 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
| 1174 0); | |
| 1175 } | |
| 1176 // Check NativeSymbolResolver for pc. | |
| 1177 uintptr_t native_start = 0; | |
| 1178 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | |
| 1179 &native_start); | |
| 1180 if (native_name == NULL) { | |
| 1181 // No native name found. | |
| 1182 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0); | |
| 1183 } | |
| 1184 ASSERT(pc >= native_start); | |
| 1185 CodeRegion* code_region = | |
| 1186 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0); | |
| 1187 code_region->SetName(native_name); | |
| 1188 free(native_name); | |
| 1189 return code_region; | |
| 1190 } | |
| 1191 | |
| 1192 intptr_t frames_; | |
| 1193 int64_t min_time_; | |
| 1194 int64_t max_time_; | |
| 1195 CodeRegionTable* live_code_table_; | |
| 1196 CodeRegionTable* dead_code_table_; | |
| 1197 CodeRegionTable* tag_code_table_; | |
| 1198 Isolate* isolate_; | |
| 1199 Isolate* vm_isolate_; | |
| 1200 }; | |
| 1201 | |
| 1202 | |
| 1203 class CodeRegionExclusiveTrieBuilder : public SampleVisitor { | |
| 1204 public: | |
| 1205 CodeRegionExclusiveTrieBuilder(Isolate* isolate, | |
| 1206 CodeRegionTable* live_code_table, | |
| 1207 CodeRegionTable* dead_code_table, | |
| 1208 CodeRegionTable* tag_code_table) | |
| 1209 : SampleVisitor(isolate), | |
| 1210 live_code_table_(live_code_table), | |
| 1211 dead_code_table_(dead_code_table), | |
| 1212 tag_code_table_(tag_code_table) { | |
| 1213 ASSERT(live_code_table_ != NULL); | |
| 1214 ASSERT(dead_code_table_ != NULL); | |
| 1215 ASSERT(tag_code_table_ != NULL); | |
| 1216 dead_code_table_offset_ = live_code_table_->Length(); | |
| 1217 tag_code_table_offset_ = dead_code_table_offset_ + | |
| 1218 dead_code_table_->Length(); | |
| 1219 intptr_t root_index = tag_code_table_->FindIndex(0); | |
| 1220 // Verify that the "0" tag does not exist. | |
| 1221 ASSERT(root_index < 0); | |
| 1222 // Insert the dummy tag CodeRegion that is used for the Trie root. | |
| 1223 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0); | |
| 1224 root_index = tag_code_table_->InsertCodeRegion(region); | |
| 1225 ASSERT(root_index >= 0); | |
| 1226 region->set_creation_serial(0); | |
| 1227 root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index); | |
| 1228 set_tag_order(Profiler::kUserVM); | |
| 1229 } | |
| 1230 | |
| 1231 void VisitSample(Sample* sample) { | |
| 1232 // Give the root a tick. | |
| 1233 root_->Tick(); | |
| 1234 CodeRegionTrieNode* current = root_; | |
| 1235 current = ProcessTags(sample, current); | |
| 1236 // Walk the sampled PCs. | |
| 1237 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | |
| 1238 if (sample->At(i) == 0) { | |
| 1239 break; | |
| 1240 } | |
| 1241 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); | |
| 1242 current = current->GetChild(index); | |
| 1243 current->Tick(); | |
| 1244 } | |
| 1245 } | |
| 1246 | |
| 1247 CodeRegionTrieNode* root() const { | |
| 1248 return root_; | |
| 1249 } | |
| 1250 | |
| 1251 Profiler::TagOrder tag_order() const { | |
| 1252 return tag_order_; | |
| 1253 } | |
| 1254 | |
| 1255 void set_tag_order(Profiler::TagOrder tag_order) { | |
| 1256 tag_order_ = tag_order; | |
| 1257 } | |
| 1258 | |
| 1259 private: | |
| 1260 CodeRegionTrieNode* ProcessUserTags(Sample* sample, | |
| 1261 CodeRegionTrieNode* current) { | |
| 1262 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); | |
| 1263 if (user_tag_index >= 0) { | |
| 1264 current = current->GetChild(user_tag_index); | |
| 1265 // Give the tag a tick. | |
| 1266 current->Tick(); | |
| 1267 } | |
| 1268 return current; | |
| 1269 } | |
| 1270 | |
| 1271 CodeRegionTrieNode* ProcessVMTags(Sample* sample, | |
| 1272 CodeRegionTrieNode* current) { | |
| 1273 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | |
| 1274 // Insert a dummy kNativeTagId node. | |
| 1275 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); | |
| 1276 current = current->GetChild(tag_index); | |
| 1277 // Give the tag a tick. | |
| 1278 current->Tick(); | |
| 1279 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | |
| 1280 // Insert a dummy kRuntimeTagId node. | |
| 1281 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); | |
| 1282 current = current->GetChild(tag_index); | |
| 1283 // Give the tag a tick. | |
| 1284 current->Tick(); | |
| 1285 } | |
| 1286 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | |
| 1287 current = current->GetChild(tag_index); | |
| 1288 // Give the tag a tick. | |
| 1289 current->Tick(); | |
| 1290 return current; | |
| 1291 } | |
| 1292 | |
| 1293 CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { | |
| 1294 // None. | |
| 1295 if (tag_order() == Profiler::kNoTags) { | |
| 1296 return current; | |
| 1297 } | |
| 1298 // User first. | |
| 1299 if ((tag_order() == Profiler::kUserVM) || | |
| 1300 (tag_order() == Profiler::kUser)) { | |
| 1301 current = ProcessUserTags(sample, current); | |
| 1302 // Only user. | |
| 1303 if (tag_order() == Profiler::kUser) { | |
| 1304 return current; | |
| 1305 } | |
| 1306 return ProcessVMTags(sample, current); | |
| 1307 } | |
| 1308 // VM first. | |
| 1309 ASSERT((tag_order() == Profiler::kVMUser) || | |
| 1310 (tag_order() == Profiler::kVM)); | |
| 1311 current = ProcessVMTags(sample, current); | |
| 1312 // Only VM. | |
| 1313 if (tag_order() == Profiler::kVM) { | |
| 1314 return current; | |
| 1315 } | |
| 1316 return ProcessUserTags(sample, current); | |
| 1317 } | |
| 1318 | |
| 1319 intptr_t FindTagIndex(uword tag) const { | |
| 1320 if (tag == 0) { | |
| 1321 return -1; | |
| 1322 } | |
| 1323 intptr_t index = tag_code_table_->FindIndex(tag); | |
| 1324 if (index <= 0) { | |
| 1325 return -1; | |
| 1326 } | |
| 1327 ASSERT(index >= 0); | |
| 1328 ASSERT((tag_code_table_->At(index))->contains(tag)); | |
| 1329 return tag_code_table_offset_ + index; | |
| 1330 } | |
| 1331 | |
| 1332 intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { | |
| 1333 intptr_t index = live_code_table_->FindIndex(pc); | |
| 1334 ASSERT(index >= 0); | |
| 1335 CodeRegion* region = live_code_table_->At(index); | |
| 1336 ASSERT(region->contains(pc)); | |
| 1337 if (region->compile_timestamp() > timestamp) { | |
| 1338 // Overwritten code, find in dead code table. | |
| 1339 index = dead_code_table_->FindIndex(pc); | |
| 1340 ASSERT(index >= 0); | |
| 1341 region = dead_code_table_->At(index); | |
| 1342 ASSERT(region->contains(pc)); | |
| 1343 ASSERT(region->compile_timestamp() <= timestamp); | |
| 1344 return index + dead_code_table_offset_; | |
| 1345 } | |
| 1346 ASSERT(region->compile_timestamp() <= timestamp); | |
| 1347 return index; | |
| 1348 } | |
| 1349 | |
| 1350 Profiler::TagOrder tag_order_; | |
| 1351 CodeRegionTrieNode* root_; | |
| 1352 CodeRegionTable* live_code_table_; | |
| 1353 CodeRegionTable* dead_code_table_; | |
| 1354 CodeRegionTable* tag_code_table_; | |
| 1355 intptr_t dead_code_table_offset_; | |
| 1356 intptr_t tag_code_table_offset_; | |
| 1357 }; | |
| 1358 | |
| 1359 | |
| 1360 class CodeRegionTableCallersBuilder { | |
| 1361 public: | |
| 1362 CodeRegionTableCallersBuilder(CodeRegionTrieNode* exclusive_root, | |
| 1363 CodeRegionTable* live_code_table, | |
| 1364 CodeRegionTable* dead_code_table, | |
| 1365 CodeRegionTable* tag_code_table) | |
| 1366 : exclusive_root_(exclusive_root), | |
| 1367 live_code_table_(live_code_table), | |
| 1368 dead_code_table_(dead_code_table), | |
| 1369 tag_code_table_(tag_code_table) { | |
| 1370 ASSERT(exclusive_root_ != NULL); | |
| 1371 ASSERT(live_code_table_ != NULL); | |
| 1372 ASSERT(dead_code_table_ != NULL); | |
| 1373 ASSERT(tag_code_table_ != NULL); | |
| 1374 dead_code_table_offset_ = live_code_table_->Length(); | |
| 1375 tag_code_table_offset_ = dead_code_table_offset_ + | |
| 1376 dead_code_table_->Length(); | |
| 1377 } | |
| 1378 | |
| 1379 void Build() { | |
| 1380 ProcessNode(exclusive_root_); | |
| 1381 } | |
| 1382 | |
| 1383 private: | |
| 1384 void ProcessNode(CodeRegionTrieNode* parent) { | |
| 1385 const ZoneGrowableArray<CodeRegionTrieNode*>& children = parent->children(); | |
| 1386 intptr_t parent_index = parent->code_region_index(); | |
| 1387 ASSERT(parent_index >= 0); | |
| 1388 CodeRegion* parent_region = At(parent_index); | |
| 1389 ASSERT(parent_region != NULL); | |
| 1390 for (intptr_t i = 0; i < children.length(); i++) { | |
| 1391 CodeRegionTrieNode* node = children[i]; | |
| 1392 ProcessNode(node); | |
| 1393 intptr_t index = node->code_region_index(); | |
| 1394 ASSERT(index >= 0); | |
| 1395 CodeRegion* region = At(index); | |
| 1396 ASSERT(region != NULL); | |
| 1397 region->AddCallee(parent_index, node->count()); | |
| 1398 parent_region->AddCaller(index, node->count()); | |
| 1399 } | |
| 1400 } | |
| 1401 | |
| 1402 CodeRegion* At(intptr_t final_index) { | |
| 1403 ASSERT(final_index >= 0); | |
| 1404 if (final_index < dead_code_table_offset_) { | |
| 1405 return live_code_table_->At(final_index); | |
| 1406 } else if (final_index < tag_code_table_offset_) { | |
| 1407 return dead_code_table_->At(final_index - dead_code_table_offset_); | |
| 1408 } else { | |
| 1409 return tag_code_table_->At(final_index - tag_code_table_offset_); | |
| 1410 } | |
| 1411 } | |
| 1412 | |
| 1413 CodeRegionTrieNode* exclusive_root_; | |
| 1414 CodeRegionTable* live_code_table_; | |
| 1415 CodeRegionTable* dead_code_table_; | |
| 1416 CodeRegionTable* tag_code_table_; | |
| 1417 intptr_t dead_code_table_offset_; | |
| 1418 intptr_t tag_code_table_offset_; | |
| 1419 }; | |
| 1420 | |
| 1421 | |
| 1422 void Profiler::PrintJSON(Isolate* isolate, JSONStream* stream, | |
| 1423 bool full, TagOrder tag_order) { | |
| 1424 ASSERT(isolate == Isolate::Current()); | |
| 1425 // Disable profile interrupts while processing the buffer. | |
| 1426 EndExecution(isolate); | |
| 1427 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | |
| 1428 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 1429 if (profiler_data == NULL) { | |
| 1430 JSONObject error(stream); | |
| 1431 error.AddProperty("type", "Error"); | |
| 1432 error.AddProperty("text", "Isolate does not have profiling enabled."); | |
| 1433 return; | |
| 1434 } | |
| 1435 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
| 1436 ASSERT(sample_buffer != NULL); | |
| 1437 { | |
| 1438 StackZone zone(isolate); | |
| 1439 { | |
| 1440 // Live code holds Dart, Native, and Collected CodeRegions. | |
| 1441 CodeRegionTable live_code_table; | |
| 1442 // Dead code holds Overwritten CodeRegions. | |
| 1443 CodeRegionTable dead_code_table; | |
| 1444 // Tag code holds Tag CodeRegions. | |
| 1445 CodeRegionTable tag_code_table; | |
| 1446 CodeRegionTableBuilder builder(isolate, | |
| 1447 &live_code_table, | |
| 1448 &dead_code_table, | |
| 1449 &tag_code_table); | |
| 1450 { | |
| 1451 ScopeStopwatch sw("FixTopFrame"); | |
| 1452 // Preprocess samples and fix the caller when the top PC is in a | |
| 1453 // stub or intrinsic without a frame. | |
| 1454 FixTopFrameVisitor fixTopFrame(isolate); | |
| 1455 sample_buffer->VisitSamples(&fixTopFrame); | |
| 1456 } | |
| 1457 { | |
| 1458 // Build CodeRegion tables. | |
| 1459 ScopeStopwatch sw("CodeRegionTableBuilder"); | |
| 1460 sample_buffer->VisitSamples(&builder); | |
| 1461 } | |
| 1462 intptr_t samples = builder.visited(); | |
| 1463 intptr_t frames = builder.frames(); | |
| 1464 if (FLAG_trace_profiler) { | |
| 1465 intptr_t total_live_code_objects = live_code_table.Length(); | |
| 1466 intptr_t total_dead_code_objects = dead_code_table.Length(); | |
| 1467 intptr_t total_tag_code_objects = tag_code_table.Length(); | |
| 1468 OS::Print("Processed %" Pd " frames\n", frames); | |
| 1469 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", | |
| 1470 total_live_code_objects, | |
| 1471 total_dead_code_objects, | |
| 1472 total_tag_code_objects); | |
| 1473 } | |
| 1474 #if defined(DEBUG) | |
| 1475 live_code_table.Verify(); | |
| 1476 dead_code_table.Verify(); | |
| 1477 tag_code_table.Verify(); | |
| 1478 if (FLAG_trace_profiler) { | |
| 1479 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); | |
| 1480 } | |
| 1481 #endif | |
| 1482 CodeRegionExclusiveTrieBuilder build_trie(isolate, | |
| 1483 &live_code_table, | |
| 1484 &dead_code_table, | |
| 1485 &tag_code_table); | |
| 1486 build_trie.set_tag_order(tag_order); | |
| 1487 { | |
| 1488 // Build CodeRegion trie. | |
| 1489 ScopeStopwatch sw("CodeRegionExclusiveTrieBuilder"); | |
| 1490 sample_buffer->VisitSamples(&build_trie); | |
| 1491 build_trie.root()->SortByCount(); | |
| 1492 } | |
| 1493 CodeRegionTableCallersBuilder build_callers(build_trie.root(), | |
| 1494 &live_code_table, | |
| 1495 &dead_code_table, | |
| 1496 &tag_code_table); | |
| 1497 { | |
| 1498 // Build CodeRegion callers. | |
| 1499 ScopeStopwatch sw("CodeRegionTableCallersBuilder"); | |
| 1500 build_callers.Build(); | |
| 1501 } | |
| 1502 { | |
| 1503 ScopeStopwatch sw("CodeTableStream"); | |
| 1504 // Serialize to JSON. | |
| 1505 JSONObject obj(stream); | |
| 1506 obj.AddProperty("type", "CpuProfile"); | |
| 1507 obj.AddProperty("id", "profile"); | |
| 1508 obj.AddProperty("samples", samples); | |
| 1509 obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth)); | |
| 1510 obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period)); | |
| 1511 obj.AddProperty("timeSpan", | |
| 1512 MicrosecondsToSeconds(builder.TimeDeltaMicros())); | |
| 1513 { | |
| 1514 JSONArray exclusive_trie(&obj, "exclusive_trie"); | |
| 1515 CodeRegionTrieNode* root = build_trie.root(); | |
| 1516 ASSERT(root != NULL); | |
| 1517 root->PrintToJSONArray(&exclusive_trie); | |
| 1518 } | |
| 1519 JSONArray codes(&obj, "codes"); | |
| 1520 for (intptr_t i = 0; i < live_code_table.Length(); i++) { | |
| 1521 CodeRegion* region = live_code_table.At(i); | |
| 1522 ASSERT(region != NULL); | |
| 1523 region->PrintToJSONArray(isolate, &codes, full); | |
| 1524 } | |
| 1525 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { | |
| 1526 CodeRegion* region = dead_code_table.At(i); | |
| 1527 ASSERT(region != NULL); | |
| 1528 region->PrintToJSONArray(isolate, &codes, full); | |
| 1529 } | |
| 1530 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { | |
| 1531 CodeRegion* region = tag_code_table.At(i); | |
| 1532 ASSERT(region != NULL); | |
| 1533 region->PrintToJSONArray(isolate, &codes, full); | |
| 1534 } | |
| 1535 } | |
| 1536 } | |
| 1537 } | |
| 1538 // Enable profile interrupts. | |
| 1539 BeginExecution(isolate); | |
| 1540 } | |
| 1541 | |
| 1542 | |
| 1543 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, | 176 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, |
| 1544 bool own_sample_buffer) { | 177 bool own_sample_buffer) { |
| 1545 ASSERT(sample_buffer != NULL); | 178 ASSERT(sample_buffer != NULL); |
| 1546 sample_buffer_ = sample_buffer; | 179 sample_buffer_ = sample_buffer; |
| 1547 own_sample_buffer_ = own_sample_buffer; | 180 own_sample_buffer_ = own_sample_buffer; |
| 1548 block_count_ = 0; | 181 block_count_ = 0; |
| 1549 } | 182 } |
| 1550 | 183 |
| 1551 | 184 |
| 1552 IsolateProfilerData::~IsolateProfilerData() { | 185 IsolateProfilerData::~IsolateProfilerData() { |
| (...skipping 528 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2081 state.pc, | 714 state.pc, |
| 2082 state.fp, | 715 state.fp, |
| 2083 sp); | 716 sp); |
| 2084 stackWalker.walk(); | 717 stackWalker.walk(); |
| 2085 #endif | 718 #endif |
| 2086 } | 719 } |
| 2087 } | 720 } |
| 2088 } | 721 } |
| 2089 | 722 |
| 2090 } // namespace dart | 723 } // namespace dart |
| OLD | NEW |