memory_region_map.cc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
  2. /* Copyright (c) 2006, Google Inc.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following disclaimer
  13. * in the documentation and/or other materials provided with the
  14. * distribution.
  15. * * Neither the name of Google Inc. nor the names of its
  16. * contributors may be used to endorse or promote products derived from
  17. * this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. * ---
  32. * Author: Maxim Lifantsev
  33. */
  34. //
  35. // Background and key design points of MemoryRegionMap.
  36. //
  37. // MemoryRegionMap is a low-level module with quite atypical requirements that
  38. // result in some degree of non-triviality of the implementation and design.
  39. //
  40. // MemoryRegionMap collects info about *all* memory regions created with
  41. // mmap, munmap, mremap, sbrk.
  42. // They key word above is 'all': all that are happening in a process
  43. // during its lifetime frequently starting even before global object
  44. // constructor execution.
  45. //
  46. // This is needed by the primary client of MemoryRegionMap:
  47. // HeapLeakChecker uses the regions and the associated stack traces
  48. // to figure out what part of the memory is the heap:
  49. // if MemoryRegionMap were to miss some (early) regions, leak checking would
  50. // stop working correctly.
  51. //
  52. // To accomplish the goal of functioning before/during global object
  53. // constructor execution MemoryRegionMap is done as a singleton service
  54. // that relies on own on-demand initialized static constructor-less data,
  55. // and only relies on other low-level modules that can also function properly
  56. // even before global object constructors run.
  57. //
  58. // Accomplishing the goal of collecting data about all mmap, munmap, mremap,
  59. // sbrk occurrences is a more involved: conceptually to do this one needs to
  60. // record some bits of data in particular about any mmap or sbrk call,
  61. // but to do that one needs to allocate memory for that data at some point,
  62. // but all memory allocations in the end themselves come from an mmap
  63. // or sbrk call (that's how the address space of the process grows).
  64. //
  65. // Also note that we need to do all the above recording from
  66. // within an mmap/sbrk hook which is sometimes/frequently is made by a memory
  67. // allocator, including the allocator MemoryRegionMap itself must rely on.
  68. // In the case of heap-checker usage this includes even the very first
  69. // mmap/sbrk call happening in the program: heap-checker gets activated due to
  70. // a link-time installed mmap/sbrk hook and it initializes MemoryRegionMap
  71. // and asks it to record info about this very first call right from that
  72. // very first hook invocation.
  73. //
  74. // MemoryRegionMap is doing its memory allocations via LowLevelAlloc:
  75. // unlike more complex standard memory allocator, LowLevelAlloc cooperates with
  76. // MemoryRegionMap by not holding any of its own locks while it calls mmap
  77. // to get memory, thus we are able to call LowLevelAlloc from
  78. // our mmap/sbrk hooks without causing a deadlock in it.
  79. // For the same reason of deadlock prevention the locking in MemoryRegionMap
  80. // itself is write-recursive which is an exception to Google's mutex usage.
  81. //
  82. // We still need to break the infinite cycle of mmap calling our hook,
  83. // which asks LowLevelAlloc for memory to record this mmap,
  84. // which (sometimes) causes mmap, which calls our hook, and so on.
  85. // We do this as follows: on a recursive call of MemoryRegionMap's
  86. // mmap/sbrk/mremap hook we record the data about the allocation in a
  87. // static fixed-sized stack (saved_regions and saved_buckets), when the
  88. // recursion unwinds but before returning from the outer hook call we unwind
  89. // this stack and move the data from saved_regions and saved_buckets to its
  90. // permanent place in the RegionSet and "bucket_table" respectively,
  91. // which can cause more allocations and mmap-s and recursion and unwinding,
  92. // but the whole process ends eventually due to the fact that for the small
  93. // allocations we are doing LowLevelAlloc reuses one mmap call and parcels out
  94. // the memory it created to satisfy several of our allocation requests.
  95. //
  96. // ========================================================================= //
  97. #include <config.h>
  98. #ifdef HAVE_UNISTD_H
  99. #include <unistd.h>
  100. #endif
  101. #ifdef HAVE_INTTYPES_H
  102. #include <inttypes.h>
  103. #endif
  104. #ifdef HAVE_MMAP
  105. #include <sys/mman.h>
  106. #elif !defined(MAP_FAILED)
  107. #define MAP_FAILED -1 // the only thing we need from mman.h
  108. #endif
  109. #ifdef HAVE_PTHREAD
  110. #include <pthread.h> // for pthread_t, pthread_self()
  111. #endif
  112. #include <stddef.h>
  113. #include <algorithm>
  114. #include <set>
  115. #include "memory_region_map.h"
  116. #include "base/googleinit.h"
  117. #include "base/logging.h"
  118. #include "base/low_level_alloc.h"
  119. #include "malloc_hook-inl.h"
  120. #include <gperftools/stacktrace.h>
  121. #include <gperftools/malloc_hook.h>
  122. // MREMAP_FIXED is a linux extension. How it's used in this file,
  123. // setting it to 0 is equivalent to saying, "This feature isn't
  124. // supported", which is right.
  125. #ifndef MREMAP_FIXED
  126. # define MREMAP_FIXED 0
  127. #endif
  128. using std::max;
  129. // ========================================================================= //
  130. int MemoryRegionMap::client_count_ = 0;
  131. int MemoryRegionMap::max_stack_depth_ = 0;
  132. MemoryRegionMap::RegionSet* MemoryRegionMap::regions_ = NULL;
  133. LowLevelAlloc::Arena* MemoryRegionMap::arena_ = NULL;
  134. SpinLock MemoryRegionMap::lock_(SpinLock::LINKER_INITIALIZED);
  135. SpinLock MemoryRegionMap::owner_lock_( // ACQUIRED_AFTER(lock_)
  136. SpinLock::LINKER_INITIALIZED);
  137. int MemoryRegionMap::recursion_count_ = 0; // GUARDED_BY(owner_lock_)
  138. pthread_t MemoryRegionMap::lock_owner_tid_; // GUARDED_BY(owner_lock_)
  139. int64 MemoryRegionMap::map_size_ = 0;
  140. int64 MemoryRegionMap::unmap_size_ = 0;
  141. HeapProfileBucket** MemoryRegionMap::bucket_table_ = NULL; // GUARDED_BY(lock_)
  142. int MemoryRegionMap::num_buckets_ = 0; // GUARDED_BY(lock_)
  143. int MemoryRegionMap::saved_buckets_count_ = 0; // GUARDED_BY(lock_)
  144. HeapProfileBucket MemoryRegionMap::saved_buckets_[20]; // GUARDED_BY(lock_)
  145. // GUARDED_BY(lock_)
  146. const void* MemoryRegionMap::saved_buckets_keys_[20][kMaxStackDepth];
  147. // ========================================================================= //
  148. // Simple hook into execution of global object constructors,
  149. // so that we do not call pthread_self() when it does not yet work.
  150. static bool libpthread_initialized = false;
  151. REGISTER_MODULE_INITIALIZER(libpthread_initialized_setter,
  152. libpthread_initialized = true);
  153. static inline bool current_thread_is(pthread_t should_be) {
  154. // Before main() runs, there's only one thread, so we're always that thread
  155. if (!libpthread_initialized) return true;
  156. // this starts working only sometime well into global constructor execution:
  157. return pthread_equal(pthread_self(), should_be);
  158. }
  159. // ========================================================================= //
  160. // Constructor-less place-holder to store a RegionSet in.
  161. union MemoryRegionMap::RegionSetRep {
  162. char rep[sizeof(RegionSet)];
  163. void* align_it; // do not need a better alignment for 'rep' than this
  164. RegionSet* region_set() { return reinterpret_cast<RegionSet*>(rep); }
  165. };
  166. // The bytes where MemoryRegionMap::regions_ will point to.
  167. // We use RegionSetRep with noop c-tor so that global construction
  168. // does not interfere.
  169. static MemoryRegionMap::RegionSetRep regions_rep;
  170. // ========================================================================= //
  171. // Has InsertRegionLocked been called recursively
  172. // (or rather should we *not* use regions_ to record a hooked mmap).
  173. static bool recursive_insert = false;
  174. void MemoryRegionMap::Init(int max_stack_depth, bool use_buckets) {
  175. RAW_VLOG(10, "MemoryRegionMap Init");
  176. RAW_CHECK(max_stack_depth >= 0, "");
  177. // Make sure we don't overflow the memory in region stacks:
  178. RAW_CHECK(max_stack_depth <= kMaxStackDepth,
  179. "need to increase kMaxStackDepth?");
  180. Lock();
  181. client_count_ += 1;
  182. max_stack_depth_ = max(max_stack_depth_, max_stack_depth);
  183. if (client_count_ > 1) {
  184. // not first client: already did initialization-proper
  185. Unlock();
  186. RAW_VLOG(10, "MemoryRegionMap Init increment done");
  187. return;
  188. }
  189. // Set our hooks and make sure they were installed:
  190. RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
  191. RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
  192. RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
  193. RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
  194. // We need to set recursive_insert since the NewArena call itself
  195. // will already do some allocations with mmap which our hooks will catch
  196. // recursive_insert allows us to buffer info about these mmap calls.
  197. // Note that Init() can be (and is) sometimes called
  198. // already from within an mmap/sbrk hook.
  199. recursive_insert = true;
  200. arena_ = LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
  201. recursive_insert = false;
  202. HandleSavedRegionsLocked(&InsertRegionLocked); // flush the buffered ones
  203. // Can't instead use HandleSavedRegionsLocked(&DoInsertRegionLocked) before
  204. // recursive_insert = false; as InsertRegionLocked will also construct
  205. // regions_ on demand for us.
  206. if (use_buckets) {
  207. const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
  208. recursive_insert = true;
  209. bucket_table_ = static_cast<HeapProfileBucket**>(
  210. MyAllocator::Allocate(table_bytes));
  211. recursive_insert = false;
  212. memset(bucket_table_, 0, table_bytes);
  213. num_buckets_ = 0;
  214. }
  215. Unlock();
  216. RAW_VLOG(10, "MemoryRegionMap Init done");
  217. }
  218. bool MemoryRegionMap::Shutdown() {
  219. RAW_VLOG(10, "MemoryRegionMap Shutdown");
  220. Lock();
  221. RAW_CHECK(client_count_ > 0, "");
  222. client_count_ -= 1;
  223. if (client_count_ != 0) { // not last client; need not really shutdown
  224. Unlock();
  225. RAW_VLOG(10, "MemoryRegionMap Shutdown decrement done");
  226. return true;
  227. }
  228. if (bucket_table_ != NULL) {
  229. for (int i = 0; i < kHashTableSize; i++) {
  230. for (HeapProfileBucket* curr = bucket_table_[i]; curr != 0; /**/) {
  231. HeapProfileBucket* bucket = curr;
  232. curr = curr->next;
  233. MyAllocator::Free(bucket->stack, 0);
  234. MyAllocator::Free(bucket, 0);
  235. }
  236. }
  237. MyAllocator::Free(bucket_table_, 0);
  238. num_buckets_ = 0;
  239. bucket_table_ = NULL;
  240. }
  241. RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
  242. RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
  243. RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
  244. RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
  245. if (regions_) regions_->~RegionSet();
  246. regions_ = NULL;
  247. bool deleted_arena = LowLevelAlloc::DeleteArena(arena_);
  248. if (deleted_arena) {
  249. arena_ = 0;
  250. } else {
  251. RAW_LOG(WARNING, "Can't delete LowLevelAlloc arena: it's being used");
  252. }
  253. Unlock();
  254. RAW_VLOG(10, "MemoryRegionMap Shutdown done");
  255. return deleted_arena;
  256. }
  257. bool MemoryRegionMap::IsRecordingLocked() {
  258. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  259. return client_count_ > 0;
  260. }
  261. // Invariants (once libpthread_initialized is true):
  262. // * While lock_ is not held, recursion_count_ is 0 (and
  263. // lock_owner_tid_ is the previous owner, but we don't rely on
  264. // that).
  265. // * recursion_count_ and lock_owner_tid_ are only written while
  266. // both lock_ and owner_lock_ are held. They may be read under
  267. // just owner_lock_.
  268. // * At entry and exit of Lock() and Unlock(), the current thread
  269. // owns lock_ iff pthread_equal(lock_owner_tid_, pthread_self())
  270. // && recursion_count_ > 0.
  271. void MemoryRegionMap::Lock() {
  272. {
  273. SpinLockHolder l(&owner_lock_);
  274. if (recursion_count_ > 0 && current_thread_is(lock_owner_tid_)) {
  275. RAW_CHECK(lock_.IsHeld(), "Invariants violated");
  276. recursion_count_++;
  277. RAW_CHECK(recursion_count_ <= 5,
  278. "recursive lock nesting unexpectedly deep");
  279. return;
  280. }
  281. }
  282. lock_.Lock();
  283. {
  284. SpinLockHolder l(&owner_lock_);
  285. RAW_CHECK(recursion_count_ == 0,
  286. "Last Unlock didn't reset recursion_count_");
  287. if (libpthread_initialized)
  288. lock_owner_tid_ = pthread_self();
  289. recursion_count_ = 1;
  290. }
  291. }
  292. void MemoryRegionMap::Unlock() {
  293. SpinLockHolder l(&owner_lock_);
  294. RAW_CHECK(recursion_count_ > 0, "unlock when not held");
  295. RAW_CHECK(lock_.IsHeld(),
  296. "unlock when not held, and recursion_count_ is wrong");
  297. RAW_CHECK(current_thread_is(lock_owner_tid_), "unlock by non-holder");
  298. recursion_count_--;
  299. if (recursion_count_ == 0) {
  300. lock_.Unlock();
  301. }
  302. }
  303. bool MemoryRegionMap::LockIsHeld() {
  304. SpinLockHolder l(&owner_lock_);
  305. return lock_.IsHeld() && current_thread_is(lock_owner_tid_);
  306. }
  307. const MemoryRegionMap::Region*
  308. MemoryRegionMap::DoFindRegionLocked(uintptr_t addr) {
  309. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  310. if (regions_ != NULL) {
  311. Region sample;
  312. sample.SetRegionSetKey(addr);
  313. RegionSet::iterator region = regions_->lower_bound(sample);
  314. if (region != regions_->end()) {
  315. RAW_CHECK(addr <= region->end_addr, "");
  316. if (region->start_addr <= addr && addr < region->end_addr) {
  317. return &(*region);
  318. }
  319. }
  320. }
  321. return NULL;
  322. }
  323. bool MemoryRegionMap::FindRegion(uintptr_t addr, Region* result) {
  324. Lock();
  325. const Region* region = DoFindRegionLocked(addr);
  326. if (region != NULL) *result = *region; // create it as an independent copy
  327. Unlock();
  328. return region != NULL;
  329. }
  330. bool MemoryRegionMap::FindAndMarkStackRegion(uintptr_t stack_top,
  331. Region* result) {
  332. Lock();
  333. const Region* region = DoFindRegionLocked(stack_top);
  334. if (region != NULL) {
  335. RAW_VLOG(10, "Stack at %p is inside region %p..%p",
  336. reinterpret_cast<void*>(stack_top),
  337. reinterpret_cast<void*>(region->start_addr),
  338. reinterpret_cast<void*>(region->end_addr));
  339. const_cast<Region*>(region)->set_is_stack(); // now we know
  340. // cast is safe (set_is_stack does not change the set ordering key)
  341. *result = *region; // create *result as an independent copy
  342. }
  343. Unlock();
  344. return region != NULL;
  345. }
  346. HeapProfileBucket* MemoryRegionMap::GetBucket(int depth,
  347. const void* const key[]) {
  348. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  349. // Make hash-value
  350. uintptr_t hash = 0;
  351. for (int i = 0; i < depth; i++) {
  352. hash += reinterpret_cast<uintptr_t>(key[i]);
  353. hash += hash << 10;
  354. hash ^= hash >> 6;
  355. }
  356. hash += hash << 3;
  357. hash ^= hash >> 11;
  358. // Lookup stack trace in table
  359. unsigned int hash_index = (static_cast<unsigned int>(hash)) % kHashTableSize;
  360. for (HeapProfileBucket* bucket = bucket_table_[hash_index];
  361. bucket != 0;
  362. bucket = bucket->next) {
  363. if ((bucket->hash == hash) && (bucket->depth == depth) &&
  364. std::equal(key, key + depth, bucket->stack)) {
  365. return bucket;
  366. }
  367. }
  368. // Create new bucket
  369. const size_t key_size = sizeof(key[0]) * depth;
  370. HeapProfileBucket* bucket;
  371. if (recursive_insert) { // recursion: save in saved_buckets_
  372. const void** key_copy = saved_buckets_keys_[saved_buckets_count_];
  373. std::copy(key, key + depth, key_copy);
  374. bucket = &saved_buckets_[saved_buckets_count_];
  375. memset(bucket, 0, sizeof(*bucket));
  376. ++saved_buckets_count_;
  377. bucket->stack = key_copy;
  378. bucket->next = NULL;
  379. } else {
  380. recursive_insert = true;
  381. const void** key_copy = static_cast<const void**>(
  382. MyAllocator::Allocate(key_size));
  383. recursive_insert = false;
  384. std::copy(key, key + depth, key_copy);
  385. recursive_insert = true;
  386. bucket = static_cast<HeapProfileBucket*>(
  387. MyAllocator::Allocate(sizeof(HeapProfileBucket)));
  388. recursive_insert = false;
  389. memset(bucket, 0, sizeof(*bucket));
  390. bucket->stack = key_copy;
  391. bucket->next = bucket_table_[hash_index];
  392. }
  393. bucket->hash = hash;
  394. bucket->depth = depth;
  395. bucket_table_[hash_index] = bucket;
  396. ++num_buckets_;
  397. return bucket;
  398. }
  399. MemoryRegionMap::RegionIterator MemoryRegionMap::BeginRegionLocked() {
  400. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  401. RAW_CHECK(regions_ != NULL, "");
  402. return regions_->begin();
  403. }
  404. MemoryRegionMap::RegionIterator MemoryRegionMap::EndRegionLocked() {
  405. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  406. RAW_CHECK(regions_ != NULL, "");
  407. return regions_->end();
  408. }
  409. inline void MemoryRegionMap::DoInsertRegionLocked(const Region& region) {
  410. RAW_VLOG(12, "Inserting region %p..%p from %p",
  411. reinterpret_cast<void*>(region.start_addr),
  412. reinterpret_cast<void*>(region.end_addr),
  413. reinterpret_cast<void*>(region.caller()));
  414. RegionSet::const_iterator i = regions_->lower_bound(region);
  415. if (i != regions_->end() && i->start_addr <= region.start_addr) {
  416. RAW_DCHECK(region.end_addr <= i->end_addr, ""); // lower_bound ensures this
  417. return; // 'region' is a subset of an already recorded region; do nothing
  418. // We can be stricter and allow this only when *i has been created via
  419. // an mmap with MAP_NORESERVE flag set.
  420. }
  421. if (DEBUG_MODE) {
  422. RAW_CHECK(i == regions_->end() || !region.Overlaps(*i),
  423. "Wow, overlapping memory regions");
  424. Region sample;
  425. sample.SetRegionSetKey(region.start_addr);
  426. i = regions_->lower_bound(sample);
  427. RAW_CHECK(i == regions_->end() || !region.Overlaps(*i),
  428. "Wow, overlapping memory regions");
  429. }
  430. region.AssertIsConsistent(); // just making sure
  431. // This inserts and allocates permanent storage for region
  432. // and its call stack data: it's safe to do it now:
  433. regions_->insert(region);
  434. RAW_VLOG(12, "Inserted region %p..%p :",
  435. reinterpret_cast<void*>(region.start_addr),
  436. reinterpret_cast<void*>(region.end_addr));
  437. if (VLOG_IS_ON(12)) LogAllLocked();
  438. }
  439. // These variables are local to MemoryRegionMap::InsertRegionLocked()
  440. // and MemoryRegionMap::HandleSavedRegionsLocked()
  441. // and are file-level to ensure that they are initialized at load time.
  442. // Number of unprocessed region inserts.
  443. static int saved_regions_count = 0;
  444. // Unprocessed inserts (must be big enough to hold all allocations that can
  445. // be caused by a InsertRegionLocked call).
  446. // Region has no constructor, so that c-tor execution does not interfere
  447. // with the any-time use of the static memory behind saved_regions.
  448. static MemoryRegionMap::Region saved_regions[20];
  449. inline void MemoryRegionMap::HandleSavedRegionsLocked(
  450. void (*insert_func)(const Region& region)) {
  451. while (saved_regions_count > 0) {
  452. // Making a local-var copy of the region argument to insert_func
  453. // including its stack (w/o doing any memory allocations) is important:
  454. // in many cases the memory in saved_regions
  455. // will get written-to during the (*insert_func)(r) call below.
  456. Region r = saved_regions[--saved_regions_count];
  457. (*insert_func)(r);
  458. }
  459. }
  460. void MemoryRegionMap::RestoreSavedBucketsLocked() {
  461. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  462. while (saved_buckets_count_ > 0) {
  463. HeapProfileBucket bucket = saved_buckets_[--saved_buckets_count_];
  464. unsigned int hash_index =
  465. static_cast<unsigned int>(bucket.hash) % kHashTableSize;
  466. bool is_found = false;
  467. for (HeapProfileBucket* curr = bucket_table_[hash_index];
  468. curr != 0;
  469. curr = curr->next) {
  470. if ((curr->hash == bucket.hash) && (curr->depth == bucket.depth) &&
  471. std::equal(bucket.stack, bucket.stack + bucket.depth, curr->stack)) {
  472. curr->allocs += bucket.allocs;
  473. curr->alloc_size += bucket.alloc_size;
  474. curr->frees += bucket.frees;
  475. curr->free_size += bucket.free_size;
  476. is_found = true;
  477. break;
  478. }
  479. }
  480. if (is_found) continue;
  481. const size_t key_size = sizeof(bucket.stack[0]) * bucket.depth;
  482. const void** key_copy = static_cast<const void**>(
  483. MyAllocator::Allocate(key_size));
  484. std::copy(bucket.stack, bucket.stack + bucket.depth, key_copy);
  485. HeapProfileBucket* new_bucket = static_cast<HeapProfileBucket*>(
  486. MyAllocator::Allocate(sizeof(HeapProfileBucket)));
  487. memset(new_bucket, 0, sizeof(*new_bucket));
  488. new_bucket->hash = bucket.hash;
  489. new_bucket->depth = bucket.depth;
  490. new_bucket->stack = key_copy;
  491. new_bucket->next = bucket_table_[hash_index];
  492. bucket_table_[hash_index] = new_bucket;
  493. ++num_buckets_;
  494. }
  495. }
  496. inline void MemoryRegionMap::InsertRegionLocked(const Region& region) {
  497. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  498. // We can be called recursively, because RegionSet constructor
  499. // and DoInsertRegionLocked() (called below) can call the allocator.
  500. // recursive_insert tells us if that's the case. When this happens,
  501. // region insertion information is recorded in saved_regions[],
  502. // and taken into account when the recursion unwinds.
  503. // Do the insert:
  504. if (recursive_insert) { // recursion: save in saved_regions
  505. RAW_VLOG(12, "Saving recursive insert of region %p..%p from %p",
  506. reinterpret_cast<void*>(region.start_addr),
  507. reinterpret_cast<void*>(region.end_addr),
  508. reinterpret_cast<void*>(region.caller()));
  509. RAW_CHECK(saved_regions_count < arraysize(saved_regions), "");
  510. // Copy 'region' to saved_regions[saved_regions_count]
  511. // together with the contents of its call_stack,
  512. // then increment saved_regions_count.
  513. saved_regions[saved_regions_count++] = region;
  514. } else { // not a recusrive call
  515. if (regions_ == NULL) { // init regions_
  516. RAW_VLOG(12, "Initializing region set");
  517. regions_ = regions_rep.region_set();
  518. recursive_insert = true;
  519. new(regions_) RegionSet();
  520. HandleSavedRegionsLocked(&DoInsertRegionLocked);
  521. recursive_insert = false;
  522. }
  523. recursive_insert = true;
  524. // Do the actual insertion work to put new regions into regions_:
  525. DoInsertRegionLocked(region);
  526. HandleSavedRegionsLocked(&DoInsertRegionLocked);
  527. recursive_insert = false;
  528. }
  529. }
  530. // We strip out different number of stack frames in debug mode
  531. // because less inlining happens in that case
  532. #ifdef NDEBUG
  533. static const int kStripFrames = 1;
  534. #else
  535. static const int kStripFrames = 3;
  536. #endif
  537. void MemoryRegionMap::RecordRegionAddition(const void* start, size_t size) {
  538. // Record start/end info about this memory acquisition call in a new region:
  539. Region region;
  540. region.Create(start, size);
  541. // First get the call stack info into the local varible 'region':
  542. int depth = 0;
  543. // NOTE: libunwind also does mmap and very much likely while holding
  544. // it's own lock(s). So some threads may first take libunwind lock,
  545. // and then take region map lock (necessary to record mmap done from
  546. // inside libunwind). On the other hand other thread(s) may do
  547. // normal mmap. Which would call this method to record it. Which
  548. // would then proceed with installing that record to region map
  549. // while holding region map lock. That may cause mmap from our own
  550. // internal allocators, so attempt to unwind in this case may cause
  551. // reverse order of taking libuwind and region map locks. Which is
  552. // obvious deadlock.
  553. //
  554. // Thankfully, we can easily detect if we're holding region map lock
  555. // and avoid recording backtrace in this (rare and largely
  556. // irrelevant) case. By doing this we "declare" that thread needing
  557. // both locks must take region map lock last. In other words we do
  558. // not allow taking libuwind lock when we already have region map
  559. // lock. Note, this is generally impossible when somebody tries to
  560. // mix cpu profiling and heap checking/profiling, because cpu
  561. // profiler grabs backtraces at arbitrary places. But at least such
  562. // combination is rarer and less relevant.
  563. if (max_stack_depth_ > 0 && !LockIsHeld()) {
  564. depth = MallocHook::GetCallerStackTrace(const_cast<void**>(region.call_stack),
  565. max_stack_depth_, kStripFrames + 1);
  566. }
  567. region.set_call_stack_depth(depth); // record stack info fully
  568. RAW_VLOG(10, "New global region %p..%p from %p",
  569. reinterpret_cast<void*>(region.start_addr),
  570. reinterpret_cast<void*>(region.end_addr),
  571. reinterpret_cast<void*>(region.caller()));
  572. // Note: none of the above allocates memory.
  573. Lock(); // recursively lock
  574. map_size_ += size;
  575. InsertRegionLocked(region);
  576. // This will (eventually) allocate storage for and copy over the stack data
  577. // from region.call_stack_data_ that is pointed by region.call_stack().
  578. if (bucket_table_ != NULL) {
  579. HeapProfileBucket* b = GetBucket(depth, region.call_stack);
  580. ++b->allocs;
  581. b->alloc_size += size;
  582. if (!recursive_insert) {
  583. recursive_insert = true;
  584. RestoreSavedBucketsLocked();
  585. recursive_insert = false;
  586. }
  587. }
  588. Unlock();
  589. }
  590. void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
  591. Lock();
  592. if (recursive_insert) {
  593. // First remove the removed region from saved_regions, if it's
  594. // there, to prevent overrunning saved_regions in recursive
  595. // map/unmap call sequences, and also from later inserting regions
  596. // which have already been unmapped.
  597. uintptr_t start_addr = reinterpret_cast<uintptr_t>(start);
  598. uintptr_t end_addr = start_addr + size;
  599. int put_pos = 0;
  600. int old_count = saved_regions_count;
  601. for (int i = 0; i < old_count; ++i, ++put_pos) {
  602. Region& r = saved_regions[i];
  603. if (r.start_addr == start_addr && r.end_addr == end_addr) {
  604. // An exact match, so it's safe to remove.
  605. RecordRegionRemovalInBucket(r.call_stack_depth, r.call_stack, size);
  606. --saved_regions_count;
  607. --put_pos;
  608. RAW_VLOG(10, ("Insta-Removing saved region %p..%p; "
  609. "now have %d saved regions"),
  610. reinterpret_cast<void*>(start_addr),
  611. reinterpret_cast<void*>(end_addr),
  612. saved_regions_count);
  613. } else {
  614. if (put_pos < i) {
  615. saved_regions[put_pos] = saved_regions[i];
  616. }
  617. }
  618. }
  619. }
  620. if (regions_ == NULL) { // We must have just unset the hooks,
  621. // but this thread was already inside the hook.
  622. Unlock();
  623. return;
  624. }
  625. if (!recursive_insert) {
  626. HandleSavedRegionsLocked(&InsertRegionLocked);
  627. }
  628. // first handle adding saved regions if any
  629. uintptr_t start_addr = reinterpret_cast<uintptr_t>(start);
  630. uintptr_t end_addr = start_addr + size;
  631. // subtract start_addr, end_addr from all the regions
  632. RAW_VLOG(10, "Removing global region %p..%p; have %" PRIuS " regions",
  633. reinterpret_cast<void*>(start_addr),
  634. reinterpret_cast<void*>(end_addr),
  635. regions_->size());
  636. Region sample;
  637. sample.SetRegionSetKey(start_addr);
  638. // Only iterate over the regions that might overlap start_addr..end_addr:
  639. for (RegionSet::iterator region = regions_->lower_bound(sample);
  640. region != regions_->end() && region->start_addr < end_addr;
  641. /*noop*/) {
  642. RAW_VLOG(13, "Looking at region %p..%p",
  643. reinterpret_cast<void*>(region->start_addr),
  644. reinterpret_cast<void*>(region->end_addr));
  645. if (start_addr <= region->start_addr &&
  646. region->end_addr <= end_addr) { // full deletion
  647. RAW_VLOG(12, "Deleting region %p..%p",
  648. reinterpret_cast<void*>(region->start_addr),
  649. reinterpret_cast<void*>(region->end_addr));
  650. RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
  651. region->end_addr - region->start_addr);
  652. RegionSet::iterator d = region;
  653. ++region;
  654. regions_->erase(d);
  655. continue;
  656. } else if (region->start_addr < start_addr &&
  657. end_addr < region->end_addr) { // cutting-out split
  658. RAW_VLOG(12, "Splitting region %p..%p in two",
  659. reinterpret_cast<void*>(region->start_addr),
  660. reinterpret_cast<void*>(region->end_addr));
  661. RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
  662. end_addr - start_addr);
  663. // Make another region for the start portion:
  664. // The new region has to be the start portion because we can't
  665. // just modify region->end_addr as it's the sorting key.
  666. Region r = *region;
  667. r.set_end_addr(start_addr);
  668. InsertRegionLocked(r);
  669. // cut *region from start:
  670. const_cast<Region&>(*region).set_start_addr(end_addr);
  671. } else if (end_addr > region->start_addr &&
  672. start_addr <= region->start_addr) { // cut from start
  673. RAW_VLOG(12, "Start-chopping region %p..%p",
  674. reinterpret_cast<void*>(region->start_addr),
  675. reinterpret_cast<void*>(region->end_addr));
  676. RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
  677. end_addr - region->start_addr);
  678. const_cast<Region&>(*region).set_start_addr(end_addr);
  679. } else if (start_addr > region->start_addr &&
  680. start_addr < region->end_addr) { // cut from end
  681. RAW_VLOG(12, "End-chopping region %p..%p",
  682. reinterpret_cast<void*>(region->start_addr),
  683. reinterpret_cast<void*>(region->end_addr));
  684. RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
  685. region->end_addr - start_addr);
  686. // Can't just modify region->end_addr (it's the sorting key):
  687. Region r = *region;
  688. r.set_end_addr(start_addr);
  689. RegionSet::iterator d = region;
  690. ++region;
  691. // It's safe to erase before inserting since r is independent of *d:
  692. // r contains an own copy of the call stack:
  693. regions_->erase(d);
  694. InsertRegionLocked(r);
  695. continue;
  696. }
  697. ++region;
  698. }
  699. RAW_VLOG(12, "Removed region %p..%p; have %" PRIuS " regions",
  700. reinterpret_cast<void*>(start_addr),
  701. reinterpret_cast<void*>(end_addr),
  702. regions_->size());
  703. if (VLOG_IS_ON(12)) LogAllLocked();
  704. unmap_size_ += size;
  705. Unlock();
  706. }
  707. void MemoryRegionMap::RecordRegionRemovalInBucket(int depth,
  708. const void* const stack[],
  709. size_t size) {
  710. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  711. if (bucket_table_ == NULL) return;
  712. HeapProfileBucket* b = GetBucket(depth, stack);
  713. ++b->frees;
  714. b->free_size += size;
  715. }
  716. void MemoryRegionMap::MmapHook(const void* result,
  717. const void* start, size_t size,
  718. int prot, int flags,
  719. int fd, off_t offset) {
  720. // TODO(maxim): replace all 0x%" PRIxS " by %p when RAW_VLOG uses a safe
  721. // snprintf reimplementation that does not malloc to pretty-print NULL
  722. RAW_VLOG(10, "MMap = 0x%" PRIxPTR " of %" PRIuS " at %" PRIu64 " "
  723. "prot %d flags %d fd %d offs %" PRId64,
  724. reinterpret_cast<uintptr_t>(result), size,
  725. reinterpret_cast<uint64>(start), prot, flags, fd,
  726. static_cast<int64>(offset));
  727. if (result != reinterpret_cast<void*>(MAP_FAILED) && size != 0) {
  728. RecordRegionAddition(result, size);
  729. }
  730. }
  731. void MemoryRegionMap::MunmapHook(const void* ptr, size_t size) {
  732. RAW_VLOG(10, "MUnmap of %p %" PRIuS "", ptr, size);
  733. if (size != 0) {
  734. RecordRegionRemoval(ptr, size);
  735. }
  736. }
  737. void MemoryRegionMap::MremapHook(const void* result,
  738. const void* old_addr, size_t old_size,
  739. size_t new_size, int flags,
  740. const void* new_addr) {
  741. RAW_VLOG(10, "MRemap = 0x%" PRIxPTR " of 0x%" PRIxPTR " %" PRIuS " "
  742. "to %" PRIuS " flags %d new_addr=0x%" PRIxPTR,
  743. (uintptr_t)result, (uintptr_t)old_addr,
  744. old_size, new_size, flags,
  745. flags & MREMAP_FIXED ? (uintptr_t)new_addr : 0);
  746. if (result != reinterpret_cast<void*>(-1)) {
  747. RecordRegionRemoval(old_addr, old_size);
  748. RecordRegionAddition(result, new_size);
  749. }
  750. }
  751. void MemoryRegionMap::SbrkHook(const void* result, ptrdiff_t increment) {
  752. RAW_VLOG(10, "Sbrk = 0x%" PRIxPTR " of %" PRIdS "", (uintptr_t)result, increment);
  753. if (result != reinterpret_cast<void*>(-1)) {
  754. if (increment > 0) {
  755. void* new_end = sbrk(0);
  756. RecordRegionAddition(result, reinterpret_cast<uintptr_t>(new_end) -
  757. reinterpret_cast<uintptr_t>(result));
  758. } else if (increment < 0) {
  759. void* new_end = sbrk(0);
  760. RecordRegionRemoval(new_end, reinterpret_cast<uintptr_t>(result) -
  761. reinterpret_cast<uintptr_t>(new_end));
  762. }
  763. }
  764. }
  765. void MemoryRegionMap::LogAllLocked() {
  766. RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
  767. RAW_LOG(INFO, "List of regions:");
  768. uintptr_t previous = 0;
  769. for (RegionSet::const_iterator r = regions_->begin();
  770. r != regions_->end(); ++r) {
  771. RAW_LOG(INFO, "Memory region 0x%" PRIxPTR "..0x%" PRIxPTR " "
  772. "from 0x%" PRIxPTR " stack=%d",
  773. r->start_addr, r->end_addr, r->caller(), r->is_stack);
  774. RAW_CHECK(previous < r->end_addr, "wow, we messed up the set order");
  775. // this must be caused by uncontrolled recursive operations on regions_
  776. previous = r->end_addr;
  777. }
  778. RAW_LOG(INFO, "End of regions list");
  779. }