1 /*
2  * Copyright (C) 2017 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 java.lang.reflect.Executable;
20 import java.lang.reflect.Method;
21 import java.util.Base64;
22 
23 public class Test996 {
24     // The line we are going to break on. This should be the println in the Transform class.
25     // We set a breakpoint here after we have redefined the class.
26     public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
27 
28     // The line we initially set a breakpoint on. This should be the doNothing call. This should be
29     // cleared by the redefinition and should only be caught on the initial run.
30     public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
31 
32     // A function that doesn't do anything. Used for giving places to break on in a function.
doNothing()33     public static void doNothing() {}
34 
35     public static final class Transform {
run(Runnable r)36         public void run(Runnable r) {
37             r.run();
38             // Make sure we don't change anything above this line to keep all the breakpoint stuff
39             // working. We will be putting a breakpoint before this line in the runnable.
40             System.out.println("Should be after first breakpoint.");
41             // This is set as a breakpoint prior to redefinition. It should not be hit.
42             doNothing();
43         }
44     }
45 
46     /* ****************************************************************************************** */
47     // Try to keep all edits to this file below the above line. If edits need to be made above this
48     // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
49     // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
50 
51     public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
52 
53     // The base64 encoding of the following class. The redefined 'run' method should have the same
54     // instructions as the original. This means that the locations of each line should stay the same
55     // and the set of valid locations will not change. We use this to ensure that breakpoints are
56     // removed from the redefined method.
57     // public static final class Transform {
58     //     public void run(Runnable r) {
59     //         r.run();
60     //         System.out.println("Doing nothing transformed");
61     //         doNothing();  // try to catch non-removed breakpoints
62     //     }
63     // }
64     private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
65             "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
66             "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
67             "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
68             "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
69             "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
70             "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
71             "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
72             "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
73             "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
74             "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
75             "AAEABwAZABwAGQ==");
76     private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
77             "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
78             "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
79             "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
80             "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
81             "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
82             "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
83             "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
84             "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
85             "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
86             "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
87             "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
88             "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
89             "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
90             "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
91             "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
92             "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
93             "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
94             "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
95             "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
96 
notifyBreakpointReached(Thread thr, Executable e, long loc)97     public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
98         int line = Breakpoint.locationToLine(e, loc);
99         if (line == -1 && e.getName().equals("run")
100                 && e.getDeclaringClass().equals(Transform.class)) {
101             // RI always reports line = -1 for obsolete methods. Just replace it with the real line
102             // for consistency.
103             line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
104         }
105         System.out.println("Breakpoint reached: " + e + " @ line=" + line);
106     }
107 
run()108     public static void run() throws Exception {
109         // Set up breakpoints
110         Breakpoint.stopBreakpointWatch(Thread.currentThread());
111         Breakpoint.startBreakpointWatch(
112                 Test996.class,
113                 Test996.class.getDeclaredMethod(
114                         "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
115                 Thread.currentThread());
116 
117         Transform t = new Transform();
118         Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
119         final long obsolete_breakpoint_location = Breakpoint.lineToLocation(
120                 non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
121 
122         System.out.println(
123                 "Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
124         long initial_breakpoint_location = Breakpoint.lineToLocation(
125                 non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
126         Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
127 
128         System.out.println("Running transform without redefinition.");
129         t.run(() -> {});
130 
131         System.out.println("Running transform with redefinition.");
132         t.run(() -> {
133             System.out.println("Redefining calling function!");
134             // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
135             Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
136             System.out.println("Setting breakpoint on now obsolete method to line " +
137                     TRANSFORM_BREAKPOINT_REDEFINED_LINE);
138             setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
139         });
140         System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
141         t.run(() -> {});
142 
143         System.out.println("Setting initial breakpoint on redefined method.");
144         long final_breakpoint_location = Breakpoint.lineToLocation(
145                 non_obsolete_run_method, TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
146         Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
147         t.run(() -> {});
148 
149         Breakpoint.stopBreakpointWatch(Thread.currentThread());
150     }
151 
setBreakpointOnObsoleteMethod(long location)152     public static native void setBreakpointOnObsoleteMethod(long location);
153 }
154