1fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe/*
2fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * Copyright (C) 2017 The Android Open Source Project
3fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe *
4fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * Licensed under the Apache License, Version 2.0 (the "License");
5fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * you may not use this file except in compliance with the License.
6fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * You may obtain a copy of the License at
7fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe *
8fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe *      http://www.apache.org/licenses/LICENSE-2.0
9fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe *
10fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * Unless required by applicable law or agreed to in writing, software
11fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * distributed under the License is distributed on an "AS IS" BASIS,
12fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * See the License for the specific language governing permissions and
14fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe * limitations under the License.
15fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe */
16fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
17fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.lang.Thread.State;
18fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.lang.reflect.Method;
19fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.Arrays;
20fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.LinkedList;
21fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.List;
22fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.Map;
23fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.concurrent.BrokenBarrierException;
24fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampeimport java.util.concurrent.CyclicBarrier;
25fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
26fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampepublic class Main {
27fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
28fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    static class Runner implements Runnable {
29fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        List<Object> locks;
30fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        List<CyclicBarrier> barriers;
31fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
32fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        public Runner(List<Object> locks, List<CyclicBarrier> barriers) {
33fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            this.locks = locks;
34fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            this.barriers = barriers;
35fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
36fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
37fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        @Override
38fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        public void run() {
39fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            step(locks, barriers);
40fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
41fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
42fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        private void step(List<Object> l, List<CyclicBarrier> b) {
43fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            if (l.isEmpty()) {
44fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                // Nothing to do, sleep indefinitely.
45fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                try {
46fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    Thread.sleep(100000000);
47fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                } catch (InterruptedException e) {
48fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    throw new RuntimeException(e);
49fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                }
50fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            } else {
51fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                Object lockObject = l.remove(0);
52fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                CyclicBarrier barrierObject = b.remove(0);
53fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
54fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                if (lockObject == null) {
55fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    // No lock object: only take barrier, recurse.
56fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    try {
57fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        barrierObject.await();
58fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    } catch (InterruptedException | BrokenBarrierException e) {
59fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        throw new RuntimeException(e);
60fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    }
61fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    step(l, b);
62fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                } else if (barrierObject != null) {
63fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    // Have barrier: sync, wait and recurse.
64fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    synchronized(lockObject) {
65fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        try {
66fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            barrierObject.await();
67fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        } catch (InterruptedException | BrokenBarrierException e) {
68fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            throw new RuntimeException(e);
69fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        }
70fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        step(l, b);
71fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    }
72fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                } else {
73fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    // Sync, and get next step (which is assumed to have object and barrier).
74fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    synchronized (lockObject) {
75fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        Object lockObject2 = l.remove(0);
76fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        CyclicBarrier barrierObject2 = b.remove(0);
77fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        synchronized(lockObject2) {
78fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            try {
79fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                                barrierObject2.await();
80fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            } catch (InterruptedException | BrokenBarrierException e) {
81fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                                throw new RuntimeException(e);
82fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            }
83fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                            step(l, b);
84fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                        }
85fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    }
86fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                }
87fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            }
88fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
89fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
90fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
91fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    public static void main(String[] args) throws Exception {
92fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        try {
93fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            testCluster1();
94fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        } catch (Exception e) {
95fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            Map<Thread,StackTraceElement[]> stacks = Thread.getAllStackTraces();
96fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            for (Map.Entry<Thread,StackTraceElement[]> entry : stacks.entrySet()) {
97fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                System.out.println(entry.getKey());
98fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                System.out.println(Arrays.toString(entry.getValue()));
99fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            }
100fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            throw e;
101fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
102fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
103fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
104fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void testCluster1() throws Exception {
105fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        // Test setup (at deadlock):
106fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //
107fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        // Thread 1:
108fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //   #0 step: synchornized(o3) { synchronized(o2) }
109fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //   #1 step: synchronized(o1)
110fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //
111fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        // Thread 2:
112fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //   #0 step: synchronized(o1)
113fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //   #1 step: synchronized(o4) { synchronized(o2) }
114fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        //
115fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        LinkedList<Object> l1 = new LinkedList<>();
116fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        LinkedList<CyclicBarrier> b1 = new LinkedList<>();
117fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        LinkedList<Object> l2 = new LinkedList<>();
118fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        LinkedList<CyclicBarrier> b2 = new LinkedList<>();
119fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
120fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object o1 = new Object();
121fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object o2 = new Object();
122fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object o3 = new Object();
123fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object o4 = new Object();
124fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
125fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l1.add(o1);
126fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l1.add(o3);
127fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l1.add(o2);
128fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l2.add(o4);
129fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l2.add(o2);
130fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        l2.add(o1);
131fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
132fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        CyclicBarrier c1 = new CyclicBarrier(3);
133fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        CyclicBarrier c2 = new CyclicBarrier(2);
134fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b1.add(c1);
135fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b1.add(null);
136fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b1.add(c2);
137fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b2.add(null);
138fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b2.add(c1);
139fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        b2.add(c2);
140fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
141fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Thread t1 = new Thread(new Runner(l1, b1));
142fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        t1.setDaemon(true);
143fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        t1.start();
144fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Thread t2 = new Thread(new Runner(l2, b2));
145fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        t2.setDaemon(true);
146fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        t2.start();
147fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
148fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        c1.await();
149fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
150fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        waitNotRunnable(t1);
151fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        waitNotRunnable(t2);
152fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Thread.sleep(250);    // Unfortunately this seems necessary. :-(
153fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
154fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        // Thread 1.
155fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        {
156fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            Object[] stack1 = getAnnotatedStack(t1);
157fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertBlockedOn(stack1[0], o2);              // Blocked on o2.
158fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertLocks(stack1[0], o3);                  // Locked o3.
159fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertStackTraceElementStep(stack1[0]);
160fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
161fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertBlockedOn(stack1[1], null);            // Frame can't be blocked.
162fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertLocks(stack1[1], o1);                  // Locked o1.
163fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertStackTraceElementStep(stack1[1]);
164fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
165fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
166fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        // Thread 2.
167fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        {
168fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            Object[] stack2 = getAnnotatedStack(t2);
169fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertBlockedOn(stack2[0], o1);              // Blocked on o1.
170fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertLocks(stack2[0]);                      // Nothing locked.
171fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertStackTraceElementStep(stack2[0]);
172fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
173fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertBlockedOn(stack2[1], null);            // Frame can't be blocked.
174fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertLocks(stack2[1], o4, o2);              // Locked o4, o2.
175fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            assertStackTraceElementStep(stack2[1]);
176fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
177fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
178fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
179fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void waitNotRunnable(Thread t) throws InterruptedException {
180fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        while (t.getState() == State.RUNNABLE) {
181fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            Thread.sleep(100);
182fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
183fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
184fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
185fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static Object[] getAnnotatedStack(Thread t) throws Exception {
186fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Class<?> vmStack = Class.forName("dalvik.system.VMStack");
187fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Method m = vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class);
188fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        return (Object[]) m.invoke(null, t);
189fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
190fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
191fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void assertEquals(Object o1, Object o2) {
192fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        if (o1 != o2) {
193fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            throw new RuntimeException("Expected " + o1 + " == " + o2);
194fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
195fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
196fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void assertLocks(Object fromTrace, Object... locks) throws Exception {
197fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object fieldValue = fromTrace.getClass().getDeclaredMethod("getHeldLocks").
198fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                invoke(fromTrace);
199fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        assertEquals((Object[]) fieldValue,
200fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                (locks == null) ? null : (locks.length == 0 ? null : locks));
201fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
202fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void assertBlockedOn(Object fromTrace, Object block) throws Exception {
203fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object fieldValue = fromTrace.getClass().getDeclaredMethod("getBlockedOn").
204fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                invoke(fromTrace);
205fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        assertEquals(fieldValue, block);
206fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
207fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void assertEquals(Object[] o1, Object[] o2) {
208fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        if (!Arrays.equals(o1, o2)) {
209fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            throw new RuntimeException(
210fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                    "Expected " + Arrays.toString(o1) + " == " + Arrays.toString(o2));
211fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
212fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
213fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    private static void assertStackTraceElementStep(Object o) throws Exception {
214fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        Object fieldValue = o.getClass().getDeclaredMethod("getStackTraceElement").invoke(o);
215fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        if (fieldValue instanceof StackTraceElement) {
216fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            StackTraceElement elem = (StackTraceElement) fieldValue;
217fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            if (!elem.getMethodName().equals("step")) {
218fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe                throw new RuntimeException("Expected step method");
219fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            }
220fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe            return;
221fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        }
222fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe        throw new RuntimeException("Expected StackTraceElement " + fieldValue + " / " + o);
223fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe    }
224fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe}
225fb6b0b1b04081f9ef7a240f702d8ce4e61a02e9fAndreas Gampe
226