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