Revision f230a1cf deps/v8/src/heap-snapshot-generator.cc
deps/v8/src/heap-snapshot-generator.cc | ||
---|---|---|
29 | 29 |
|
30 | 30 |
#include "heap-snapshot-generator-inl.h" |
31 | 31 |
|
32 |
#include "allocation-tracker.h" |
|
32 | 33 |
#include "heap-profiler.h" |
33 | 34 |
#include "debug.h" |
34 | 35 |
#include "types.h" |
... | ... | |
397 | 398 |
} |
398 | 399 |
|
399 | 400 |
|
400 |
void HeapObjectsMap::MoveObject(Address from, Address to) { |
|
401 |
void HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
|
|
401 | 402 |
ASSERT(to != NULL); |
402 | 403 |
ASSERT(from != NULL); |
403 | 404 |
if (from == to) return; |
... | ... | |
428 | 429 |
int from_entry_info_index = |
429 | 430 |
static_cast<int>(reinterpret_cast<intptr_t>(from_value)); |
430 | 431 |
entries_.at(from_entry_info_index).addr = to; |
432 |
// Size of an object can change during its life, so to keep information |
|
433 |
// about the object in entries_ consistent, we have to adjust size when the |
|
434 |
// object is migrated. |
|
435 |
if (FLAG_heap_profiler_trace_objects) { |
|
436 |
PrintF("Move object from %p to %p old size %6d new size %6d\n", |
|
437 |
from, |
|
438 |
to, |
|
439 |
entries_.at(from_entry_info_index).size, |
|
440 |
object_size); |
|
441 |
} |
|
442 |
entries_.at(from_entry_info_index).size = object_size; |
|
431 | 443 |
to_entry->value = from_value; |
432 | 444 |
} |
433 | 445 |
} |
434 | 446 |
|
435 | 447 |
|
448 |
void HeapObjectsMap::NewObject(Address addr, int size) { |
|
449 |
if (FLAG_heap_profiler_trace_objects) { |
|
450 |
PrintF("New object : %p %6d. Next address is %p\n", |
|
451 |
addr, |
|
452 |
size, |
|
453 |
addr + size); |
|
454 |
} |
|
455 |
ASSERT(addr != NULL); |
|
456 |
FindOrAddEntry(addr, size, false); |
|
457 |
} |
|
458 |
|
|
459 |
|
|
460 |
void HeapObjectsMap::UpdateObjectSize(Address addr, int size) { |
|
461 |
FindOrAddEntry(addr, size, false); |
|
462 |
} |
|
463 |
|
|
464 |
|
|
436 | 465 |
SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) { |
437 | 466 |
HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr), |
438 | 467 |
false); |
... | ... | |
445 | 474 |
|
446 | 475 |
|
447 | 476 |
SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr, |
448 |
unsigned int size) { |
|
477 |
unsigned int size, |
|
478 |
bool accessed) { |
|
449 | 479 |
ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); |
450 | 480 |
HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr), |
451 | 481 |
true); |
... | ... | |
453 | 483 |
int entry_index = |
454 | 484 |
static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); |
455 | 485 |
EntryInfo& entry_info = entries_.at(entry_index); |
456 |
entry_info.accessed = true; |
|
486 |
entry_info.accessed = accessed; |
|
487 |
if (FLAG_heap_profiler_trace_objects) { |
|
488 |
PrintF("Update object size : %p with old size %d and new size %d\n", |
|
489 |
addr, |
|
490 |
entry_info.size, |
|
491 |
size); |
|
492 |
} |
|
457 | 493 |
entry_info.size = size; |
458 | 494 |
return entry_info.id; |
459 | 495 |
} |
460 | 496 |
entry->value = reinterpret_cast<void*>(entries_.length()); |
461 | 497 |
SnapshotObjectId id = next_id_; |
462 | 498 |
next_id_ += kObjectIdStep; |
463 |
entries_.Add(EntryInfo(id, addr, size)); |
|
499 |
entries_.Add(EntryInfo(id, addr, size, accessed));
|
|
464 | 500 |
ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); |
465 | 501 |
return id; |
466 | 502 |
} |
... | ... | |
472 | 508 |
|
473 | 509 |
|
474 | 510 |
void HeapObjectsMap::UpdateHeapObjectsMap() { |
511 |
if (FLAG_heap_profiler_trace_objects) { |
|
512 |
PrintF("Begin HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n", |
|
513 |
entries_map_.occupancy()); |
|
514 |
} |
|
475 | 515 |
heap_->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
476 | 516 |
"HeapSnapshotsCollection::UpdateHeapObjectsMap"); |
477 | 517 |
HeapIterator iterator(heap_); |
... | ... | |
479 | 519 |
obj != NULL; |
480 | 520 |
obj = iterator.next()) { |
481 | 521 |
FindOrAddEntry(obj->address(), obj->Size()); |
522 |
if (FLAG_heap_profiler_trace_objects) { |
|
523 |
PrintF("Update object : %p %6d. Next address is %p\n", |
|
524 |
obj->address(), |
|
525 |
obj->Size(), |
|
526 |
obj->address() + obj->Size()); |
|
527 |
} |
|
482 | 528 |
} |
483 | 529 |
RemoveDeadEntries(); |
530 |
if (FLAG_heap_profiler_trace_objects) { |
|
531 |
PrintF("End HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n", |
|
532 |
entries_map_.occupancy()); |
|
533 |
} |
|
534 |
} |
|
535 |
|
|
536 |
|
|
537 |
namespace { |
|
538 |
|
|
539 |
|
|
540 |
struct HeapObjectInfo { |
|
541 |
HeapObjectInfo(HeapObject* obj, int expected_size) |
|
542 |
: obj(obj), |
|
543 |
expected_size(expected_size) { |
|
544 |
} |
|
545 |
|
|
546 |
HeapObject* obj; |
|
547 |
int expected_size; |
|
548 |
|
|
549 |
bool IsValid() const { return expected_size == obj->Size(); } |
|
550 |
|
|
551 |
void Print() const { |
|
552 |
if (expected_size == 0) { |
|
553 |
PrintF("Untracked object : %p %6d. Next address is %p\n", |
|
554 |
obj->address(), |
|
555 |
obj->Size(), |
|
556 |
obj->address() + obj->Size()); |
|
557 |
} else if (obj->Size() != expected_size) { |
|
558 |
PrintF("Wrong size %6d: %p %6d. Next address is %p\n", |
|
559 |
expected_size, |
|
560 |
obj->address(), |
|
561 |
obj->Size(), |
|
562 |
obj->address() + obj->Size()); |
|
563 |
} else { |
|
564 |
PrintF("Good object : %p %6d. Next address is %p\n", |
|
565 |
obj->address(), |
|
566 |
expected_size, |
|
567 |
obj->address() + obj->Size()); |
|
568 |
} |
|
569 |
} |
|
570 |
}; |
|
571 |
|
|
572 |
|
|
573 |
static int comparator(const HeapObjectInfo* a, const HeapObjectInfo* b) { |
|
574 |
if (a->obj < b->obj) return -1; |
|
575 |
if (a->obj > b->obj) return 1; |
|
576 |
return 0; |
|
577 |
} |
|
578 |
|
|
579 |
|
|
580 |
} // namespace |
|
581 |
|
|
582 |
|
|
583 |
int HeapObjectsMap::FindUntrackedObjects() { |
|
584 |
List<HeapObjectInfo> heap_objects(1000); |
|
585 |
|
|
586 |
HeapIterator iterator(heap_); |
|
587 |
int untracked = 0; |
|
588 |
for (HeapObject* obj = iterator.next(); |
|
589 |
obj != NULL; |
|
590 |
obj = iterator.next()) { |
|
591 |
HashMap::Entry* entry = entries_map_.Lookup( |
|
592 |
obj->address(), ComputePointerHash(obj->address()), false); |
|
593 |
if (entry == NULL) { |
|
594 |
++untracked; |
|
595 |
if (FLAG_heap_profiler_trace_objects) { |
|
596 |
heap_objects.Add(HeapObjectInfo(obj, 0)); |
|
597 |
} |
|
598 |
} else { |
|
599 |
int entry_index = static_cast<int>( |
|
600 |
reinterpret_cast<intptr_t>(entry->value)); |
|
601 |
EntryInfo& entry_info = entries_.at(entry_index); |
|
602 |
if (FLAG_heap_profiler_trace_objects) { |
|
603 |
heap_objects.Add(HeapObjectInfo(obj, |
|
604 |
static_cast<int>(entry_info.size))); |
|
605 |
if (obj->Size() != static_cast<int>(entry_info.size)) |
|
606 |
++untracked; |
|
607 |
} else { |
|
608 |
CHECK_EQ(obj->Size(), static_cast<int>(entry_info.size)); |
|
609 |
} |
|
610 |
} |
|
611 |
} |
|
612 |
if (FLAG_heap_profiler_trace_objects) { |
|
613 |
PrintF("\nBegin HeapObjectsMap::FindUntrackedObjects. %d entries in map.\n", |
|
614 |
entries_map_.occupancy()); |
|
615 |
heap_objects.Sort(comparator); |
|
616 |
int last_printed_object = -1; |
|
617 |
bool print_next_object = false; |
|
618 |
for (int i = 0; i < heap_objects.length(); ++i) { |
|
619 |
const HeapObjectInfo& object_info = heap_objects[i]; |
|
620 |
if (!object_info.IsValid()) { |
|
621 |
++untracked; |
|
622 |
if (last_printed_object != i - 1) { |
|
623 |
if (i > 0) { |
|
624 |
PrintF("%d objects were skipped\n", i - 1 - last_printed_object); |
|
625 |
heap_objects[i - 1].Print(); |
|
626 |
} |
|
627 |
} |
|
628 |
object_info.Print(); |
|
629 |
last_printed_object = i; |
|
630 |
print_next_object = true; |
|
631 |
} else if (print_next_object) { |
|
632 |
object_info.Print(); |
|
633 |
print_next_object = false; |
|
634 |
last_printed_object = i; |
|
635 |
} |
|
636 |
} |
|
637 |
if (last_printed_object < heap_objects.length() - 1) { |
|
638 |
PrintF("Last %d objects were skipped\n", |
|
639 |
heap_objects.length() - 1 - last_printed_object); |
|
640 |
} |
|
641 |
PrintF("End HeapObjectsMap::FindUntrackedObjects. %d entries in map.\n\n", |
|
642 |
entries_map_.occupancy()); |
|
643 |
} |
|
644 |
return untracked; |
|
484 | 645 |
} |
485 | 646 |
|
486 | 647 |
|
... | ... | |
587 | 748 |
HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap) |
588 | 749 |
: is_tracking_objects_(false), |
589 | 750 |
names_(heap), |
590 |
ids_(heap) { |
|
751 |
ids_(heap), |
|
752 |
allocation_tracker_(NULL) { |
|
591 | 753 |
} |
592 | 754 |
|
593 | 755 |
|
... | ... | |
597 | 759 |
|
598 | 760 |
|
599 | 761 |
HeapSnapshotsCollection::~HeapSnapshotsCollection() { |
762 |
delete allocation_tracker_; |
|
600 | 763 |
snapshots_.Iterate(DeleteHeapSnapshot); |
601 | 764 |
} |
602 | 765 |
|
603 | 766 |
|
767 |
void HeapSnapshotsCollection::StartHeapObjectsTracking() { |
|
768 |
ids_.UpdateHeapObjectsMap(); |
|
769 |
if (allocation_tracker_ == NULL) { |
|
770 |
allocation_tracker_ = new AllocationTracker(&ids_, names()); |
|
771 |
} |
|
772 |
is_tracking_objects_ = true; |
|
773 |
} |
|
774 |
|
|
775 |
|
|
776 |
void HeapSnapshotsCollection::StopHeapObjectsTracking() { |
|
777 |
ids_.StopHeapObjectsTracking(); |
|
778 |
if (allocation_tracker_ != NULL) { |
|
779 |
delete allocation_tracker_; |
|
780 |
allocation_tracker_ = NULL; |
|
781 |
} |
|
782 |
} |
|
783 |
|
|
784 |
|
|
604 | 785 |
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name, |
605 | 786 |
unsigned uid) { |
606 | 787 |
is_tracking_objects_ = true; // Start watching for heap objects moves. |
... | ... | |
644 | 825 |
} |
645 | 826 |
|
646 | 827 |
|
828 |
void HeapSnapshotsCollection::NewObjectEvent(Address addr, int size) { |
|
829 |
DisallowHeapAllocation no_allocation; |
|
830 |
ids_.NewObject(addr, size); |
|
831 |
if (allocation_tracker_ != NULL) { |
|
832 |
allocation_tracker_->NewObjectEvent(addr, size); |
|
833 |
} |
|
834 |
} |
|
835 |
|
|
836 |
|
|
647 | 837 |
size_t HeapSnapshotsCollection::GetUsedMemorySize() const { |
648 | 838 |
size_t size = sizeof(*this); |
649 | 839 |
size += names_.GetUsedMemorySize(); |
... | ... | |
1301 | 1491 |
AllocationSite* site) { |
1302 | 1492 |
SetInternalReference(site, entry, "transition_info", site->transition_info(), |
1303 | 1493 |
AllocationSite::kTransitionInfoOffset); |
1494 |
SetInternalReference(site, entry, "nested_site", site->nested_site(), |
|
1495 |
AllocationSite::kNestedSiteOffset); |
|
1496 |
SetInternalReference(site, entry, "dependent_code", site->dependent_code(), |
|
1497 |
AllocationSite::kDependentCodeOffset); |
|
1304 | 1498 |
} |
1305 | 1499 |
|
1306 | 1500 |
|
... | ... | |
2438 | 2632 |
const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5; |
2439 | 2633 |
|
2440 | 2634 |
void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) { |
2635 |
if (AllocationTracker* allocation_tracker = |
|
2636 |
snapshot_->collection()->allocation_tracker()) { |
|
2637 |
allocation_tracker->PrepareForSerialization(); |
|
2638 |
} |
|
2441 | 2639 |
ASSERT(writer_ == NULL); |
2442 | 2640 |
writer_ = new OutputStreamWriter(stream); |
2443 | 2641 |
SerializeImpl(); |
... | ... | |
2461 | 2659 |
SerializeEdges(); |
2462 | 2660 |
if (writer_->aborted()) return; |
2463 | 2661 |
writer_->AddString("],\n"); |
2662 |
|
|
2663 |
writer_->AddString("\"trace_function_infos\":["); |
|
2664 |
SerializeTraceNodeInfos(); |
|
2665 |
if (writer_->aborted()) return; |
|
2666 |
writer_->AddString("],\n"); |
|
2667 |
writer_->AddString("\"trace_tree\":["); |
|
2668 |
SerializeTraceTree(); |
|
2669 |
if (writer_->aborted()) return; |
|
2670 |
writer_->AddString("],\n"); |
|
2671 |
|
|
2464 | 2672 |
writer_->AddString("\"strings\":["); |
2465 | 2673 |
SerializeStrings(); |
2466 | 2674 |
if (writer_->aborted()) return; |
... | ... | |
2472 | 2680 |
|
2473 | 2681 |
int HeapSnapshotJSONSerializer::GetStringId(const char* s) { |
2474 | 2682 |
HashMap::Entry* cache_entry = strings_.Lookup( |
2475 |
const_cast<char*>(s), ObjectHash(s), true);
|
|
2683 |
const_cast<char*>(s), StringHash(s), true);
|
|
2476 | 2684 |
if (cache_entry->value == NULL) { |
2477 | 2685 |
cache_entry->value = reinterpret_cast<void*>(next_string_id_++); |
2478 | 2686 |
} |
... | ... | |
2621 | 2829 |
JSON_S("shortcut") "," |
2622 | 2830 |
JSON_S("weak")) "," |
2623 | 2831 |
JSON_S("string_or_number") "," |
2624 |
JSON_S("node")))); |
|
2832 |
JSON_S("node")) "," |
|
2833 |
JSON_S("trace_function_info_fields") ":" JSON_A( |
|
2834 |
JSON_S("function_id") "," |
|
2835 |
JSON_S("name") "," |
|
2836 |
JSON_S("script_name") "," |
|
2837 |
JSON_S("script_id") "," |
|
2838 |
JSON_S("line") "," |
|
2839 |
JSON_S("column")) "," |
|
2840 |
JSON_S("trace_node_fields") ":" JSON_A( |
|
2841 |
JSON_S("id") "," |
|
2842 |
JSON_S("function_id") "," |
|
2843 |
JSON_S("count") "," |
|
2844 |
JSON_S("size") "," |
|
2845 |
JSON_S("children")))); |
|
2625 | 2846 |
#undef JSON_S |
2626 | 2847 |
#undef JSON_O |
2627 | 2848 |
#undef JSON_A |
... | ... | |
2629 | 2850 |
writer_->AddNumber(snapshot_->entries().length()); |
2630 | 2851 |
writer_->AddString(",\"edge_count\":"); |
2631 | 2852 |
writer_->AddNumber(snapshot_->edges().length()); |
2853 |
writer_->AddString(",\"trace_function_count\":"); |
|
2854 |
uint32_t count = 0; |
|
2855 |
AllocationTracker* tracker = snapshot_->collection()->allocation_tracker(); |
|
2856 |
if (tracker) { |
|
2857 |
count = tracker->id_to_function_info()->occupancy(); |
|
2858 |
} |
|
2859 |
writer_->AddNumber(count); |
|
2632 | 2860 |
} |
2633 | 2861 |
|
2634 | 2862 |
|
... | ... | |
2642 | 2870 |
} |
2643 | 2871 |
|
2644 | 2872 |
|
2873 |
void HeapSnapshotJSONSerializer::SerializeTraceTree() { |
|
2874 |
AllocationTracker* tracker = snapshot_->collection()->allocation_tracker(); |
|
2875 |
if (!tracker) return; |
|
2876 |
AllocationTraceTree* traces = tracker->trace_tree(); |
|
2877 |
SerializeTraceNode(traces->root()); |
|
2878 |
} |
|
2879 |
|
|
2880 |
|
|
2881 |
void HeapSnapshotJSONSerializer::SerializeTraceNode(AllocationTraceNode* node) { |
|
2882 |
// The buffer needs space for 4 unsigned ints, 4 commas, [ and \0 |
|
2883 |
const int kBufferSize = |
|
2884 |
4 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT |
|
2885 |
+ 4 + 1 + 1; |
|
2886 |
EmbeddedVector<char, kBufferSize> buffer; |
|
2887 |
int buffer_pos = 0; |
|
2888 |
buffer_pos = utoa(node->id(), buffer, buffer_pos); |
|
2889 |
buffer[buffer_pos++] = ','; |
|
2890 |
buffer_pos = utoa(node->function_id(), buffer, buffer_pos); |
|
2891 |
buffer[buffer_pos++] = ','; |
|
2892 |
buffer_pos = utoa(node->allocation_count(), buffer, buffer_pos); |
|
2893 |
buffer[buffer_pos++] = ','; |
|
2894 |
buffer_pos = utoa(node->allocation_size(), buffer, buffer_pos); |
|
2895 |
buffer[buffer_pos++] = ','; |
|
2896 |
buffer[buffer_pos++] = '['; |
|
2897 |
buffer[buffer_pos++] = '\0'; |
|
2898 |
writer_->AddString(buffer.start()); |
|
2899 |
|
|
2900 |
Vector<AllocationTraceNode*> children = node->children(); |
|
2901 |
for (int i = 0; i < children.length(); i++) { |
|
2902 |
if (i > 0) { |
|
2903 |
writer_->AddCharacter(','); |
|
2904 |
} |
|
2905 |
SerializeTraceNode(children[i]); |
|
2906 |
} |
|
2907 |
writer_->AddCharacter(']'); |
|
2908 |
} |
|
2909 |
|
|
2910 |
|
|
2911 |
// 0-based position is converted to 1-based during the serialization. |
|
2912 |
static int SerializePosition(int position, const Vector<char>& buffer, |
|
2913 |
int buffer_pos) { |
|
2914 |
if (position == -1) { |
|
2915 |
buffer[buffer_pos++] = '0'; |
|
2916 |
} else { |
|
2917 |
ASSERT(position >= 0); |
|
2918 |
buffer_pos = utoa(static_cast<unsigned>(position + 1), buffer, buffer_pos); |
|
2919 |
} |
|
2920 |
return buffer_pos; |
|
2921 |
} |
|
2922 |
|
|
2923 |
|
|
2924 |
void HeapSnapshotJSONSerializer::SerializeTraceNodeInfos() { |
|
2925 |
AllocationTracker* tracker = snapshot_->collection()->allocation_tracker(); |
|
2926 |
if (!tracker) return; |
|
2927 |
// The buffer needs space for 6 unsigned ints, 6 commas, \n and \0 |
|
2928 |
const int kBufferSize = |
|
2929 |
6 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT |
|
2930 |
+ 6 + 1 + 1; |
|
2931 |
EmbeddedVector<char, kBufferSize> buffer; |
|
2932 |
HashMap* id_to_function_info = tracker->id_to_function_info(); |
|
2933 |
bool first_entry = true; |
|
2934 |
for (HashMap::Entry* p = id_to_function_info->Start(); |
|
2935 |
p != NULL; |
|
2936 |
p = id_to_function_info->Next(p)) { |
|
2937 |
SnapshotObjectId id = |
|
2938 |
static_cast<SnapshotObjectId>(reinterpret_cast<intptr_t>(p->key)); |
|
2939 |
AllocationTracker::FunctionInfo* info = |
|
2940 |
reinterpret_cast<AllocationTracker::FunctionInfo* >(p->value); |
|
2941 |
int buffer_pos = 0; |
|
2942 |
if (first_entry) { |
|
2943 |
first_entry = false; |
|
2944 |
} else { |
|
2945 |
buffer[buffer_pos++] = ','; |
|
2946 |
} |
|
2947 |
buffer_pos = utoa(id, buffer, buffer_pos); |
|
2948 |
buffer[buffer_pos++] = ','; |
|
2949 |
buffer_pos = utoa(GetStringId(info->name), buffer, buffer_pos); |
|
2950 |
buffer[buffer_pos++] = ','; |
|
2951 |
buffer_pos = utoa(GetStringId(info->script_name), buffer, buffer_pos); |
|
2952 |
buffer[buffer_pos++] = ','; |
|
2953 |
// The cast is safe because script id is a non-negative Smi. |
|
2954 |
buffer_pos = utoa(static_cast<unsigned>(info->script_id), buffer, |
|
2955 |
buffer_pos); |
|
2956 |
buffer[buffer_pos++] = ','; |
|
2957 |
buffer_pos = SerializePosition(info->line, buffer, buffer_pos); |
|
2958 |
buffer[buffer_pos++] = ','; |
|
2959 |
buffer_pos = SerializePosition(info->column, buffer, buffer_pos); |
|
2960 |
buffer[buffer_pos++] = '\n'; |
|
2961 |
buffer[buffer_pos++] = '\0'; |
|
2962 |
writer_->AddString(buffer.start()); |
|
2963 |
} |
|
2964 |
} |
|
2965 |
|
|
2966 |
|
|
2645 | 2967 |
void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) { |
2646 | 2968 |
writer_->AddCharacter('\n'); |
2647 | 2969 |
writer_->AddCharacter('\"'); |
... | ... | |
2693 | 3015 |
|
2694 | 3016 |
|
2695 | 3017 |
void HeapSnapshotJSONSerializer::SerializeStrings() { |
2696 |
List<HashMap::Entry*> sorted_strings; |
|
2697 |
SortHashMap(&strings_, &sorted_strings); |
|
3018 |
ScopedVector<const unsigned char*> sorted_strings( |
|
3019 |
strings_.occupancy() + 1); |
|
3020 |
for (HashMap::Entry* entry = strings_.Start(); |
|
3021 |
entry != NULL; |
|
3022 |
entry = strings_.Next(entry)) { |
|
3023 |
int index = static_cast<int>(reinterpret_cast<uintptr_t>(entry->value)); |
|
3024 |
sorted_strings[index] = reinterpret_cast<const unsigned char*>(entry->key); |
|
3025 |
} |
|
2698 | 3026 |
writer_->AddString("\"<dummy>\""); |
2699 |
for (int i = 0; i < sorted_strings.length(); ++i) {
|
|
3027 |
for (int i = 1; i < sorted_strings.length(); ++i) {
|
|
2700 | 3028 |
writer_->AddCharacter(','); |
2701 |
SerializeString( |
|
2702 |
reinterpret_cast<const unsigned char*>(sorted_strings[i]->key)); |
|
3029 |
SerializeString(sorted_strings[i]); |
|
2703 | 3030 |
if (writer_->aborted()) return; |
2704 | 3031 |
} |
2705 | 3032 |
} |
2706 | 3033 |
|
2707 | 3034 |
|
2708 |
template<typename T> |
|
2709 |
inline static int SortUsingEntryValue(const T* x, const T* y) { |
|
2710 |
uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value); |
|
2711 |
uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value); |
|
2712 |
if (x_uint > y_uint) { |
|
2713 |
return 1; |
|
2714 |
} else if (x_uint == y_uint) { |
|
2715 |
return 0; |
|
2716 |
} else { |
|
2717 |
return -1; |
|
2718 |
} |
|
2719 |
} |
|
2720 |
|
|
2721 |
|
|
2722 |
void HeapSnapshotJSONSerializer::SortHashMap( |
|
2723 |
HashMap* map, List<HashMap::Entry*>* sorted_entries) { |
|
2724 |
for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) |
|
2725 |
sorted_entries->Add(p); |
|
2726 |
sorted_entries->Sort(SortUsingEntryValue); |
|
2727 |
} |
|
2728 |
|
|
2729 | 3035 |
} } // namespace v8::internal |
Also available in: Unified diff