Ignore:
Timestamp:
Mar 13, 2017, 5:39:24 PM (8 years ago)
Author:
[email protected]
Message:

Make the HeapVerifier useful again.
https://bugs.webkit.org/show_bug.cgi?id=161752

Reviewed by Filip Pizlo.

Resurrect the HeapVerifier. Here's what the verifier now offers:

  1. It captures the list of cells before and after GCs up to N GC cycles. N is set by JSC_numberOfGCCyclesToRecordForVerification. Currently, N defaults to 3.

This is useful if we're debugging in lldb and want to check if a candidate
cell pointer was observed by the GC during the last N GC cycles. We can do
this check buy calling HeapVerifier::checkIfRecorded() with the cell address.

HeapVerifier::checkIfRecorded() is robust and can be used on bogus addresses.
If the candidate cell was previously recorded by the HeapVerifier during a
GC cycle, checkIfRecorded() will dump any useful info it has on that cell.

  1. The HeapVerifier will verify that cells in its captured list after a GC are sane. Some examples of cell insanity are:
    • the cell claims to belong to a different VM.
    • the cell has a NULL structureID.
    • the cell has a NULL structure.
    • the cell's structure has a NULL structureID.
    • the cell's structure has a NULL structure.
    • the cell's structure's structure has a NULL structureID.
    • the cell's structure's structure has a NULL structure.

These are all signs of corruption or a GC bug. The verifier will report any
insanity it finds, and then crash with a RELEASE_ASSERT.

  1. Since the HeapVerifier captures list of cells in the heap before and after GCs for the last N GCs, it will also automatically "trim" dead cells those list after the most recent GC.

"trim" here means that the CellProfile in the HeapVerifier's lists will be
updated to reflect that the cell is now dead. It still keeps a record of the
dead cell pointer and the meta data collected about it back when it was alive.
As a result, checkIfRecorded() will also report if the candidate cell passed
to it is a dead object from a previous GC cycle.

  1. Each CellProfile captured by the HeapVerifier now track the following info:
    • the cell's HeapCell::Kind.
    • the cell's liveness.
    • if is JSCell, the cell's classInfo()->className.
    • an associated timestamp.
    • an associated stack trace.

Currently, the timestamp is only used for the time when the cell was recorded
by the HeapVerifier during GC. The stack trace is currently unused.

However, these fields are kept there so that we can instrument the VM (during
a debugging session, which requires rebuilding the VM) and record interesting
stack traces like that of the time of allocation of the cell. Since
capturing the stack traces for each cell is a very heavy weight operation,
the HeapVerifier code does not do this by default. Instead, we just leave
the building blocks for doing so in place to ease future debugging efforts.

  • heap/Heap.cpp:

(JSC::Heap::runBeginPhase):
(JSC::Heap::runEndPhase):
(JSC::Heap::didFinishCollection):

  • heap/Heap.h:

(JSC::Heap::verifier):

  • heap/MarkedAllocator.h:

(JSC::MarkedAllocator::takeLastActiveBlock): Deleted.

  • heap/MarkedSpace.h:
  • heap/MarkedSpaceInlines.h:

(JSC::MarkedSpace::forEachLiveCell):

  • tools/CellList.cpp:

(JSC::CellList::find):
(JSC::CellList::reset):
(JSC::CellList::findCell): Deleted.

  • tools/CellList.h:

(JSC::CellList::CellList):
(JSC::CellList::name):
(JSC::CellList::size):
(JSC::CellList::cells):
(JSC::CellList::add):
(JSC::CellList::reset): Deleted.

  • tools/CellProfile.h:

(JSC::CellProfile::CellProfile):
(JSC::CellProfile::cell):
(JSC::CellProfile::jsCell):
(JSC::CellProfile::isJSCell):
(JSC::CellProfile::kind):
(JSC::CellProfile::isLive):
(JSC::CellProfile::isDead):
(JSC::CellProfile::setIsLive):
(JSC::CellProfile::setIsDead):
(JSC::CellProfile::timestamp):
(JSC::CellProfile::className):
(JSC::CellProfile::stackTrace):
(JSC::CellProfile::setStackTrace):

  • tools/HeapVerifier.cpp:

