The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / heap-snapshot-generator.cc @ f230a1cf

History | View | Annotate | Download (102 KB)

1
// Copyright 2013 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27

    
28
#include "v8.h"
29

    
30
#include "heap-snapshot-generator-inl.h"
31

    
32
#include "allocation-tracker.h"
33
#include "heap-profiler.h"
34
#include "debug.h"
35
#include "types.h"
36

    
37
namespace v8 {
38
namespace internal {
39

    
40

    
41
HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to)
42
    : type_(type),
43
      from_index_(from),
44
      to_index_(to),
45
      name_(name) {
46
  ASSERT(type == kContextVariable
47
      || type == kProperty
48
      || type == kInternal
49
      || type == kShortcut);
50
}
51

    
52

    
53
HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to)
54
    : type_(type),
55
      from_index_(from),
56
      to_index_(to),
57
      index_(index) {
58
  ASSERT(type == kElement || type == kHidden || type == kWeak);
59
}
60

    
61

    
62
void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) {
63
  to_entry_ = &snapshot->entries()[to_index_];
64
}
65

    
66

    
67
const int HeapEntry::kNoEntry = -1;
68

    
69
HeapEntry::HeapEntry(HeapSnapshot* snapshot,
70
                     Type type,
71
                     const char* name,
72
                     SnapshotObjectId id,
73
                     int self_size)
74
    : type_(type),
75
      children_count_(0),
76
      children_index_(-1),
77
      self_size_(self_size),
78
      id_(id),
79
      snapshot_(snapshot),
80
      name_(name) { }
81

    
82

    
83
void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
84
                                  const char* name,
85
                                  HeapEntry* entry) {
86
  HeapGraphEdge edge(type, name, this->index(), entry->index());
87
  snapshot_->edges().Add(edge);
88
  ++children_count_;
89
}
90

    
91

    
92
void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
93
                                    int index,
94
                                    HeapEntry* entry) {
95
  HeapGraphEdge edge(type, index, this->index(), entry->index());
96
  snapshot_->edges().Add(edge);
97
  ++children_count_;
98
}
99

    
100

    
101
Handle<HeapObject> HeapEntry::GetHeapObject() {
102
  return snapshot_->collection()->FindHeapObjectById(id());
103
}
104

    
105

    
106
void HeapEntry::Print(
107
    const char* prefix, const char* edge_name, int max_depth, int indent) {
108
  STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
109
  OS::Print("%6d @%6u %*c %s%s: ",
110
            self_size(), id(), indent, ' ', prefix, edge_name);
111
  if (type() != kString) {
112
    OS::Print("%s %.40s\n", TypeAsString(), name_);
113
  } else {
114
    OS::Print("\"");
115
    const char* c = name_;
116
    while (*c && (c - name_) <= 40) {
117
      if (*c != '\n')
118
        OS::Print("%c", *c);
119
      else
120
        OS::Print("\\n");
121
      ++c;
122
    }
123
    OS::Print("\"\n");
124
  }
125
  if (--max_depth == 0) return;
126
  Vector<HeapGraphEdge*> ch = children();
127
  for (int i = 0; i < ch.length(); ++i) {
128
    HeapGraphEdge& edge = *ch[i];
129
    const char* edge_prefix = "";
130
    EmbeddedVector<char, 64> index;
131
    const char* edge_name = index.start();
132
    switch (edge.type()) {
133
      case HeapGraphEdge::kContextVariable:
134
        edge_prefix = "#";
135
        edge_name = edge.name();
136
        break;
137
      case HeapGraphEdge::kElement:
138
        OS::SNPrintF(index, "%d", edge.index());
139
        break;
140
      case HeapGraphEdge::kInternal:
141
        edge_prefix = "$";
142
        edge_name = edge.name();
143
        break;
144
      case HeapGraphEdge::kProperty:
145
        edge_name = edge.name();
146
        break;
147
      case HeapGraphEdge::kHidden:
148
        edge_prefix = "$";
149
        OS::SNPrintF(index, "%d", edge.index());
150
        break;
151
      case HeapGraphEdge::kShortcut:
152
        edge_prefix = "^";
153
        edge_name = edge.name();
154
        break;
155
      case HeapGraphEdge::kWeak:
156
        edge_prefix = "w";
157
        OS::SNPrintF(index, "%d", edge.index());
158
        break;
159
      default:
160
        OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
161
    }
162
    edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
163
  }
164
}
165

    
166

    
167
const char* HeapEntry::TypeAsString() {
168
  switch (type()) {
169
    case kHidden: return "/hidden/";
170
    case kObject: return "/object/";
171
    case kClosure: return "/closure/";
172
    case kString: return "/string/";
173
    case kCode: return "/code/";
174
    case kArray: return "/array/";
175
    case kRegExp: return "/regexp/";
176
    case kHeapNumber: return "/number/";
177
    case kNative: return "/native/";
178
    case kSynthetic: return "/synthetic/";
179
    case kConsString: return "/concatenated string/";
180
    case kSlicedString: return "/sliced string/";
181
    default: return "???";
182
  }
183
}
184

    
185

    
186
// It is very important to keep objects that form a heap snapshot
187
// as small as possible.
188
namespace {  // Avoid littering the global namespace.
189

    
190
template <size_t ptr_size> struct SnapshotSizeConstants;
191

    
192
template <> struct SnapshotSizeConstants<4> {
193
  static const int kExpectedHeapGraphEdgeSize = 12;
194
  static const int kExpectedHeapEntrySize = 24;
195
};
196

    
197
template <> struct SnapshotSizeConstants<8> {
198
  static const int kExpectedHeapGraphEdgeSize = 24;
199
  static const int kExpectedHeapEntrySize = 32;
200
};
201

    
202
}  // namespace
203

    
204
HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
205
                           const char* title,
206
                           unsigned uid)
207
    : collection_(collection),
208
      title_(title),
209
      uid_(uid),
210
      root_index_(HeapEntry::kNoEntry),
211
      gc_roots_index_(HeapEntry::kNoEntry),
212
      natives_root_index_(HeapEntry::kNoEntry),
213
      max_snapshot_js_object_id_(0) {
214
  STATIC_CHECK(
215
      sizeof(HeapGraphEdge) ==
216
      SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
217
  STATIC_CHECK(
218
      sizeof(HeapEntry) ==
219
      SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
220
  for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
221
    gc_subroot_indexes_[i] = HeapEntry::kNoEntry;
222
  }
223
}
224

    
225

    
226
void HeapSnapshot::Delete() {
227
  collection_->RemoveSnapshot(this);
228
  delete this;
229
}
230

    
231

    
232
void HeapSnapshot::RememberLastJSObjectId() {
233
  max_snapshot_js_object_id_ = collection_->last_assigned_id();
234
}
235

    
236

    
237
HeapEntry* HeapSnapshot::AddRootEntry() {
238
  ASSERT(root_index_ == HeapEntry::kNoEntry);
239
  ASSERT(entries_.is_empty());  // Root entry must be the first one.
240
  HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
241
                              "",
242
                              HeapObjectsMap::kInternalRootObjectId,
243
                              0);
244
  root_index_ = entry->index();
245
  ASSERT(root_index_ == 0);
246
  return entry;
247
}
248

    
249

    
250
HeapEntry* HeapSnapshot::AddGcRootsEntry() {
251
  ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
252
  HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
253
                              "(GC roots)",
254
                              HeapObjectsMap::kGcRootsObjectId,
255
                              0);
256
  gc_roots_index_ = entry->index();
257
  return entry;
258
}
259

    
260

    
261
HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
262
  ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
263
  ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
264
  HeapEntry* entry = AddEntry(
265
      HeapEntry::kSynthetic,
266
      VisitorSynchronization::kTagNames[tag],
267
      HeapObjectsMap::GetNthGcSubrootId(tag),
268
      0);
269
  gc_subroot_indexes_[tag] = entry->index();
270
  return entry;
271
}
272

    
273

    
274
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
275
                                  const char* name,
276
                                  SnapshotObjectId id,
277
                                  int size) {
278
  HeapEntry entry(this, type, name, id, size);
279
  entries_.Add(entry);
280
  return &entries_.last();
281
}
282

    
283

    
284
void HeapSnapshot::FillChildren() {
285
  ASSERT(children().is_empty());
286
  children().Allocate(edges().length());
287
  int children_index = 0;
288
  for (int i = 0; i < entries().length(); ++i) {
289
    HeapEntry* entry = &entries()[i];
290
    children_index = entry->set_children_index(children_index);
291
  }
292
  ASSERT(edges().length() == children_index);
293
  for (int i = 0; i < edges().length(); ++i) {
294
    HeapGraphEdge* edge = &edges()[i];
295
    edge->ReplaceToIndexWithEntry(this);
296
    edge->from()->add_child(edge);
297
  }
298
}
299

    
300

    
301
class FindEntryById {
302
 public:
303
  explicit FindEntryById(SnapshotObjectId id) : id_(id) { }
304
  int operator()(HeapEntry* const* entry) {
305
    if ((*entry)->id() == id_) return 0;
306
    return (*entry)->id() < id_ ? -1 : 1;
307
  }
308
 private:
309
  SnapshotObjectId id_;
310
};
311

    
312

    
313
HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
314
  List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
315
  // Perform a binary search by id.
316
  int index = SortedListBSearch(*entries_by_id, FindEntryById(id));
317
  if (index == -1)
318
    return NULL;
319
  return entries_by_id->at(index);
320
}
321

    
322

    
323
template<class T>
324
static int SortByIds(const T* entry1_ptr,
325
                     const T* entry2_ptr) {
326
  if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
327
  return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
328
}
329

    
330

    
331
List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
332
  if (sorted_entries_.is_empty()) {
333
    sorted_entries_.Allocate(entries_.length());
334
    for (int i = 0; i < entries_.length(); ++i) {
335
      sorted_entries_[i] = &entries_[i];
336
    }
337
    sorted_entries_.Sort(SortByIds);
338
  }
339
  return &sorted_entries_;
340
}
341

    
342

    
343
void HeapSnapshot::Print(int max_depth) {
344
  root()->Print("", "", max_depth, 0);
345
}
346

    
347

    
348
template<typename T, class P>
349
static size_t GetMemoryUsedByList(const List<T, P>& list) {
350
  return list.length() * sizeof(T) + sizeof(list);
351
}
352

    
353

    
354
size_t HeapSnapshot::RawSnapshotSize() const {
355
  return
356
      sizeof(*this) +
357
      GetMemoryUsedByList(entries_) +
358
      GetMemoryUsedByList(edges_) +
359
      GetMemoryUsedByList(children_) +
360
      GetMemoryUsedByList(sorted_entries_);
361
}
362

    
363

    
364
// We split IDs on evens for embedder objects (see
365
// HeapObjectsMap::GenerateId) and odds for native objects.
366
const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
367
const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
368
    HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
369
const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
370
    HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
371
const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
372
    HeapObjectsMap::kGcRootsFirstSubrootId +
373
    VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
374

    
375

    
376
static bool AddressesMatch(void* key1, void* key2) {
377
  return key1 == key2;
378
}
379

    
380

    
381
HeapObjectsMap::HeapObjectsMap(Heap* heap)
382
    : next_id_(kFirstAvailableObjectId),
383
      entries_map_(AddressesMatch),
384
      heap_(heap) {
385
  // This dummy element solves a problem with entries_map_.
386
  // When we do lookup in HashMap we see no difference between two cases:
387
  // it has an entry with NULL as the value or it has created
388
  // a new entry on the fly with NULL as the default value.
389
  // With such dummy element we have a guaranty that all entries_map_ entries
390
  // will have the value field grater than 0.
391
  // This fact is using in MoveObject method.
392
  entries_.Add(EntryInfo(0, NULL, 0));
393
}
394

    
395

    
396
void HeapObjectsMap::SnapshotGenerationFinished() {
397
  RemoveDeadEntries();
398
}
399

    
400

    
401
void HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
402
  ASSERT(to != NULL);
403
  ASSERT(from != NULL);
404
  if (from == to) return;
405
  void* from_value = entries_map_.Remove(from, ComputePointerHash(from));
