// Copyright 2013 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
#include "v8.h"
#include "allocation-tracker.h"
#include "code-stubs.h"
-#include "heap-profiler.h"
+#include "conversions.h"
#include "debug.h"
+#include "heap-profiler.h"
#include "types.h"
-#include "v8conversions.h"
namespace v8 {
namespace internal {
HeapEntriesMap::HeapEntriesMap()
- : entries_(HeapThingsMatch) {
+ : entries_(HashMap::PointersMatch) {
}
HeapObjectsSet::HeapObjectsSet()
- : entries_(HeapEntriesMap::HeapThingsMatch) {
+ : entries_(HashMap::PointersMatch) {
}
static void MarkVisitedField(HeapObject* obj, int offset) {
if (offset < 0) return;
Address field = obj->address() + offset;
- ASSERT(!Memory::Object_at(field)->IsFailure());
ASSERT(Memory::Object_at(field)->IsHeapObject());
- *field |= kFailureTag;
+ intptr_t p = reinterpret_cast<intptr_t>(Memory::Object_at(field));
+ ASSERT(!IsMarked(p));
+ intptr_t p_tagged = p | kTag;
+ Memory::Object_at(field) = reinterpret_cast<Object*>(p_tagged);
}
private:
bool CheckVisitedAndUnmark(Object** field) {
- if ((*field)->IsFailure()) {
- intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
- *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
+ intptr_t p = reinterpret_cast<intptr_t>(*field);
+ if (IsMarked(p)) {
+ intptr_t p_untagged = (p & ~kTaggingMask) | kHeapObjectTag;
+ *field = reinterpret_cast<Object*>(p_untagged);
ASSERT((*field)->IsHeapObject());
return true;
}
return false;
}
+
+ static const intptr_t kTaggingMask = 3;
+ static const intptr_t kTag = 3;
+
+ static bool IsMarked(intptr_t p) { return (p & kTaggingMask) == kTag; }
+
V8HeapExplorer* generator_;
HeapObject* parent_obj_;
int parent_;
};
-void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
- HeapEntry* heap_entry = GetEntry(obj);
- if (heap_entry == NULL) return; // No interest in this object.
- int entry = heap_entry->index();
+bool V8HeapExplorer::ExtractReferencesPass1(int entry, HeapObject* obj) {
+ if (obj->IsFixedArray()) return false; // FixedArrays are processed on pass 2
if (obj->IsJSGlobalProxy()) {
ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
ExtractJSObjectReferences(entry, JSObject::cast(obj));
} else if (obj->IsString()) {
ExtractStringReferences(entry, String::cast(obj));
- } else if (obj->IsContext()) {
- ExtractContextReferences(entry, Context::cast(obj));
} else if (obj->IsMap()) {
ExtractMapReferences(entry, Map::cast(obj));
} else if (obj->IsSharedFunctionInfo()) {
} else if (obj->IsAllocationSite()) {
ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
}
- SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ return true;
+}
+
- // Extract unvisited fields as hidden references and restore tags
- // of visited fields.
- IndexedReferencesExtractor refs_extractor(this, obj, entry);
- obj->Iterate(&refs_extractor);
+bool V8HeapExplorer::ExtractReferencesPass2(int entry, HeapObject* obj) {
+ if (!obj->IsFixedArray()) return false;
+
+ if (obj->IsContext()) {
+ ExtractContextReferences(entry, Context::cast(obj));
+ } else {
+ ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
+ }
+ return true;
}
}
#define EXTRACT_CONTEXT_FIELD(index, type, name) \
- if (Context::index < Context::FIRST_WEAK_SLOT) { \
+ if (Context::index < Context::FIRST_WEAK_SLOT || \
+ Context::index == Context::MAP_CACHE_INDEX) { \
SetInternalReference(context, entry, #name, context->get(Context::index), \
FixedArray::OffsetOfElementAt(Context::index)); \
} else { \
TagObject(back_pointer, "(back pointer)");
SetInternalReference(transitions, transitions_entry,
"back_pointer", back_pointer);
+
+ if (FLAG_collect_maps && map->CanTransition()) {
+ if (!transitions->IsSimpleTransition()) {
+ if (transitions->HasPrototypeTransitions()) {
+ FixedArray* prototype_transitions =
+ transitions->GetPrototypeTransitions();
+ MarkAsWeakContainer(prototype_transitions);
+ TagObject(prototype_transitions, "(prototype transitions");
+ SetInternalReference(transitions, transitions_entry,
+ "prototype_transitions", prototype_transitions);
+ }
+ // TODO(alph): transitions keys are strong links.
+ MarkAsWeakContainer(transitions);
+ }
+ }
+
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry,
"transitions", transitions,
"descriptors", descriptors,
Map::kDescriptorsOffset);
+ MarkAsWeakContainer(map->code_cache());
SetInternalReference(map, entry,
"code_cache", map->code_cache(),
Map::kCodeCacheOffset);
"constructor", map->constructor(),
Map::kConstructorOffset);
TagObject(map->dependent_code(), "(dependent code)");
+ MarkAsWeakContainer(map->dependent_code());
SetInternalReference(map, entry,
"dependent_code", map->dependent_code(),
Map::kDependentCodeOffset);
SetInternalReference(obj, entry,
"optimized_code_map", shared->optimized_code_map(),
SharedFunctionInfo::kOptimizedCodeMapOffset);
+ SetInternalReference(obj, entry,
+ "feedback_vector", shared->feedback_vector(),
+ SharedFunctionInfo::kFeedbackVectorOffset);
SetWeakReference(obj, entry,
"initial_map", shared->initial_map(),
SharedFunctionInfo::kInitialMapOffset);
ExtractCellReferences(entry, cell);
SetInternalReference(cell, entry, "type", cell->type(),
PropertyCell::kTypeOffset);
+ MarkAsWeakContainer(cell->dependent_code());
SetInternalReference(cell, entry, "dependent_code", cell->dependent_code(),
PropertyCell::kDependentCodeOffset);
}
AllocationSite::kTransitionInfoOffset);
SetInternalReference(site, entry, "nested_site", site->nested_site(),
AllocationSite::kNestedSiteOffset);
+ MarkAsWeakContainer(site->dependent_code());
SetInternalReference(site, entry, "dependent_code", site->dependent_code(),
AllocationSite::kDependentCodeOffset);
// Do not visit weak_next as it is not visited by the StaticVisitor,
}
+void V8HeapExplorer::ExtractFixedArrayReferences(int entry, FixedArray* array) {
+ bool is_weak = weak_containers_.Contains(array);
+ for (int i = 0, l = array->length(); i < l; ++i) {
+ if (is_weak) {
+ SetWeakReference(array, entry,
+ i, array->get(i), array->OffsetOfElementAt(i));
+ } else {
+ SetInternalReference(array, entry,
+ i, array->get(i), array->OffsetOfElementAt(i));
+ }
+ }
+}
+
+
void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
if (!js_obj->IsJSFunction()) return;
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
break;
- case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
// return its name. This is for instances of binding objects, which
// have prototype constructor type "Object".
Object* constructor_prop = NULL;
- LookupResult result(heap->isolate());
- object->LocalLookupRealNamedProperty(heap->constructor_string(), &result);
+ Isolate* isolate = heap->isolate();
+ LookupResult result(isolate);
+ object->LocalLookupRealNamedProperty(
+ isolate->factory()->constructor_string(), &result);
if (!result.IsFound()) return object->constructor_name();
constructor_prop = result.GetLazyValue();
heap_->IterateRoots(&extractor, VISIT_ALL);
extractor.FillReferences(this);
+ // We have to do two passes as sometimes FixedArrays are used
+ // to weakly hold their items, and it's impossible to distinguish
+ // between these cases without processing the array owner first.
+ bool interrupted =
+ IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass1>() ||
+ IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass2>();
+
+ if (interrupted) {
+ filler_ = NULL;
+ return false;
+ }
+
+ filler_ = NULL;
+ return progress_->ProgressReport(true);
+}
+
+
+template<V8HeapExplorer::ExtractReferencesMethod extractor>
+bool V8HeapExplorer::IterateAndExtractSinglePass() {
// Now iterate the whole heap.
bool interrupted = false;
HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next(), progress_->ProgressStep()) {
- if (!interrupted) {
- ExtractReferences(obj);
- if (!progress_->ProgressReport(false)) interrupted = true;
+ if (interrupted) continue;
+
+ HeapEntry* heap_entry = GetEntry(obj);
+ int entry = heap_entry->index();
+ if ((this->*extractor)(entry, obj)) {
+ SetInternalReference(obj, entry,
+ "map", obj->map(), HeapObject::kMapOffset);
+ // Extract unvisited fields as hidden references and restore tags
+ // of visited fields.
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
}
- }
- if (interrupted) {
- filler_ = NULL;
- return false;
- }
- filler_ = NULL;
- return progress_->ProgressReport(true);
+ if (!progress_->ProgressReport(false)) interrupted = true;
+ }
+ return interrupted;
}
}
+void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry == NULL) return;
+ if (IsEssentialObject(child_obj)) {
+ filler_->SetNamedReference(HeapGraphEdge::kWeak,
+ parent_entry,
+ names_->GetFormatted("%d", index),
+ child_entry);
+ }
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+}
+
+
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
int parent_entry,
Name* reference_name,
GlobalObject* global = context->global_object();
if (global->IsJSGlobalObject()) {
bool is_debug_object = false;
-#ifdef ENABLE_DEBUGGER_SUPPORT
is_debug_object = heap_->isolate()->debug()->IsDebugGlobal(global);
-#endif
if (!is_debug_object && !user_roots_.Contains(global)) {
user_roots_.Insert(global);
SetUserGlobalReference(global);
}
+void V8HeapExplorer::MarkAsWeakContainer(Object* object) {
+ if (IsEssentialObject(object) && object->IsFixedArray()) {
+ weak_containers_.Insert(object);
+ }
+}
+
+
class GlobalObjectsEnumerator : public ObjectVisitor {
public:
virtual void VisitPointers(Object** start, Object** end) {
debug_heap->Verify();
#endif
- SetProgressTotal(1); // 1 pass.
+ SetProgressTotal(2); // 2 passes.
#ifdef VERIFY_HEAP
debug_heap->Verify();