changeset 1083:6c93505a6763

7901237: Benchmark list is corrupted when incremental compilation is used Summary: Merge benchmark entries during the benchmark generation.
author shade
date Mon, 12 Jan 2015 22:07:09 +0300
parents 868c073c1c49
children b99ec1daf32d
files jmh-core-ct/src/test/java/org/openjdk/jmh/ct/CompileTest.java jmh-core-ct/src/test/java/org/openjdk/jmh/ct/InMemoryGeneratorDestination.java jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/Benchmark1.java jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/Benchmark2.java jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/MultipleSessionsTest.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/BenchmarkGenerator.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/FileSystemDestination.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/GeneratorDestination.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceElementWarning.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceThrowableWarning.java jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceWarning.java jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java jmh-core/src/main/java/org/openjdk/jmh/runner/WorkloadParams.java jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java jmh-generator-annprocess/src/main/java/org/openjdk/jmh/generators/annotations/APGeneratorDestinaton.java
diffstat 15 files changed, 595 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/CompileTest.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/CompileTest.java	Mon Jan 12 22:07:09 2015 +0300
@@ -27,8 +27,6 @@
 import junit.framework.Assert;
 import org.openjdk.jmh.generators.asm.ASMGeneratorSource;
 import org.openjdk.jmh.generators.core.BenchmarkGenerator;
-import org.openjdk.jmh.generators.core.GeneratorDestination;
-import org.openjdk.jmh.generators.core.MetadataInfo;
 import org.openjdk.jmh.generators.reflection.RFGeneratorSource;
 
 import javax.tools.Diagnostic;
