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. We set a
25   // 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") && e.getDeclaringClass().equals(Transform.class)) {
100       // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
101       // consistency.
102       line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
103     }
104     System.out.println("Breakpoint reached: " + e + " @ line=" + line);
105   }
106 
run()107   public static void run() throws Exception {
108     // Set up breakpoints
109     Breakpoint.stopBreakpointWatch(Thread.currentThread());
110     Breakpoint.startBreakpointWatch(
111         Test996.class,
112         Test996.class.getDeclaredMethod(
113             "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
114         Thread.currentThread());
115 
116     Transform t = new Transform();
117     Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
118     final long obsolete_breakpoint_location =
119         Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
120 
121     System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
122     long initial_breakpoint_location =
123         Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
124     Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
125 
126     System.out.println("Running transform without redefinition.");
127     t.run(() -> {});
128 
129     System.out.println("Running transform with redefinition.");
130     t.run(() -> {
131       System.out.println("Redefining calling function!");
132       // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
133       Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
134       System.out.println("Setting breakpoint on now obsolete method to line " +
135           TRANSFORM_BREAKPOINT_REDEFINED_LINE);
136       setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
137     });
138     System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
139     t.run(() -> {});
140 
141     System.out.println("Setting initial breakpoint on redefined method.");
142     long final_breakpoint_location =
143         Breakpoint.lineToLocation(non_obsolete_run_method,
144                                   TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
145     Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
146     t.run(() -> {});
147 
148     Breakpoint.stopBreakpointWatch(Thread.currentThread());
149   }
150 
setBreakpointOnObsoleteMethod(long location)151   public static native void setBreakpointOnObsoleteMethod(long location);
152 }
153