Mercurial > hg > truffle
comparison src/share/vm/services/memTracker.hpp @ 10986:1f4355cee9a2
8013651: NMT: reserve/release sequence id's in incorrect order due to race
Summary: Fixed NMT race condition for realloc, uncommit and release
Reviewed-by: coleenp, ccheung
author | zgu |
---|---|
date | Tue, 18 Jun 2013 08:44:08 -0400 |
parents | 35f8765422b9 |
children | 5f7a4367c787 |
comparison
equal
deleted
inserted
replaced
10984:cd2118b62475 | 10986:1f4355cee9a2 |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. | 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * | 4 * |
5 * This code is free software; you can redistribute it and/or modify it | 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 | 6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
52 NMT_error_reporting, // shutdown by vmError::report_and_die() | 52 NMT_error_reporting, // shutdown by vmError::report_and_die() |
53 NMT_out_of_generation, // running out of generation queue | 53 NMT_out_of_generation, // running out of generation queue |
54 NMT_sequence_overflow // overflow the sequence number | 54 NMT_sequence_overflow // overflow the sequence number |
55 }; | 55 }; |
56 | 56 |
57 class Tracker { | |
58 public: | |
59 void discard() { } | |
60 | |
61 void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL) { } | |
62 void record(address old_addr, address new_addr, size_t size, | |
63 MEMFLAGS flags, address pc = NULL) { } | |
64 }; | |
65 | |
66 private: | |
67 static Tracker _tkr; | |
68 | |
57 | 69 |
58 public: | 70 public: |
59 static inline void init_tracking_options(const char* option_line) { } | 71 static inline void init_tracking_options(const char* option_line) { } |
60 static inline bool is_on() { return false; } | 72 static inline bool is_on() { return false; } |
61 static const char* reason() { return "Native memory tracking is not implemented"; } | 73 static const char* reason() { return "Native memory tracking is not implemented"; } |
66 static inline void start() { } | 78 static inline void start() { } |
67 | 79 |
68 static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, | 80 static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, |
69 address pc = 0, Thread* thread = NULL) { } | 81 address pc = 0, Thread* thread = NULL) { } |
70 static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { } | 82 static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { } |
71 static inline void record_realloc(address old_addr, address new_addr, size_t size, | |
72 MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { } | |
73 static inline void record_arena_size(address addr, size_t size) { } | 83 static inline void record_arena_size(address addr, size_t size) { } |
74 static inline void record_virtual_memory_reserve(address addr, size_t size, | 84 static inline void record_virtual_memory_reserve(address addr, size_t size, |
75 address pc = 0, Thread* thread = NULL) { } | 85 MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { } |
86 static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size, | |
87 MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { } | |
76 static inline void record_virtual_memory_commit(address addr, size_t size, | 88 static inline void record_virtual_memory_commit(address addr, size_t size, |
77 address pc = 0, Thread* thread = NULL) { } | 89 address pc = 0, Thread* thread = NULL) { } |
78 static inline void record_virtual_memory_uncommit(address addr, size_t size, | |
79 Thread* thread = NULL) { } | |
80 static inline void record_virtual_memory_release(address addr, size_t size, | |
81 Thread* thread = NULL) { } | |
82 static inline void record_virtual_memory_type(address base, MEMFLAGS flags, | 90 static inline void record_virtual_memory_type(address base, MEMFLAGS flags, |
83 Thread* thread = NULL) { } | 91 Thread* thread = NULL) { } |
92 static inline Tracker get_realloc_tracker() { return _tkr; } | |
93 static inline Tracker get_virtual_memory_uncommit_tracker() { return _tkr; } | |
94 static inline Tracker get_virtual_memory_release_tracker() { return _tkr; } | |
84 static inline bool baseline() { return false; } | 95 static inline bool baseline() { return false; } |
85 static inline bool has_baseline() { return false; } | 96 static inline bool has_baseline() { return false; } |
86 | 97 |
87 static inline void set_autoShutdown(bool value) { } | 98 static inline void set_autoShutdown(bool value) { } |
88 static void shutdown(ShutdownReason reason) { } | 99 static void shutdown(ShutdownReason reason) { } |
161 NMT_started, // NMT fully started | 172 NMT_started, // NMT fully started |
162 NMT_shutdown_pending, // shutdown pending | 173 NMT_shutdown_pending, // shutdown pending |
163 NMT_final_shutdown, // in final phase of shutdown | 174 NMT_final_shutdown, // in final phase of shutdown |
164 NMT_shutdown // shutdown | 175 NMT_shutdown // shutdown |
165 }; | 176 }; |
177 | |
178 public: | |
179 class Tracker : public StackObj { | |
180 friend class MemTracker; | |
181 public: | |
182 enum MemoryOperation { | |
183 NoOp, // no op | |
184 Malloc, // malloc | |
185 Realloc, // realloc | |
186 Free, // free | |
187 Reserve, // virtual memory reserve | |
188 Commit, // virtual memory commit | |
189 ReserveAndCommit, // virtual memory reserve and commit | |
190 StackAlloc = ReserveAndCommit, // allocate thread stack | |
191 Type, // assign virtual memory type | |
192 Uncommit, // virtual memory uncommit | |
193 Release, // virtual memory release | |
194 ArenaSize, // set arena size | |
195 StackRelease // release thread stack | |
196 }; | |
197 | |
198 | |
199 protected: | |
200 Tracker(MemoryOperation op, Thread* thr = NULL); | |
201 | |
202 public: | |
203 void discard(); | |
204 | |
205 void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL); | |
206 void record(address old_addr, address new_addr, size_t size, | |
207 MEMFLAGS flags, address pc = NULL); | |
208 | |
209 private: | |
210 bool _need_thread_critical_lock; | |
211 JavaThread* _java_thread; | |
212 MemoryOperation _op; // memory operation | |
213 jint _seq; // reserved sequence number | |
214 }; | |
215 | |
166 | 216 |
167 public: | 217 public: |
168 // native memory tracking level | 218 // native memory tracking level |
169 enum NMTLevel { | 219 enum NMTLevel { |
170 NMT_off, // native memory tracking is off | 220 NMT_off, // native memory tracking is off |
274 static void start(); | 324 static void start(); |
275 | 325 |
276 // record a 'malloc' call | 326 // record a 'malloc' call |
277 static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, | 327 static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, |
278 address pc = 0, Thread* thread = NULL) { | 328 address pc = 0, Thread* thread = NULL) { |
279 if (is_on() && NMT_CAN_TRACK(flags)) { | 329 Tracker tkr(Tracker::Malloc, thread); |
280 assert(size > 0, "Sanity check"); | 330 tkr.record(addr, size, flags, pc); |
281 create_memory_record(addr, (flags|MemPointerRecord::malloc_tag()), size, pc, thread); | |
282 } | |
283 } | 331 } |
284 // record a 'free' call | 332 // record a 'free' call |
285 static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { | 333 static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { |
286 if (is_on() && NMT_CAN_TRACK(flags)) { | 334 Tracker tkr(Tracker::Free, thread); |
287 create_memory_record(addr, MemPointerRecord::free_tag(), 0, 0, thread); | 335 tkr.record(addr, 0, flags, DEBUG_CALLER_PC); |
288 } | 336 } |
289 } | 337 |
290 // record a 'realloc' call | |
291 static inline void record_realloc(address old_addr, address new_addr, size_t size, | |
292 MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { | |
293 if (is_on() && NMT_CAN_TRACK(flags)) { | |
294 assert(size > 0, "Sanity check"); | |
295 record_free(old_addr, flags, thread); | |
296 record_malloc(new_addr, size, flags, pc, thread); | |
297 } | |
298 } | |
299 | |
300 // record arena memory size | |
301 static inline void record_arena_size(address addr, size_t size) { | 338 static inline void record_arena_size(address addr, size_t size) { |
302 // we add a positive offset to arena address, so we can have arena memory record | 339 Tracker tkr(Tracker::ArenaSize); |
303 // sorted after arena record | 340 tkr.record(addr, size); |
304 if (is_on() && !UseMallocOnly) { | |
305 assert(addr != NULL, "Sanity check"); | |
306 create_memory_record((addr + sizeof(void*)), MemPointerRecord::arena_size_tag(), size, | |
307 DEBUG_CALLER_PC, NULL); | |
308 } | |
309 } | 341 } |
310 | 342 |
311 // record a virtual memory 'reserve' call | 343 // record a virtual memory 'reserve' call |
312 static inline void record_virtual_memory_reserve(address addr, size_t size, | 344 static inline void record_virtual_memory_reserve(address addr, size_t size, |
313 address pc = 0, Thread* thread = NULL) { | 345 MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { |
314 if (is_on()) { | 346 assert(size > 0, "Sanity check"); |
315 assert(size > 0, "Sanity check"); | 347 Tracker tkr(Tracker::Reserve, thread); |
316 create_memory_record(addr, MemPointerRecord::virtual_memory_reserve_tag(), | 348 tkr.record(addr, size, flags, pc); |
317 size, pc, thread); | |
318 } | |
319 } | 349 } |
320 | 350 |
321 static inline void record_thread_stack(address addr, size_t size, Thread* thr, | 351 static inline void record_thread_stack(address addr, size_t size, Thread* thr, |
322 address pc = 0) { | 352 address pc = 0) { |
323 if (is_on()) { | 353 Tracker tkr(Tracker::StackAlloc, thr); |
324 assert(size > 0 && thr != NULL, "Sanity check"); | 354 tkr.record(addr, size, mtThreadStack, pc); |
325 create_memory_record(addr, MemPointerRecord::virtual_memory_reserve_tag() | mtThreadStack, | |
326 size, pc, thr); | |
327 create_memory_record(addr, MemPointerRecord::virtual_memory_commit_tag() | mtThreadStack, | |
328 size, pc, thr); | |
329 } | |
330 } | 355 } |
331 | 356 |
332 static inline void release_thread_stack(address addr, size_t size, Thread* thr) { | 357 static inline void release_thread_stack(address addr, size_t size, Thread* thr) { |
333 if (is_on()) { | 358 Tracker tkr(Tracker::StackRelease, thr); |
334 assert(size > 0 && thr != NULL, "Sanity check"); | 359 tkr.record(addr, size, mtThreadStack, DEBUG_CALLER_PC); |
335 assert(!thr->is_Java_thread(), "too early"); | |
336 create_memory_record(addr, MemPointerRecord::virtual_memory_uncommit_tag() | mtThreadStack, | |
337 size, DEBUG_CALLER_PC, thr); | |
338 create_memory_record(addr, MemPointerRecord::virtual_memory_release_tag() | mtThreadStack, | |
339 size, DEBUG_CALLER_PC, thr); | |
340 } | |
341 } | 360 } |
342 | 361 |
343 // record a virtual memory 'commit' call | 362 // record a virtual memory 'commit' call |
344 static inline void record_virtual_memory_commit(address addr, size_t size, | 363 static inline void record_virtual_memory_commit(address addr, size_t size, |
345 address pc, Thread* thread = NULL) { | 364 address pc, Thread* thread = NULL) { |
346 if (is_on()) { | 365 Tracker tkr(Tracker::Commit, thread); |
347 assert(size > 0, "Sanity check"); | 366 tkr.record(addr, size, mtNone, pc); |
348 create_memory_record(addr, MemPointerRecord::virtual_memory_commit_tag(), | 367 } |
349 size, pc, thread); | 368 |
350 } | 369 static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size, |
351 } | 370 MEMFLAGS flags, address pc, Thread* thread = NULL) { |
352 | 371 Tracker tkr(Tracker::ReserveAndCommit, thread); |
353 // record a virtual memory 'uncommit' call | 372 tkr.record(addr, size, flags, pc); |
354 static inline void record_virtual_memory_uncommit(address addr, size_t size, | 373 } |
355 Thread* thread = NULL) { | 374 |
356 if (is_on()) { | |
357 assert(size > 0, "Sanity check"); | |
358 create_memory_record(addr, MemPointerRecord::virtual_memory_uncommit_tag(), | |
359 size, DEBUG_CALLER_PC, thread); | |
360 } | |
361 } | |
362 | |
363 // record a virtual memory 'release' call | |
364 static inline void record_virtual_memory_release(address addr, size_t size, | |
365 Thread* thread = NULL) { | |
366 if (is_on()) { | |
367 assert(size > 0, "Sanity check"); | |
368 create_memory_record(addr, MemPointerRecord::virtual_memory_release_tag(), | |
369 size, DEBUG_CALLER_PC, thread); | |
370 } | |
371 } | |
372 | 375 |
373 // record memory type on virtual memory base address | 376 // record memory type on virtual memory base address |
374 static inline void record_virtual_memory_type(address base, MEMFLAGS flags, | 377 static inline void record_virtual_memory_type(address base, MEMFLAGS flags, |
375 Thread* thread = NULL) { | 378 Thread* thread = NULL) { |
376 if (is_on()) { | 379 Tracker tkr(Tracker::Type); |
377 assert(base > 0, "wrong base address"); | 380 tkr.record(base, 0, flags); |
378 assert((flags & (~mt_masks)) == 0, "memory type only"); | 381 } |
379 create_memory_record(base, (flags | MemPointerRecord::virtual_memory_type_tag()), | 382 |
380 0, DEBUG_CALLER_PC, thread); | 383 // Get memory trackers for memory operations that can result race conditions. |
381 } | 384 // The memory tracker has to be obtained before realloc, virtual memory uncommit |
385 // and virtual memory release, and call tracker.record() method if operation | |
386 // succeeded, or tracker.discard() to abort the tracking. | |
387 static inline Tracker get_realloc_tracker() { | |
388 return Tracker(Tracker::Realloc); | |
389 } | |
390 | |
391 static inline Tracker get_virtual_memory_uncommit_tracker() { | |
392 return Tracker(Tracker::Uncommit); | |
393 } | |
394 | |
395 static inline Tracker get_virtual_memory_release_tracker() { | |
396 return Tracker(Tracker::Release); | |
382 } | 397 } |
383 | 398 |
384 | 399 |
385 // create memory baseline of current memory snapshot | 400 // create memory baseline of current memory snapshot |
386 static bool baseline(); | 401 static bool baseline(); |
441 // pending recorder queue. Recorders are queued to pending queue | 456 // pending recorder queue. Recorders are queued to pending queue |
442 // when they are overflowed or collected at nmt sync point. | 457 // when they are overflowed or collected at nmt sync point. |
443 static void enqueue_pending_recorder(MemRecorder* rec); | 458 static void enqueue_pending_recorder(MemRecorder* rec); |
444 static MemRecorder* get_pending_recorders(); | 459 static MemRecorder* get_pending_recorders(); |
445 static void delete_all_pending_recorders(); | 460 static void delete_all_pending_recorders(); |
461 | |
462 // write a memory tracking record in recorder | |
463 static void write_tracking_record(address addr, MEMFLAGS type, | |
464 size_t size, jint seq, address pc, JavaThread* thread); | |
465 | |
466 static bool is_single_threaded_bootstrap() { | |
467 return _state == NMT_bootstrapping_single_thread; | |
468 } | |
469 | |
470 static void check_NMT_load(Thread* thr) { | |
471 assert(thr != NULL, "Sanity check"); | |
472 if (_slowdown_calling_thread && thr != _worker_thread) { | |
473 os::yield_all(); | |
474 } | |
475 } | |
476 | |
477 static void inc_pending_op_count() { | |
478 Atomic::inc(&_pending_op_count); | |
479 } | |
480 | |
481 static void dec_pending_op_count() { | |
482 Atomic::dec(&_pending_op_count); | |
483 assert(_pending_op_count >= 0, "Sanity check"); | |
484 } | |
485 | |
446 | 486 |
447 private: | 487 private: |
448 // retrieve a pooled memory record or create new one if there is not | 488 // retrieve a pooled memory record or create new one if there is not |
449 // one available | 489 // one available |
450 static MemRecorder* get_new_or_pooled_instance(); | 490 static MemRecorder* get_new_or_pooled_instance(); |
520 static volatile bool _worker_thread_idle; | 560 static volatile bool _worker_thread_idle; |
521 | 561 |
522 // if NMT should slow down calling thread to allow | 562 // if NMT should slow down calling thread to allow |
523 // worker thread to catch up | 563 // worker thread to catch up |
524 static volatile bool _slowdown_calling_thread; | 564 static volatile bool _slowdown_calling_thread; |
565 | |
566 // pending memory op count. | |
567 // Certain memory ops need to pre-reserve sequence number | |
568 // before memory operation can happen to avoid race condition. | |
569 // See MemTracker::Tracker for detail | |
570 static volatile jint _pending_op_count; | |
525 }; | 571 }; |
526 | 572 |
527 #endif // !INCLUDE_NMT | 573 #endif // !INCLUDE_NMT |
528 | 574 |
529 #endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP | 575 #endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP |