1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 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 compiler.compilercontrol.share.scenario; 25 26 import compiler.compilercontrol.share.actions.BaseAction; 27 import compiler.compilercontrol.share.processors.CommandProcessor; 28 import compiler.compilercontrol.share.processors.LogProcessor; 29 import compiler.compilercontrol.share.processors.PrintProcessor; 30 import compiler.compilercontrol.share.processors.QuietProcessor; 31 import jdk.test.lib.Asserts; 32 import jdk.test.lib.OutputAnalyzer; 33 import jdk.test.lib.Pair; 34 import jdk.test.lib.ProcessTools; 35 import pool.PoolHelper; 36 37 import java.io.BufferedReader; 38 import java.io.IOException; 39 import java.io.InputStreamReader; 40 import java.io.PrintWriter; 41 import java.lang.reflect.Executable; 42 import java.net.ServerSocket; 43 import java.net.Socket; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.Collections; 47 import java.util.HashMap; 48 import java.util.LinkedHashSet; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Set; 52 import java.util.concurrent.Callable; 53 import java.util.function.Consumer; 54 55 /** 56 * Test scenario 57 */ 58 public final class Scenario { 59 private final List<String> vmopts; 60 private final Map<Executable, State> states; 61 private final List<Consumer<OutputAnalyzer>> processors; 62 63 private Scenario(List<String> vmopts, 64 Map<Executable, State> states, 65 List<CompileCommand> commands) { 66 this.vmopts = vmopts; 67 this.states = states; 68 processors = new ArrayList<>(); 69 processors.add(new LogProcessor(states)); 70 processors.add(new PrintProcessor(states)); 71 List<CompileCommand> nonQuieted = new ArrayList<>(); 72 List<CompileCommand> quieted = new ArrayList<>(); 73 boolean metQuiet = false; 74 for (CompileCommand cc : commands) { 75 metQuiet |= cc.command == Command.QUIET; 76 if (metQuiet) { 77 quieted.add(cc); 78 } else { 79 nonQuieted.add(cc); 80 } 81 } 82 processors.add(new CommandProcessor(nonQuieted)); 83 processors.add(new QuietProcessor(quieted)); 84 } 85 86 /** 87 * Executes scenario 88 */ 89 public void execute() { 90 // Construct execution command with CompileCommand and class 91 List<String> argsList = new ArrayList<>(); 92 // Add VM options 93 argsList.addAll(vmopts); 94 // Add class name that would be executed as a main in a separate VM 95 String classCmd = BaseAction.class.getName(); 96 // Add class name 97 Collections.addAll(argsList, classCmd); 98 int port = connectTestVM(); 99 argsList.add(String.valueOf(port)); 100 OutputAnalyzer output; 101 try { 102 output = ProcessTools.executeTestJvmAllArgs( 103 argsList.toArray(new String[argsList.size()])); 104 } catch (Throwable thr) { 105 throw new Error("Execution failed", thr); 106 } 107 output.shouldHaveExitValue(0); 108 for (Consumer<OutputAnalyzer> processor : processors) { 109 processor.accept(output); 110 } 111 } 112 113 /* 114 * Performs connection with a test VM, sends method states and performs 115 * JCMD operations on a test VM. 116 */ 117 private int connectTestVM() { 118 int port; 119 try { 120 ServerSocket serverSocket = new ServerSocket(0); 121 port = serverSocket.getLocalPort(); 122 Asserts.assertNotEquals(port, -1, "Socket is not bound: " + port); 123 new Thread(() -> { 124 try ( 125 Socket socket = serverSocket.accept(); 126 PrintWriter pw = new PrintWriter(socket.getOutputStream(), 127 true); 128 BufferedReader in = new BufferedReader( 129 new InputStreamReader(socket.getInputStream()))) { 130 // Get pid of the executed process 131 int pid = Integer.parseInt(in.readLine()); 132 Asserts.assertNE(pid, 0, "Got incorrect pid"); 133 // serialize and send state map 134 for (Executable x : states.keySet()) { 135 pw.println("{"); 136 pw.println(x.toGenericString()); 137 pw.println(states.get(x).toString()); 138 pw.println("}"); 139 } 140 } catch (IOException e) { 141 throw new Error("Failed to write data", e); 142 } 143 }).start(); 144 } catch (IOException e) { 145 throw new Error("IO error", e); 146 } 147 return port; 148 } 149 150 /** 151 * Gets states of methods for this scenario 152 * 153 * @return pairs of executable and its state 154 */ 155 public Map<Executable, State> getStates() { 156 return states; 157 } 158 159 /** 160 * Type of the command 161 */ 162 public static enum Type { 163 OPTION, 164 FILE, 165 DIRECTIVE 166 } 167 168 public static Builder getBuilder() { 169 return new Builder(); 170 } 171 172 public static class Builder { 173 private final Set<String> vmopts = new LinkedHashSet<>(); 174 private final Map<Type, StateBuilder> builders = new HashMap<>(); 175 176 public Builder() { 177 builders.put(Type.FILE, new CommandFileBuilder()); 178 builders.put(Type.OPTION, new CommandOptionsBuilder()); 179 builders.put(Type.DIRECTIVE, new DirectiveBuilder()); 180 } 181 182 public void add(CompileCommand compileCommand, Type type) { 183 String[] vmOptions = compileCommand.command.vmOpts; 184 Collections.addAll(vmopts, vmOptions); 185 StateBuilder builder = builders.get(type); 186 Asserts.assertNotNull(builder, "Missing builder for the type: " 187 + type); 188 builder.add(compileCommand); 189 } 190 191 public Scenario build() { 192 // Get all VM options 193 List<String> options = new ArrayList<>(); 194 options.addAll(vmopts); 195 for (StateBuilder builder : builders.values()) { 196 options.addAll(builder.getOptions()); 197 } 198 // Get states from each of the state builders 199 Map<Executable, State> commandFileStates = 200 builders.get(Type.FILE).getStates(); 201 Map<Executable, State> commandOptionStates = 202 builders.get(Type.OPTION).getStates(); 203 Map<Executable, State> directiveFileStates = 204 builders.get(Type.DIRECTIVE).getStates(); 205 // Merge states 206 List<Pair<Executable, Callable<?>>> methods = new PoolHelper() 207 .getAllMethods(); 208 Map<Executable, State> finalStates = new HashMap<>(); 209 for (Pair<Executable, Callable<?>> pair : methods) { 210 Executable x = pair.first; 211 State commandOptionState = commandOptionStates.get(x); 212 State commandFileState = commandFileStates.get(x); 213 State st = State.merge(commandOptionState, commandFileState); 214 State directiveState = directiveFileStates.get(x); 215 st = State.merge(st, directiveState); 216 finalStates.put(x, st); 217 } 218 /* Create a list of commands from options and file 219 to handle quiet command */ 220 List<CompileCommand> ccList = new ArrayList<>(); 221 ccList.addAll(builders.get(Type.OPTION).getCompileCommands()); 222 ccList.addAll(builders.get(Type.FILE).getCompileCommands()); 223 return new Scenario(options, finalStates, ccList); 224 } 225 } 226 }