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