406
  if (from_value == NULL) {
407
    // It may occur that some untracked object moves to an address X and there
408
    // is a tracked object at that address. In this case we should remove the
409
    // entry as we know that the object has died.
410
    void* to_value = entries_map_.Remove(to, ComputePointerHash(to));
411
    if (to_value != NULL) {
412
      int to_entry_info_index =
413
          static_cast<int>(reinterpret_cast<intptr_t>(to_value));
414
      entries_.at(to_entry_info_index).addr = NULL;
415
    }
416
  } else {
417
    HashMap::Entry* to_entry = entries_map_.Lookup(to, ComputePointerHash(to),
418
                                                   true);
419
    if (to_entry->value != NULL) {
420
      // We found the existing entry with to address for an old object.
421
      // Without this operation we will have two EntryInfo's with the same
422
      // value in addr field. It is bad because later at RemoveDeadEntries
423
      // one of this entry will be removed with the corresponding entries_map_
424
      // entry.
425
      int to_entry_info_index =
426
          static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
427
      entries_.at(to_entry_info_index).addr = NULL;
428
    }
429
    int from_entry_info_index =
430
        static_cast<int>(reinterpret_cast<intptr_t>(from_value));
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;
443
    to_entry->value = from_value;
444
  }
445
}
446

    
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

    
465
SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
466
  HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
467
                                              false);
468
  if (entry == NULL) return 0;
469
  int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
470
  EntryInfo& entry_info = entries_.at(entry_index);
471
  ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
472
  return entry_info.id;
473
}
474

    
475

    
476
SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
477
                                                unsigned int size,
478
                                                bool accessed) {
479
  ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
480
  HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
481
                                              true);
482
  if (entry->value != NULL) {
483
    int entry_index =
484
        static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
485
    EntryInfo& entry_info = entries_.at(entry_index);
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
    }
493
    entry_info.size = size;
494
    return entry_info.id;
495
  }
496
  entry->value = reinterpret_cast<void*>(entries_.length());
497
  SnapshotObjectId id = next_id_;
498
  next_id_ += kObjectIdStep;
499
  entries_.Add(EntryInfo(id, addr, size, accessed));
500
  ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
501
  return id;
502
}
503

    
504

    
505
void HeapObjectsMap::StopHeapObjectsTracking() {
506
  time_intervals_.Clear();
507
}
508

    
509

    
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
  }
515
  heap_->CollectAllGarbage(Heap::kMakeHeapIterableMask,
516
                          "HeapSnapshotsCollection::UpdateHeapObjectsMap");
517
  HeapIterator iterator(heap_);
518
  for (HeapObject* obj = iterator.next();
519
       obj != NULL;
520
       obj = iterator.next()) {
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
    }
528
  }
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;
645
}
646

    
647

    
648
SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
649
  UpdateHeapObjectsMap();
650
  time_intervals_.Add(TimeInterval(next_id_));
651
  int prefered_chunk_size = stream->GetChunkSize();
652
  List<v8::HeapStatsUpdate> stats_buffer;
653
  ASSERT(!entries_.is_empty());
654
  EntryInfo* entry_info = &entries_.first();
655
  EntryInfo* end_entry_info = &entries_.last() + 1;
656
  for (int time_interval_index = 0;
657
       time_interval_index < time_intervals_.length();
658
       ++time_interval_index) {
659
    TimeInterval& time_interval = time_intervals_[time_interval_index];
660
    SnapshotObjectId time_interval_id = time_interval.id;
661
    uint32_t entries_size = 0;
662
    EntryInfo* start_entry_info = entry_info;
663
    while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
664
      entries_size += entry_info->size;
665
      ++entry_info;
666
    }
667
    uint32_t entries_count =
668
        static_cast<uint32_t>(entry_info - start_entry_info);
669
    if (time_interval.count != entries_count ||
670
        time_interval.size != entries_size) {
671
      stats_buffer.Add(v8::HeapStatsUpdate(
672
          time_interval_index,
673
          time_interval.count = entries_count,
674
          time_interval.size = entries_size));
675
      if (stats_buffer.length() >= prefered_chunk_size) {
676
        OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
677
            &stats_buffer.first(), stats_buffer.length());
678
        if (result == OutputStream::kAbort) return last_assigned_id();
679
        stats_buffer.Clear();
680
      }
681
    }
682
  }
683
  ASSERT(entry_info == end_entry_info);
684
  if (!stats_buffer.is_empty()) {
685
    OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
686
        &stats_buffer.first(), stats_buffer.length());
687
    if (result == OutputStream::kAbort) return last_assigned_id();
688
  }
689
  stream->EndOfStream();
690
  return last_assigned_id();
691
}
692

    
693

    
694
void HeapObjectsMap::RemoveDeadEntries() {
695
  ASSERT(entries_.length() > 0 &&
696
         entries_.at(0).id == 0 &&
697
         entries_.at(0).addr == NULL);
698
  int first_free_entry = 1;
699
  for (int i = 1; i < entries_.length(); ++i) {
700
    EntryInfo& entry_info = entries_.at(i);
701
    if (entry_info.accessed) {
702
      if (first_free_entry != i) {
703
        entries_.at(first_free_entry) = entry_info;
704
      }
705
      entries_.at(first_free_entry).accessed = false;
706
      HashMap::Entry* entry = entries_map_.Lookup(
707
          entry_info.addr, ComputePointerHash(entry_info.addr), false);
708
      ASSERT(entry);
709
      entry->value = reinterpret_cast<void*>(first_free_entry);
710
      ++first_free_entry;
711
    } else {
712
      if (entry_info.addr) {
713
        entries_map_.Remove(entry_info.addr,
714
                            ComputePointerHash(entry_info.addr));
715
      }
716
    }
717
  }
718
  entries_.Rewind(first_free_entry);
719
  ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
720
         entries_map_.occupancy());
721
}
722

    
723

    
724
SnapshotObjectId HeapObjectsMap::GenerateId(Heap* heap,
725
                                            v8::RetainedObjectInfo* info) {
726
  SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
727
  const char* label = info->GetLabel();
728
  id ^= StringHasher::HashSequentialString(label,
729
                                           static_cast<int>(strlen(label)),
730
                                           heap->HashSeed());
731
  intptr_t element_count = info->GetElementCount();
732
  if (element_count != -1)
733
    id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
734
                             v8::internal::kZeroHashSeed);
735
  return id << 1;
736
}
737

    
738

    
739
size_t HeapObjectsMap::GetUsedMemorySize() const {
740
  return
741
      sizeof(*this) +
742
      sizeof(HashMap::Entry) * entries_map_.capacity() +
743
      GetMemoryUsedByList(entries_) +
744
      GetMemoryUsedByList(time_intervals_);
745
}
746

    
747

    
748
HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap)
749
    : is_tracking_objects_(false),
750
      names_(heap),
751
      ids_(heap),
752
      allocation_tracker_(NULL) {
753
}
754

    
755

    
756
static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
757
  delete *snapshot_ptr;
758
}
759

    
760

    
761
HeapSnapshotsCollection::~HeapSnapshotsCollection() {
762
  delete allocation_tracker_;
763
  snapshots_.Iterate(DeleteHeapSnapshot);
764
}
765

    
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

    
785
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
786
                                                   unsigned uid) {
787
  is_tracking_objects_ = true;  // Start watching for heap objects moves.
788
  return new HeapSnapshot(this, name, uid);
789
}
790

    
791

    
792
void HeapSnapshotsCollection::SnapshotGenerationFinished(
793
    HeapSnapshot* snapshot) {
794
  ids_.SnapshotGenerationFinished();
795
  if (snapshot != NULL) {
796
    snapshots_.Add(snapshot);
797
  }
798
}
799

    
800

    
801
void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
802
  snapshots_.RemoveElement(snapshot);
803
}
804

    
805

    
806
Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
807
    SnapshotObjectId id) {
808
  // First perform a full GC in order to avoid dead objects.
809
  heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
810
                          "HeapSnapshotsCollection::FindHeapObjectById");
811
  DisallowHeapAllocation no_allocation;
812
  HeapObject* object = NULL;
813
  HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable);
814
  // Make sure that object with the given id is still reachable.
815
  for (HeapObject* obj = iterator.next();
816
       obj != NULL;
817
       obj = iterator.next()) {
818
    if (ids_.FindEntry(obj->address()) == id) {
819
      ASSERT(object == NULL);
820
      object = obj;
821
      // Can't break -- kFilterUnreachable requires full heap traversal.
822
    }
823
  }
824
  return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
825
}
826

    
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

    
837
size_t HeapSnapshotsCollection::GetUsedMemorySize() const {
838
  size_t size = sizeof(*this);
839
  size += names_.GetUsedMemorySize();
840
  size += ids_.GetUsedMemorySize();
841
  size += GetMemoryUsedByList(snapshots_);
842
  for (int i = 0; i < snapshots_.length(); ++i) {
843
    size += snapshots_[i]->RawSnapshotSize();
844
  }
845
  return size;
846
}
847

    
848

    
849
HeapEntriesMap::HeapEntriesMap()
850
    : entries_(HeapThingsMatch) {
851
}
852

    
853

    
854
int HeapEntriesMap::Map(HeapThing thing) {
855
  HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
856
  if (cache_entry == NULL) return HeapEntry::kNoEntry;
857
  return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
858
}
859

    
860

    
861
void HeapEntriesMap::Pair(HeapThing thing, int entry) {
862
  HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
863
  ASSERT(cache_entry->value == NULL);
864
  cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
865
}
866

    
867

    
868
HeapObjectsSet::HeapObjectsSet()
869
    : entries_(HeapEntriesMap::HeapThingsMatch) {
870
}
871

    
872

    
873
void HeapObjectsSet::Clear() {
874
  entries_.Clear();
875
}
876

    
877

    
878
bool HeapObjectsSet::Contains(Object* obj) {
879
  if (!obj->IsHeapObject()) return false;
880
  HeapObject* object = HeapObject::cast(obj);
881
  return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL;
882
}
883

    
884

    
885
void HeapObjectsSet::Insert(Object* obj) {
886
  if (!obj->IsHeapObject()) return;
887
  HeapObject* object = HeapObject::cast(obj);
888
  entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
889
}
890

    
891

    
892
const char* HeapObjectsSet::GetTag(Object* obj) {
893
  HeapObject* object = HeapObject::cast(obj);
894
  HashMap::Entry* cache_entry =
895
      entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
896
  return cache_entry != NULL
897
      ? reinterpret_cast<const char*>(cache_entry->value)
898
      : NULL;
899
}
900

    
901

    
902
void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
903
  if (!obj->IsHeapObject()) return;
904
  HeapObject* object = HeapObject::cast(obj);
905
  HashMap::Entry* cache_entry =
906
      entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
907
  cache_entry->value = const_cast<char*>(tag);
908
}
909

    
910

    
911
HeapObject* const V8HeapExplorer::kInternalRootObject =
912
    reinterpret_cast<HeapObject*>(
913
        static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
914
HeapObject* const V8HeapExplorer::kGcRootsObject =
915
    reinterpret_cast<HeapObject*>(
916
        static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
917
HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
918
    reinterpret_cast<HeapObject*>(
919
        static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
920
HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
921
    reinterpret_cast<HeapObject*>(
922
        static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
923

    
924

    
925
V8HeapExplorer::V8HeapExplorer(
926
    HeapSnapshot* snapshot,
927
    SnapshottingProgressReportingInterface* progress,
928
    v8::HeapProfiler::ObjectNameResolver* resolver)
929
    : heap_(snapshot->collection()->heap()),
930
      snapshot_(snapshot),
931
      collection_(snapshot_->collection()),
932
      progress_(progress),
933
      filler_(NULL),
934
      global_object_name_resolver_(resolver) {
935
}
936

    
937

    
938
V8HeapExplorer::~V8HeapExplorer() {
939
}
940

    
941

    
942
HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
943
  return AddEntry(reinterpret_cast<HeapObject*>(ptr));
944
}
945

    
946

    
947
HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
948
  if (object == kInternalRootObject) {
949
    snapshot_->AddRootEntry();
950
    return snapshot_->root();
951
  } else if (object == kGcRootsObject) {
952
    HeapEntry* entry = snapshot_->AddGcRootsEntry();
953
    return entry;
954
  } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
955
    HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
956
    return entry;
957
  } else if (object->IsJSFunction()) {
958
    JSFunction* func = JSFunction::cast(object);
959
    SharedFunctionInfo* shared = func->shared();
960
    const char* name = shared->bound() ? "native_bind" :
961
        collection_->names()->GetName(String::cast(shared->name()));
962
    return AddEntry(object, HeapEntry::kClosure, name);
963
  } else if (object->IsJSRegExp()) {
964
    JSRegExp* re = JSRegExp::cast(object);
965
    return AddEntry(object,
966
                    HeapEntry::kRegExp,
967
                    collection_->names()->GetName(re->Pattern()));
968
  } else if (object->IsJSObject()) {
969
    const char* name = collection_->names()->GetName(
970
        GetConstructorName(JSObject::cast(object)));
971
    if (object->IsJSGlobalObject()) {
972
      const char* tag = objects_tags_.GetTag(object);
973
      if (tag != NULL) {
974
        name = collection_->names()->GetFormatted("%s / %s", name, tag);
975
      }
976
    }
977
    return AddEntry(object, HeapEntry::kObject, name);
978
  } else if (object->IsString()) {
979
    String* string = String::cast(object);
980
    if (string->IsConsString())
981
      return AddEntry(object,
982
                      HeapEntry::kConsString,
983
                      "(concatenated string)");
984
    if (string->IsSlicedString())
985
      return AddEntry(object,
986
                      HeapEntry::kSlicedString,
987
                      "(sliced string)");
988
    return AddEntry(object,
989
                    HeapEntry::kString,
990
                    collection_->names()->GetName(String::cast(object)));
991
  } else if (object->IsCode()) {
992
    return AddEntry(object, HeapEntry::kCode, "");
993
  } else if (object->IsSharedFunctionInfo()) {
994
    String* name = String::cast(SharedFunctionInfo::cast(object)->name());
995
    return AddEntry(object,
996
                    HeapEntry::kCode,
997
                    collection_->names()->GetName(name));
998
  } else if (object->IsScript()) {
999
    Object* name = Script::cast(object)->name();
1000
    return AddEntry(object,
1001
                    HeapEntry::kCode,
1002
                    name->IsString()
1003
                        ? collection_->names()->GetName(String::cast(name))
1004
                        : "");
1005
  } else if (object->IsNativeContext()) {
1006
    return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
1007
  } else if (object->IsContext()) {
1008
    return AddEntry(object, HeapEntry::kObject, "system / Context");
1009
  } else if (object->IsFixedArray() ||
1010
             object->IsFixedDoubleArray() ||
1011
             object->IsByteArray() ||
1012
             object->IsExternalArray()) {
1013
    return AddEntry(object, HeapEntry::kArray, "");
1014
  } else if (object->IsHeapNumber()) {
1015
    return AddEntry(object, HeapEntry::kHeapNumber, "number");
1016
  }
1017
  return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
1018
}
1019

    
1020

    
1021
HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1022
                                    HeapEntry::Type type,
1023
                                    const char* name) {
1024
  int object_size = object->Size();
1025
  SnapshotObjectId object_id =
1026
    collection_->GetObjectId(object->address(), object_size);
1027
  return snapshot_->AddEntry(type, name, object_id, object_size);
1028
}
1029

    
1030

    
1031
class GcSubrootsEnumerator : public ObjectVisitor {
1032
 public:
1033
  GcSubrootsEnumerator(
1034
      SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
1035
      : filler_(filler),
1036
        explorer_(explorer),
1037
        previous_object_count_(0),
1038
        object_count_(0) {
1039
  }
1040
  void VisitPointers(Object** start, Object** end) {
1041
    object_count_ += end - start;
1042
  }
1043
  void Synchronize(VisitorSynchronization::SyncTag tag) {
1044
    // Skip empty subroots.
1045
    if (previous_object_count_ != object_count_) {
1046
      previous_object_count_ = object_count_;
1047
      filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
1048
    }
1049
  }
1050
 private:
1051
  SnapshotFillerInterface* filler_;
1052
  V8HeapExplorer* explorer_;
1053
  intptr_t previous_object_count_;
1054
  intptr_t object_count_;
1055
};
1056

    
1057

    
1058
void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
1059
  filler->AddEntry(kInternalRootObject, this);
1060
  filler->AddEntry(kGcRootsObject, this);
1061
  GcSubrootsEnumerator enumerator(filler, this);
1062
  heap_->IterateRoots(&enumerator, VISIT_ALL);
1063
}
1064

    
1065

    
1066
const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
1067
  switch (object->map()->instance_type()) {
1068
    case MAP_TYPE:
1069
      switch (Map::cast(object)->instance_type()) {
1070
#define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
1071
        case instance_type: return "system / Map (" #Name ")";
1072
      STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
1073
#undef MAKE_STRING_MAP_CASE
1074
        default: return "system / Map";
1075
      }
1076
    case CELL_TYPE: return "system / Cell";
1077
    case PROPERTY_CELL_TYPE: return "system / PropertyCell";
1078
    case FOREIGN_TYPE: return "system / Foreign";
1079
    case ODDBALL_TYPE: return "system / Oddball";
1080
#define MAKE_STRUCT_CASE(NAME, Name, name) \
1081
    case NAME##_TYPE: return "system / "#Name;
1082
  STRUCT_LIST(MAKE_STRUCT_CASE)
1083
#undef MAKE_STRUCT_CASE
1084
    default: return "system";
1085
  }
1086
}
1087

    
1088

    
1089
int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
1090
  int objects_count = 0;
1091
  for (HeapObject* obj = iterator->next();
1092
       obj != NULL;
1093
       obj = iterator->next()) {
1094
    objects_count++;
1095
  }
1096
  return objects_count;
1097
}
1098

    
1099

    
1100
class IndexedReferencesExtractor : public ObjectVisitor {
1101
 public:
1102
  IndexedReferencesExtractor(V8HeapExplorer* generator,
1103
                             HeapObject* parent_obj,
1104
                             int parent)
1105
      : generator_(generator),
1106
        parent_obj_(parent_obj),
1107
        parent_(parent),
1108
        next_index_(0) {
1109
  }
1110
  void VisitCodeEntry(Address entry_address) {
1111
     Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
1112
     generator_->SetInternalReference(parent_obj_, parent_, "code", code);
1113
     generator_->TagObject(code, "(code)");
1114
  }
1115
  void VisitPointers(Object** start, Object** end) {
1116
    for (Object** p = start; p < end; p++) {
1117
      if (CheckVisitedAndUnmark(p)) continue;
1118
      generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
1119
    }
1120
  }
1121
  static void MarkVisitedField(HeapObject* obj, int offset) {
1122
    if (offset < 0) return;
1123
    Address field = obj->address() + offset;
1124
    ASSERT(!Memory::Object_at(field)->IsFailure());
1125
    ASSERT(Memory::Object_at(field)->IsHeapObject());
1126
    *field |= kFailureTag;
1127
  }
1128

    
1129
 private:
1130
  bool CheckVisitedAndUnmark(Object** field) {
1131
    if ((*field)->IsFailure()) {
1132
      intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
1133
      *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
1134
      ASSERT((*field)->IsHeapObject());
1135
      return true;
1136
    }
1137
    return false;
1138
  }
1139
  V8HeapExplorer* generator_;
1140
  HeapObject* parent_obj_;
1141
  int parent_;
1142
  int next_index_;
1143
};
1144

    
1145

    
1146
void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
1147
  HeapEntry* heap_entry = GetEntry(obj);
1148
  if (heap_entry == NULL) return;  // No interest in this object.
1149
  int entry = heap_entry->index();
1150

    
1151
  if (obj->IsJSGlobalProxy()) {
1152
    ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
1153
  } else if (obj->IsJSObject()) {
1154
    ExtractJSObjectReferences(entry, JSObject::cast(obj));
1155
  } else if (obj->IsString()) {
1156
    ExtractStringReferences(entry, String::cast(obj));
1157
  } else if (obj->IsContext()) {
1158
    ExtractContextReferences(entry, Context::cast(obj));
1159
  } else if (obj->IsMap()) {
1160
    ExtractMapReferences(entry, Map::cast(obj));
1161
  } else if (obj->IsSharedFunctionInfo()) {
1162
    ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
1163
  } else if (obj->IsScript()) {
1164
    ExtractScriptReferences(entry, Script::cast(obj));
1165
  } else if (obj->IsAccessorPair()) {
1166
    ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
1167
  } else if (obj->IsCodeCache()) {
1168
    ExtractCodeCacheReferences(entry, CodeCache::cast(obj));
1169
  } else if (obj->IsCode()) {
1170
    ExtractCodeReferences(entry, Code::cast(obj));
1171
  } else if (obj->IsCell()) {
1172
    ExtractCellReferences(entry, Cell::cast(obj));
1173
  } else if (obj->IsPropertyCell()) {
1174
    ExtractPropertyCellReferences(entry, PropertyCell::cast(obj));
1175
  } else if (obj->IsAllocationSite()) {
1176
    ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
1177
  }
1178
  SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
1179

    
1180
  // Extract unvisited fields as hidden references and restore tags
1181
  // of visited fields.
1182
  IndexedReferencesExtractor refs_extractor(this, obj, entry);
1183
  obj->Iterate(&refs_extractor);
1184
}
1185

    
1186

    
1187
void V8HeapExplorer::ExtractJSGlobalProxyReferences(
1188
    int entry, JSGlobalProxy* proxy) {
1189
  SetInternalReference(proxy, entry,
1190
                       "native_context", proxy->native_context(),
1191
                       JSGlobalProxy::kNativeContextOffset);
1192
}
1193

    
1194

    
1195
void V8HeapExplorer::ExtractJSObjectReferences(
1196
    int entry, JSObject* js_obj) {
1197
  HeapObject* obj = js_obj;
1198
  ExtractClosureReferences(js_obj, entry);
1199
  ExtractPropertyReferences(js_obj, entry);
1200
  ExtractElementReferences(js_obj, entry);
1201
  ExtractInternalReferences(js_obj, entry);
1202
  SetPropertyReference(
1203
      obj, entry, heap_->proto_string(), js_obj->GetPrototype());
1204
  if (obj->IsJSFunction()) {
1205
    JSFunction* js_fun = JSFunction::cast(js_obj);
1206
    Object* proto_or_map = js_fun->prototype_or_initial_map();
1207
    if (!proto_or_map->IsTheHole()) {
1208
      if (!proto_or_map->IsMap()) {
1209
        SetPropertyReference(
1210
            obj, entry,
1211
            heap_->prototype_string(), proto_or_map,
1212
            NULL,
1213
            JSFunction::kPrototypeOrInitialMapOffset);
1214
      } else {
1215
        SetPropertyReference(
1216
            obj, entry,
1217
            heap_->prototype_string(), js_fun->prototype());
1218
        SetInternalReference(
1219
            obj, entry, "initial_map", proto_or_map,
1220
            JSFunction::kPrototypeOrInitialMapOffset);
1221
      }
1222
    }
1223
    SharedFunctionInfo* shared_info = js_fun->shared();
1224
    // JSFunction has either bindings or literals and never both.
1225
    bool bound = shared_info->bound();
1226
    TagObject(js_fun->literals_or_bindings(),
1227
              bound ? "(function bindings)" : "(function literals)");
1228
    SetInternalReference(js_fun, entry,
1229
                         bound ? "bindings" : "literals",
1230
                         js_fun->literals_or_bindings(),
1231
                         JSFunction::kLiteralsOffset);
1232
    TagObject(shared_info, "(shared function info)");
1233
    SetInternalReference(js_fun, entry,
1234
                         "shared", shared_info,
1235
                         JSFunction::kSharedFunctionInfoOffset);
1236
    TagObject(js_fun->context(), "(context)");
1237
    SetInternalReference(js_fun, entry,
1238
                         "context", js_fun->context(),
1239
                         JSFunction::kContextOffset);
1240
    for (int i = JSFunction::kNonWeakFieldsEndOffset;
1241
         i < JSFunction::kSize;
1242
         i += kPointerSize) {
1243
      SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
1244
    }
1245
  } else if (obj->IsGlobalObject()) {
1246
    GlobalObject* global_obj = GlobalObject::cast(obj);
1247
    SetInternalReference(global_obj, entry,
1248
                         "builtins", global_obj->builtins(),
1249
                         GlobalObject::kBuiltinsOffset);
1250
    SetInternalReference(global_obj, entry,
1251
                         "native_context", global_obj->native_context(),
1252
                         GlobalObject::kNativeContextOffset);
1253
    SetInternalReference(global_obj, entry,
1254
                         "global_receiver", global_obj->global_receiver(),
1255
                         GlobalObject::kGlobalReceiverOffset);
1256
  }
1257
  TagObject(js_obj->properties(), "(object properties)");
1258
  SetInternalReference(obj, entry,
1259
                       "properties", js_obj->properties(),
1260
                       JSObject::kPropertiesOffset);
1261
  TagObject(js_obj->elements(), "(object elements)");
1262
  SetInternalReference(obj, entry,
1263
                       "elements", js_obj->elements(),
1264
                       JSObject::kElementsOffset);
1265
}
1266

    
1267

    
1268
void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
1269
  if (string->IsConsString()) {
1270
    ConsString* cs = ConsString::cast(string);
1271
    SetInternalReference(cs, entry, "first", cs->first(),
1272
                         ConsString::kFirstOffset);
1273
    SetInternalReference(cs, entry, "second", cs->second(),
1274
                         ConsString::kSecondOffset);
1275
  } else if (string->IsSlicedString()) {
1276
    SlicedString* ss = SlicedString::cast(string);
1277
    SetInternalReference(ss, entry, "parent", ss->parent(),
1278
                         SlicedString::kParentOffset);
1279
  }
