Commit 40c34606 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[heap] Instrument mark-compact to track retaining paths for debugging.

This patch adds a new intrinsic: %DebugTrackRetainingPath(object).
Calling the intrinsic in JS code saves a weak reference to the given
object in GC internal table of tracked objects.

Each subsequent full GC prints to stdout the retaining path for each
tracked object (if it is still alive). The retaining path is the real
path that the marker took from the root set to the tracked object.

This is useful for investigating of memory leaks:
1) Add %DebugTrackRetainingPath(leaking_object) in JS code.
For example:
  function foo() {
    let x = { bar: "bar"};
    %DebugTrackRetainingPath(x);
    return () => { return x; }
  }

  let closure = foo();
  gc();

2) Run d8 with --allow-natives-syntax --track-retaining-path --expose-gc.

3) Check the retaining path in stdout.

For more detailed inspection, run d8 in gdb and set breakpoint in

v8: :internal::Heap::PrintRetainingPath.
Change-Id: I01a0faac1e009bc6c321fa75613900b49d2b036f
Reviewed-on: https://chromium-review.googlesource.com/575972
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46766}
parent 7cfd0c24
...@@ -650,11 +650,15 @@ DEFINE_BOOL(track_gc_object_stats, false, ...@@ -650,11 +650,15 @@ DEFINE_BOOL(track_gc_object_stats, false,
"track object counts and memory usage") "track object counts and memory usage")
DEFINE_BOOL(trace_gc_object_stats, false, DEFINE_BOOL(trace_gc_object_stats, false,
"trace object counts and memory usage") "trace object counts and memory usage")
DEFINE_BOOL(track_retaining_path, false,
"enable support for tracking retaining path")
DEFINE_INT(gc_stats, 0, "Used by tracing internally to enable gc statistics") DEFINE_INT(gc_stats, 0, "Used by tracing internally to enable gc statistics")
DEFINE_IMPLICATION(trace_gc_object_stats, track_gc_object_stats) DEFINE_IMPLICATION(trace_gc_object_stats, track_gc_object_stats)
DEFINE_VALUE_IMPLICATION(track_gc_object_stats, gc_stats, 1) DEFINE_VALUE_IMPLICATION(track_gc_object_stats, gc_stats, 1)
DEFINE_VALUE_IMPLICATION(trace_gc_object_stats, gc_stats, 1) DEFINE_VALUE_IMPLICATION(trace_gc_object_stats, gc_stats, 1)
DEFINE_NEG_IMPLICATION(trace_gc_object_stats, incremental_marking) DEFINE_NEG_IMPLICATION(trace_gc_object_stats, incremental_marking)
DEFINE_NEG_IMPLICATION(track_retaining_path, incremental_marking)
DEFINE_NEG_IMPLICATION(track_retaining_path, concurrent_marking)
DEFINE_BOOL(track_detached_contexts, true, DEFINE_BOOL(track_detached_contexts, true,
"track native contexts that are expected to be garbage collected") "track native contexts that are expected to be garbage collected")
DEFINE_BOOL(trace_detached_contexts, false, DEFINE_BOOL(trace_detached_contexts, false,
......
...@@ -407,6 +407,99 @@ void Heap::ReportStatisticsAfterGC() { ...@@ -407,6 +407,99 @@ void Heap::ReportStatisticsAfterGC() {
} }
} }
void Heap::AddRetainingPathTarget(Handle<HeapObject> object) {
if (!FLAG_track_retaining_path) {
PrintF("Retaining path tracking requires --trace-retaining-path\n");
} else {
Handle<WeakFixedArray> array = WeakFixedArray::Add(
handle(retaining_path_targets(), isolate()), object);
set_retaining_path_targets(*array);
}
}
bool Heap::IsRetainingPathTarget(HeapObject* object) {
WeakFixedArray::Iterator it(retaining_path_targets());
HeapObject* target;
while ((target = it.Next<HeapObject>()) != nullptr) {
if (target == object) return true;
}
return false;
}
namespace {
const char* RootToString(Root root) {
switch (root) {
#define ROOT_CASE(root_id, ignore, description) \
case Root::root_id: \
return description;
ROOT_ID_LIST(ROOT_CASE)
#undef ROOT_CASE
case Root::kCodeFlusher:
return "(Code flusher)";
case Root::kPartialSnapshotCache:
return "(Partial snapshot cache)";
case Root::kWeakCollections:
return "(Weak collections)";
case Root::kWrapperTracing:
return "(Wrapper tracing)";
case Root::kUnknown:
return "(Unknown)";
}
UNREACHABLE();
return nullptr;
}
} // namespace
void Heap::PrintRetainingPath(HeapObject* target) {
PrintF("\n\n\n");
PrintF("#################################################\n");
PrintF("Retaining path for %p:\n", static_cast<void*>(target));
HeapObject* object = target;
std::vector<HeapObject*> retaining_path;
Root root = Root::kUnknown;
while (true) {
retaining_path.push_back(object);
if (retainer_.count(object)) {
object = retainer_[object];
} else {
if (retaining_root_.count(object)) {
root = retaining_root_[object];
}
break;
}
}
int distance = static_cast<int>(retaining_path.size());
for (auto object : retaining_path) {
PrintF("\n");
PrintF("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
PrintF("Distance from root %d: ", distance);
object->ShortPrint();
PrintF("\n");
#ifdef OBJECT_PRINT
object->Print();
PrintF("\n");
#endif
--distance;
}
PrintF("\n");
PrintF("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
PrintF("Root: %s\n", RootToString(root));
PrintF("-------------------------------------------------\n");
}
void Heap::AddRetainer(HeapObject* retainer, HeapObject* object) {
retainer_[object] = retainer;
if (IsRetainingPathTarget(object)) {
PrintRetainingPath(object);
}
}
void Heap::AddRetainingRoot(Root root, HeapObject* object) {
retaining_root_[object] = root;
if (IsRetainingPathTarget(object)) {
PrintRetainingPath(object);
}
}
void Heap::IncrementDeferredCount(v8::Isolate::UseCounterFeature feature) { void Heap::IncrementDeferredCount(v8::Isolate::UseCounterFeature feature) {
deferred_counters_[feature]++; deferred_counters_[feature]++;
...@@ -452,6 +545,10 @@ void Heap::GarbageCollectionPrologue() { ...@@ -452,6 +545,10 @@ void Heap::GarbageCollectionPrologue() {
} }
CheckNewSpaceExpansionCriteria(); CheckNewSpaceExpansionCriteria();
UpdateNewSpaceAllocationCounter(); UpdateNewSpaceAllocationCounter();
if (FLAG_track_retaining_path) {
retainer_.clear();
retaining_root_.clear();
}
} }
size_t Heap::SizeOfObjects() { size_t Heap::SizeOfObjects() {
...@@ -966,6 +1063,7 @@ void Heap::CollectAllAvailableGarbage(GarbageCollectionReason gc_reason) { ...@@ -966,6 +1063,7 @@ void Heap::CollectAllAvailableGarbage(GarbageCollectionReason gc_reason) {
break; break;
} }
} }
set_current_gc_flags(kNoGCFlags); set_current_gc_flags(kNoGCFlags);
new_space_->Shrink(); new_space_->Shrink();
UncommitFromSpace(); UncommitFromSpace();
...@@ -2859,6 +2957,7 @@ void Heap::CreateInitialObjects() { ...@@ -2859,6 +2957,7 @@ void Heap::CreateInitialObjects() {
set_detached_contexts(empty_fixed_array()); set_detached_contexts(empty_fixed_array());
set_retained_maps(ArrayList::cast(empty_fixed_array())); set_retained_maps(ArrayList::cast(empty_fixed_array()));
set_retaining_path_targets(undefined_value());
set_weak_object_to_code_table(*WeakHashTable::New(isolate(), 16, TENURED)); set_weak_object_to_code_table(*WeakHashTable::New(isolate(), 16, TENURED));
...@@ -2994,6 +3093,7 @@ bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) { ...@@ -2994,6 +3093,7 @@ bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) {
case kWeakObjectToCodeTableRootIndex: case kWeakObjectToCodeTableRootIndex:
case kWeakNewSpaceObjectToCodeListRootIndex: case kWeakNewSpaceObjectToCodeListRootIndex:
case kRetainedMapsRootIndex: case kRetainedMapsRootIndex:
case kRetainingPathTargetsRootIndex:
case kCodeCoverageListRootIndex: case kCodeCoverageListRootIndex:
case kNoScriptSharedFunctionInfosRootIndex: case kNoScriptSharedFunctionInfosRootIndex:
case kWeakStackTraceListRootIndex: case kWeakStackTraceListRootIndex:
...@@ -4508,7 +4608,6 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action, ...@@ -4508,7 +4608,6 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action,
return result; return result;
} }
void Heap::IdleNotificationEpilogue(GCIdleTimeAction action, void Heap::IdleNotificationEpilogue(GCIdleTimeAction action,
GCIdleTimeHeapState heap_state, GCIdleTimeHeapState heap_state,
double start_ms, double deadline_in_ms) { double start_ms, double deadline_in_ms) {
...@@ -5908,7 +6007,7 @@ void Heap::RegisterExternallyReferencedObject(Object** object) { ...@@ -5908,7 +6007,7 @@ void Heap::RegisterExternallyReferencedObject(Object** object) {
incremental_marking()->WhiteToGreyAndPush(heap_object); incremental_marking()->WhiteToGreyAndPush(heap_object);
} else { } else {
DCHECK(mark_compact_collector()->in_use()); DCHECK(mark_compact_collector()->in_use());
mark_compact_collector()->MarkObject(heap_object); mark_compact_collector()->MarkExternallyReferencedObject(heap_object);
} }
} }
......
...@@ -203,6 +203,7 @@ using v8::MemoryPressureLevel; ...@@ -203,6 +203,7 @@ using v8::MemoryPressureLevel;
V(FixedArray, materialized_objects, MaterializedObjects) \ V(FixedArray, materialized_objects, MaterializedObjects) \
V(FixedArray, microtask_queue, MicrotaskQueue) \ V(FixedArray, microtask_queue, MicrotaskQueue) \
V(FixedArray, detached_contexts, DetachedContexts) \ V(FixedArray, detached_contexts, DetachedContexts) \
V(HeapObject, retaining_path_targets, RetainingPathTargets) \
V(ArrayList, retained_maps, RetainedMaps) \ V(ArrayList, retained_maps, RetainedMaps) \
V(WeakHashTable, weak_object_to_code_table, WeakObjectToCodeTable) \ V(WeakHashTable, weak_object_to_code_table, WeakObjectToCodeTable) \
/* weak_new_space_object_to_code_list is an array of weak cells, where */ \ /* weak_new_space_object_to_code_list is an array of weak cells, where */ \
...@@ -1480,8 +1481,16 @@ class Heap { ...@@ -1480,8 +1481,16 @@ class Heap {
void MergeAllocationSitePretenuringFeedback( void MergeAllocationSitePretenuringFeedback(
const base::HashMap& local_pretenuring_feedback); const base::HashMap& local_pretenuring_feedback);
// ============================================================================= // ===========================================================================
// Retaining path tracking. ==================================================
// ===========================================================================
// Adds the given object to the weak table of retaining path targets.
// On each GC if the marker discovers the object, it will print the retaining
// path. This requires --track-retaining-path flag.
void AddRetainingPathTarget(Handle<HeapObject> object);
// =============================================================================
#ifdef VERIFY_HEAP #ifdef VERIFY_HEAP
// Verify the heap is in its normal state before or after a GC. // Verify the heap is in its normal state before or after a GC.
void Verify(); void Verify();
...@@ -2142,9 +2151,16 @@ class Heap { ...@@ -2142,9 +2151,16 @@ class Heap {
MUST_USE_RESULT AllocationResult MUST_USE_RESULT AllocationResult
AllocateCode(int object_size, bool immovable); AllocateCode(int object_size, bool immovable);
void set_force_oom(bool value) { force_oom_ = value; }
// ===========================================================================
// Retaining path tracing ====================================================
// =========================================================================== // ===========================================================================
void set_force_oom(bool value) { force_oom_ = value; } void AddRetainer(HeapObject* retainer, HeapObject* object);
void AddRetainingRoot(Root root, HeapObject* object);
bool IsRetainingPathTarget(HeapObject* object);
void PrintRetainingPath(HeapObject* object);
// The amount of external memory registered through the API. // The amount of external memory registered through the API.
int64_t external_memory_; int64_t external_memory_;
...@@ -2382,12 +2398,17 @@ class Heap { ...@@ -2382,12 +2398,17 @@ class Heap {
HeapObject* pending_layout_change_object_; HeapObject* pending_layout_change_object_;
std::map<HeapObject*, HeapObject*> retainer_;
std::map<HeapObject*, Root> retaining_root_;
// Classes in "heap" can be friends. // Classes in "heap" can be friends.
friend class AlwaysAllocateScope; friend class AlwaysAllocateScope;
friend class ConcurrentMarking; friend class ConcurrentMarking;
friend class GCCallbacksScope; friend class GCCallbacksScope;
friend class GCTracer; friend class GCTracer;
friend class HeapIterator; friend class HeapIterator;
template <typename ConcreteVisitor>
friend class MarkingVisitor;
friend class IdleScavengeObserver; friend class IdleScavengeObserver;
friend class IncrementalMarking; friend class IncrementalMarking;
friend class IncrementalMarkingJob; friend class IncrementalMarkingJob;
......
...@@ -294,7 +294,7 @@ class IncrementalMarkingMarkingVisitor final ...@@ -294,7 +294,7 @@ class IncrementalMarkingMarkingVisitor final
Object* target = *p; Object* target = *p;
if (target->IsHeapObject()) { if (target->IsHeapObject()) {
collector_->RecordSlot(host, p, target); collector_->RecordSlot(host, p, target);
MarkObject(target); MarkObject(host, target);
} }
} }
...@@ -304,13 +304,13 @@ class IncrementalMarkingMarkingVisitor final ...@@ -304,13 +304,13 @@ class IncrementalMarkingMarkingVisitor final
Object* target = *p; Object* target = *p;
if (target->IsHeapObject()) { if (target->IsHeapObject()) {
collector_->RecordSlot(host, p, target); collector_->RecordSlot(host, p, target);
MarkObject(target); MarkObject(host, target);
} }
} }
} }
// Marks the object grey and pushes it on the marking stack. // Marks the object grey and pushes it on the marking stack.
V8_INLINE void MarkObject(Object* obj) { V8_INLINE void MarkObject(HeapObject* host, Object* obj) {
incremental_marking_->WhiteToGreyAndPush(HeapObject::cast(obj)); incremental_marking_->WhiteToGreyAndPush(HeapObject::cast(obj));
} }
......
...@@ -21,10 +21,23 @@ void MarkCompactCollector::PushBlack(HeapObject* obj) { ...@@ -21,10 +21,23 @@ void MarkCompactCollector::PushBlack(HeapObject* obj) {
} }
} }
void MarkCompactCollector::MarkObject(HeapObject* obj) { void MarkCompactCollector::MarkObject(HeapObject* host, HeapObject* obj) {
if (ObjectMarking::WhiteToBlack<AccessMode::NON_ATOMIC>( if (ObjectMarking::WhiteToBlack<AccessMode::NON_ATOMIC>(
obj, MarkingState::Internal(obj))) { obj, MarkingState::Internal(obj))) {
PushBlack(obj); PushBlack(obj);
if (V8_UNLIKELY(FLAG_track_retaining_path)) {
heap_->AddRetainer(host, obj);
}
}
}
void MarkCompactCollector::MarkExternallyReferencedObject(HeapObject* obj) {
if (ObjectMarking::WhiteToBlack<AccessMode::NON_ATOMIC>(
obj, MarkingState::Internal(obj))) {
PushBlack(obj);
if (V8_UNLIKELY(FLAG_track_retaining_path)) {
heap_->AddRetainingRoot(Root::kWrapperTracing, obj);
}
} }
} }
......
...@@ -1052,7 +1052,8 @@ class MarkCompactMarkingVisitor final ...@@ -1052,7 +1052,8 @@ class MarkCompactMarkingVisitor final
Object** end) final { Object** end) final {
// Mark all objects pointed to in [start, end). // Mark all objects pointed to in [start, end).
const int kMinRangeForMarkingRecursion = 64; const int kMinRangeForMarkingRecursion = 64;
if (end - start >= kMinRangeForMarkingRecursion) { if (end - start >= kMinRangeForMarkingRecursion &&
V8_LIKELY(!FLAG_track_retaining_path)) {
if (VisitUnmarkedObjects(host, start, end)) return; if (VisitUnmarkedObjects(host, start, end)) return;
// We are close to a stack overflow, so just mark the objects. // We are close to a stack overflow, so just mark the objects.
} }
...@@ -1062,8 +1063,8 @@ class MarkCompactMarkingVisitor final ...@@ -1062,8 +1063,8 @@ class MarkCompactMarkingVisitor final
} }
// Marks the object black and pushes it on the marking stack. // Marks the object black and pushes it on the marking stack.
V8_INLINE void MarkObject(HeapObject* object) { V8_INLINE void MarkObject(HeapObject* host, HeapObject* object) {
collector_->MarkObject(object); collector_->MarkObject(host, object);
} }
// Marks the object black without pushing it on the marking stack. Returns // Marks the object black without pushing it on the marking stack. Returns
...@@ -1076,7 +1077,7 @@ class MarkCompactMarkingVisitor final ...@@ -1076,7 +1077,7 @@ class MarkCompactMarkingVisitor final
if (!(*p)->IsHeapObject()) return; if (!(*p)->IsHeapObject()) return;
HeapObject* target_object = HeapObject::cast(*p); HeapObject* target_object = HeapObject::cast(*p);
collector_->RecordSlot(host, p, target_object); collector_->RecordSlot(host, p, target_object);
collector_->MarkObject(target_object); collector_->MarkObject(host, target_object);
} }
protected: protected:
...@@ -1106,7 +1107,7 @@ class MarkCompactMarkingVisitor final ...@@ -1106,7 +1107,7 @@ class MarkCompactMarkingVisitor final
Map* map = obj->map(); Map* map = obj->map();
ObjectMarking::WhiteToBlack(obj, MarkingState::Internal(obj)); ObjectMarking::WhiteToBlack(obj, MarkingState::Internal(obj));
// Mark the map pointer and the body. // Mark the map pointer and the body.
collector_->MarkObject(map); collector_->MarkObject(obj, map);
Visit(map, obj); Visit(map, obj);
} }
} }
...@@ -1132,19 +1133,20 @@ class MarkCompactCollector::RootMarkingVisitor : public ObjectVisitor, ...@@ -1132,19 +1133,20 @@ class MarkCompactCollector::RootMarkingVisitor : public ObjectVisitor,
: collector_(heap->mark_compact_collector()), visitor_(collector_) {} : collector_(heap->mark_compact_collector()), visitor_(collector_) {}
void VisitPointer(HeapObject* host, Object** p) override { void VisitPointer(HeapObject* host, Object** p) override {
MarkObjectByPointer(p); MarkObjectByPointer(host, p);
} }
void VisitPointers(HeapObject* host, Object** start, Object** end) override { void VisitPointers(HeapObject* host, Object** start, Object** end) override {
for (Object** p = start; p < end; p++) MarkObjectByPointer(p); for (Object** p = start; p < end; p++) MarkObjectByPointer(host, p);
} }
void VisitRootPointer(Root root, Object** p) override { void VisitRootPointer(Root root, Object** p) override {
MarkObjectByPointer(p); MarkObjectByPointer(nullptr, p, root);
} }
void VisitRootPointers(Root root, Object** start, Object** end) override { void VisitRootPointers(Root root, Object** start, Object** end) override {
for (Object** p = start; p < end; p++) MarkObjectByPointer(p); for (Object** p = start; p < end; p++)
MarkObjectByPointer(nullptr, p, root);
} }
// Skip the weak next code link in a code object, which is visited in // Skip the weak next code link in a code object, which is visited in
...@@ -1152,16 +1154,24 @@ class MarkCompactCollector::RootMarkingVisitor : public ObjectVisitor, ...@@ -1152,16 +1154,24 @@ class MarkCompactCollector::RootMarkingVisitor : public ObjectVisitor,
void VisitNextCodeLink(Code* host, Object** p) override {} void VisitNextCodeLink(Code* host, Object** p) override {}
private: private:
void MarkObjectByPointer(Object** p) { void MarkObjectByPointer(HeapObject* host, Object** p,
Root root = Root::kUnknown) {
if (!(*p)->IsHeapObject()) return; if (!(*p)->IsHeapObject()) return;
HeapObject* object = HeapObject::cast(*p); HeapObject* object = HeapObject::cast(*p);
if (ObjectMarking::WhiteToBlack<AccessMode::NON_ATOMIC>( if (ObjectMarking::WhiteToBlack<AccessMode::NON_ATOMIC>(
object, MarkingState::Internal(object))) { object, MarkingState::Internal(object))) {
if (V8_UNLIKELY(FLAG_track_retaining_path)) {
if (host) {
object->GetHeap()->AddRetainer(host, object);
} else {
object->GetHeap()->AddRetainingRoot(root, object);
}
}
Map* map = object->map(); Map* map = object->map();
// Mark the map pointer and body, and push them on the marking stack. // Mark the map pointer and body, and push them on the marking stack.
collector_->MarkObject(map); collector_->MarkObject(object, map);
visitor_.Visit(map, object); visitor_.Visit(map, object);
// Mark all the objects reachable from the map and body. May leave // Mark all the objects reachable from the map and body. May leave
// overflowed objects in the heap. // overflowed objects in the heap.
...@@ -1961,7 +1971,7 @@ void MarkCompactCollector::EmptyMarkingWorklist() { ...@@ -1961,7 +1971,7 @@ void MarkCompactCollector::EmptyMarkingWorklist() {
object, MarkingState::Internal(object)))); object, MarkingState::Internal(object))));
Map* map = object->map(); Map* map = object->map();
MarkObject(map); MarkObject(object, map);
visitor.Visit(map, object); visitor.Visit(map, object);
} }
DCHECK(marking_worklist()->IsEmpty()); DCHECK(marking_worklist()->IsEmpty());
......
...@@ -681,7 +681,10 @@ class MarkCompactCollector final : public MarkCompactCollectorBase { ...@@ -681,7 +681,10 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
// Marks the object black and pushes it on the marking stack. // Marks the object black and pushes it on the marking stack.
// This is for non-incremental marking only. // This is for non-incremental marking only.
V8_INLINE void MarkObject(HeapObject* obj); V8_INLINE void MarkObject(HeapObject* host, HeapObject* obj);
// Used by wrapper tracing.
V8_INLINE void MarkExternallyReferencedObject(HeapObject* obj);
// Mark the heap roots and all objects reachable from them. // Mark the heap roots and all objects reachable from them.
void MarkRoots(RootMarkingVisitor* visitor); void MarkRoots(RootMarkingVisitor* visitor);
......
...@@ -321,6 +321,9 @@ void MarkingVisitor<ConcreteVisitor>::MarkMapContents(Map* map) { ...@@ -321,6 +321,9 @@ void MarkingVisitor<ConcreteVisitor>::MarkMapContents(Map* map) {
// just mark the entire descriptor array. // just mark the entire descriptor array.
if (!map->is_prototype_map()) { if (!map->is_prototype_map()) {
DescriptorArray* descriptors = map->instance_descriptors(); DescriptorArray* descriptors = map->instance_descriptors();
if (V8_UNLIKELY(FLAG_track_retaining_path)) {
heap_->AddRetainer(map, descriptors);
}
if (visitor->MarkObjectWithoutPush(descriptors) && if (visitor->MarkObjectWithoutPush(descriptors) &&
descriptors->length() > 0) { descriptors->length() > 0) {
visitor->VisitPointers(descriptors, descriptors->GetFirstElementAddress(), visitor->VisitPointers(descriptors, descriptors->GetFirstElementAddress(),
...@@ -392,7 +395,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeEntry(JSFunction* host, ...@@ -392,7 +395,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeEntry(JSFunction* host,
ConcreteVisitor* visitor = static_cast<ConcreteVisitor*>(this); ConcreteVisitor* visitor = static_cast<ConcreteVisitor*>(this);
Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address)); Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
collector_->RecordCodeEntrySlot(host, entry_address, code); collector_->RecordCodeEntrySlot(host, entry_address, code);
visitor->MarkObject(code); visitor->MarkObject(host, code);
} }
template <typename ConcreteVisitor> template <typename ConcreteVisitor>
...@@ -403,7 +406,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitEmbeddedPointer(Code* host, ...@@ -403,7 +406,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitEmbeddedPointer(Code* host,
HeapObject* object = HeapObject::cast(rinfo->target_object()); HeapObject* object = HeapObject::cast(rinfo->target_object());
collector_->RecordRelocSlot(host, rinfo, object); collector_->RecordRelocSlot(host, rinfo, object);
if (!host->IsWeakObject(object)) { if (!host->IsWeakObject(object)) {
visitor->MarkObject(object); visitor->MarkObject(host, object);
} }
} }
...@@ -415,7 +418,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCellPointer(Code* host, ...@@ -415,7 +418,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCellPointer(Code* host,
Cell* cell = rinfo->target_cell(); Cell* cell = rinfo->target_cell();
collector_->RecordRelocSlot(host, rinfo, cell); collector_->RecordRelocSlot(host, rinfo, cell);
if (!host->IsWeakObject(cell)) { if (!host->IsWeakObject(cell)) {
visitor->MarkObject(cell); visitor->MarkObject(host, cell);
} }
} }
...@@ -427,7 +430,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitDebugTarget(Code* host, ...@@ -427,7 +430,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitDebugTarget(Code* host,
rinfo->IsPatchedDebugBreakSlotSequence()); rinfo->IsPatchedDebugBreakSlotSequence());
Code* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address()); Code* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address());
collector_->RecordRelocSlot(host, rinfo, target); collector_->RecordRelocSlot(host, rinfo, target);
visitor->MarkObject(target); visitor->MarkObject(host, target);
} }
template <typename ConcreteVisitor> template <typename ConcreteVisitor>
...@@ -437,7 +440,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeTarget(Code* host, ...@@ -437,7 +440,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeTarget(Code* host,
DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode()));
Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
collector_->RecordRelocSlot(host, rinfo, target); collector_->RecordRelocSlot(host, rinfo, target);
visitor->MarkObject(target); visitor->MarkObject(host, target);
} }
template <typename ConcreteVisitor> template <typename ConcreteVisitor>
...@@ -448,7 +451,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeAgeSequence(Code* host, ...@@ -448,7 +451,7 @@ void MarkingVisitor<ConcreteVisitor>::VisitCodeAgeSequence(Code* host,
Code* target = rinfo->code_age_stub(); Code* target = rinfo->code_age_stub();
DCHECK_NOT_NULL(target); DCHECK_NOT_NULL(target);
collector_->RecordRelocSlot(host, rinfo, target); collector_->RecordRelocSlot(host, rinfo, target);
visitor->MarkObject(target); visitor->MarkObject(host, target);
} }
} // namespace internal } // namespace internal
......
...@@ -269,6 +269,7 @@ void ByteArray::ByteArrayPrint(std::ostream& os) { // NOLINT ...@@ -269,6 +269,7 @@ void ByteArray::ByteArrayPrint(std::ostream& os) { // NOLINT
void BytecodeArray::BytecodeArrayPrint(std::ostream& os) { // NOLINT void BytecodeArray::BytecodeArrayPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "BytecodeArray");
Disassemble(os); Disassemble(os);
} }
...@@ -1090,9 +1091,22 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT ...@@ -1090,9 +1091,22 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
os << "\n - bytecode = " << shared()->bytecode_array(); os << "\n - bytecode = " << shared()->bytecode_array();
} }
} }
shared()->PrintSourceCode(os);
JSObjectPrintBody(os, this); JSObjectPrintBody(os, this);
} }
void SharedFunctionInfo::PrintSourceCode(std::ostream& os) {
if (HasSourceCode()) {
os << "\n - source code = ";
String* source = String::cast(Script::cast(script())->source());
int start = start_position();
int length = end_position() - start;
std::unique_ptr<char[]> source_string = source->ToCString(
DISALLOW_NULLS, FAST_STRING_TRAVERSAL, start, length, NULL);
os << source_string.get();
}
}
void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "SharedFunctionInfo"); HeapObject::PrintHeader(os, "SharedFunctionInfo");
os << "\n - name = "; os << "\n - name = ";
...@@ -1113,15 +1127,7 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT ...@@ -1113,15 +1127,7 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT
if (HasBytecodeArray()) { if (HasBytecodeArray()) {
os << "\n - bytecode_array = " << bytecode_array(); os << "\n - bytecode_array = " << bytecode_array();
} }
if (HasSourceCode()) { PrintSourceCode(os);
os << "\n - source code = ";
String* source = String::cast(Script::cast(script())->source());
int start = start_position();
int length = end_position() - start;
std::unique_ptr<char[]> source_string = source->ToCString(
DISALLOW_NULLS, FAST_STRING_TRAVERSAL, start, length, NULL);
os << source_string.get();
}
// Script files are often large, hard to read. // Script files are often large, hard to read.
// os << "\n - script ="; // os << "\n - script =";
// script()->Print(os); // script()->Print(os);
......
...@@ -391,6 +391,9 @@ class SharedFunctionInfo : public HeapObject { ...@@ -391,6 +391,9 @@ class SharedFunctionInfo : public HeapObject {
// Dispatched behavior. // Dispatched behavior.
DECL_PRINTER(SharedFunctionInfo) DECL_PRINTER(SharedFunctionInfo)
DECL_VERIFIER(SharedFunctionInfo) DECL_VERIFIER(SharedFunctionInfo)
#ifdef OBJECT_PRINT
void PrintSourceCode(std::ostream& os);
#endif
void ResetForNewContext(int new_ic_age); void ResetForNewContext(int new_ic_age);
......
...@@ -612,6 +612,17 @@ RUNTIME_FUNCTION(Runtime_DebugTrace) { ...@@ -612,6 +612,17 @@ RUNTIME_FUNCTION(Runtime_DebugTrace) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugTrackRetainingPath) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
if (!FLAG_track_retaining_path) {
PrintF("DebugTrackRetainingPath requires --track-retaining-path flag.\n");
} else {
CONVERT_ARG_HANDLE_CHECKED(HeapObject, object, 0);
isolate->heap()->AddRetainingPathTarget(object);
}
return isolate->heap()->undefined_value();
}
// This will not allocate (flatten the string), but it may run // This will not allocate (flatten the string), but it may run
// very slowly for very deeply nested ConsStrings. For debugging use only. // very slowly for very deeply nested ConsStrings. For debugging use only.
......
...@@ -569,6 +569,7 @@ namespace internal { ...@@ -569,6 +569,7 @@ namespace internal {
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \ F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
F(DebugPrint, 1, 1) \ F(DebugPrint, 1, 1) \
F(DebugTrace, 0, 1) \ F(DebugTrace, 0, 1) \
F(DebugTrackRetainingPath, 1, 1) \
F(GetExceptionDetails, 1, 1) \ F(GetExceptionDetails, 1, 1) \
F(GlobalPrint, 1, 1) \ F(GlobalPrint, 1, 1) \
F(SystemBreak, 0, 1) \ F(SystemBreak, 0, 1) \
......
...@@ -48,7 +48,9 @@ enum class Root { ...@@ -48,7 +48,9 @@ enum class Root {
// TODO(ulan): Merge with the ROOT_ID_LIST. // TODO(ulan): Merge with the ROOT_ID_LIST.
kCodeFlusher, kCodeFlusher,
kPartialSnapshotCache, kPartialSnapshotCache,
kWeakCollections kWeakCollections,
kWrapperTracing,
kUnknown
}; };
// Abstract base class for visiting, and optionally modifying, the // Abstract base class for visiting, and optionally modifying, the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment