/src/hermes/include/hermes/VM/GCBase.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #ifndef HERMES_VM_GCBASE_H |
9 | | #define HERMES_VM_GCBASE_H |
10 | | |
11 | | #include "hermes/Inst/Inst.h" |
12 | | #include "hermes/Platform/Logging.h" |
13 | | #include "hermes/Public/CrashManager.h" |
14 | | #include "hermes/Public/GCConfig.h" |
15 | | #include "hermes/Public/GCTripwireContext.h" |
16 | | #include "hermes/Support/CheckedMalloc.h" |
17 | | #include "hermes/Support/OSCompat.h" |
18 | | #include "hermes/Support/StatsAccumulator.h" |
19 | | #include "hermes/VM/AllocOptions.h" |
20 | | #include "hermes/VM/BuildMetadata.h" |
21 | | #include "hermes/VM/CellKind.h" |
22 | | #include "hermes/VM/CompressedPointer.h" |
23 | | #include "hermes/VM/GCDecl.h" |
24 | | #include "hermes/VM/GCExecTrace.h" |
25 | | #include "hermes/VM/GCPointer.h" |
26 | | #include "hermes/VM/HeapAlign.h" |
27 | | #include "hermes/VM/HeapSnapshot.h" |
28 | | #include "hermes/VM/HermesValue.h" |
29 | | #include "hermes/VM/SlotAcceptor.h" |
30 | | #include "hermes/VM/SlotVisitor.h" |
31 | | #include "hermes/VM/SmallHermesValue.h" |
32 | | #include "hermes/VM/StackTracesTree-NoRuntime.h" |
33 | | #include "hermes/VM/StorageProvider.h" |
34 | | #include "hermes/VM/StringRefUtils.h" |
35 | | #include "hermes/VM/VTable.h" |
36 | | #include "hermes/VM/WeakRefSlot.h" |
37 | | |
38 | | #include "llvh/ADT/ArrayRef.h" |
39 | | #include "llvh/ADT/BitVector.h" |
40 | | #include "llvh/ADT/DenseMap.h" |
41 | | #include "llvh/Support/ErrorHandling.h" |
42 | | |
43 | | #include <cassert> |
44 | | #include <chrono> |
45 | | #include <cstdint> |
46 | | #include <cstdlib> |
47 | | #include <cstring> |
48 | | #include <list> |
49 | | #include <random> |
50 | | #include <system_error> |
51 | | #include <vector> |
52 | | #pragma GCC diagnostic push |
53 | | |
54 | | #ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32 |
55 | | #pragma GCC diagnostic ignored "-Wshorten-64-to-32" |
56 | | #endif |
57 | | namespace hermes { |
58 | | namespace vm { |
59 | | |
60 | | /// Forward declarations; |
61 | | namespace detail { |
62 | | struct WeakRefKey; |
63 | | } |
64 | | template <CellKind C> |
65 | | class JSWeakMapImpl; |
66 | | using JSWeakMap = JSWeakMapImpl<CellKind::JSWeakMapKind>; |
67 | | |
68 | | class GCCell; |
69 | | |
70 | | #ifdef HERMESVM_GC_RUNTIME |
71 | | #define RUNTIME_GC_KINDS GC_KIND(HadesGC) |
72 | | #endif |
73 | | |
74 | | /// Used by XorPtr to separate encryption keys between uses. |
75 | | enum XorPtrKeyID { |
76 | | ArrayBufferData, |
77 | | JSFunctionCodeBlock, |
78 | | DummyObjectFinalizerCallback, |
79 | | _NumKeys |
80 | | }; |
81 | | |
82 | | // A specific GC class extend GCBase, and override its virtual functions. |
83 | | // In addition, it must implement the following methods: |
84 | | |
85 | | /// Allocate a new cell of type \p T and size \p size. Instantiate an object of |
86 | | /// type \p T in the newly allocated cell, using \p args as the arguments to its |
87 | | /// constructor. If necessary perform a GC cycle, which may potentially move |
88 | | /// allocated objects. \p fixedSize should indicate whether the allocation is |
89 | | /// for a fixed-size, small object; some GCs may allow optimizations on this |
90 | | /// basis. \p hasFinalizer must be \p HasFinalizer::Yes if cells of the given |
91 | | /// type require a finalizer to be called. |
92 | | /// \pre size must be heap-aligned. |
93 | | /// |
94 | | /// template < |
95 | | /// typename T, |
96 | | /// bool fixedSize = true, |
97 | | /// HasFinalizer hasFinalizer = HasFinalizer::No, |
98 | | /// LongLived longLived = LongLived::No, |
99 | | /// class... Args> |
100 | | /// inline T *makeA(uint32_t size, Args &&... args); |
101 | | /// |
102 | | /// In some GCs, objects can have associated memory allocated outside the heap, |
103 | | /// and this memory can influence GC initiation and heap sizing heuristics. |
104 | | /// This method tests whether an external memory allocation is too large (e.g., |
105 | | /// larger than the max size of the heap): |
106 | | /// |
107 | | /// bool canAllocExternalMemory(uint32_t size); |
108 | | /// |
109 | | /// These APIs inform the GC of this external memory. |
110 | | /// |
111 | | /// void creditExternalMemory(GCCell *alloc, uint32_t size); |
112 | | /// void debitExternalMemory(GCCell *alloc, uint32_t size); |
113 | | /// |
114 | | /// Force a garbage collection cycle. The provided cause will be used in |
115 | | /// logging. |
116 | | /// void collect(std::string cause); |
117 | | /// |
118 | | /// The maximum size of any one allocation allowable by the GC in any state. |
119 | | /// static constexpr uint32_t maxAllocationSizeImpl(); |
120 | | /// |
121 | | /// Mark a pointer to a GCCell. |
122 | | /// template <class T> void mark(T *&ptr); |
123 | | /// |
124 | | /// Returns true if \p p points into the heap. |
125 | | /// bool contains(const void *p) const; |
126 | | /// |
127 | | /// Return the lower bound of the heap's virtual address range (inclusive). |
128 | | /// char *lowLim() const; |
129 | | /// |
130 | | /// Return the upper bound of the heap's virtual address range (exclusive). |
131 | | /// char *hiLim() const; |
132 | | /// |
133 | | /// Iterate over all objects in the heap. |
134 | | /// void forAllObjs(const std::function<void(GCCell *)> &callback); |
135 | | /// |
136 | | /// In the "mark" functions below, Name is one of const char*, int, |
137 | | /// unsigned, or const StringPrimitive*: |
138 | | /// |
139 | | /// Mark a HermesValue which may or may not be a pointer. |
140 | | /// void mark(HermesValue &hv, Name name); |
141 | | /// void mark(HermesValue &hv); |
142 | | /// |
143 | | /// Mark a T* location. This location must be outside the heap. |
144 | | /// void mark(T *&ptr, Name name); |
145 | | /// void mark(T *&ptr); |
146 | | /// |
147 | | /// Mark a GCPointer<T>, which must be within the heap. |
148 | | /// void mark(GCPointer<T> &ptr, Name name); |
149 | | /// |
150 | | /// Return true if the GC is active and is calling into the VM. |
151 | | /// If true, some objects in the heap may have an invalid VTable pointer. |
152 | | /// bool calledByGC() const; |
153 | | /// |
154 | | /// Various forms of write barriers: these can have empty implementations |
155 | | /// for GCs that don't require them: |
156 | | /// |
157 | | /// The given value is being written at the given loc (required to |
158 | | /// be in the heap). If value is a pointer, execute a write barrier. |
159 | | /// void writeBarrier(GCHermesValue *loc, HermesValue value); |
160 | | /// void writeBarrier( |
161 | | /// const GCSmallHermesValue *loc, |
162 | | /// SmallHermesValue value); |
163 | | /// |
164 | | /// The given pointer value is being written at the given loc (required to |
165 | | /// be in the heap). The value is may be null. Execute a write barrier. |
166 | | /// void writeBarrier(const GCPointerBase *loc, const GCCell *value); |
167 | | /// |
168 | | /// The given value/pointer is being written at a previously uninitialized loc |
169 | | /// (required to be in the heap). |
170 | | /// void constructorWriteBarrier( |
171 | | /// const GCHermesValue *loc, |
172 | | /// HermesValue value); |
173 | | /// void constructorWriteBarrier( |
174 | | /// const GCSmallHermesValue *loc, |
175 | | /// SmallHermesValue value); |
176 | | /// void constructorWriteBarrier( |
177 | | /// const GCPointerBase *loc, |
178 | | /// const GCCell *value); |
179 | | /// |
180 | | /// A weak ref is about to be read. Executes a read barrier so the GC can |
181 | | /// take action such as extending the lifetime of the reference. The |
182 | | /// HermesValue version does nothing if the value isn't a pointer. |
183 | | /// void weakRefReadBarrier(GCCell *value); |
184 | | /// |
185 | | /// We copied HermesValues into the given region. Note that \p numHVs is |
186 | | /// the number of HermesValues in the the range, not the char length. |
187 | | /// Do any necessary barriers. |
188 | | /// void writeBarrierRange(GCHermesValue* start, uint32_t numHVs); |
189 | | /// void writeBarrierRange( |
190 | | /// const GCSmallHermesValue *start, uint32_t |
191 | | /// numHVs); |
192 | | /// void constructorWriteBarrierRange( |
193 | | /// const GCHermesValue *start, |
194 | | /// uint32_t numHVs); |
195 | | /// void constructorWriteBarrierRange( |
196 | | /// const GCSmallHermesValue *start, |
197 | | /// uint32_t numHVs); |
198 | | /// |
199 | | /// The given loc or region is about to be overwritten, but the new value is |
200 | | /// not important. Perform any necessary barriers. |
201 | | /// void snapshotWriteBarrier(const GCHermesValue *loc); |
202 | | /// void snapshotWriteBarrier(const GCSmallHermesValue *loc); |
203 | | /// void snapshotWriteBarrier(const GCPointerBase *loc); |
204 | | /// void snapshotWriteBarrier(const GCSymboldID *symbol); |
205 | | /// void snapshotWriteBarrierRange( |
206 | | /// const GCHermesValue *start, |
207 | | /// uint32_t numHVs); |
208 | | /// void snapshotWriteBarrierRange( |
209 | | /// const GCSmallHermesValue *start, |
210 | | /// uint32_t numHVs); |
211 | | /// |
212 | | /// In debug builds: is a write barrier necessary for a write of the given |
213 | | /// GC pointer \p value to the given \p loc? |
214 | | /// bool needsWriteBarrier(void *loc, void *value); |
215 | | /// |
216 | | /// This is intended to be called only from within object or root mark |
217 | | /// functions and indicates whether the \c mark() operation, called within |
218 | | /// the current GC phase, is the first such call that guarantees that the |
219 | | /// location passed to mark will contain the final, correct, pointer value |
220 | | /// after the mark call. |
221 | | /// bool isUpdatingPointers() const; |
222 | | /// |
223 | | /// It must also have the inner type: |
224 | | /// class Size; |
225 | | /// Which provides at least these functions publicly: |
226 | | /// Constructor from either a GCConfig or the min and max heap size. |
227 | | /// explicit Size(const GCConfig &conf); |
228 | | /// Size(gcheapsize_t min, gcheapsize_t max); |
229 | | /// Return the minimum amount of bytes holdable by this heap. |
230 | | /// gcheapsize_t min() const; |
231 | | /// Return the maximum amount of bytes holdable by this heap. |
232 | | /// gcheapsize_t max() const; |
233 | | /// Return the total amount of bytes of storage this GC will require. |
234 | | /// This will be a multiple of AlignedStorage::size(). |
235 | | /// gcheapsize_t storageFootprint() const; |
236 | | /// |
237 | | class GCBase { |
238 | | public: |
239 | | static const char kNaturalCauseForAnalytics[]; |
240 | | static const char kHandleSanCauseForAnalytics[]; |
241 | | |
242 | | /// An interface enabling the garbage collector to mark roots and free |
243 | | /// symbols. |
244 | | struct GCCallbacks { |
245 | | /// Virtual destructor to avoid warnings. |
246 | | virtual ~GCCallbacks() = 0; |
247 | | |
248 | | /// Callback that will be invoked by the GC to mark all roots in the |
249 | | /// beginning of every GC by calling "gc->mark()". |
250 | | /// The \p markLongLived argument indicates whether root data structures |
251 | | /// that contain only references to long-lived objects (allocated |
252 | | /// via allocLongLived) are required to be scanned. A generational |
253 | | /// collector, for example, might take advantage of this. |
254 | | virtual void markRoots( |
255 | | RootAndSlotAcceptorWithNames &acceptor, |
256 | | bool markLongLived = true) = 0; |
257 | | |
258 | | /// Callback that will be invoked by the GC to mark all weak roots in the |
259 | | /// beginning of every GC. |
260 | | virtual void markWeakRoots( |
261 | | WeakRootAcceptor &weakAcceptor, |
262 | | bool markLongLived = true) = 0; |
263 | | |
264 | | /// Callback that might be invoked by the GC before it completes marking. |
265 | | /// Not all GCs will call this. It should be used to mark any roots that |
266 | | /// might change without executing proper read and write barriers on the GC. |
267 | | /// An example would be something that skipped weak ref read barriers in a |
268 | | /// signal handler. If they aren't marked, they would be collected as |
269 | | /// garbage. |
270 | | /// While it is possible to implement this just as forwarding to |
271 | | /// \c markRoots, to be faster it should try to mark only things that would |
272 | | /// not have been properly doing barriers. |
273 | | virtual void markRootsForCompleteMarking( |
274 | | RootAndSlotAcceptorWithNames &acceptor) = 0; |
275 | | |
276 | | /// \return one higher than the largest symbol in the identifier table. This |
277 | | /// enables the GC to size its internal structures for symbol marking. |
278 | | /// Optionally invoked at the beginning of a garbage collection. |
279 | | virtual unsigned getSymbolsEnd() const = 0; |
280 | | |
281 | | /// If any symbols are marked by the IdentifierTable, clear that marking. |
282 | | /// Optionally invoked at the beginning of some collections. |
283 | | virtual void unmarkSymbols() = 0; |
284 | | |
285 | | /// Free all symbols which are not marked as \c true in \p markedSymbols. |
286 | | /// Optionally invoked at the end of a garbage collection. |
287 | | virtual void freeSymbols(const llvh::BitVector &markedSymbols) = 0; |
288 | | |
289 | | /// Prints any statistics maintained in the Runtime about GC to \p |
290 | | /// os. At present, this means the breakdown of markRoots time by |
291 | | /// "phase" within markRoots. |
292 | | virtual void printRuntimeGCStats(JSONEmitter &json) const = 0; |
293 | | |
294 | | /// \returns the approximate usage of memory external to the GC such as |
295 | | /// malloc by the roots of the object graph. |
296 | | virtual size_t mallocSize() const = 0; |
297 | | |
298 | | /// Visits every entry in the identifier table and calls acceptor with |
299 | | /// the entry as argument. This is intended to be used only for Snapshots, |
300 | | /// as it is slow. The function passed as acceptor shouldn't perform any |
301 | | /// heap operations. |
302 | | virtual void visitIdentifiers( |
303 | | const std::function<void(SymbolID, const StringPrimitive *)> |
304 | | &acceptor) = 0; |
305 | | |
306 | | /// Convert the given symbol into its UTF-8 string representation. |
307 | | /// \post The implementation of this function must not perform any GC |
308 | | /// operations, such as allocations, mutating values in the heap, or |
309 | | /// making handles. |
310 | | virtual std::string convertSymbolToUTF8(SymbolID id) = 0; |
311 | | |
312 | | /// Returns the current stack as a string. This function will not cause |
313 | | /// any allocs in the GC. |
314 | | virtual std::string getCallStackNoAlloc() = 0; |
315 | | |
316 | | /// This is called with CollectionStart at the start of each GC, and with |
317 | | /// CollectionEnd at the end. |
318 | | /// \param extraInfo contains more detailed extra info for specific GC. |
319 | | virtual void onGCEvent(GCEventKind kind, const std::string &extraInfo) = 0; |
320 | | |
321 | | /// Return the current VM instruction pointer which can be used to derive |
322 | | /// the current VM stack-trace. It's "slow" because it's virtual. |
323 | | virtual const inst::Inst *getCurrentIPSlow() const = 0; |
324 | | |
325 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
326 | | /// Return a \c StackTracesTreeNode representing the current VM stack-trace |
327 | | /// at this point. |
328 | | virtual StackTracesTreeNode *getCurrentStackTracesTreeNode( |
329 | | const inst::Inst *ip) = 0; |
330 | | |
331 | | /// Get a StackTraceTree which can be used to recover stack-traces from \c |
332 | | /// StackTraceTreeNode() as returned by \c getCurrentStackTracesTreeNode() . |
333 | | virtual StackTracesTree *getStackTracesTree() = 0; |
334 | | #endif |
335 | | |
336 | | #ifdef HERMES_SLOW_DEBUG |
337 | | /// \return true if the given symbol is a live entry in the identifier |
338 | | /// table. |
339 | | /// NOTE: Used by CheckHeapWellFormedAcceptor to make sure a symbol |
340 | | /// that is discovered live is marked as live. |
341 | | virtual bool isSymbolLive(SymbolID id) = 0; |
342 | | |
343 | | /// \return An associated heap cell for the symbol if one exists, null |
344 | | /// otherwise. |
345 | | virtual const void *getStringForSymbol(SymbolID id) = 0; |
346 | | #endif |
347 | | }; |
348 | | |
349 | | /// Stats for collections. Time unit, where applicable, is seconds. |
350 | | struct CumulativeHeapStats { |
351 | | unsigned numCollections{0}; |
352 | | |
353 | | /// Summary statistics for GC wall times. For Hades, this should only track |
354 | | /// time spent on the mutator. |
355 | | StatsAccumulator<double> gcWallTime; |
356 | | |
357 | | /// Summary statistics for GC CPU times. |
358 | | StatsAccumulator<double> gcCPUTime; |
359 | | |
360 | | gcheapsize_t finalHeapSize{0}; |
361 | | |
362 | | /// Bytes allocated just before a collection. |
363 | | StatsAccumulator<gcheapsize_t, uint64_t> usedBefore; |
364 | | |
365 | | /// Bytes alive after a collection. |
366 | | StatsAccumulator<gcheapsize_t, uint64_t> usedAfter; |
367 | | }; |
368 | | |
369 | | struct HeapInfo { |
370 | | /// Number of garbage collections (of any kind) since creation. |
371 | | unsigned numCollections{0}; |
372 | | /// Total (cumulative) bytes allocated within the JS heap since creation. |
373 | | uint64_t totalAllocatedBytes{0}; |
374 | | /// Number of currently allocated bytes within the JS heap. Some may be |
375 | | /// in unreachable objects (unless a full collection just occurred). |
376 | | uint64_t allocatedBytes{0}; |
377 | | /// Current capacity of the JS heap, in bytes. |
378 | | uint64_t heapSize{0}; |
379 | | /// Estimate of amount of current malloc space used by the runtime and any |
380 | | /// auxiliary allocations owned by heap objects. (Calculated by querying |
381 | | /// each finalizable object to report its malloc usage.) |
382 | | unsigned mallocSizeEstimate{0}; |
383 | | /// The total amount of Virtual Address space (VA) that the GC is using. |
384 | | uint64_t va{0}; |
385 | | /// Number of bytes retained by objects as external memory on the C++ heap. |
386 | | /// This is typically associated with allocations that are modified |
387 | | /// infrequently, and can therefore be stored in a counter in the GC, making |
388 | | /// them cheaper to query. This is a subset of mallocSizeEstimate. |
389 | | uint64_t externalBytes{0}; |
390 | | /// Cumulative number of mark stack overflows in full collections |
391 | | /// (zero if non-generational GC). |
392 | | unsigned numMarkStackOverflows{0}; |
393 | | /// Stats for full collections (zeroes if non-generational GC). |
394 | | CumulativeHeapStats fullStats; |
395 | | /// Stats for collections in the young generation (zeroes if |
396 | | /// non-generational GC). |
397 | | CumulativeHeapStats youngGenStats; |
398 | | }; |
399 | | |
400 | | #ifndef NDEBUG |
401 | | struct DebugHeapInfo { |
402 | | /// Number of currently allocated objects present in the heap. Some may be |
403 | | /// unreachable. |
404 | | unsigned numAllocatedObjects{0}; |
405 | | /// Number of reachable objects in the last collection. |
406 | | unsigned numReachableObjects{0}; |
407 | | /// Number of collected objects in the last collection. |
408 | | unsigned numCollectedObjects{0}; |
409 | | /// Number of finalized objects in the last collection. |
410 | | unsigned numFinalizedObjects{0}; |
411 | | /// Number of marked symbols. |
412 | | unsigned numMarkedSymbols{0}; |
413 | | /// Number of hidden classes alive after the last collection. |
414 | | unsigned numHiddenClasses{0}; |
415 | | /// Number of "leaf" hidden classes alive after the last collection. |
416 | | unsigned numLeafHiddenClasses{0}; |
417 | | |
418 | | // Assert any invariants that should hold among the fields of the |
419 | | // DebugHeapInfo. |
420 | | void assertInvariants() const; |
421 | | }; |
422 | | #endif |
423 | | |
424 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
425 | | /// When enabled, every allocation gets an attached stack-trace and an |
426 | | /// object ID. When disabled old allocations continue to be tracked but |
427 | | /// no new allocations get a stack-trace. |
428 | | struct AllocationLocationTracker final { |
429 | | explicit AllocationLocationTracker(GCBase *gc); |
430 | | |
431 | | /// Returns true if tracking is enabled for new allocations. |
432 | | bool isEnabled() const; |
433 | | /// Must be called by GC implementations whenever a new allocation is made. |
434 | | void newAlloc(const GCCell *ptr, uint32_t sz); |
435 | | |
436 | | /// If an object's size changes, update the entry here. |
437 | | void updateSize(const GCCell *ptr, uint32_t oldSize, uint32_t newSize); |
438 | | |
439 | | /// Must be called by GC implementations whenever an allocation is freed. |
440 | | void freeAlloc(const GCCell *ptr, uint32_t sz); |
441 | | /// Returns data needed to reconstruct the JS stack used to create the |
442 | | /// specified allocation. |
443 | | StackTracesTreeNode *getStackTracesTreeNodeForAlloc( |
444 | | HeapSnapshot::NodeID id) const; |
445 | | |
446 | | /// A Fragment is a time bound for when objects are allocated. Any |
447 | | /// allocations that occur before the lastSeenObjectID_ are in this |
448 | | /// fragment. Allocations increment the numObjects_ and numBytes_. Free'd |
449 | | /// cells from this fragment decrement numObjects_ and numBytes_. |
450 | | struct Fragment { |
451 | | HeapSnapshot::NodeID lastSeenObjectID_; |
452 | | std::chrono::microseconds timestamp_; |
453 | | /// Number of objects still alive in this fragment. Incremented when |
454 | | /// objects are created, decremented when objects are destroyed. |
455 | | uint64_t numObjects_; |
456 | | /// Total size of objects still alive in this fragment. |
457 | | uint64_t numBytes_; |
458 | | /// If true, one of numObjects or numBytes changed since the last flush. |
459 | | bool touchedSinceLastFlush_; |
460 | | }; |
461 | | |
462 | | /// This must match the definition in jsi::Instrumentation to avoid |
463 | | /// unnecessary copying. |
464 | | using HeapStatsUpdate = std::tuple<uint64_t, uint64_t, uint64_t>; |
465 | | |
466 | | /// Enable location tracking. |
467 | | void enable(std::function<void( |
468 | | uint64_t, |
469 | | std::chrono::microseconds, |
470 | | std::vector<HeapStatsUpdate>)> callback); |
471 | | |
472 | | /// Disable location tracking - turns \c newAlloc() into a no-op. Existing |
473 | | /// allocations continue to be tracked. |
474 | | void disable(); |
475 | | |
476 | | /// Flush the current fragment and write all flushed fragments to \p snap. |
477 | | void addSamplesToSnapshot(HeapSnapshot &snap); |
478 | | |
479 | | private: |
480 | | /// Flush out heap profiler data to the callback after a new kFlushThreshold |
481 | | /// bytes are allocated. |
482 | | static constexpr uint64_t kFlushThreshold = 128 * (1 << 10); |
483 | | /// This mutex protects stackMap_ and fragments_. Specifically does not |
484 | | /// protect enabled_, because enabled_ should only be changed while the GC |
485 | | /// isn't running anyway. |
486 | | Mutex mtx_; |
487 | | /// Associates allocations at their current location with their stack trace |
488 | | /// data. |
489 | | llvh::DenseMap<HeapSnapshot::NodeID, StackTracesTreeNode *> stackMap_; |
490 | | /// We need access to the GCBase to collect the current stack when nodes are |
491 | | /// allocated. |
492 | | GCBase *gc_; |
493 | | /// Indicates if tracking of new allocations is enabled. |
494 | | bool enabled_{false}; |
495 | | /// Time when the profiler was started. |
496 | | std::chrono::steady_clock::time_point startTime_; |
497 | | /// This should be called periodically whenever the last seen object ID is |
498 | | /// updated. |
499 | | std::function< |
500 | | void(uint64_t, std::chrono::microseconds, std::vector<HeapStatsUpdate>)> |
501 | | fragmentCallback_; |
502 | | /// All samples that have been flushed. Only needs the last object ID to be |
503 | | /// written to the file. |
504 | | std::vector<Fragment> fragments_; |
505 | | |
506 | | /// Updates the last fragment to have the current last ID and timestamp, |
507 | | /// then calls fragmentCallback_ with both the new fragment and any changed |
508 | | /// fragments from freeAlloc. |
509 | | void flushCallback(); |
510 | | |
511 | | /// Find the fragment corresponding to the given id. |
512 | | /// \return fragments_.back() if none exists (it's the currently active |
513 | | /// fragment). |
514 | | Fragment &findFragmentForID(HeapSnapshot::NodeID id); |
515 | | }; |
516 | | |
517 | | class SamplingAllocationLocationTracker final { |
518 | | public: |
519 | 105 | explicit inline SamplingAllocationLocationTracker(GCBase *gc) : gc_(gc) {} |
520 | | |
521 | | /// Returns true if tracking is enabled for new allocations. |
522 | 621k | bool isEnabled() const { |
523 | 621k | return !!dist_; |
524 | 621k | } |
525 | | |
526 | | /// Must be called by GC implementations whenever a new allocation is made. |
527 | | void newAlloc(const GCCell *ptr, uint32_t sz); |
528 | | |
529 | | /// Must be called by GC implementations whenever an allocation is freed. |
530 | | void freeAlloc(const GCCell *ptr, uint32_t sz); |
531 | | |
532 | | /// If an object's size changes, update the entry here. |
533 | | void updateSize(const GCCell *ptr, uint32_t oldSize, uint32_t newSize); |
534 | | |
535 | | /// Turn the sampling memory profiler on. About once every |
536 | | /// \p samplingInterval bytes are allocated, sample the allocation by |
537 | | /// recording its stack. |
538 | | /// \param seed If non-negative, use as the seed for the random sampling |
539 | | /// mechanism, giving deterministic output. |
540 | | void enable(size_t samplingInterval, int64_t seed); |
541 | | |
542 | | void disable(llvh::raw_ostream &os); |
543 | | |
544 | | private: |
545 | | struct Sample final { |
546 | | size_t size; |
547 | | StackTracesTreeNode *node; |
548 | | /// This is the auto-incremented sample ID, not the ID of the object |
549 | | /// associated with the sample. |
550 | | uint64_t id; |
551 | | }; |
552 | | |
553 | | /// This mutex protects stackMap_ and samples_. Not needed for enabling and |
554 | | /// disabling because those only happen while the world is stopped. |
555 | | Mutex mtx_; |
556 | | |
557 | | GCBase *gc_; |
558 | | |
559 | | /// Subtract from this each allocation. If it would underflow below zero, |
560 | | /// take a sample. |
561 | | size_t limit_{0}; |
562 | | |
563 | | /// Track all samples that have been taken. |
564 | | llvh::DenseMap<HeapSnapshot::NodeID, Sample> samples_; |
565 | | |
566 | | /// Use a poisson distribution to decide when to take the next sample. |
567 | | std::minstd_rand randomEngine_; |
568 | | std::unique_ptr<std::poisson_distribution<>> dist_; |
569 | | |
570 | | /// An auto-incrementing integer representing a unique ID for a sample. |
571 | | /// Used for ordering samples. |
572 | | uint64_t nextSampleID_{1}; |
573 | | |
574 | | /// \return How many bytes should be waited until the next sample. |
575 | | size_t nextSample(); |
576 | | }; |
577 | | #endif |
578 | | |
579 | | class IDTracker final { |
580 | | public: |
581 | | /// These are IDs that are reserved for special objects. |
582 | | enum class ReservedObjectID : HeapSnapshot::NodeID { |
583 | | // The ID for the super root object. |
584 | | SuperRoot, |
585 | | // The ID for the (GC roots) object. |
586 | | GCRoots, |
587 | | #define ROOT_SECTION(name) name, |
588 | | #include "hermes/VM/RootSections.def" |
589 | | IdentifierTableLookupVector, |
590 | | IdentifierTableHashTable, |
591 | | IdentifierTableMarkedSymbols, |
592 | | JSIHermesValueList, |
593 | | JSIWeakHermesValueList, |
594 | | WeakRefSlotStorage, |
595 | | Undefined, |
596 | | Null, |
597 | | True, |
598 | | False, |
599 | | FirstNonReservedID, |
600 | | }; |
601 | | |
602 | | // 0 is guaranteed to never be a valid node ID. |
603 | | static constexpr HeapSnapshot::NodeID kInvalidNode = 0; |
604 | | |
605 | 105 | static constexpr HeapSnapshot::NodeID reserved(ReservedObjectID id) { |
606 | | // All reserved IDs should be odd numbers to signify that they're JS |
607 | | // objects and not native objects. This follows v8's output. |
608 | 105 | return static_cast<std::underlying_type<ReservedObjectID>::type>(id) * 2 + |
609 | 105 | 1; |
610 | 105 | } |
611 | | |
612 | | /// A comparator for doubles that allows NaN. |
613 | | struct DoubleComparator { |
614 | 0 | static double getEmptyKey() { |
615 | | // Use a non-canonical NaN value as an empty value, which should never |
616 | | // occur naturally. |
617 | | // NOTE: HermesValue uses NaN tagging internally so we can use that to |
618 | | // get the encoding. |
619 | 0 | return llvh::BitsToDouble(HermesValue::encodeUndefinedValue().getRaw()); |
620 | 0 | } |
621 | 0 | static double getTombstoneKey() { |
622 | | // Use a non-canonical NaN value as the tombstone, which should never |
623 | | // occur naturally. |
624 | | // NOTE: HermesValue uses NaN tagging internally so we can use that to |
625 | | // get the encoding. |
626 | 0 | return llvh::BitsToDouble(HermesValue::encodeNullValue().getRaw()); |
627 | 0 | } |
628 | 0 | static unsigned getHashValue(double val) { |
629 | 0 | return std::hash<uint64_t>{}(llvh::DoubleToBits(val)); |
630 | 0 | } |
631 | 0 | static bool isEqual(double LHS, double RHS) { |
632 | 0 | return llvh::DoubleToBits(LHS) == llvh::DoubleToBits(RHS); |
633 | 0 | } |
634 | | }; |
635 | | |
636 | | explicit IDTracker(); |
637 | | |
638 | | /// Return true if IDs are being tracked. |
639 | | bool isTrackingIDs(); |
640 | | |
641 | | /// Get the unique object id of the given object. |
642 | | /// If one does not yet exist, start tracking it. |
643 | | HeapSnapshot::NodeID getObjectID(CompressedPointer cell); |
644 | | |
645 | | /// \return true if the cell has an object ID associated with it, false if |
646 | | /// there is none. |
647 | | bool hasObjectID(CompressedPointer cell); |
648 | | |
649 | | /// Same as \c getObjectID, except it asserts if the cell doesn't have an |
650 | | /// ID. |
651 | | HeapSnapshot::NodeID getObjectIDMustExist(CompressedPointer cell); |
652 | | |
653 | | /// Get the unique object id of the symbol with the given symbol \p sym. If |
654 | | /// one does not yet exist, start tracking it. |
655 | | HeapSnapshot::NodeID getObjectID(SymbolID sym); |
656 | | |
657 | | /// Get the unique object id of the given native memory (non-JS-heap). |
658 | | /// If one does not yet exist, start tracking it. |
659 | | HeapSnapshot::NodeID getNativeID(const void *mem); |
660 | | |
661 | | /// Get a list of IDs for native memory attached to the given node. |
662 | | /// List will be empty if nothing is attached yet. Then push onto the end |
663 | | /// with nextNativeID(). |
664 | | /// When the NodeID that these are attached to is untracked, so are the |
665 | | /// attached native NodeIDs. |
666 | | llvh::SmallVector<HeapSnapshot::NodeID, 1> &getExtraNativeIDs( |
667 | | HeapSnapshot::NodeID node); |
668 | | |
669 | | /// Assign a unique ID to a literal number value that occurs in the heap. |
670 | | /// Can be used to make fake nodes that will display their numeric value. |
671 | | HeapSnapshot::NodeID getNumberID(double num); |
672 | | |
673 | | /// Get the object pointer for the given ID. This is the inverse of \c |
674 | | /// getObjectID. |
675 | | /// Returns none if there is no object for that ID. |
676 | | llvh::Optional<CompressedPointer> getObjectForID(HeapSnapshot::NodeID id); |
677 | | |
678 | | /// Tell the tracker that an object has moved locations. |
679 | | /// This must be called in a safe order, if A moves to B, and C moves to A, |
680 | | /// the first move must be recorded before the second. |
681 | | void moveObject( |
682 | | CompressedPointer oldLocation, |
683 | | CompressedPointer newLocation); |
684 | | |
685 | | /// Remove the object from being tracked. This should be done to keep the |
686 | | /// tracking working set small. |
687 | | void untrackObject(CompressedPointer cell); |
688 | | |
689 | | /// Remove the symbol from being tracked. This needs to be done to allow |
690 | | /// symbols to be re-used. |
691 | | void untrackSymbol(uint32_t symIdx); |
692 | | |
693 | | /// Remove the native memory from being tracked. This should be done to keep |
694 | | /// the tracking working set small. It is also required to be done when |
695 | | /// malloc'ed memory is freed, since addresses can be re-used by future |
696 | | /// allocations. |
697 | | void untrackNative(const void *mem); |
698 | | |
699 | | /// \return True if this is tracking any native IDs, false if there are no |
700 | | /// native IDs being tracked. |
701 | | bool hasNativeIDs(); |
702 | | |
703 | | /// Get the current last ID. All other existing IDs are less than or equal |
704 | | /// to this one. |
705 | | HeapSnapshot::NodeID lastID() const; |
706 | | |
707 | | /// Get the next unique native ID for a chunk of native memory. |
708 | | /// NOTE: public to get assigned native ids without needing to reserve in |
709 | | /// advance. |
710 | | HeapSnapshot::NodeID nextNativeID(); |
711 | | |
712 | | private: |
713 | | /// Get the next unique object ID for a newly created object. |
714 | | HeapSnapshot::NodeID nextObjectID(); |
715 | | /// Get the next unique number ID for a number. |
716 | | HeapSnapshot::NodeID nextNumberID(); |
717 | | |
718 | | /// JS heap nodes are represented by odd-numbered IDs, while native nodes |
719 | | /// are represented with even-numbered IDs. This requirement is enforced by |
720 | | /// the Chrome snapshot viewer. |
721 | | static constexpr HeapSnapshot::NodeID kIDStep = 2; |
722 | | |
723 | | /// This mutex protects objectIDMap_, symbolIDMap_, and numberIDMap_. |
724 | | /// Specifically does not protect lastID_, since there's only one allocator |
725 | | /// at a time, and only new allocations affect lastID_. |
726 | | Mutex mtx_; |
727 | | |
728 | | /// The last ID assigned to a non-native object. Object IDs are not |
729 | | /// recycled so that snapshots don't confuse two objects with each other. |
730 | | /// NOTE: Reserved guarantees that this is an odd number. |
731 | | HeapSnapshot::NodeID lastID_{ |
732 | | reserved(ReservedObjectID::FirstNonReservedID)}; |
733 | | |
734 | | /// Map of object pointers to IDs. Only populated once the first heap |
735 | | /// snapshot is requested, or the first time the memory profiler is turned |
736 | | /// on, or if JSI tracing is in effect. |
737 | | /// This map's size is O(number of cells in the heap), which can grow quite |
738 | | /// large. Using compressed pointers keeps the size small. |
739 | | llvh::DenseMap<CompressedPointer::RawType, HeapSnapshot::NodeID> |
740 | | objectIDMap_; |
741 | | |
742 | | /// The inverse of \c objectIDMap_. Only used for debugging views on heap |
743 | | /// snapshots. To avoid wasting memory in the case where the debugger hasn't |
744 | | /// requested any, it is populated lazily as each entry is requested. We |
745 | | /// expect the vast majority of objects aren't inspected in the snapshot. |
746 | | llvh::DenseMap<HeapSnapshot::NodeID, CompressedPointer::RawType> |
747 | | idObjectMap_; |
748 | | |
749 | | /// Map of native pointers to IDs. Populated according to |
750 | | /// the same rules as the objectIDMap_. |
751 | | llvh::DenseMap<const void *, HeapSnapshot::NodeID> nativeIDMap_; |
752 | | |
753 | | /// Map from a JS heap object ID to additional lazily created IDs for |
754 | | /// objects. Most useful for native IDs that are attached to a heap object |
755 | | /// but don't have a stable pointer to use (such as std::vector and |
756 | | /// llvh::DenseMap). |
757 | | llvh::DenseMap< |
758 | | HeapSnapshot::NodeID, |
759 | | llvh::SmallVector<HeapSnapshot::NodeID, 1>> |
760 | | extraNativeIDs_; |
761 | | |
762 | | /// Map from symbol indices to unique IDs. Populated according to |
763 | | /// the same rules as the objectIDMap_. |
764 | | llvh::DenseMap<uint32_t, HeapSnapshot::NodeID> symbolIDMap_; |
765 | | |
766 | | /// Map of numeric values to IDs. Used to give numbers in the heap a unique |
767 | | /// node. |
768 | | llvh::DenseMap<double, HeapSnapshot::NodeID, DoubleComparator> numberIDMap_; |
769 | | }; |
770 | | |
771 | | enum class HeapKind { HadesGC, MallocGC }; |
772 | | |
773 | | GCBase( |
774 | | GCCallbacks &gcCallbacks, |
775 | | PointerBase &pointerBase, |
776 | | const GCConfig &gcConfig, |
777 | | std::shared_ptr<CrashManager> crashMgr, |
778 | | HeapKind kind); |
779 | | |
780 | 105 | virtual ~GCBase() {} |
781 | | |
782 | | /// Create a fixed size object of type T. |
783 | | /// \return a pointer to the newly created object in the GC heap. |
784 | | template < |
785 | | typename T, |
786 | | HasFinalizer hasFinalizer = HasFinalizer::No, |
787 | | LongLived longLived = LongLived::No, |
788 | | class... Args> |
789 | | T *makeAFixed(Args &&...args); |
790 | | |
791 | | /// Create a variable size object of type T and size \p size. |
792 | | /// \return a pointer to the newly created object in the GC heap. |
793 | | template < |
794 | | typename T, |
795 | | HasFinalizer hasFinalizer = HasFinalizer::No, |
796 | | LongLived longLived = LongLived::No, |
797 | | class... Args> |
798 | | T *makeAVariable(uint32_t size, Args &&...args); |
799 | | |
800 | | template < |
801 | | typename T, |
802 | | bool fixedSize = true, |
803 | | HasFinalizer hasFinalizer = HasFinalizer::No, |
804 | | LongLived longLived = LongLived::No, |
805 | | class... Args> |
806 | | T *makeA(uint32_t size, Args &&...args); |
807 | | |
808 | | /// Name to identify this heap in logs. |
809 | 979 | const std::string &getName() const { |
810 | 979 | return name_; |
811 | 979 | } |
812 | | |
813 | | /// \return the base of pointers in the heap. |
814 | | /// NOTE: This normally should not be needed, Runtime provides it. |
815 | | /// However in some scenarios there is only a GC available, not a |
816 | | /// Runtime. In those cases use this function. |
817 | 12.7M | PointerBase &getPointerBase() const { |
818 | 12.7M | return pointerBase_; |
819 | 12.7M | } |
820 | | |
821 | 18.3M | GCCallbacks &getCallbacks() const { |
822 | 18.3M | return gcCallbacks_; |
823 | 18.3M | } |
824 | | |
825 | | /// Forwards to the GC callback \p convertSymbolToUTF8, see documentation |
826 | | /// for that function. |
827 | 0 | std::string convertSymbolToUTF8(SymbolID id) { |
828 | 0 | return gcCallbacks_.convertSymbolToUTF8(id); |
829 | 0 | } |
830 | | |
831 | | /// Called by the Runtime to inform the GC that it is about to execute JS for |
832 | | /// the first time. |
833 | | void runtimeWillExecute(); |
834 | | |
835 | | /// Inform the GC that TTI has been reached. (In case, for example, |
836 | | /// behavior should change at that point. Default behavior is to do |
837 | | /// nothing.) |
838 | 0 | virtual void ttiReached() {} |
839 | | |
840 | | /// Do anything necessary to record the current number of allocated |
841 | | /// objects in numAllocatedObjects_. Default is to do nothing. |
842 | 0 | virtual void recordNumAllocatedObjects() {} |
843 | | |
844 | | /// Print any and all collected statistics to the give output stream, \p os. |
845 | | void printAllCollectedStats(llvh::raw_ostream &os); |
846 | | |
847 | | /// Total number of collections of any kind. |
848 | 0 | unsigned getNumGCs() const { |
849 | 0 | return cumStats_.numCollections; |
850 | 0 | } |
851 | | |
852 | | /// Total wall time in seconds of all pauses due to collections so far. |
853 | 0 | double getGCTime() const { |
854 | 0 | return cumStats_.gcWallTime.sum(); |
855 | 0 | } |
856 | | |
857 | | /// Total CPU time in seconds of all pauses due to collections so far. |
858 | 0 | double getGCCPUTime() const { |
859 | 0 | return cumStats_.gcCPUTime.sum(); |
860 | 0 | } |
861 | | |
862 | 0 | GCCallbacks &getGCCallbacks() const { |
863 | 0 | return gcCallbacks_; |
864 | 0 | } |
865 | | |
866 | | /// Cumulative stats over time so far. |
867 | 0 | virtual size_t getPeakAllocatedBytes() const { |
868 | 0 | return cumStats_.usedBefore.max(); |
869 | 0 | } |
870 | 0 | virtual size_t getPeakLiveAfterGC() const { |
871 | 0 | return cumStats_.usedAfter.max(); |
872 | 0 | } |
873 | | |
874 | | /// Populate \p info with information about the heap. |
875 | | virtual void getHeapInfo(HeapInfo &info); |
876 | | /// Same as \c getHeapInfo, and it adds the amount of malloc memory in use. |
877 | | virtual void getHeapInfoWithMallocSize(HeapInfo &info); |
878 | | |
879 | | /// Return a reference to the GCExecTrace object, which is used if |
880 | | /// we're keeping track of information about GCs, for tracing, for example. |
881 | | const GCExecTrace &getGCExecTrace() const; |
882 | | |
883 | | /// Populate \p info with crash manager information about the heap |
884 | | virtual void getCrashManagerHeapInfo(CrashManager::HeapInformation &info) = 0; |
885 | | |
886 | | #ifndef NDEBUG |
887 | | /// Populate \p info with more detailed information about the heap that is |
888 | | /// too expensive to know during production builds. |
889 | | virtual void getDebugHeapInfo(DebugHeapInfo &info); |
890 | | |
891 | | /// \return Number of weak ref slots currently in use. |
892 | | /// Inefficient. For testing/debugging. |
893 | | size_t countUsedWeakRefs() const; |
894 | | |
895 | | /// Return true if \p ptr is currently pointing at valid accessable memory, |
896 | | /// allocated to an object. |
897 | | virtual bool validPointer(const void *ptr) const = 0; |
898 | | #endif |
899 | | |
900 | | #ifdef HERMESVM_GC_RUNTIME |
901 | | inline static constexpr uint32_t minAllocationSizeImpl(); |
902 | | |
903 | | inline static constexpr uint32_t maxAllocationSizeImpl(); |
904 | | #endif |
905 | | |
906 | | inline static constexpr uint32_t minAllocationSize(); |
907 | | |
908 | | inline static constexpr uint32_t maxAllocationSize(); |
909 | | |
910 | | /// Dump detailed heap contents to the given output stream, \p os. |
911 | | virtual void dump(llvh::raw_ostream &os, bool verbose = false); |
912 | | |
913 | | /// Run the finalizers for all heap objects. |
914 | | virtual void finalizeAll() = 0; |
915 | | |
916 | | /// Force a garbage collection cycle. The provided cause will be used in |
917 | | /// logging. |
918 | | virtual void collect(std::string cause, bool canEffectiveOOM = false) = 0; |
919 | | |
920 | | /// Iterate over all objects in the heap, and call \p callback on them. |
921 | | /// \param callback A function to call on each found object. |
922 | | virtual void forAllObjs(const std::function<void(GCCell *)> &callback) = 0; |
923 | | |
924 | | /// \return true if the pointer lives in the young generation. |
925 | 0 | virtual bool inYoungGen(const void *p) const { |
926 | 0 | return false; |
927 | 0 | } |
928 | | |
929 | | /// Returns whether an external allocation of the given \p size fits |
930 | | /// within the maximum heap size. (Note that this does not guarantee that the |
931 | | /// allocation will "succeed" -- the size plus the used() of the heap may |
932 | | /// still exceed the max heap size. But if it fails, the allocation can never |
933 | | /// succeed.) |
934 | 0 | virtual bool canAllocExternalMemory(uint32_t size) { |
935 | 0 | return true; |
936 | 0 | } |
937 | | |
938 | | WeakRefSlot *allocWeakSlot(CompressedPointer ptr); |
939 | | |
940 | | #ifndef NDEBUG |
941 | | /// \name Debug APIs |
942 | | /// \{ |
943 | 0 | virtual bool calledByBackgroundThread() const { |
944 | 0 | return false; |
945 | 0 | } |
946 | 0 | virtual bool calledByGC() const { |
947 | 0 | return true; |
948 | 0 | } |
949 | | virtual bool dbgContains(const void *ptr) const = 0; |
950 | 0 | virtual void trackReachable(CellKind kind, unsigned sz) {} |
951 | | virtual bool needsWriteBarrier(void *loc, GCCell *value) = 0; |
952 | | /// \} |
953 | | #endif |
954 | | |
955 | | /// Do any logging of info about the heap that is useful, then dies with a |
956 | | /// fatal out-of-memory error. |
957 | | LLVM_ATTRIBUTE_NORETURN void oom(std::error_code reason); |
958 | | |
959 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
960 | | /// Creates a snapshot of the heap and writes it to the given \p fileName. |
961 | | /// \return An error code on failure, else an empty error code. |
962 | | std::error_code createSnapshotToFile(const std::string &fileName); |
963 | | |
964 | | /// Creates a snapshot of the heap, which includes information about what |
965 | | /// objects exist, their sizes, and what they point to. |
966 | | virtual void createSnapshot(llvh::raw_ostream &os) = 0; |
967 | | void createSnapshot(GC &gc, llvh::raw_ostream &os); |
968 | | |
969 | | /// Subclasses can override and add more specific native memory usage. |
970 | | virtual void snapshotAddGCNativeNodes(HeapSnapshot &snap); |
971 | | |
972 | | /// Subclasses can override and add more specific edges. |
973 | | virtual void snapshotAddGCNativeEdges(HeapSnapshot &snap); |
974 | | |
975 | | /// Turn on the heap profiler, which will track when allocations are made and |
976 | | /// the stack trace of when they were created. |
977 | | virtual void enableHeapProfiler( |
978 | | std::function<void( |
979 | | uint64_t, |
980 | | std::chrono::microseconds, |
981 | | std::vector<GCBase::AllocationLocationTracker::HeapStatsUpdate>)> |
982 | | fragmentCallback); |
983 | | |
984 | | /// Turn off the heap profiler, which will stop tracking new allocations and |
985 | | /// not record any stack traces. |
986 | | /// Disabling will not forget any objects that are still alive. This way |
987 | | /// re-enabling later will still remember earlier objects. |
988 | | virtual void disableHeapProfiler(); |
989 | | |
990 | | /// Turn the sampling memory profiler on. About once every |
991 | | /// \p samplingInterval bytes are allocated, sample the allocation by |
992 | | /// recording its stack. |
993 | | /// \param seed If non-negative, use as the seed for the random sampling |
994 | | /// mechanism, giving deterministic output. |
995 | | virtual void enableSamplingHeapProfiler( |
996 | | size_t samplingInterval, |
997 | | int64_t seed); |
998 | | |
999 | | /// Turn off the sampling heap profiler, which will stop tracking new |
1000 | | /// allocations and not record any stack traces. Write out the results of the |
1001 | | /// trace to \p os. After this call, any remembered data about sampled objects |
1002 | | /// will be gone. |
1003 | | virtual void disableSamplingHeapProfiler(llvh::raw_ostream &os); |
1004 | | #endif // HERMES_MEMORY_INSTRUMENTATION |
1005 | | |
1006 | | /// Inform the GC about external memory retained by objects. |
1007 | | virtual void creditExternalMemory(GCCell *alloc, uint32_t size) = 0; |
1008 | | virtual void debitExternalMemory(GCCell *alloc, uint32_t size) = 0; |
1009 | | |
1010 | | #ifdef HERMESVM_GC_RUNTIME |
1011 | | /// Default implementations for read and write barriers: do nothing. |
1012 | | void writeBarrier(const GCHermesValue *loc, HermesValue value); |
1013 | | void writeBarrier(const GCSmallHermesValue *loc, SmallHermesValue value); |
1014 | | void writeBarrier(const GCPointerBase *loc, const GCCell *value); |
1015 | | void constructorWriteBarrier(const GCHermesValue *loc, HermesValue value); |
1016 | | void constructorWriteBarrier( |
1017 | | const GCSmallHermesValue *loc, |
1018 | | SmallHermesValue value); |
1019 | | void constructorWriteBarrier(const GCPointerBase *loc, const GCCell *value); |
1020 | | void writeBarrierRange(const GCHermesValue *start, uint32_t numHVs); |
1021 | | void writeBarrierRange(const GCSmallHermesValue *start, uint32_t numHVs); |
1022 | | void constructorWriteBarrierRange( |
1023 | | const GCHermesValue *start, |
1024 | | uint32_t numHVs); |
1025 | | void constructorWriteBarrierRange( |
1026 | | const GCSmallHermesValue *start, |
1027 | | uint32_t numHVs); |
1028 | | void snapshotWriteBarrier(const GCHermesValue *loc); |
1029 | | void snapshotWriteBarrier(const GCSmallHermesValue *loc); |
1030 | | void snapshotWriteBarrier(const GCPointerBase *loc); |
1031 | | void snapshotWriteBarrier(const GCSymbolID *symbol); |
1032 | | void snapshotWriteBarrierRange(const GCHermesValue *start, uint32_t numHVs); |
1033 | | void snapshotWriteBarrierRange( |
1034 | | const GCSmallHermesValue *start, |
1035 | | uint32_t numHVs); |
1036 | | void weakRefReadBarrier(GCCell *value); |
1037 | | #endif |
1038 | | |
1039 | | /// @name Marking APIs |
1040 | | /// @{ |
1041 | | |
1042 | | /// Marks a cell by its metadata. |
1043 | | /// \p cell The heap object to mark. |
1044 | | /// \p acceptor The action to perform on each slot in the cell. |
1045 | | template <typename Acceptor> |
1046 | | inline void markCell(GCCell *cell, Acceptor &acceptor); |
1047 | | |
1048 | | /// Same as the normal \c markCell, but for cells that don't have a valid |
1049 | | /// CellKind. |
1050 | | template <typename Acceptor> |
1051 | | inline void markCell(GCCell *cell, CellKind kind, Acceptor &acceptor); |
1052 | | |
1053 | | /// Same as the normal \c markCell, but takes a visitor instead. |
1054 | | template <typename Acceptor> |
1055 | | inline void |
1056 | | markCell(SlotVisitor<Acceptor> &visitor, GCCell *cell, CellKind kind); |
1057 | | |
1058 | | /// Marks a cell by its metadata, but only for the slots that point between |
1059 | | /// [begin, end). |
1060 | | template <typename Acceptor> |
1061 | | inline void markCellWithinRange( |
1062 | | SlotVisitor<Acceptor> &visitor, |
1063 | | GCCell *cell, |
1064 | | CellKind kind, |
1065 | | const char *begin, |
1066 | | const char *end); |
1067 | | |
1068 | | /// Marks a cell by its metadata, and outputs the names of the slots. |
1069 | | /// Meant to be used by heap snapshots. |
1070 | | template <typename Acceptor> |
1071 | | inline void markCellWithNames( |
1072 | | SlotVisitorWithNames<Acceptor> &visitor, |
1073 | | GCCell *cell); |
1074 | | |
1075 | | /// Utilities for WeakMap marking. |
1076 | | |
1077 | | /// \return a list of pointers to all the WeakRefKeys in \p weakMap. |
1078 | | /// The \p gc argument is passed to methods that verify they're only |
1079 | | /// called during GC. |
1080 | | static std::vector<detail::WeakRefKey *> buildKeyList( |
1081 | | GC &gc, |
1082 | | JSWeakMap *weakMap); |
1083 | | |
1084 | | /// For all non-null keys in \p weakMap that are unreachable, clear |
1085 | | /// the key (clear the pointer in the WeakRefSlot) and value (set it |
1086 | | /// to undefined). |
1087 | | template <typename KeyReachableFunc> |
1088 | | static void clearEntriesWithUnreachableKeys( |
1089 | | GC &gc, |
1090 | | JSWeakMap *weakMap, |
1091 | | KeyReachableFunc keyReachable); |
1092 | | |
1093 | | /// For all reachable keys in \p weakMap, mark from the |
1094 | | /// corresponding value using the given \p acceptor, reaching a |
1095 | | /// transitive closure. The acceptor is required to have a property |
1096 | | /// we don't normally require: it must be idempotent. I.e., it must |
1097 | | /// function properly when applied multiple times to the same |
1098 | | /// pointer slot during a collection. "Mark-in-place" acceptors |
1099 | | /// will generally have this property. Uses \p objIsMarked to |
1100 | | /// determine whether an object is marked, and, for entries whose |
1101 | | /// keys are marked, invokes \p markFromVal on the corresponding value. |
1102 | | /// These have the following specs: |
1103 | | /// |
1104 | | /// * objIsMarked: (GCCell*) ==> bool |
1105 | | /// Returns whether a GCCell is marked. |
1106 | | /// |
1107 | | /// * markFromVal: GCCell *cell, GCHermesValue &cellRef) ==> bool |
1108 | | /// If the argument is unmarked, mark it, schedule for scanning. |
1109 | | /// Returns whether the object was newly marked. |
1110 | | /// |
1111 | | /// If the \p unreachableKeys map has an entry for \p weakMap, assumes the |
1112 | | /// list of WeakRefKeys contains all possibly-unreachable keys; any |
1113 | | /// other keys are assumed to have already been found reachable. |
1114 | | /// Ensures that \p unreachableKeys has an accurate value for \p |
1115 | | /// weakMap before return. \return whether any previously-unmarked |
1116 | | /// values were marked. |
1117 | | template < |
1118 | | typename Acceptor, |
1119 | | typename ObjIsMarkedFunc, |
1120 | | typename MarkFromValFunc> |
1121 | | static bool markFromReachableWeakMapKeys( |
1122 | | GC &gc, |
1123 | | JSWeakMap *weakMap, |
1124 | | Acceptor &acceptor, |
1125 | | llvh::DenseMap<JSWeakMap *, std::vector<detail::WeakRefKey *>> |
1126 | | *unreachableKeys, |
1127 | | ObjIsMarkedFunc objIsMarked, |
1128 | | MarkFromValFunc markFromVal); |
1129 | | |
1130 | | /// \return A reference to the mutex that controls accessing any WeakRef. |
1131 | | /// This mutex must be held if a WeakRef is created or modified. |
1132 | 1.71M | WeakRefMutex &weakRefMutex() { |
1133 | 1.71M | return weakRefMutex_; |
1134 | 1.71M | } |
1135 | | |
1136 | | /// Assumes that all known reachable WeakMaps have been collected in |
1137 | | /// \p reachableWeakMaps. For all these WeakMaps, find all |
1138 | | /// reachable keys and mark from the corresponding value using the given \p |
1139 | | /// acceptor, reaching a transitive closure. |
1140 | | /// Do this until no newly reachable objects are found in a |
1141 | | /// traversal of the WeakMaps. We assume that WeakMaps found newly |
1142 | | /// reachable are added to \p reachableWeakMaps, and do not assume |
1143 | | /// we've reached transitive closure until all maps are scanned. |
1144 | | /// Uses \p objIsMarked to determine whether an object is marked, |
1145 | | /// and, for entries whose keys are marked, invokes \p |
1146 | | /// checkValIsMarked on the corresponding value. Used \p |
1147 | | /// drainMarkStack to ensure that the transitive closure of what's |
1148 | | /// currently on the mark stack is marked. Requires \p acceptor |
1149 | | /// to be idempotent: it must be legal to apply the acceptor |
1150 | | /// multiple times to the same slot. The function arguments have |
1151 | | /// the following specs: |
1152 | | /// |
1153 | | /// * objIsMarked: (GCCell *) ==> bool |
1154 | | /// Returns whether a GCCell is marked. |
1155 | | /// |
1156 | | /// * markFromVal: (GCCell *cell, HermesValue &cellRef) ==> bool |
1157 | | /// Requires that \p cell is non-null, and the value of \p |
1158 | | /// cellRef. If the argument is unmarked, ensure that its |
1159 | | /// transitive closure is marked. Returns whether the object was |
1160 | | /// newly marked. |
1161 | | /// |
1162 | | /// * drainMarkStack: (Acceptor &acceptor) ==> void |
1163 | | /// Ensures that the mark stack used by the collector is empty; |
1164 | | /// the transitive closure of the original contents is marked. |
1165 | | /// |
1166 | | /// * checkMarkStackOverflow: () ==> bool |
1167 | | /// Returns whether mark stack overflow has occurred. |
1168 | | /// |
1169 | | /// Some collectors compute the allocated bytes during GC. If this |
1170 | | /// is done in \p drainMarkStack, that will cover all objects except |
1171 | | /// WeakWaps, which are never pushed on the mark stack. Thus: |
1172 | | /// \return the total size of reachable WeakMaps. |
1173 | | template < |
1174 | | typename Acceptor, |
1175 | | typename ObjIsMarkedFunc, |
1176 | | typename MarkFromValFunc, |
1177 | | typename DrainMarkStackFunc, |
1178 | | typename CheckMarkStackOverflowFunc> |
1179 | | static gcheapsize_t completeWeakMapMarking( |
1180 | | GC &gc, |
1181 | | Acceptor &acceptor, |
1182 | | std::vector<JSWeakMap *> &reachableWeakMaps, |
1183 | | ObjIsMarkedFunc objIsMarked, |
1184 | | MarkFromValFunc markFromVal, |
1185 | | DrainMarkStackFunc drainMarkStack, |
1186 | | CheckMarkStackOverflowFunc checkMarkStackOverflow); |
1187 | | |
1188 | | /// @} |
1189 | | |
1190 | | /// If false, all reachable objects in the heap will have a dereference-able |
1191 | | /// VTable pointer which describes its type and size. If true, both reachable |
1192 | | /// objects and unreachable objects may not have dereference-able VTable |
1193 | | /// pointers, and any reads from JS heap memory may give strange results. |
1194 | 312k | bool inGC() const { |
1195 | 312k | return inGC_; |
1196 | 312k | } |
1197 | | |
1198 | 0 | HeapKind getKind() const { |
1199 | 0 | return heapKind_; |
1200 | 0 | } |
1201 | | |
1202 | | /// \return A string representation of the kind of GC. |
1203 | | virtual std::string getKindAsStr() const = 0; |
1204 | | |
1205 | 1.22k | bool isTrackingIDs() { |
1206 | 1.22k | #ifdef HERMES_MEMORY_INSTRUMENTATION |
1207 | 1.22k | return getIDTracker().isTrackingIDs() || |
1208 | 1.22k | getAllocationLocationTracker().isEnabled() || |
1209 | 1.22k | getSamplingAllocationTracker().isEnabled(); |
1210 | | #else |
1211 | | return getIDTracker().isTrackingIDs(); |
1212 | | #endif |
1213 | 1.22k | } |
1214 | | |
1215 | 7.92k | IDTracker &getIDTracker() { |
1216 | 7.92k | return idTracker_; |
1217 | 7.92k | } |
1218 | | |
1219 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
1220 | 1.22k | AllocationLocationTracker &getAllocationLocationTracker() { |
1221 | 1.22k | return allocationLocationTracker_; |
1222 | 1.22k | } |
1223 | | |
1224 | 1.22k | SamplingAllocationLocationTracker &getSamplingAllocationTracker() { |
1225 | 1.22k | return samplingAllocationTracker_; |
1226 | 1.22k | } |
1227 | | #endif |
1228 | | |
1229 | | /// \name Snapshot ID methods |
1230 | | /// \{ |
1231 | | // This set of methods are all mirrors of IDTracker, except with pointer |
1232 | | // compression done automatically. |
1233 | | HeapSnapshot::NodeID getObjectID(const GCCell *cell); |
1234 | | HeapSnapshot::NodeID getObjectIDMustExist(const GCCell *cell); |
1235 | | HeapSnapshot::NodeID getObjectID(CompressedPointer cell); |
1236 | | HeapSnapshot::NodeID getObjectID(SymbolID sym); |
1237 | | HeapSnapshot::NodeID getNativeID(const void *mem); |
1238 | | /// \return The ID for the given value. If the value cannot be represented |
1239 | | /// with an ID, returns None. |
1240 | | llvh::Optional<HeapSnapshot::NodeID> getSnapshotID(HermesValue val); |
1241 | | |
1242 | | /// \return The object pointer for the given \p id. This is quite slow. |
1243 | | /// If there is no such object this returns nullptr. |
1244 | | void *getObjectForID(HeapSnapshot::NodeID id); |
1245 | | |
1246 | | /// \return True if the given cell has an ID associated with it. |
1247 | | bool hasObjectID(const GCCell *cell); |
1248 | | /// Records that a new allocation has occurred. |
1249 | | void newAlloc(const GCCell *ptr, uint32_t sz); |
1250 | | /// Moves an object to a new address and a new size for all trackers. |
1251 | | void moveObject( |
1252 | | const GCCell *oldPtr, |
1253 | | uint32_t oldSize, |
1254 | | const GCCell *newPtr, |
1255 | | uint32_t newSize); |
1256 | | /// Untracks a freed object from all trackers. |
1257 | | void untrackObject(const GCCell *cell, uint32_t sz); |
1258 | | /// \} |
1259 | | |
1260 | | #ifndef NDEBUG |
1261 | | /// \return The next debug allocation ID for embedding directly into a GCCell. |
1262 | | /// NOTE: This is not the same ID as is used for stable lifetime tracking, use |
1263 | | /// \p getObjectID for that. |
1264 | | uint64_t nextObjectID(); |
1265 | | #endif |
1266 | | |
1267 | | // Mangling scheme used by MSVC encode public/private into the name. |
1268 | | // As a result, vanilla "ifdef public" trick leads to link errors. |
1269 | | #if defined(UNIT_TEST) || defined(_MSC_VER) |
1270 | | public: |
1271 | | #else |
1272 | | protected: |
1273 | | #endif |
1274 | | |
1275 | | /// dataSize is the live data in bytes, now is the current time point. The |
1276 | | /// function checks these parameters against the limits set at initialisation. |
1277 | | /// If the conditions are met, the tripwire is triggered and tripwireCallback_ |
1278 | | /// is called. |
1279 | | void checkTripwire(size_t dataSize); |
1280 | | |
1281 | | // Visibility here is public for unit_tests and protected otherwise |
1282 | | |
1283 | | protected: |
1284 | | /// An RAII-style object used to denote regions when a GC cycle is considered |
1285 | | /// active. |
1286 | | class GCCycle final { |
1287 | | public: |
1288 | | explicit GCCycle(GCBase &gc, std::string extraInfo = ""); |
1289 | | ~GCCycle(); |
1290 | | |
1291 | 0 | const std::string &extraInfo() { |
1292 | 0 | return extraInfo_; |
1293 | 0 | } |
1294 | | |
1295 | | private: |
1296 | | GCBase &gc_; |
1297 | | std::string extraInfo_; |
1298 | | bool previousInGC_; |
1299 | | }; |
1300 | | |
1301 | | /// Returns the number of bytes allocated allocated since the last GC. |
1302 | | /// TODO: Implement this for heaps other than GenGC |
1303 | | /// (at which point this can become an abstract function). |
1304 | 0 | virtual gcheapsize_t bytesAllocatedSinceLastGC() const { |
1305 | 0 | return 0; |
1306 | 0 | } |
1307 | | |
1308 | | /// Convenience method to invoke the mark roots function provided at |
1309 | | /// initialization, using the context provided then (on this heap). |
1310 | | /// The \p markLongLived argument indicates whether root data structures |
1311 | | /// containing only pointers to objects allocated via allocLongLived |
1312 | | /// are required to be marked. In this collector, such objects will |
1313 | | /// be allocated in the old gen, and references to them need not be |
1314 | | /// marked during young-gen collection. |
1315 | 1.96k | void markRoots(RootAndSlotAcceptorWithNames &acceptor, bool markLongLived) { |
1316 | 1.96k | gcCallbacks_.markRoots(acceptor, markLongLived); |
1317 | 1.96k | } |
1318 | | |
1319 | | /// Convenience method to invoke the mark weak roots function provided at |
1320 | | /// initialization, using the context provided then (on this heap). |
1321 | 1.96k | void markWeakRoots(WeakRootAcceptor &acceptor, bool markLongLived) { |
1322 | 1.96k | gcCallbacks_.markWeakRoots(acceptor, markLongLived); |
1323 | 1.96k | acceptor.beginRootSection(RootAcceptor::Section::WeakRefSlots); |
1324 | 1.32M | for (auto &slot : weakSlots_) { |
1325 | 1.32M | slot.markWeakRoots(acceptor); |
1326 | 1.32M | } |
1327 | 1.96k | acceptor.endRootSection(); |
1328 | 1.96k | } |
1329 | | |
1330 | | /// Frees the weak slot, so it can be re-used by future WeakRef allocations. |
1331 | 3.00k | void freeWeakSlot(WeakRefSlot *slot) { |
1332 | 3.00k | slot->free(firstFreeWeak_); |
1333 | 3.00k | firstFreeWeak_ = slot; |
1334 | 3.00k | } |
1335 | | |
1336 | | /// Print the cumulative statistics. |
1337 | | virtual void printStats(JSONEmitter &json); |
1338 | | |
1339 | | /// Record statistics from a single GC, which are specified in the given |
1340 | | /// \p event, in the overall cumulative stats struct. |
1341 | | void recordGCStats(const GCAnalyticsEvent &event, bool onMutator); |
1342 | | |
1343 | | /// Record statistics from a single GC, which are specified in the given |
1344 | | /// \p event, in the given cumulative stats struct. |
1345 | | void recordGCStats( |
1346 | | const GCAnalyticsEvent &event, |
1347 | | CumulativeHeapStats *stats, |
1348 | | bool onMutator); |
1349 | | |
1350 | | /// Print detailed stats of the breakdown of the roots and heap in terms of |
1351 | | /// the number of pointers, symbols, HermesValues, etc. |
1352 | | void sizeDiagnosticCensus(size_t allocatedBytes); |
1353 | | |
1354 | | /// Do any additional GC-specific logging that is useful before dying with |
1355 | | /// out-of-memory. Takes a char buffer to avoid dynamic allocations. |
1356 | | virtual void oomDetail( |
1357 | | llvh::MutableArrayRef<char> detailBuffer, |
1358 | | std::error_code reason); |
1359 | | |
1360 | | /// If a cell has any weak references to mark, and the acceptor supports |
1361 | | /// marking them, mark those weak references. |
1362 | | template <typename Acceptor> |
1363 | | void markWeakRefsIfNecessary(GCCell *cell, CellKind kind, Acceptor &acceptor); |
1364 | | |
1365 | | /// Overload of \p markWeakRefsIfNecessary for acceptors that support marking |
1366 | | /// weak references. |
1367 | | /// Don't call this directly, use the three-argument variant instead. |
1368 | | template <typename Acceptor> |
1369 | | void markWeakRefsIfNecessary( |
1370 | | GCCell *cell, |
1371 | | CellKind kind, |
1372 | | Acceptor &acceptor, |
1373 | 4.51M | std::true_type) { |
1374 | | // In C++17, we could implement this via "constexpr if" rather than |
1375 | | // overloads with std::true_type. |
1376 | | // Once C++17 is available, switch to using that. |
1377 | 4.51M | if (auto *cb = VTable::getVTable(kind)->getMarkWeakCallback()) { |
1378 | 883k | std::lock_guard<Mutex> wrLk{weakRefMutex()}; |
1379 | 883k | cb(cell, acceptor); |
1380 | 883k | } |
1381 | 4.51M | } void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::HadesGC::MarkAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::HadesGC::MarkAcceptor&, std::__1::integral_constant<bool, true>) Line | Count | Source | 1373 | 734k | std::true_type) { | 1374 | | // In C++17, we could implement this via "constexpr if" rather than | 1375 | | // overloads with std::true_type. | 1376 | | // Once C++17 is available, switch to using that. | 1377 | 734k | if (auto *cb = VTable::getVTable(kind)->getMarkWeakCallback()) { | 1378 | 145k | std::lock_guard<Mutex> wrLk{weakRefMutex()}; | 1379 | 145k | cb(cell, acceptor); | 1380 | 145k | } | 1381 | 734k | } |
void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::CheckHeapWellFormedAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::CheckHeapWellFormedAcceptor&, std::__1::integral_constant<bool, true>) Line | Count | Source | 1373 | 3.77M | std::true_type) { | 1374 | | // In C++17, we could implement this via "constexpr if" rather than | 1375 | | // overloads with std::true_type. | 1376 | | // Once C++17 is available, switch to using that. | 1377 | 3.77M | if (auto *cb = VTable::getVTable(kind)->getMarkWeakCallback()) { | 1378 | 738k | std::lock_guard<Mutex> wrLk{weakRefMutex()}; | 1379 | 738k | cb(cell, acceptor); | 1380 | 738k | } | 1381 | 3.77M | } |
|
1382 | | |
1383 | | /// Overload of \p markWeakRefsIfNecessary for acceptors that do not support |
1384 | | /// marking weak references. |
1385 | | /// Don't call this directly, use the three-argument variant instead. |
1386 | | template <typename Acceptor> |
1387 | | static void markWeakRefsIfNecessary( |
1388 | | GCCell *, |
1389 | | CellKind kind, |
1390 | | Acceptor &, |
1391 | 3.08M | std::false_type) {} Unexecuted instantiation: GCBase.cpp:void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::(anonymous namespace)::PrimitiveNodeAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::(anonymous namespace)::PrimitiveNodeAcceptor&, std::__1::integral_constant<bool, false>) Unexecuted instantiation: GCBase.cpp:void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::(anonymous namespace)::EdgeAddingAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::(anonymous namespace)::EdgeAddingAcceptor&, std::__1::integral_constant<bool, false>) Unexecuted instantiation: GCBase.cpp:void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::GCBase::sizeDiagnosticCensus(unsigned long)::HeapSizeDiagnosticAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::GCBase::sizeDiagnosticCensus(unsigned long)::HeapSizeDiagnosticAcceptor&, std::__1::integral_constant<bool, false>) Unexecuted instantiation: void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::HadesGC::EvacAcceptor<true> >(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::HadesGC::EvacAcceptor<true>&, std::__1::integral_constant<bool, false>) void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::HadesGC::EvacAcceptor<false> >(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::HadesGC::EvacAcceptor<false>&, std::__1::integral_constant<bool, false>) Line | Count | Source | 1391 | 45.6k | std::false_type) {} |
Unexecuted instantiation: void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::SkipWeakRefsAcceptor<hermes::vm::HadesGC::MarkAcceptor> >(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::SkipWeakRefsAcceptor<hermes::vm::HadesGC::MarkAcceptor>&, std::__1::integral_constant<bool, false>) HadesGC.cpp:void hermes::vm::GCBase::markWeakRefsIfNecessary<hermes::vm::HadesGC::verifyCardTable()::VerifyCardDirtyAcceptor>(hermes::vm::GCCell*, hermes::vm::CellKind, hermes::vm::HadesGC::verifyCardTable()::VerifyCardDirtyAcceptor&, std::__1::integral_constant<bool, false>) Line | Count | Source | 1391 | 3.03M | std::false_type) {} |
|
1392 | | |
1393 | | template <typename T, class... Args> |
1394 | 647k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { |
1395 | 647k | auto *cell = new (ptr) T(std::forward<Args>(args)...); |
1396 | 647k | constexpr auto kind = T::getCellKind(); |
1397 | 647k | cell->setKindAndSize({kind, size}); |
1398 | 647k | return cell; |
1399 | 647k | } Unexecuted instantiation: hermes::vm::BigIntPrimitive* hermes::vm::GCBase::constructCell<hermes::vm::BigIntPrimitive, unsigned int&>(void*, unsigned int, unsigned int&) hermes::vm::BoxedDouble* hermes::vm::GCBase::constructCell<hermes::vm::BoxedDouble, double&>(void*, unsigned int, double&) Line | Count | Source | 1394 | 2.76k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 2.76k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 2.76k | constexpr auto kind = T::getCellKind(); | 1397 | 2.76k | cell->setKindAndSize({kind, size}); | 1398 | 2.76k | return cell; | 1399 | 2.76k | } |
hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32>* hermes::vm::GCBase::constructCell<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32>>(void*, unsigned int) Line | Count | Source | 1394 | 58.8k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 58.8k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 58.8k | constexpr auto kind = T::getCellKind(); | 1397 | 58.8k | cell->setKindAndSize({kind, size}); | 1398 | 58.8k | return cell; | 1399 | 58.8k | } |
hermes::vm::Environment* hermes::vm::GCBase::constructCell<hermes::vm::Environment, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Environment>&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Environment>&, unsigned int&) Line | Count | Source | 1394 | 493 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 493 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 493 | constexpr auto kind = T::getCellKind(); | 1397 | 493 | cell->setKindAndSize({kind, size}); | 1398 | 493 | return cell; | 1399 | 493 | } |
hermes::vm::NativeConstructor* hermes::vm::GCBase::constructCell<hermes::vm::NativeConstructor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&) Line | Count | Source | 1394 | 4.30k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 4.30k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 4.30k | constexpr auto kind = T::getCellKind(); | 1397 | 4.30k | cell->setKindAndSize({kind, size}); | 1398 | 4.30k | return cell; | 1399 | 4.30k | } |
Unexecuted instantiation: hermes::vm::NativeConstructor* hermes::vm::GCBase::constructCell<hermes::vm::NativeConstructor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), hermes::vm::CallResult<hermes::vm::PseudoHandle<hermes::vm::JSObject>, (hermes::vm::detail::CallResultSpecialize)6> (*&)(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, void*), hermes::vm::CellKind&) hermes::vm::ArrayStorageBase<hermes::vm::HermesValue>* hermes::vm::GCBase::constructCell<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue>>(void*, unsigned int) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
hermes::vm::BoundFunction* hermes::vm::GCBase::constructCell<hermes::vm::BoundFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::MutableHandle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::MutableHandle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue> >&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
hermes::vm::NativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::NativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)) Line | Count | Source | 1394 | 45.2k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 45.2k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 45.2k | constexpr auto kind = T::getCellKind(); | 1397 | 45.2k | cell->setKindAndSize({kind, size}); | 1398 | 45.2k | return cell; | 1399 | 45.2k | } |
Unexecuted instantiation: hermes::vm::NativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::NativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs)) hermes::vm::JSFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&) Line | Count | Source | 1394 | 4.00k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 4.00k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 4.00k | constexpr auto kind = T::getCellKind(); | 1397 | 4.00k | cell->setKindAndSize({kind, size}); | 1398 | 4.00k | return cell; | 1399 | 4.00k | } |
Unexecuted instantiation: hermes::vm::JSAsyncFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSAsyncFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&) Unexecuted instantiation: hermes::vm::JSGeneratorFunction* hermes::vm::GCBase::constructCell<hermes::vm::JSGeneratorFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&) Unexecuted instantiation: hermes::vm::GeneratorInnerFunction* hermes::vm::GCBase::constructCell<hermes::vm::GeneratorInnerFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&, unsigned int>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Domain>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::Environment>&, hermes::vm::CodeBlock*&, unsigned int&&) hermes::vm::Domain* hermes::vm::GCBase::constructCell<hermes::vm::Domain>(void*, unsigned int) Line | Count | Source | 1394 | 283 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 283 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 283 | constexpr auto kind = T::getCellKind(); | 1397 | 283 | cell->setKindAndSize({kind, size}); | 1398 | 283 | return cell; | 1399 | 283 | } |
Unexecuted instantiation: hermes::vm::RequireContext* hermes::vm::GCBase::constructCell<hermes::vm::RequireContext, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) hermes::vm::HiddenClass* hermes::vm::GCBase::constructCell<hermes::vm::HiddenClass, hermes::vm::Runtime&, hermes::vm::ClassFlags&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::SymbolID&, hermes::vm::PropertyFlags&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::ClassFlags&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::SymbolID&, hermes::vm::PropertyFlags&, unsigned int&) Line | Count | Source | 1394 | 63.0k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 63.0k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 63.0k | constexpr auto kind = T::getCellKind(); | 1397 | 63.0k | cell->setKindAndSize({kind, size}); | 1398 | 63.0k | return cell; | 1399 | 63.0k | } |
hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, unsigned long&>(void*, unsigned int, unsigned long&) Line | Count | Source | 1394 | 86.3k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 86.3k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 86.3k | constexpr auto kind = T::getCellKind(); | 1397 | 86.3k | cell->setKindAndSize({kind, size}); | 1398 | 86.3k | return cell; | 1399 | 86.3k | } |
hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, unsigned long&>(void*, unsigned int, unsigned long&) Line | Count | Source | 1394 | 159 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 159 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 159 | constexpr auto kind = T::getCellKind(); | 1397 | 159 | cell->setKindAndSize({kind, size}); | 1398 | 159 | return cell; | 1399 | 159 | } |
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, unsigned long&>(void*, unsigned int, unsigned long&) Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, unsigned long&>(void*, unsigned int, unsigned long&) Unexecuted instantiation: hermes::vm::Arguments* hermes::vm::GCBase::constructCell<hermes::vm::Arguments, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&) hermes::vm::JSArray* hermes::vm::GCBase::constructCell<hermes::vm::JSArray, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::GCPointerBase::NoBarriers>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::GCPointerBase::NoBarriers&&) Line | Count | Source | 1394 | 14.9k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 14.9k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 14.9k | constexpr auto kind = T::getCellKind(); | 1397 | 14.9k | cell->setKindAndSize({kind, size}); | 1398 | 14.9k | return cell; | 1399 | 14.9k | } |
Unexecuted instantiation: hermes::vm::JSArrayIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSArrayIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::IterationKind&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::IterationKind&) Unexecuted instantiation: hermes::vm::JSArrayBuffer* hermes::vm::GCBase::constructCell<hermes::vm::JSArrayBuffer, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSDataView* hermes::vm::GCBase::constructCell<hermes::vm::JSDataView, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSDate* hermes::vm::GCBase::constructCell<hermes::vm::JSDate, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) hermes::vm::JSError* hermes::vm::GCBase::constructCell<hermes::vm::JSError, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, bool&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, bool&) Line | Count | Source | 1394 | 32 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 32 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 32 | constexpr auto kind = T::getCellKind(); | 1397 | 32 | cell->setKindAndSize({kind, size}); | 1398 | 32 | return cell; | 1399 | 32 | } |
Unexecuted instantiation: hermes::vm::JSGenerator* hermes::vm::GCBase::constructCell<hermes::vm::JSGenerator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) hermes::vm::JSObject* hermes::vm::GCBase::constructCell<hermes::vm::JSObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::GCPointerBase::NoBarriers>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::GCPointerBase::NoBarriers&&) Line | Count | Source | 1394 | 6.24k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 6.24k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 6.24k | constexpr auto kind = T::getCellKind(); | 1397 | 6.24k | cell->setKindAndSize({kind, size}); | 1398 | 6.24k | return cell; | 1399 | 6.24k | } |
Unexecuted instantiation: hermes::vm::JSProxy* hermes::vm::GCBase::constructCell<hermes::vm::JSProxy, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) hermes::vm::JSRegExp* hermes::vm::GCBase::constructCell<hermes::vm::JSRegExp, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Line | Count | Source | 1394 | 77 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 77 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 77 | constexpr auto kind = T::getCellKind(); | 1397 | 77 | cell->setKindAndSize({kind, size}); | 1398 | 77 | return cell; | 1399 | 77 | } |
Unexecuted instantiation: hermes::vm::JSMapImpl<(hermes::vm::CellKind)47>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapImpl<(hermes::vm::CellKind)47>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSMapImpl<(hermes::vm::CellKind)48>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapImpl<(hermes::vm::CellKind)48>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)50>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)50>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)49>* hermes::vm::GCBase::constructCell<hermes::vm::JSMapIteratorImpl<(hermes::vm::CellKind)49>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<signed char, (hermes::vm::CellKind)35>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<short, (hermes::vm::CellKind)36>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<int, (hermes::vm::CellKind)37>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)38>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned char, (hermes::vm::CellKind)39>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned short, (hermes::vm::CellKind)40>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned int, (hermes::vm::CellKind)41>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<float, (hermes::vm::CellKind)42>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<double, (hermes::vm::CellKind)43>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<long, (hermes::vm::CellKind)44>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>* hermes::vm::GCBase::constructCell<hermes::vm::JSTypedArray<unsigned long, (hermes::vm::CellKind)45>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)51>* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)51>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&) Unexecuted instantiation: hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)52>* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakMapImpl<(hermes::vm::CellKind)52>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&) Unexecuted instantiation: hermes::vm::JSWeakRef* hermes::vm::GCBase::constructCell<hermes::vm::JSWeakRef, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::DecoratedObject* hermes::vm::GCBase::constructCell<hermes::vm::DecoratedObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, std::__1::unique_ptr<hermes::vm::DecoratedObject::Decoration, std::__1::default_delete<hermes::vm::DecoratedObject::Decoration> > >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, std::__1::unique_ptr<hermes::vm::DecoratedObject::Decoration, std::__1::default_delete<hermes::vm::DecoratedObject::Decoration> >&&) Unexecuted instantiation: hermes::vm::FinalizableNativeFunction* hermes::vm::GCBase::constructCell<hermes::vm::FinalizableNativeFunction, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), void (*&)(void*)>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, void*&, hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> (*&)(void*, hermes::vm::Runtime&, hermes::vm::NativeArgs), void (*&)(void*)) hermes::vm::HostObject* hermes::vm::GCBase::constructCell<hermes::vm::HostObject, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, std::__1::unique_ptr<hermes::vm::HostObjectProxy, std::__1::default_delete<hermes::vm::HostObjectProxy> > >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, std::__1::unique_ptr<hermes::vm::HostObjectProxy, std::__1::default_delete<hermes::vm::HostObjectProxy> >&&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
Unexecuted instantiation: hermes::vm::NativeState* hermes::vm::GCBase::constructCell<hermes::vm::NativeState, void*&, void (*&)(void*)>(void*, unsigned int, void*&, void (*&)(void*)) hermes::vm::JSString* hermes::vm::GCBase::constructCell<hermes::vm::JSString, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
Unexecuted instantiation: hermes::vm::JSStringIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSStringIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&) Unexecuted instantiation: hermes::vm::JSBigInt* hermes::vm::GCBase::constructCell<hermes::vm::JSBigInt, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&) hermes::vm::JSNumber* hermes::vm::GCBase::constructCell<hermes::vm::JSNumber, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, double&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&) Line | Count | Source | 1394 | 108 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 108 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 108 | constexpr auto kind = T::getCellKind(); | 1397 | 108 | cell->setKindAndSize({kind, size}); | 1398 | 108 | return cell; | 1399 | 108 | } |
hermes::vm::JSBoolean* hermes::vm::GCBase::constructCell<hermes::vm::JSBoolean, hermes::vm::Runtime&, bool&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, bool&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
Unexecuted instantiation: hermes::vm::JSSymbol* hermes::vm::GCBase::constructCell<hermes::vm::JSSymbol, hermes::vm::Runtime&, hermes::vm::SymbolID&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::SymbolID&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&) hermes::vm::PropertyAccessor* hermes::vm::GCBase::constructCell<hermes::vm::PropertyAccessor, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::Handle<hermes::vm::Callable>&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::Callable>&, hermes::vm::Handle<hermes::vm::Callable>&) Line | Count | Source | 1394 | 4.64k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 4.64k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 4.64k | constexpr auto kind = T::getCellKind(); | 1397 | 4.64k | cell->setKindAndSize({kind, size}); | 1398 | 4.64k | return cell; | 1399 | 4.64k | } |
hermes::vm::FillerCell* hermes::vm::GCBase::constructCell<hermes::vm::FillerCell>(void*, unsigned int) Line | Count | Source | 1394 | 274 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 274 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 274 | constexpr auto kind = T::getCellKind(); | 1397 | 274 | cell->setKindAndSize({kind, size}); | 1398 | 274 | return cell; | 1399 | 274 | } |
Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment>(void*, unsigned int) hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>>(void*, unsigned int) Line | Count | Source | 1394 | 1 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 1 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 1 | constexpr auto kind = T::getCellKind(); | 1397 | 1 | cell->setKindAndSize({kind, size}); | 1398 | 1 | return cell; | 1399 | 1 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment>(void*, unsigned int) Line | Count | Source | 1394 | 868 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 868 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 868 | constexpr auto kind = T::getCellKind(); | 1397 | 868 | cell->setKindAndSize({kind, size}); | 1398 | 868 | return cell; | 1399 | 868 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>* hermes::vm::GCBase::constructCell<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>>(void*, unsigned int) Line | Count | Source | 1394 | 2.49k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 2.49k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 2.49k | constexpr auto kind = T::getCellKind(); | 1397 | 2.49k | cell->setKindAndSize({kind, size}); | 1398 | 2.49k | return cell; | 1399 | 2.49k | } |
Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, llvh::ArrayRef<char16_t>&>(void*, unsigned int, llvh::ArrayRef<char16_t>&) Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char16_t, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, true>, unsigned int&>(void*, unsigned int, unsigned int&) Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, llvh::ArrayRef<char>&>(void*, unsigned int, llvh::ArrayRef<char>&) Unexecuted instantiation: hermes::vm::DynamicStringPrimitive<char, true>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, true>, unsigned int&>(void*, unsigned int, unsigned int&) hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, llvh::ArrayRef<char16_t>&>(void*, unsigned int, llvh::ArrayRef<char16_t>&) Line | Count | Source | 1394 | 13.4k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 13.4k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 13.4k | constexpr auto kind = T::getCellKind(); | 1397 | 13.4k | cell->setKindAndSize({kind, size}); | 1398 | 13.4k | return cell; | 1399 | 13.4k | } |
hermes::vm::DynamicStringPrimitive<char16_t, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char16_t, false>, unsigned int&>(void*, unsigned int, unsigned int&) Line | Count | Source | 1394 | 31.8k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 31.8k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 31.8k | constexpr auto kind = T::getCellKind(); | 1397 | 31.8k | cell->setKindAndSize({kind, size}); | 1398 | 31.8k | return cell; | 1399 | 31.8k | } |
hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, llvh::ArrayRef<char>&>(void*, unsigned int, llvh::ArrayRef<char>&) Line | Count | Source | 1394 | 224k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 224k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 224k | constexpr auto kind = T::getCellKind(); | 1397 | 224k | cell->setKindAndSize({kind, size}); | 1398 | 224k | return cell; | 1399 | 224k | } |
hermes::vm::DynamicStringPrimitive<char, false>* hermes::vm::GCBase::constructCell<hermes::vm::DynamicStringPrimitive<char, false>, unsigned int&>(void*, unsigned int, unsigned int&) Line | Count | Source | 1394 | 516 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 516 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 516 | constexpr auto kind = T::getCellKind(); | 1397 | 516 | cell->setKindAndSize({kind, size}); | 1398 | 516 | return cell; | 1399 | 516 | } |
hermes::vm::ExternalStringPrimitive<char16_t>* hermes::vm::GCBase::constructCell<hermes::vm::ExternalStringPrimitive<char16_t>, std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> > >(void*, unsigned int, std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >&&) Line | Count | Source | 1394 | 109 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 109 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 109 | constexpr auto kind = T::getCellKind(); | 1397 | 109 | cell->setKindAndSize({kind, size}); | 1398 | 109 | return cell; | 1399 | 109 | } |
hermes::vm::ExternalStringPrimitive<char>* hermes::vm::GCBase::constructCell<hermes::vm::ExternalStringPrimitive<char>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(void*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Line | Count | Source | 1394 | 9 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 9 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 9 | constexpr auto kind = T::getCellKind(); | 1397 | 9 | cell->setKindAndSize({kind, size}); | 1398 | 9 | return cell; | 1399 | 9 | } |
hermes::vm::BufferedStringPrimitive<char16_t>* hermes::vm::GCBase::constructCell<hermes::vm::BufferedStringPrimitive<char16_t>, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char16_t> >&>(void*, unsigned int, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char16_t> >&) Line | Count | Source | 1394 | 64 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 64 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 64 | constexpr auto kind = T::getCellKind(); | 1397 | 64 | cell->setKindAndSize({kind, size}); | 1398 | 64 | return cell; | 1399 | 64 | } |
hermes::vm::BufferedStringPrimitive<char>* hermes::vm::GCBase::constructCell<hermes::vm::BufferedStringPrimitive<char>, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char> >&>(void*, unsigned int, hermes::vm::Runtime&, unsigned int&, hermes::vm::Handle<hermes::vm::ExternalStringPrimitive<char> >&) Line | Count | Source | 1394 | 30.3k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 30.3k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 30.3k | constexpr auto kind = T::getCellKind(); | 1397 | 30.3k | cell->setKindAndSize({kind, size}); | 1398 | 30.3k | return cell; | 1399 | 30.3k | } |
hermes::vm::SingleObject<(hermes::vm::CellKind)60>* hermes::vm::GCBase::constructCell<hermes::vm::SingleObject<(hermes::vm::CellKind)60>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
hermes::vm::SingleObject<(hermes::vm::CellKind)59>* hermes::vm::GCBase::constructCell<hermes::vm::SingleObject<(hermes::vm::CellKind)59>, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
hermes::vm::HadesGC::OldGen::FreelistCell* hermes::vm::GCBase::constructCell<hermes::vm::HadesGC::OldGen::FreelistCell>(void*, unsigned int) Line | Count | Source | 1394 | 27.1k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 27.1k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 27.1k | constexpr auto kind = T::getCellKind(); | 1397 | 27.1k | cell->setKindAndSize({kind, size}); | 1398 | 27.1k | return cell; | 1399 | 27.1k | } |
hermes::vm::DictPropertyMap* hermes::vm::GCBase::constructCell<hermes::vm::DictPropertyMap, unsigned int&, unsigned int&>(void*, unsigned int, unsigned int&, unsigned int&) Line | Count | Source | 1394 | 23.8k | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 23.8k | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 23.8k | constexpr auto kind = T::getCellKind(); | 1397 | 23.8k | cell->setKindAndSize({kind, size}); | 1398 | 23.8k | return cell; | 1399 | 23.8k | } |
Unexecuted instantiation: hermes::vm::testhelpers::DummyObject* hermes::vm::GCBase::constructCell<hermes::vm::testhelpers::DummyObject, hermes::vm::HadesGC&>(void*, unsigned int, hermes::vm::HadesGC&) Unexecuted instantiation: hermes::vm::HashMapEntry* hermes::vm::GCBase::constructCell<hermes::vm::HashMapEntry>(void*, unsigned int) hermes::vm::OrderedHashMap* hermes::vm::GCBase::constructCell<hermes::vm::OrderedHashMap, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32> >&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::ArrayStorageBase<hermes::vm::HermesValue32> >&) Line | Count | Source | 1394 | 105 | static T *constructCell(void *ptr, uint32_t size, Args &&...args) { | 1395 | 105 | auto *cell = new (ptr) T(std::forward<Args>(args)...); | 1396 | 105 | constexpr auto kind = T::getCellKind(); | 1397 | 105 | cell->setKindAndSize({kind, size}); | 1398 | 105 | return cell; | 1399 | 105 | } |
Unexecuted instantiation: hermes::vm::JSCallSite* hermes::vm::GCBase::constructCell<hermes::vm::JSCallSite, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::JSError>&, unsigned int&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::JSError>&, unsigned int&) Unexecuted instantiation: hermes::vm::JSCallableProxy* hermes::vm::GCBase::constructCell<hermes::vm::JSCallableProxy, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>, hermes::vm::Handle<hermes::vm::HiddenClass> >(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&&, hermes::vm::Handle<hermes::vm::HiddenClass>&&) Unexecuted instantiation: hermes::vm::JSRegExpStringIterator* hermes::vm::GCBase::constructCell<hermes::vm::JSRegExpStringIterator, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, bool&, bool&>(void*, unsigned int, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::HiddenClass>&&, hermes::vm::Handle<hermes::vm::JSObject>&, hermes::vm::Handle<hermes::vm::StringPrimitive>&, bool&, bool&) |
1400 | | |
1401 | | /// Number of finalized objects in the last collection. |
1402 | | unsigned numFinalizedObjects_{0}; |
1403 | | |
1404 | | /// The total number of bytes allocated in the execution. |
1405 | | uint64_t totalAllocatedBytes_{0}; |
1406 | | |
1407 | | /// A trace of GC execution. |
1408 | | GCExecTrace execTrace_; |
1409 | | |
1410 | | /// These fields are not available in optimized builds. |
1411 | | #ifndef NDEBUG |
1412 | | /// Number of currently allocated objects present in the heap before the start |
1413 | | /// of the last collection. Some may be unreachable. |
1414 | | unsigned numAllocatedObjects_{0}; |
1415 | | /// Number of reachable objects in the last collection. (More properly, this |
1416 | | /// is the number not known to be unreachable: if a GC does not consider |
1417 | | /// determine the reachability of some subset of objects, for example, an old |
1418 | | /// generation in a generational collection, those objects should be included |
1419 | | /// in this count.) |
1420 | | unsigned numReachableObjects_{0}; |
1421 | | /// Number of collected objects in the last collection. Equal to |
1422 | | /// numAllocatedObjects_ (at the start of the last collection), |
1423 | | /// minus numReachableObjects_ found in that collection. |
1424 | | unsigned numCollectedObjects_{0}; |
1425 | | /// Number of marked symbols. |
1426 | | unsigned numMarkedSymbols_{0}; |
1427 | | /// Number of hidden classes alive after the last collection. |
1428 | | unsigned numHiddenClasses_{0}; |
1429 | | /// Number of "leaf" hidden classes alive after the last collection. |
1430 | | unsigned numLeafHiddenClasses_{0}; |
1431 | | |
1432 | | /// Associate a semi-unique (until it overflows) id with every allocation |
1433 | | /// for easier identification when debugging. |
1434 | | uint64_t debugAllocationCounter_{0}; |
1435 | | #endif |
1436 | | |
1437 | | /// User-supplied callbacks invoked by the GC to query information or perform |
1438 | | /// tasks. |
1439 | | GCCallbacks &gcCallbacks_; |
1440 | | |
1441 | | /// Base of all pointers in compressed pointers implementation. |
1442 | | PointerBase &pointerBase_; |
1443 | | |
1444 | | /// A place to log crash data if a crash is about to occur. |
1445 | | std::shared_ptr<CrashManager> crashMgr_; |
1446 | | |
1447 | | HeapKind heapKind_; |
1448 | | |
1449 | | /// Callback called once for each GC event that wants to be logged. Can be |
1450 | | /// null if no analytics are requested. |
1451 | | std::function<void(const GCAnalyticsEvent &)> analyticsCallback_; |
1452 | | |
1453 | | /// Capture all analytics events to print stats at the end. |
1454 | | std::vector<GCAnalyticsEvent> analyticsEvents_; |
1455 | | |
1456 | | /// Whether to output GC statistics at the end of execution. |
1457 | | bool recordGcStats_{false}; |
1458 | | |
1459 | | /// Whether or not a GC cycle is currently occurring. |
1460 | | bool inGC_{false}; |
1461 | | |
1462 | | /// Whether the GC has OOMed. Currently only useful for understanding crash |
1463 | | /// dumps, particularly when HERMESVM_EXCEPTION_ON_OOM is set. |
1464 | | bool hasOOMed_{false}; |
1465 | | |
1466 | | /// The block of fields below records values of various metrics at |
1467 | | /// the start of execution, so that we can get the values at the end |
1468 | | /// and subtract. The "runtimeWillExecute" method is called at |
1469 | | /// first bytecode execution, but also when executing the bodies of |
1470 | | /// other bundles. We want to record these at the first such call. |
1471 | | /// This field tells whether these values have been recorded yet. |
1472 | | bool execStartTimeRecorded_{false}; |
1473 | | |
1474 | | /// Time at which execution of the Hermes VM began. |
1475 | | std::chrono::time_point<std::chrono::steady_clock> execStartTime_; |
1476 | | std::chrono::microseconds execStartCPUTime_; |
1477 | | /// Number of context switches before execution of the Hermes VM began. |
1478 | | long startNumVoluntaryContextSwitches_{0}; |
1479 | | long startNumInvoluntaryContextSwitches_{0}; |
1480 | | |
1481 | | // The cumulative GC stats. |
1482 | | CumulativeHeapStats cumStats_; |
1483 | | |
1484 | | /// Name to identify this heap in logs. |
1485 | | std::string name_; |
1486 | | |
1487 | | /// weakSlots_ is a list of all the weak pointers in the system. They are |
1488 | | /// invalidated if they point to an object that is dead, and do not count |
1489 | | /// towards whether an object is live or dead. |
1490 | | /// The state enum of a WeakRefSlot that is not free may be modified |
1491 | | /// concurrently, those values are protected by the weakRefMutex_. |
1492 | | std::deque<WeakRefSlot> weakSlots_; |
1493 | | |
1494 | | /// Pointer to the first free weak reference slot. Free weak refs are chained |
1495 | | /// together in a linked list. |
1496 | | WeakRefSlot *firstFreeWeak_{nullptr}; |
1497 | | |
1498 | | /// Any thread that modifies a WeakRefSlot or a data structure containing |
1499 | | /// WeakRefs that the GC will mark must hold this mutex. The GC will hold this |
1500 | | /// mutex while scanning any weak references. |
1501 | | WeakRefMutex weakRefMutex_; |
1502 | | |
1503 | | /// Tracks what objects need a stable identity for features such as heap |
1504 | | /// snapshots and the memory profiler. |
1505 | | IDTracker idTracker_; |
1506 | | |
1507 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
1508 | | /// Attaches stack-traces to objects when enabled. |
1509 | | AllocationLocationTracker allocationLocationTracker_; |
1510 | | |
1511 | | /// Attaches stack-traces to objects when enabled. |
1512 | | SamplingAllocationLocationTracker samplingAllocationTracker_; |
1513 | | #endif |
1514 | | |
1515 | | #ifndef NDEBUG |
1516 | | /// The number of reasons why no allocation is allowed in this heap right |
1517 | | /// now. |
1518 | | uint32_t noAllocLevel_{0}; |
1519 | | |
1520 | | friend class NoAllocScope; |
1521 | | #endif |
1522 | | |
1523 | | #ifdef HERMESVM_SANITIZE_HANDLES |
1524 | | /// \return true if we should run handle sanitization and the coin flip with |
1525 | | /// probability sanitizeRate_ has passed. |
1526 | | bool shouldSanitizeHandles(); |
1527 | | |
1528 | | /// Whether to keep moving the heap around to detect unsanitary GC handles. |
1529 | | double sanitizeRate_{1.0}; |
1530 | | |
1531 | | /// PRNG for sanitizing at a less than 1.0 rate. |
1532 | | std::minstd_rand randomEngine_; |
1533 | | #else |
1534 | | /// Sanitize handles is completely disabled (and ignored at runtime) without |
1535 | | /// a special build mode. |
1536 | | static constexpr double sanitizeRate_{0.0}; |
1537 | | |
1538 | 0 | static constexpr bool shouldSanitizeHandles() { |
1539 | 0 | return false; |
1540 | 0 | } |
1541 | | #endif |
1542 | | |
1543 | | private: |
1544 | | #ifdef HERMESVM_GC_RUNTIME |
1545 | | /// Use the kind tag of the GC to statically call a function with one of the |
1546 | | /// available runtime GCs. |
1547 | | template <typename Func> |
1548 | | auto runtimeGCDispatch(Func f) { |
1549 | | switch (getKind()) { |
1550 | | #define GC_KIND(kind) \ |
1551 | | case GCBase::HeapKind::kind: \ |
1552 | | return f(llvh::cast<kind>(this)); |
1553 | | RUNTIME_GC_KINDS |
1554 | | #undef GC_KIND |
1555 | | default: |
1556 | | llvm_unreachable("No other valid GC for RuntimeGC"); |
1557 | | } |
1558 | | } |
1559 | | #endif |
1560 | | |
1561 | | template <typename T, XorPtrKeyID K> |
1562 | | friend class XorPtr; |
1563 | | |
1564 | | /// Randomly generated key used to obfuscate pointers in XorPtr. |
1565 | | uintptr_t pointerEncryptionKey_[XorPtrKeyID::_NumKeys]; |
1566 | | |
1567 | | /// Callback called if it's not null when the Live Data Tripwire is |
1568 | | /// triggered. |
1569 | | std::function<void(GCTripwireContext &)> tripwireCallback_; |
1570 | | |
1571 | | /// Maximum size limit before the heap size tripwire will trigger. |
1572 | | gcheapsize_t tripwireLimit_; |
1573 | | |
1574 | | /// True if the tripwire has already been called on this heap. |
1575 | | bool tripwireCalled_{false}; |
1576 | | }; |
1577 | | |
1578 | | // Utilities for formatting time durations and memory sizes. |
1579 | | |
1580 | | /// An object that, written to an ostream, formats the given # of |
1581 | | /// secs in appropriate units (down to microseconds). |
1582 | | struct DurationFormatObj { |
1583 | | double secs; |
1584 | | }; |
1585 | | llvh::raw_ostream &operator<<( |
1586 | | llvh::raw_ostream &os, |
1587 | | const DurationFormatObj &dfo); |
1588 | 0 | inline DurationFormatObj formatSecs(double secs) { |
1589 | 0 | return {secs}; |
1590 | 0 | } |
1591 | | |
1592 | | /// An object that, written to an ostream, formats the given # of |
1593 | | /// bytes in appropriate units (bytes to GiB). |
1594 | | struct SizeFormatObj { |
1595 | | uint64_t bytes; |
1596 | | }; |
1597 | | llvh::raw_ostream &operator<<(llvh::raw_ostream &os, const SizeFormatObj &sfo); |
1598 | 0 | inline SizeFormatObj formatSize(uint64_t size) { |
1599 | 0 | return {size}; |
1600 | 0 | } |
1601 | | |
1602 | | } // namespace vm |
1603 | | } // namespace hermes |
1604 | | #pragma GCC diagnostic pop |
1605 | | |
1606 | | #endif // HERMES_VM_GCBASE_H |