EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.util]

COVERAGE SUMMARY FOR SOURCE FILE [AbbaLockingDetector.java]

nameclass, %method, %block, %line, %
AbbaLockingDetector.java100% (1/1)0%   (0/12)0%   (0/528)0%   (0/112)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbbaLockingDetector100% (1/1)0%   (0/12)0%   (0/528)0%   (0/112)
AbbaLockingDetector (): void 0%   (0/1)0%   (0/19)0%   (0/5)
dumpStackTraceElement (ThreadInfo, StringBuilder, int, StackTraceElement): void 0%   (0/1)0%   (0/78)0%   (0/15)
generateOrdering (List, ThreadInfo): void 0%   (0/1)0%   (0/41)0%   (0/9)
getObjectName (MonitorInfo): String 0%   (0/1)0%   (0/14)0%   (0/1)
getStackTraceForThread (ThreadInfo): String 0%   (0/1)0%   (0/152)0%   (0/25)
markHigher (List, ThreadInfo): void 0%   (0/1)0%   (0/123)0%   (0/23)
processThreadList (ThreadInfo []): void 0%   (0/1)0%   (0/34)0%   (0/7)
reset (): void 0%   (0/1)0%   (0/7)0%   (0/3)
run (): void 0%   (0/1)0%   (0/9)0%   (0/6)
startCollecting (): AbbaLockingDetector 0%   (0/1)0%   (0/16)0%   (0/4)
stopCollecting (): AbbaLockingDetector 0%   (0/1)0%   (0/16)0%   (0/7)
tick (): void 0%   (0/1)0%   (0/19)0%   (0/7)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, Version
3 * 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html). Initial Developer: H2 Group
5 */
6package org.h2.util;
7 
8import java.lang.management.ManagementFactory;
9import java.lang.management.MonitorInfo;
10import java.lang.management.ThreadInfo;
11import java.lang.management.ThreadMXBean;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Comparator;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19import java.util.WeakHashMap;
20 
21/**
22 * Utility to detect AB-BA deadlocks.
23 */
24public class AbbaLockingDetector implements Runnable {
25 
26    private int tickIntervalMs = 2;
27    private volatile boolean stop;
28 
29    private final ThreadMXBean threadMXBean =
30            ManagementFactory.getThreadMXBean();
31    private Thread thread;
32 
33    /**
34     * Map of (object A) -> ( map of (object locked before object A) ->
35     * (stack trace where locked) )
36     */
37    private final Map<String, Map<String, String>> lockOrdering =
38            new WeakHashMap<String, Map<String, String>>();
39    private final Set<String> knownDeadlocks = new HashSet<String>();
40 
41    /**
42     * Start collecting locking data.
43     *
44     * @return this
45     */
46    public AbbaLockingDetector startCollecting() {
47        thread = new Thread(this, "AbbaLockingDetector");
48        thread.setDaemon(true);
49        thread.start();
50        return this;
51    }
52 
53    /**
54     * Reset the state.
55     */
56    public synchronized void reset() {
57        lockOrdering.clear();
58        knownDeadlocks.clear();
59    }
60 
61    /**
62     * Stop collecting.
63     *
64     * @return this
65     */
66    public AbbaLockingDetector stopCollecting() {
67        stop = true;
68        if (thread != null) {
69            try {
70                thread.join();
71            } catch (InterruptedException e) {
72                // ignore
73            }
74            thread = null;
75        }
76        return this;
77    }
78 
79    @Override
80    public void run() {
81        while (!stop) {
82            try {
83                tick();
84            } catch (Throwable t) {
85                break;
86            }
87        }
88    }
89 
90    private void tick() {
91        if (tickIntervalMs > 0) {
92            try {
93                Thread.sleep(tickIntervalMs);
94            } catch (InterruptedException ex) {
95                // ignore
96            }
97        }
98 
99        ThreadInfo[] list = threadMXBean.dumpAllThreads(
100                // lockedMonitors
101                true,
102                // lockedSynchronizers
103                false);
104        processThreadList(list);
105    }
106 
107    private void processThreadList(ThreadInfo[] threadInfoList) {
108        final List<String> lockOrder = new ArrayList<String>();
109        for (ThreadInfo threadInfo : threadInfoList) {
110            lockOrder.clear();
111            generateOrdering(lockOrder, threadInfo);
112            if (lockOrder.size() > 1) {
113                markHigher(lockOrder, threadInfo);
114            }
115        }
116    }
117 
118    /**
119     * We cannot simply call getLockedMonitors because it is not guaranteed to
120     * return the locks in the correct order.
121     */
122    private static void generateOrdering(final List<String> lockOrder,
123            ThreadInfo info) {
124        final MonitorInfo[] lockedMonitors = info.getLockedMonitors();
125        Arrays.sort(lockedMonitors, new Comparator<MonitorInfo>() {
126            @Override
127            public int compare(MonitorInfo a, MonitorInfo b) {
128                return b.getLockedStackDepth() - a.getLockedStackDepth();
129            }
130        });
131        for (MonitorInfo mi : lockedMonitors) {
132            String lockName = getObjectName(mi);
133            if (lockName.equals("sun.misc.Launcher$AppClassLoader")) {
134                // ignore, it shows up everywhere
135                continue;
136            }
137            // Ignore locks which are locked multiple times in
138            // succession - Java locks are recursive.
139            if (!lockOrder.contains(lockName)) {
140                lockOrder.add(lockName);
141            }
142        }
143    }
144 
145    private synchronized void markHigher(List<String> lockOrder,
146            ThreadInfo threadInfo) {
147        String topLock = lockOrder.get(lockOrder.size() - 1);
148        Map<String, String> map = lockOrdering.get(topLock);
149        if (map == null) {
150            map = new WeakHashMap<String, String>();
151            lockOrdering.put(topLock, map);
152        }
153        String oldException = null;
154        for (int i = 0; i < lockOrder.size() - 1; i++) {
155            String olderLock = lockOrder.get(i);
156            Map<String, String> oldMap = lockOrdering.get(olderLock);
157            boolean foundDeadLock = false;
158            if (oldMap != null) {
159                String e = oldMap.get(topLock);
160                if (e != null) {
161                    foundDeadLock = true;
162                    String deadlockType = topLock + " " + olderLock;
163                    if (!knownDeadlocks.contains(deadlockType)) {
164                        System.out.println(topLock + " synchronized after \n " + olderLock
165                                + ", but in the past before\n" + "AFTER\n" +
166                                    getStackTraceForThread(threadInfo)
167                                + "BEFORE\n" + e);
168                        knownDeadlocks.add(deadlockType);
169                    }
170                }
171            }
172            if (!foundDeadLock && !map.containsKey(olderLock)) {
173                if (oldException == null) {
174                    oldException = getStackTraceForThread(threadInfo);
175                }
176                map.put(olderLock, oldException);
177            }
178        }
179    }
180 
181    /**
182     * Dump data in the same format as {@link ThreadInfo#toString()}, but with
183     * some modifications (no stack frame limit, and removal of uninteresting
184     * stack frames)
185     */
186    private static String getStackTraceForThread(ThreadInfo info) {
187        StringBuilder sb = new StringBuilder("\"" +
188                info.getThreadName() + "\"" + " Id=" +
189                info.getThreadId() + " " + info.getThreadState());
190        if (info.getLockName() != null) {
191            sb.append(" on " + info.getLockName());
192        }
193        if (info.getLockOwnerName() != null) {
194            sb.append(" owned by \"" + info.getLockOwnerName() +
195                    "\" Id=" + info.getLockOwnerId());
196        }
197        if (info.isSuspended()) {
198            sb.append(" (suspended)");
199        }
200        if (info.isInNative()) {
201            sb.append(" (in native)");
202        }
203        sb.append('\n');
204        final StackTraceElement[] stackTrace = info.getStackTrace();
205        final MonitorInfo[] lockedMonitors = info.getLockedMonitors();
206        boolean startDumping = false;
207        for (int i = 0; i < stackTrace.length; i++) {
208            StackTraceElement e = stackTrace[i];
209            if (startDumping) {
210                dumpStackTraceElement(info, sb, i, e);
211            }
212 
213            for (MonitorInfo mi : lockedMonitors) {
214                if (mi.getLockedStackDepth() == i) {
215                    // Only start dumping the stack from the first time we lock
216                    // something.
217                    // Removes a lot of unnecessary noise from the output.
218                    if (!startDumping) {
219                        dumpStackTraceElement(info, sb, i, e);
220                        startDumping = true;
221                    }
222                    sb.append("\t-  locked " + mi);
223                    sb.append('\n');
224                }
225            }
226        }
227        return sb.toString();
228    }
229 
230    private static void dumpStackTraceElement(ThreadInfo info,
231            StringBuilder sb, int i, StackTraceElement e) {
232        sb.append('\t').append("at ").append(e.toString());
233        sb.append('\n');
234        if (i == 0 && info.getLockInfo() != null) {
235            Thread.State ts = info.getThreadState();
236            switch (ts) {
237            case BLOCKED:
238                sb.append("\t-  blocked on " + info.getLockInfo());
239                sb.append('\n');
240                break;
241            case WAITING:
242                sb.append("\t-  waiting on " + info.getLockInfo());
243                sb.append('\n');
244                break;
245            case TIMED_WAITING:
246                sb.append("\t-  waiting on " + info.getLockInfo());
247                sb.append('\n');
248                break;
249            default:
250            }
251        }
252    }
253 
254    private static String getObjectName(MonitorInfo info) {
255        return info.getClassName() + "@" +
256                Integer.toHexString(info.getIdentityHashCode());
257    }
258 
259}

[all classes][org.h2.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov