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