blob: 8c860de787a029043a72535d38a76a12012aa2f2 [file] [log] [blame]
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.compilercontrol.share.scenario;
import compiler.compilercontrol.share.method.MethodDescriptor;
import compiler.compilercontrol.share.pool.PoolHelper;
import compiler.compilercontrol.share.processors.CommandProcessor;
import compiler.compilercontrol.share.processors.LogProcessor;
import compiler.compilercontrol.share.processors.PrintDirectivesProcessor;
import compiler.compilercontrol.share.processors.PrintProcessor;
import jdk.test.lib.Asserts;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.util.Pair;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
/**
* Test scenario
*/
public final class Scenario {
private final boolean isValid;
private final Map<Executable, State> states;
private final List<Consumer<OutputAnalyzer>> processors;
private final Executor executor;
private final Consumer<List<OutputAnalyzer>> jcmdProcessor;
private Scenario(boolean isValid,
List<String> vmopts,
Map<Executable, State> states,
List<CompileCommand> compileCommands,
List<JcmdCommand> jcmdCommands,
List<CompileCommand> directives) {
this.isValid = isValid;
this.states = states;
processors = new ArrayList<>();
processors.add(new LogProcessor(states));
processors.add(new PrintProcessor(states));
List<CompileCommand> nonQuieted = new ArrayList<>();
List<CompileCommand> quieted = new ArrayList<>();
boolean metQuiet = false;
for (CompileCommand cc : compileCommands) {
metQuiet |= cc.command == Command.QUIET;
if (metQuiet) {
quieted.add(cc);
} else {
nonQuieted.add(cc);
}
}
processors.add(new CommandProcessor(nonQuieted, quieted));
List<String> jcmdExecCommands = new ArrayList<>();
boolean addCommandMet = false;
boolean printCommandMet = false;
for (JcmdCommand cmd : jcmdCommands) {
switch (cmd.jcmdType) {
case ADD:
if (!addCommandMet) {
jcmdExecCommands.add(JcmdType.ADD.command);
}
addCommandMet = true;
break;
case PRINT:
printCommandMet = true;
break;
default:
jcmdExecCommands.add(cmd.jcmdType.command);
break;
}
}
// Add print command only in the end to get directives printed
if (printCommandMet) {
jcmdExecCommands.add(JcmdType.PRINT.command);
}
jcmdProcessor = new PrintDirectivesProcessor(directives);
executor = new Executor(isValid, vmopts, states, jcmdExecCommands);
}
/**
* Executes scenario
*/
public void execute() {
List<OutputAnalyzer> outputList = executor.execute();
// The first one contains output from the test VM
OutputAnalyzer mainOuput = outputList.get(0);
if (isValid) {
mainOuput.shouldHaveExitValue(0);
processors.forEach(processor -> processor.accept(mainOuput));
// only the last output contains directives got from print command
List<OutputAnalyzer> last = new ArrayList<>();
last.add(outputList.get(outputList.size() - 1));
jcmdProcessor.accept(last);
} else {
Asserts.assertNE(mainOuput.getExitValue(), 0, "VM should exit with "
+ "error for incorrect directives");
mainOuput.shouldContain("Parsing of compiler directives failed");
}
}
/**
* Gets states of methods for this scenario
*
* @return pairs of executable and its state
*/
public Map<Executable, State> getStates() {
return states;
}
public static enum Compiler {
C1("c1"),
C2("c2");
public final String name;
Compiler(String name) {
this.name = name;
}
}
/**
* Type of diagnostic (jcmd) command
*/
public static enum JcmdType {
ADD("Compiler.directives_add " + Type.JCMD.fileName),
PRINT("Compiler.directives_print"),
CLEAR("Compiler.directives_clear"),
REMOVE("Compiler.directives_remove");
public final String command;
private JcmdType(String command) {
this.command = command;
}
}
/**
* Type of the compile command
*/
public static enum Type {
OPTION(""),
FILE("command_file"),
DIRECTIVE("directives.json"),
JCMD("jcmd_directives.json") {
@Override
public CompileCommand createCompileCommand(Command command,
MethodDescriptor md, Compiler compiler) {
return new JcmdCommand(command, md, compiler, this,
JcmdType.ADD);
}
};
public final String fileName;
public CompileCommand createCompileCommand(Command command,
MethodDescriptor md, Compiler compiler) {
return new CompileCommand(command, md, compiler, this);
}
private Type(String fileName) {
this.fileName = fileName;
}
}
public static Builder getBuilder() {
return new Builder();
}
public static class Builder {
private final Set<String> vmopts = new LinkedHashSet<>();
private final Map<Type, StateBuilder<CompileCommand>> builders
= new HashMap<>();
private final JcmdStateBuilder jcmdStateBuilder;
private final List<JcmdCommand> jcmdCommands = new ArrayList<>();
private boolean logCommandMet = false;
public Builder() {
addFlag("-Xmixed");
builders.put(Type.FILE, new CommandFileBuilder(Type.FILE.fileName));
builders.put(Type.OPTION, new CommandOptionsBuilder());
builders.put(Type.DIRECTIVE, new DirectiveBuilder(
Type.DIRECTIVE.fileName));
jcmdStateBuilder = new JcmdStateBuilder(Type.JCMD.fileName);
}
public void addFlag(String flag) {
vmopts.add(flag);
}
public void add(CompileCommand compileCommand) {
String[] vmOptions = compileCommand.command.vmOpts;
Collections.addAll(vmopts, vmOptions);
if (compileCommand.command == Command.LOG) {
logCommandMet = true;
}
if (compileCommand.type == Type.JCMD) {
jcmdStateBuilder.add((JcmdCommand) compileCommand);
jcmdCommands.add((JcmdCommand) compileCommand);
} else {
StateBuilder<CompileCommand> builder = builders.get(
compileCommand.type);
if (builder == null) {
throw new Error("TESTBUG: Missing builder for the type: "
+ compileCommand.type);
}
builder.add(compileCommand);
}
}
public Scenario build() {
boolean isValid = true;
// Get states from each of the state builders
Map<Executable, State> commandFileStates
= builders.get(Type.FILE).getStates();
Map<Executable, State> commandOptionStates
= builders.get(Type.OPTION).getStates();
Map<Executable, State> directiveFileStates
= builders.get(Type.DIRECTIVE).getStates();
// check if directives stack was cleared by jcmd
boolean isClearedState = false;
if (jcmdContainsCommand(JcmdType.CLEAR)) {
isClearedState = true;
}
// Merge states
List<Pair<Executable, Callable<?>>> methods = new PoolHelper()
.getAllMethods();
Map<Executable, State> finalStates = new HashMap<>();
Map<Executable, State> jcmdStates = jcmdStateBuilder.getStates();
for (Pair<Executable, Callable<?>> pair : methods) {
Executable x = pair.first;
State commandOptionState = commandOptionStates.get(x);
State commandFileState = commandFileStates.get(x);
State st = State.merge(commandOptionState, commandFileState);
if (!isClearedState) {
State directiveState = directiveFileStates.get(x);
State jcmdState = jcmdStates.get(x);
if (jcmdState != null) {
st = State.merge(st, jcmdState);
} else if (directiveState != null) {
st = State.merge(st, directiveState);
}
}
finalStates.put(x, st);
}
/*
* Create a list of commands from options and file
* to handle quiet command
*/
List<CompileCommand> ccList = new ArrayList<>();
ccList.addAll(builders.get(Type.OPTION).getCompileCommands());
ccList.addAll(builders.get(Type.FILE).getCompileCommands());
// Create a list of directives to check which one was printed
List<CompileCommand> directives = new ArrayList<>();
if (jcmdContainsCommand(JcmdType.PRINT)) {
if (!isClearedState) {
directives.addAll(builders.get(Type.DIRECTIVE)
.getCompileCommands());
}
directives.addAll(jcmdStateBuilder.getCompileCommands());
}
// Get all VM options after we build all states and files
List<String> options = new ArrayList<>();
options.addAll(vmopts);
for (StateBuilder<?> builder : builders.values()) {
options.addAll(builder.getOptions());
isValid &= builder.isValid();
}
options.addAll(jcmdStateBuilder.getOptions());
/*
* Update final states if LogCompilation is enabled and
* there is no any log command, then all methods should be logged
*/
boolean isLogComp = vmopts.stream()
.anyMatch(opt -> opt.contains("-XX:+LogCompilation"));
if (isLogComp && !logCommandMet) {
finalStates.entrySet()
.forEach(entry -> entry.getValue().setLog(true));
}
return new Scenario(isValid, options, finalStates, ccList,
jcmdCommands, directives);
}
// shows if jcmd have passed a specified jcmd command type
private boolean jcmdContainsCommand(JcmdType type) {
for (JcmdCommand jcmdCommand : jcmdCommands) {
if (jcmdCommand.jcmdType == type) {
return true;
}
}
return false;
}
}
}