PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorUpdateCountersEvent::data() { RefPtr<JSONObject> data = JSONObject::create(); if (isMainThread()) { data->setNumber("documents", InspectorCounters::counterValue(InspectorCounters::DocumentCounter)); data->setNumber("nodes", InspectorCounters::counterValue(InspectorCounters::NodeCounter)); data->setNumber("jsEventListeners", InspectorCounters::counterValue(InspectorCounters::JSEventListenerCounter)); } data->setNumber("jsHeapSizeUsed", static_cast<double>(usedHeapSize())); return TracedValue::fromJSONValue(data); }
void V8GCController::gcPrologue(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags) { if (isMainThread()) ScriptForbiddenScope::enter(); // TODO(haraken): A GC callback is not allowed to re-enter V8. This means // that it's unsafe to run Oilpan's GC in the GC callback because it may // run finalizers that call into V8. To avoid the risk, we should post // a task to schedule the Oilpan's GC. // (In practice, there is no finalizer that calls into V8 and thus is safe.) v8::HandleScope scope(isolate); switch (type) { case v8::kGCTypeScavenge: if (ThreadState::current()) ThreadState::current()->willStartV8GC(BlinkGC::V8MinorGC); TRACE_EVENT_BEGIN1("devtools.timeline,v8", "MinorGC", "usedHeapSizeBefore", usedHeapSize(isolate)); visitWeakHandlesForMinorGC(isolate); break; case v8::kGCTypeMarkSweepCompact: if (ThreadState::current()) ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", "usedHeapSizeBefore", usedHeapSize(isolate), "type", "atomic pause"); gcPrologueForMajorGC(isolate, flags & v8::kGCCallbackFlagConstructRetainedObjectInfos); break; case v8::kGCTypeIncrementalMarking: if (ThreadState::current()) ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", "usedHeapSizeBefore", usedHeapSize(isolate), "type", "incremental marking"); gcPrologueForMajorGC(isolate, flags & v8::kGCCallbackFlagConstructRetainedObjectInfos); break; case v8::kGCTypeProcessWeakCallbacks: TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", "usedHeapSizeBefore", usedHeapSize(isolate), "type", "weak processing"); break; default: ASSERT_NOT_REACHED(); } }
void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type) { // An empty stack could merely mean that the timeline agent was turned on in the middle of // an event. Don't treat as an error. if (!m_recordStack.isEmpty()) { TimelineRecordEntry entry = m_recordStack.last(); m_recordStack.removeLast(); ASSERT(entry.type == type); entry.record->setObject("data", entry.data); entry.record->setArray("children", entry.children); entry.record->setNumber("endTime", timestamp()); size_t usedHeapSizeDelta = usedHeapSize() - entry.usedHeapSizeAtStart; if (usedHeapSizeDelta) entry.record->setNumber("usedHeapSizeDelta", usedHeapSizeDelta); addRecordToTimeline(entry.record, type); } }
void InspectorTimelineAgent::setDOMCounters(TypeBuilder::Timeline::TimelineEvent* record) { record->setUsedHeapSize(usedHeapSize()); if (m_includeDOMCounters) { int documentCount = 0; int nodeCount = 0; if (m_inspectorType == PageInspector) { documentCount = InspectorCounters::counterValue(InspectorCounters::DocumentCounter); nodeCount = InspectorCounters::counterValue(InspectorCounters::NodeCounter); } int listenerCount = ThreadLocalInspectorCounters::current().counterValue(ThreadLocalInspectorCounters::JSEventListenerCounter); RefPtr<TypeBuilder::Timeline::DOMCounters> counters = TypeBuilder::Timeline::DOMCounters::create() .setDocuments(documentCount) .setNodes(nodeCount) .setJsEventListeners(listenerCount); record->setCounters(counters.release()); } }
void V8GCController::gcEpilogue(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags) { switch (type) { case v8::kGCTypeScavenge: TRACE_EVENT_END1("devtools.timeline,v8", "MinorGC", "usedHeapSizeAfter", usedHeapSize(isolate)); // TODO(haraken): Remove this. See the comment in gcPrologue. if (ThreadState::current()) ThreadState::current()->scheduleV8FollowupGCIfNeeded(BlinkGC::V8MinorGC); break; case v8::kGCTypeMarkSweepCompact: TRACE_EVENT_END1("devtools.timeline,v8", "MajorGC", "usedHeapSizeAfter", usedHeapSize(isolate)); if (ThreadState::current()) ThreadState::current()->scheduleV8FollowupGCIfNeeded(BlinkGC::V8MajorGC); break; case v8::kGCTypeIncrementalMarking: TRACE_EVENT_END1("devtools.timeline,v8", "MajorGC", "usedHeapSizeAfter", usedHeapSize(isolate)); break; case v8::kGCTypeProcessWeakCallbacks: TRACE_EVENT_END1("devtools.timeline,v8", "MajorGC", "usedHeapSizeAfter", usedHeapSize(isolate)); break; default: ASSERT_NOT_REACHED(); } if (isMainThread()) ScriptForbiddenScope::exit(); // v8::kGCCallbackFlagForced forces a Blink heap garbage collection // when a garbage collection was forced from V8. This is either used // for tests that force GCs from JavaScript to verify that objects die // when expected. if (flags & v8::kGCCallbackFlagForced) { // This single GC is not enough for two reasons: // (1) The GC is not precise because the GC scans on-stack pointers conservatively. // (2) One GC is not enough to break a chain of persistent handles. It's possible that // some heap allocated objects own objects that contain persistent handles // pointing to other heap allocated objects. To break the chain, we need multiple GCs. // // Regarding (1), we force a precise GC at the end of the current event loop. So if you want // to collect all garbage, you need to wait until the next event loop. // Regarding (2), it would be OK in practice to trigger only one GC per gcEpilogue, because // GCController.collectAll() forces multiple V8's GC. Heap::collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::ForcedGC); // Forces a precise GC at the end of the current event loop. if (ThreadState::current()) { RELEASE_ASSERT(!ThreadState::current()->isInGC()); ThreadState::current()->setGCState(ThreadState::FullGCScheduled); } } // v8::kGCCallbackFlagCollectAllAvailableGarbage is used when V8 handles // low memory notifications. if (flags & v8::kGCCallbackFlagCollectAllAvailableGarbage) { // This single GC is not enough. See the above comment. Heap::collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::ForcedGC); // Do not force a precise GC at the end of the current event loop. // According to UMA stats, the collection rate of the precise GC // scheduled at the end of the low memory handling is extremely low, // because the above conservative GC is sufficient for collecting // most objects. So we intentionally don't schedule a precise GC here. } TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_THREAD, "data", InspectorUpdateCountersEvent::data()); }
void V8GCController::gcEpilogue(v8::GCType type, v8::GCCallbackFlags flags) { // FIXME: It would be nice if the GC callbacks passed the Isolate directly.... v8::Isolate* isolate = v8::Isolate::GetCurrent(); if (type == v8::kGCTypeScavenge) minorGCEpilogue(isolate); else if (type == v8::kGCTypeMarkSweepCompact) majorGCEpilogue(isolate); // Forces a Blink heap garbage collection when a garbage collection // was forced from V8. This is used for tests that force GCs from // JavaScript to verify that objects die when expected. if (flags & v8::kGCCallbackFlagForced) { // This single GC is not enough for two reasons: // (1) The GC is not precise because the GC scans on-stack pointers conservatively. // (2) One GC is not enough to break a chain of persistent handles. It's possible that // some heap allocated objects own objects that contain persistent handles // pointing to other heap allocated objects. To break the chain, we need multiple GCs. // // Regarding (1), we force a precise GC at the end of the current event loop. So if you want // to collect all garbage, you need to wait until the next event loop. // Regarding (2), it would be OK in practice to trigger only one GC per gcEpilogue, because // GCController.collectAll() forces 7 V8's GC. Heap::collectGarbage(ThreadState::HeapPointersOnStack, ThreadState::ForcedGC); // Forces a precise GC at the end of the current event loop. Heap::setForcePreciseGCForTesting(); } TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent", "usedHeapSizeAfter", usedHeapSize(isolate)); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data()); }
void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) { // FIXME: It would be nice if the GC callbacks passed the Isolate directly.... v8::Isolate* isolate = v8::Isolate::GetCurrent(); TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent", "usedHeapSizeBefore", usedHeapSize(isolate)); if (type == v8::kGCTypeScavenge) minorGCPrologue(isolate); else if (type == v8::kGCTypeMarkSweepCompact) majorGCPrologue(isolate, flags & v8::kGCCallbackFlagConstructRetainedObjectInfos); }
void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame) { commitFrameRecord(); RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0); setFrameIdentifier(record.get(), frame); m_recordStack.append(TimelineRecordEntry(record.release(), data, InspectorArray::create(), type, usedHeapSize())); }