1280
}
1281

    
1282

    
1283
void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
1284
  if (context == context->declaration_context()) {
1285
    ScopeInfo* scope_info = context->closure()->shared()->scope_info();
1286
    // Add context allocated locals.
1287
    int context_locals = scope_info->ContextLocalCount();
1288
    for (int i = 0; i < context_locals; ++i) {
1289
      String* local_name = scope_info->ContextLocalName(i);
1290
      int idx = Context::MIN_CONTEXT_SLOTS + i;
1291
      SetContextReference(context, entry, local_name, context->get(idx),
1292
                          Context::OffsetOfElementAt(idx));
1293
    }
1294
    if (scope_info->HasFunctionName()) {
1295
      String* name = scope_info->FunctionName();
1296
      VariableMode mode;
1297
      int idx = scope_info->FunctionContextSlotIndex(name, &mode);
1298
      if (idx >= 0) {
1299
        SetContextReference(context, entry, name, context->get(idx),
1300
                            Context::OffsetOfElementAt(idx));
1301
      }
1302
    }
1303
  }
1304

    
1305
#define EXTRACT_CONTEXT_FIELD(index, type, name) \
1306
  SetInternalReference(context, entry, #name, context->get(Context::index), \
1307
      FixedArray::OffsetOfElementAt(Context::index));
1308
  EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure);
1309
  EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous);
1310
  EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
1311
  EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global);
1312
  if (context->IsNativeContext()) {
1313
    TagObject(context->jsfunction_result_caches(),
1314
              "(context func. result caches)");
1315
    TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1316
    TagObject(context->runtime_context(), "(runtime context)");
1317
    TagObject(context->embedder_data(), "(context data)");
1318
    NATIVE_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD);
1319
#undef EXTRACT_CONTEXT_FIELD
1320
    for (int i = Context::FIRST_WEAK_SLOT;
1321
         i < Context::NATIVE_CONTEXT_SLOTS;
1322
         ++i) {
1323
      SetWeakReference(context, entry, i, context->get(i),
1324
          FixedArray::OffsetOfElementAt(i));
1325
    }
1326
  }
1327
}
1328

    
1329

    
1330
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
1331
  if (map->HasTransitionArray()) {
1332
    TransitionArray* transitions = map->transitions();
1333
    int transitions_entry = GetEntry(transitions)->index();
1334
    Object* back_pointer = transitions->back_pointer_storage();
1335
    TagObject(back_pointer, "(back pointer)");
1336
    SetInternalReference(transitions, transitions_entry,
1337
                         "back_pointer", back_pointer);
1338
    TagObject(transitions, "(transition array)");
1339
    SetInternalReference(map, entry,
1340
                         "transitions", transitions,
1341
                         Map::kTransitionsOrBackPointerOffset);
1342
  } else {
1343
    Object* back_pointer = map->GetBackPointer();
1344
    TagObject(back_pointer, "(back pointer)");
1345
    SetInternalReference(map, entry,
1346
                         "back_pointer", back_pointer,
1347
                         Map::kTransitionsOrBackPointerOffset);
1348
  }
1349
  DescriptorArray* descriptors = map->instance_descriptors();
1350
  TagObject(descriptors, "(map descriptors)");
1351
  SetInternalReference(map, entry,
1352
                       "descriptors", descriptors,
1353
                       Map::kDescriptorsOffset);
1354

    
1355
  SetInternalReference(map, entry,
1356
                       "code_cache", map->code_cache(),
1357
                       Map::kCodeCacheOffset);
1358
  SetInternalReference(map, entry,
1359
                       "prototype", map->prototype(), Map::kPrototypeOffset);
1360
  SetInternalReference(map, entry,
1361
                       "constructor", map->constructor(),
1362
                       Map::kConstructorOffset);
1363
  TagObject(map->dependent_code(), "(dependent code)");
1364
  SetInternalReference(map, entry,
1365
                       "dependent_code", map->dependent_code(),
1366
                       Map::kDependentCodeOffset);
1367
}
1368

    
1369

    
1370
void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
1371
    int entry, SharedFunctionInfo* shared) {
1372
  HeapObject* obj = shared;
1373
  SetInternalReference(obj, entry,
1374
                       "name", shared->name(),
1375
                       SharedFunctionInfo::kNameOffset);
1376
  TagObject(shared->code(), "(code)");
1377
  SetInternalReference(obj, entry,
1378
                       "code", shared->code(),
1379
                       SharedFunctionInfo::kCodeOffset);
1380
  TagObject(shared->scope_info(), "(function scope info)");
1381
  SetInternalReference(obj, entry,
1382
                       "scope_info", shared->scope_info(),
1383
                       SharedFunctionInfo::kScopeInfoOffset);
1384
  SetInternalReference(obj, entry,
1385
                       "instance_class_name", shared->instance_class_name(),
1386
                       SharedFunctionInfo::kInstanceClassNameOffset);
1387
  SetInternalReference(obj, entry,
1388
                       "script", shared->script(),
1389
                       SharedFunctionInfo::kScriptOffset);
1390
  TagObject(shared->construct_stub(), "(code)");
1391
  SetInternalReference(obj, entry,
1392
                       "construct_stub", shared->construct_stub(),
1393
                       SharedFunctionInfo::kConstructStubOffset);
1394
  SetInternalReference(obj, entry,
1395
                       "function_data", shared->function_data(),
1396
                       SharedFunctionInfo::kFunctionDataOffset);
1397
  SetInternalReference(obj, entry,
1398
                       "debug_info", shared->debug_info(),
1399
                       SharedFunctionInfo::kDebugInfoOffset);
1400
  SetInternalReference(obj, entry,
1401
                       "inferred_name", shared->inferred_name(),
1402
                       SharedFunctionInfo::kInferredNameOffset);
1403
  SetWeakReference(obj, entry,
1404
                   1, shared->initial_map(),
1405
                   SharedFunctionInfo::kInitialMapOffset);
1406
}
1407

    
1408

    
1409
void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) {
1410
  HeapObject* obj = script;
1411
  SetInternalReference(obj, entry,
1412
                       "source", script->source(),
1413
                       Script::kSourceOffset);
1414
  SetInternalReference(obj, entry,
1415
                       "name", script->name(),
1416
                       Script::kNameOffset);
1417
  SetInternalReference(obj, entry,
1418
                       "data", script->data(),
1419
                       Script::kDataOffset);
1420
  SetInternalReference(obj, entry,
1421
                       "context_data", script->context_data(),
1422
                       Script::kContextOffset);
1423
  TagObject(script->line_ends(), "(script line ends)");
1424
  SetInternalReference(obj, entry,
1425
                       "line_ends", script->line_ends(),
1426
                       Script::kLineEndsOffset);
1427
}
1428

    
1429

    
1430
void V8HeapExplorer::ExtractAccessorPairReferences(
1431
    int entry, AccessorPair* accessors) {
1432
  SetInternalReference(accessors, entry, "getter", accessors->getter(),
1433
                       AccessorPair::kGetterOffset);
1434
  SetInternalReference(accessors, entry, "setter", accessors->setter(),
1435
                       AccessorPair::kSetterOffset);
1436
}
1437

    
1438

    
1439
void V8HeapExplorer::ExtractCodeCacheReferences(
1440
    int entry, CodeCache* code_cache) {
1441
  TagObject(code_cache->default_cache(), "(default code cache)");
1442
  SetInternalReference(code_cache, entry,
1443
                       "default_cache", code_cache->default_cache(),
1444
                       CodeCache::kDefaultCacheOffset);
1445
  TagObject(code_cache->normal_type_cache(), "(code type cache)");
1446
  SetInternalReference(code_cache, entry,
1447
                       "type_cache", code_cache->normal_type_cache(),
1448
                       CodeCache::kNormalTypeCacheOffset);
1449
}
1450

    
1451

    
1452
void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) {
1453
  TagObject(code->relocation_info(), "(code relocation info)");
1454
  SetInternalReference(code, entry,
1455
                       "relocation_info", code->relocation_info(),
1456
                       Code::kRelocationInfoOffset);
1457
  SetInternalReference(code, entry,
1458
                       "handler_table", code->handler_table(),
1459
                       Code::kHandlerTableOffset);
1460
  TagObject(code->deoptimization_data(), "(code deopt data)");
1461
  SetInternalReference(code, entry,
1462
                       "deoptimization_data", code->deoptimization_data(),
1463
                       Code::kDeoptimizationDataOffset);
1464
  if (code->kind() == Code::FUNCTION) {
1465
    SetInternalReference(code, entry,
1466
                         "type_feedback_info", code->type_feedback_info(),
1467
                         Code::kTypeFeedbackInfoOffset);
1468
  }
1469
  SetInternalReference(code, entry,
1470
                       "gc_metadata", code->gc_metadata(),
1471
                       Code::kGCMetadataOffset);
1472
}
1473

    
1474

    
1475
void V8HeapExplorer::ExtractCellReferences(int entry, Cell* cell) {
1476
  SetInternalReference(cell, entry, "value", cell->value(), Cell::kValueOffset);
1477
}
1478

    
1479

    
1480
void V8HeapExplorer::ExtractPropertyCellReferences(int entry,
1481
                                                   PropertyCell* cell) {
1482
  ExtractCellReferences(entry, cell);
1483
  SetInternalReference(cell, entry, "type", cell->type(),
1484
                       PropertyCell::kTypeOffset);
1485
  SetInternalReference(cell, entry, "dependent_code", cell->dependent_code(),
1486
                       PropertyCell::kDependentCodeOffset);
1487
}
1488

    
1489

    
1490
void V8HeapExplorer::ExtractAllocationSiteReferences(int entry,
1491
                                                     AllocationSite* site) {
1492
  SetInternalReference(site, entry, "transition_info", site->transition_info(),
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);
1498
}
1499

    
1500

    
1501
void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
1502
  if (!js_obj->IsJSFunction()) return;
1503

    
1504
  JSFunction* func = JSFunction::cast(js_obj);
1505
  if (func->shared()->bound()) {
1506
    FixedArray* bindings = func->function_bindings();
1507
    SetNativeBindReference(js_obj, entry, "bound_this",
1508
                           bindings->get(JSFunction::kBoundThisIndex));
1509
    SetNativeBindReference(js_obj, entry, "bound_function",
1510
                           bindings->get(JSFunction::kBoundFunctionIndex));
1511
    for (int i = JSFunction::kBoundArgumentsStartIndex;
1512
         i < bindings->length(); i++) {
1513
      const char* reference_name = collection_->names()->GetFormatted(
1514
          "bound_argument_%d",
1515
          i - JSFunction::kBoundArgumentsStartIndex);
1516
      SetNativeBindReference(js_obj, entry, reference_name,
1517
                             bindings->get(i));
1518
    }
1519
  }
