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