1 /* 2 * Copyright (C) 2019 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 import art.Redefinition; 18 import java.lang.reflect.Method; 19 import java.util.Arrays; 20 import java.util.Base64; 21 import java.util.concurrent.CountDownLatch; 22 import java.util.concurrent.Phaser; 23 import java.util.function.Consumer; 24 25 public class Main { 26 public static final int NUM_THREADS = 10; 27 public static final boolean PRINT = false; 28 29 // import java.util.function.Consumer; 30 // 31 // class Transform { 32 // public void sayHi(Consumer<Consumer<String>> r, Consumer<String> reporter) { 33 // reporter.accept("goodbye - Start method sayHi"); 34 // r.accept(reporter); 35 // reporter.accept("goodbye - End method sayHi"); 36 // } 37 // } 38 private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( 39 "yv66vgAAADUAGwoABgARCAASCwATABQIABUHABYHABcBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" 40 + "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBAD0oTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjtM" 41 + "amF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOylWAQAJU2lnbmF0dXJlAQCEKExqYXZhL3V0aWwv" 42 + "ZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjxMamF2YS9sYW5n" 43 + "L1N0cmluZzs+Oz47TGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjxMamF2YS9sYW5nL1N0cmlu" 44 + "Zzs+OylWAQAKU291cmNlRmlsZQEADlRyYW5zZm9ybS5qYXZhDAAHAAgBABxnb29kYnllIC0gU3Rh" 45 + "cnQgbWV0aG9kIHNheUhpBwAYDAAZABoBABpnb29kYnllIC0gRW5kIG1ldGhvZCBzYXlIaQEACVRy" 46 + "YW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIB" 47 + "AAZhY2NlcHQBABUoTGphdmEvbGFuZy9PYmplY3Q7KVYAIAAFAAYAAAAAAAIAAAAHAAgAAQAJAAAA" 48 + "HQABAAEAAAAFKrcAAbEAAAABAAoAAAAGAAEAAAAHAAEACwAMAAIACQAAADwAAgADAAAAGCwSArkA" 49 + "AwIAKyy5AAMCACwSBLkAAwIAsQAAAAEACgAAABIABAAAAAkACAAKAA8ACwAXAAwADQAAAAIADgAB" 50 + "AA8AAAACABA="); 51 52 private static final byte[] DEX_BYTES = Base64.getDecoder().decode( 53 "ZGV4CjAzNQA8rBr8fYSjBsIDrOiAknAnKu+2xbIe3RAsBAAAcAAAAHhWNBIAAAAAAAAAAHQDAAAU" 54 + "AAAAcAAAAAUAAADAAAAAAwAAANQAAAAAAAAAAAAAAAQAAAD4AAAAAQAAABgBAAD0AgAAOAEAAJwB" 55 + "AACfAQAApwEAAK0BAACzAQAAwAEAAN8BAADzAQAABwIAACYCAABFAgAAVQIAAFgCAABcAgAAYQIA" 56 + "AGkCAACFAgAAowIAAKoCAACxAgAABAAAAAUAAAAGAAAACAAAAAsAAAALAAAABAAAAAAAAAAMAAAA" 57 + "BAAAAIwBAAANAAAABAAAAJQBAAAAAAAAAQAAAAAAAgARAAAAAgAAAAEAAAADAAEADgAAAAAAAAAA" 58 + "AAAAAgAAAAAAAAAKAAAAXAMAAD8DAAAAAAAAAQABAAEAAAB8AQAABAAAAHAQAgAAAA4ABAADAAIA" 59 + "AACAAQAADgAAABoAEAByIAMAAwByIAMAMgAaAg8AciADACMADgAHAA4ACQIAAA5aPFoAAAAAAQAA" 60 + "AAIAAAACAAAAAwADAAEoAAY8aW5pdD4ABD47KVYABD47PjsAC0xUcmFuc2Zvcm07AB1MZGFsdmlr" 61 + "L2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0" 62 + "cmluZzsAHUxqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7AB1MamF2YS91dGlsL2Z1bmN0aW9u" 63 + "L0NvbnN1bWVyPAAOVHJhbnNmb3JtLmphdmEAAVYAAlZMAANWTEwABmFjY2VwdAAaZ29vZGJ5ZSAt" 64 + "IEVuZCBtZXRob2Qgc2F5SGkAHGdvb2RieWUgLSBTdGFydCBtZXRob2Qgc2F5SGkABXNheUhpAAV2" 65 + "YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEi" 66 + "OiI3MTExYTM1YmFlNmQ1MTg1ZGNmYjMzOGQ2MTA3NGFjYTg0MjZjMDA2IiwidmVyc2lvbiI6IjEu" 67 + "NS4xNC1kZXYifQACAQESHAgXABcJFwkXBxcDFwkXBxcCAAABAQCAgAS4AgEB0AIAAAAAAAAAAQAA" 68 + "ACkDAABQAwAAAAAAAAEAAAAAAAAAAQAAAFQDAAAPAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAA" 69 + "AgAAAAUAAADAAAAAAwAAAAMAAADUAAAABQAAAAQAAAD4AAAABgAAAAEAAAAYAQAAASAAAAIAAAA4" 70 + "AQAAAyAAAAIAAAB8AQAAARAAAAIAAACMAQAAAiAAABQAAACcAQAABCAAAAEAAAApAwAAACAAAAEA" 71 + "AAA/AwAAAxAAAAIAAABQAwAABiAAAAEAAABcAwAAABAAAAEAAAB0AwAA"); 72 73 // A class that we can use to keep track of the output of this test. 74 private static class TestWatcher implements Consumer<String> { 75 private StringBuilder sb; 76 private String thread; TestWatcher(String thread)77 public TestWatcher(String thread) { 78 sb = new StringBuilder(); 79 this.thread = thread; 80 } 81 82 @Override accept(String s)83 public void accept(String s) { 84 String msg = thread + ": \t" + s; 85 maybePrint(msg); 86 sb.append(msg); 87 sb.append('\n'); 88 } 89 getOutput()90 public String getOutput() { 91 return sb.toString(); 92 } 93 clear()94 public void clear() { 95 sb = new StringBuilder(); 96 } 97 } 98 main(String[] args)99 public static void main(String[] args) throws Exception { 100 doTest(new Transform()); 101 } 102 103 private static boolean interpreting = true; 104 doTest(Transform t)105 public static void doTest(Transform t) throws Exception { 106 TestWatcher[] watchers = new TestWatcher[NUM_THREADS]; 107 for (int i = 0; i < NUM_THREADS; i++) { 108 watchers[i] = new TestWatcher("Thread " + i); 109 } 110 111 // This just prints something out to show we are running the Runnable. 112 Consumer<Consumer<String>> say_nothing = (Consumer<String> w) -> { 113 w.accept("Not doing anything here"); 114 }; 115 116 // Run ensureJitCompiled here since it might get GCd 117 ensureJitCompiled(Transform.class, "sayHi"); 118 final CountDownLatch arrive = new CountDownLatch(NUM_THREADS); 119 final CountDownLatch depart = new CountDownLatch(1); 120 Consumer<Consumer<String>> request_redefine = (Consumer<String> w) -> { 121 try { 122 arrive.countDown(); 123 w.accept("Requesting redefinition"); 124 depart.await(); 125 } catch (Exception e) { 126 throw new RuntimeException("Failed to do something", e); 127 } 128 }; 129 Thread redefinition_thread = new RedefinitionThread(arrive, depart); 130 redefinition_thread.start(); 131 Thread[] threads = new Thread[NUM_THREADS]; 132 for (int i = 0; i < NUM_THREADS; i++) { 133 threads[i] = new TestThread(t, watchers[i], say_nothing, request_redefine); 134 threads[i].start(); 135 } 136 redefinition_thread.join(); 137 Arrays.stream(threads).forEach((thr) -> { 138 try { 139 thr.join(); 140 } catch (Exception e) { 141 throw new RuntimeException("Failed to join: ", e); 142 } 143 }); 144 Arrays.stream(watchers).forEach((w) -> { System.out.println(w.getOutput()); }); 145 } 146 147 private static class RedefinitionThread extends Thread { 148 private CountDownLatch arrivalLatch; 149 private CountDownLatch departureLatch; RedefinitionThread(CountDownLatch arrival, CountDownLatch departure)150 public RedefinitionThread(CountDownLatch arrival, CountDownLatch departure) { 151 super("Redefine thread!"); 152 this.arrivalLatch = arrival; 153 this.departureLatch = departure; 154 } 155 run()156 public void run() { 157 try { 158 this.arrivalLatch.await(); 159 maybePrint("REDEFINITION THREAD: redefining something!"); 160 Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); 161 maybePrint("REDEFINITION THREAD: redefined something!"); 162 this.departureLatch.countDown(); 163 } catch (Exception e) { 164 e.printStackTrace(System.out); 165 throw new RuntimeException("Failed to redefine", e); 166 } 167 } 168 } 169 maybePrint(String s)170 private static synchronized void maybePrint(String s) { 171 if (PRINT) { 172 System.out.println(s); 173 } 174 } 175 176 private static class TestThread extends Thread { 177 private Transform t; 178 private TestWatcher w; 179 private Consumer<Consumer<String>> do_nothing; 180 private Consumer<Consumer<String>> request_redefinition; TestThread(Transform t, TestWatcher w, Consumer<Consumer<String>> do_nothing, Consumer<Consumer<String>> request_redefinition)181 public TestThread(Transform t, 182 TestWatcher w, 183 Consumer<Consumer<String>> do_nothing, 184 Consumer<Consumer<String>> request_redefinition) { 185 super(); 186 this.t = t; 187 this.w = w; 188 this.do_nothing = do_nothing; 189 this.request_redefinition = request_redefinition; 190 } 191 run()192 public void run() { 193 w.clear(); 194 t.sayHi(do_nothing, w); 195 t.sayHi(request_redefinition, w); 196 t.sayHi(do_nothing, w); 197 } 198 } 199 ensureJitCompiled(Class c, String name)200 private static native void ensureJitCompiled(Class c, String name); 201 } 202