@@ -41,15 +39,10 @@
 import javax.tools.ToolProvider;
 import java.io.File;
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 public class CompileTest {
@@ -57,14 +50,14 @@
     private static final String GENERATOR_TYPE = System.getProperty("jmh.ct.generator", "notset");
 
     public static void assertFail(Class<?> klass) {
-        TestGeneratorDestination destination = doTest(klass);
+        InMemoryGeneratorDestination destination = doTest(klass);
         if (!destination.hasErrors()) {
             Assert.fail("Should have failed.");
         }
     }
 
     public static void assertFail(Class<?> klass, String error) {
-        TestGeneratorDestination destination = doTest(klass);
+        InMemoryGeneratorDestination destination = doTest(klass);
         if (!destination.hasErrors()) {
             Assert.fail("Should have failed.");
         }
@@ -78,7 +71,7 @@
     }
 
     public static void assertOK(Class<?> klass) {
-        TestGeneratorDestination destination = doTest(klass);
+        InMemoryGeneratorDestination destination = doTest(klass);
 
         if (destination.hasErrors()) {
             StringBuilder sb = new StringBuilder();
@@ -101,14 +94,11 @@
         }
 
         Collection<JavaSourceFromString> sources = new ArrayList<JavaSourceFromString>();
-        for (Map.Entry<String, StringWriter> e : destination.classBodies.entrySet()) {
-            String name = e.getKey();
-            String body = e.getValue().toString();
-            sources.add(new JavaSourceFromString(name, body));
+        for (Map.Entry<String, String> e : destination.getClasses().entrySet()) {
+            sources.add(new JavaSourceFromString(e.getKey(), e.getValue()));
         }
 
         JavaCompiler.CompilationTask task = javac.getTask(null, fm, diagnostics, null, null, sources);
-
         boolean success = task.call();
 
         if (!success) {
@@ -136,7 +126,7 @@
         }
     }
 
-    private static TestGeneratorDestination doTest(Class<?> klass) {
+    private static InMemoryGeneratorDestination doTest(Class<?> klass) {
         if (GENERATOR_TYPE.equalsIgnoreCase("reflection")) {
             return doTestReflection(klass);
         }
@@ -146,9 +136,9 @@
         throw new IllegalStateException("Unhandled compile test generator: " + GENERATOR_TYPE);
     }
 
-    private static TestGeneratorDestination doTestReflection(Class<?> klass) {
+    private static InMemoryGeneratorDestination doTestReflection(Class<?> klass) {
         RFGeneratorSource source = new RFGeneratorSource();
-        TestGeneratorDestination destination = new TestGeneratorDestination();
+        InMemoryGeneratorDestination destination = new InMemoryGeneratorDestination();
         source.processClasses(klass);
 
         BenchmarkGenerator gen = new BenchmarkGenerator();
@@ -157,9 +147,9 @@
         return destination;
     }
 
-    private static TestGeneratorDestination doTestAsm(Class<?> klass) {
+    private static InMemoryGeneratorDestination doTestAsm(Class<?> klass) {
         ASMGeneratorSource source = new ASMGeneratorSource();
-        TestGeneratorDestination destination = new TestGeneratorDestination();
+        InMemoryGeneratorDestination destination = new InMemoryGeneratorDestination();
 
         String name = "/" + klass.getCanonicalName().replaceAll("\\.", "/") + ".class";
         try {
@@ -174,52 +164,5 @@
         return destination;
     }
 
-    public static class TestGeneratorDestination implements GeneratorDestination {
-
-        List<String> errors = new ArrayList<String>();
-
-        private Map<String, StringWriter> classBodies = new HashMap<String, StringWriter>();
-
-        @Override
-        public Writer newResource(String resourcePath) throws IOException {
-            return new PrintWriter(System.out, true);
-        }
-
-        @Override
-        public Writer newClass(String className) throws IOException {
-            StringWriter sw = classBodies.get(className);
-            if (sw != null) {
-                throw new IllegalStateException("Already writing the class");
-            } else {
-                sw = new StringWriter();
-                classBodies.put(className, sw);
-            }
-            return new PrintWriter(sw, true);
-        }
-
-        @Override
-        public void printError(String message) {
-            errors.add(message);
-        }
-
-        @Override
-        public void printError(String message, MetadataInfo element) {
-            errors.add(message);
-        }
-
-        @Override
-        public void printError(String message, Throwable throwable) {
-            errors.add(message + ":\n" + throwable.toString());
-        }
-
-        public boolean hasErrors() {
-            return !errors.isEmpty();
-        }
-
-        public List<String> getErrors() {
-            return errors;
-        }
-    }
-
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/InMemoryGeneratorDestination.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.ct;
+
+import org.openjdk.jmh.generators.core.GeneratorDestination;
+import org.openjdk.jmh.generators.core.MetadataInfo;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class InMemoryGeneratorDestination implements GeneratorDestination {
+
+    private List<String> errors = new ArrayList<String>();
+    private List<String> warnings = new ArrayList<String>();
+
+    private Map<String, StringWriter> classBodies = new HashMap<String, StringWriter>();
+    private Map<String, StringWriter> resourceBodies = new HashMap<String, StringWriter>();
+
+    @Override
+    public Writer newResource(String resourcePath) throws IOException {
+        StringWriter sw = new StringWriter();
+        resourceBodies.put(resourcePath, sw);
+        return new PrintWriter(sw, true);
+    }
+
+    @Override
+    public Reader getResource(String resourcePath) throws IOException {
+        StringWriter sw = resourceBodies.get(resourcePath);
+        if (sw == null) {
+            throw new IOException("Does not exist: " + resourcePath);
+        }
+        return new StringReader(sw.toString());
+    }
+
+    @Override
+    public Writer newClass(String className) throws IOException {
+        StringWriter sw = new StringWriter();
+        classBodies.put(className, sw);
+        return new PrintWriter(sw, true);
+    }
+
+    @Override
+    public void printError(String message) {
+        errors.add(message);
+    }
+
+    @Override
+    public void printError(String message, MetadataInfo element) {
+        errors.add(message);
+    }
+
+    @Override
+    public void printError(String message, Throwable throwable) {
+        errors.add(message + ":\n" + throwable.toString());
+    }
+
+    public boolean hasErrors() {
+        return !errors.isEmpty();
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    @Override
+    public void printWarning(String message) {
+        warnings.add(message);
+    }
+
+    @Override
+    public void printWarning(String message, MetadataInfo element) {
+        warnings.add(message);
+    }
+
+    @Override
+    public void printWarning(String message, Throwable throwable) {
+        warnings.add(message + ":\n" + throwable.toString());
+    }
+
+    public boolean hasWarnings() {
+        return !warnings.isEmpty();
+    }
+
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    public Map<String, String> getClasses() {
+        Map<String, String> result = new HashMap<String, String>();
+        for (Map.Entry<String, StringWriter> e : classBodies.entrySet()) {
+            result.put(e.getKey(), e.getValue().toString());
+        }
+        return result;
+    }
+
+    public Map<String, String> getResources() {
+        Map<String, String> result = new HashMap<String, String>();
+        for (Map.Entry<String, StringWriter> e : resourceBodies.entrySet()) {
+            result.put(e.getKey(), e.getValue().toString());
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/Benchmark1.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.ct.multsession;
+
+import org.openjdk.jmh.annotations.Benchmark;
+
+public class Benchmark1 {
+
+    @Benchmark
+    public void test() {}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/Benchmark2.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.ct.multsession;
+
+import org.openjdk.jmh.annotations.Benchmark;
+
+public class Benchmark2 {
+
+    @Benchmark
+    public void test() {}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-ct/src/test/java/org/openjdk/jmh/ct/multsession/MultipleSessionsTest.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.ct.multsession;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.openjdk.jmh.ct.InMemoryGeneratorDestination;
+import org.openjdk.jmh.generators.core.BenchmarkGenerator;
+import org.openjdk.jmh.generators.reflection.RFGeneratorSource;
+import org.openjdk.jmh.runner.BenchmarkList;
+
+public class MultipleSessionsTest {
+
+    @Test
+    public void testAppend() {
+        InMemoryGeneratorDestination dst = new InMemoryGeneratorDestination();
+
+        {
+            RFGeneratorSource src = new RFGeneratorSource();
+            BenchmarkGenerator gen = new BenchmarkGenerator();
+
+            src.processClasses(Benchmark1.class);
+
+            gen.generate(src, dst);
+            gen.complete(src, dst);
+
+            Assert.assertFalse("First stage error", dst.hasErrors());
+            Assert.assertFalse("First stage warnings", dst.hasWarnings());
+
+            String[] list = dst.getResources().get(BenchmarkList.BENCHMARK_LIST.substring(1)).split("\n");
+            Assert.assertEquals("First stage should have only 1 benchmark", 1, list.length);
+        }
+
+        {
+            RFGeneratorSource src = new RFGeneratorSource();
+            BenchmarkGenerator gen = new BenchmarkGenerator();
+
+            src.processClasses(Benchmark2.class);
+
+            gen.generate(src, dst);
+            gen.complete(src, dst);
+
+            Assert.assertFalse("Second stage error", dst.hasErrors());
+            Assert.assertFalse("Second stage warnings", dst.hasWarnings());
+
+            String[] list = dst.getResources().get(BenchmarkList.BENCHMARK_LIST.substring(1)).split("\n");
+            Assert.assertEquals("Second stage should have 2 benchmarks", 2, list.length);
+        }
+    }
+
+    @Test
+    public void testOverwrite() {
+        InMemoryGeneratorDestination dst = new InMemoryGeneratorDestination();
+
+        {
+            RFGeneratorSource src = new RFGeneratorSource();
+            BenchmarkGenerator gen = new BenchmarkGenerator();
+
+            src.processClasses(Benchmark1.class);
+
+            gen.generate(src, dst);
+            gen.complete(src, dst);
+
+            Assert.assertFalse("First stage error", dst.hasErrors());
+            Assert.assertFalse("First stage warnings", dst.hasWarnings());
+
+            String[] list = dst.getResources().get(BenchmarkList.BENCHMARK_LIST.substring(1)).split("\n");
+            Assert.assertEquals("First stage should have only 1 benchmark", 1, list.length);
+        }
+
+        {
+            RFGeneratorSource src = new RFGeneratorSource();
+            BenchmarkGenerator gen = new BenchmarkGenerator();
+
+            src.processClasses(Benchmark1.class);
+
+            gen.generate(src, dst);
+            gen.complete(src, dst);
+
+            Assert.assertFalse("Second stage error", dst.hasErrors());
+            Assert.assertTrue("Second stage warnings", dst.hasWarnings());
+            boolean hasOurWarning = false;
+            for (String warning : dst.getWarnings()) {
+                hasOurWarning |= (warning.contains("Benchmark1") && warning.contains("overwriting"));
+            }
+            Assert.assertTrue("Should have our warning: " + dst.getWarnings(), hasOurWarning);
+
+            String[] list = dst.getResources().get(BenchmarkList.BENCHMARK_LIST.substring(1)).split("\n");
+            Assert.assertEquals("Second stage should have 1 benchmark", 1, list.length);
+        }
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/generators/core/BenchmarkGenerator.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/BenchmarkGenerator.java	Mon Jan 12 22:07:09 2015 +0300
@@ -52,6 +52,7 @@
 import org.openjdk.jmh.runner.BenchmarkListEntry;
 import org.openjdk.jmh.runner.Defaults;
 import org.openjdk.jmh.runner.InfraControl;
+import org.openjdk.jmh.util.FileUtils;
 import org.openjdk.jmh.util.HashMultimap;
 import org.openjdk.jmh.util.Multimap;
 import org.openjdk.jmh.util.SampleBuffer;
@@ -59,6 +60,7 @@
 import javax.annotation.Generated;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.Reader;
 import java.lang.annotation.IncompleteAnnotationException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -151,9 +153,23 @@
     public void complete(GeneratorSource source, GeneratorDestination destination) {
         compilerControl.finish(source, destination);
 
-        // Processing completed, final round. Print all added methods to file
+        // Processing completed, final round.
         try {
-            PrintWriter writer = new PrintWriter(destination.newResource(BenchmarkList.BENCHMARK_LIST.substring(1)));
+            // Collect all benchmark entries here
+            Set<BenchmarkListEntry> entries = new HashSet<BenchmarkListEntry>();
+
+            // Try to read the benchmark entries from previous generator sessions.
+            try {
+                Reader reader = destination.getResource(BenchmarkList.BENCHMARK_LIST.substring(1));
+                Collection<String> existingLines = FileUtils.readAllLines(reader);
+                for (String line : existingLines) {
+                    entries.add(new BenchmarkListEntry(line));
+                }
+            } catch (IOException e) {
+                // Expected in most cases, move on.
+            }
+
+            // Generate new benchmark entries, potentially overwriting the previous lines
             for (BenchmarkInfo info : benchmarkInfos) {
                 try {
                     MethodGroup group = info.methodGroup;
@@ -182,14 +198,26 @@
                                 group.getOperationsPerInvocation(),
                                 group.getTimeout()
                         );
-                        writer.println(br.toLine());
+
+                        if (entries.contains(br)) {
+                            destination.printWarning("Benchmark entry " + br + " already exists, overwriting");
+                            entries.remove(br);
+                        }
+
+                        entries.add(br);
                     }
                 } catch (GenerationException ge) {
                     destination.printError(ge.getMessage(), ge.getElement());
                 }
             }
 
+            // Write out the complete benchmark list
+            PrintWriter writer = new PrintWriter(destination.newResource(BenchmarkList.BENCHMARK_LIST.substring(1)));
+            for (BenchmarkListEntry entry : entries) {
+                writer.println(entry.toLine());
+            }
             writer.close();
+
         } catch (IOException ex) {
             destination.printError("Error writing benchmark list", ex);
         }
--- a/jmh-core/src/main/java/org/openjdk/jmh/generators/core/FileSystemDestination.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/FileSystemDestination.java	Mon Jan 12 22:07:09 2015 +0300
@@ -25,8 +25,10 @@
 package org.openjdk.jmh.generators.core;
 
 import java.io.File;
+import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.Reader;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -37,11 +39,13 @@
     private final File resourceDir;
     private final File sourceDir;
     private final List<SourceError> sourceErrors;
+    private final List<SourceWarning> sourceWarnings;
 
     public FileSystemDestination(File resourceDir, File sourceDir) {
         this.resourceDir = resourceDir;
         this.sourceDir = sourceDir;
         this.sourceErrors = new ArrayList<SourceError>();
+        this.sourceWarnings = new ArrayList<SourceWarning>();
     }
 
     @Override
@@ -55,6 +59,16 @@
     }
 
     @Override
+    public Reader getResource(String resourcePath) throws IOException {
+        String pathName = resourceDir.getAbsolutePath() + "/" + resourcePath;
+        File p = new File(pathName.substring(0, pathName.lastIndexOf("/")));
+        if (!p.isFile() && !p.exists()) {
+            throw new IOException("Unable to open " + pathName);
+        }
+        return new FileReader(new File(pathName));
+    }
+
+    @Override
     public Writer newClass(String className) throws IOException {
         String pathName = sourceDir.getAbsolutePath() + "/" + className.replaceAll("\\.", "/");
         File p = new File(pathName.substring(0, pathName.lastIndexOf("/")));
@@ -87,4 +101,27 @@
         return sourceErrors;
     }
 
+    @Override
+    public void printWarning(String message) {
+        sourceWarnings.add(new SourceWarning(message));
+    }
+
+    @Override
+    public void printWarning(String message, MetadataInfo element) {
+        sourceWarnings.add(new SourceElementWarning(message, element));
+    }
+
+    @Override
+    public void printWarning(String message, Throwable throwable) {
+        sourceWarnings.add(new SourceThrowableWarning(message, throwable));
+    }
+
+    public boolean hasWarnings() {
+        return !sourceWarnings.isEmpty();
+    }
+
+    public Collection<SourceWarning> getWarnings() {
+        return sourceWarnings;
+    }
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/generators/core/GeneratorDestination.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/GeneratorDestination.java	Mon Jan 12 22:07:09 2015 +0300
@@ -25,6 +25,7 @@
 package org.openjdk.jmh.generators.core;
 
 import java.io.IOException;
+import java.io.Reader;
 import java.io.Writer;
 
 /**
@@ -45,6 +46,16 @@
     Writer newResource(String resourcePath) throws IOException;
 
     /**
+     * Returns the Reader for the given resource.
+     * Callers are responsible for closing Readers.
+     *
+     * @param resourcePath resource path
+     * @return reader usable to read the resource
+     * @throws java.io.IOException if something wacked happens
+     */
+    Reader getResource(String resourcePath) throws IOException;
+
+    /**
      * Returns the Writer for the given class.
      * Callers are responsible for closing Writers.
      *
@@ -80,4 +91,30 @@
      */
     void printError(String message, Throwable throwable);
 
+    /**
+     * Print the warning.
+     * Calling this method should not terminate anything.
+     *
+     * @param message warning.
+     */
+    void printWarning(String message);
+
+    /**
+     * Print the warning.
+     * Calling this method should not terminate anything.
+     *
+     * @param message warning.
+     * @param element metadata element, to which this error is tailored
+     */
+    void printWarning(String message, MetadataInfo element);
+
+    /**
+     * Print the warning.
+     * Calling this method should not terminate anything.
+     *
+     * @param message warning.
+     * @param throwable exception causing the error
+     */
+    void printWarning(String message, Throwable throwable);
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceElementWarning.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2005, 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.generators.core;
+
+public class SourceElementWarning extends SourceWarning {
+
+    private final MetadataInfo element;
+
+    public SourceElementWarning(String message, MetadataInfo element) {
+        super(message);
+        this.element = element;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "\n   [" + element.toString() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceThrowableWarning.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2005, 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.generators.core;
+
+import org.openjdk.jmh.util.Utils;
+
+public class SourceThrowableWarning extends SourceWarning {
+
+    private final Throwable element;
+
+    public SourceThrowableWarning(String message, Throwable element) {
+        super(message);
+        this.element = element;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "\n" + Utils.throwableToString(element);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/generators/core/SourceWarning.java	Mon Jan 12 22:07:09 2015 +0300
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2005, 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 org.openjdk.jmh.generators.core;
+
+public class SourceWarning {
+
+    private final String message;
+
+    public SourceWarning(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public String toString() {
+        return message;
+    }
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java	Mon Jan 12 22:07:09 2015 +0300
@@ -233,12 +233,7 @@
 
     @Override
     public String toString() {
-        return "BenchmarkListEntry{" +
-                "userName='" + userName + '\'' +
-                ", generatedName='" + generatedName + '\'' +
-                ", mode=" + mode +
-                ", workloadParams=" + workloadParams +
-                '}';
+        return "{\'" + userName + "\', " + mode + ", " + workloadParams + "}";
     }
 
     public Optional<TimeValue> getWarmupTime() {
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/WorkloadParams.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/WorkloadParams.java	Mon Jan 12 22:07:09 2015 +0300
@@ -93,6 +93,11 @@
         return params != null ? params.hashCode() : 0;
     }
 
+    @Override
+    public String toString() {
+        return params.toString();
+    }
+
     public WorkloadParams copy() {
         return new WorkloadParams(params);
     }
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java	Mon Jan 12 22:07:09 2015 +0300
@@ -30,6 +30,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -138,23 +139,25 @@
         }
     }
 
-    public static Collection<String> readAllLines(File file) throws IOException {
-        FileInputStream fis = null;
-        try {
-            fis = new FileInputStream(file);
-            BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
+    public static Collection<String> readAllLines(Reader src) throws IOException {
+            BufferedReader reader = new BufferedReader(src);
             List<String> lines = new ArrayList<String>();
             String line;
             while ((line = reader.readLine()) != null) {
                 lines.add(line);
             }
             return lines;
-        } finally {
-            FileUtils.safelyClose(fis);
-        }
-
     }
 
+    public static Collection<String> readAllLines(File file) throws IOException {
+        FileReader fr = null;
+        try {
+            fr = new FileReader(file);
+            return readAllLines(fr);
+        } finally {
+            FileUtils.safelyClose(fr);
+        }
+    }
 
     public static Collection<File> getClasses(File root) {
         Collection<File> result = new ArrayList<File>();
--- a/jmh-generator-annprocess/src/main/java/org/openjdk/jmh/generators/annotations/APGeneratorDestinaton.java	Mon Jan 12 19:28:48 2015 +0300
+++ b/jmh-generator-annprocess/src/main/java/org/openjdk/jmh/generators/annotations/APGeneratorDestinaton.java	Mon Jan 12 22:07:09 2015 +0300
@@ -33,6 +33,7 @@
 import javax.tools.Diagnostic;
 import javax.tools.StandardLocation;
 import java.io.IOException;
+import java.io.Reader;
 import java.io.Writer;
 
 public class APGeneratorDestinaton implements GeneratorDestination {
@@ -51,6 +52,11 @@
     }
 
     @Override
+    public Reader getResource(String resourcePath) throws IOException {
+        return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", resourcePath).openReader(true);
+    }
+
+    @Override
     public Writer newClass(String className) throws IOException {
         return processingEnv.getFiler().createSourceFile(className).openWriter();
     }
@@ -70,4 +76,19 @@
         printError(message + " " + Utils.throwableToString(throwable));
     }
 
+    @Override
+    public void printWarning(String message) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message);
+    }
+
+    @Override
+    public void printWarning(String message, MetadataInfo element) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message, ((APMetadataInfo)element).getElement());
+    }
+
+    @Override
+    public void printWarning(String message, Throwable throwable) {
+        printWarning(message + " " + Utils.throwableToString(throwable));
+    }
+
 }