blob: 6e76b679940ba51c72415e3d89601fd6a8880b4b [file] [log] [blame]
Andreas Gampe544416e2018-01-26 11:39:46 -08001/*
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
17package com.android.server;
18
19import static org.junit.Assert.assertEquals;
20
21import android.support.test.runner.AndroidJUnit4;
22
23import java.io.PrintWriter;
24import java.io.StringWriter;
25
26import org.junit.Test;
27import org.junit.runner.RunWith;
28
29/**
30 * Unit tests for {@link WatchdogDiagnostics}
31 */
32@RunWith(AndroidJUnit4.class)
33public 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}