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