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