6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package lib.jdb; 25 26 import jdk.test.lib.Utils; 27 import jdk.test.lib.process.OutputAnalyzer; 28 import jdk.test.lib.process.ProcessTools; 29 30 import java.io.IOException; 31 import java.nio.file.Files; 32 import java.nio.file.Paths; 33 import java.util.Arrays; 34 import java.util.LinkedList; 35 import java.util.List; 36 import java.util.concurrent.TimeUnit; 37 import java.util.concurrent.TimeoutException; 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 import java.util.stream.Collectors; 41 42 public abstract class JdbTest { 43 44 public static class LaunchOptions { 45 public final String debuggeeClass; 46 public final List<String> debuggeeOptions = new LinkedList<>(); 47 public String sourceFilename; 48 49 public LaunchOptions(String debuggeeClass) { 50 this.debuggeeClass = debuggeeClass; 51 } 52 public LaunchOptions addDebuggeeOption(String option) { 53 debuggeeOptions.add(option); 54 return this; 55 } 56 public LaunchOptions addDebuggeeOptions(String[] options) { 57 debuggeeOptions.addAll(Arrays.asList(options)); 58 return this; 59 } 60 public LaunchOptions setSourceFilename(String name) { 61 sourceFilename = name; 62 return this; 63 } 64 } 65 66 public JdbTest(LaunchOptions launchOptions) { 67 this.launchOptions = launchOptions; 68 } 69 public JdbTest(String debuggeeClass) { 70 this(new LaunchOptions(debuggeeClass)); 71 } 72 73 // sourceFilename is used by setBreakpoints and redefineClass 74 public JdbTest(String debuggeeClass, String sourceFilename) { 75 this(new LaunchOptions(debuggeeClass).setSourceFilename(sourceFilename)); 76 } 77 78 protected Jdb jdb; 79 protected Process debuggee; 80 private final List<String> debuggeeOutput = new LinkedList<>(); 81 private final LaunchOptions launchOptions; 82 83 // returns the whole jdb output as a string 84 public String getJdbOutput() { 85 return jdb == null ? "" : jdb.getJdbOutput(); 86 } 87 88 // returns the whole debuggee output as a string 89 public String getDebuggeeOutput() { 90 return debuggeeOutput.stream().collect(Collectors.joining(lineSeparator)); 91 } 92 93 public void run() { 94 try { 95 setup(); 96 runCases(); 97 } catch (Throwable e) { 98 jdb.log("======================================="); 99 jdb.log("Exception thrown during test execution: " + e.getMessage()); 100 jdb.log("======================================="); 101 throw e; 102 } finally { 103 shutdown(); 104 } 105 } 106 107 protected void setup() { 108 /* run debuggee as: 109 java -agentlib:jdwp=transport=dt_socket,address=0,server=n,suspend=y <debuggeeClass> 110 it reports something like : Listening for transport dt_socket at address: 60810 111 after that connect jdb by: 112 jdb -connect com.sun.jdi.SocketAttach:port=60810 113 */ 114 // launch debuggee 115 List<String> debuggeeArgs = new LinkedList<>(); 116 // specify address=0 to automatically select free port 117 debuggeeArgs.add("-agentlib:jdwp=transport=dt_socket,address=0,server=y,suspend=y"); 118 debuggeeArgs.addAll(launchOptions.debuggeeOptions); 119 debuggeeArgs.add(launchOptions.debuggeeClass); 120 ProcessBuilder pbDebuggee = ProcessTools.createJavaProcessBuilder(true, debuggeeArgs.toArray(new String[0])); 121 122 // debuggeeListen[0] - transport, debuggeeListen[1] - address 123 String[] debuggeeListen = new String[2]; 124 Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(\\d+)\\b"); 125 try { 126 debuggee = ProcessTools.startProcess("debuggee", pbDebuggee, 127 s -> debuggeeOutput.add(s), // output consumer 128 s -> { // warm-up predicate 129 Matcher m = listenRegexp.matcher(s); 130 if (!m.matches()) { 131 return false; 132 } 133 debuggeeListen[0] = m.group(1); 134 debuggeeListen[1] = m.group(2); 135 return true; 136 }, 137 30, TimeUnit.SECONDS); 138 } catch (IOException | InterruptedException | TimeoutException ex) { 139 throw new RuntimeException("failed to launch debuggee", ex); 140 } 141 142 // launch jdb 143 try { 144 jdb = new Jdb("-connect", "com.sun.jdi.SocketAttach:port=" + debuggeeListen[1]); 145 } catch (Throwable ex) { 146 // terminate debuggee if something went wrong 147 debuggee.destroy(); 148 throw ex; 149 } 150 // wait while jdb is initialized 151 jdb.waitForPrompt(1, false); 152 } 153 154 protected abstract void runCases(); 155 156 protected void shutdown() { 157 if (jdb != null) { 158 jdb.shutdown(); 159 } 160 // shutdown debuggee 161 if (debuggee != null && debuggee.isAlive()) { 162 try { 163 debuggee.waitFor(Utils.adjustTimeout(10), TimeUnit.SECONDS); 164 } catch (InterruptedException e) { 165 // ignore 166 } finally { 167 if (debuggee.isAlive()) { 168 debuggee.destroy(); 169 } 170 } 171 } 172 } 173 174 protected static final String lineSeparator = System.getProperty("line.separator"); 175 176 177 // Parses the specified source file for "@{id} breakpoint" tags and returns 178 // list of the line numbers containing the tag. 179 // Example: 180 // System.out.println("BP is here"); // @1 breakpoint 181 public static List<Integer> parseBreakpoints(String filePath, int id) { 182 final String pattern = "@" + id + " breakpoint"; 183 int lineNum = 1; 184 List<Integer> result = new LinkedList<>(); 185 try { 186 for (String line: Files.readAllLines(Paths.get(filePath))) { 187 if (line.contains(pattern)) { 188 result.add(lineNum); 189 } | 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package lib.jdb; 25 26 import jdk.test.lib.process.OutputAnalyzer; 27 28 import java.io.IOException; 29 import java.nio.file.Files; 30 import java.nio.file.Paths; 31 import java.util.Arrays; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.concurrent.TimeUnit; 35 import java.util.stream.Collectors; 36 37 public abstract class JdbTest { 38 39 public static class LaunchOptions { 40 public final String debuggeeClass; 41 public final List<String> debuggeeOptions = new LinkedList<>(); 42 public String sourceFilename; 43 44 public LaunchOptions(String debuggeeClass) { 45 this.debuggeeClass = debuggeeClass; 46 } 47 public LaunchOptions addDebuggeeOption(String option) { 48 debuggeeOptions.add(option); 49 return this; 50 } 51 public LaunchOptions addDebuggeeOptions(String[] options) { 52 debuggeeOptions.addAll(Arrays.asList(options)); 53 return this; 54 } 55 public LaunchOptions setSourceFilename(String name) { 56 sourceFilename = name; 57 return this; 58 } 59 } 60 61 public JdbTest(LaunchOptions launchOptions) { 62 this.launchOptions = launchOptions; 63 } 64 public JdbTest(String debuggeeClass) { 65 this(new LaunchOptions(debuggeeClass)); 66 } 67 68 // sourceFilename is used by setBreakpoints and redefineClass 69 public JdbTest(String debuggeeClass, String sourceFilename) { 70 this(new LaunchOptions(debuggeeClass).setSourceFilename(sourceFilename)); 71 } 72 73 protected Jdb jdb; 74 protected Debuggee debuggee; 75 private final LaunchOptions launchOptions; 76 77 // returns the whole jdb output as a string 78 public String getJdbOutput() { 79 return jdb == null ? "" : jdb.getJdbOutput(); 80 } 81 82 // returns the whole debuggee output as a string 83 public String getDebuggeeOutput() { 84 return debuggee == null ? "" : debuggee.getOutput(); 85 } 86 87 public void run() { 88 try { 89 setup(); 90 runCases(); 91 } catch (Throwable e) { 92 jdb.log("======================================="); 93 jdb.log("Exception thrown during test execution: " + e.getMessage()); 94 jdb.log("======================================="); 95 throw e; 96 } finally { 97 shutdown(); 98 } 99 } 100 101 protected void setup() { 102 /* run debuggee as: 103 java -agentlib:jdwp=transport=dt_socket,server=n,suspend=y <debuggeeClass> 104 it reports something like : Listening for transport dt_socket at address: 60810 105 after that connect jdb by: 106 jdb -connect com.sun.jdi.SocketAttach:port=60810 107 */ 108 // launch debuggee 109 debuggee = Debuggee.launcher(launchOptions.debuggeeClass) 110 .addOptions(launchOptions.debuggeeOptions) 111 .launch(); 112 113 // launch jdb 114 try { 115 jdb = new Jdb("-connect", "com.sun.jdi.SocketAttach:port=" + debuggee.getAddress()); 116 } catch (Throwable ex) { 117 // terminate debuggee if something went wrong 118 debuggee.shutdown(); 119 throw ex; 120 } 121 // wait while jdb is initialized 122 jdb.waitForPrompt(1, false); 123 } 124 125 protected abstract void runCases(); 126 127 protected void shutdown() { 128 if (jdb != null) { 129 jdb.shutdown(); 130 } 131 // shutdown debuggee 132 if (debuggee != null) { 133 if (!debuggee.waitFor(10, TimeUnit.SECONDS)) { 134 debuggee.shutdown(); 135 } 136 } 137 } 138 139 protected static final String lineSeparator = System.getProperty("line.separator"); 140 141 142 // Parses the specified source file for "@{id} breakpoint" tags and returns 143 // list of the line numbers containing the tag. 144 // Example: 145 // System.out.println("BP is here"); // @1 breakpoint 146 public static List<Integer> parseBreakpoints(String filePath, int id) { 147 final String pattern = "@" + id + " breakpoint"; 148 int lineNum = 1; 149 List<Integer> result = new LinkedList<>(); 150 try { 151 for (String line: Files.readAllLines(Paths.get(filePath))) { 152 if (line.contains(pattern)) { 153 result.add(lineNum); 154 } |