1520
}
1521

    
1522

    
1523
void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
1524
  if (js_obj->HasFastProperties()) {
1525
    DescriptorArray* descs = js_obj->map()->instance_descriptors();
1526
    int real_size = js_obj->map()->NumberOfOwnDescriptors();
1527
    for (int i = 0; i < real_size; i++) {
1528
      switch (descs->GetType(i)) {
1529
        case FIELD: {
1530
          int index = descs->GetFieldIndex(i);
1531

    
1532
          Name* k = descs->GetKey(i);
1533
          if (index < js_obj->map()->inobject_properties()) {
1534
            Object* value = js_obj->InObjectPropertyAt(index);
1535
            if (k != heap_->hidden_string()) {
1536
              SetPropertyReference(
1537
                  js_obj, entry,
1538
                  k, value,
1539
                  NULL,
1540
                  js_obj->GetInObjectPropertyOffset(index));
1541
            } else {
1542
              TagObject(value, "(hidden properties)");
1543
              SetInternalReference(
1544
                  js_obj, entry,
1545
                  "hidden_properties", value,
1546
                  js_obj->GetInObjectPropertyOffset(index));
1547
            }
1548
          } else {
1549
            Object* value = js_obj->RawFastPropertyAt(index);
1550
            if (k != heap_->hidden_string()) {
1551
              SetPropertyReference(js_obj, entry, k, value);
1552
            } else {
1553
              TagObject(value, "(hidden properties)");
1554
              SetInternalReference(js_obj, entry, "hidden_properties", value);
1555
            }
1556
          }
1557
          break;
1558
        }
1559
        case CONSTANT:
1560
          SetPropertyReference(
1561
              js_obj, entry,
1562
              descs->GetKey(i), descs->GetConstant(i));
1563
          break;
1564
        case CALLBACKS:
1565
          ExtractAccessorPairProperty(
1566
              js_obj, entry,
1567
              descs->GetKey(i), descs->GetValue(i));
1568
          break;
1569
        case NORMAL:  // only in slow mode
1570
        case HANDLER:  // only in lookup results, not in descriptors
1571
        case INTERCEPTOR:  // only in lookup results, not in descriptors
1572
          break;
1573
        case TRANSITION:
1574
        case NONEXISTENT:
1575
          UNREACHABLE();
1576
          break;
1577
      }
1578
    }
1579
  } else {
1580
    NameDictionary* dictionary = js_obj->property_dictionary();
1581
    int length = dictionary->Capacity();
1582
    for (int i = 0; i < length; ++i) {
1583
      Object* k = dictionary->KeyAt(i);
1584
      if (dictionary->IsKey(k)) {
1585
        Object* target = dictionary->ValueAt(i);
1586
        // We assume that global objects can only have slow properties.
1587
        Object* value = target->IsPropertyCell()
1588
            ? PropertyCell::cast(target)->value()
1589
            : target;
1590
        if (k == heap_->hidden_string()) {
1591
          TagObject(value, "(hidden properties)");
1592
          SetInternalReference(js_obj, entry, "hidden_properties", value);
1593
          continue;
1594
        }
1595
        if (ExtractAccessorPairProperty(js_obj, entry, k, value)) continue;
1596
        SetPropertyReference(js_obj, entry, String::cast(k), value);
1597
      }
1598
    }
1599
  }
1600
}
1601

    
1602

    
1603
bool V8HeapExplorer::ExtractAccessorPairProperty(
1604
    JSObject* js_obj, int entry, Object* key, Object* callback_obj) {
1605
  if (!callback_obj->IsAccessorPair()) return false;
1606
  AccessorPair* accessors = AccessorPair::cast(callback_obj);
1607
  Object* getter = accessors->getter();
1608
  if (!getter->IsOddball()) {
1609
    SetPropertyReference(js_obj, entry, String::cast(key), getter, "get %s");
1610
  }
1611
  Object* setter = accessors->setter();
1612
  if (!setter->IsOddball()) {
1613
    SetPropertyReference(js_obj, entry, String::cast(key), setter, "set %s");
1614
  }
1615
  return true;
1616
}
1617

    
1618

    
1619
void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) {
1620
  if (js_obj->HasFastObjectElements()) {
1621
    FixedArray* elements = FixedArray::cast(js_obj->elements());
1622
    int length = js_obj->IsJSArray() ?
1623
        Smi::cast(JSArray::cast(js_obj)->length())->value() :
1624
        elements->length();
1625
    for (int i = 0; i < length; ++i) {
1626
      if (!elements->get(i)->IsTheHole()) {
1627
        SetElementReference(js_obj, entry, i, elements->get(i));
1628
      }
1629
    }
1630
  } else if (js_obj->HasDictionaryElements()) {
1631
    SeededNumberDictionary* dictionary = js_obj->element_dictionary();
1632
    int length = dictionary->Capacity();
1633
    for (int i = 0; i < length; ++i) {
1634
      Object* k = dictionary->KeyAt(i);
1635
      if (dictionary->IsKey(k)) {
1636
        ASSERT(k->IsNumber());
1637
        uint32_t index = static_cast<uint32_t>(k->Number());
1638
        SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
1639
      }
1640
    }
1641
  }
1642
}
1643

    
1644

    
1645
void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
1646
  int length = js_obj->GetInternalFieldCount();
1647
  for (int i = 0; i < length; ++i) {
1648
    Object* o = js_obj->GetInternalField(i);
1649
    SetInternalReference(
1650
        js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
1651
  }
1652
}
1653

    
1654

    
1655
String* V8HeapExplorer::GetConstructorName(JSObject* object) {
1656
  Heap* heap = object->GetHeap();
1657
  if (object->IsJSFunction()) return heap->closure_string();
1658
  String* constructor_name = object->constructor_name();
1659
  if (constructor_name == heap->Object_string()) {
1660
    // Look up an immediate "constructor" property, if it is a function,
1661
    // return its name. This is for instances of binding objects, which
1662
    // have prototype constructor type "Object".
1663
    Object* constructor_prop = NULL;
1664
    LookupResult result(heap->isolate());
1665
    object->LocalLookupRealNamedProperty(heap->constructor_string(), &result);
1666
    if (!result.IsFound()) return object->constructor_name();
1667

    
1668
    constructor_prop = result.GetLazyValue();
1669
    if (constructor_prop->IsJSFunction()) {
1670
      Object* maybe_name =
1671
          JSFunction::cast(constructor_prop)->shared()->name();
1672
      if (maybe_name->IsString()) {
1673
        String* name = String::cast(maybe_name);
1674
        if (name->length() > 0) return name;
1675
      }
1676
    }
1677
  }
1678
  return object->constructor_name();
1679
}
1680

    
1681

    
1682
HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
1683
  if (!obj->IsHeapObject()) return NULL;
1684
  return filler_->FindOrAddEntry(obj, this);
1685
}
1686

    
1687

    
1688
class RootsReferencesExtractor : public ObjectVisitor {
1689
 private:
1690
  struct IndexTag {
1691
    IndexTag(int index, VisitorSynchronization::SyncTag tag)
1692
        : index(index), tag(tag) { }
1693
    int index;
1694
    VisitorSynchronization::SyncTag tag;
1695
  };
1696

    
1697
 public:
1698
  RootsReferencesExtractor()
1699
      : collecting_all_references_(false),
1700
        previous_reference_count_(0) {
1701
  }
1702

    
1703
  void VisitPointers(Object** start, Object** end) {
1704
    if (collecting_all_references_) {
1705
      for (Object** p = start; p < end; p++) all_references_.Add(*p);
1706
    } else {
1707
      for (Object** p = start; p < end; p++) strong_references_.Add(*p);
1708
    }
1709
  }
1710

    
1711
  void SetCollectingAllReferences() { collecting_all_references_ = true; }
1712

    
1713
  void FillReferences(V8HeapExplorer* explorer) {
1714
    ASSERT(strong_references_.length() <= all_references_.length());
1715
    for (int i = 0; i < reference_tags_.length(); ++i) {
1716
      explorer->SetGcRootsReference(reference_tags_[i].tag);
1717
    }
1718
    int strong_index = 0, all_index = 0, tags_index = 0;
1719
    while (all_index < all_references_.length()) {
1720
      if (strong_index < strong_references_.length() &&
1721
          strong_references_[strong_index] == all_references_[all_index]) {
1722
        explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
1723
                                        false,
1724
                                        all_references_[all_index++]);
1725
        ++strong_index;
1726
      } else {
1727
        explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
1728
                                        true,
1729
                                        all_references_[all_index++]);
1730
      }
1731
      if (reference_tags_[tags_index].index == all_index) ++tags_index;
1732
    }
1733
  }
1734

    
1735
  void Synchronize(VisitorSynchronization::SyncTag tag) {
1736
    if (collecting_all_references_ &&
1737
        previous_reference_count_ != all_references_.length()) {
1738
      previous_reference_count_ = all_references_.length();
1739
      reference_tags_.Add(IndexTag(previous_reference_count_, tag));
1740
    }
1741
  }
1742

    
1743
 private:
1744
  bool collecting_all_references_;
1745
  List<Object*> strong_references_;
1746
  List<Object*> all_references_;
1747
  int previous_reference_count_;
1748
  List<IndexTag> reference_tags_;
1749
};
1750

    
1751

    
1752
bool V8HeapExplorer::IterateAndExtractReferences(
1753
    SnapshotFillerInterface* filler) {
1754
  HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
1755

    
1756
  filler_ = filler;
1757
  bool interrupted = false;
1758

    
1759
  // Heap iteration with filtering must be finished in any case.
1760
  for (HeapObject* obj = iterator.next();
1761
       obj != NULL;
1762
       obj = iterator.next(), progress_->ProgressStep()) {
1763
    if (!interrupted) {
1764
      ExtractReferences(obj);
1765
      if (!progress_->ProgressReport(false)) interrupted = true;
1766
    }
1767
  }
1768
  if (interrupted) {
1769
    filler_ = NULL;
1770
    return false;
1771
  }
1772

    
1773
  SetRootGcRootsReference();
1774
  RootsReferencesExtractor extractor;
1775
  heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
1776
  extractor.SetCollectingAllReferences();
1777
  heap_->IterateRoots(&extractor, VISIT_ALL);
1778
  extractor.FillReferences(this);
1779
  filler_ = NULL;
1780
  return progress_->ProgressReport(true);
1781
}
1782

    
1783

    
1784
bool V8HeapExplorer::IsEssentialObject(Object* object) {
1785
  return object->IsHeapObject()
1786
      && !object->IsOddball()
1787
      && object != heap_->empty_byte_array()
1788
      && object != heap_->empty_fixed_array()
1789
      && object != heap_->empty_descriptor_array()
1790
      && object != heap_->fixed_array_map()
1791
      && object != heap_->cell_map()
1792
      && object != heap_->global_property_cell_map()
1793
      && object != heap_->shared_function_info_map()
1794
      && object != heap_->free_space_map()
1795
      && object != heap_->one_pointer_filler_map()
1796
      && object != heap_->two_pointer_filler_map();
1797
}
1798

    
1799

    
1800
void V8HeapExplorer::SetContextReference(HeapObject* parent_obj,
1801
                                         int parent_entry,
1802
                                         String* reference_name,
1803
                                         Object* child_obj,
1804
                                         int field_offset) {
1805
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1806
  HeapEntry* child_entry = GetEntry(child_obj);
1807
  if (child_entry != NULL) {
1808
    filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
1809
                               parent_entry,
1810
                               collection_->names()->GetName(reference_name),
1811
                               child_entry);
1812
    IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1813
  }
1814
}
1815

    
1816

    
1817
void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
1818
                                            int parent_entry,
1819
                                            const char* reference_name,
1820
                                            Object* child_obj) {
1821
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1822
  HeapEntry* child_entry = GetEntry(child_obj);
1823
  if (child_entry != NULL) {
1824
    filler_->SetNamedReference(HeapGraphEdge::kShortcut,
1825
                               parent_entry,
1826
                               reference_name,
1827
                               child_entry);
1828
  }
1829
}
1830

    
1831

    
1832
void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
1833
                                         int parent_entry,
1834
                                         int index,
1835
                                         Object* child_obj) {
1836
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1837
  HeapEntry* child_entry = GetEntry(child_obj);
1838
  if (child_entry != NULL) {
1839
    filler_->SetIndexedReference(HeapGraphEdge::kElement,
1840
                                 parent_entry,
1841
                                 index,
1842
                                 child_entry);
1843
  }
1844
}
1845

    
1846

    
1847
void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
1848
                                          int parent_entry,
1849
                                          const char* reference_name,
1850
                                          Object* child_obj,
1851
                                          int field_offset) {
1852
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1853
  HeapEntry* child_entry = GetEntry(child_obj);
1854
  if (child_entry == NULL) return;
1855
  if (IsEssentialObject(child_obj)) {
1856
    filler_->SetNamedReference(HeapGraphEdge::kInternal,
1857
                               parent_entry,
1858
                               reference_name,
1859
                               child_entry);
1860
  }
1861
  IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1862
}
1863

    
1864

    
1865
void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
1866
                                          int parent_entry,
1867
                                          int index,
1868
                                          Object* child_obj,
1869
                                          int field_offset) {
1870
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1871
  HeapEntry* child_entry = GetEntry(child_obj);
1872
  if (child_entry == NULL) return;
1873
  if (IsEssentialObject(child_obj)) {
1874
    filler_->SetNamedReference(HeapGraphEdge::kInternal,
1875
                               parent_entry,
1876
                               collection_->names()->GetName(index),
1877
                               child_entry);
1878
  }
1879
  IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1880
}
1881

    
1882

    
1883
void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
1884
                                        int parent_entry,
1885
                                        int index,
1886
                                        Object* child_obj) {
1887
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1888
  HeapEntry* child_entry = GetEntry(child_obj);
1889
  if (child_entry != NULL && IsEssentialObject(child_obj)) {
1890
    filler_->SetIndexedReference(HeapGraphEdge::kHidden,
1891
                                 parent_entry,
1892
                                 index,
1893
                                 child_entry);
1894
  }
1895
}
1896

    
1897

    
1898
void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
1899
                                      int parent_entry,
1900
                                      int index,
1901
                                      Object* child_obj,
