PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/base/heap-scan.h

https://gitlab.com/alvinahmadov2/hhvm
C Header | 385 lines | 281 code | 21 blank | 83 comment | 46 complexity | 18a926ef0913f381435cd356c9747800 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2015 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. +----------------------------------------------------------------------+
  15. */
  16. #ifndef HPHP_HEAP_SCAN_H
  17. #define HPHP_HEAP_SCAN_H
  18. #include "hphp/runtime/base/string-data.h"
  19. #include "hphp/runtime/base/array-data.h"
  20. #include "hphp/runtime/base/object-data.h"
  21. #include "hphp/runtime/base/resource-data.h"
  22. #include "hphp/runtime/base/ref-data.h"
  23. #include "hphp/runtime/base/proxy-array.h"
  24. #include "hphp/runtime/base/packed-array.h"
  25. #include "hphp/runtime/base/packed-array-defs.h"
  26. #include "hphp/runtime/base/mixed-array.h"
  27. #include "hphp/runtime/base/mixed-array-defs.h"
  28. #include "hphp/runtime/base/apc-local-array.h"
  29. #include "hphp/runtime/base/apc-local-array-defs.h"
  30. #include "hphp/runtime/base/thread-info.h"
  31. #include "hphp/runtime/vm/globals-array.h"
  32. #include "hphp/runtime/base/rds-header.h"
  33. #include "hphp/runtime/base/imarker.h"
  34. #include "hphp/runtime/base/memory-manager.h"
  35. #include "hphp/runtime/ext/extension-registry.h"
  36. #include "hphp/runtime/base/request-event-handler.h"
  37. #include "hphp/runtime/server/server-note.h"
  38. #include "hphp/runtime/ext/asio/ext_sleep-wait-handle.h"
  39. #include "hphp/runtime/ext/asio/asio-external-thread-event-queue.h"
  40. #include "hphp/runtime/ext/asio/ext_reschedule-wait-handle.h"
  41. #include "hphp/runtime/ext/asio/ext_external-thread-event-wait-handle.h"
  42. #include "hphp/runtime/ext/asio/ext_resumable-wait-handle.h"
  43. #ifdef ENABLE_ZEND_COMPAT
  44. #include "hphp/runtime/ext_zend_compat/php-src/TSRM/TSRM.h"
  45. #endif
  46. namespace HPHP {
  47. template<class F> void scanHeader(const Header* h, F& mark) {
  48. switch (h->kind()) {
  49. case HeaderKind::Proxy:
  50. return h->proxy_.scan(mark);
  51. case HeaderKind::Empty:
  52. return;
  53. case HeaderKind::Packed:
  54. return PackedArray::scan(&h->arr_, mark);
  55. case HeaderKind::Struct:
  56. return h->struct_.scan(mark);
  57. case HeaderKind::Mixed:
  58. return h->mixed_.scan(mark);
  59. case HeaderKind::Apc:
  60. return h->apc_.scan(mark);
  61. case HeaderKind::Globals:
  62. return h->globals_.scan(mark);
  63. case HeaderKind::Object:
  64. case HeaderKind::WaitHandle:
  65. case HeaderKind::ResumableObj:
  66. case HeaderKind::AwaitAllWH:
  67. case HeaderKind::Vector:
  68. case HeaderKind::Map:
  69. case HeaderKind::Set:
  70. case HeaderKind::Pair:
  71. case HeaderKind::ImmVector:
  72. case HeaderKind::ImmMap:
  73. case HeaderKind::ImmSet:
  74. return h->obj_.scan(mark);
  75. case HeaderKind::Resource:
  76. return h->res_.data()->scan(mark);
  77. case HeaderKind::Ref:
  78. return h->ref_.scan(mark);
  79. case HeaderKind::SmallMalloc:
  80. return mark((&h->small_)+1, h->small_.padbytes - sizeof(SmallNode));
  81. case HeaderKind::BigMalloc:
  82. return mark((&h->big_)+1, h->big_.nbytes - sizeof(BigNode));
  83. case HeaderKind::NativeData:
  84. return h->nativeObj()->scan(mark);
  85. case HeaderKind::ResumableFrame:
  86. return h->resumableObj()->scan(mark);
  87. case HeaderKind::String:
  88. case HeaderKind::Free:
  89. // these don't have pointers. some clients might generically
  90. // scan them even if they aren't interesting.
  91. return;
  92. case HeaderKind::BigObj:
  93. case HeaderKind::Hole:
  94. // these aren't legitimate headers, and heap iteration should skip them.
  95. break;
  96. }
  97. always_assert(false && "corrupt header in worklist");
  98. }
  99. template<class F> void ObjectData::scan(F& mark) const {
  100. if (m_hdr.kind == HeaderKind::ResumableObj) {
  101. // scan the frame locals, iterators, and Resumable
  102. auto r = Resumable::FromObj(this);
  103. auto frame = reinterpret_cast<const TypedValue*>(r) -
  104. r->actRec()->func()->numSlotsInFrame();
  105. mark(frame, uintptr_t(this) - uintptr_t(frame));
  106. assert(!getVMClass()->getNativeDataInfo());
  107. } else if (m_hdr.kind == HeaderKind::WaitHandle) {
  108. // scan C++ properties after [ObjectData] header
  109. mark(this + 1, asio_object_size(this) - sizeof(*this));
  110. assert(!getVMClass()->getNativeDataInfo());
  111. } else if (m_hdr.kind == HeaderKind::AwaitAllWH) {
  112. auto wh = static_cast<const c_AwaitAllWaitHandle*>(this);
  113. wh->scanChildren(mark);
  114. assert(!getVMClass()->getNativeDataInfo());
  115. } else if (getAttribute(HasNativeData)) {
  116. // [NativeNode][NativeData][ObjectData][props]
  117. Native::nativeDataScan(this, mark);
  118. }
  119. auto props = propVec();
  120. if (getAttribute(IsCppBuiltin)) {
  121. // [ObjectData][C++ fields][props]
  122. // TODO t6169228 virtual call for exact marking
  123. mark(this + 1, uintptr_t(props) - uintptr_t(this + 1));
  124. }
  125. for (size_t i = 0, n = m_cls->numDeclProperties(); i < n; ++i) {
  126. mark(props[i]);
  127. }
  128. if (getAttribute(HasDynPropArr)) {
  129. // nb: dynamic property arrays are in ExecutionContext::dynPropTable,
  130. // which is not marked as a root. Mark the array when scanning the object.
  131. mark.implicit(g_context->dynPropTable[this].arr());
  132. }
  133. }
  134. template<class F> void ResourceData::scan(F& mark) const {
  135. ExtMarker<F> bridge(mark);
  136. vscan(bridge);
  137. }
  138. template<class F> void RequestEventHandler::scan(F& mark) const {
  139. ExtMarker<F> bridge(mark);
  140. vscan(bridge);
  141. }
  142. template<class F> void scan_ezc_resources(F& mark) {
  143. #ifdef ENABLE_ZEND_COMPAT
  144. ExtMarker<F> bridge(mark);
  145. ts_scan_resources(bridge);
  146. #endif
  147. }
  148. template<class F> void ExtendedException::scan(F& mark) const {
  149. ExtMarker<F> bridge(mark);
  150. vscan(bridge);
  151. }
  152. // [<-stack[iters[locals[params[ActRec[stack[iters[locals[ActRec...
  153. // ^m_top ^fp ^firstAR
  154. //
  155. // +-----------------------+
  156. // top -> | current eval stack |
  157. // +-----------------------+
  158. // | iterators |
  159. // +-----------------------+
  160. // | locals, params |
  161. // +-----------------------+
  162. // fp -> | current ActRec |
  163. // +-----------------------+
  164. // | caller's eval stack |
  165. // +-----------------------+
  166. // ...
  167. // +-----------------------+
  168. // firstAR -> | ActRec |
  169. // +-----------------------+
  170. //
  171. // fp{sfp}... forms the true execution stack, but it chains
  172. // in and out of resumables. sp always points into the vm stack section.
  173. // locals/iterators/actrec are missing in some stack frames.
  174. //
  175. // ActRec:
  176. // m_sfp prev ActRec*
  177. // m_savedRIP return addr
  178. // m_func Func*
  179. // m_soff return vmpc
  180. // m_argc_flags
  181. // m_this|m_cls ObjectData* or Class*
  182. // m_varenv|extraArgs
  183. //
  184. // for reference, see vm/unwind.cpp and visitStackElms() in bytecode.h
  185. // scan the whole rds, including the php stack and the rds segment.
  186. // * stack: should be exact-scannable.
  187. // * rds threadlocal part: conservative scan until we teach the
  188. // jit to tell us where the pointers live.
  189. // * rds shared part: should not contain heap pointers!
  190. template<class F> void scanRds(F& mark, rds::Header* rds) {
  191. // rds sections
  192. auto markSection = [&](folly::Range<const char*> r) {
  193. mark(r.begin(), r.size());
  194. };
  195. mark.where("rds-normal");
  196. markSection(rds::normalSection());
  197. mark.where("rds-local");
  198. markSection(rds::localSection());
  199. mark.where("rds-persistent");
  200. markSection(rds::persistentSection());
  201. // php stack TODO #6509338 exactly scan the php stack.
  202. mark.where("php-stack");
  203. auto stack_end = (TypedValue*)rds->vmRegs.stack.getStackHighAddress();
  204. auto sp = rds->vmRegs.stack.top();
  205. mark(sp, (stack_end - sp) * sizeof(*sp));
  206. }
  207. template<class F>
  208. void MemoryManager::scanSweepLists(F& mark) const {
  209. for (auto s = m_sweepables.next(); s != &m_sweepables; s = s->next()) {
  210. if (auto h = static_cast<Header*>(s->owner())) {
  211. assert(h->kind() == HeaderKind::Resource || isObjectKind(h->kind()));
  212. if (isObjectKind(h->kind())) {
  213. mark.implicit(&h->obj_);
  214. } else {
  215. mark.implicit(&h->res_);
  216. }
  217. }
  218. }
  219. for (auto node: m_natives) {
  220. mark.implicit(Native::obj(node));
  221. }
  222. }
  223. template <typename F>
  224. void MemoryManager::scanRootMaps(F& m) const {
  225. if (m_objectRoots) {
  226. for(const auto& root : *m_objectRoots) {
  227. scan(root.second, m);
  228. }
  229. }
  230. if (m_resourceRoots) {
  231. for(const auto& root : *m_resourceRoots) {
  232. scan(root.second, m);
  233. }
  234. }
  235. for (const auto& root : m_exceptionRoots) {
  236. root->scan(m);
  237. }
  238. }
  239. template<class F>
  240. void ThreadLocalManager::scan(F& mark) const {
  241. auto list = getList(pthread_getspecific(m_key));
  242. if (!list) return;
  243. for (auto p = list->head; p != nullptr;) {
  244. auto node = static_cast<ThreadLocalNode<void>*>(p);
  245. if (node->m_p) mark(node->m_p, node->m_size);
  246. p = node->m_next;
  247. }
  248. }
  249. // Scan request-local roots
  250. template<class F> void scanRoots(F& mark) {
  251. // rds, including php stack
  252. if (auto rds = rds::header()) {
  253. scanRds(mark, rds);
  254. }
  255. // ExecutionContext
  256. if (!g_context.isNull()) {
  257. mark.where("g_context");
  258. g_context->scan(mark);
  259. }
  260. // ThreadInfo
  261. mark.where("ThreadInfo");
  262. if (!ThreadInfo::s_threadInfo.isNull()) {
  263. TI().scan(mark);
  264. }
  265. // C++ stack
  266. mark.where("cpp-stack");
  267. CALLEE_SAVED_BARRIER(); // ensure stack contains callee-saved registers
  268. auto sp = stack_top_ptr();
  269. mark(sp, s_stackLimit + s_stackSize - uintptr_t(sp));
  270. // C++ threadlocal data, but don't scan MemoryManager
  271. mark.where("cpp-tdata");
  272. auto tdata = getCppTdata(); // tdata = { ptr, size }
  273. if (tdata.second > 0) {
  274. auto tm = (char*)tdata.first;
  275. auto tm_end = tm + tdata.second;
  276. auto mm = (char*)&MM();
  277. auto mm_end = mm + sizeof(MemoryManager);
  278. assert(mm >= tm && mm_end <= tm_end);
  279. mark(tm, mm - tm);
  280. mark(mm_end, tm_end - mm_end);
  281. }
  282. // ThreadLocal nodes
  283. mark.where("ThreadLocalManager");
  284. ThreadLocalManager::GetManager().scan(mark);
  285. // Extension thread locals
  286. mark.where("extensions");
  287. ExtMarker<F> xm(mark);
  288. ExtensionRegistry::scanExtensions(xm);
  289. // Root maps
  290. mark.where("rootmaps");
  291. MM().scanRootMaps(mark);
  292. // treat sweep lists as roots until we are ready to test what happens
  293. // when we start calling various sweep() functions early.
  294. mark.where("sweeplists");
  295. MM().scanSweepLists(mark);
  296. // these have rogue thread_local stuff
  297. if (auto asio = AsioSession::Get()) {
  298. mark.where("AsioSession");
  299. asio->scan(mark);
  300. }
  301. mark.where("get_server_note");
  302. get_server_note()->scan(mark);
  303. mark.where("ezc resources");
  304. scan_ezc_resources(mark);
  305. }
  306. template <typename T, typename F>
  307. void scan(const req::ptr<T>& ptr, F& mark) {
  308. ptr->scan(mark);
  309. }
  310. // information about heap objects, indexed by valid object starts.
  311. struct PtrMap {
  312. void insert(const Header* h) {
  313. assert(!sorted_);
  314. regions_.emplace_back(h, h->size());
  315. }
  316. const Header* header(const void* p) const {
  317. assert(sorted_);
  318. // Find the first region which begins beyond p.
  319. auto it =
  320. std::upper_bound(
  321. regions_.begin(),
  322. regions_.end(),
  323. p,
  324. [](const void* p,
  325. const std::pair<const Header*, std::size_t>& region) {
  326. return p < region.first;
  327. }
  328. );
  329. // If its the first region, p is before any region, so there's no
  330. // header. Otherwise, backup to the previous region.
  331. if (it == regions_.begin()) return nullptr;
  332. --it;
  333. // p can only potentially point within this previous region, so check that.
  334. return (uintptr_t(p) < uintptr_t(it->first) + it->second) ?
  335. it->first : nullptr;
  336. }
  337. bool isHeader(const void* p) const {
  338. auto h = header(p);
  339. return h && h == p;
  340. }
  341. void prepare() {
  342. assert(!sorted_);
  343. std::sort(regions_.begin(), regions_.end());
  344. assert(sanityCheck());
  345. sorted_ = true;
  346. }
  347. private:
  348. bool sanityCheck() const {
  349. // Verify that all the regions are in increasing and non-overlapping order.
  350. void* last = nullptr;
  351. for (const auto& region : regions_) {
  352. if (!last || last <= region.first) {
  353. last = (void*)(uintptr_t(region.first) + region.second);
  354. } else {
  355. return false;
  356. }
  357. }
  358. return true;
  359. }
  360. std::vector<std::pair<const Header*, std::size_t>> regions_;
  361. bool sorted_ = false;
  362. };
  363. }
  364. #endif