• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package art;
18 
19 import dalvik.system.InMemoryDexClassLoader;
20 import dalvik.system.VMRuntime;
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.Base64;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.function.Supplier;
26 
27 public class Test2001 {
28   private static final int NUM_THREADS = 20;
29   // Don't perform more than this many repeats per thread to prevent OOMEs
30   private static int TASK_COUNT_LIMIT = 1000;
31 
32   public static class Transform {
33     public String greetingEnglish;
34 
Transform()35     public Transform() {
36       this.greetingEnglish = "Hello";
37     }
38 
sayHi()39     public String sayHi() {
40       return greetingEnglish + " from " + Thread.currentThread().getName();
41     }
42   }
43 
44   /**
45    * base64 encoded class/dex file for
46    * public static class Transform {
47    *   public String greetingEnglish;
48    *   public String greetingFrench;
49    *   public String greetingDanish;
50    *   public String greetingJapanese;
51    *
52    *   public Transform() {
53    *     this.greetingEnglish = "Hello World";
54    *     this.greetingFrench = "Bonjour le Monde";
55    *     this.greetingDanish = "Hej Verden";
56    *     this.greetingJapanese = "こんにちは世界";
57    *   }
58    *   public String sayHi() {
59    *     return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
60    *   }
61    *   public String sayHiEnglish() {
62    *     return greetingEnglish;
63    *   }
64    *   public String sayHiDanish() {
65    *     return greetingDanish;
66    *   }
67    *   public String sayHiJapanese() {
68    *     return greetingJapanese;
69    *   }
70    *   public String sayHiFrench() {
71    *     return greetingFrench;
72    *   }
73    * }
74    */
75   private static final byte[] DEX_BYTES =
76       Base64.getDecoder()
77           .decode(
78               "ZGV4CjAzNQCnKPY06VRa4aM/zFW0MYLmRxT/NtXxD/H4BgAAcAAAAHhWNBIAAAAAAAAAADQGAAAl"
79                   + "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAAAABQAA+AEAAEoD"
80                   + "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA"
81                   + "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA"
82                   + "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAMUFAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO"
83                   + "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA"
84                   + "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA"
85                   + "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA"
86                   + "BwACABUAAAAHAAAAFgAAAAAAAAABAAAABAAAAAAAAAAQAAAAJAYAAOsFAAAAAAAABwABAAIAAAAt"
87                   + "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF"
88                   + "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA"
89                   + "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA"
90                   + "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU"
91                   + "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA"
92                   + "WxADAA4ACwAOPEtLS0sAEgAOABgADgAVAA4AHgAOABsADgAAAAABAAAABQAGIGZyb20gAAIsIAAG"
93                   + "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA"
94                   + "GExhcnQvVGVzdDIwMDEkVHJhbnNmb3JtOwAOTGFydC9UZXN0MjAwMTsAIkxkYWx2aWsvYW5ub3Rh"
95                   + "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph"
96                   + "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp"
97                   + "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDIwMDEuamF2YQAJVHJhbnNmb3JtAAFWAAth"
98                   + "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz"
99                   + "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt"
100                   + "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph"
101                   + "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAiwF+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWci"
102                   + "LCJoYXMtY2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MSwic2hhLTEiOiJmNjJiOGNlNmEwNTkw"
103                   + "MDU0ZWYzNGExYWVkZTcwYjQ2NjY4ZThiNDlmIiwidmVyc2lvbiI6IjIuMC4xLWRldiJ9AAfjgZPj"
104                   + "gpPjgavjgaHjga/kuJbnlYwAAgIBIhgBAgMCEwQJGxcRAAQBBQABAQEBAQEBAIGABOwFAQH4AwEB"
105                   + "jAUBAaQFAQG8BQEB1AUAAAAAAAAAAgAAANwFAADiBQAAGAYAAAAAAAAAAAAAAAAAABAAAAAAAAAA"
106                   + "AQAAAAAAAAABAAAAJQAAAHAAAAACAAAACQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAF"
107                   + "AAAADAAAAHgBAAAGAAAAAQAAANgBAAABIAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQD"
108                   + "AAACIAAAJQAAAEoDAAAEIAAAAgAAANwFAAAAIAAAAQAAAOsFAAADEAAAAgAAABQGAAAGIAAAAQAA"
109                   + "ACQGAAAAEAAAAQAAADQGAAA=");
110 
111   /*
112    * base64 encoded class/dex file for
113     package art;
114     import java.util.function.Supplier;
115     public class SubTransform extends art.Test2001.Transform implements Supplier<String> {
116       public SubTransform() {
117         super();
118       }
119       public String get() {
120         return "from SUBCLASS: " + super.sayHi();
121       }
122     }
123    */
124   private static final byte[] SUB_DEX_BYTES =
125       Base64.getDecoder()
126           .decode(
127               "ZGV4CjAzNQBawzkIDf9khFw00md41U4vIqRuhqBTjM+0BAAAcAAAAHhWNBIAAAAAAAAAAPwDAAAV"
128                   + "AAAAcAAAAAgAAADEAAAABAAAAOQAAAAAAAAAAAAAAAgAAAAUAQAAAQAAAFQBAABAAwAAdAEAAAIC"
129                   + "AAAKAgAADgIAABECAAAVAgAAKQIAAEMCAABiAgAAdgIAAIoCAAClAgAAxAIAAOMCAAD2AgAA+QIA"
130                   + "AAEDAAASAwAAFwMAAB4DAAAoAwAALwMAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAANAAAA"
131                   + "AgAAAAMAAAAAAAAAAgAAAAQAAAAAAAAAAwAAAAUAAAD8AQAADQAAAAcAAAAAAAAAAAADAAAAAAAA"
132                   + "AAAAEAAAAAAAAQAQAAAAAQADAAAAAAABAAEAEQAAAAUAAwAAAAAABQACAA4AAAAFAAEAEgAAAAAA"
133                   + "AAABAAAAAQAAAPQBAAAMAAAA7AMAAMsDAAAAAAAAAgABAAEAAADpAQAABQAAAG4QAgABAAwAEQAA"
134                   + "AAQAAQACAAAA7QEAABYAAABvEAQAAwAMACIBBQBwEAUAAQAaAg8AbiAGACEAbiAGAAEAbhAHAAEA"
135                   + "DAARAAEAAQABAAAA5AEAAAQAAABwEAMAAAAOAAYADjwABAAOAAkADgAAAAABAAAABgAAAAEAAAAE"
136                   + "AAY8aW5pdD4AAj47AAFMAAJMTAASTGFydC9TdWJUcmFuc2Zvcm07ABhMYXJ0L1Rlc3QyMDAxJFRy"
137                   + "YW5zZm9ybTsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABJMamF2YS9sYW5nL09iamVj"
138                   + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAdTGphdmEv"
139                   + "dXRpbC9mdW5jdGlvbi9TdXBwbGllcjsAHUxqYXZhL3V0aWwvZnVuY3Rpb24vU3VwcGxpZXI8ABFT"
140                   + "dWJUcmFuc2Zvcm0uamF2YQABVgAGYXBwZW5kAA9mcm9tIFNVQkNMQVNTOiAAA2dldAAFc2F5SGkA"
141                   + "CHRvU3RyaW5nAAV2YWx1ZQCLAX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsImhhcy1j"
142                   + "aGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImY2MmI4Y2U2YTA1OTAwNTRlZjM0"
143                   + "YTFhZWRlNzBiNDY2NjhlOGI0OWYiLCJ2ZXJzaW9uIjoiMi4wLjEtZGV2In0AAgIBExwEFwUXCxcI"
144                   + "FwEAAAECAIGABMwDAcEg9AIBAZADAAAAAAAAAQAAAL0DAADkAwAAAAAAAAAAAAAAAAAADwAAAAAA"
145                   + "AAABAAAAAAAAAAEAAAAVAAAAcAAAAAIAAAAIAAAAxAAAAAMAAAAEAAAA5AAAAAUAAAAIAAAAFAEA"
146                   + "AAYAAAABAAAAVAEAAAEgAAADAAAAdAEAAAMgAAADAAAA5AEAAAEQAAACAAAA9AEAAAIgAAAVAAAA"
147                   + "AgIAAAQgAAABAAAAvQMAAAAgAAABAAAAywMAAAMQAAACAAAA4AMAAAYgAAABAAAA7AMAAAAQAAAB"
148                   + "AAAA/AMAAA==");
149 
run()150   public static void run() throws Exception {
151     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
152     doTest();
153   }
154 
mkTransform()155   public static Supplier<String> mkTransform() {
156     try {
157       return (Supplier<String>)
158           (new InMemoryDexClassLoader(
159                   ByteBuffer.wrap(SUB_DEX_BYTES), Test2001.class.getClassLoader())
160               .loadClass("art.SubTransform")
161               .newInstance());
162     } catch (Exception e) {
163       return () -> {
164         return e.toString();
165       };
166     }
167   }
168 
169   public static final class MyThread extends Thread {
MyThread(CountDownLatch delay, int id)170     public MyThread(CountDownLatch delay, int id) {
171       super("Thread: " + id);
172       this.thr_id = id;
173       this.results = new ArrayList<>(TASK_COUNT_LIMIT);
174       this.finish = false;
175       this.delay = delay;
176     }
177 
run()178     public void run() {
179       delay.countDown();
180       while (!finish && results.size() < TASK_COUNT_LIMIT) {
181         Supplier<String> t = mkTransform();
182         results.add(t.get());
183       }
184     }
185 
finish()186     public void finish() throws Exception {
187       finish = true;
188       this.join();
189     }
190 
Check()191     public void Check() throws Exception {
192       for (String s : results) {
193         if (!s.equals("from SUBCLASS: Hello from " + getName())
194             && !s.equals("from SUBCLASS: Hello, null, null, null from " + getName())
195             && !s.equals(
196                 "from SUBCLASS: Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from "
197                     + getName())) {
198           System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
199         }
200       }
201     }
202 
203     public ArrayList<String> results;
204     public volatile boolean finish;
205     public int thr_id;
206     public CountDownLatch delay;
207   }
208 
startThreads(int num_threads)209   public static MyThread[] startThreads(int num_threads) throws Exception {
210     CountDownLatch cdl = new CountDownLatch(num_threads);
211     MyThread[] res = new MyThread[num_threads];
212     for (int i = 0; i < num_threads; i++) {
213       res[i] = new MyThread(cdl, i);
214       res[i].start();
215     }
216     cdl.await();
217     return res;
218   }
219 
finishThreads(MyThread[] thrs)220   public static void finishThreads(MyThread[] thrs) throws Exception {
221     for (MyThread t : thrs) {
222       t.finish();
223     }
224     for (MyThread t : thrs) {
225       t.Check();
226     }
227   }
228 
doTest()229   public static void doTest() throws Exception {
230     if (!VMRuntime.getRuntime().is64Bit()) {
231       // Reduce the task count limit on 32-bit VMs. Since we create a class loader for each task,
232       // we create too many linear alloc spaces. On 32-bit systems this might cause an OOM because
233       // we run out of virtual address space.
234       TASK_COUNT_LIMIT = 100;
235     }
236 
237     MyThread[] threads = startThreads(NUM_THREADS);
238     Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
239     finishThreads(threads);
240   }
241 }
242