1902
                                      int field_offset) {
1903
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1904
  HeapEntry* child_entry = GetEntry(child_obj);
1905
  if (child_entry == NULL) return;
1906
  if (IsEssentialObject(child_obj)) {
1907
    filler_->SetIndexedReference(HeapGraphEdge::kWeak,
1908
                                 parent_entry,
1909
                                 index,
1910
                                 child_entry);
1911
  }
1912
  IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1913
}
1914

    
1915

    
1916
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
1917
                                          int parent_entry,
1918
                                          Name* reference_name,
1919
                                          Object* child_obj,
1920
                                          const char* name_format_string,
1921
                                          int field_offset) {
1922
  ASSERT(parent_entry == GetEntry(parent_obj)->index());
1923
  HeapEntry* child_entry = GetEntry(child_obj);
1924
  if (child_entry != NULL) {
1925
    HeapGraphEdge::Type type =
1926
        reference_name->IsSymbol() || String::cast(reference_name)->length() > 0
1927
            ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
1928
    const char* name = name_format_string != NULL && reference_name->IsString()
1929
        ? collection_->names()->GetFormatted(
1930
              name_format_string,
1931
              *String::cast(reference_name)->ToCString(
1932
                  DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)) :
1933
        collection_->names()->GetName(reference_name);
1934

    
1935
    filler_->SetNamedReference(type,
1936
                               parent_entry,
1937
                               name,
1938
                               child_entry);
1939
    IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1940
  }
1941
}
1942

    
1943

    
1944
void V8HeapExplorer::SetRootGcRootsReference() {
1945
  filler_->SetIndexedAutoIndexReference(
1946
      HeapGraphEdge::kElement,
1947
      snapshot_->root()->index(),
1948
      snapshot_->gc_roots());
1949
}
1950

    
1951

    
1952
void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
1953
  HeapEntry* child_entry = GetEntry(child_obj);
1954
  ASSERT(child_entry != NULL);
1955
  filler_->SetNamedAutoIndexReference(
1956
      HeapGraphEdge::kShortcut,
1957
      snapshot_->root()->index(),
1958
      child_entry);
1959
}
1960

    
1961

    
1962
void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
1963
  filler_->SetIndexedAutoIndexReference(
1964
      HeapGraphEdge::kElement,
1965
      snapshot_->gc_roots()->index(),
1966
      snapshot_->gc_subroot(tag));
1967
}
1968

    
1969

    
1970
void V8HeapExplorer::SetGcSubrootReference(
1971
    VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
1972
  HeapEntry* child_entry = GetEntry(child_obj);
1973
  if (child_entry != NULL) {
1974
    const char* name = GetStrongGcSubrootName(child_obj);
1975
    if (name != NULL) {
1976
      filler_->SetNamedReference(
1977
          HeapGraphEdge::kInternal,
1978
          snapshot_->gc_subroot(tag)->index(),
1979
          name,
1980
          child_entry);
1981
    } else {
1982
      filler_->SetIndexedAutoIndexReference(
1983
          is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
1984
          snapshot_->gc_subroot(tag)->index(),
1985
          child_entry);
1986
    }
1987

    
1988
    // Add a shortcut to JS global object reference at snapshot root.
1989
    if (child_obj->IsNativeContext()) {
1990
      Context* context = Context::cast(child_obj);
1991
      GlobalObject* global = context->global_object();
1992
      if (global->IsJSGlobalObject()) {
1993
        bool is_debug_object = false;
1994
#ifdef ENABLE_DEBUGGER_SUPPORT
1995
        is_debug_object = heap_->isolate()->debug()->IsDebugGlobal(global);
1996
#endif
1997
        if (!is_debug_object && !user_roots_.Contains(global)) {
1998
          user_roots_.Insert(global);
1999
          SetUserGlobalReference(global);
2000
        }
2001
      }
2002
    }
2003
  }
2004
}
2005

    
2006

    
2007
const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
2008
  if (strong_gc_subroot_names_.is_empty()) {
2009
#define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name);
2010
#define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name)
2011
    STRONG_ROOT_LIST(ROOT_NAME)
2012
#undef ROOT_NAME
2013
#define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map)
2014
    STRUCT_LIST(STRUCT_MAP_NAME)
2015
#undef STRUCT_MAP_NAME
2016
#define STRING_NAME(name, str) NAME_ENTRY(name)
2017
    INTERNALIZED_STRING_LIST(STRING_NAME)
2018
#undef STRING_NAME
2019
#undef NAME_ENTRY
2020
    CHECK(!strong_gc_subroot_names_.is_empty());
2021
  }
2022
  return strong_gc_subroot_names_.GetTag(object);
2023
}
2024

    
2025

    
2026
void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
2027
  if (IsEssentialObject(obj)) {
2028
    HeapEntry* entry = GetEntry(obj);
2029
    if (entry->name()[0] == '\0') {
2030
      entry->set_name(tag);
2031
    }
2032
  }
2033
}
2034

    
2035

    
2036
class GlobalObjectsEnumerator : public ObjectVisitor {
2037
 public:
2038
  virtual void VisitPointers(Object** start, Object** end) {
2039
    for (Object** p = start; p < end; p++) {
2040
      if ((*p)->IsNativeContext()) {
2041
        Context* context = Context::cast(*p);
2042
        JSObject* proxy = context->global_proxy();
2043
        if (proxy->IsJSGlobalProxy()) {
2044
          Object* global = proxy->map()->prototype();
2045
          if (global->IsJSGlobalObject()) {
2046
            objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
2047
          }
2048
        }
2049
      }
2050
    }
2051
  }
2052
  int count() { return objects_.length(); }
2053
  Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
2054

    
2055
 private:
2056
  List<Handle<JSGlobalObject> > objects_;
2057
};
2058

    
2059

    
2060
// Modifies heap. Must not be run during heap traversal.
2061
void V8HeapExplorer::TagGlobalObjects() {
2062
  Isolate* isolate = heap_->isolate();
2063
  HandleScope scope(isolate);
2064
  GlobalObjectsEnumerator enumerator;
2065
  isolate->global_handles()->IterateAllRoots(&enumerator);
2066
  const char** urls = NewArray<const char*>(enumerator.count());
2067
  for (int i = 0, l = enumerator.count(); i < l; ++i) {
2068
    if (global_object_name_resolver_) {
2069
      HandleScope scope(isolate);
2070
      Handle<JSGlobalObject> global_obj = enumerator.at(i);
2071
      urls[i] = global_object_name_resolver_->GetName(
2072
          Utils::ToLocal(Handle<JSObject>::cast(global_obj)));
2073
    } else {
2074
      urls[i] = NULL;
2075
    }
2076
  }
2077

    
2078
  DisallowHeapAllocation no_allocation;
2079
  for (int i = 0, l = enumerator.count(); i < l; ++i) {
2080
    objects_tags_.SetTag(*enumerator.at(i), urls[i]);
2081
  }
2082

    
2083
  DeleteArray(urls);
2084
}
2085

    
2086

    
2087
class GlobalHandlesExtractor : public ObjectVisitor {
2088
 public:
2089
  explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
2090
      : explorer_(explorer) {}
2091
  virtual ~GlobalHandlesExtractor() {}
2092
  virtual void VisitPointers(Object** start, Object** end) {
2093
    UNREACHABLE();
2094
  }
2095
  virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
2096
    explorer_->VisitSubtreeWrapper(p, class_id);
2097
  }
2098
 private:
2099
  NativeObjectsExplorer* explorer_;
2100
};
2101

    
2102

    
2103
class BasicHeapEntriesAllocator : public HeapEntriesAllocator {
2104
 public:
2105
  BasicHeapEntriesAllocator(
2106
      HeapSnapshot* snapshot,
2107
      HeapEntry::Type entries_type)
2108
    : snapshot_(snapshot),
2109
      collection_(snapshot_->collection()),
2110
      entries_type_(entries_type) {
2111
  }
2112
  virtual HeapEntry* AllocateEntry(HeapThing ptr);
2113
 private:
2114
  HeapSnapshot* snapshot_;
2115
  HeapSnapshotsCollection* collection_;
2116
  HeapEntry::Type entries_type_;
2117
};
2118

    
2119

    
2120
HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
2121
  v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
2122
  intptr_t elements = info->GetElementCount();
2123
  intptr_t size = info->GetSizeInBytes();
2124
  const char* name = elements != -1
2125
      ? collection_->names()->GetFormatted(
2126
            "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements)
2127
      : collection_->names()->GetCopy(info->GetLabel());
2128
  return snapshot_->AddEntry(
2129
      entries_type_,
2130
      name,
2131
      HeapObjectsMap::GenerateId(collection_->heap(), info),
2132
      size != -1 ? static_cast<int>(size) : 0);
2133
}
2134

    
2135

    
2136
NativeObjectsExplorer::NativeObjectsExplorer(
2137
    HeapSnapshot* snapshot,
2138
    SnapshottingProgressReportingInterface* progress)
2139
    : isolate_(snapshot->collection()->heap()->isolate()),
2140
      snapshot_(snapshot),
2141
      collection_(snapshot_->collection()),
2142
      progress_(progress),
2143
      embedder_queried_(false),
2144
      objects_by_info_(RetainedInfosMatch),
2145
      native_groups_(StringsMatch),
2146
      filler_(NULL) {
2147
  synthetic_entries_allocator_ =
2148
      new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic);
2149
  native_entries_allocator_ =
2150
      new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative);
2151
}
2152

    
2153

    
2154
NativeObjectsExplorer::~NativeObjectsExplorer() {
2155
  for (HashMap::Entry* p = objects_by_info_.Start();
2156
       p != NULL;
2157
       p = objects_by_info_.Next(p)) {
2158
    v8::RetainedObjectInfo* info =
2159
        reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2160
    info->Dispose();
2161
    List<HeapObject*>* objects =
2162
        reinterpret_cast<List<HeapObject*>* >(p->value);
2163
    delete objects;
2164
  }
2165
  for (HashMap::Entry* p = native_groups_.Start();
2166
       p != NULL;
2167
       p = native_groups_.Next(p)) {
2168
    v8::RetainedObjectInfo* info =
2169
        reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
2170
    info->Dispose();
2171
  }
2172
  delete synthetic_entries_allocator_;
2173
  delete native_entries_allocator_;
2174
}
2175

    
2176

    
2177
int NativeObjectsExplorer::EstimateObjectsCount() {
2178
  FillRetainedObjects();
2179
  return objects_by_info_.occupancy();
2180
}
2181

    
2182

    
2183
void NativeObjectsExplorer::FillRetainedObjects() {
2184
  if (embedder_queried_) return;
2185
  Isolate* isolate = isolate_;
2186
  const GCType major_gc_type = kGCTypeMarkSweepCompact;
2187
  // Record objects that are joined into ObjectGroups.
2188
  isolate->heap()->CallGCPrologueCallbacks(
2189
      major_gc_type, kGCCallbackFlagConstructRetainedObjectInfos);
2190
  List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
2191
  for (int i = 0; i < groups->length(); ++i) {
2192
    ObjectGroup* group = groups->at(i);
2193
    if (group->info == NULL) continue;
2194
    List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info);
2195
    for (size_t j = 0; j < group->length; ++j) {
2196
      HeapObject* obj = HeapObject::cast(*group->objects[j]);
2197
      list->Add(obj);
2198
      in_groups_.Insert(obj);
2199
    }
2200
    group->info = NULL;  // Acquire info object ownership.
2201
  }
2202
  isolate->global_handles()->RemoveObjectGroups();
2203
  isolate->heap()->CallGCEpilogueCallbacks(major_gc_type);
2204
  // Record objects that are not in ObjectGroups, but have class ID.
2205
  GlobalHandlesExtractor extractor(this);
2206
  isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
2207
  embedder_queried_ = true;