(JSC::HeapVerifier::startGC):
(JSC::HeapVerifier::endGC):
(JSC::HeapVerifier::gatherLiveCells):
(JSC::trimDeadCellsFromList):
(JSC::HeapVerifier::trimDeadCells):
(JSC::HeapVerifier::printVerificationHeader):
(JSC::HeapVerifier::verifyCellList):
(JSC::HeapVerifier::validateCell):
(JSC::HeapVerifier::validateJSCell):
(JSC::HeapVerifier::verify):
(JSC::HeapVerifier::reportCell):
(JSC::HeapVerifier::checkIfRecorded):
(JSC::HeapVerifier::initializeGCCycle): Deleted.
(JSC::GatherCellFunctor::GatherCellFunctor): Deleted.
(JSC::GatherCellFunctor::visit): Deleted.
(JSC::GatherCellFunctor::operator()): Deleted.
(JSC::HeapVerifier::verifyButterflyIsInStorageSpace): Deleted.

  • tools/HeapVerifier.h:

(JSC::HeapVerifier::GCCycle::reset):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/tools/CellProfile.h

    r213675 r213883  
    2626#pragma once
    2727
     28#include "JSCell.h"
     29#include "StackTrace.h"
     30#include "Structure.h"
     31#include <wtf/MonotonicTime.h>
     32
    2833namespace JSC {
    2934
    30 class JSCell;
     35struct CellProfile {
     36    enum Liveness {
     37        Unknown,
     38        Dead,
     39        Live
     40    };
    3141
    32 struct CellProfile {
    33     CellProfile(JSCell* cell, bool isConfirmedDead = false)
    34         : cell(cell)
    35         , isConfirmedDead(isConfirmedDead)
     42    CellProfile(HeapCell* cell, HeapCell::Kind kind, Liveness liveness)
     43        : m_cell(cell)
     44        , m_kind(kind)
     45        , m_liveness(liveness)
     46        , m_timestamp(MonotonicTime::now())
    3647    {
     48        if (m_kind == HeapCell::JSCell && m_liveness != Dead)
     49            m_className = jsCell()->structure()->classInfo()->className;
    3750    }
     51
     52    CellProfile(CellProfile&& other)
     53        : m_cell(other.m_cell)
     54        , m_kind(other.m_kind)
     55        , m_liveness(other.m_liveness)
     56        , m_timestamp(other.m_timestamp)
     57        , m_className(other.m_className)
     58        , m_stackTrace(WTFMove(other.m_stackTrace))
     59    { }
     60
     61    HeapCell* cell() const { return m_cell; }
     62    JSCell* jsCell() const
     63    {
     64        ASSERT(isJSCell());
     65        return static_cast<JSCell*>(m_cell);
     66    }
     67
     68    bool isJSCell() const { return m_kind == HeapCell::JSCell; }
    3869   
    39     JSCell* cell;
    40     bool isConfirmedDead;
     70    HeapCell::Kind kind() const { return m_kind; }
     71
     72    bool isLive() const { return m_liveness == Live; }
     73    bool isDead() const { return m_liveness == Dead; }
     74
     75    void setIsLive() { m_liveness = Live; }
     76    void setIsDead() { m_liveness = Dead; }
     77
     78    MonotonicTime timestamp() const { return m_timestamp; }
     79
     80    const char* className() const { return m_className; }
     81
     82    StackTrace* stackTrace() const { return m_stackTrace.get(); }
     83    void setStackTrace(StackTrace* trace) { m_stackTrace = std::unique_ptr<StackTrace>(trace); }
     84
     85private:
     86    HeapCell* m_cell;
     87    HeapCell::Kind m_kind;
     88    Liveness m_liveness { Unknown };
     89    MonotonicTime m_timestamp;
     90    const char* m_className { nullptr };
     91    std::unique_ptr<StackTrace> m_stackTrace;
    4192};
    4293
Note: See TracChangeset for help on using the changeset viewer.