2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef HPHP_HEAP_SCAN_H
17 #define HPHP_HEAP_SCAN_H
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/heap-graph.h"
21 #include "hphp/runtime/base/memory-manager.h"
22 #include "hphp/runtime/base/mixed-array.h"
23 #include "hphp/runtime/base/mixed-array-defs.h"
24 #include "hphp/runtime/base/object-data.h"
25 #include "hphp/runtime/base/packed-array.h"
26 #include "hphp/runtime/base/packed-array-defs.h"
27 #include "hphp/runtime/base/rds-header.h"
28 #include "hphp/runtime/base/req-root.h"
29 #include "hphp/runtime/base/resource-data.h"
30 #include "hphp/runtime/base/string-data.h"
31 #include "hphp/runtime/base/request-info.h"
33 #include "hphp/runtime/ext/asio/asio-external-thread-event-queue.h"
34 #include "hphp/runtime/ext/asio/ext_async-function-wait-handle.h"
35 #include "hphp/runtime/ext/asio/ext_async-generator.h"
36 #include "hphp/runtime/ext/asio/ext_external-thread-event-wait-handle.h"
37 #include "hphp/runtime/ext/asio/ext_reschedule-wait-handle.h"
38 #include "hphp/runtime/ext/asio/ext_resumable-wait-handle.h"
39 #include "hphp/runtime/ext/asio/ext_sleep-wait-handle.h"
40 #include "hphp/runtime/ext/extension-registry.h"
41 #include "hphp/runtime/ext/generator/ext_generator.h"
43 #include "hphp/runtime/server/server-note.h"
45 #include "hphp/runtime/vm/named-entity.h"
46 #include "hphp/runtime/vm/named-entity-defs.h"
47 #include "hphp/runtime/vm/runtime.h"
49 #include "hphp/util/hphp-config.h"
51 #include "hphp/util/rds-local.h"
52 #include "hphp/util/type-scan.h"
56 inline void scanFrameSlots(const ActRec
* ar
, type_scan::Scanner
& scanner
) {
57 // layout: [iters][locals][ActRec]
59 auto num_locals
= ar
->func()->numLocals();
60 auto locals
= frame_local(ar
, num_locals
- 1);
61 scanner
.scan(*locals
, num_locals
* sizeof(TypedValue
));
62 auto num_iters
= ar
->func()->numIterators();
63 auto iters
= frame_iter(ar
, num_iters
- 1);
64 scanner
.scan(*iters
, num_iters
* sizeof(Iter
));
67 inline void scanNative(const NativeNode
* node
, type_scan::Scanner
& scanner
) {
68 auto obj
= Native::obj(node
);
69 auto ndi
= obj
->getVMClass()->getNativeDataInfo();
70 auto data
= (const char*)obj
- ndi
->sz
;
71 scanner
.scanByIndex(node
->typeIndex(), data
, ndi
->sz
);
72 if (auto off
= node
->arOff()) {
73 scanFrameSlots((const ActRec
*)((const char*)node
+ off
), scanner
);
77 inline void scanAFWH(const c_Awaitable
* wh
, type_scan::Scanner
& scanner
) {
78 assertx(!wh
->hasNativeData());
79 // scan ResumableHeader before object
80 auto r
= Resumable::FromObj(wh
);
81 if (!wh
->isFinished()) {
82 scanFrameSlots(r
->actRec(), scanner
);
85 return wh
->scan(scanner
);
88 inline void scanMemoSlots(const ObjectData
* obj
,
89 type_scan::Scanner
& scanner
,
91 auto const cls
= obj
->getVMClass();
92 assertx(cls
->hasMemoSlots());
94 if (!obj
->getAttribute(ObjectData::UsedMemoCache
)) return;
96 auto const numSlots
= cls
->numMemoSlots();
98 for (Slot i
= 0; i
< numSlots
; ++i
) scanner
.scan(*obj
->memoSlot(i
));
100 auto const ndi
= cls
->getNativeDataInfo();
101 for (Slot i
= 0; i
< numSlots
; ++i
) {
102 scanner
.scan(*obj
->memoSlotNativeData(i
, ndi
->sz
));
107 inline void scanHeapObject(const HeapObject
* h
, type_scan::Scanner
& scanner
) {
109 case HeaderKind::Packed
:
110 case HeaderKind::Vec
:
111 return PackedArray::scan(static_cast<const ArrayData
*>(h
), scanner
);
112 case HeaderKind::Mixed
:
113 case HeaderKind::Dict
:
114 return static_cast<const MixedArray
*>(h
)->scan(scanner
);
115 case HeaderKind::Keyset
:
116 return static_cast<const SetArray
*>(h
)->scan(scanner
);
117 case HeaderKind::BespokeVArray
:
118 case HeaderKind::BespokeDArray
:
119 case HeaderKind::BespokeDict
:
120 case HeaderKind::BespokeVec
:
121 case HeaderKind::BespokeKeyset
:
122 return static_cast<const BespokeArray
*>(h
)->scan(scanner
);
123 case HeaderKind::Closure
:
124 scanner
.scan(*static_cast<const c_Closure
*>(h
)->hdr());
125 return static_cast<const c_Closure
*>(h
)->scan(scanner
);
126 case HeaderKind::Object
:
127 // NativeObject should hit the NativeData case below.
128 return static_cast<const ObjectData
*>(h
)->scan(scanner
);
129 case HeaderKind::WaitHandle
:
130 // scan C++ properties after [ObjectData] header. should pick up
131 // unioned and bit-packed fields
132 return static_cast<const c_Awaitable
*>(h
)->scan(scanner
);
133 case HeaderKind::AwaitAllWH
:
134 // scan C++ properties after [ObjectData] header. should pick up
135 // unioned and bit-packed fields
136 return static_cast<const c_AwaitAllWaitHandle
*>(h
)->scan(scanner
);
137 case HeaderKind::AsyncFuncWH
:
138 return scanAFWH(static_cast<const c_Awaitable
*>(h
), scanner
);
139 case HeaderKind::NativeData
: {
140 auto native
= static_cast<const NativeNode
*>(h
);
141 scanNative(native
, scanner
);
142 auto const obj
= Native::obj(native
);
143 if (UNLIKELY(obj
->getVMClass()->hasMemoSlots())) {
144 scanMemoSlots(obj
, scanner
, true);
146 return obj
->scan(scanner
);
148 case HeaderKind::AsyncFuncFrame
:
149 return scanAFWH(asyncFuncWH(h
), scanner
);
150 case HeaderKind::ClosureHdr
:
151 scanner
.scan(*static_cast<const ClosureHdr
*>(h
));
152 return closureObj(h
)->scan(scanner
);
153 case HeaderKind::MemoData
: {
154 auto const obj
= memoObj(h
);
155 scanMemoSlots(obj
, scanner
, false);
156 return obj
->scan(scanner
);
158 case HeaderKind::Pair
:
159 return static_cast<const c_Pair
*>(h
)->scan(scanner
);
160 case HeaderKind::Vector
:
161 case HeaderKind::ImmVector
:
162 return static_cast<const BaseVector
*>(h
)->scan(scanner
);
163 case HeaderKind::Map
:
164 case HeaderKind::ImmMap
:
165 case HeaderKind::Set
:
166 case HeaderKind::ImmSet
:
167 return static_cast<const HashCollection
*>(h
)->scan(scanner
);
168 case HeaderKind::Resource
: {
169 auto res
= static_cast<const ResourceHdr
*>(h
);
170 return scanner
.scanByIndex(res
->typeIndex(), res
->data(),
171 res
->heapSize() - sizeof(ResourceHdr
));
173 case HeaderKind::ClsMeth
:
174 // ClsMeth only holds pointers to non-request allocated data
176 case HeaderKind::RClsMeth
: {
177 auto const rclsmeth
= static_cast<const RClsMethData
*>(h
);
178 return PackedArray::scan(rclsmeth
->m_arr
, scanner
);
180 case HeaderKind::Record
:
181 return static_cast<const RecordData
*>(h
)->scan(scanner
);
182 case HeaderKind::RFunc
: {
183 auto const rfunc
= static_cast<const RFuncData
*>(h
);
184 return PackedArray::scan(rfunc
->m_arr
, scanner
);
186 case HeaderKind::Cpp
:
187 case HeaderKind::SmallMalloc
:
188 case HeaderKind::BigMalloc
: {
189 auto n
= static_cast<const MallocNode
*>(h
);
190 return scanner
.scanByIndex(n
->typeIndex(), n
+ 1,
191 n
->nbytes
- sizeof(MallocNode
));
193 case HeaderKind::String
:
194 case HeaderKind::Free
:
195 case HeaderKind::Hole
:
196 // these don't have pointers. some clients might generically
197 // scan them even if they aren't interesting.
199 case HeaderKind::NativeObject
:
200 // should have scanned the NativeData header.
202 case HeaderKind::Slab
:
203 // these aren't legitimate headers, and heap iteration should skip them.
206 always_assert(false && "corrupt header in worklist");
209 inline void c_AwaitAllWaitHandle::scan(type_scan::Scanner
& scanner
) const {
210 scanner
.scanByIndex(m_tyindex
, this, heapSize());
211 ObjectData::scan(scanner
); // in case of dynprops
214 inline void c_Awaitable::scan(type_scan::Scanner
& scanner
) const {
215 assertx(kind() != HeaderKind::AwaitAllWH
);
217 kind() == HeaderKind::AsyncFuncWH
? sizeof(c_AsyncFunctionWaitHandle
) :
218 asio_object_size(this);
219 scanner
.scanByIndex(m_tyindex
, this, size
);
220 ObjectData::scan(scanner
);
223 inline void RecordBase::scan(type_scan::Scanner
& scanner
) const {
224 auto fields
= fieldVec();
225 scanner
.scan(*fields
, m_record
->numFields() * sizeof(*fields
));
228 inline void RecordData::scan(type_scan::Scanner
& scanner
) const {
229 RecordBase::scan(scanner
);
232 inline void ObjectData::scan(type_scan::Scanner
& scanner
) const {
233 props()->scan(m_cls
->countablePropsEnd(), scanner
);
234 if (getAttribute(HasDynPropArr
)) {
235 // nb: dynamic property arrays are in ExecutionContext::dynPropTable,
236 // which is not marked as a root. Scan the entry pair, so both the key
237 // and value are scanned.
238 auto iter
= g_context
->dynPropTable
.find(this);
239 scanner
.scan(*iter
); // *iter is pair<ObjectData*,ArrayNoDtor>
243 // [<-stack[iters[locals[params[ActRec[stack[iters[locals[ActRec...
244 // ^m_top ^fp ^firstAR
246 // +-----------------------+
247 // top -> | current eval stack |
248 // +-----------------------+
250 // +-----------------------+
251 // | locals, params |
252 // +-----------------------+
253 // fp -> | current ActRec |
254 // +-----------------------+
255 // | caller's eval stack |
256 // +-----------------------+
258 // +-----------------------+
259 // firstAR -> | ActRec |
260 // +-----------------------+
262 // fp{sfp}... forms the true execution stack, but it chains
263 // in and out of resumables. sp always points into the vm stack section.
264 // locals/iterators/actrec are missing in some stack frames.
267 // m_sfp prev ActRec*
268 // m_savedRIP return addr
270 // m_callOffAndFlags caller's vmpc
272 // m_this|m_cls ObjectData* or Class*
273 // m_varenv|extraArgs
275 // for reference, see vm/unwind.cpp and visitStackElms() in bytecode.h
277 // Descriptive wrapper types to annotate root nodes
278 struct PhpStack
{ TYPE_SCAN_CONSERVATIVE_ALL
; void* dummy
; };
279 struct CppStack
{ TYPE_SCAN_CONSERVATIVE_ALL
; void* dummy
; };
282 void MemoryManager::iterateRoots(Fn fn
) const {
283 fn(&m_sweepables
, sizeof(m_sweepables
),
284 type_scan::getIndexForScan
<SweepableList
>());
285 for (auto& node
: m_natives
) {
286 fn(&node
, sizeof(node
), type_scan::getIndexForScan
<NativeNode
*>());
288 for (const auto root
: m_root_handles
) {
293 template<class Fn
> void ThreadLocalManager::iterate(Fn fn
) const {
294 auto list
= getList(pthread_getspecific(m_key
));
296 for (auto p
= list
->head
; p
!= nullptr;) {
297 auto node
= static_cast<ThreadLocalNode
<void>*>(p
);
299 fn(node
->m_p
, node
->m_size
, node
->m_tyindex
);
305 // Visit request-local roots. Each invocation of fn represents one root
306 // instance of a given type and size, containing potentially several
308 template<class Fn
> void iterateConservativeRoots(Fn fn
) {
309 auto rds
= rds::header();
311 // php header and stack
312 // TODO #6509338 exactly scan the php stack.
314 fn(rds
, sizeof(*rds
), type_scan::getIndexForScan
<rds::Header
>());
315 auto stack_end
= rds
->vmRegs
.stack
.getStackHighAddress();
316 auto sp
= rds
->vmRegs
.stack
.top();
317 fn(sp
, uintptr_t(stack_end
) - uintptr_t(sp
),
318 type_scan::getIndexForScan
<PhpStack
>());
321 if (!g_context
.isNull()) {
322 // m_nestedVMs contains MInstrState, which has a conservatively-scanned
323 // fields. Scan it now, then ignore when ExecutionContext is scanned.
324 fn(&g_context
->m_nestedVMs
, sizeof(g_context
->m_nestedVMs
),
325 type_scan::getIndexForScan
<ExecutionContext::VMStateVec
>());
328 // cpp stack. ensure stack contains callee-saved registers.
329 CALLEE_SAVED_BARRIER();
330 auto sp
= stack_top_ptr_conservative();
331 fn(sp
, s_stackLimit
+ s_stackSize
- uintptr_t(sp
),
332 type_scan::getIndexForScan
<CppStack
>());
335 template<class Fn
> void iterateExactRoots(Fn fn
) {
336 auto rds
= rds::header();
340 rds::forEachNormalAlloc(fn
);
343 // Local section (mainly static properties).
344 // static properties have a per-class, versioned, bool in rds::Normal,
345 // tracked by Class::m_sPropCacheInit, plus one TypedValue in rds::Local
346 // for each property, tracked in Class::m_sPropCache. Just scan the
347 // properties in rds::Local. We ignore the state of the bool, because it
348 // is not initialized until after all sprops are initialized, and it's
349 // necessary to scan static properties *during* initialization.
351 rds::forEachLocalAlloc(fn
);
354 rds::local::iterateRoots(fn
);
356 // Root handles & sweep lists
357 tl_heap
->iterateRoots(fn
);
359 // ThreadLocal nodes (but skip MemoryManager).
360 ThreadLocalManager::GetManager().iterate(fn
);
363 template<class Fn
> void iterateRoots(Fn fn
) {
364 iterateConservativeRoots(fn
);
365 iterateExactRoots(fn
);