From e5f7b8b0da93eb02265c934963239acbb3b4d1b6 Mon Sep 17 00:00:00 2001 From: never Date: Mon, 16 May 2011 22:16:44 -0700 Subject: [PATCH] 6996747: SIGSEGV in nmethod::cleanup_inline_caches / CompiledIC::verify Reviewed-by: kvn, iveresov --- src/share/vm/runtime/globals.hpp | 6 ++ src/share/vm/runtime/sweeper.cpp | 122 +++++++++++++++++++++++++++++++ src/share/vm/runtime/sweeper.hpp | 7 ++ src/share/vm/runtime/thread.cpp | 10 +++ src/share/vm/runtime/thread.hpp | 17 ++++- 5 files changed, 160 insertions(+), 2 deletions(-) diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index f0a50abb7..a3c1939e2 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -2909,6 +2909,12 @@ class CommandLineFlags { product(intx, NmethodSweepCheckInterval, 5, \ "Compilers wake up every n seconds to possibly sweep nmethods") \ \ + notproduct(bool, LogSweeper, false, \ + "Keep a ring buffer of sweeper activity") \ + \ + notproduct(intx, SweeperLogEntries, 1024, \ + "Number of records in the ring buffer of sweeper activity") \ + \ notproduct(intx, MemProfilingInterval, 500, \ "Time between each invocation of the MemProfiler") \ \ diff --git a/src/share/vm/runtime/sweeper.cpp b/src/share/vm/runtime/sweeper.cpp index 0d980526a..f9ad79ef6 100644 --- a/src/share/vm/runtime/sweeper.cpp +++ b/src/share/vm/runtime/sweeper.cpp @@ -37,6 +37,94 @@ #include "utilities/events.hpp" #include "utilities/xmlstream.hpp" +#ifdef ASSERT + +#define SWEEP(nm) record_sweep(nm, __LINE__) +// Sweeper logging code +class SweeperRecord { + public: + int traversal; + int invocation; + int compile_id; + long traversal_mark; + int state; + const char* kind; + address vep; + address uep; + int line; + + void print() { + tty->print_cr("traversal = %d invocation = %d compile_id = %d %s uep = " PTR_FORMAT " vep = " + PTR_FORMAT " state = %d traversal_mark %d line = %d", + traversal, + invocation, + compile_id, + kind == NULL ? "" : kind, + uep, + vep, + state, + traversal_mark, + line); + } +}; + +static int _sweep_index = 0; +static SweeperRecord* _records = NULL; + +void NMethodSweeper::report_events(int id, address entry) { + if (_records != NULL) { + for (int i = _sweep_index; i < SweeperLogEntries; i++) { + if (_records[i].uep == entry || + _records[i].vep == entry || + _records[i].compile_id == id) { + _records[i].print(); + } + } + for (int i = 0; i < _sweep_index; i++) { + if (_records[i].uep == entry || + _records[i].vep == entry || + _records[i].compile_id == id) { + _records[i].print(); + } + } + } +} + +void NMethodSweeper::report_events() { + if (_records != NULL) { + for (int i = _sweep_index; i < SweeperLogEntries; i++) { + // skip empty records + if (_records[i].vep == NULL) continue; + _records[i].print(); + } + for (int i = 0; i < _sweep_index; i++) { + // skip empty records + if (_records[i].vep == NULL) continue; + _records[i].print(); + } + } +} + +void NMethodSweeper::record_sweep(nmethod* nm, int line) { + if (_records != NULL) { + _records[_sweep_index].traversal = _traversals; + _records[_sweep_index].traversal_mark = nm->_stack_traversal_mark; + _records[_sweep_index].invocation = _invocations; + _records[_sweep_index].compile_id = nm->compile_id(); + _records[_sweep_index].kind = nm->compile_kind(); + _records[_sweep_index].state = nm->_state; + _records[_sweep_index].vep = nm->verified_entry_point(); + _records[_sweep_index].uep = nm->entry_point(); + _records[_sweep_index].line = line; + + _sweep_index = (_sweep_index + 1) % SweeperLogEntries; + } +} +#else +#define SWEEP(nm) +#endif + + long NMethodSweeper::_traversals = 0; // No. of stack traversals performed nmethod* NMethodSweeper::_current = NULL; // Current nmethod int NMethodSweeper::_seen = 0 ; // No. of nmethods we have currently processed in current pass of CodeCache @@ -137,6 +225,13 @@ void NMethodSweeper::possibly_sweep() { if (old != 0) { return; } +#ifdef ASSERT + if (LogSweeper && _records == NULL) { + // Create the ring buffer for the logging code + _records = NEW_C_HEAP_ARRAY(SweeperRecord, SweeperLogEntries); + memset(_records, 0, sizeof(SweeperRecord) * SweeperLogEntries); + } +#endif if (_invocations > 0) { sweep_code_cache(); _invocations--; @@ -213,10 +308,29 @@ void NMethodSweeper::sweep_code_cache() { } } +class NMethodMarker: public StackObj { + private: + CompilerThread* _thread; + public: + NMethodMarker(nmethod* nm) { + _thread = CompilerThread::current(); + _thread->set_scanned_nmethod(nm); + } + ~NMethodMarker() { + _thread->set_scanned_nmethod(NULL); + } +}; + void NMethodSweeper::process_nmethod(nmethod *nm) { assert(!CodeCache_lock->owned_by_self(), "just checking"); + // Make sure this nmethod doesn't get unloaded during the scan, + // since the locks acquired below might safepoint. + NMethodMarker nmm(nm); + + SWEEP(nm); + // Skip methods that are currently referenced by the VM if (nm->is_locked_by_vm()) { // But still remember to clean-up inline caches for alive nmethods @@ -224,8 +338,10 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { // Clean-up all inline caches that points to zombie/non-reentrant methods MutexLocker cl(CompiledIC_lock); nm->cleanup_inline_caches(); + SWEEP(nm); } else { _locked_seen++; + SWEEP(nm); } return; } @@ -247,6 +363,7 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { } nm->mark_for_reclamation(); _rescan = true; + SWEEP(nm); } } else if (nm->is_not_entrant()) { // If there is no current activations of this method on the @@ -257,6 +374,7 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { } nm->make_zombie(); _rescan = true; + SWEEP(nm); } else { // Still alive, clean up its inline caches MutexLocker cl(CompiledIC_lock); @@ -265,6 +383,7 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { // request a rescan. If this method stays on the stack for a // long time we don't want to keep rescanning the code cache. _not_entrant_seen_on_stack++; + SWEEP(nm); } } else if (nm->is_unloaded()) { // Unloaded code, just make it a zombie @@ -273,10 +392,12 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { if (nm->is_osr_method()) { // No inline caches will ever point to osr methods, so we can just remove it MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + SWEEP(nm); nm->flush(); } else { nm->make_zombie(); _rescan = true; + SWEEP(nm); } } else { assert(nm->is_alive(), "should be alive"); @@ -293,6 +414,7 @@ void NMethodSweeper::process_nmethod(nmethod *nm) { // Clean-up all inline caches that points to zombie/non-reentrant methods MutexLocker cl(CompiledIC_lock); nm->cleanup_inline_caches(); + SWEEP(nm); } } diff --git a/src/share/vm/runtime/sweeper.hpp b/src/share/vm/runtime/sweeper.hpp index 8758497d7..f9ccb5353 100644 --- a/src/share/vm/runtime/sweeper.hpp +++ b/src/share/vm/runtime/sweeper.hpp @@ -57,6 +57,13 @@ class NMethodSweeper : public AllStatic { public: static long traversal_count() { return _traversals; } +#ifdef ASSERT + // Keep track of sweeper activity in the ring buffer + static void record_sweep(nmethod* nm, int line); + static void report_events(int id, address entry); + static void report_events(); +#endif + static void scan_stacks(); // Invoked at the end of each safepoint static void sweep_code_cache(); // Concurrent part of sweep job static void possibly_sweep(); // Compiler threads call this to sweep diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp index 02f4e7f49..51c45746d 100644 --- a/src/share/vm/runtime/thread.cpp +++ b/src/share/vm/runtime/thread.cpp @@ -2942,12 +2942,22 @@ CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters) _queue = queue; _counters = counters; _buffer_blob = NULL; + _scanned_nmethod = NULL; #ifndef PRODUCT _ideal_graph_printer = NULL; #endif } +void CompilerThread::oops_do(OopClosure* f, CodeBlobClosure* cf) { + JavaThread::oops_do(f, cf); + if (_scanned_nmethod != NULL && cf != NULL) { + // Safepoints can occur when the sweeper is scanning an nmethod so + // process it here to make sure it isn't unloaded in the middle of + // a scan. + cf->do_code_blob(_scanned_nmethod); + } +} // ======= Threads ======== diff --git a/src/share/vm/runtime/thread.hpp b/src/share/vm/runtime/thread.hpp index eefe93bc7..8387ab0f0 100644 --- a/src/share/vm/runtime/thread.hpp +++ b/src/share/vm/runtime/thread.hpp @@ -439,7 +439,7 @@ class Thread: public ThreadShadow { // GC support // Apply "f->do_oop" to all root oops in "this". // Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames - void oops_do(OopClosure* f, CodeBlobClosure* cf); + virtual void oops_do(OopClosure* f, CodeBlobClosure* cf); // Handles the parallel case for the method below. private: @@ -1381,7 +1381,7 @@ public: void trace_frames() PRODUCT_RETURN; // Print an annotated view of the stack frames - void print_frame_layout(int depth = 0, bool validate_only = false) PRODUCT_RETURN; + void print_frame_layout(int depth = 0, bool validate_only = false) NOT_DEBUG_RETURN; void validate_frame_layout() { print_frame_layout(0, true); } @@ -1698,6 +1698,8 @@ class CompilerThread : public JavaThread { CompileQueue* _queue; BufferBlob* _buffer_blob; + nmethod* _scanned_nmethod; // nmethod being scanned by the sweeper + public: static CompilerThread* current(); @@ -1726,6 +1728,11 @@ class CompilerThread : public JavaThread { _log = log; } + // GC support + // Apply "f->do_oop" to all root oops in "this". + // Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames + void oops_do(OopClosure* f, CodeBlobClosure* cf); + #ifndef PRODUCT private: IdealGraphPrinter *_ideal_graph_printer; @@ -1737,6 +1744,12 @@ public: // Get/set the thread's current task CompileTask* task() { return _task; } void set_task(CompileTask* task) { _task = task; } + + // Track the nmethod currently being scanned by the sweeper + void set_scanned_nmethod(nmethod* nm) { + assert(_scanned_nmethod == NULL || nm == NULL, "should reset to NULL before writing a new value"); + _scanned_nmethod = nm; + } }; inline CompilerThread* CompilerThread::current() { -- GitLab