提交 9f3c4fca authored 作者: Thomas Mueller's avatar Thomas Mueller

ABBA deadlock detection via code instrumentation

上级 cf602925
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Utility to detect AB-BA deadlocks.
*/
public class AbbaDetector {
private static final boolean TRACE = false;
private static final ThreadLocal<Deque<Object>> STACK =
new ThreadLocal<Deque<Object>>() {
@Override protected Deque<Object> initialValue() {
return new ArrayDeque<Object>();
}
};
private static final Map<Object, Map<Object, Exception>> ORDER =
new WeakHashMap<Object, Map<Object, Exception>>();
private static final Set<String> KNOWN = new HashSet<String>();
public static Object begin(Object o) {
if (o == null) {
o = new SecurityManager() {
Class<?> clazz = getClassContext()[2];
}.clazz;
}
Deque<Object> stack = STACK.get();
if (!stack.isEmpty()) {
if (stack.contains(o)) {
// already synchronized on this
return o;
}
while (!stack.isEmpty()) {
Object last = stack.peek();
if (Thread.holdsLock(last)) {
break;
}
stack.pop();
}
}
if (TRACE) {
String thread = "[thread " + Thread.currentThread().getId() + "]";
String ident = new String(new char[stack.size() * 2]).replace((char) 0, ' ');
System.out.println(thread + " " + ident +
"sync " + getName(o));
}
if (stack.size() > 0) {
markHigher(o, stack);
}
stack.push(o);
return o;
}
private static Object getTest(Object o) {
// return o.getClass();
return o;
}
private static String getName(Object o) {
return o.getClass().getSimpleName() + ":" + System.identityHashCode(o);
}
public static synchronized void markHigher(Object o, Deque<Object> older) {
Object test = getTest(o);
Map<Object, Exception> map = ORDER.get(test);
if (map == null) {
map = new WeakHashMap<Object, Exception>();
ORDER.put(test, map);
}
Exception oldException = null;
for (Object old : older) {
Object oldTest = getTest(old);
if (oldTest == test) {
continue;
}
Map<Object, Exception> oldMap = ORDER.get(oldTest);
if (oldMap != null) {
Exception e = oldMap.get(test);
if (e != null) {
String deadlockType = test.getClass() + " " + oldTest.getClass();
if (!KNOWN.contains(deadlockType)) {
String message = getName(test) +
" synchronized after \n " + getName(oldTest) +
", but in the past before";
RuntimeException ex = new RuntimeException(message);
ex.initCause(e);
ex.printStackTrace(System.out);
// throw ex;
KNOWN.add(deadlockType);
}
}
}
if (!map.containsKey(oldTest)) {
if (oldException == null) {
oldException = new Exception("Before");
}
map.put(oldTest, oldException);
}
}
}
public static void main(String... args) {
Integer a = 1;
Float b = 2.0f;
synchronized (a) {
synchronized (b) {
System.out.println("a, then b");
}
}
synchronized (b) {
synchronized (a) {
System.out.println("b, then a");
}
}
}
}
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.build.code;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Enable / disable AB-BA deadlock detector code.
*/
public class AbbaDetect {
/**
* This method is called when executing this application from the command
* line.
*
* @param args the command line parameters
*/
public static void main(String... args) throws Exception {
String baseDir = "src/main";
process(new File(baseDir), true);
}
private static void process(File file, boolean enable) throws IOException {
String name = file.getName();
if (file.isDirectory()) {
if (name.equals("CVS") || name.equals(".svn")) {
return;
}
for (File f : file.listFiles()) {
process(f, enable);
}
return;
}
if (!name.endsWith(".java")) {
return;
}
if (name.endsWith("AbbaDetector.java")) {
return;
}
RandomAccessFile in = new RandomAccessFile(file, "r");
byte[] data = new byte[(int) file.length()];
in.readFully(data);
in.close();
String source = new String(data, "UTF-8");
String original = source;
source = disable(source);
if (enable) {
String s2 = enable(source);
if (!source.equals(disable(s2))) {
throw new IOException("Could not revert changes for file " + file);
}
source = s2;
}
if (source.equals(original)) {
return;
}
File newFile = new File(file + ".new");
RandomAccessFile out = new RandomAccessFile(newFile, "rw");
out.write(source.getBytes("UTF-8"));
out.close();
File oldFile = new File(file + ".old");
file.renameTo(oldFile);
newFile.renameTo(file);
oldFile.delete();
}
private static String disable(String source) {
source = source.replaceAll("\\{org.h2.util.AbbaDetector.begin\\(.*\\);", "{");
source = source.replaceAll("org.h2.util.AbbaDetector.begin\\((.*\\(\\))\\)", "$1");
source = source.replaceAll("org.h2.util.AbbaDetector.begin\\((.*)\\)", "$1");
source = source.replaceAll("synchronized ", "synchronized ");
return source;
}
private static String enable(String source) {
// the word synchronized within single line comments comments
source = source.replaceAll("(// .* synchronized )([^ ])", "$1 $2");
source = source.replaceAll("synchronized \\((.*)\\(\\)\\)", "synchronized \\(org.h2.util.AbbaDetector.begin\\($1\\(\\)\\)\\)");
source = source.replaceAll("synchronized \\((.*)\\)", "synchronized \\(org.h2.util.AbbaDetector.begin\\($1\\)\\)");
source = source.replaceAll("static synchronized ([^ (].*)\\{", "static synchronized $1{org.h2.util.AbbaDetector.begin\\(null\\);");
source = source.replaceAll("synchronized ([^ (].*)\\{", "synchronized $1{org.h2.util.AbbaDetector.begin\\(this\\);");
return source;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论