Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8274794: Print all owned locks in hs_err file #5958

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -31,6 +31,7 @@
#include "runtime/osThread.hpp"
#include "runtime/safepointMechanism.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadCritical.hpp"
#include "utilities/events.hpp"
#include "utilities/macros.hpp"

@@ -267,7 +268,26 @@ bool Monitor::wait(int64_t timeout) {
return wait_status != 0; // return true IFF timeout
}

static GrowableArray<Mutex*>* _mutex_array = NULL;

static void add_mutex(Mutex* var) {
// This is at startup so doesn't need sync.
if (_mutex_array == NULL) {
_mutex_array = new (ResourceObj::C_HEAP, mtThread) GrowableArray<Mutex*>(128, mtThread);
}
ThreadCritical tc;
coleenp marked this conversation as resolved.
Show resolved Hide resolved
_mutex_array->push(var);
}

static void remove_mutex(Mutex* var) {
ThreadCritical tc;
int i = _mutex_array->find_from_end(var);
assert(i != -1, "Mutex not in list");
_mutex_array->remove_at(i);
}

Mutex::~Mutex() {
remove_mutex(this);
assert_owner(NULL);
os::free(const_cast<char*>(_name));
}
@@ -288,6 +308,7 @@ Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(NULL) {
assert(_rank > nosafepoint || _allow_vm_block,
"Locks that don't check for safepoint should always allow the vm to block: %s", name);
#endif
add_mutex(this);
}

bool Mutex::owned_by_self() const {
@@ -300,6 +321,26 @@ void Mutex::print_on_error(outputStream* st) const {
st->print(" - owner thread: " PTR_FORMAT, p2i(owner()));
}

// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void Mutex::print_owned_locks_on_error(outputStream* st) {
coleenp marked this conversation as resolved.
Show resolved Hide resolved
st->print("VM Mutex/Monitor currently owned by a thread: ");
coleenp marked this conversation as resolved.
Show resolved Hide resolved
bool none = true;
for (int i = 0; i < _mutex_array->length(); i++) {
// see if it has an owner
if (_mutex_array->at(i)->owner() != NULL) {
if (none) {
// print format used by Mutex::print_on_error()
st->print_cr(" ([mutex/lock_event])");
none = false;
}
_mutex_array->at(i)->print_on_error(st);
st->cr();
}
}
if (none) st->print_cr("None");
}

// ----------------------------------------------------------------------------------
// Non-product code
//
@@ -190,6 +190,11 @@ class Mutex : public CHeapObj<mtSynchronizer> {
const char *name() const { return _name; }

void print_on_error(outputStream* st) const;

// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
static void print_owned_locks_on_error(outputStream* st);

#ifndef PRODUCT
void print_on(outputStream* st) const;
void print() const { print_on(::tty); }
@@ -157,11 +157,6 @@ Mutex* Bootclasspath_lock = NULL;
Monitor* JVMCI_lock = NULL;
#endif


#define MAX_NUM_MUTEX 128
static Mutex* _mutex_array[MAX_NUM_MUTEX];
static int _num_mutex;

#ifdef ASSERT
void assert_locked_or_safepoint(const Mutex* lock) {
// check if this thread owns the lock (common case)
@@ -194,26 +189,18 @@ void assert_locked_or_safepoint_or_handshake(const Mutex* lock, const JavaThread
}
#endif

static void add_mutex(Mutex* var) {
assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
_mutex_array[_num_mutex++] = var;
}

#define def(var, type, pri, vm_block) { \
var = new type(Mutex::pri, #var, vm_block); \
add_mutex(var); \
}

// Specify relative ranked lock
#ifdef ASSERT
#define defl(var, type, held_lock, vm_block) { \
var = new type(held_lock->rank()-1, #var, vm_block); \
add_mutex(var); \
}
#else
#define defl(var, type, held_lock, vm_block) { \
var = new type(Mutex::safepoint, #var, vm_block); \
add_mutex(var); \
}
#endif

@@ -380,23 +367,3 @@ GCMutexLocker::GCMutexLocker(Mutex* mutex) {
_mutex->lock();
}
}

// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void print_owned_locks_on_error(outputStream* st) {
st->print("VM Mutex/Monitor currently owned by a thread: ");
bool none = true;
for (int i = 0; i < _num_mutex; i++) {
// see if it has an owner
if (_mutex_array[i]->owner() != NULL) {
if (none) {
// print format used by Mutex::print_on_error()
st->print_cr(" ([mutex/lock_event])");
none = false;
}
_mutex_array[i]->print_on_error(st);
st->cr();
}
}
if (none) st->print_cr("None");
}
@@ -169,12 +169,6 @@ extern Mutex* tty_lock; // lock to synchronize output.
// order. If their implementations change such that these assumptions
// are violated, a whole lot of code will break.

// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void print_owned_locks_on_error(outputStream* st);

char *lock_name(Mutex *mutex);
coleenp marked this conversation as resolved.
Show resolved Hide resolved

// for debugging: check that we're already owning this lock (or are at a safepoint / handshake)
#ifdef ASSERT
void assert_locked_or_safepoint(const Mutex* lock);
@@ -1015,7 +1015,7 @@ void VMError::report(outputStream* st, bool _verbose) {

// mutexes/monitors that currently have an owner
if (_verbose) {
print_owned_locks_on_error(st);
Mutex::print_owned_locks_on_error(st);
st->cr();
}

@@ -1913,6 +1913,12 @@ void VMError::controlled_crash(int how) {
switch (how) {
case 1: assert(how == 0, "test assert"); break;
case 2: guarantee(how == 0, "test guarantee"); break;
case 3: {
Mutex* ErrorTest_lock = new Mutex(Mutex::nosafepoint, "ErrorTest_lock");
MutexLocker ml(ErrorTest_lock, Mutex::_no_safepoint_check_flag);
assert(how == 0, "test assert with lock");
break;
coleenp marked this conversation as resolved.
Show resolved Hide resolved
}

// The other cases are unused.
case 14: crash_with_segfault(); break;
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/


/*
* @test
* @bug 8274794
* @summary Test that locks are printed in the Error file.
* @library /test/lib
* @modules java.base/jdk.internal.misc
* @requires (vm.debug == true)
* @run driver ErrorFileLocksTest
*/

import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

import java.io.*;
import java.util.regex.Pattern;

public class ErrorFileLocksTest {
coleenp marked this conversation as resolved.
Show resolved Hide resolved

public static void do_test() throws Exception {

ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-Xmx64M",
"-XX:-CreateCoredumpOnCrash",
"-XX:ErrorHandlerTest=3",
"-version");

OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");

File f = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
System.out.println("Found hs error file at " + f.getAbsolutePath());

ErrorFileScanner.scanHsErrorFileForContent(f, new Pattern[] {
Pattern.compile("# *Internal Error.*"),
Pattern.compile(".*VM Mutex/Monitor currently owned by a thread:.*"),
Pattern.compile(".*Threads_lock - owner thread:.*"),
Pattern.compile(".*ErrorTest_lock - owner thread:.*")
});
}

public static void main(String[] args) throws Exception {
do_test();
}

}



@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, SAP. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -42,46 +42,6 @@

public class ErrorFileOverwriteTest {

private static File findHsErrorFileInOutput(OutputAnalyzer output) {

String hs_err_file = output.firstMatch("# *(\\S*hs_err_pid.*\\.log)", 1);
if(hs_err_file ==null) {
throw new RuntimeException("Did not find hs-err file in output.\n");
}

File f = new File(hs_err_file);
if (!f.exists()) {
throw new RuntimeException("hs-err file missing at "
+ f.getAbsolutePath() + ".\n");
}

return f;

}

private static void scanHsErrorFileForContent(File f, Pattern[] pattern) throws IOException {
FileInputStream fis = new FileInputStream(f);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;

int currentPattern = 0;

String lastLine = null;
while ((line = br.readLine()) != null && currentPattern < pattern.length) {
if (pattern[currentPattern].matcher(line).matches()) {
System.out.println("Found: " + line + ".");
currentPattern++;
}
lastLine = line;
}
br.close();

if (currentPattern < pattern.length) {
throw new RuntimeException("hs-err file incomplete (first missing pattern: " + pattern[currentPattern] + ")");
}

}

public static void do_test(boolean with_percent_p) throws Exception {

// Crash twice.
@@ -110,10 +70,10 @@ public static void do_test(boolean with_percent_p) throws Exception {
output_detail.shouldMatch("# " + errorFileStem + ".*");
System.out.println("First crash: Found expected output on tty. Ok.");

File f = findHsErrorFileInOutput(output_detail);
File f = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
System.out.println("First crash: Found hs error file at " + f.getAbsolutePath());

scanHsErrorFileForContent(f, new Pattern[] {
ErrorFileScanner.scanHsErrorFileForContent(f, new Pattern[] {
Pattern.compile("# *Internal Error.*"),
Pattern.compile("Command Line:.*-XX:ErrorHandlerTest=1.*-XX:ErrorFile=" + errorFileStem + ".*")
});
@@ -136,7 +96,7 @@ public static void do_test(boolean with_percent_p) throws Exception {
output_detail.shouldMatch("# " + errorFileStem + ".*");
System.out.println("Second crash: Found expected output on tty. Ok.");

File f2 = findHsErrorFileInOutput(output_detail);
File f2 = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
System.out.println("Second crash: Found hs error file at " + f2.getAbsolutePath());

if (with_percent_p) {
@@ -145,7 +105,7 @@ public static void do_test(boolean with_percent_p) throws Exception {
}
}

scanHsErrorFileForContent(f2, new Pattern[] {
ErrorFileScanner.scanHsErrorFileForContent(f2, new Pattern[] {
Pattern.compile("# *Internal Error.*"),
Pattern.compile("Command Line:.*-XX:ErrorHandlerTest=2.*-XX:ErrorFile=" + errorFileStem + ".*")
});