Andreas Gampe | 544416e | 2018-01-26 11:39:46 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.server; |
| 18 | |
| 19 | import static org.junit.Assert.assertEquals; |
| 20 | |
| 21 | import android.support.test.runner.AndroidJUnit4; |
| 22 | |
| 23 | import java.io.PrintWriter; |
| 24 | import java.io.StringWriter; |
| 25 | |
| 26 | import org.junit.Test; |
| 27 | import org.junit.runner.RunWith; |
| 28 | |
| 29 | /** |
| 30 | * Unit tests for {@link WatchdogDiagnostics} |
| 31 | */ |
| 32 | @RunWith(AndroidJUnit4.class) |
| 33 | public class WatchdogDiagnosticsTest { |
| 34 | |
| 35 | private static class TestThread1 extends Thread { |
| 36 | Object lock1; |
| 37 | Object lock2; |
| 38 | volatile boolean inB = false; |
| 39 | |
| 40 | public TestThread1(Object lock1, Object lock2) { |
| 41 | super("TestThread1"); |
| 42 | this.lock1 = lock1; |
| 43 | this.lock2 = lock2; |
| 44 | } |
| 45 | |
| 46 | @Override |
| 47 | public void run() { |
| 48 | a(); |
| 49 | } |
| 50 | |
| 51 | private void a() { |
| 52 | synchronized(lock1) { |
| 53 | b(); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | private void b() { |
| 58 | inB = true; |
| 59 | synchronized(lock2) { |
| 60 | // Nothing. |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | private static class TestThread2 extends Thread { |
| 66 | Object lock1; |
| 67 | Object lock2; |
| 68 | volatile boolean inY = false; |
| 69 | |
| 70 | public TestThread2(Object lock1, Object lock2) { |
| 71 | super("TestThread2"); |
| 72 | this.lock1 = lock1; |
| 73 | this.lock2 = lock2; |
| 74 | } |
| 75 | |
| 76 | @Override |
| 77 | public void run() { |
| 78 | x(); |
| 79 | } |
| 80 | |
| 81 | private void x() { |
| 82 | synchronized(lock1) { |
| 83 | y(); |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | private void y() { |
| 88 | synchronized(lock2) { |
| 89 | inY = true; |
| 90 | try { |
| 91 | lock2.wait(); |
| 92 | } catch (Exception exc) { |
| 93 | throw new RuntimeException(exc); |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | @Test |
| 100 | public void printAnnotatedStack() throws Exception { |
| 101 | // Preparation. |
| 102 | |
| 103 | Object heldLock1 = new Object(); |
| 104 | Object heldLock2 = 0; |
| 105 | Object waitLock = "123"; |
| 106 | |
| 107 | TestThread1 thread1 = new TestThread1(heldLock1, heldLock2); |
| 108 | TestThread2 thread2 = new TestThread2(heldLock2, waitLock); |
| 109 | |
| 110 | // Start the second thread, ensure it grabs heldLock2. |
| 111 | thread2.start(); |
| 112 | while(!thread2.inY) { |
| 113 | Thread.yield(); |
| 114 | } |
| 115 | |
| 116 | // Start the first thread, ensure it made progress. |
| 117 | thread1.start(); |
| 118 | while(!thread1.inB) { |
| 119 | Thread.yield(); |
| 120 | } |
| 121 | |
| 122 | // Now wait till both are no longer in runnable state. |
| 123 | while (thread1.getState() == Thread.State.RUNNABLE) { |
| 124 | Thread.yield(); |
| 125 | } |
| 126 | while (thread2.getState() == Thread.State.RUNNABLE) { |
| 127 | Thread.yield(); |
| 128 | } |
| 129 | |
| 130 | // Now do the test. |
| 131 | StringWriter stringBuffer = new StringWriter(); |
| 132 | PrintWriter print = new PrintWriter(stringBuffer, true); |
| 133 | |
| 134 | { |
| 135 | WatchdogDiagnostics.printAnnotatedStack(thread1, print); |
| 136 | |
| 137 | String output = stringBuffer.toString(); |
| 138 | String expected = |
| 139 | "TestThread1 annotated stack trace:\n" + |
| 140 | " at com.android.server.WatchdogDiagnosticsTest$TestThread1.b(" + |
| 141 | "WatchdogDiagnosticsTest.java:59)\n" + |
| 142 | " - waiting to lock <HASH> (a java.lang.Integer)\n" + |
| 143 | " at com.android.server.WatchdogDiagnosticsTest$TestThread1.a(" + |
| 144 | "WatchdogDiagnosticsTest.java:53)\n" + |
| 145 | " - locked <HASH> (a java.lang.Object)\n" + |
| 146 | " at com.android.server.WatchdogDiagnosticsTest$TestThread1.run(" + |
| 147 | "WatchdogDiagnosticsTest.java:48)\n"; |
| 148 | assertEquals(expected, filterHashes(output)); |
| 149 | } |
| 150 | |
| 151 | stringBuffer.getBuffer().setLength(0); |
| 152 | |
| 153 | { |
| 154 | WatchdogDiagnostics.printAnnotatedStack(thread2, print); |
| 155 | |
| 156 | String output = stringBuffer.toString(); |
| 157 | String expected = |
| 158 | "TestThread2 annotated stack trace:\n" + |
| 159 | " at java.lang.Object.wait(Native Method)\n" + |
| 160 | " at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" + |
| 161 | "WatchdogDiagnosticsTest.java:91)\n" + |
| 162 | " - locked <HASH> (a java.lang.String)\n" + |
| 163 | " at com.android.server.WatchdogDiagnosticsTest$TestThread2.x(" + |
| 164 | "WatchdogDiagnosticsTest.java:83)\n" + |
| 165 | " - locked <HASH> (a java.lang.Integer)\n" + |
| 166 | " at com.android.server.WatchdogDiagnosticsTest$TestThread2.run(" + |
| 167 | "WatchdogDiagnosticsTest.java:78)\n"; |
| 168 | assertEquals(expected, filterHashes(output)); |
| 169 | } |
| 170 | |
| 171 | // Let the threads finish. |
| 172 | synchronized (waitLock) { |
| 173 | waitLock.notifyAll(); |
| 174 | } |
| 175 | |
| 176 | thread1.join(); |
| 177 | thread2.join(); |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * A filter function that removes hash codes (which will change between tests and cannot be |
| 182 | * controlled.) |
| 183 | * <p> |
| 184 | * Note: leaves "<HASH>" to indicate that something was replaced. |
| 185 | */ |
| 186 | private static String filterHashes(String t) { |
| 187 | return t.replaceAll("<0x[0-9a-f]{8}>", "<HASH>"); |
| 188 | } |
| 189 | } |