blob: 6a5dac52b7867aa51782d40c3c1588900601a04a [file] [log] [blame]
Alex Light80777ed2019-12-17 17:07:33 +00001/*
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
17package art;
18
19import java.lang.reflect.Field;
20import java.util.*;
21import java.util.concurrent.CountDownLatch;
22public class Test2005 {
23 private static final int NUM_THREADS = 20;
24 private static final String DEFAULT_VAL = "DEFAULT_VALUE";
25
26 public static final class Transform {
27 public String greetingEnglish;
28 public Transform() {
29 this.greetingEnglish = "Hello";
30 }
31 public String sayHi() {
32 return greetingEnglish + " from " + Thread.currentThread().getName();
33 }
34 }
35
36 /**
37 * base64 encoded class/dex file for
38 * public static final class Transform {
39 * public String greetingEnglish;
40 * public String greetingFrench;
41 * public String greetingDanish;
42 * public String greetingJapanese;
43 *
44 * public Transform() {
45 * this.greetingEnglish = "Hello World";
46 * this.greetingFrench = "Bonjour le Monde";
47 * this.greetingDanish = "Hej Verden";
48 * this.greetingJapanese = "こんにちは世界";
49 * }
50 * public String sayHi() {
51 * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " +
52 * sayHiJapanese() + " from " + Thread.currentThread().getName();
53 * }
54 * public String sayHiEnglish() {
55 * return greetingEnglish;
56 * }
57 * public String sayHiDanish() {
58 * return greetingDanish;
59 * }
60 * public String sayHiJapanese() {
61 * return greetingJapanese;
62 * }
63 * public String sayHiFrench() {
64 * return greetingFrench;
65 * }
66 * }
67 */
68 private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
69 "ZGV4CjAzNQAgJ1QXHJ8PAODMKTV14wyH4oKGOMK1yyL4BgAAcAAAAHhWNBIAAAAAAAAAADQGAAAl"
70 + "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAAAABQAA+AEAAEoD"
71 + "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA"
72 + "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA"
73 + "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAMUFAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO"
74 + "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA"
75 + "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA"
76 + "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA"
77 + "BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAAJAYAAOsFAAAAAAAABwABAAIAAAAt"
78 + "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF"
79 + "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA"
80 + "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA"
81 + "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU"
82 + "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA"
83 + "WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG"
84 + "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA"
85 + "GExhcnQvVGVzdDIwMDUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MjAwNTsAIkxkYWx2aWsvYW5ub3Rh"
86 + "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph"
87 + "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp"
88 + "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDIwMDUuamF2YQAJVHJhbnNmb3JtAAFWAAth"
89 + "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz"
90 + "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt"
91 + "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph"
92 + "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAiwF+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWci"
93 + "LCJoYXMtY2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MSwic2hhLTEiOiI5N2RmNmVkNzlhNzQw"
94 + "ZWVhMzM4MmNiNWRhOTIyYmI1YmJjMDg2NDMzIiwidmVyc2lvbiI6IjIuMC45LWRldiJ9AAfjgZPj"
95 + "gpPjgavjgaHjga/kuJbnlYwAAgIBIhgBAgMCEwQZGxcRAAQBBQABAQEBAQEBAIGABOwFAQH4AwEB"
96 + "jAUBAaQFAQG8BQEB1AUAAAAAAAAAAgAAANwFAADiBQAAGAYAAAAAAAAAAAAAAAAAABAAAAAAAAAA"
97 + "AQAAAAAAAAABAAAAJQAAAHAAAAACAAAACQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAF"
98 + "AAAADAAAAHgBAAAGAAAAAQAAANgBAAABIAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQD"
99 + "AAACIAAAJQAAAEoDAAAEIAAAAgAAANwFAAAAIAAAAQAAAOsFAAADEAAAAgAAABQGAAAGIAAAAQAA"
100 + "ACQGAAAAEAAAAQAAADQGAAA=");
101
102 public static void run() throws Exception {
103 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
104 doTest();
105 }
106
107 public static final class MyThread extends Thread {
108 public MyThread(CountDownLatch delay, int id) {
109 super("Thread: " + id);
110 this.thr_id = id;
Alex Lightf1ede362020-01-09 11:00:11 -0800111 this.results = new HashSet<>();
Alex Light80777ed2019-12-17 17:07:33 +0000112 this.finish = false;
113 this.delay = delay;
114 }
115
116 public void run() {
117 delay.countDown();
Alex Lightf1ede362020-01-09 11:00:11 -0800118 while (!finish) {
Alex Light80777ed2019-12-17 17:07:33 +0000119 Transform t = new Transform();
120 results.add(t.sayHi());
121 }
122 }
123
124 public void finish() throws Exception {
125 finish = true;
126 this.join();
127 }
128
129 public void Check() throws Exception {
130 for (String s : results) {
131 if (!s.equals("Hello from " + getName())
132 && !s.equals("Hello, " + DEFAULT_VAL + ", " + DEFAULT_VAL + ", " + DEFAULT_VAL
133 + " from " + getName())
134 && !s.equals(
135 "Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + getName())) {
136 System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
137 }
138 }
139 }
140
Alex Lightf1ede362020-01-09 11:00:11 -0800141 public HashSet<String> results;
Alex Light80777ed2019-12-17 17:07:33 +0000142 public volatile boolean finish;
143 public int thr_id;
144 public CountDownLatch delay;
145 }
146
147 public static MyThread[] startThreads(int num_threads) throws Exception {
148 CountDownLatch cdl = new CountDownLatch(num_threads);
149 MyThread[] res = new MyThread[num_threads];
150 for (int i = 0; i < num_threads; i++) {
151 res[i] = new MyThread(cdl, i);
152 res[i].start();
153 }
154 cdl.await();
155 return res;
156 }
157 public static void finishThreads(MyThread[] thrs) throws Exception {
158 for (MyThread t : thrs) {
159 t.finish();
160 }
161 for (MyThread t : thrs) {
162 t.Check();
163 }
164 }
165
166 public static void doRedefinition() throws Exception {
167 // Get the current set of fields.
168 Field[] fields = Transform.class.getDeclaredFields();
169 // Get all the threads in the 'main' thread group
170 ThreadGroup mytg = Thread.currentThread().getThreadGroup();
171 Thread[] all_threads = new Thread[mytg.activeCount()];
172 mytg.enumerate(all_threads);
173 Set<Thread> thread_set = new HashSet<>(Arrays.asList(all_threads));
Alex Light93be70e2020-01-07 17:05:42 +0000174 // We don't want to suspend ourself, that would cause a deadlock.
Alex Light80777ed2019-12-17 17:07:33 +0000175 thread_set.remove(Thread.currentThread());
Alex Light93be70e2020-01-07 17:05:42 +0000176 // If some of the other threads finished between calling mytg.activeCount and enumerate we will
177 // have nulls. These nulls are interpreted as currentThread by SuspendThreadList so we want to
178 // get rid of them.
179 thread_set.remove(null);
Alex Light80777ed2019-12-17 17:07:33 +0000180 // Suspend them.
181 Suspension.suspendList(thread_set.toArray(new Thread[0]));
182 // Actual redefine.
183 Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
184 // Get the new fields.
185 Field[] new_fields = Transform.class.getDeclaredFields();
186 Set<Field> field_set = new HashSet(Arrays.asList(new_fields));
187 field_set.removeAll(Arrays.asList(fields));
188 // Initialize the new fields on the old objects and resume.
189 UpdateFieldValuesAndResumeThreads(thread_set.toArray(new Thread[0]),
190 Transform.class,
191 field_set.toArray(new Field[0]),
192 DEFAULT_VAL);
193 }
194
195 public static void doTest() throws Exception {
196 MyThread[] threads = startThreads(NUM_THREADS);
197
198 doRedefinition();
199 finishThreads(threads);
200 }
201 public static native void UpdateFieldValuesAndResumeThreads(
202 Thread[] t, Class<?> redefined_class, Field[] new_fields, String default_val);
203}