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.method.MethodDescriptor;
  27 import compiler.compilercontrol.share.processors.CommandProcessor;
  28 import compiler.compilercontrol.share.processors.LogProcessor;
  29 import compiler.compilercontrol.share.processors.PrintDirectivesProcessor;
  30 import compiler.compilercontrol.share.processors.PrintProcessor;
  31 import jdk.test.lib.Asserts;
  32 import jdk.test.lib.OutputAnalyzer;
  33 import jdk.test.lib.Pair;
  34 import pool.PoolHelper;
  35 
  36 import java.lang.reflect.Executable;
  37 import java.util.ArrayList;
  38 import java.util.Collections;
  39 import java.util.HashMap;
  40 import java.util.LinkedHashSet;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Set;
  44 import java.util.concurrent.Callable;
  45 import java.util.function.Consumer;
  46 
  47 /**
  48  * Test scenario
  49  */
  50 public final class Scenario {
  51     private final boolean isValid;
  52     private final Map<Executable, State> states;
  53     private final List<Consumer<OutputAnalyzer>> processors;
  54     private final Executor executor;
  55     private final Consumer<List<OutputAnalyzer>> jcmdProcessor;
  56 
  57     private Scenario(boolean isValid,
  58                      List<String> vmopts,
  59                      Map<Executable, State> states,
  60                      List<CompileCommand> compileCommands,
  61                      List<JcmdCommand> jcmdCommands,
  62                      List<CompileCommand> directives) {
  63         this.isValid = isValid;
  64         this.states = states;
  65         processors = new ArrayList<>();
  66         processors.add(new LogProcessor(states));
  67         processors.add(new PrintProcessor(states));
  68         List<CompileCommand> nonQuieted = new ArrayList<>();
  69         List<CompileCommand> quieted = new ArrayList<>();
  70         boolean metQuiet = false;
  71         for (CompileCommand cc : compileCommands) {
  72             metQuiet |= cc.command == Command.QUIET;
  73             if (metQuiet) {
  74                 quieted.add(cc);
  75             } else {
  76                 nonQuieted.add(cc);
  77             }
  78         }
  79         processors.add(new CommandProcessor(nonQuieted, quieted));
  80         List<String> jcmdExecCommands = new ArrayList<>();
  81         boolean addCommandMet = false;
  82         boolean printCommandMet = false;
  83         for (JcmdCommand cmd : jcmdCommands) {
  84             switch (cmd.jcmdType) {
  85                 case ADD:
  86                     if (!addCommandMet) {
  87                         jcmdExecCommands.add(JcmdType.ADD.command);
  88                     }
  89                     addCommandMet = true;
  90                     break;
  91                 case PRINT:
  92                     printCommandMet = true;
  93                     break;
  94                 default:
  95                     jcmdExecCommands.add(cmd.jcmdType.command);
  96                     break;
  97             }
  98         }
  99         // Add print command only in the end to get directives printed
 100         if (printCommandMet) {
 101             jcmdExecCommands.add(JcmdType.PRINT.command);
 102         }
 103         jcmdProcessor = new PrintDirectivesProcessor(directives);
 104         executor = new Executor(isValid, vmopts, states, jcmdExecCommands);
 105     }
 106 
 107     /**
 108      * Executes scenario
 109      */
 110     public void execute() {
 111         List<OutputAnalyzer> outputList = executor.execute();
 112         // The first one contains output from the test VM
 113         OutputAnalyzer mainOuput = outputList.get(0);
 114         if (isValid) {
 115             mainOuput.shouldHaveExitValue(0);
 116             processors.forEach(processor -> processor.accept(mainOuput));
 117             // only the last output contains directives got from print command
 118             List<OutputAnalyzer> last = new ArrayList<>();
 119             last.add(outputList.get(outputList.size() - 1));
 120             jcmdProcessor.accept(last);
 121         } else {
 122             Asserts.assertNE(mainOuput.getExitValue(), 0, "VM should exit with "
 123                     + "error for incorrect directives");
 124             mainOuput.shouldContain("Parsing of compiler directives failed");
 125         }
 126     }
 127 
 128     /**
 129      * Gets states of methods for this scenario
 130      *
 131      * @return pairs of executable and its state
 132      */
 133     public Map<Executable, State> getStates() {
 134         return states;
 135     }
 136 
 137     public static enum Compiler {
 138         C1("c1"),
 139         C2("c2");
 140 
 141         public final String name;
 142 
 143         Compiler(String name) {
 144             this.name = name;
 145         }
 146     }
 147 
 148     /**
 149      * Type of diagnostic (jcmd) command
 150      */
 151     public static enum JcmdType {
 152         ADD("Compiler.directives_add " + Type.JCMD.fileName),
 153         PRINT("Compiler.directives_print"),
 154         CLEAR("Compiler.directives_clear"),
 155         REMOVE("Compiler.directives_remove");
 156 
 157         public final String command;
 158         private JcmdType(String command) {
 159             this.command = command;
 160         }
 161     }
 162 
 163     /**
 164      * Type of the compile command
 165      */
 166     public static enum Type {
 167         OPTION(""),
 168         FILE("command_file"),
 169         DIRECTIVE("directives.json"),
 170         JCMD("jcmd_directives.json") {
 171             @Override
 172             public CompileCommand createCompileCommand(Command command,
 173                     MethodDescriptor md, Compiler compiler) {
 174                 return new JcmdCommand(command, md, compiler, this,
 175                         JcmdType.ADD);
 176             }
 177         };
 178 
 179         public final String fileName;
 180 
 181         public CompileCommand createCompileCommand(Command command,
 182                 MethodDescriptor md, Compiler compiler) {
 183             return new CompileCommand(command, md, compiler, this);
 184         }
 185 
 186         private Type(String fileName) {
 187             this.fileName = fileName;
 188         }
 189     }
 190 
 191     public static Builder getBuilder() {
 192         return new Builder();
 193     }
 194 
 195     public static class Builder {
 196         private final Set<String> vmopts = new LinkedHashSet<>();
 197         private final Map<Type, StateBuilder<CompileCommand>> builders
 198                 = new HashMap<>();
 199         private final JcmdStateBuilder jcmdStateBuilder;
 200         private final List<JcmdCommand> jcmdCommands = new ArrayList<>();
 201 
 202         public Builder() {
 203             builders.put(Type.FILE, new CommandFileBuilder(Type.FILE.fileName));
 204             builders.put(Type.OPTION, new CommandOptionsBuilder());
 205             builders.put(Type.DIRECTIVE, new DirectiveBuilder(
 206                     Type.DIRECTIVE.fileName));
 207             jcmdStateBuilder = new JcmdStateBuilder(Type.JCMD.fileName);
 208         }
 209 
 210         public void add(CompileCommand compileCommand) {
 211             String[] vmOptions = compileCommand.command.vmOpts;
 212             Collections.addAll(vmopts, vmOptions);
 213             if (compileCommand.type == Type.JCMD) {
 214                 jcmdStateBuilder.add((JcmdCommand) compileCommand);
 215                 jcmdCommands.add((JcmdCommand) compileCommand);
 216             } else {
 217                 StateBuilder<CompileCommand> builder = builders.get(
 218                         compileCommand.type);
 219                 if (builder == null) {
 220                     throw new Error("TESTBUG: Missing builder for the type: "
 221                             + compileCommand.type);
 222                 }
 223                 builder.add(compileCommand);
 224             }
 225         }
 226 
 227         public Scenario build() {
 228             boolean isValid = true;
 229 
 230             // Get states from each of the state builders
 231             Map<Executable, State> commandFileStates
 232                     = builders.get(Type.FILE).getStates();
 233             Map<Executable, State> commandOptionStates
 234                     = builders.get(Type.OPTION).getStates();
 235             Map<Executable, State> directiveFileStates
 236                     = builders.get(Type.DIRECTIVE).getStates();
 237 
 238             // check if directives stack was cleared by jcmd
 239             boolean isClearedState = false;
 240             if (jcmdContainsCommand(JcmdType.CLEAR)) {
 241                 isClearedState = true;
 242             }
 243 
 244             // Merge states
 245             List<Pair<Executable, Callable<?>>> methods = new PoolHelper()
 246                     .getAllMethods();
 247             Map<Executable, State> finalStates = new HashMap<>();
 248             Map<Executable, State> jcmdStates = jcmdStateBuilder.getStates();
 249             for (Pair<Executable, Callable<?>> pair : methods) {
 250                 Executable x = pair.first;
 251                 State commandOptionState = commandOptionStates.get(x);
 252                 State commandFileState = commandFileStates.get(x);
 253                 State st = State.merge(commandOptionState, commandFileState);
 254                 if (!isClearedState) {
 255                     State directiveState = directiveFileStates.get(x);
 256                     State jcmdState = jcmdStates.get(x);
 257                     if (jcmdState != null) {
 258                         st = State.merge(st, jcmdState);
 259                     } else if (directiveState != null) {
 260                         st = State.merge(st, directiveState);
 261                     }
 262                 }
 263                 finalStates.put(x, st);
 264             }
 265 
 266             /*
 267              * Create a list of commands from options and file
 268              * to handle quiet command
 269              */
 270             List<CompileCommand> ccList = new ArrayList<>();
 271             ccList.addAll(builders.get(Type.OPTION).getCompileCommands());
 272             ccList.addAll(builders.get(Type.FILE).getCompileCommands());
 273 
 274             // Create a list of directives to check which one was printed
 275             List<CompileCommand> directives = new ArrayList<>();
 276             if (jcmdContainsCommand(JcmdType.PRINT)) {
 277                 if (!isClearedState) {
 278                     directives.addAll(builders.get(Type.DIRECTIVE)
 279                             .getCompileCommands());
 280                 }
 281                 directives.addAll(jcmdStateBuilder.getCompileCommands());
 282             }
 283 
 284             // Get all VM options after we build all states and files
 285             List<String> options = new ArrayList<>();
 286             options.addAll(vmopts);
 287             for (StateBuilder<?> builder : builders.values()) {
 288                 options.addAll(builder.getOptions());
 289                 isValid &= builder.isValid();
 290             }
 291             options.addAll(jcmdStateBuilder.getOptions());
 292             return new Scenario(isValid, options, finalStates, ccList,
 293                     jcmdCommands, directives);
 294         }
 295 
 296         // shows if jcmd have passed a specified jcmd command type
 297         private boolean jcmdContainsCommand(JcmdType type) {
 298             for (JcmdCommand jcmdCommand : jcmdCommands) {
 299                 if (jcmdCommand.jcmdType == type) {
 300                     return true;
 301                 }
 302             }
 303             return false;
 304         }
 305     }
 306 }