2208
}
2209

    
2210

    
2211
void NativeObjectsExplorer::FillImplicitReferences() {
2212
  Isolate* isolate = isolate_;
2213
  List<ImplicitRefGroup*>* groups =
2214
      isolate->global_handles()->implicit_ref_groups();
2215
  for (int i = 0; i < groups->length(); ++i) {
2216
    ImplicitRefGroup* group = groups->at(i);
2217
    HeapObject* parent = *group->parent;
2218
    int parent_entry =
2219
        filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
2220
    ASSERT(parent_entry != HeapEntry::kNoEntry);
2221
    Object*** children = group->children;
2222
    for (size_t j = 0; j < group->length; ++j) {
2223
      Object* child = *children[j];
2224
      HeapEntry* child_entry =
2225
          filler_->FindOrAddEntry(child, native_entries_allocator_);
2226
      filler_->SetNamedReference(
2227
          HeapGraphEdge::kInternal,
2228
          parent_entry,
2229
          "native",
2230
          child_entry);
2231
    }
2232
  }
2233
  isolate->global_handles()->RemoveImplicitRefGroups();
2234
}
2235

    
2236
List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
2237
    v8::RetainedObjectInfo* info) {
2238
  HashMap::Entry* entry =
2239
      objects_by_info_.Lookup(info, InfoHash(info), true);
2240
  if (entry->value != NULL) {
2241
    info->Dispose();
2242
  } else {
2243
    entry->value = new List<HeapObject*>(4);
2244
  }
2245
  return reinterpret_cast<List<HeapObject*>* >(entry->value);
2246
}
2247

    
2248

    
2249
bool NativeObjectsExplorer::IterateAndExtractReferences(
2250
    SnapshotFillerInterface* filler) {
2251
  filler_ = filler;
2252
  FillRetainedObjects();
2253
  FillImplicitReferences();
2254
  if (EstimateObjectsCount() > 0) {
2255
    for (HashMap::Entry* p = objects_by_info_.Start();
2256
         p != NULL;
2257
         p = objects_by_info_.Next(p)) {
2258
      v8::RetainedObjectInfo* info =
2259
          reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2260
      SetNativeRootReference(info);
2261
      List<HeapObject*>* objects =
2262
          reinterpret_cast<List<HeapObject*>* >(p->value);
2263
      for (int i = 0; i < objects->length(); ++i) {
2264
        SetWrapperNativeReferences(objects->at(i), info);
2265
      }
2266
    }
2267
    SetRootNativeRootsReference();
2268
  }
2269
  filler_ = NULL;
2270
  return true;
2271
}
2272

    
2273

    
2274
class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo {
2275
 public:
2276
  explicit NativeGroupRetainedObjectInfo(const char* label)
2277
      : disposed_(false),
2278
        hash_(reinterpret_cast<intptr_t>(label)),
2279
        label_(label) {
2280
  }
2281

    
2282
  virtual ~NativeGroupRetainedObjectInfo() {}
2283
  virtual void Dispose() {
2284
    CHECK(!disposed_);
2285
    disposed_ = true;
2286
    delete this;
2287
  }
2288
  virtual bool IsEquivalent(RetainedObjectInfo* other) {
2289
    return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel());
2290
  }
2291
  virtual intptr_t GetHash() { return hash_; }
2292
  virtual const char* GetLabel() { return label_; }
2293

    
2294
 private:
2295
  bool disposed_;
2296
  intptr_t hash_;
2297
  const char* label_;
2298
};
2299

    
2300

    
2301
NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo(
2302
    const char* label) {
2303
  const char* label_copy = collection_->names()->GetCopy(label);
2304
  uint32_t hash = StringHasher::HashSequentialString(
2305
      label_copy,
2306
      static_cast<int>(strlen(label_copy)),
2307
      isolate_->heap()->HashSeed());
2308
  HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy),
2309
                                                hash, true);
2310
  if (entry->value == NULL) {
2311
    entry->value = new NativeGroupRetainedObjectInfo(label);
2312
  }
2313
  return static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2314
}
2315

    
2316

    
2317
void NativeObjectsExplorer::SetNativeRootReference(
2318
    v8::RetainedObjectInfo* info) {
2319
  HeapEntry* child_entry =
2320
      filler_->FindOrAddEntry(info, native_entries_allocator_);
2321
  ASSERT(child_entry != NULL);
2322
  NativeGroupRetainedObjectInfo* group_info =
2323
      FindOrAddGroupInfo(info->GetGroupLabel());
2324
  HeapEntry* group_entry =
2325
      filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
2326
  filler_->SetNamedAutoIndexReference(
2327
      HeapGraphEdge::kInternal,
2328
      group_entry->index(),
2329
      child_entry);
2330
}
2331

    
2332

    
2333
void NativeObjectsExplorer::SetWrapperNativeReferences(
2334
    HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2335
  HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2336
  ASSERT(wrapper_entry != NULL);
2337
  HeapEntry* info_entry =
2338
      filler_->FindOrAddEntry(info, native_entries_allocator_);
2339
  ASSERT(info_entry != NULL);
2340
  filler_->SetNamedReference(HeapGraphEdge::kInternal,
2341
                             wrapper_entry->index(),
2342
                             "native",
2343
                             info_entry);
2344
  filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
2345
                                        info_entry->index(),
2346
                                        wrapper_entry);
2347
}
2348

    
2349

    
2350
void NativeObjectsExplorer::SetRootNativeRootsReference() {
2351
  for (HashMap::Entry* entry = native_groups_.Start();
2352
       entry;
2353
       entry = native_groups_.Next(entry)) {
2354
    NativeGroupRetainedObjectInfo* group_info =
2355
        static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2356
    HeapEntry* group_entry =
2357
        filler_->FindOrAddEntry(group_info, native_entries_allocator_);
2358
    ASSERT(group_entry != NULL);
2359
    filler_->SetIndexedAutoIndexReference(
2360
        HeapGraphEdge::kElement,
2361
        snapshot_->root()->index(),
2362
        group_entry);
2363
  }
2364
}
2365

    
2366

    
2367
void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2368
  if (in_groups_.Contains(*p)) return;
2369
  Isolate* isolate = isolate_;
2370
  v8::RetainedObjectInfo* info =
2371
      isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
2372
  if (info == NULL) return;
2373
  GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
2374
}
2375

    
2376

    
2377
class SnapshotFiller : public SnapshotFillerInterface {
2378
 public:
2379
  explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
2380
      : snapshot_(snapshot),
2381
        collection_(snapshot->collection()),
2382
        entries_(entries) { }
2383
  HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2384
    HeapEntry* entry = allocator->AllocateEntry(ptr);
2385
    entries_->Pair(ptr, entry->index());
2386
    return entry;
2387
  }
2388
  HeapEntry* FindEntry(HeapThing ptr) {
2389
    int index = entries_->Map(ptr);
2390
    return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
2391
  }
2392
  HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2393
    HeapEntry* entry = FindEntry(ptr);
2394
    return entry != NULL ? entry : AddEntry(ptr, allocator);
2395
  }
2396
  void SetIndexedReference(HeapGraphEdge::Type type,
2397
                           int parent,
2398
                           int index,
2399
                           HeapEntry* child_entry) {
2400
    HeapEntry* parent_entry = &snapshot_->entries()[parent];
2401
    parent_entry->SetIndexedReference(type, index, child_entry);
2402
  }
2403
  void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
2404
                                    int parent,
2405
                                    HeapEntry* child_entry) {
2406
    HeapEntry* parent_entry = &snapshot_->entries()[parent];
2407
    int index = parent_entry->children_count() + 1;
2408
    parent_entry->SetIndexedReference(type, index, child_entry);
2409
  }
2410
  void SetNamedReference(HeapGraphEdge::Type type,
2411
                         int parent,
2412
                         const char* reference_name,
2413
                         HeapEntry* child_entry) {
2414
    HeapEntry* parent_entry = &snapshot_->entries()[parent];
2415
    parent_entry->SetNamedReference(type, reference_name, child_entry);
2416
  }
2417
  void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
2418
                                  int parent,
2419
                                  HeapEntry* child_entry) {
2420
    HeapEntry* parent_entry = &snapshot_->entries()[parent];
2421
    int index = parent_entry->children_count() + 1;
2422
    parent_entry->SetNamedReference(
2423
        type,
2424
        collection_->names()->GetName(index),
2425
        child_entry);
2426
  }
2427

    
2428
 private:
2429
  HeapSnapshot* snapshot_;
2430
  HeapSnapshotsCollection* collection_;
2431
  HeapEntriesMap* entries_;
2432
};
2433

    
2434

    
2435
HeapSnapshotGenerator::HeapSnapshotGenerator(
2436
    HeapSnapshot* snapshot,
2437
    v8::ActivityControl* control,
2438
    v8::HeapProfiler::ObjectNameResolver* resolver,
2439
    Heap* heap)
2440
    : snapshot_(snapshot),
2441
      control_(control),
2442
      v8_heap_explorer_(snapshot_, this, resolver),
2443
      dom_explorer_(snapshot_, this),
2444
      heap_(heap) {
2445
}
2446

    
2447

    
2448
bool HeapSnapshotGenerator::GenerateSnapshot() {
2449
  v8_heap_explorer_.TagGlobalObjects();
2450

    
2451
  // TODO(1562) Profiler assumes that any object that is in the heap after
2452
  // full GC is reachable from the root when computing dominators.
2453
  // This is not true for weakly reachable objects.
2454
  // As a temporary solution we call GC twice.
2455
  heap_->CollectAllGarbage(
2456
      Heap::kMakeHeapIterableMask,
2457
      "HeapSnapshotGenerator::GenerateSnapshot");
2458
  heap_->CollectAllGarbage(
2459
      Heap::kMakeHeapIterableMask,
2460
      "HeapSnapshotGenerator::GenerateSnapshot");
2461

    
2462
#ifdef VERIFY_HEAP
2463
  Heap* debug_heap = heap_;
2464
  CHECK(!debug_heap->old_data_space()->was_swept_conservatively());
2465
  CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively());
2466
  CHECK(!debug_heap->code_space()->was_swept_conservatively());
2467
  CHECK(!debug_heap->cell_space()->was_swept_conservatively());
2468
  CHECK(!debug_heap->property_cell_space()->
2469
        was_swept_conservatively());
2470
  CHECK(!debug_heap->map_space()->was_swept_conservatively());
2471
#endif
2472

    
2473
  // The following code uses heap iterators, so we want the heap to be
2474
  // stable. It should follow TagGlobalObjects as that can allocate.
2475
  DisallowHeapAllocation no_alloc;
2476

    
2477
#ifdef VERIFY_HEAP
2478
  debug_heap->Verify();
2479
#endif
2480

    
2481
  SetProgressTotal(1);  // 1 pass.
2482

    
2483
#ifdef VERIFY_HEAP
2484
  debug_heap->Verify();
2485
#endif
2486

    
2487
  if (!FillReferences()) return false;
2488

    
2489
  snapshot_->FillChildren();
2490
  snapshot_->RememberLastJSObjectId();
2491

    
2492
  progress_counter_ = progress_total_;
2493
  if (!ProgressReport(true)) return false;
2494
  return true;
2495
}
2496

    
2497

    
2498
void HeapSnapshotGenerator::ProgressStep() {
2499
  ++progress_counter_;
2500
}
2501

    
2502

    
2503
bool HeapSnapshotGenerator::ProgressReport(bool force) {
2504
  const int kProgressReportGranularity = 10000;
2505
  if (control_ != NULL
2506
      && (force || progress_counter_ % kProgressReportGranularity == 0)) {
2507
      return
2508
          control_->ReportProgressValue(progress_counter_, progress_total_) ==
2509
          v8::ActivityControl::kContinue;
2510
  }
2511
  return true;
2512
}
2513

    
2514

    
2515
void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
2516
  if (control_ == NULL) return;
2517
  HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
2518
  progress_total_ = iterations_count * (
2519
      v8_heap_explorer_.EstimateObjectsCount(&iterator) +
2520
      dom_explorer_.EstimateObjectsCount());
2521
  progress_counter_ = 0;
