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