1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_HPP 26 #define SHARE_VM_SERVICES_MALLOC_TRACKER_HPP 27 28 #if INCLUDE_NMT 29 30 #include "memory/allocation.hpp" 31 #include "runtime/atomic.hpp" 32 #include "runtime/threadCritical.hpp" 33 #include "services/nmtCommon.hpp" 34 #include "utilities/nativeCallStack.hpp" 35 36 /* 37 * This counter class counts memory allocation and deallocation, 38 * records total memory allocation size and number of allocations. 39 * The counters are updated atomically. 40 */ 41 class MemoryCounter VALUE_OBJ_CLASS_SPEC { 42 private: 43 size_t _count; 44 size_t _size; 45 46 DEBUG_ONLY(size_t _peak_count;) 47 DEBUG_ONLY(size_t _peak_size; ) 48 49 public: 50 MemoryCounter() : _count(0), _size(0) { 51 DEBUG_ONLY(_peak_count = 0;) 52 DEBUG_ONLY(_peak_size = 0;) 53 } 54 55 inline void allocate(size_t sz) { 56 Atomic::add(1, (volatile MemoryCounterType*)&_count); 57 if (sz > 0) { 58 Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); 59 DEBUG_ONLY(_peak_size = MAX2(_peak_size, _size)); 60 } 61 DEBUG_ONLY(_peak_count = MAX2(_peak_count, _count);) 62 } 63 64 inline void deallocate(size_t sz) { 65 assert(_count > 0, "Negative counter"); 66 assert(_size >= sz, "Negative size"); 67 Atomic::add(-1, (volatile MemoryCounterType*)&_count); 68 if (sz > 0) { 69 Atomic::add(-(MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); 70 } 71 } 72 73 inline void resize(long sz) { 74 if (sz != 0) { 75 Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); 76 DEBUG_ONLY(_peak_size = MAX2(_size, _peak_size);) 77 } 78 } 79 80 inline size_t count() const { return _count; } 81 inline size_t size() const { return _size; } 82 DEBUG_ONLY(inline size_t peak_count() const { return _peak_count; }) 83 DEBUG_ONLY(inline size_t peak_size() const { return _peak_size; }) 84 85 }; 86 87 /* 88 * Malloc memory used by a particular subsystem. 89 * It includes the memory acquired through os::malloc() 90 * call and arena's backing memory. 91 */ 92 class MallocMemory VALUE_OBJ_CLASS_SPEC { 93 private: 94 MemoryCounter _malloc; 95 MemoryCounter _arena; 96 97 public: 98 MallocMemory() { } 99 100 inline void record_malloc(size_t sz) { 101 _malloc.allocate(sz); 102 } 103 104 inline void record_free(size_t sz) { 105 _malloc.deallocate(sz); 106 } 107 108 inline void record_new_arena() { 109 _arena.allocate(0); 110 } 111 112 inline void record_arena_free() { 113 _arena.deallocate(0); 114 } 115 116 inline void record_arena_size_change(long sz) { 117 _arena.resize(sz); 118 } 119 120 inline size_t malloc_size() const { return _malloc.size(); } 121 inline size_t malloc_count() const { return _malloc.count();} 122 inline size_t arena_size() const { return _arena.size(); } 123 inline size_t arena_count() const { return _arena.count(); } 124 125 DEBUG_ONLY(inline const MemoryCounter& malloc_counter() const { return _malloc; }) 126 DEBUG_ONLY(inline const MemoryCounter& arena_counter() const { return _arena; }) 127 }; 128 129 class MallocMemorySummary; 130 131 // A snapshot of malloc'd memory, includes malloc memory 132 // usage by types and memory used by tracking itself. 133 class MallocMemorySnapshot : public ResourceObj { 134 friend class MallocMemorySummary; 135 136 private: 137 MallocMemory _malloc[mt_number_of_types]; 138 MemoryCounter _tracking_header; 139 140 141 public: 142 inline MallocMemory* by_type(MEMFLAGS flags) { 143 int index = NMTUtil::flag_to_index(flags); 144 assert(index >= 0 && index < mt_number_of_types, "Index out of bound"); 145 return &_malloc[index]; 146 } 147 148 inline MallocMemory* by_index(int index) { 149 assert(index >= 0, "Index out of bound"); 150 assert(index < mt_number_of_types, "Index out of bound"); 151 return &_malloc[index]; 152 } 153 154 inline MemoryCounter* malloc_overhead() { 155 return &_tracking_header; 156 } 157 158 // Total malloc'd memory amount 159 size_t total() const; 160 // Total malloc'd memory used by arenas 161 size_t total_arena() const; 162 163 inline size_t thread_count() const { 164 MallocMemorySnapshot* s = const_cast<MallocMemorySnapshot*>(this); 165 return s->by_type(mtThreadStack)->malloc_count(); 166 } 167 168 void copy_to(MallocMemorySnapshot* s) { 169 // Need to make sure that mtChunks don't get deallocated while the 170 // copy is going on, because their size is adjusted using this 171 // buffer in make_adjustment(). 172 ThreadCritical tc; 173 s->_tracking_header = _tracking_header; 174 for (int index = 0; index < mt_number_of_types; index ++) { 175 s->_malloc[index] = _malloc[index]; 176 } 177 } 178 179 // Make adjustment by subtracting chunks used by arenas 180 // from total chunks to get total free chunk size 181 void make_adjustment(); 182 }; 183 184 /* 185 * This class is for collecting malloc statistics at summary level 186 */ 187 class MallocMemorySummary : AllStatic { 188 private: 189 // Reserve memory for placement of MallocMemorySnapshot object 190 static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; 191 192 public: 193 static void initialize(); 194 195 static inline void record_malloc(size_t size, MEMFLAGS flag) { 196 as_snapshot()->by_type(flag)->record_malloc(size); 197 } 198 199 static inline void record_free(size_t size, MEMFLAGS flag) { 200 as_snapshot()->by_type(flag)->record_free(size); 201 } 202 203 static inline void record_new_arena(MEMFLAGS flag) { 204 as_snapshot()->by_type(flag)->record_new_arena(); 205 } 206 207 static inline void record_arena_free(MEMFLAGS flag) { 208 as_snapshot()->by_type(flag)->record_arena_free(); 209 } 210 211 static inline void record_arena_size_change(long size, MEMFLAGS flag) { 212 as_snapshot()->by_type(flag)->record_arena_size_change(size); 213 } 214 215 static void snapshot(MallocMemorySnapshot* s) { 216 as_snapshot()->copy_to(s); 217 s->make_adjustment(); 218 } 219 220 // Record memory used by malloc tracking header 221 static inline void record_new_malloc_header(size_t sz) { 222 as_snapshot()->malloc_overhead()->allocate(sz); 223 } 224 225 static inline void record_free_malloc_header(size_t sz) { 226 as_snapshot()->malloc_overhead()->deallocate(sz); 227 } 228 229 // The memory used by malloc tracking headers 230 static inline size_t tracking_overhead() { 231 return as_snapshot()->malloc_overhead()->size(); 232 } 233 234 static MallocMemorySnapshot* as_snapshot() { 235 return (MallocMemorySnapshot*)_snapshot; 236 } 237 }; 238 239 240 /* 241 * Malloc tracking header. 242 * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose, 243 * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build). 244 */ 245 246 class MallocHeader VALUE_OBJ_CLASS_SPEC { 247 #ifdef _LP64 248 size_t _size : 64; 249 size_t _flags : 8; 250 size_t _pos_idx : 16; 251 size_t _bucket_idx: 40; 252 #define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(40) 253 #define MAX_BUCKET_LENGTH right_n_bits(16) 254 #else 255 size_t _size : 32; 256 size_t _flags : 8; 257 size_t _pos_idx : 8; 258 size_t _bucket_idx: 16; 259 #define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(16) 260 #define MAX_BUCKET_LENGTH right_n_bits(8) 261 #endif // _LP64 262 263 public: 264 MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) { 265 assert(sizeof(MallocHeader) == sizeof(void*) * 2, 266 "Wrong header size"); 267 268 if (level == NMT_minimal) { 269 return; 270 } 271 272 _flags = flags; 273 set_size(size); 274 if (level == NMT_detail) { 275 size_t bucket_idx; 276 size_t pos_idx; 277 if (record_malloc_site(stack, size, &bucket_idx, &pos_idx)) { 278 assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index"); 279 assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index"); 280 _bucket_idx = bucket_idx; 281 _pos_idx = pos_idx; 282 } 283 } 284 285 MallocMemorySummary::record_malloc(size, flags); 286 MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader)); 287 } 288 289 inline size_t size() const { return _size; } 290 inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; } 291 bool get_stack(NativeCallStack& stack) const; 292 293 // Cleanup tracking information before the memory is released. 294 void release() const; 295 296 private: 297 inline void set_size(size_t size) { 298 _size = size; 299 } 300 bool record_malloc_site(const NativeCallStack& stack, size_t size, 301 size_t* bucket_idx, size_t* pos_idx) const; 302 }; 303 304 305 // Main class called from MemTracker to track malloc activities 306 class MallocTracker : AllStatic { 307 public: 308 // Initialize malloc tracker for specific tracking level 309 static bool initialize(NMT_TrackingLevel level); 310 311 static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to); 312 313 // malloc tracking header size for specific tracking level 314 static inline size_t malloc_header_size(NMT_TrackingLevel level) { 315 return (level == NMT_off) ? 0 : sizeof(MallocHeader); 316 } 317 318 // Parameter name convention: 319 // memblock : the beginning address for user data 320 // malloc_base: the beginning address that includes malloc tracking header 321 // 322 // The relationship: 323 // memblock = (char*)malloc_base + sizeof(nmt header) 324 // 325 326 // Record malloc on specified memory block 327 static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, 328 const NativeCallStack& stack, NMT_TrackingLevel level); 329 330 // Record free on specified memory block 331 static void* record_free(void* memblock); 332 333 // Offset memory address to header address 334 static inline void* get_base(void* memblock); 335 static inline void* get_base(void* memblock, NMT_TrackingLevel level) { 336 if (memblock == NULL || level == NMT_off) return memblock; 337 return (char*)memblock - malloc_header_size(level); 338 } 339 340 // Get memory size 341 static inline size_t get_size(void* memblock) { 342 MallocHeader* header = malloc_header(memblock); 343 return header->size(); 344 } 345 346 // Get memory type 347 static inline MEMFLAGS get_flags(void* memblock) { 348 MallocHeader* header = malloc_header(memblock); 349 return header->flags(); 350 } 351 352 // Get header size 353 static inline size_t get_header_size(void* memblock) { 354 return (memblock == NULL) ? 0 : sizeof(MallocHeader); 355 } 356 357 static inline void record_new_arena(MEMFLAGS flags) { 358 MallocMemorySummary::record_new_arena(flags); 359 } 360 361 static inline void record_arena_free(MEMFLAGS flags) { 362 MallocMemorySummary::record_arena_free(flags); 363 } 364 365 static inline void record_arena_size_change(int size, MEMFLAGS flags) { 366 MallocMemorySummary::record_arena_size_change(size, flags); 367 } 368 private: 369 static inline MallocHeader* malloc_header(void *memblock) { 370 assert(memblock != NULL, "NULL pointer"); 371 MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader)); 372 return header; 373 } 374 }; 375 376 #endif // INCLUDE_NMT 377 378 379 #endif //SHARE_VM_SERVICES_MALLOC_TRACKER_HPP