2522
}
2523

    
2524

    
2525
bool HeapSnapshotGenerator::FillReferences() {
2526
  SnapshotFiller filler(snapshot_, &entries_);
2527
  v8_heap_explorer_.AddRootEntries(&filler);
2528
  return v8_heap_explorer_.IterateAndExtractReferences(&filler)
2529
      && dom_explorer_.IterateAndExtractReferences(&filler);
2530
}
2531

    
2532

    
2533
template<int bytes> struct MaxDecimalDigitsIn;
2534
template<> struct MaxDecimalDigitsIn<4> {
2535
  static const int kSigned = 11;
2536
  static const int kUnsigned = 10;
2537
};
2538
template<> struct MaxDecimalDigitsIn<8> {
2539
  static const int kSigned = 20;
2540
  static const int kUnsigned = 20;
2541
};
2542

    
2543

    
2544
class OutputStreamWriter {
2545
 public:
2546
  explicit OutputStreamWriter(v8::OutputStream* stream)
2547
      : stream_(stream),
2548
        chunk_size_(stream->GetChunkSize()),
2549
        chunk_(chunk_size_),
2550
        chunk_pos_(0),
2551
        aborted_(false) {
2552
    ASSERT(chunk_size_ > 0);
2553
  }
2554
  bool aborted() { return aborted_; }
2555
  void AddCharacter(char c) {
2556
    ASSERT(c != '\0');
2557
    ASSERT(chunk_pos_ < chunk_size_);
2558
    chunk_[chunk_pos_++] = c;
2559
    MaybeWriteChunk();
2560
  }
2561
  void AddString(const char* s) {
2562
    AddSubstring(s, StrLength(s));
2563
  }
2564
  void AddSubstring(const char* s, int n) {
2565
    if (n <= 0) return;
2566
    ASSERT(static_cast<size_t>(n) <= strlen(s));
2567
    const char* s_end = s + n;
2568
    while (s < s_end) {
2569
      int s_chunk_size = Min(
2570
          chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
2571
      ASSERT(s_chunk_size > 0);
2572
      OS::MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
2573
      s += s_chunk_size;
2574
      chunk_pos_ += s_chunk_size;
2575
      MaybeWriteChunk();
2576
    }
2577
  }
2578
  void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
2579
  void Finalize() {
2580
    if (aborted_) return;
2581
    ASSERT(chunk_pos_ < chunk_size_);
2582
    if (chunk_pos_ != 0) {
2583
      WriteChunk();
2584
    }
2585
    stream_->EndOfStream();
2586
  }
2587

    
2588
 private:
2589
  template<typename T>
2590
  void AddNumberImpl(T n, const char* format) {
2591
    // Buffer for the longest value plus trailing \0
2592
    static const int kMaxNumberSize =
2593
        MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
2594
    if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
2595
      int result = OS::SNPrintF(
2596
          chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
2597
      ASSERT(result != -1);
2598
      chunk_pos_ += result;
2599
      MaybeWriteChunk();
2600
    } else {
2601
      EmbeddedVector<char, kMaxNumberSize> buffer;
2602
      int result = OS::SNPrintF(buffer, format, n);
2603
      USE(result);
2604
      ASSERT(result != -1);
2605
      AddString(buffer.start());
2606
    }
2607
  }
2608
  void MaybeWriteChunk() {
2609
    ASSERT(chunk_pos_ <= chunk_size_);
2610
    if (chunk_pos_ == chunk_size_) {
2611
      WriteChunk();
2612
    }
2613
  }
2614
  void WriteChunk() {
2615
    if (aborted_) return;
2616
    if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
2617
        v8::OutputStream::kAbort) aborted_ = true;
2618
    chunk_pos_ = 0;
2619
  }
2620

    
2621
  v8::OutputStream* stream_;
2622
  int chunk_size_;
2623
  ScopedVector<char> chunk_;
2624
  int chunk_pos_;
2625
  bool aborted_;
2626
};
2627

    
2628

    
2629
// type, name|index, to_node.
2630
const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
2631
// type, name, id, self_size, children_index.
2632
const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
2633

    
2634
void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
2635
  if (AllocationTracker* allocation_tracker =
2636
      snapshot_->collection()->allocation_tracker()) {
2637
    allocation_tracker->PrepareForSerialization();
2638
  }
2639
  ASSERT(writer_ == NULL);
2640
  writer_ = new OutputStreamWriter(stream);
2641
  SerializeImpl();
2642
  delete writer_;
2643
  writer_ = NULL;
2644
}
2645

    
2646

    
2647
void HeapSnapshotJSONSerializer::SerializeImpl() {
2648
  ASSERT(0 == snapshot_->root()->index());
2649
  writer_->AddCharacter('{');
2650
  writer_->AddString("\"snapshot\":{");
2651
  SerializeSnapshot();
2652
  if (writer_->aborted()) return;
2653
  writer_->AddString("},\n");
2654
  writer_->AddString("\"nodes\":[");
2655
  SerializeNodes();
2656
  if (writer_->aborted()) return;
2657
  writer_->AddString("],\n");
2658
  writer_->AddString("\"edges\":[");
2659
  SerializeEdges();
2660
  if (writer_->aborted()) return;
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

    
2672
  writer_->AddString("\"strings\":[");
2673
  SerializeStrings();
2674
  if (writer_->aborted()) return;
2675
  writer_->AddCharacter(']');
2676
  writer_->AddCharacter('}');
2677
  writer_->Finalize();
2678
}
2679

    
2680

    
2681
int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
2682
  HashMap::Entry* cache_entry = strings_.Lookup(
2683
      const_cast<char*>(s), StringHash(s), true);
2684
  if (cache_entry->value == NULL) {
2685
    cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
2686
  }
2687
  return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
2688
}
2689

    
2690

    
2691
static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) {
2692
  int number_of_digits = 0;
2693
  unsigned t = value;
2694
  do {
2695
    ++number_of_digits;
2696
  } while (t /= 10);
2697

    
2698
  buffer_pos += number_of_digits;
2699
  int result = buffer_pos;
2700
  do {
2701
    int last_digit = value % 10;
2702
    buffer[--buffer_pos] = '0' + last_digit;
2703
    value /= 10;
2704
  } while (value);
2705
  return result;
2706
}
2707

    
2708

    
2709
void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
2710
                                               bool first_edge) {
2711
  // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
2712
  static const int kBufferSize =
2713
      MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2;  // NOLINT
2714
  EmbeddedVector<char, kBufferSize> buffer;
2715
  int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
2716
      || edge->type() == HeapGraphEdge::kHidden
2717
      || edge->type() == HeapGraphEdge::kWeak
2718
      ? edge->index() : GetStringId(edge->name());
2719
  int buffer_pos = 0;
2720
  if (!first_edge) {
2721
    buffer[buffer_pos++] = ',';
2722
  }
2723
  buffer_pos = utoa(edge->type(), buffer, buffer_pos);
2724
  buffer[buffer_pos++] = ',';
2725
  buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
2726
  buffer[buffer_pos++] = ',';
2727
  buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos);
2728
  buffer[buffer_pos++] = '\n';
2729
  buffer[buffer_pos++] = '\0';
2730
  writer_->AddString(buffer.start());
2731
}
2732

    
2733

    
2734
void HeapSnapshotJSONSerializer::SerializeEdges() {
2735
  List<HeapGraphEdge*>& edges = snapshot_->children();
2736
  for (int i = 0; i < edges.length(); ++i) {
2737
    ASSERT(i == 0 ||
2738
           edges[i - 1]->from()->index() <= edges[i]->from()->index());
2739
    SerializeEdge(edges[i], i == 0);
2740
    if (writer_->aborted()) return;
2741
  }
2742
}
2743

    
2744

    
2745
void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
2746
  // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0
2747
  static const int kBufferSize =
2748
      5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned  // NOLINT
2749
      + 5 + 1 + 1;
2750
  EmbeddedVector<char, kBufferSize> buffer;
2751
  int buffer_pos = 0;
2752
  if (entry_index(entry) != 0) {
2753
    buffer[buffer_pos++] = ',';
2754
  }
2755
  buffer_pos = utoa(entry->type(), buffer, buffer_pos);
2756
  buffer[buffer_pos++] = ',';
2757
  buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
2758
  buffer[buffer_pos++] = ',';
2759
  buffer_pos = utoa(entry->id(), buffer, buffer_pos);
2760
  buffer[buffer_pos++] = ',';
2761
  buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
2762
  buffer[buffer_pos++] = ',';
2763
  buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
2764
  buffer[buffer_pos++] = '\n';
2765
  buffer[buffer_pos++] = '\0';
2766
  writer_->AddString(buffer.start());
2767
}
2768

    
2769

    
2770
void HeapSnapshotJSONSerializer::SerializeNodes() {
2771
  List<HeapEntry>& entries = snapshot_->entries();
2772
  for (int i = 0; i < entries.length(); ++i) {
2773
    SerializeNode(&entries[i]);
2774
    if (writer_->aborted()) return;
2775
  }
2776
}
2777

    
2778

    
2779
void HeapSnapshotJSONSerializer::SerializeSnapshot() {
2780
  writer_->AddString("\"title\":\"");
2781
  writer_->AddString(snapshot_->title());
2782
  writer_->AddString("\"");
2783
  writer_->AddString(",\"uid\":");
2784
  writer_->AddNumber(snapshot_->uid());
2785
  writer_->AddString(",\"meta\":");
2786
  // The object describing node serialization layout.
2787
  // We use a set of macros to improve readability.
2788
#define JSON_A(s) "[" s "]"
2789
#define JSON_O(s) "{" s "}"
2790
#define JSON_S(s) "\"" s "\""
2791
  writer_->AddString(JSON_O(
2792
    JSON_S("node_fields") ":" JSON_A(
2793
        JSON_S("type") ","
2794
        JSON_S("name") ","
2795
        JSON_S("id") ","
2796
        JSON_S("self_size") ","
2797
        JSON_S("edge_count")) ","
2798
    JSON_S("node_types") ":" JSON_A(
2799
        JSON_A(
2800
            JSON_S("hidden") ","
2801
            JSON_S("array") ","
2802
            JSON_S("string") ","
2803
            JSON_S("object") ","
2804
            JSON_S("code") ","
2805
            JSON_S("closure") ","
2806
            JSON_S("regexp") ","
2807
            JSON_S("number") ","
2808
            JSON_S("native") ","
2809
            JSON_S("synthetic") ","
2810
            JSON_S("concatenated string") ","
2811
            JSON_S("sliced string")) ","
2812
        JSON_S("string") ","
2813
        JSON_S("number") ","
2814
        JSON_S("number") ","
2815
        JSON_S("number") ","
2816
        JSON_S("number") ","
2817
        JSON_S("number")) ","
2818
    JSON_S("edge_fields") ":" JSON_A(
2819
        JSON_S("type") ","
2820
        JSON_S("name_or_index") ","
2821
        JSON_S("to_node")) ","
2822
    JSON_S("edge_types") ":" JSON_A(
2823
        JSON_A(
2824
            JSON_S("context") ","
2825
            JSON_S("element") ","
2826
            JSON_S("property") ","
2827
            JSON_S("internal") ","
2828
            JSON_S("hidden") ","
2829
            JSON_S("shortcut") ","
2830
            JSON_S("weak")) ","
2831
        JSON_S("string_or_number") ","
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"))));
2846
#undef JSON_S
2847
#undef JSON_O
2848
#undef JSON_A
2849
  writer_->AddString(",\"node_count\":");
2850
  writer_->AddNumber(snapshot_->entries().length());
2851
  writer_->AddString(",\"edge_count\":");
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);
2860
}
2861

    
2862

    
2863
static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
2864
  static const char hex_chars[] = "0123456789ABCDEF";
2865
  w->AddString("\\u");
2866
  w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
2867
  w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
2868
  w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
2869
  w->AddCharacter(hex_chars[u & 0xf]);
2870
}
2871

    
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

    
2967
void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
2968
  writer_->AddCharacter('\n');
2969
  writer_->AddCharacter('\"');
2970
  for ( ; *s != '\0'; ++s) {
2971
    switch (*s) {
2972
      case '\b':
2973
        writer_->AddString("\\b");
2974
        continue;
2975
      case '\f':
2976
        writer_->AddString("\\f");
2977
        continue;
2978
      case '\n':
2979
        writer_->AddString("\\n");
2980
        continue;
2981
      case '\r':
2982
        writer_->AddString("\\r");
2983
        continue;
2984
      case '\t':
2985
        writer_->AddString("\\t");
2986
        continue;
2987
      case '\"':
2988
      case '\\':
2989
        writer_->AddCharacter('\\');
2990
        writer_->AddCharacter(*s);
2991
        continue;
2992
      default:
2993
        if (*s > 31 && *s < 128) {
2994
          writer_->AddCharacter(*s);
2995
        } else if (*s <= 31) {
2996
          // Special character with no dedicated literal.
2997
          WriteUChar(writer_, *s);
2998
        } else {
2999
          // Convert UTF-8 into \u UTF-16 literal.
3000
          unsigned length = 1, cursor = 0;
3001
          for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
3002
          unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
3003
          if (c != unibrow::Utf8::kBadChar) {
3004
            WriteUChar(writer_, c);
3005
            ASSERT(cursor != 0);
3006
            s += cursor - 1;
3007
          } else {
3008
            writer_->AddCharacter('?');
3009
          }
3010
        }
3011
    }
3012
  }
3013
  writer_->AddCharacter('\"');
3014
}
3015

    
3016

    
3017
void HeapSnapshotJSONSerializer::SerializeStrings() {
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
  }
3026
  writer_->AddString("\"<dummy>\"");
3027
  for (int i = 1; i < sorted_strings.length(); ++i) {
3028
    writer_->AddCharacter(',');
3029
    SerializeString(sorted_strings[i]);
3030
    if (writer_->aborted()) return;
3031
  }
3032
}
3033

    
3034

    
3035
} }  // namespace v8::internal