1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package art;
16 
17 import java.io.*;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.Method;
20 import java.util.concurrent.CountDownLatch;
21 import java.util.function.Consumer;
22 import java.util.function.IntConsumer;
23 import java.util.function.Supplier;
24 
25 public class Test1971 {
26   public static final boolean PRINT_STACK_TRACE = false;
27   public static final int NUM_THREADS = 3;
28 
29   public static class ReturnValue {
30     public final Thread target;
31     public final Thread creator;
32     public final Thread.State state;
33     public final StackTraceElement stack[];
34 
ReturnValue(Thread thr)35     public ReturnValue(Thread thr) {
36       target = thr;
37       creator = Thread.currentThread();
38       state = thr.getState();
39       stack = thr.getStackTrace();
40     }
41 
toString()42     public String toString() {
43       String stackTrace =
44           PRINT_STACK_TRACE
45               ?  ",\n\tstate: "
46                   + state
47                   + ",\n\tstack:\n"
48                   + safeDumpStackTrace(stack, "\t\t")
49                   + ",\n\t"
50               : "";
51       return this.getClass().getName()
52                   + " { thread: " + target.getName()
53                   + ", creator: " + creator.getName()
54                   + stackTrace + " }";
55     }
56   }
57 
safeDumpStackTrace(StackTraceElement st[], String prefix)58   private static String safeDumpStackTrace(StackTraceElement st[], String prefix) {
59     ByteArrayOutputStream baos = new ByteArrayOutputStream();
60     PrintStream os = new PrintStream(baos);
61     for (StackTraceElement e : st) {
62       os.println(
63           prefix
64               + e.getClassName()
65               + "."
66               + e.getMethodName()
67               + "("
68               + (e.isNativeMethod() ? "Native Method" : e.getFileName())
69               + ")");
70       if (e.getClassName().equals("art.Test1971") && e.getMethodName().equals("runTests")) {
71         os.println(prefix + "<Additional frames hidden>");
72         break;
73       }
74     }
75     os.flush();
76     return baos.toString();
77   }
78 
79   public static final class ForcedExit extends ReturnValue {
ForcedExit(Thread thr)80     public ForcedExit(Thread thr) {
81       super(thr);
82     }
83   }
84 
85   public static final class NormalExit extends ReturnValue {
NormalExit()86     public NormalExit() {
87       super(Thread.currentThread());
88     }
89   }
90 
runTest(Consumer<String> con)91   public static void runTest(Consumer<String> con) {
92     String thread_name = Thread.currentThread().getName();
93     con.accept("Thread: " + thread_name + " method returned: " + targetMethod());
94   }
targetMethod()95   public static Object targetMethod() {
96     // Set a breakpoint here and perform a force-early-return
97     return new NormalExit();
98   }
99 
run()100   public static void run() throws Exception {
101     SuspendEvents.setupTest();
102 
103     final String[] results = new String[NUM_THREADS];
104     final Thread[] targets = new Thread[NUM_THREADS];
105     final CountDownLatch cdl = new CountDownLatch(1);
106     final CountDownLatch startup = new CountDownLatch(NUM_THREADS);
107     for (int i = 0; i < NUM_THREADS; i++) {
108       final int idx = i;
109       targets[i] = new Thread(() -> {
110         try {
111           startup.countDown();
112           cdl.await();
113           runTest((s) -> {
114             synchronized(results) {
115               results[idx] = s;
116             }
117           });
118         } catch (Exception e) {
119           throw new Error("Failed to run test!", e);
120         }
121       }, "Test1971 - Thread " + i);
122       targets[i].start();
123     }
124     // Wait for the targets to start.
125     startup.await();
126     final Method targetMethod = Test1971.class.getDeclaredMethod("targetMethod");
127     final long targetLoc = 0;
128     // Setup breakpoints on all targets.
129     for (Thread thr : targets) {
130       try {
131         SuspendEvents.setupSuspendBreakpointFor(targetMethod, targetLoc, thr);
132       } catch (RuntimeException e) {
133         if (e.getMessage().equals("JVMTI_ERROR_DUPLICATE")) {
134           continue;
135         } else {
136           throw e;
137         }
138       }
139     }
140     // Allow tests to continue.
141     cdl.countDown();
142     // Wait for breakpoint to be hit on all threads.
143     for (Thread thr : targets) {
144       SuspendEvents.waitForSuspendHit(thr);
145     }
146     final CountDownLatch force_return_start = new CountDownLatch(NUM_THREADS);
147     final CountDownLatch force_return_latch = new CountDownLatch(1);
148     Thread[] returners = new Thread[NUM_THREADS];
149     for (int i = 0; i < NUM_THREADS; i++) {
150       final int idx = i;
151       final Thread target = targets[i];
152       returners[i] = new Thread(() -> {
153         try {
154           force_return_start.countDown();
155           force_return_latch.await();
156           if (idx % 5 != 0) {
157             NonStandardExit.forceEarlyReturn(target, new ForcedExit(target));
158           }
159           Suspension.resume(target);
160         } catch (Exception e) {
161           throw new Error("Failed to resume!", e);
162         }
163       }, "Concurrent thread force-returner - " + i);
164       returners[i].start();
165     }
166     // Force-early-return and resume on all threads simultaneously.
167     force_return_start.await();
168     force_return_latch.countDown();
169 
170     // Wait for all threads to finish.
171     for (int i = 0; i < NUM_THREADS; i++) {
172       returners[i].join();
173       targets[i].join();
174     }
175 
176     // Print results
177     for (int i = 0; i < NUM_THREADS; i++) {
178       System.out.println("Thread " + i + ": " + results[i]);
179     }
180   }
181 }
182