aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2015-12-07 23:10:29 +0000
committerandroid-build-merger <android-build-merger@google.com>2015-12-07 23:10:29 +0000
commitf18823a974f84d583b620f7e48ab0b103f818e3b (patch)
tree647c3bc71c5f4aaded306e94acccb6b093efe11a
parent610a0d3081101f7c69d762eda68884b76b999c1b (diff)
parente236301e5fc778bffe1748ed80d7936e6c807012 (diff)
downloadcaliper-f18823a974f84d583b620f7e48ab0b103f818e3b.tar.gz
Upgrade to latest (at the time of writing) version of Caliper
am: e236301e5f * commit 'e236301e5fc778bffe1748ed80d7936e6c807012': Upgrade to latest (at the time of writing) version of Caliper
-rw-r--r--.gitignore6
-rw-r--r--Android.mk80
-rw-r--r--README.android9
-rw-r--r--caliper/pom.xml228
-rw-r--r--caliper/src/main/java/com/google/caliper/AfterExperiment.java40
-rw-r--r--caliper/src/main/java/com/google/caliper/All.java (renamed from caliper/src/main/java/com/google/caliper/UploadResults.java)14
-rw-r--r--caliper/src/main/java/com/google/caliper/AllocationMeasurer.java165
-rw-r--r--caliper/src/main/java/com/google/caliper/Arguments.java420
-rw-r--r--caliper/src/main/java/com/google/caliper/BeforeExperiment.java40
-rw-r--r--caliper/src/main/java/com/google/caliper/Benchmark.java94
-rw-r--r--caliper/src/main/java/com/google/caliper/CaliperRc.java63
-rw-r--r--caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java67
-rw-r--r--caliper/src/main/java/com/google/caliper/ConsoleReport.java427
-rw-r--r--caliper/src/main/java/com/google/caliper/CountingPrintStream.java77
-rw-r--r--caliper/src/main/java/com/google/caliper/DalvikVm.java62
-rw-r--r--caliper/src/main/java/com/google/caliper/DebugMeasurer.java42
-rw-r--r--caliper/src/main/java/com/google/caliper/Environment.java63
-rw-r--r--caliper/src/main/java/com/google/caliper/EnvironmentGetter.java178
-rw-r--r--caliper/src/main/java/com/google/caliper/InProcessRunner.java117
-rw-r--r--caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/Json.java226
-rw-r--r--caliper/src/main/java/com/google/caliper/LogConstants.java43
-rw-r--r--caliper/src/main/java/com/google/caliper/Measurement.java93
-rw-r--r--caliper/src/main/java/com/google/caliper/MeasurementSet.java264
-rw-r--r--caliper/src/main/java/com/google/caliper/Measurer.java44
-rw-r--r--caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/Param.java8
-rw-r--r--caliper/src/main/java/com/google/caliper/Parameter.java202
-rw-r--r--caliper/src/main/java/com/google/caliper/Result.java56
-rw-r--r--caliper/src/main/java/com/google/caliper/ResultsReader.java64
-rw-r--r--caliper/src/main/java/com/google/caliper/Run.java93
-rw-r--r--caliper/src/main/java/com/google/caliper/Runner.java438
-rw-r--r--caliper/src/main/java/com/google/caliper/Scenario.java80
-rw-r--r--caliper/src/main/java/com/google/caliper/ScenarioResult.java88
-rw-r--r--caliper/src/main/java/com/google/caliper/ScenarioSelection.java253
-rw-r--r--caliper/src/main/java/com/google/caliper/SimpleBenchmark.java227
-rw-r--r--caliper/src/main/java/com/google/caliper/StandardVm.java46
-rw-r--r--caliper/src/main/java/com/google/caliper/TimeMeasurer.java180
-rw-r--r--caliper/src/main/java/com/google/caliper/TypeConverter.java65
-rw-r--r--caliper/src/main/java/com/google/caliper/UserException.java196
-rw-r--r--caliper/src/main/java/com/google/caliper/Vm.java46
-rw-r--r--caliper/src/main/java/com/google/caliper/VmFactory.java47
-rw-r--r--caliper/src/main/java/com/google/caliper/Xml.java160
-rw-r--r--caliper/src/main/java/com/google/caliper/XmlUtils.java49
-rw-r--r--caliper/src/main/java/com/google/caliper/api/AfterRep.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/api/BeforeRep.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/api/Footprint.java38
-rw-r--r--caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java38
-rw-r--r--caliper/src/main/java/com/google/caliper/api/ResultProcessor.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java27
-rw-r--r--caliper/src/main/java/com/google/caliper/api/VmOptions.java38
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java37
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java70
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java66
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java83
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java29
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/LogMessage.java24
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java64
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java30
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java217
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/Renderer.java (renamed from caliper/src/main/java/com/google/caliper/MeasurementType.java)11
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java52
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java40
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java49
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java60
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java46
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java68
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java59
-rw-r--r--caliper/src/main/java/com/google/caliper/bridge/package-info.java20
-rw-r--r--caliper/src/main/java/com/google/caliper/config/CaliperConfig.java211
-rw-r--r--caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java98
-rw-r--r--caliper/src/main/java/com/google/caliper/config/ConfigModule.java47
-rw-r--r--caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java107
-rw-r--r--caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java46
-rw-r--r--caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java107
-rw-r--r--caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java97
-rw-r--r--caliper/src/main/java/com/google/caliper/config/VmConfig.java144
-rw-r--r--caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/json/GsonModule.java80
-rw-r--r--caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java66
-rw-r--r--caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java66
-rw-r--r--caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java117
-rw-r--r--caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java39
-rw-r--r--caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java72
-rw-r--r--caliper/src/main/java/com/google/caliper/memory/Chain.java204
-rw-r--r--caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java260
-rw-r--r--caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java223
-rw-r--r--caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java66
-rw-r--r--caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java41
-rw-r--r--caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java147
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Defaults.java32
-rw-r--r--caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Host.java129
-rw-r--r--caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java125
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Measurement.java130
-rw-r--r--caliper/src/main/java/com/google/caliper/model/PersistentHashing.java (renamed from caliper/src/test/java/com/google/caliper/AllTests.java)24
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Run.java115
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Scenario.java144
-rw-r--r--caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java39
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Trial.java169
-rw-r--r--caliper/src/main/java/com/google/caliper/model/Value.java81
-rw-r--r--caliper/src/main/java/com/google/caliper/model/VmSpec.java128
-rw-r--r--caliper/src/main/java/com/google/caliper/model/package-info.java25
-rw-r--r--caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/options/CaliperOptions.java39
-rw-r--r--caliper/src/main/java/com/google/caliper/options/CommandLineParser.java444
-rw-r--r--caliper/src/main/java/com/google/caliper/options/OptionsModule.java83
-rw-r--r--caliper/src/main/java/com/google/caliper/options/ParsedOptions.java417
-rw-r--r--caliper/src/main/java/com/google/caliper/options/package-info.java20
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/Platform.java147
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java27
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java43
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java98
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java73
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/jvm/JvmModule.java (renamed from caliper/src/main/java/com/google/caliper/ConfigurationException.java)23
-rw-r--r--caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java208
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java229
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java152
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java199
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java102
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java55
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java102
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java86
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/CaliperMain.java138
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/CaliperRun.java24
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java29
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java149
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java119
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/Experiment.java82
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java29
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java99
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java307
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java284
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java112
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/HttpUploader.java40
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/Instrument.java231
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java28
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java55
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InstrumentName.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java40
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/JarFinder.java207
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/LocalPort.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/MainComponent.java64
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/MainModule.java52
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java58
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java124
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/Parameter.java133
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ParameterSet.java92
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/PlatformModule.java51
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java38
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java52
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java137
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/RunnerModule.java74
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/Running.java59
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java469
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java28
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java48
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java218
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ServiceModule.java35
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java34
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/StreamService.java434
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java30
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialId.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialModule.java145
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialNumber.java33
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java50
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java147
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java113
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialResult.java45
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java28
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java199
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java26
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java28
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/TrialScoped.java36
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/UserCodeException.java36
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java54
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java71
-rw-r--r--caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java209
-rw-r--r--caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java36
-rw-r--r--caliper/src/main/java/com/google/caliper/util/InterleavedReader.java128
-rw-r--r--caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java54
-rw-r--r--caliper/src/main/java/com/google/caliper/util/OutputModule.java48
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Parser.java24
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Parsers.java111
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Reflection.java42
-rw-r--r--caliper/src/main/java/com/google/caliper/util/ShortDuration.java352
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Stderr.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Stdout.java31
-rw-r--r--caliper/src/main/java/com/google/caliper/util/Util.java154
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java61
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java90
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/Allocation.java93
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java61
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/AllocationStats.java215
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java73
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java56
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java84
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java144
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java152
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/Worker.java77
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java37
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java88
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/WorkerMain.java80
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/WorkerModule.java144
-rw-r--r--caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java30
-rw-r--r--caliper/src/main/resources/com/google/caliper/config/default-config.properties14
-rw-r--r--caliper/src/main/resources/com/google/caliper/config/global-config.properties90
-rw-r--r--caliper/src/test/java/com/google/caliper/CaliperTest.java93
-rw-r--r--caliper/src/test/java/com/google/caliper/JsonTest.java67
-rw-r--r--caliper/src/test/java/com/google/caliper/MeasurementSetTest.java216
-rw-r--r--caliper/src/test/java/com/google/caliper/ParameterTest.java71
-rw-r--r--caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java81
-rw-r--r--caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java48
-rw-r--r--caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java116
-rw-r--r--caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java77
-rw-r--r--caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java178
-rw-r--r--caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java89
-rw-r--r--caliper/src/test/java/com/google/caliper/config/VmConfigTest.java47
-rw-r--r--caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java123
-rw-r--r--caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java171
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java113
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java60
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java220
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java63
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java66
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java100
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java123
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java149
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java195
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java55
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java183
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java110
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java255
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java126
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java252
-rw-r--r--caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java174
-rw-r--r--caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java39
-rw-r--r--caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java61
-rw-r--r--caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java60
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt288
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt770
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt200
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt352
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt832
-rw-r--r--caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt200
-rw-r--r--examples/pom.xml81
-rw-r--r--examples/src/main/java/examples/ArraySortBenchmark.java15
-rw-r--r--examples/src/main/java/examples/BitSetBenchmark.java39
-rw-r--r--examples/src/main/java/examples/CharacterBenchmark.java43
-rw-r--r--examples/src/main/java/examples/CompressionSizeBenchmark.java11
-rw-r--r--examples/src/main/java/examples/ContainsBenchmark.java30
-rw-r--r--examples/src/main/java/examples/CopyArrayBenchmark.java50
-rw-r--r--examples/src/main/java/examples/DemoBenchmark.java23
-rw-r--r--examples/src/main/java/examples/DoubleToStringBenchmark.java16
-rw-r--r--examples/src/main/java/examples/DoubleToStringBenchmark2.java17
-rw-r--r--examples/src/main/java/examples/EnumSetContainsBenchmark.java15
-rw-r--r--examples/src/main/java/examples/ExpensiveObjectsBenchmark.java24
-rw-r--r--examples/src/main/java/examples/FormatterBenchmark.java24
-rw-r--r--examples/src/main/java/examples/IntModBenchmark.java22
-rw-r--r--examples/src/main/java/examples/ListIterationBenchmark.java26
-rw-r--r--examples/src/main/java/examples/ListModificationBenchmark.java106
-rw-r--r--examples/src/main/java/examples/LoopingBackwardsBenchmark.java13
-rw-r--r--examples/src/main/java/examples/MessageDigestCreationBenchmark.java11
-rw-r--r--examples/src/main/java/examples/NoOpBenchmark.java30
-rw-r--r--examples/src/main/java/examples/StringBuilderBenchmark.java227
-rw-r--r--examples/src/main/java/examples/Utf8Benchmark.java141
-rw-r--r--examples/src/main/java/examples/VarargsBenchmark.java236
-rw-r--r--expectations/knownfailures.txt76
-rw-r--r--lib/gson-1.7.1.jarbin173590 -> 0 bytes
-rw-r--r--lib/gson-2.2.2-sources.jarbin0 -> 126470 bytes
-rw-r--r--lib/gson-2.2.2.jarbin0 -> 189285 bytes
-rw-r--r--lib/gson-2.2.2.jar.txt (renamed from lib/gson-1.7.1.jar.txt)0
-rw-r--r--lib/java-allocation-instrumenter-2.0-sources.jarbin0 -> 14935 bytes
-rw-r--r--lib/jersey-client-1.11-sources.jarbin0 -> 120038 bytes
-rw-r--r--lib/jersey-client-1.11.jarbin0 -> 130481 bytes
-rw-r--r--lib/jersey-client-1.11.jar.txt712
-rw-r--r--lib/jersey-core-1.11-sources.jarbin0 -> 433400 bytes
-rw-r--r--lib/jersey-core-1.11.jarbin0 -> 461795 bytes
-rw-r--r--lib/jersey-core-1.11.jar.txt712
-rw-r--r--lib/joda-time-2.1-sources.jarbin0 -> 700266 bytes
-rw-r--r--lib/joda-time-2.1.jarbin0 -> 570478 bytes
-rw-r--r--lib/joda-time-2.1.jar.txt202
-rw-r--r--lib/jsr311-api-1.1.1-sources.jarbin0 -> 70022 bytes
-rw-r--r--lib/jsr311-api-1.1.1.jarbin0 -> 46367 bytes
-rw-r--r--lib/jsr311-api-1.1.1.jar.txt712
-rw-r--r--tutorial/Tutorial.java29
294 files changed, 25123 insertions, 5839 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c4e65ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.iml
+*.ipr
+*.iws
+.idea
+target
+*~
diff --git a/Android.mk b/Android.mk
index 1207464..ee45b62 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,9 @@
LOCAL_PATH := $(call my-dir)
+# Include definitions of DAGGER2_PROCESSOR_CLASSES/LIBRARIES
+include external/dagger2/dagger2_annotation_processor.mk
+
# build caliper host jar
# ============================================================
@@ -24,16 +27,55 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, caliper/src/main/java/)
LOCAL_JAVA_RESOURCE_DIRS := caliper/src/main/resources
+LOCAL_IS_HOST_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
+ apache-commons-math-host \
caliper-gson-host \
caliper-java-allocation-instrumenter-host \
+ caliper-jersey-client-host \
+ caliper-jersey-core-host \
+ caliper-joda-time-host \
+ caliper-jsr311-api-host \
+ dagger2-host \
+ dagger2-inject-host \
guavalib
+# Use Dagger2 annotation processor
+PROCESSOR_LIBRARIES := $(DAGGER2_PROCESSOR_LIBRARIES)
+PROCESSOR_CLASSES := $(DAGGER2_PROCESSOR_CLASSES)
+include external/dagger2/java_annotation_processors.mk
+
include $(BUILD_HOST_JAVA_LIBRARY)
+# Remember the location of the generated files, this is needed for when
+# building for target
+caliper_host_generated_sources_dir := $(local-generated-sources-dir)/annotation_processor_output
+
+# build caliper target api jar
+# ============================================================
+# This contains just those classes needed for benchmarks to compile.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := caliper-api-target
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := \
+ caliper/src/main/java/com/google/caliper/AfterExperiment.java \
+ caliper/src/main/java/com/google/caliper/BeforeExperiment.java \
+ caliper/src/main/java/com/google/caliper/Param.java \
+ caliper/src/main/java/com/google/caliper/All.java \
+ caliper/src/main/java/com/google/caliper/Benchmark.java
+
+include $(BUILD_JAVA_LIBRARY)
+
# build caliper tests
# ============================================================
+# vogar --expectations $ANDROID_BUILD_TOP/external/caliper/expectations/knownfailures.txt \
+ --test-only \
+ --classpath $ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/caliper-tests_intermediates/javalib.jar \
+ com.google.caliper
include $(CLEAR_VARS)
@@ -41,19 +83,49 @@ LOCAL_MODULE := caliper-tests
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, caliper/src/test/java/)
+LOCAL_JAVA_RESOURCE_DIRS := caliper/src/test/resources
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ caliper-host \
+ junit \
+ mockito-host
+
+# Use Dagger2 annotation processor
+PROCESSOR_LIBRARIES := $(DAGGER2_PROCESSOR_LIBRARIES)
+PROCESSOR_CLASSES := $(DAGGER2_PROCESSOR_CLASSES)
+include external/dagger2/java_annotation_processors.mk
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# build caliper examples
+# ============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := caliper-examples
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := $(call all-java-files-under, examples/src/main/java/)
+LOCAL_IS_HOST_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
caliper-host \
- junit
+ junit \
+ mockito-host
include $(BUILD_HOST_JAVA_LIBRARY)
-# Build dependencies.
+# Build host dependencies.
# ============================================================
include $(CLEAR_VARS)
LOCAL_PREBUILT_JAVA_LIBRARIES := \
- caliper-gson-host:lib/gson-1.7.1$(COMMON_JAVA_PACKAGE_SUFFIX) \
- caliper-java-allocation-instrumenter-host:lib/java-allocation-instrumenter-2.0$(COMMON_JAVA_PACKAGE_SUFFIX)
+ caliper-gson-host:lib/gson-2.2.2$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ caliper-java-allocation-instrumenter-host:lib/java-allocation-instrumenter-2.0$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ caliper-jersey-client-host:lib/jersey-client-1.11$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ caliper-jersey-core-host:lib/jersey-core-1.11$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ caliper-joda-time-host:lib/joda-time-2.1$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ caliper-jsr311-api-host:lib/jsr311-api-1.1.1$(COMMON_JAVA_PACKAGE_SUFFIX)
include $(BUILD_HOST_PREBUILT)
diff --git a/README.android b/README.android
index 91617b5..850d2ee 100644
--- a/README.android
+++ b/README.android
@@ -2,9 +2,14 @@ URL: https://github.com/google/caliper.git
License: Apache 2
Description: "Google's Caliper Benchmarking And Measuring Tool"
-Version: 0.5-rc1
+Version: 73efbe138dafba57d6a890257961ba83f41b89f2
-Some of the examples do not build, that is an issue with upstream.
+This uses the Dagger2 dependency injection framework which runs as an annotation
+processor and generates the dependency injecting code. At the moment the
+generated code for the caliper-host target can be found at:
+ out/host/common/gen/JAVA_LIBRARIES/caliper-host_intermediates/annotation_processor_output/
+For the caliper-tests target it can be found at:
+ out/host/common/gen/JAVA_LIBRARIES/caliper-tests_intermediates/annotation_processor_output/
Local Patches:
None
diff --git a/caliper/pom.xml b/caliper/pom.xml
index befd583..9884724 100644
--- a/caliper/pom.xml
+++ b/caliper/pom.xml
@@ -1,22 +1,57 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2011 Google Inc.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>com.google.caliper</groupId>
- <artifactId>caliper</artifactId>
- <packaging>jar</packaging>
- <version>0.5-rc1</version>
- <inceptionYear>2009</inceptionYear>
- <name>caliper</name>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
- <url>http://code.google.com/p/caliper/</url>
+
+ <groupId>com.google.caliper</groupId>
+ <artifactId>caliper</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>caliper</name>
<description>Caliper: Microbenchmarking Framework for Java</description>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
+
+ <url>http://code.google.com/p/caliper/</url>
+
+ <inceptionYear>2009</inceptionYear>
+
+ <organization>
+ <name>Google Inc.</name>
+ <url>http://www.google.com</url>
+ </organization>
+
+ <developers>
+ <developer>
+ <name>Gregory Kick</name>
+ <organization>Google Inc.</organization>
+ </developer>
+ <developer>
+ <name>Jesse Wilson</name>
+ </developer>
+ </developers>
+
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
@@ -24,55 +59,97 @@
<distribution>repo</distribution>
</license>
</licenses>
+
<scm>
<connection>scm:git:http://code.google.com/p/caliper/</connection>
<developerConnection>scm:git:http://code.google.com/p/caliper/</developerConnection>
<url>http://code.google.com/p/caliper/source/browse</url>
</scm>
+
<issueManagement>
<system>Google Code Issue Tracking</system>
<url>http://code.google.com/p/caliper/issues/list</url>
</issueManagement>
- <organization>
- <name>Google, Inc.</name>
- <url>http://www.google.com</url>
- </organization>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <gpg.skip>true</gpg.skip>
+ </properties>
+
<dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>1.3.9</version>
+ <scope>provided</scope><!-- used only for annotations -->
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
- <version>1.7.1</version>
+ <version>2.2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>joda-time</groupId>
+ <artifactId>joda-time</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-client</artifactId>
+ <version>1.11</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger</artifactId>
+ <version>2.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>2.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>11.0.1</version>
- <scope>compile</scope>
+ <version>18.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ <version>3.6.7.Final</version>
+ <scope>provided</scope><!-- used only for annotations -->
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math</artifactId>
+ <version>2.2</version>
</dependency>
<dependency>
<groupId>com.google.code.java-allocation-instrumenter</groupId>
<artifactId>java-allocation-instrumenter</artifactId>
- <version>2.0</version>
+ <version>3.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>3.8.2</version>
+ <version>4.10</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
+
<build>
<defaultGoal>package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
+ <version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
@@ -80,8 +157,13 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
- <version>2.8</version>
+ <version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
@@ -90,12 +172,19 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <arguments>-DenableCiProfile=true</arguments>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
- <version>2.1.2</version>
+ <version>2.4</version>
<executions>
<execution>
<id>attach-sources</id>
- <phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
@@ -105,7 +194,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>2.7</version>
+ <version>2.10.1</version>
<executions>
<execution>
<id>generate-javadocs</id>
@@ -117,18 +206,91 @@
</executions>
<configuration>
<links>
- <link>http://download.oracle.com/javase/1.5.0/docs/api/</link>
+ <link>http://download.oracle.com/javase/1.6.0/docs/api/</link>
</links>
<version>true</version>
<show>public</show>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>all</shadedClassifierName>
+ <!-- disable relocation as it is breaking some reflection -->
+ <!-- <relocations>
+ <relocation>
+ <pattern>com.google</pattern>
+ <shadedPattern>com.google.caliper.shaded.com.google</shadedPattern>
+ <excludes>
+ <exclude>com.google.caliper.**</exclude>
+ </excludes>
+ </relocation>
+ <relocation>
+ <pattern>com.sun.jersey</pattern>
+ <shadedPattern>com.google.caliper.shaded.com.sun.jersey</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>com.sun.ws</pattern>
+ <shadedPattern>com.google.caliper.shaded.com.sun.ws</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>javax.inject</pattern>
+ <shadedPattern>com.google.caliper.shaded.javax.inject</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>javax.ws</pattern>
+ <shadedPattern>com.google.caliper.shaded.javax.ws</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.aopalliance</pattern>
+ <shadedPattern>com.google.caliper.shaded.org.aopalliance</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.joda</pattern>
+ <shadedPattern>com.google.caliper.shaded.org.joda</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.objectweb</pattern>
+ <shadedPattern>com.google.caliper.shaded.org.objectweb</shadedPattern>
+ </relocation>
+ </relocations> -->
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <manifestEntries>
+ <Premain-Class>com.google.monitoring.runtime.instrumentation.AllocationInstrumenter</Premain-Class>
+ <Can-Redefine-Classes>true</Can-Redefine-Classes>
+ <Can-Retransform-Classes>true</Can-Retransform-Classes>
+ </manifestEntries>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
- <developers>
- <developer>
- <name>Jesse Wilson</name>
- <organization>Google Inc.</organization>
- </developer>
- </developers>
+
</project>
diff --git a/caliper/src/main/java/com/google/caliper/AfterExperiment.java b/caliper/src/main/java/com/google/caliper/AfterExperiment.java
new file mode 100644
index 0000000..19f6cf7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/AfterExperiment.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for methods to be run after an experiment has been performed.
+ *
+ * @see BeforeExperiment
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface AfterExperiment {
+ /**
+ * A qualifier for which types of experiments this method should run. For example, annotating a
+ * method with {@code @AfterExperiment(Benchmark.class)} will cause it to only run for
+ * {@link Benchmark} experiments. By default, annotated methods run for all experiments.
+ */
+ Class<? extends Annotation> value() default All.class;
+}
diff --git a/caliper/src/main/java/com/google/caliper/UploadResults.java b/caliper/src/main/java/com/google/caliper/All.java
index 7dae7af..08b2456 100644
--- a/caliper/src/main/java/com/google/caliper/UploadResults.java
+++ b/caliper/src/main/java/com/google/caliper/All.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc.
+ * Copyright (C) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,13 +16,11 @@
package com.google.caliper;
-import java.io.File;
+import java.lang.annotation.Target;
/**
- * Usage: UploadResults <file_or_dir>
+ * The default value of lifecycle annotations that indicates it should associate with all benchmark
+ * types.
*/
-public class UploadResults {
- public static void main(String[] args) {
- new Runner().uploadResultsFileOrDir(new File(args[0]));
- }
-}
+@Target({}) // should never actually be applied
+public @interface All {}
diff --git a/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java
deleted file mode 100644
index 2ec3488..0000000
--- a/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.NonConstantMemoryUsage;
-import com.google.common.base.Supplier;
-import com.google.monitoring.runtime.instrumentation.AllocationRecorder;
-import com.google.monitoring.runtime.instrumentation.Sampler;
-
-public abstract class AllocationMeasurer extends Measurer {
-
- protected static final int ALLOCATION_DISPLAY_THRESHOLD = 50;
-
- private boolean log;
- private long tempAllocationCount;
- private long allocationsToIgnore;
- private long numberOfAllocations;
- private long allocationCount;
- private long outOfThreadAllocationCount;
- private boolean recordAllocations;
- protected String type;
-
- protected AllocationMeasurer() {
- log = false;
- allocationsToIgnore = 0;
- numberOfAllocations = 0;
- allocationCount = 0;
- outOfThreadAllocationCount = 0;
- recordAllocations = false;
-
- final Thread allocatingThread = Thread.currentThread();
- AllocationRecorder.addSampler(new Sampler() {
- // allocated {@code newObj} of type {@code desc}, whose size is {@code size}.
- // if this was not an array, {@code count} is -1. If it was array, {@code count} is the
- // size of the array.
- @Override public void sampleAllocation(int count, String desc, Object newObj, long size) {
- if (recordAllocations) {
- if (Thread.currentThread().equals(allocatingThread)) {
- if (log) {
- logAllocation(count, desc, size);
- } else if (numberOfAllocations == 0) {
- log("see first run for list of allocations");
- }
- allocationCount = incrementAllocationCount(allocationCount, count, size);
- tempAllocationCount++;
- numberOfAllocations++;
- } else {
- outOfThreadAllocationCount = incrementAllocationCount(outOfThreadAllocationCount, count, size);
- numberOfAllocations++;
- }
- }
- }
- });
- }
-
- protected abstract long incrementAllocationCount(long orig, int count, long size);
-
- private void logAllocation(int count, String desc, long size) {
- if (numberOfAllocations >= allocationsToIgnore) {
- if (numberOfAllocations < ALLOCATION_DISPLAY_THRESHOLD + allocationsToIgnore) {
- log("allocating " + desc + (count == -1 ? "" : " array with " + count + " elements")
- + " with size " + size + " bytes");
- } else if (numberOfAllocations == ALLOCATION_DISPLAY_THRESHOLD + allocationsToIgnore) {
- log("...more allocations...");
- }
- }
- }
-
- @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) throws Exception {
-
- // warm up, for some reason the very first time anything is measured, it will have a few more
- // allocations.
- measureAllocations(testSupplier.get(), 1, 0);
-
- // The "one" case serves as a base line. There may be caching, lazy loading, etc going on here.
- tempAllocationCount = 0; // count the number of times the sampler is called in one rep
- long one = measureAllocationsTotal(testSupplier.get(), 1);
- long oneAllocations = tempAllocationCount;
-
- // we expect that the delta between any two consecutive reps will be constant
- tempAllocationCount = 0; // count the number of times the sampler is called in two reps
- long two = measureAllocationsTotal(testSupplier.get(), 2);
- long twoAllocations = tempAllocationCount;
- long expectedDiff = two - one;
- // there is some overhead on the first call that we can ignore for the purposes of measurement
- long unitsToIgnore = one - expectedDiff;
- allocationsToIgnore = 2 * oneAllocations - twoAllocations;
- log("ignoring " + allocationsToIgnore + " allocation(s) per measurement as overhead");
-
- Measurement[] allocationMeasurements = new Measurement[4];
- log = true;
- allocationMeasurements[0] = measureAllocations(testSupplier.get(), 1, unitsToIgnore);
- log = false;
- for (int i = 1; i < allocationMeasurements.length; i++) {
- allocationMeasurements[i] =
- measureAllocations(testSupplier.get(), i + 1, unitsToIgnore);
- if (Math.round(allocationMeasurements[i].getRaw()) != expectedDiff) {
- throw new NonConstantMemoryUsage();
- }
- }
-
- // The above logic guarantees that all the measurements are equal, so we only need to return a
- // single measurement.
- allocationsToIgnore = 0;
- return new MeasurementSet(allocationMeasurements[0]);
- }
-
- private Measurement measureAllocations(ConfiguredBenchmark benchmark, int reps, long toIgnore)
- throws Exception {
- prepareForTest();
- log(LogConstants.MEASURED_SECTION_STARTING);
- resetAllocations();
- recordAllocations = true;
- benchmark.run(reps);
- recordAllocations = false;
- log(LogConstants.MEASURED_SECTION_DONE);
- long allocations = (allocationCount - toIgnore) / reps;
- long outOfThreadAllocations = outOfThreadAllocationCount;
- log(allocations + " " + type + "(s) allocated per rep");
- log(outOfThreadAllocations + " out of thread " + type + "(s) allocated in " + reps + " reps");
- benchmark.close();
- return getMeasurement(benchmark, allocations);
- }
-
- protected abstract Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations);
-
- private long measureAllocationsTotal(ConfiguredBenchmark benchmark, int reps)
- throws Exception {
- prepareForTest();
- log(LogConstants.MEASURED_SECTION_STARTING);
- resetAllocations();
- recordAllocations = true;
- benchmark.run(reps);
- recordAllocations = false;
- log(LogConstants.MEASURED_SECTION_DONE);
- long allocations = allocationCount;
- long outOfThreadAllocations = outOfThreadAllocationCount;
- log(allocations + " " + type + "(s) allocated in " + reps + " reps");
- if (outOfThreadAllocations > 0) {
- log(outOfThreadAllocations + " out of thread " + type + "(s) allocated in " + reps + " reps");
- }
- benchmark.close();
- return allocations;
- }
-
- private void resetAllocations() {
- allocationCount = 0;
- outOfThreadAllocationCount = 0;
- numberOfAllocations = 0;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Arguments.java b/caliper/src/main/java/com/google/caliper/Arguments.java
deleted file mode 100644
index cceb079..0000000
--- a/caliper/src/main/java/com/google/caliper/Arguments.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.DisplayUsageException;
-import com.google.caliper.UserException.IncompatibleArgumentsException;
-import com.google.caliper.UserException.InvalidParameterValueException;
-import com.google.caliper.UserException.MultipleBenchmarkClassesException;
-import com.google.caliper.UserException.NoBenchmarkClassException;
-import com.google.caliper.UserException.UnrecognizedOptionException;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-
-import java.io.File;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Parse command line arguments for the runner and in-process runner.
- */
-public final class Arguments {
- private String suiteClassName;
-
- /** JVMs to run in the benchmark */
- private final Set<String> userVms = Sets.newLinkedHashSet();
-
- /**
- * Parameter values specified by the user on the command line. Parameters with
- * no value in this multimap will get their values from the benchmark suite.
- */
- private final Multimap<String, String> userParameters = LinkedHashMultimap.create();
-
- /**
- * VM parameters like {memory=[-Xmx64,-Xmx128],jit=[-client,-server]}
- */
- private final Multimap<String, String> vmParameters = LinkedHashMultimap.create();
-
- private int trials = 1;
- private long warmupMillis = 3000;
- private long runMillis = 1000;
- private String timeUnit = null;
- private String instanceUnit = null;
- private String memoryUnit = null;
- private File saveResultsFile = null;
- private File uploadResultsFile = null;
- private boolean captureVmLog = false;
- private boolean printScore = false;
- private boolean measureMemory = false;
- private boolean debug = false;
- private int debugReps = defaultDebugReps;
- private MeasurementType measurementType;
- private MeasurementType primaryMeasurementType;
-
- /**
- * A signal that indicates a JSON object interleaved with the process output.
- * Should be short, but unlikely to show up in the processes natural output.
- */
- private String marker = "//ZxJ/";
-
- private static final String defaultDelimiter = ",";
- private static final int defaultDebugReps = 1000;
-
- public String getSuiteClassName() {
- return suiteClassName;
- }
-
- public Set<String> getUserVms() {
- return userVms;
- }
-
- public int getTrials() {
- return trials;
- }
-
- public Multimap<String, String> getVmParameters() {
- return vmParameters;
- }
-
- public Multimap<String, String> getUserParameters() {
- return userParameters;
- }
-
- public long getWarmupMillis() {
- return warmupMillis;
- }
-
- public long getRunMillis() {
- return runMillis;
- }
-
- public String getTimeUnit() {
- return timeUnit;
- }
-
- public String getInstanceUnit() {
- return instanceUnit;
- }
-
- public String getMemoryUnit() {
- return memoryUnit;
- }
-
- public File getSaveResultsFile() {
- return saveResultsFile;
- }
-
- public File getUploadResultsFile() {
- return uploadResultsFile;
- }
-
- public boolean getCaptureVmLog() {
- return captureVmLog;
- }
-
- public boolean printScore() {
- return printScore;
- }
-
- public boolean getMeasureMemory() {
- return measureMemory;
- }
-
- public MeasurementType getMeasurementType() {
- return measurementType;
- }
-
- public MeasurementType getPrimaryMeasurementType() {
- return primaryMeasurementType;
- }
-
- public boolean getDebug() {
- return debug;
- }
-
- public int getDebugReps() {
- return debugReps;
- }
-
- public String getMarker() {
- return marker;
- }
-
- public static Arguments parse(String[] argsArray) {
- Arguments result = new Arguments();
-
- Iterator<String> args = Iterators.forArray(argsArray);
- String delimiter = defaultDelimiter;
- Map<String, String> userParameterStrings = Maps.newLinkedHashMap();
- Map<String, String> vmParameterStrings = Maps.newLinkedHashMap();
- String vmString = null;
- boolean standardRun = false;
- while (args.hasNext()) {
- String arg = args.next();
-
- if ("--help".equals(arg)) {
- throw new DisplayUsageException();
- }
-
- if (arg.startsWith("-D") || arg.startsWith("-J")) {
-
- /*
- * Handle user parameters (-D) and VM parameters (-J) of these forms:
- *
- * -Dlength=100
- * -Jmemory=-Xmx1024M
- * -Dlength=100,200
- * -Jmemory=-Xmx1024M,-Xmx2048M
- * -Dlength 100
- * -Jmemory -Xmx1024M
- * -Dlength 100,200
- * -Jmemory -Xmx1024M,-Xmx2048M
- */
-
- String name;
- String value;
- int equalsSign = arg.indexOf('=');
- if (equalsSign == -1) {
- name = arg.substring(2);
- value = args.next();
- } else {
- name = arg.substring(2, equalsSign);
- value = arg.substring(equalsSign + 1);
- }
-
- String previousValue;
- if (arg.startsWith("-D")) {
- previousValue = userParameterStrings.put(name, value);
- } else {
- previousValue = vmParameterStrings.put(name, value);
- }
- if (previousValue != null) {
- throw new UserException.DuplicateParameterException(arg);
- }
- standardRun = true;
- // TODO: move warmup/run to caliperrc
- } else if ("--captureVmLog".equals(arg)) {
- result.captureVmLog = true;
- standardRun = true;
- } else if ("--warmupMillis".equals(arg)) {
- result.warmupMillis = Long.parseLong(args.next());
- standardRun = true;
- } else if ("--runMillis".equals(arg)) {
- result.runMillis = Long.parseLong(args.next());
- standardRun = true;
- } else if ("--trials".equals(arg)) {
- String value = args.next();
- try {
- result.trials = Integer.parseInt(value);
- if (result.trials < 1) {
- throw new UserException.InvalidTrialsException(value);
- }
- } catch (NumberFormatException e) {
- throw new UserException.InvalidTrialsException(value);
- }
- standardRun = true;
- } else if ("--vm".equals(arg)) {
- if (vmString != null) {
- throw new UserException.DuplicateParameterException(arg);
- }
- vmString = args.next();
- standardRun = true;
- } else if ("--delimiter".equals(arg)) {
- delimiter = args.next();
- standardRun = true;
- } else if ("--timeUnit".equals(arg)) {
- result.timeUnit = args.next();
- standardRun = true;
- } else if ("--instanceUnit".equals(arg)) {
- result.instanceUnit = args.next();
- standardRun = true;
- } else if ("--memoryUnit".equals(arg)) {
- result.memoryUnit = args.next();
- standardRun = true;
- } else if ("--saveResults".equals(arg) || "--xmlSave".equals(arg)) {
- // TODO: unsupport legacy --xmlSave
- result.saveResultsFile = new File(args.next());
- standardRun = true;
- } else if ("--uploadResults".equals(arg)) {
- result.uploadResultsFile = new File(args.next());
- } else if ("--printScore".equals(arg)) {
- result.printScore = true;
- standardRun = true;
- } else if ("--measureMemory".equals(arg)) {
- result.measureMemory = true;
- standardRun = true;
- } else if ("--debug".equals(arg)) {
- result.debug = true;
- } else if ("--debug-reps".equals(arg)) {
- String value = args.next();
- try {
- result.debugReps = Integer.parseInt(value);
- if (result.debugReps < 1) {
- throw new UserException.InvalidDebugRepsException(value);
- }
- } catch (NumberFormatException e) {
- throw new UserException.InvalidDebugRepsException(value);
- }
- } else if ("--marker".equals(arg)) {
- result.marker = args.next();
- } else if ("--measurementType".equals(arg)) {
- String measurementType = args.next();
- try {
- result.measurementType = MeasurementType.valueOf(measurementType);
- } catch (Exception e) {
- throw new InvalidParameterValueException(arg, measurementType);
- }
- standardRun = true;
- } else if ("--primaryMeasurementType".equals(arg)) {
- String measurementType = args.next().toUpperCase();
- try {
- result.primaryMeasurementType = MeasurementType.valueOf(measurementType);
- } catch (Exception e) {
- throw new InvalidParameterValueException(arg, measurementType);
- }
- standardRun = true;
- } else if (arg.startsWith("-")) {
- throw new UnrecognizedOptionException(arg);
-
- } else {
- if (result.suiteClassName != null) {
- throw new MultipleBenchmarkClassesException(result.suiteClassName, arg);
- }
- result.suiteClassName = arg;
- }
- }
-
- Splitter delimiterSplitter = Splitter.on(delimiter);
-
- if (vmString != null) {
- Iterables.addAll(result.userVms, delimiterSplitter.split(vmString));
- }
-
- Set<String> duplicates = Sets.intersection(
- userParameterStrings.keySet(), vmParameterStrings.keySet());
- if (!duplicates.isEmpty()) {
- throw new UserException.DuplicateParameterException(duplicates);
- }
-
- for (Map.Entry<String, String> entry : userParameterStrings.entrySet()) {
- result.userParameters.putAll(entry.getKey(), delimiterSplitter.split(entry.getValue()));
- }
- for (Map.Entry<String, String> entry : vmParameterStrings.entrySet()) {
- result.vmParameters.putAll(entry.getKey(), delimiterSplitter.split(entry.getValue()));
- }
-
- if (standardRun && result.uploadResultsFile != null) {
- throw new IncompatibleArgumentsException("--uploadResults");
- }
-
- if (result.suiteClassName == null && result.uploadResultsFile == null) {
- throw new NoBenchmarkClassException();
- }
-
- if (result.primaryMeasurementType != null
- && result.primaryMeasurementType != MeasurementType.TIME && !result.measureMemory) {
- throw new IncompatibleArgumentsException(
- "--primaryMeasurementType " + result.primaryMeasurementType.toString().toLowerCase());
- }
-
- return result;
- }
-
- public static void printUsage() {
- System.out.println();
- System.out.println("Usage: Runner [OPTIONS...] <benchmark>");
- System.out.println();
- System.out.println(" <benchmark>: a benchmark class or suite");
- System.out.println();
- System.out.println("OPTIONS");
- System.out.println();
- System.out.println(" -D<param>=<value>: fix a benchmark parameter to a given value.");
- System.out.println(" Multiple values can be supplied by separating them with the");
- System.out.println(" delimiter specified in the --delimiter argument.");
- System.out.println();
- System.out.println(" For example: \"-Dfoo=bar,baz,bat\"");
- System.out.println();
- System.out.println(" \"benchmark\" is a special parameter that can be used to specify");
- System.out.println(" which benchmark methods to run. For example, if a benchmark has");
- System.out.println(" the method \"timeFoo\", it can be run alone by using");
- System.out.println(" \"-Dbenchmark=Foo\". \"benchmark\" also accepts a delimiter");
- System.out.println(" separated list of methods to run.");
- System.out.println();
- System.out.println(" -J<param>=<value>: set a JVM argument to the given value.");
- System.out.println(" Multiple values can be supplied by separating them with the");
- System.out.println(" delimiter specified in the --delimiter argument.");
- System.out.println();
- System.out.println(" For example: \"-JmemoryMax=-Xmx32M,-Xmx512M\"");
- System.out.println();
- System.out.println(" --delimiter <delimiter>: character or string to use as a delimiter");
- System.out.println(" for parameter and vm values.");
- System.out.println(" Default: \"" + defaultDelimiter + "\"");
- System.out.println();
- System.out.println(" --warmupMillis <millis>: duration to warmup each benchmark");
- System.out.println();
- System.out.println(" --runMillis <millis>: duration to execute each benchmark");
- System.out.println();
- System.out.println(" --captureVmLog: record the VM's just-in-time compiler and GC logs.");
- System.out.println(" This may slow down or break benchmark display tools.");
- System.out.println();
- System.out.println(" --measureMemory: measure the number of allocations done and the amount of");
- System.out.println(" memory used by invocations of the benchmark.");
- System.out.println(" Default: off");
- System.out.println();
- System.out.println(" --vm <vm>: executable to test benchmark on. Multiple VMs may be passed");
- System.out.println(" in as a list separated by the delimiter specified in the");
- System.out.println(" --delimiter argument.");
- System.out.println();
- System.out.println(" --timeUnit <unit>: unit of time to use for result. Depends on the units");
- System.out.println(" defined in the benchmark's getTimeUnitNames() method, if defined.");
- System.out.println(" Default Options: ns, us, ms, s");
- System.out.println();
- System.out.println(" --instanceUnit <unit>: unit to use for allocation instances result.");
- System.out.println(" Depends on the units defined in the benchmark's");
- System.out.println(" getInstanceUnitNames() method, if defined.");
- System.out.println(" Default Options: instances, K instances, M instances, B instances");
- System.out.println();
- System.out.println(" --memoryUnit <unit>: unit to use for allocation memory size result.");
- System.out.println(" Depends on the units defined in the benchmark's");
- System.out.println(" getMemoryUnitNames() method, if defined.");
- System.out.println(" Default Options: B, KB, MB, GB");
- System.out.println();
- System.out.println(" --saveResults <file/dir>: write results to this file or directory");
- System.out.println();
- System.out.println(" --printScore: if present, also display an aggregate score for this run,");
- System.out.println(" where higher is better. This number has no particular meaning,");
- System.out.println(" but can be compared to scores from other runs that use the exact");
- System.out.println(" same arguments.");
- System.out.println();
- System.out.println(" --uploadResults <file/dir>: upload this file or directory of files");
- System.out.println(" to the web app. This argument ends Caliper early and is thus");
- System.out.println(" incompatible with all other arguments.");
- System.out.println();
- System.out.println(" --debug: run without measurement for use with debugger or profiling.");
- System.out.println();
- System.out.println(" --debug-reps: fixed number of reps to run with --debug.");
- System.out.println(" Default: \"" + defaultDebugReps + "\"");
-
- // adding new options? don't forget to update executeForked()
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/BeforeExperiment.java b/caliper/src/main/java/com/google/caliper/BeforeExperiment.java
new file mode 100644
index 0000000..ec67f4c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/BeforeExperiment.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for methods to be run before an experiment has been performed.
+ *
+ * @see AfterExperiment
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface BeforeExperiment {
+ /**
+ * A qualifier for which types of experiments this method should run. For example, annotating a
+ * method with {@code @BeforeExperiment(Benchmark.class)} will cause it to only run for
+ * {@link Benchmark} experiments. By default, annotated methods run for all experiments.
+ */
+ Class<? extends Annotation> value() default All.class;
+}
diff --git a/caliper/src/main/java/com/google/caliper/Benchmark.java b/caliper/src/main/java/com/google/caliper/Benchmark.java
index 06bda82..252017c 100644
--- a/caliper/src/main/java/com/google/caliper/Benchmark.java
+++ b/caliper/src/main/java/com/google/caliper/Benchmark.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,40 +16,64 @@
package com.google.caliper;
-import java.util.Map;
-import java.util.Set;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
/**
- * A collection of benchmarks that share a set of configuration parameters.
+ * Annotation for benchmark methods. To write a benchmark:
+ *
+ * <ol>
+ * <li>Annotate one or more methods with this annotation.
+ * <li>Annotate any fields with {@literal @}{@link Param} that should have parameter values
+ * injected (see {@literal @}{@link Param} for more details)
+ * <li>Optionally use {@link BeforeExperiment} and {@link AfterExperiment} on setup and teardown
+ * methods
+ * </ol>
+ *
+ * <p>Since many benchmarks may execute in a shorter duration than is accurately measured by
+ * available timers, benchmark methods <i>may</i> take either an {@code int} or {@code long}
+ * argument representing a number of repetitions to perform in a given execution. It is critical
+ * that the work done in the benchmark method scale linearly to the number of repetitions.
+ *
+ * <p>Benchmark methods may return any value. It will be ignored.
+ *
+ * <p>This class is instantiated and injected only once per child VM invocation, to measure one
+ * particular combination of parameters.
+ *
+ * <p>For example: <pre> {@code
+ * public final class MyBenchmark {
+ * {@literal @}Param FeatureEnum feature;
+ * {@literal @}Param({"1", "10", "100"}) int size;
+ * private MyObject objectToBenchmark;
+ *
+ * {@literal @}BeforeExperiment void initializeObject() {
+ * objectToBenchmark = new MyObject(size);
+ * }
+ *
+ * {@literal @}Benchmark int foo(int reps) {
+ * MyObject object = objectToBenchmark; // copy to local to avoid field access overhead
+ * int dummy = 0;
+ * for (int i = 0; i < reps; i++) {
+ * dummy += object.foo(feature);
+ * }
+ * // return a dummy value so the JIT compiler doesn't optimize away the entire method.
+ * return dummy;
+ * }
+ *
+ * {@literal @}Benchmark int bar() {
+ * // benchmark another operation of MyObject that doesn't require a reps parameter
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The benchmark class MyBenchmark has two benchmark methods ({@code foo} and {@code bar}) and
+ * two {@link Param Params} ({@code feature} and {@code size}). For each experiment performed by
+ * Caliper (e.g. {@code foo} with {@code feature == FeatureEnum.A} and {@code size == 100}),
+ * {@code initializeObject} will be called exactly once, but {@code foo} may be called many times.
*/
-public interface Benchmark {
-
- Set<String> parameterNames();
-
- Set<String> parameterValues(String parameterName);
-
- ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues);
-
- /**
- * A mapping of units to their values. Their values must be integers, but all values are relative,
- * so if one unit is 1.5 times the size of another, then these units can be expressed as
- * {"unit1"=10,"unit2"=15}. The smallest unit given by the function will be used to display
- * immediate results when running at the command line.
- *
- * e.g. 0% Scenario{...} 16.08<SMALLEST-UNIT>; σ=1.72<SMALLEST-UNIT> @ 3 trials
- */
- Map<String, Integer> getTimeUnitNames();
-
- Map<String, Integer> getInstanceUnitNames();
-
- Map<String, Integer> getMemoryUnitNames();
-
- /**
- * Converts nanoseconds to the smallest unit defined in {@link #getTimeUnitNames()}.
- */
- double nanosToUnits(double nanos);
-
- double instancesToUnits(long instances);
-
- double bytesToUnits(long bytes);
-} \ No newline at end of file
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface Benchmark {}
diff --git a/caliper/src/main/java/com/google/caliper/CaliperRc.java b/caliper/src/main/java/com/google/caliper/CaliperRc.java
deleted file mode 100644
index 21a283f..0000000
--- a/caliper/src/main/java/com/google/caliper/CaliperRc.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-public class CaliperRc {
- public static final CaliperRc INSTANCE = new CaliperRc();
-
- private final Properties properties = new Properties();
-
- private CaliperRc() {
- try {
- String caliperRcEnvVar = System.getenv("CALIPERRC");
- File caliperRcFile = (caliperRcEnvVar == null)
- ? new File(System.getProperty("user.home"), ".caliperrc")
- : new File(caliperRcEnvVar);
- if (caliperRcFile.exists()) {
- InputStream in = new FileInputStream(caliperRcFile);
- properties.load(in);
- in.close();
- } else {
- // create it with a template
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public String getApiKey() {
- return properties.getProperty("apiKey");
- }
-
- public String getPostUrl() {
- return properties.getProperty("postUrl");
- }
-
- /**
- * The HTTP proxy host name and port number separated by a colon, such as
- * foo.com:8080
- */
- public String getProxy() {
- return properties.getProperty("proxy");
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java b/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java
deleted file mode 100644
index f150ec1..0000000
--- a/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import java.util.Map;
-
-public abstract class ConfiguredBenchmark {
-
- private final Benchmark underlyingBenchmark;
-
- protected ConfiguredBenchmark(Benchmark underlyingBenchmark) {
- this.underlyingBenchmark = underlyingBenchmark;
- }
-
- /**
- * Runs the benchmark through {@code reps} iterations.
- *
- * @return any object or null. Benchmark implementors may keep an accumulating
- * value to prevent the runtime from optimizing away the code under test.
- * Such an accumulator value can be returned here.
- */
- public abstract Object run(int reps) throws Exception;
-
- public abstract void close() throws Exception;
-
- public final Benchmark getBenchmark() {
- return underlyingBenchmark;
- }
-
- public final double nanosToUnits(double nanos) {
- return underlyingBenchmark.nanosToUnits(nanos);
- }
-
- public final Map<String, Integer> timeUnitNames() {
- return underlyingBenchmark.getTimeUnitNames();
- }
-
- public final double instancesToUnits(long instances) {
- return underlyingBenchmark.instancesToUnits(instances);
- }
-
- public final Map<String, Integer> instanceUnitNames() {
- return underlyingBenchmark.getInstanceUnitNames();
- }
-
- public final double bytesToUnits(long bytes) {
- return underlyingBenchmark.bytesToUnits(bytes);
- }
-
- public final Map<String, Integer> memoryUnitNames() {
- return underlyingBenchmark.getMemoryUnitNames();
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/ConsoleReport.java b/caliper/src/main/java/com/google/caliper/ConsoleReport.java
deleted file mode 100644
index 8449bda..0000000
--- a/caliper/src/main/java/com/google/caliper/ConsoleReport.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.util.LinearTranslation;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Ordering;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * Prints a report containing the tested values and the corresponding
- * measurements. Measurements are grouped by variable using indentation.
- * Alongside numeric values, quick-glance ascii art bar charts are printed.
- * Sample output (this may not represent the exact form that is produced):
- * <pre>
- * benchmark d ns linear runtime
- * ConcatenationBenchmark 3.14159265 4397 ========================
- * ConcatenationBenchmark -0.0 223 ===============
- * FormatterBenchmark 3.14159265 33999 ==============================
- * FormatterBenchmark -0.0 26399 =============================
- * </pre>
- */
-final class ConsoleReport {
-
- private static final int barGraphWidth = 30;
-
- private static final int UNITS_FOR_SCORE_100 = 1;
- private static final int UNITS_FOR_SCORE_10 = 1000000000; // 1 s
-
- private static final LinearTranslation scoreTranslation =
- new LinearTranslation(Math.log(UNITS_FOR_SCORE_10), 10,
- Math.log(UNITS_FOR_SCORE_100), 100);
-
- public static final Ordering<Entry<String, Integer>> UNIT_ORDERING =
- new Ordering<Entry<String, Integer>>() {
- @Override public int compare(Entry<String, Integer> a, Entry<String, Integer> b) {
- return a.getValue().compareTo(b.getValue());
- }
- };
-
- private final List<Variable> variables;
- private final Run run;
- private final List<Scenario> scenarios;
-
- private final List<MeasurementType> orderedMeasurementTypes;
- private final MeasurementType type;
- private final double maxValue;
- private final double logMinValue;
- private final double logMaxValue;
- private final EnumMap<MeasurementType, Integer> decimalDigitsMap =
- new EnumMap<MeasurementType, Integer>(MeasurementType.class);
- private final EnumMap<MeasurementType, Double> divideByMap =
- new EnumMap<MeasurementType, Double>(MeasurementType.class);
- private final EnumMap<MeasurementType, String> unitMap =
- new EnumMap<MeasurementType, String>(MeasurementType.class);
- private final EnumMap<MeasurementType, Integer> measurementColumnLengthMap =
- new EnumMap<MeasurementType, Integer>(MeasurementType.class);
- private boolean printScore;
-
- ConsoleReport(Run run, Arguments arguments) {
- this.run = run;
- unitMap.put(MeasurementType.TIME, arguments.getTimeUnit());
- unitMap.put(MeasurementType.INSTANCE, arguments.getInstanceUnit());
- unitMap.put(MeasurementType.MEMORY, arguments.getMemoryUnit());
-
- if (arguments.getMeasureMemory()) {
- orderedMeasurementTypes = Arrays.asList(
- MeasurementType.TIME, MeasurementType.INSTANCE, MeasurementType.MEMORY);
- } else {
- orderedMeasurementTypes = Arrays.asList(MeasurementType.TIME);
- }
-
- if (arguments.getPrimaryMeasurementType() != null) {
- this.type = arguments.getPrimaryMeasurementType();
- } else {
- this.type = MeasurementType.TIME;
- }
-
- double min = Double.POSITIVE_INFINITY;
- double max = 0;
-
- Multimap<String, String> nameToValues = LinkedHashMultimap.create();
- List<Variable> variablesBuilder = new ArrayList<Variable>();
- for (Entry<Scenario, ScenarioResult> entry : this.run.getMeasurements().entrySet()) {
- Scenario scenario = entry.getKey();
- double d = entry.getValue().getMeasurementSet(type).medianUnits();
-
- min = Math.min(min, d);
- max = Math.max(max, d);
-
- for (Entry<String, String> variable : scenario.getVariables().entrySet()) {
- String name = variable.getKey();
- nameToValues.put(name, variable.getValue());
- }
- }
-
- for (Entry<String, Collection<String>> entry : nameToValues.asMap().entrySet()) {
- Variable variable = new Variable(entry.getKey(), entry.getValue());
- variablesBuilder.add(variable);
- }
-
- /*
- * Figure out how much influence each variable has on the measured value.
- * We sum the measurements taken with each value of each variable. For
- * variable that have influence on the measurement, the sums will differ
- * by value. If the variable has little influence, the sums will be similar
- * to one another and close to the overall average. We take the standard
- * deviation across each variable's collection of sums. Higher standard
- * deviation implies higher influence on the measured result.
- */
- double sumOfAllMeasurements = 0;
- for (ScenarioResult measurement : this.run.getMeasurements().values()) {
- sumOfAllMeasurements += measurement.getMeasurementSet(type).medianUnits();
- }
- for (Variable variable : variablesBuilder) {
- int numValues = variable.values.size();
- double[] sumForValue = new double[numValues];
- for (Entry<Scenario, ScenarioResult> entry
- : this.run.getMeasurements().entrySet()) {
- Scenario scenario = entry.getKey();
- sumForValue[variable.index(scenario)] +=
- entry.getValue().getMeasurementSet(type).medianUnits();
- }
- double mean = sumOfAllMeasurements / sumForValue.length;
- double stdDeviationSquared = 0;
- for (double value : sumForValue) {
- double distance = value - mean;
- stdDeviationSquared += distance * distance;
- }
- variable.stdDeviation = Math.sqrt(stdDeviationSquared / numValues);
- }
-
- this.variables = new StandardDeviationOrdering().reverse().sortedCopy(variablesBuilder);
- this.scenarios = new ByVariablesOrdering().sortedCopy(this.run.getMeasurements().keySet());
- this.maxValue = max;
- this.logMinValue = Math.log(min);
- this.logMaxValue = Math.log(max);
-
- EnumMap<MeasurementType, Integer> digitsBeforeDecimalMap =
- new EnumMap<MeasurementType, Integer>(MeasurementType.class);
- EnumMap<MeasurementType, Integer> decimalPointMap =
- new EnumMap<MeasurementType, Integer>(MeasurementType.class);
- for (MeasurementType measurementType : orderedMeasurementTypes) {
- double maxForType = 0;
- double minForType = Double.POSITIVE_INFINITY;
- for (Entry<Scenario, ScenarioResult> entry : this.run.getMeasurements().entrySet()) {
- double d = entry.getValue().getMeasurementSet(measurementType).medianUnits();
- minForType = Math.min(minForType, d);
- maxForType = Math.max(maxForType, d);
- }
-
- unitMap.put(measurementType,
- getUnit(unitMap.get(measurementType), measurementType, minForType));
-
- divideByMap.put(measurementType,
- (double) getUnits(measurementType).get(unitMap.get(measurementType)));
-
- int numDigitsInMin = ceil(Math.log10(minForType));
- decimalDigitsMap.put(measurementType,
- ceil(Math.max(0, ceil(Math.log10(divideByMap.get(measurementType))) + 3 - numDigitsInMin)));
-
- digitsBeforeDecimalMap.put(measurementType,
- Math.max(1, ceil(Math.log10(maxForType / divideByMap.get(measurementType)))));
-
- decimalPointMap.put(measurementType, decimalDigitsMap.get(measurementType) > 0 ? 1 : 0);
-
- measurementColumnLengthMap.put(measurementType, Math.max(maxForType > 0
- ? digitsBeforeDecimalMap.get(measurementType) + decimalPointMap.get(measurementType)
- + decimalDigitsMap.get(measurementType)
- : 1, unitMap.get(measurementType).trim().length()));
- }
-
- this.printScore = arguments.printScore();
- }
-
- private String getUnit(String userSuppliedUnit, MeasurementType measurementType, double min) {
- Map<String, Integer> units = getUnits(measurementType);
-
- if (userSuppliedUnit == null) {
- List<Entry<String, Integer>> entries = UNIT_ORDERING.reverse().sortedCopy(units.entrySet());
- for (Entry<String, Integer> entry : entries) {
- if (min / entry.getValue() >= 1) {
- return entry.getKey();
- }
- }
- // if no unit works, just use the smallest available unit.
- return entries.get(entries.size() - 1).getKey();
- }
-
- if (!units.keySet().contains(userSuppliedUnit)) {
- throw new RuntimeException("\"" + unitMap.get(measurementType) + "\" is not a valid unit.");
- }
- return userSuppliedUnit;
- }
-
- private Map<String, Integer> getUnits(MeasurementType measurementType) {
- Map<String, Integer> units = null;
- for (Entry<Scenario, ScenarioResult> entry : run.getMeasurements().entrySet()) {
- if (units == null) {
- units = entry.getValue().getMeasurementSet(measurementType).getUnitNames();
- } else {
- if (!units.equals(entry.getValue().getMeasurementSet(measurementType).getUnitNames())) {
- throw new RuntimeException("measurement sets for run contain multiple, incompatible unit"
- + " sets.");
- }
- }
- }
- if (units == null) {
- throw new RuntimeException("run has no measurements.");
- }
- if (units.isEmpty()) {
- throw new RuntimeException("no units specified.");
- }
- return units;
- }
-
- /**
- * A variable and the set of values to which it has been assigned.
- */
- private static class Variable {
- final String name;
- final ImmutableList<String> values;
- final int maxLength;
- double stdDeviation;
-
- Variable(String name, Collection<String> values) {
- this.name = name;
- this.values = ImmutableList.copyOf(values);
-
- int maxLen = name.length();
- for (String value : values) {
- maxLen = Math.max(maxLen, value.length());
- }
- this.maxLength = maxLen;
- }
-
- String get(Scenario scenario) {
- return scenario.getVariables().get(name);
- }
-
- int index(Scenario scenario) {
- return values.indexOf(get(scenario));
- }
-
- boolean isInteresting() {
- return values.size() > 1;
- }
- }
-
- /**
- * Orders the different variables by their standard deviation. This results
- * in an appropriate grouping of output values.
- */
- private static class StandardDeviationOrdering extends Ordering<Variable> {
- public int compare(Variable a, Variable b) {
- return Double.compare(a.stdDeviation, b.stdDeviation);
- }
- }
-
- /**
- * Orders scenarios by the variables.
- */
- private class ByVariablesOrdering extends Ordering<Scenario> {
- public int compare(Scenario a, Scenario b) {
- for (Variable variable : variables) {
- int aValue = variable.values.indexOf(variable.get(a));
- int bValue = variable.values.indexOf(variable.get(b));
- int diff = aValue - bValue;
- if (diff != 0) {
- return diff;
- }
- }
- return 0;
- }
- }
-
- void displayResults() {
- printValues();
- System.out.println();
- printUninterestingVariables();
- printCharCounts();
- }
-
- private void printCharCounts() {
- int systemOutCharCount = 0;
- int systemErrCharCount = 0;
- for (ScenarioResult scenarioResult : run.getMeasurements().values()) {
- for (MeasurementType measurementType : MeasurementType.values()) {
- MeasurementSet measurementSet = scenarioResult.getMeasurementSet(measurementType);
- if (measurementSet != null) {
- systemOutCharCount += measurementSet.getSystemOutCharCount();
- systemErrCharCount += measurementSet.getSystemErrCharCount();
- }
- }
- }
- if (systemOutCharCount > 0 || systemErrCharCount > 0) {
- System.out.println();
- System.out.println("Note: benchmarks printed " + systemOutCharCount
- + " characters to System.out and " + systemErrCharCount + " characters to System.err."
- + " Use --debug to see this output.");
- }
- }
-
- /**
- * Prints a table of values.
- */
- private void printValues() {
- // header
- for (Variable variable : variables) {
- if (variable.isInteresting()) {
- System.out.printf("%" + variable.maxLength + "s ", variable.name);
- }
- }
- // doesn't make sense to show graphs at all for 1
- // scenario, since it leads to vacuous graphs.
- boolean showGraphs = scenarios.size() > 1;
-
- EnumMap<MeasurementType, String> numbersFormatMap =
- new EnumMap<MeasurementType, String>(MeasurementType.class);
- for (MeasurementType measurementType : orderedMeasurementTypes) {
- if (measurementType != type) {
- System.out.printf("%" + measurementColumnLengthMap.get(measurementType) + "s ",
- unitMap.get(measurementType).trim());
- }
-
- numbersFormatMap.put(measurementType,
- "%" + measurementColumnLengthMap.get(measurementType)
- + "." + decimalDigitsMap.get(measurementType) + "f"
- + (type == measurementType ? "" : " "));
- }
-
- System.out.printf("%" + measurementColumnLengthMap.get(type) + "s", unitMap.get(type).trim());
- if (showGraphs) {
- System.out.print(" linear runtime");
- }
- System.out.println();
-
- double sumOfLogs = 0.0;
-
- for (Scenario scenario : scenarios) {
- for (Variable variable : variables) {
- if (variable.isInteresting()) {
- System.out.printf("%" + variable.maxLength + "s ", variable.get(scenario));
- }
- }
- ScenarioResult measurement = run.getMeasurements().get(scenario);
- sumOfLogs += Math.log(measurement.getMeasurementSet(type).medianUnits());
-
- for (MeasurementType measurementType : orderedMeasurementTypes) {
- if (measurementType != type) {
- System.out.printf(numbersFormatMap.get(measurementType),
- measurement.getMeasurementSet(measurementType).medianUnits() / divideByMap.get(measurementType));
- }
- }
-
- System.out.printf(numbersFormatMap.get(type),
- measurement.getMeasurementSet(type).medianUnits() / divideByMap.get(type));
- if (showGraphs) {
- System.out.printf(" %s", barGraph(measurement.getMeasurementSet(type).medianUnits()));
- }
- System.out.println();
- }
-
- if (printScore) {
- // arithmetic mean of logs, aka log of geometric mean
- double meanLogUnits = sumOfLogs / scenarios.size();
- System.out.format("%nScore: %.3f%n", scoreTranslation.translate(meanLogUnits));
- }
- }
-
- /**
- * Prints variables with only one unique value.
- */
- private void printUninterestingVariables() {
- for (Variable variable : variables) {
- if (!variable.isInteresting()) {
- System.out.println(variable.name + ": " + Iterables.getOnlyElement(variable.values));
- }
- }
- }
-
- /**
- * Returns a string containing a bar of proportional width to the specified
- * value.
- */
- private String barGraph(double value) {
- int graphLength = floor(value / maxValue * barGraphWidth);
- graphLength = Math.max(1, graphLength);
- graphLength = Math.min(barGraphWidth, graphLength);
- return Strings.repeat("=", graphLength);
- }
-
- @SuppressWarnings("NumericCastThatLosesPrecision")
- private static int floor(double d) {
- return (int) d;
- }
-
- @SuppressWarnings("NumericCastThatLosesPrecision")
- private static int ceil(double d) {
- return (int) Math.ceil(d);
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/CountingPrintStream.java b/caliper/src/main/java/com/google/caliper/CountingPrintStream.java
deleted file mode 100644
index 5c19a61..0000000
--- a/caliper/src/main/java/com/google/caliper/CountingPrintStream.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import java.io.PrintStream;
-
-/**
- * Counts how many characters were written.
- */
-final class CountingPrintStream extends PrintStream {
-
- private final PrintStream delegate;
- private int count;
-
- CountingPrintStream(PrintStream delegate) {
- super(delegate);
- this.delegate = delegate;
- }
-
- public int getCount() {
- return count;
- }
-
- @Override public void flush() {
- delegate.flush();
- }
-
- @Override public void close() {
- delegate.close();
- }
-
- @Override public boolean checkError() {
- return delegate.checkError();
- }
-
- @Override protected void setError() {
- throw new UnsupportedOperationException();
- }
-
- @Override protected void clearError() {
- throw new UnsupportedOperationException();
- }
-
- @Override public void write(int b) {
- count++;
- delegate.write(b);
- }
-
- @Override public void write(byte[] buffer, int offset, int length) {
- count += length;
- delegate.write(buffer, offset, length);
- }
-
- @Override public void print(char[] chars) {
- count += chars.length;
- delegate.print(chars);
- }
-
- @Override public void print(String s) {
- count += s.length();
- delegate.print(s);
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/DalvikVm.java b/caliper/src/main/java/com/google/caliper/DalvikVm.java
deleted file mode 100644
index f89cc28..0000000
--- a/caliper/src/main/java/com/google/caliper/DalvikVm.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableList;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The dalvikvm run on Android devices via the app_process executable.
- */
-final class DalvikVm extends Vm {
-
- public static boolean isDalvikVm() {
- return "Dalvik".equals(System.getProperty("java.vm.name"));
- }
-
- public static String vmName() {
- return "app_process";
- }
-
- @Override public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) {
- if (!arguments.getCaptureVmLog()) {
- return ImmutableList.of();
- }
-
- List<String> result = new ArrayList<String>();
- if (arguments.getCaptureVmLog()) {
- // TODO: currently GC goes to logcat.
- // result.add("-verbose:gc");
- }
- return result;
- }
-
- @Override public ProcessBuilder newProcessBuilder(File workingDirectory, String classPath,
- ImmutableList<String> vmArgs, String className, ImmutableList<String> applicationArgs) {
- ProcessBuilder result = new ProcessBuilder();
- result.directory(workingDirectory);
- result.command().addAll(vmArgs);
- result.command().add("-Djava.class.path=" + classPath);
- result.command().add(workingDirectory.getPath());
- result.command().add(className);
- result.command().addAll(applicationArgs);
- return result;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/DebugMeasurer.java b/caliper/src/main/java/com/google/caliper/DebugMeasurer.java
deleted file mode 100644
index 64d4b7f..0000000
--- a/caliper/src/main/java/com/google/caliper/DebugMeasurer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.base.Supplier;
-
-/**
- * Measure's the benchmark's per-trial execution time.
- */
-class DebugMeasurer extends Measurer {
-
- private final int reps;
-
- DebugMeasurer(int reps) {
- checkArgument(reps > 0);
- this.reps = reps;
- }
-
- @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier)
- throws Exception {
- ConfiguredBenchmark benchmark = testSupplier.get();
- benchmark.run(reps);
- benchmark.close();
- return null;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Environment.java b/caliper/src/main/java/com/google/caliper/Environment.java
deleted file mode 100644
index 75c2813..0000000
--- a/caliper/src/main/java/com/google/caliper/Environment.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A description of an environment in which benchmarks are run.
- *
- * WARNING: a JSON representation of this class is stored on the app engine server. If any changes
- * are made to this class, a deserialization adapter must be written for this class to ensure
- * backwards compatibility.
- *
- * <p>Gwt-safe
- */
-@SuppressWarnings("serial")
-@GwtCompatible
-public final class Environment
- implements Serializable /* for GWT Serialization */ {
- private /*final*/ Map<String, String> propertyMap;
-
- public Environment(Map<String, String> propertyMap) {
- this.propertyMap = new HashMap<String, String>(propertyMap);
- }
-
- public Map<String, String> getProperties() {
- return propertyMap;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof Environment
- && ((Environment) o).propertyMap.equals(propertyMap);
- }
-
- @Override public int hashCode() {
- return propertyMap.hashCode();
- }
-
- @Override public String toString() {
- return propertyMap.toString();
- }
-
- @SuppressWarnings("unused")
- private Environment() {} // for GWT Serialization
-}
diff --git a/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java b/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java
deleted file mode 100644
index 9b7150b..0000000
--- a/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableMultiset;
-import com.google.common.collect.Multimap;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class EnvironmentGetter {
-
- public Environment getEnvironmentSnapshot() {
- Map<String, String> propertyMap = new HashMap<String, String>();
-
- @SuppressWarnings("unchecked")
- Map<String, String> sysProps = (Map<String, String>) (Map) System.getProperties();
-
- // Sometimes java.runtime.version is more descriptive than java.version
- String version = sysProps.get("java.version");
- String alternateVersion = sysProps.get("java.runtime.version");
- if (alternateVersion != null && alternateVersion.length() > version.length()) {
- version = alternateVersion;
- }
- propertyMap.put("jre.version", version);
-
- propertyMap.put("jre.vmname", sysProps.get("java.vm.name"));
- propertyMap.put("jre.vmversion", sysProps.get("java.vm.version"));
- propertyMap.put("jre.availableProcessors",
- Integer.toString(Runtime.getRuntime().availableProcessors()));
-
- String osName = sysProps.get("os.name");
- propertyMap.put("os.name", osName);
- propertyMap.put("os.version", sysProps.get("os.version"));
- propertyMap.put("os.arch", sysProps.get("os.arch"));
-
- try {
- propertyMap.put("host.name", InetAddress.getLocalHost().getHostName());
- } catch (UnknownHostException ignored) {
- }
-
- if (osName.equals("Linux")) {
- getLinuxEnvironment(propertyMap);
- }
-
- return new Environment(propertyMap);
- }
-
- private void getLinuxEnvironment(Map<String, String> propertyMap) {
- // the following probably doesn't work on ALL linux
- Multimap<String, String> cpuInfo = propertiesFromLinuxFile("/proc/cpuinfo");
- propertyMap.put("host.cpus", Integer.toString(cpuInfo.get("processor").size()));
- String s = "cpu cores";
- propertyMap.put("host.cpu.cores", describe(cpuInfo, s));
- propertyMap.put("host.cpu.names", describe(cpuInfo, "model name"));
- propertyMap.put("host.cpu.cachesize", describe(cpuInfo, "cache size"));
-
- Multimap<String, String> memInfo = propertiesFromLinuxFile("/proc/meminfo");
- // TODO redo memInfo.toString() so we don't get square brackets
- propertyMap.put("host.memory.physical", memInfo.get("MemTotal").toString());
- propertyMap.put("host.memory.swap", memInfo.get("SwapTotal").toString());
-
- getAndroidEnvironment(propertyMap);
- }
-
- private void getAndroidEnvironment(Map<String, String> propertyMap) {
- try {
- Map<String, String> map = getAndroidProperties();
- String manufacturer = map.get("ro.product.manufacturer");
- String device = map.get("ro.product.device");
- propertyMap.put("android.device", manufacturer + " " + device); // "Motorola sholes"
-
- String brand = map.get("ro.product.brand");
- String model = map.get("ro.product.model");
- propertyMap.put("android.model", brand + " " + model); // "verizon Droid"
-
- String release = map.get("ro.build.version.release");
- String id = map.get("ro.build.id");
- propertyMap.put("android.release", release + " " + id); // "Gingerbread GRH07B"
- } catch (IOException ignored) {
- }
- }
-
- private static String describe(Multimap<String, String> cpuInfo, String s) {
- Collection<String> strings = cpuInfo.get(s);
- // TODO redo the ImmutableMultiset.toString() call so we don't get square brackets
- return (strings.size() == 1)
- ? strings.iterator().next()
- : ImmutableMultiset.copyOf(strings).toString();
- }
-
- /**
- * Returns the key/value pairs from the specified properties-file like
- * reader. Unlike standard Java properties files, {@code reader} is allowed
- * to list the same property multiple times. Comments etc. are unsupported.
- */
- private static Multimap<String, String> propertiesFileToMultimap(Reader reader)
- throws IOException {
- ImmutableMultimap.Builder<String, String> result = ImmutableMultimap.builder();
- BufferedReader in = new BufferedReader(reader);
-
- String line;
- while((line = in.readLine()) != null) {
- String[] parts = line.split("\\s*\\:\\s*", 2);
- if (parts.length == 2) {
- result.put(parts[0], parts[1]);
- }
- }
- in.close();
-
- return result.build();
- }
-
- private static Multimap<String, String> propertiesFromLinuxFile(String file) {
- try {
- Process process = Runtime.getRuntime().exec(new String[]{"/bin/cat", file});
- return propertiesFileToMultimap(
- new InputStreamReader(process.getInputStream(), "ISO-8859-1"));
- } catch (IOException e) {
- return ImmutableMultimap.of();
- }
- }
-
- public static void main(String[] args) {
- Environment snapshot = new EnvironmentGetter().getEnvironmentSnapshot();
- for (Map.Entry<String, String> entry : snapshot.getProperties().entrySet()) {
- System.out.println(entry.getKey() + " " + entry.getValue());
- }
- }
-
- /**
- * Android properties are available from adb shell /system/bin/getprop. That
- * program prints Android system properties in this format:
- * [ro.product.model]: [Droid]
- * [ro.product.brand]: [verizon]
- */
- private static Map<String, String> getAndroidProperties() throws IOException {
- Map<String, String> result = new HashMap<String, String>();
-
- Process process = Runtime.getRuntime().exec(new String[] {"/system/bin/getprop"});
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(process.getInputStream(), "ISO-8859-1"));
-
- Pattern pattern = Pattern.compile("\\[([^\\]]*)\\]: \\[([^\\]]*)\\]");
- String line;
- while ((line = reader.readLine()) != null) {
- Matcher matcher = pattern.matcher(line);
- if (matcher.matches()) {
- result.put(matcher.group(1), matcher.group(2));
- }
- }
- return result;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/InProcessRunner.java b/caliper/src/main/java/com/google/caliper/InProcessRunner.java
deleted file mode 100644
index 1ef4788..0000000
--- a/caliper/src/main/java/com/google/caliper/InProcessRunner.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.ExceptionFromUserCodeException;
-import com.google.common.base.Supplier;
-
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.List;
-
-/**
- * Executes a benchmark in the current VM.
- */
-final class InProcessRunner {
-
- public void run(String... args) {
- Arguments arguments = Arguments.parse(args);
-
- ScenarioSelection scenarioSelection = new ScenarioSelection(arguments);
-
- try {
- Measurer measurer = getMeasurer(arguments);
- List<Scenario> scenarios = scenarioSelection.select();
- // We only expect one scenario right now - if we have more, something has gone wrong.
- // This matters for things like reading the measurements. This is only done once, so if
- // multiple scenarios are executed, they will be ignored!
- if (scenarios.size() != 1) {
- throw new IllegalArgumentException("Invalid arguments to subprocess. Expected exactly one "
- + "scenario but got " + scenarios.size());
- }
- Scenario scenario = scenarios.get(0);
-
- System.out.println("starting " + scenario);
- MeasurementSet measurementSet = run(scenarioSelection, scenario, measurer);
- System.out.println(arguments.getMarker() + Json.measurementSetToJson(measurementSet));
- } catch (UserException e) {
- throw e;
- } catch (Exception e) {
- throw new ExceptionFromUserCodeException(e);
- }
- }
-
- public MeasurementSet run(final ScenarioSelection scenarioSelection, final Scenario scenario,
- Measurer measurer) throws Exception {
- Supplier<ConfiguredBenchmark> supplier = new Supplier<ConfiguredBenchmark>() {
- @Override public ConfiguredBenchmark get() {
- return scenarioSelection.createBenchmark(scenario);
- }
- };
-
- PrintStream out = System.out;
- PrintStream err = System.err;
- measurer.setLogStream(out);
- CountingPrintStream countedOut = new CountingPrintStream(out);
- CountingPrintStream countedErr = new CountingPrintStream(err);
- System.setOut(countedOut);
- System.setErr(countedErr);
- try {
- MeasurementSet measurementSet = measurer.run(supplier);
- if (measurementSet != null) {
- measurementSet = measurementSet.plusCharCounts(
- countedOut.getCount(), countedErr.getCount());
- }
- return measurementSet;
- } finally {
- System.setOut(out);
- System.setErr(err);
- }
- }
-
- private Measurer getMeasurer(Arguments arguments) {
- if (arguments.getMeasurementType() == MeasurementType.TIME) {
- return new TimeMeasurer(arguments.getWarmupMillis(), arguments.getRunMillis());
- } else if (arguments.getMeasurementType() == MeasurementType.INSTANCE) {
- return new InstancesAllocationMeasurer();
- } else if (arguments.getMeasurementType() == MeasurementType.MEMORY) {
- return new MemoryAllocationMeasurer();
- } else if (arguments.getMeasurementType() == MeasurementType.DEBUG) {
- return new DebugMeasurer(arguments.getDebugReps());
- } else {
- throw new IllegalArgumentException("unrecognized measurement type: "
- + arguments.getMeasurementType());
- }
- }
-
- public static void main(String... args) throws Exception {
- try {
- new InProcessRunner().run(args);
- System.exit(0); // user code may have leave non-daemon threads behind!
- } catch (UserException e) {
- e.display(); // TODO: send this to the host process
- System.out.println(LogConstants.CALIPER_LOG_PREFIX + LogConstants.SCENARIOS_FINISHED);
- System.exit(1);
- }
- }
-
- public PrintStream nullPrintStream() {
- return new PrintStream(new OutputStream() {
- public void write(int b) {}
- });
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java
deleted file mode 100644
index 3fe89c7..0000000
--- a/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-public final class InstancesAllocationMeasurer extends AllocationMeasurer {
-
- InstancesAllocationMeasurer() {
- type = "instance";
- }
-
- @Override protected long incrementAllocationCount(long oldAllocationCount, int arrayCount,
- long size) {
- return oldAllocationCount + 1;
- }
-
- @Override protected Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations) {
- return new Measurement(benchmark.instanceUnitNames(), allocations,
- benchmark.instancesToUnits(allocations));
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Json.java b/caliper/src/main/java/com/google/caliper/Json.java
deleted file mode 100644
index 9e3f809..0000000
--- a/caliper/src/main/java/com/google/caliper/Json.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-import com.google.gson.reflect.TypeToken;
-
-import java.lang.reflect.Type;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-
-/**
- * Ordinarily serialization should be done within the class that is being serialized. However,
- * many of these classes are used by GWT, which dies when it sees Gson.
- */
-public final class Json {
- /**
- * This Gson instance must be used when serializing a class that includes a Run as a member
- * or as a member of a member (etc.), otherwise the Map<Scenario, ScenarioResult> will not
- * be correctly serialized.
- */
- private static final Gson GSON_INSTANCE =
- new GsonBuilder()
- .registerTypeAdapter(Date.class, new DateTypeAdapter())
- .registerTypeAdapter(Run.class, new RunTypeAdapter())
- .registerTypeAdapter(Measurement.class, new MeasurementDeserializer())
- .create();
-
- public static Gson getGsonInstance() {
- return GSON_INSTANCE;
- }
-
- public static String measurementSetToJson(MeasurementSet measurementSet) {
- return new Gson().toJson(measurementSet);
- }
-
- /**
- * Attempts to extract a MeasurementSet from a string, assuming it is JSON. If this fails, it
- * tries to extract it from the string assuming it is a space-separated list of double values.
- */
- public static MeasurementSet measurementSetFromJson(String measurementSetJson) {
- try {
- return getGsonInstance().fromJson(measurementSetJson, MeasurementSet.class);
- } catch (JsonParseException e) {
- // might be an old MeasurementSet, so fall back on failure to the old, space separated
- // serialization method.
- try {
- String[] measurementStrings = measurementSetJson.split("\\s+");
- List<Measurement> measurements = new ArrayList<Measurement>();
- for (String s : measurementStrings) {
- measurements.add(
- new Measurement(ImmutableMap.of("ns", 1, "us", 1000, "ms", 1000000, "s", 1000000000),
- Double.valueOf(s), Double.valueOf(s)));
- }
- // seconds and variations is the default unit
- return new MeasurementSet(measurements.toArray(new Measurement[measurements.size()]));
- } catch (NumberFormatException ignore) {
- throw new IllegalArgumentException("Not a measurement set: " + measurementSetJson);
- }
- }
- }
-
- public static MeasurementSet measurementSetFromJson(JsonObject measurementSetJson) {
- return getGsonInstance().fromJson(measurementSetJson, MeasurementSet.class);
- }
-
- /**
- * Backwards compatibility!
- */
- private static class MeasurementDeserializer implements JsonDeserializer<Measurement> {
- @Override public Measurement deserialize(JsonElement jsonElement, Type type,
- JsonDeserializationContext context) throws JsonParseException {
- JsonObject obj = jsonElement.getAsJsonObject();
- if (obj.has("raw") && obj.has("processed")) {
- return new Measurement(
- context.<Map<String, Integer>>deserialize(obj.get("unitNames"),
- new TypeToken<Map<String, Integer>>() {}.getType()),
- context.<Double>deserialize(obj.get("raw"), Double.class),
- context.<Double>deserialize(obj.get("processed"), Double.class));
- }
- if (obj.has("nanosPerRep") && obj.has("unitsPerRep") && obj.has("unitNames")) {
- return new Measurement(
- context.<Map<String, Integer>>deserialize(obj.get("unitNames"),
- new TypeToken<Map<String, Integer>>() {}.getType()),
- context.<Double>deserialize(obj.get("nanosPerRep"), Double.class),
- context.<Double>deserialize(obj.get("unitsPerRep"), Double.class));
- }
- throw new JsonParseException(obj.toString());
- }
- }
-
- /**
- * This adapter is necessary because gson doesn't handle Maps more complex than Map<String, ...>
- * in a useful way. For example, Map<Scenario, ScenarioResult>'s serialized version simply uses
- * Scenario.toString() as the keys. This adapter stores this Map as lists of
- * KeyValuePair<Scenario, ScenarioResult> instead, to preserve the Scenario objects on
- * deserialization.
- */
- private static class RunTypeAdapter implements JsonSerializer<Run>, JsonDeserializer<Run> {
-
- @Override public Run deserialize(JsonElement jsonElement, Type type,
- JsonDeserializationContext context) throws JsonParseException {
-
- List<KeyValuePair<Scenario, ScenarioResult>> mapList = context.deserialize(
- jsonElement.getAsJsonObject().get("measurements"),
- new TypeToken<List<KeyValuePair<Scenario, ScenarioResult>>>() {}.getType());
- Map<Scenario, ScenarioResult> measurements = new LinkedHashMap<Scenario, ScenarioResult>();
- for (KeyValuePair<Scenario, ScenarioResult> entry : mapList) {
- measurements.put(entry.getKey(), entry.getValue());
- }
-
- String benchmarkName =
- context.deserialize(jsonElement.getAsJsonObject().get("benchmarkName"), String.class);
-
- Date executedTimestamp = context.deserialize(
- jsonElement.getAsJsonObject().get("executedTimestamp"), Date.class);
-
- return new Run(measurements, benchmarkName, executedTimestamp);
- }
-
- @Override public JsonElement serialize(Run run, Type type, JsonSerializationContext context) {
- JsonObject result = new JsonObject();
- result.add("benchmarkName", context.serialize(run.getBenchmarkName()));
- result.add("executedTimestamp", context.serialize(run.getExecutedTimestamp()));
-
- List<KeyValuePair<Scenario, ScenarioResult>> mapList =
- new ArrayList<KeyValuePair<Scenario, ScenarioResult>>();
- for (Map.Entry<Scenario, ScenarioResult> entry : run.getMeasurements().entrySet()) {
- mapList.add(new KeyValuePair<Scenario, ScenarioResult>(entry.getKey(), entry.getValue()));
- }
- result.add("measurements", context.serialize(mapList,
- new TypeToken<List<KeyValuePair<Scenario, ScenarioResult>>>() {}.getType()));
-
- return result;
- }
- }
-
- private static class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
- private final DateFormat dateFormat;
-
- private DateTypeAdapter() {
- dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz", Locale.US);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- @Override public synchronized JsonElement serialize(Date date, Type type,
- JsonSerializationContext jsonSerializationContext) {
- return new JsonPrimitive(dateFormat.format(date));
- }
-
- @Override public synchronized Date deserialize(JsonElement jsonElement, Type type,
- JsonDeserializationContext jsonDeserializationContext) {
- String dateString = jsonElement.getAsString();
- // first try to parse as an ISO 8601 date
- try {
- return dateFormat.parse(dateString);
- } catch (ParseException ignored) {
- }
- // next, try a GSON-style locale-specific dates (for Caliper r282 and earlier)
- try {
- return DateFormat.getDateTimeInstance().parse(dateString);
- } catch (ParseException ignored) {
- }
- throw new JsonParseException(dateString);
- }
- }
-
- /**
- * This is similar to the Map.Entry class, but is necessary since Entrys are not supported
- * by gson.
- */
- private static class KeyValuePair<K, V> {
- private K k;
- private V v;
-
- KeyValuePair(K k, V v) {
- this.k = k;
- this.v = v;
- }
-
- public K getKey() {
- return k;
- }
-
- public V getValue() {
- return v;
- }
-
- @SuppressWarnings("unused")
- private KeyValuePair() {} // for gson
- }
-
- private Json() {} // static class
-}
diff --git a/caliper/src/main/java/com/google/caliper/LogConstants.java b/caliper/src/main/java/com/google/caliper/LogConstants.java
deleted file mode 100644
index 09acebe..0000000
--- a/caliper/src/main/java/com/google/caliper/LogConstants.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-public final class LogConstants {
- /**
- * Must be prepended to line of XML that represents normalized scenario.
- */
- public static final String SCENARIO_JSON_PREFIX = "[scenario] ";
- public static final String MEASUREMENT_JSON_PREFIX = "[measurement] ";
-
- /**
- * Must be prepended to any logs that are to be included in the run event log.
- */
- public static final String CALIPER_LOG_PREFIX = "[caliper] ";
- public static final String SCENARIOS_STARTING = "[starting scenarios]";
- public static final String STARTING_SCENARIO_PREFIX = "[starting scenario] ";
- public static final String SCENARIO_FINISHED = "[scenario finished]";
- public static final String SCENARIOS_FINISHED = "[scenarios finished]";
-
- /**
- * All events will be logged from when {@code MEASURED_SECTION_STARTING} is logged until
- * {@code MEASURED_SECTION_DONE} is logged.
- */
- public static final String MEASURED_SECTION_STARTING = "[starting measured section]";
- public static final String MEASURED_SECTION_DONE = "[done measured section]";
-
- private LogConstants() {}
-}
diff --git a/caliper/src/main/java/com/google/caliper/Measurement.java b/caliper/src/main/java/com/google/caliper/Measurement.java
deleted file mode 100644
index 35865b6..0000000
--- a/caliper/src/main/java/com/google/caliper/Measurement.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Represents a measurement of a single run of a benchmark.
- */
-@SuppressWarnings("serial")
-@GwtCompatible
-public final class Measurement
- implements Serializable /* for GWT */ {
-
- public static final Comparator<Measurement> SORT_BY_NANOS = new Comparator<Measurement>() {
- @Override public int compare(Measurement a, Measurement b) {
- double aNanos = a.getRaw();
- double bNanos = b.getRaw();
- return Double.compare(aNanos, bNanos);
- }
- };
-
- public static final Comparator<Measurement> SORT_BY_UNITS = new Comparator<Measurement>() {
- @Override public int compare(Measurement a, Measurement b) {
- double aNanos = a.getProcessed();
- double bNanos = b.getProcessed();
- return Double.compare(aNanos, bNanos);
- }
- };
-
- private /*final*/ double raw;
- private /*final*/ double processed;
- private /*final*/ Map<String, Integer> unitNames;
-
- public Measurement(Map<String, Integer> unitNames, double raw, double processed) {
- this.unitNames = new HashMap<String, Integer>(unitNames);
- this.raw = raw;
- this.processed = processed;
- }
-
- public Map<String, Integer> getUnitNames() {
- return new HashMap<String, Integer>(unitNames);
- }
-
- public double getRaw() {
- return raw;
- }
-
- public double getProcessed() {
- return processed;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof Measurement
- && ((Measurement) o).raw == raw
- && ((Measurement) o).processed == processed
- && ((Measurement) o).unitNames.equals(unitNames);
- }
-
- @Override public int hashCode() {
- return (int) raw // Double.doubleToLongBits doesn't exist on GWT
- + (int) processed * 37
- + unitNames.hashCode() * 1373;
- }
-
- @Override public String toString() {
- return (raw != processed
- ? raw + "/" + processed
- : Double.toString(raw));
- }
-
- @SuppressWarnings("unused")
- private Measurement() {} /* for GWT */
-}
diff --git a/caliper/src/main/java/com/google/caliper/MeasurementSet.java b/caliper/src/main/java/com/google/caliper/MeasurementSet.java
deleted file mode 100644
index eb0b42a..0000000
--- a/caliper/src/main/java/com/google/caliper/MeasurementSet.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A collection of measurements of the same scenario.
- */
-@SuppressWarnings("serial")
-@GwtCompatible
-public final class MeasurementSet
- implements Serializable /* for GWT Serialization */ {
-
- private /*final*/ List<Measurement> measurements;
- /**
- * Mapping of user-defined units to relative sizes.
- */
- private /*final*/ Map<String, Integer> unitNames;
-
- private /*final*/ int systemOutCharCount;
- private /*final*/ int systemErrCharCount;
-
- public MeasurementSet(Measurement... measurements) {
- this(0, 0, getUnitNamesFromMeasurements(measurements), Arrays.asList(measurements));
- }
-
- private static Map<String, Integer> getUnitNamesFromMeasurements(Measurement... measurements) {
- Map<String, Integer> unitNameToAssign = null;
- for (Measurement measurement : measurements) {
- if (unitNameToAssign == null) {
- unitNameToAssign = new HashMap<String, Integer>(measurement.getUnitNames());
- } else if (!unitNameToAssign.equals(measurement.getUnitNames())) {
- throw new IllegalArgumentException("incompatible unit names: " + unitNameToAssign + " and "
- + measurement.getUnitNames());
- }
- }
- return unitNameToAssign;
- }
-
- /**
- * Constructor to use directly from plusMeasurement. Skips some excessive checking and takes a
- * list directly.
- */
- private MeasurementSet(int systemOutCharCount, int systemErrCharCount,
- Map<String, Integer> unitNames, List<Measurement> measurements) {
- this.systemOutCharCount = systemOutCharCount;
- this.systemErrCharCount = systemErrCharCount;
- this.unitNames = unitNames;
- this.measurements = measurements;
- }
-
- /**
- * This is the same as getUnitNames(), but is for backwards compatibility on the server
- * when null pointer exceptions need to be avoided.
- */
- public Map<String, Integer> getUnitNames(Map<String, Integer> defaultValue) {
- if (unitNames == null) {
- return defaultValue;
- }
- return new HashMap<String, Integer>(unitNames);
- }
-
- public Map<String, Integer> getUnitNames() {
- return new HashMap<String, Integer>(unitNames);
- }
-
- public List<Measurement> getMeasurements() {
- return new ArrayList<Measurement>(measurements);
- }
-
- public int size() {
- return measurements.size();
- }
-
- public int getSystemOutCharCount() {
- return systemOutCharCount;
- }
-
- public int getSystemErrCharCount() {
- return systemErrCharCount;
- }
-
- public List<Double> getMeasurementsRaw() {
- List<Double> measurementRaw = new ArrayList<Double>();
- for (Measurement measurement : measurements) {
- measurementRaw.add(measurement.getRaw());
- }
- return measurementRaw;
- }
-
- public List<Double> getMeasurementUnits() {
- List<Double> measurementUnits = new ArrayList<Double>();
- for (Measurement measurement : measurements) {
- measurementUnits.add(measurement.getProcessed());
- }
- return measurementUnits;
- }
-
- /**
- * Returns the median measurement, with respect to raw units.
- */
- public double medianRaw() {
- return median(getMeasurementsRaw());
- }
-
- /**
- * Returns the median measurement, with respect to user-defined units.
- */
- public double medianUnits() {
- return median(getMeasurementUnits());
- }
-
- private double median(List<Double> doubles) {
- Collections.sort(doubles);
- int n = doubles.size();
- return (n % 2 == 0)
- ? (doubles.get(n / 2 - 1) + doubles.get(n / 2)) / 2
- : doubles.get(n / 2);
- }
-
- /**
- * Returns the average measurement with respect to raw units.
- */
- public double meanRaw() {
- return mean(getMeasurementsRaw());
- }
-
- /**
- * Returns the average measurement with respect to user-defined units.
- */
- public double meanUnits() {
- return mean(getMeasurementUnits());
- }
-
- private double mean(List<Double> doubles) {
- double sum = 0;
- for (double d : doubles) {
- sum += d;
- }
- return sum / doubles.size();
- }
-
- public double standardDeviationRaw() {
- return standardDeviation(getMeasurementsRaw());
- }
-
- public double standardDeviationUnits() {
- return standardDeviation(getMeasurementUnits());
- }
-
- /**
- * Returns the standard deviation of the measurements.
- */
- private double standardDeviation(List<Double> doubles) {
- double mean = mean(doubles);
- double sumOfSquares = 0;
- for (double d : doubles) {
- double delta = (d - mean);
- sumOfSquares += (delta * delta);
- }
- return Math.sqrt(sumOfSquares / (doubles.size() - 1));
- }
-
- public double minRaw() {
- return min(getMeasurementsRaw());
- }
-
- public double minUnits() {
- return min(getMeasurementUnits());
- }
-
- /**
- * Returns the minimum measurement.
- */
- private double min(List<Double> doubles) {
- Collections.sort(doubles);
- return doubles.get(0);
- }
-
- public double maxRaw() {
- return max(getMeasurementsRaw());
- }
-
- public double maxUnits() {
- return max(getMeasurementUnits());
- }
-
- /**
- * Returns the maximum measurement.
- */
- private double max(List<Double> doubles) {
- Collections.sort(doubles, Collections.reverseOrder());
- return doubles.get(0);
- }
-
- /**
- * Returns a new measurement set that contains the measurements in this set
- * plus the given additional measurement.
- */
- public MeasurementSet plusMeasurement(Measurement measurement) {
- // verify that this Measurement is compatible with this MeasurementSet
- if (unitNames != null && !unitNames.equals(measurement.getUnitNames())) {
- throw new IllegalArgumentException("new measurement incompatible with units of measurement "
- + "set. Expected " + unitNames + " but got " + measurement.getUnitNames());
- }
-
- List<Measurement> resultMeasurements = new ArrayList<Measurement>(measurements);
- resultMeasurements.add(measurement);
- Map<String, Integer> newUnitNames = unitNames == null ? measurement.getUnitNames() : unitNames;
- return new MeasurementSet(systemOutCharCount, systemErrCharCount,
- newUnitNames, resultMeasurements);
- }
-
- public MeasurementSet plusCharCounts(int systemOutCharCount, int systemErrCharCount) {
- return new MeasurementSet(this.systemOutCharCount + systemOutCharCount,
- this.systemErrCharCount + systemErrCharCount, unitNames, measurements);
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof MeasurementSet
- && ((MeasurementSet) o).measurements.equals(measurements)
- && ((MeasurementSet) o).unitNames.equals(unitNames)
- && ((MeasurementSet) o).systemOutCharCount == systemOutCharCount
- && ((MeasurementSet) o).systemErrCharCount == systemErrCharCount;
- }
-
- @Override public int hashCode() {
- return measurements.hashCode()
- + unitNames.hashCode() * 37
- + systemOutCharCount * 1373
- + systemErrCharCount * 53549;
- }
-
- @Override public String toString() {
- return measurements.toString() + " " + unitNames + " "
- + systemOutCharCount + "/" + systemErrCharCount;
- }
-
- @SuppressWarnings("unused")
- private MeasurementSet() {} // for GWT Serialization
-}
diff --git a/caliper/src/main/java/com/google/caliper/Measurer.java b/caliper/src/main/java/com/google/caliper/Measurer.java
deleted file mode 100644
index 93002e6..0000000
--- a/caliper/src/main/java/com/google/caliper/Measurer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.base.Supplier;
-
-import java.io.PrintStream;
-
-abstract class Measurer {
-
- private PrintStream logStream = System.out;
-
- /**
- * Sets the stream used to log caliper events.
- */
- void setLogStream(PrintStream logStream) {
- this.logStream = logStream;
- }
-
- public abstract MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) throws Exception;
-
- protected void prepareForTest() {
- System.gc();
- System.gc();
- }
-
- protected final void log(String message) {
- logStream.println(LogConstants.CALIPER_LOG_PREFIX + message);
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java
deleted file mode 100644
index a28aabf..0000000
--- a/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-public final class MemoryAllocationMeasurer extends AllocationMeasurer {
-
- public MemoryAllocationMeasurer() {
- type = "byte";
- }
-
- @Override protected long incrementAllocationCount(long oldAllocationCount, int arrayCount,
- long size) {
- return oldAllocationCount + size;
- }
-
- @Override protected Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations) {
- return new Measurement(benchmark.memoryUnitNames(), allocations,
- benchmark.bytesToUnits(allocations));
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Param.java b/caliper/src/main/java/com/google/caliper/Param.java
index f4cdf45..82e3471 100644
--- a/caliper/src/main/java/com/google/caliper/Param.java
+++ b/caliper/src/main/java/com/google/caliper/Param.java
@@ -29,12 +29,6 @@ import java.lang.annotation.Target;
* <ul>
* <li>The command line, if specified using {@code -Dname=value1,value2,value3}
* <li>Otherwise, the {@link #value()} list given in the annotation
- * <li>Otherwise, Caliper looks for a static method named {@code paramName + "Values"} (for
- * example, if the parameter field is {@code size}, it looks for {@code sizeValues()}). The
- * method can return any subtype of {@link Iterable}. The contents of that iterable are used as
- * the parameter values.
- * <li>Otherwise, Caliper repeats the previous check looking for a static <em>field</em> instead
- * of a method.
* <li>Otherwise, if the parameter type is either {@code boolean} or an {@code enum} type, Caliper
* assumes you want all possible values.
* <li>Finally, if none of the above match, Caliper will display an error and exit.
@@ -47,7 +41,7 @@ import java.lang.annotation.Target;
*
* <p>Caliper will test every possible combination of parameter values for your benchmark. For
* example, if you have two parameters, {@code -Dletter=a,b,c -Dnumber=1,2}, Caliper will construct
- * six independent "scenarios" and perform measurement for each one.
+ * six independent "scenarios" and perform measurement for each one.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
diff --git a/caliper/src/main/java/com/google/caliper/Parameter.java b/caliper/src/main/java/com/google/caliper/Parameter.java
deleted file mode 100644
index c79bc86..0000000
--- a/caliper/src/main/java/com/google/caliper/Parameter.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/**
- * A parameter in a {@link SimpleBenchmark}.
- *
- * @param <T> the (possibly wrapped) type of the parameter field, such as {@link
- * String} or {@link Integer}
- */
-abstract class Parameter<T> {
-
- private final Field field;
-
- private Parameter(Field field) {
- this.field = field;
- }
-
- /**
- * Returns all parameters for the given class.
- */
- public static Map<String, Parameter<?>> forClass(Class<? extends Benchmark> suiteClass) {
- Map<String, Parameter<?>> parameters = new TreeMap<String, Parameter<?>>();
- for (Field field : suiteClass.getDeclaredFields()) {
- if (field.isAnnotationPresent(Param.class)) {
- field.setAccessible(true);
- Parameter<?> parameter = forField(suiteClass, field);
- parameters.put(parameter.getName(), parameter);
- }
- }
- return parameters;
- }
-
- @VisibleForTesting
- static Parameter<?> forField(
- Class<? extends Benchmark> suiteClass, final Field field) {
- // First check for String values on the annotation itself
- final Object[] defaults = field.getAnnotation(Param.class).value();
- if (defaults.length > 0) {
- return new Parameter<Object>(field) {
- @Override public Iterable<Object> values() throws Exception {
- return Arrays.asList(defaults);
- }
- };
- // TODO: or should we continue so we can give an error/warning if params are also give in a
- // method or field?
- }
-
- Parameter<?> result = null;
- Type returnType = null;
- Member member = null;
-
- // Now check for a fooValues() method
- try {
- final Method valuesMethod = suiteClass.getDeclaredMethod(field.getName() + "Values");
- if (!Modifier.isStatic(valuesMethod.getModifiers())) {
- throw new ConfigurationException("Values method must be static " + member);
- }
- valuesMethod.setAccessible(true);
- member = valuesMethod;
- returnType = valuesMethod.getGenericReturnType();
- result = new Parameter<Object>(field) {
- @SuppressWarnings("unchecked") // guarded below
- @Override public Iterable<Object> values() throws Exception {
- return (Iterable<Object>) valuesMethod.invoke(null);
- }
- };
- } catch (NoSuchMethodException ignored) {
- }
-
- // Now check for a fooValues field
- try {
- final Field valuesField = suiteClass.getDeclaredField(field.getName() + "Values");
- if (!Modifier.isStatic(valuesField.getModifiers())) {
- throw new ConfigurationException("Values field must be static " + member);
- }
- valuesField.setAccessible(true);
- member = valuesField;
- if (result != null) {
- throw new ConfigurationException("Two values members defined for " + field);
- }
- returnType = valuesField.getGenericType();
- result = new Parameter<Object>(field) {
- @SuppressWarnings("unchecked") // guarded below
- @Override public Iterable<Object> values() throws Exception {
- return (Iterable<Object>) valuesField.get(null);
- }
- };
- } catch (NoSuchFieldException ignored) {
- }
-
- // If there isn't a values member but the parameter is an enum, we default
- // to EnumSet.allOf.
- if (member == null && field.getType().isEnum()) {
- returnType = Collection.class;
- result = new Parameter<Object>(field) {
- // TODO: figure out the simplest way to make this compile and be green in IDEA too
- @SuppressWarnings({"unchecked", "RawUseOfParameterizedType", "RedundantCast"})
- // guarded above
- @Override public Iterable<Object> values() throws Exception {
- Set<Enum> set = EnumSet.allOf((Class<Enum>) field.getType());
- return Collections.<Object>unmodifiableSet(set);
- }
- };
- }
-
- // If it's boolean, default to (true, false)
- if (member == null && field.getType() == boolean.class) {
- returnType = Collection.class;
- result = new Parameter<Object>(field) {
- @Override public Iterable<Object> values() throws Exception {
- return Arrays.<Object>asList(Boolean.TRUE, Boolean.FALSE);
- }
- };
- }
-
- if (result == null) {
- return new Parameter<Object>(field) {
- @Override public Iterable<Object> values() {
- // TODO: need tests to make sure this fails properly when no cmdline params given and
- // works properly when they are given. Also, can we restructure the code so that we
- // just throw here instead of later?
- return Collections.emptySet();
- }
- };
- } else if (!isValidReturnType(returnType)) {
- throw new ConfigurationException("Invalid return type " + returnType
- + " for values member " + member + "; must be Collection");
- }
- return result;
- }
-
- private static boolean isValidReturnType(Type type) {
- if (type instanceof Class) {
- return isIterableClass(type);
- }
- if (type instanceof ParameterizedType) {
- return isIterableClass(((ParameterizedType) type).getRawType());
- }
- return false;
- }
-
- private static boolean isIterableClass(Type returnClass) {
- return Iterable.class.isAssignableFrom((Class<?>) returnClass);
- }
-
- /**
- * Sets the value of this property to the specified value for the given suite.
- */
- public void set(Benchmark suite, Object value) throws Exception {
- field.set(suite, value);
- }
-
- /**
- * Returns the available values of the property as specified by the suite.
- */
- public abstract Iterable<T> values() throws Exception;
-
- /**
- * Returns the parameter's type, such as double.class.
- */
- public Type getType() {
- return field.getGenericType();
- }
-
- /**
- * Returns the field's name.
- */
- String getName() {
- return field.getName();
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Result.java b/caliper/src/main/java/com/google/caliper/Result.java
deleted file mode 100644
index c57f5a3..0000000
--- a/caliper/src/main/java/com/google/caliper/Result.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-/**
- * Represents an invocation of a benchmark, including the run itself, as well as the environment
- * in which the run occurred.
- */
-public final class Result {
- private /*final*/ Run run;
- private /*final*/ Environment environment;
-
- public Result(Run run, Environment environment) {
- this.run = run;
- this.environment = environment;
- }
-
- public Run getRun() {
- return run;
- }
-
- public Environment getEnvironment() {
- return environment;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof Result
- && ((Result) o).run.equals(run)
- && ((Result) o).environment.equals(environment);
- }
-
- @Override public int hashCode() {
- return run.hashCode() * 37 + environment.hashCode();
- }
-
- @Override public String toString() {
- return run + "@" + environment;
- }
-
- @SuppressWarnings("unused")
- private Result() {} // for gson
-}
diff --git a/caliper/src/main/java/com/google/caliper/ResultsReader.java b/caliper/src/main/java/com/google/caliper/ResultsReader.java
deleted file mode 100644
index 2396dd3..0000000
--- a/caliper/src/main/java/com/google/caliper/ResultsReader.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.gson.JsonParseException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-/**
- * Helps with deserialization of results, given uncertainty about the format (xml or json) they
- * are in.
- */
-public final class ResultsReader {
- public Result getResult(InputStream in) throws IOException {
- // save input into a byte array since we may need to read it twice
- byte[] postedData = readAllBytes(in);
- Result result;
- InputStreamReader baisJsonReader = new InputStreamReader(new ByteArrayInputStream(postedData));
- try {
- result = Json.getGsonInstance().fromJson(baisJsonReader, Result.class);
- } catch (JsonParseException e) {
- // probably an old client is trying to send data, so try to parse it as XML instead.
- ByteArrayInputStream baisXml = new ByteArrayInputStream(postedData);
- try {
- result = Xml.resultFromXml(baisXml);
- } catch (Exception e2) {
- throw new RuntimeException(e);
- } finally {
- baisXml.close();
- }
- } finally {
- baisJsonReader.close();
- }
- return result;
- }
-
- private byte[] readAllBytes(InputStream in) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buf = new byte[4096];
- int read;
- while ((read = in.read(buf)) != -1) {
- baos.write(buf, 0, read);
- }
- return baos.toByteArray();
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Run.java b/caliper/src/main/java/com/google/caliper/Run.java
deleted file mode 100644
index 00f9350..0000000
--- a/caliper/src/main/java/com/google/caliper/Run.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * The complete result of a benchmark suite run.
- *
- * WARNING: a JSON representation of this class is stored on the app engine server. If any changes
- * are made to this class, a deserialization adapter must be written for this class to ensure
- * backwards compatibility.
- *
- * <p>Gwt-safe.
- */
-@SuppressWarnings("serial")
-@GwtCompatible
-public final class Run
- implements Serializable /* for GWT Serialization */ {
-
- private /*final*/ Map<Scenario, ScenarioResult> measurements;
- private /*final*/ String benchmarkName;
- private /*final*/ long executedTimestamp;
-
- // TODO: add more run properties such as checksums of the executed code
-
- public Run(Map<Scenario, ScenarioResult> measurements,
- String benchmarkName, Date executedTimestamp) {
- if (benchmarkName == null || executedTimestamp == null) {
- throw new NullPointerException();
- }
-
- this.measurements = new LinkedHashMap<Scenario, ScenarioResult>(measurements);
- this.benchmarkName = benchmarkName;
- this.executedTimestamp = executedTimestamp.getTime();
- }
-
- public Map<Scenario, ScenarioResult> getMeasurements() {
- return measurements;
- }
-
- public String getBenchmarkName() {
- return benchmarkName;
- }
-
- public Date getExecutedTimestamp() {
- return new Date(executedTimestamp);
- }
-
- @Override public boolean equals(Object o) {
- if (o instanceof Run) {
- Run that = (Run) o;
- return measurements.equals(that.measurements)
- && benchmarkName.equals(that.benchmarkName)
- && executedTimestamp == that.executedTimestamp;
- }
-
- return false;
- }
-
- @Override public int hashCode() {
- int result = measurements.hashCode();
- result = result * 37 + benchmarkName.hashCode();
- result = result * 37 + (int) ((executedTimestamp >> 32) ^ executedTimestamp);
- return result;
- }
-
- @Override public String toString() {
- return measurements.toString();
- }
-
- @SuppressWarnings("unused")
- private Run() {} // for GWT Serialization
-}
diff --git a/caliper/src/main/java/com/google/caliper/Runner.java b/caliper/src/main/java/com/google/caliper/Runner.java
deleted file mode 100644
index 57715c9..0000000
--- a/caliper/src/main/java/com/google/caliper/Runner.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.DisplayUsageException;
-import com.google.caliper.UserException.ExceptionFromUserCodeException;
-import com.google.caliper.util.InterleavedReader;
-import com.google.common.base.Joiner;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ObjectArrays;
-import com.google.common.io.Closeables;
-import com.google.gson.JsonObject;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.URL;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TimeZone;
-import java.util.regex.Pattern;
-
-/**
- * Creates, executes and reports benchmark runs.
- */
-public final class Runner {
-
- private static final FileFilter UPLOAD_FILE_FILTER = new FileFilter() {
- @Override public boolean accept(File file) {
- return file.getName().endsWith(".xml") || file.getName().endsWith(".json");
- }
- };
-
- private static final String FILE_NAME_DATE_FORMAT = "yyyy-MM-dd'T'HH-mm-ssZ";
-
- private static final Splitter ARGUMENT_SPLITTER
- = Splitter.on(Pattern.compile("\\s+")).omitEmptyStrings();
-
- /** Command line arguments to the process */
- private Arguments arguments;
- private ScenarioSelection scenarioSelection;
-
- private String createFileName(Result result) {
- String timestamp = createTimestamp();
- return String.format("%s.%s.json", result.getRun().getBenchmarkName(), timestamp);
- }
-
- private String createTimestamp() {
- SimpleDateFormat dateFormat = new SimpleDateFormat(FILE_NAME_DATE_FORMAT, Locale.US);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- dateFormat.setLenient(true);
- return dateFormat.format(new Date());
- }
-
- public void run(String... args) {
- this.arguments = Arguments.parse(args);
- File resultsUploadFile = arguments.getUploadResultsFile();
- if (resultsUploadFile != null) {
- uploadResultsFileOrDir(resultsUploadFile);
- return;
- }
- this.scenarioSelection = new ScenarioSelection(arguments);
- if (arguments.getDebug()) {
- debug();
- return;
- }
- Result result = runOutOfProcess();
- new ConsoleReport(result.getRun(), arguments).displayResults();
- boolean saveResultsLocally = arguments.getSaveResultsFile() != null;
- try {
- postResults(result);
- } catch (Exception e) {
- System.out.println();
- System.out.println(e);
- saveResultsLocally = true;
- }
-
- if (saveResultsLocally) {
- saveResults(result);
- }
- }
-
- void uploadResultsFileOrDir(File resultsFileOrDir) {
- try {
- if (resultsFileOrDir.isDirectory()) {
- for (File resultsFile : resultsFileOrDir.listFiles(UPLOAD_FILE_FILTER)) {
- uploadResults(resultsFile);
- }
- } else {
- uploadResults(resultsFileOrDir);
- }
- } catch (Exception e) {
- throw new RuntimeException("uploading XML file failed", e);
- }
- }
-
- private void uploadResults(File resultsUploadFile) throws IOException {
- System.out.println();
- System.out.println("Uploading " + resultsUploadFile.getCanonicalPath());
- InputStream inputStream = new FileInputStream(resultsUploadFile);
- try {
- Result result = new ResultsReader().getResult(inputStream);
- postResults(result);
- } finally {
- inputStream.close();
- }
- }
-
- private void saveResults(Result result) {
- File resultsFile = arguments.getSaveResultsFile();
- File destinationFile;
- if (resultsFile == null) {
- File dir = new File("./caliper-results");
- dir.mkdirs();
- destinationFile = new File(dir, createFileName(result));
- } else if (resultsFile.exists() && resultsFile.isDirectory()) {
- destinationFile = new File(resultsFile, createFileName(result));
- } else {
- // assume this is a file
- File parent = resultsFile.getParentFile();
- if (parent != null) {
- parent.mkdirs();
- }
- destinationFile = resultsFile;
- }
-
- PrintStream filePrintStream;
- try {
- filePrintStream = new PrintStream(new FileOutputStream(destinationFile));
- } catch (FileNotFoundException e) {
- throw new RuntimeException("can't open " + destinationFile, e);
- }
- String resultJson = Json.getGsonInstance().toJson(result);
- try {
- System.out.println();
- System.out.println("Writing results to " + destinationFile.getCanonicalPath());
- filePrintStream.print(resultJson);
- } catch (Exception e) {
- System.out.println(e);
- System.out.println("Failed to write results to file, writing to standard out instead:");
- System.out.println(resultJson);
- System.out.flush();
- } finally {
- filePrintStream.close();
- }
- }
-
- private void postResults(Result result) {
- CaliperRc caliperrc = CaliperRc.INSTANCE;
- String postUrl = caliperrc.getPostUrl();
- String apiKey = caliperrc.getApiKey();
- if (postUrl == null || apiKey == null) {
- // TODO: probably nicer to show a message if only one is null
- return;
- }
-
- try {
- URL url = new URL(postUrl + apiKey + "/" + result.getRun().getBenchmarkName());
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(getProxy());
- urlConnection.setDoOutput(true);
- String resultJson = Json.getGsonInstance().toJson(result);
- urlConnection.getOutputStream().write(resultJson.getBytes());
- if (urlConnection.getResponseCode() == 200) {
- System.out.println("");
- System.out.println("View current and previous benchmark results online:");
- BufferedReader in = new BufferedReader(
- new InputStreamReader(urlConnection.getInputStream()));
- System.out.println(" " + in.readLine());
- in.close();
- return;
- }
-
- System.out.println("Posting to " + postUrl + " failed: "
- + urlConnection.getResponseMessage());
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(urlConnection.getInputStream()));
- String line;
- while ((line = reader.readLine()) != null) {
- System.out.println(line);
- }
- reader.close();
- } catch (IOException e) {
- throw new RuntimeException("Posting to " + postUrl + " failed.", e);
- }
- }
-
- private Proxy getProxy() {
- String proxyAddress = CaliperRc.INSTANCE.getProxy();
- if (proxyAddress == null) {
- return Proxy.NO_PROXY;
- }
-
- String[] proxyHostAndPort = proxyAddress.trim().split(":");
- return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(
- proxyHostAndPort[0], Integer.parseInt(proxyHostAndPort[1])));
- }
-
- private ScenarioResult runScenario(Scenario scenario) {
- MeasurementResult timeMeasurementResult = measure(scenario, MeasurementType.TIME);
- MeasurementSet allocationMeasurements = null;
- String allocationEventLog = null;
- MeasurementSet memoryMeasurements = null;
- String memoryEventLog = null;
- if (arguments.getMeasureMemory()) {
- MeasurementResult allocationsMeasurementResult =
- measure(scenario, MeasurementType.INSTANCE);
- allocationMeasurements = allocationsMeasurementResult.getMeasurements();
- allocationEventLog = allocationsMeasurementResult.getEventLog();
- MeasurementResult memoryMeasurementResult =
- measure(scenario, MeasurementType.MEMORY);
- memoryMeasurements = memoryMeasurementResult.getMeasurements();
- memoryEventLog = memoryMeasurementResult.getEventLog();
- }
-
- return new ScenarioResult(timeMeasurementResult.getMeasurements(),
- timeMeasurementResult.getEventLog(),
- allocationMeasurements, allocationEventLog,
- memoryMeasurements, memoryEventLog);
- }
-
- private static class MeasurementResult {
- private final MeasurementSet measurements;
- private final String eventLog;
-
- MeasurementResult(MeasurementSet measurements, String eventLog) {
- this.measurements = measurements;
- this.eventLog = eventLog;
- }
-
- public MeasurementSet getMeasurements() {
- return measurements;
- }
-
- public String getEventLog() {
- return eventLog;
- }
- }
-
- private MeasurementResult measure(Scenario scenario, MeasurementType type) {
- Vm vm = new VmFactory().createVm(scenario);
- // this must be done before starting the forked process on certain VMs
- ProcessBuilder processBuilder = createCommand(scenario, vm, type)
- .redirectErrorStream(true);
- Process timeProcess;
- try {
- timeProcess = processBuilder.start();
- } catch (IOException e) {
- throw new RuntimeException("failed to start subprocess", e);
- }
-
- MeasurementSet measurementSet = null;
- StringBuilder eventLog = new StringBuilder();
- InterleavedReader reader = null;
- try {
- reader = new InterleavedReader(arguments.getMarker(),
- new InputStreamReader(timeProcess.getInputStream()));
- Object o;
- while ((o = reader.read()) != null) {
- if (o instanceof String) {
- eventLog.append(o);
- } else if (measurementSet == null) {
- JsonObject jsonObject = (JsonObject) o;
- measurementSet = Json.measurementSetFromJson(jsonObject);
- } else {
- throw new RuntimeException("Unexpected value: " + o);
- }
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- Closeables.closeQuietly(reader);
- timeProcess.destroy();
- }
-
- if (measurementSet == null) {
- String message = "Failed to execute " + Joiner.on(" ").join(processBuilder.command());
- System.err.println(" " + message);
- System.err.println(eventLog.toString());
- throw new ConfigurationException(message);
- }
-
- return new MeasurementResult(measurementSet, eventLog.toString());
- }
-
- private ProcessBuilder createCommand(Scenario scenario, Vm vm, MeasurementType type) {
- File workingDirectory = new File(System.getProperty("user.dir"));
-
- String classPath = System.getProperty("java.class.path");
- if (classPath == null || classPath.length() == 0) {
- throw new IllegalStateException("java.class.path is undefined in " + System.getProperties());
- }
-
- ImmutableList.Builder<String> vmArgs = ImmutableList.builder();
- vmArgs.addAll(ARGUMENT_SPLITTER.split(scenario.getVariables().get(Scenario.VM_KEY)));
- if (type == MeasurementType.INSTANCE || type == MeasurementType.MEMORY) {
- String allocationJarFile = System.getenv("ALLOCATION_JAR");
- vmArgs.add("-javaagent:" + allocationJarFile);
- }
- vmArgs.addAll(vm.getVmSpecificOptions(type, arguments));
-
- Map<String, String> vmParameters = scenario.getVariables(
- scenarioSelection.getVmParameterNames());
- for (String vmParameter : vmParameters.values()) {
- vmArgs.addAll(ARGUMENT_SPLITTER.split(vmParameter));
- }
-
- ImmutableList.Builder<String> caliperArgs = ImmutableList.builder();
- caliperArgs.add("--warmupMillis").add(Long.toString(arguments.getWarmupMillis()));
- caliperArgs.add("--runMillis").add(Long.toString(arguments.getRunMillis()));
- caliperArgs.add("--measurementType").add(type.toString());
- caliperArgs.add("--marker").add(arguments.getMarker());
-
- Map<String,String> userParameters = scenario.getVariables(
- scenarioSelection.getUserParameterNames());
- for (Entry<String, String> entry : userParameters.entrySet()) {
- caliperArgs.add("-D" + entry.getKey() + "=" + entry.getValue());
- }
- caliperArgs.add(arguments.getSuiteClassName());
-
- return vm.newProcessBuilder(workingDirectory, classPath,
- vmArgs.build(), InProcessRunner.class.getName(), caliperArgs.build());
- }
-
- private void debug() {
- try {
- int debugReps = arguments.getDebugReps();
- InProcessRunner runner = new InProcessRunner();
- DebugMeasurer measurer = new DebugMeasurer(debugReps);
- for (Scenario scenario : scenarioSelection.select()) {
- System.out.println("running " + debugReps + " debug reps of " + scenario);
- runner.run(scenarioSelection, scenario, measurer);
- }
- } catch (Exception e) {
- throw new ExceptionFromUserCodeException(e);
- }
- }
-
- private Result runOutOfProcess() {
- Date executedDate = new Date();
- ImmutableMap.Builder<Scenario, ScenarioResult> resultsBuilder = ImmutableMap.builder();
-
- try {
- List<Scenario> scenarios = scenarioSelection.select();
-
- int i = 0;
- for (Scenario scenario : scenarios) {
- beforeMeasurement(i++, scenarios.size(), scenario);
- ScenarioResult scenarioResult = runScenario(scenario);
- afterMeasurement(arguments.getMeasureMemory(), scenarioResult);
- resultsBuilder.put(scenario, scenarioResult);
- }
- System.out.println();
-
- Environment environment = new EnvironmentGetter().getEnvironmentSnapshot();
- return new Result(
- new Run(resultsBuilder.build(), arguments.getSuiteClassName(), executedDate),
- environment);
- } catch (Exception e) {
- throw new ExceptionFromUserCodeException(e);
- }
- }
-
- private void beforeMeasurement(int index, int total, Scenario scenario) {
- double percentDone = (double) index / total;
- System.out.printf("%2.0f%% %s", percentDone * 100, scenario);
- }
-
- private void afterMeasurement(boolean memoryMeasured, ScenarioResult scenarioResult) {
- String memoryMeasurements = "";
- if (memoryMeasured) {
- MeasurementSet instanceMeasurementSet =
- scenarioResult.getMeasurementSet(MeasurementType.INSTANCE);
- String instanceUnit =
- ConsoleReport.UNIT_ORDERING.min(instanceMeasurementSet.getUnitNames().entrySet()).getKey();
- MeasurementSet memoryMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.MEMORY);
- String memoryUnit =
- ConsoleReport.UNIT_ORDERING.min(memoryMeasurementSet.getUnitNames().entrySet()).getKey();
- memoryMeasurements = String.format(", allocated %s%s for a total of %s%s",
- Math.round(instanceMeasurementSet.medianUnits()), instanceUnit,
- Math.round(memoryMeasurementSet.medianUnits()), memoryUnit);
- }
-
- MeasurementSet timeMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.TIME);
- String unit =
- ConsoleReport.UNIT_ORDERING.min(timeMeasurementSet.getUnitNames().entrySet()).getKey();
- System.out.printf(" %.2f %s; \u03C3=%.2f %s @ %d trials%s%n", timeMeasurementSet.medianUnits(),
- unit, timeMeasurementSet.standardDeviationUnits(), unit,
- timeMeasurementSet.getMeasurements().size(), memoryMeasurements);
- }
-
- public static void main(String[] args) {
- try {
- new Runner().run(args);
- System.exit(0); // user code may have leave non-daemon threads behind!
- } catch (DisplayUsageException e) {
- e.display();
- System.exit(0);
- } catch (UserException e) {
- e.display();
- System.exit(1);
- }
- }
-
- @SuppressWarnings("unchecked") // temporary fakery
- public static void main(Class<? extends Benchmark> suite, String[] args) {
- main(ObjectArrays.concat(args, suite.getName()));
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Scenario.java b/caliper/src/main/java/com/google/caliper/Scenario.java
deleted file mode 100644
index bc026d8..0000000
--- a/caliper/src/main/java/com/google/caliper/Scenario.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A configured benchmark.
- *
- * WARNING: a JSON representation of this class is stored on the app engine server. If any changes
- * are made to this class, a deserialization adapter must be written for this class to ensure
- * backwards compatibility.
- *
- * <p>Gwt-safe.
- */
-@SuppressWarnings("serial")
-@GwtCompatible
-public final class Scenario
- implements Serializable /* for GWT */ {
-
- static final String VM_KEY = "vm";
- static final String TRIAL_KEY = "trial";
-
- private /*final*/ Map<String, String> variables;
-
- public Scenario(Map<String, String> variables) {
- this.variables = new LinkedHashMap<String, String>(variables);
- }
-
- public Map<String, String> getVariables() {
- return variables;
- }
-
- /**
- * Returns the named set of variables.
- */
- public Map<String, String> getVariables(Set<String> names) {
- Map<String, String> result = new LinkedHashMap<String, String>(variables);
- result.keySet().retainAll(names);
- if (!result.keySet().equals(names)) {
- throw new IllegalArgumentException("Not all of " + names + " are in " + result.keySet());
- }
- return result;
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof Scenario
- && ((Scenario) o).getVariables().equals(variables);
- }
-
- @Override public int hashCode() {
- return variables.hashCode();
- }
-
- @Override public String toString() {
- return "Scenario" + variables;
- }
-
- @SuppressWarnings("unused")
- private Scenario() {} // for GWT
-}
diff --git a/caliper/src/main/java/com/google/caliper/ScenarioResult.java b/caliper/src/main/java/com/google/caliper/ScenarioResult.java
deleted file mode 100644
index 1fa687d..0000000
--- a/caliper/src/main/java/com/google/caliper/ScenarioResult.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.annotations.GwtCompatible;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Holds the results for a particular scenario, including timing measurements, memory use
- * measurements, and event logs for both, recording significant events during measurement.
- *
- * WARNING: a JSON representation of this class is stored on the app engine server. If any changes
- * are made to this class, a deserialization adapter must be written for this class to ensure
- * backwards compatibility.
- *
- * <p>Gwt-safe.
- */
-@SuppressWarnings({"serial", "FieldMayBeFinal"})
-@GwtCompatible
-public final class ScenarioResult
- implements Serializable /* for GWT Serialization */ {
-
- // want these to be EnumMaps, but that upsets GWT
- private /*final*/ Map<String, MeasurementSet> measurementSetMap
- = new HashMap<String, MeasurementSet>();
- private /*final*/ Map<String, String> eventLogMap
- = new HashMap<String, String>();
-
- public ScenarioResult(MeasurementSet timeMeasurementSet,
- String timeEventLog, MeasurementSet instanceMeasurementSet,
- String instanceEventLog, MeasurementSet memoryMeasurementSet,
- String memoryEventLog) {
- if (timeMeasurementSet != null) {
- measurementSetMap.put(MeasurementType.TIME.toString(), timeMeasurementSet);
- eventLogMap.put(MeasurementType.TIME.toString(), timeEventLog);
- }
- if (instanceMeasurementSet != null) {
- measurementSetMap.put(MeasurementType.INSTANCE.toString(), instanceMeasurementSet);
- eventLogMap.put(MeasurementType.INSTANCE.toString(), instanceEventLog);
- }
- if (memoryMeasurementSet != null) {
- measurementSetMap.put(MeasurementType.MEMORY.toString(), memoryMeasurementSet);
- eventLogMap.put(MeasurementType.MEMORY.toString(), memoryEventLog);
- }
- }
-
- public MeasurementSet getMeasurementSet(MeasurementType type) {
- return measurementSetMap.get(type.toString());
- }
-
- public String getEventLog(MeasurementType type) {
- return eventLogMap.get(type.toString());
- }
-
- @Override public boolean equals(Object o) {
- return o instanceof ScenarioResult
- && ((ScenarioResult) o).measurementSetMap.equals(measurementSetMap)
- && ((ScenarioResult) o).eventLogMap.equals(eventLogMap);
- }
-
- @Override public int hashCode() {
- return measurementSetMap.hashCode() * 37 + eventLogMap.hashCode();
- }
-
- @Override public String toString() {
- return "measurementSetMap: " + measurementSetMap + ", eventLogMap: " + eventLogMap;
- }
-
- @SuppressWarnings("unused")
- private ScenarioResult() {} // for GWT Serialization
-}
diff --git a/caliper/src/main/java/com/google/caliper/ScenarioSelection.java b/caliper/src/main/java/com/google/caliper/ScenarioSelection.java
deleted file mode 100644
index fcfead0..0000000
--- a/caliper/src/main/java/com/google/caliper/ScenarioSelection.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.AbstractBenchmarkException;
-import com.google.caliper.UserException.DoesntImplementBenchmarkException;
-import com.google.caliper.UserException.ExceptionFromUserCodeException;
-import com.google.caliper.UserException.NoParameterlessConstructorException;
-import com.google.caliper.UserException.NoSuchClassException;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * Figures out which scenarios to benchmark given a benchmark suite, set of user
- * parameters, and set of user VMs.
- */
-public final class ScenarioSelection {
-
- private final Set<String> userVms;
- private final Multimap<String, String> vmParameters;
- private final String suiteClassName;
-
- /**
- * The user parameters specified on the command line. This may be a subset of
- * the effective user parameters because parameters not specified here may get
- * default values from the benchmark class.
- */
- private final Multimap<String, String> userParameterArguments;
-
- /**
- * The actual user parameters we'll use to run in the benchmark. This contains
- * the userParameterArguments plus the default user parameters.
- */
- private Multimap<String, String> userParameters;
-
- private final int trials;
- private Benchmark suite;
-
-
- public ScenarioSelection(Arguments arguments) {
- this(arguments.getUserVms(), arguments.getVmParameters(), arguments.getSuiteClassName(),
- arguments.getUserParameters(), arguments.getTrials());
- }
-
- public ScenarioSelection(Set<String> userVms, Multimap<String, String> vmParameters,
- String suiteClassName, Multimap<String, String> userParameterArguments, int trials) {
- this.userVms = userVms;
- this.vmParameters = vmParameters;
- this.suiteClassName = suiteClassName;
- this.userParameterArguments = userParameterArguments;
- this.trials = trials;
- }
-
- /**
- * Returns the selected scenarios for this benchmark.
- */
- public List<Scenario> select() {
- prepareSuite();
- userParameters = computeUserParameters();
- return createScenarios();
- }
-
- /**
- * Returns a normalized version of {@code scenario}, with information from {@code suite}
- * assisting in correcting problems.
- */
- public Scenario normalizeScenario(Scenario scenario) {
- // This only applies to SimpleBenchmarks since they accept the special "benchmark"
- // parameter. This is a special case because SimpleBenchmark is the most commonly
- // used benchmark class. Have to do this horrible stuff since Benchmark API
- // doesn't provide scenario-normalization (and it shouldn't), which SimpleBenchmark
- // requires.
- if (suite instanceof SimpleBenchmark) {
- return ((SimpleBenchmark) suite).normalizeScenario(scenario);
- }
-
- return scenario;
- }
-
- public Set<String> getUserParameterNames() {
- if (userParameters == null) {
- throw new IllegalStateException();
- }
- return userParameters.keySet();
- }
-
- public Set<String> getVmParameterNames() {
- return vmParameters.keySet();
- }
-
- public ConfiguredBenchmark createBenchmark(Scenario scenario) {
- return suite.createBenchmark(scenario.getVariables(getUserParameterNames()));
- }
-
- private void prepareSuite() {
- Class<?> benchmarkClass;
- try {
- benchmarkClass = getClassByName(suiteClassName);
- } catch (ExceptionInInitializerError e) {
- throw new ExceptionFromUserCodeException(e.getCause());
- } catch (ClassNotFoundException ignored) {
- throw new NoSuchClassException(suiteClassName);
- }
-
- Object s;
- try {
- Constructor<?> constructor = benchmarkClass.getDeclaredConstructor();
- constructor.setAccessible(true);
- s = constructor.newInstance();
- } catch (InstantiationException ignore) {
- throw new AbstractBenchmarkException(benchmarkClass);
- } catch (NoSuchMethodException ignore) {
- throw new NoParameterlessConstructorException(benchmarkClass);
- } catch (IllegalAccessException impossible) {
- throw new AssertionError(impossible); // shouldn't happen since we setAccessible(true)
- } catch (InvocationTargetException e) {
- throw new ExceptionFromUserCodeException(e.getCause());
- }
-
- if (s instanceof Benchmark) {
- this.suite = (Benchmark) s;
- } else {
- throw new DoesntImplementBenchmarkException(benchmarkClass);
- }
- }
-
- private static Class<?> getClassByName(String className) throws ClassNotFoundException {
- try {
- return Class.forName(className);
- } catch (ClassNotFoundException ignored) {
- // try replacing the last dot with a $, in case that helps
- // example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1
- // amusingly, the $ character means three different things in this one line alone
- String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1");
- return Class.forName(newName);
- }
- }
-
- private Multimap<String, String> computeUserParameters() {
- Multimap<String, String> result = LinkedHashMultimap.create();
- for (String key : suite.parameterNames()) {
- // first check if the user has specified values
- Collection<String> userValues = userParameterArguments.get(key);
- if (!userValues.isEmpty()) {
- result.putAll(key, userValues);
- // TODO: type convert 'em to validate?
-
- } else { // otherwise use the default values from the suite
- Set<String> values = suite.parameterValues(key);
- if (values.isEmpty()) {
- throw new ConfigurationException(key + " has no values. "
- + "Did you forget a -D" + key + "=<value> command line argument?");
- }
- result.putAll(key, values);
- }
- }
- return result;
- }
-
- /**
- * Returns a complete set of scenarios with every combination of variables.
- */
- private List<Scenario> createScenarios() {
- List<ScenarioBuilder> builders = new ArrayList<ScenarioBuilder>();
- builders.add(new ScenarioBuilder());
-
- Map<String, Collection<String>> variables = new LinkedHashMap<String, Collection<String>>();
- variables.put(Scenario.VM_KEY, userVms.isEmpty() ? VmFactory.defaultVms() : userVms);
- variables.put(Scenario.TRIAL_KEY, newListOfSize(trials));
- variables.putAll(userParameters.asMap());
- variables.putAll(vmParameters.asMap());
-
- for (Entry<String, Collection<String>> entry : variables.entrySet()) {
- Iterator<String> values = entry.getValue().iterator();
- if (!values.hasNext()) {
- throw new ConfigurationException("Not enough values for " + entry);
- }
-
- String firstValue = values.next();
- for (ScenarioBuilder builder : builders) {
- builder.variables.put(entry.getKey(), firstValue);
- }
-
- // multiply the size of the specs by the number of alternate values
- int size = builders.size();
- while (values.hasNext()) {
- String alternate = values.next();
- for (int s = 0; s < size; s++) {
- ScenarioBuilder copy = builders.get(s).copy();
- copy.variables.put(entry.getKey(), alternate);
- builders.add(copy);
- }
- }
- }
-
- List<Scenario> result = new ArrayList<Scenario>();
- for (ScenarioBuilder builder : builders) {
- result.add(normalizeScenario(builder.build()));
- }
-
- return result;
- }
-
- /**
- * Returns a list containing {@code count} distinct elements.
- */
- private Collection<String> newListOfSize(int count) {
- List<String> result = new ArrayList<String>();
- for (int i = 0; i < count; i++) {
- result.add(Integer.toString(i));
- }
- return result;
- }
-
- private static class ScenarioBuilder {
- final Map<String, String> variables = new LinkedHashMap<String, String>();
-
- ScenarioBuilder copy() {
- ScenarioBuilder result = new ScenarioBuilder();
- result.variables.putAll(variables);
- return result;
- }
-
- public Scenario build() {
- return new Scenario(variables);
- }
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java b/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java
deleted file mode 100644
index 01fc3a8..0000000
--- a/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.ExceptionFromUserCodeException;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A convenience class for implementing benchmarks in plain code.
- * Implementing classes must have a no-arguments constructor.
- *
- * <h3>Benchmarks</h3>
- * The benchmarks of a suite are defined by . They may be
- * static. They are not permitted to take parameters . . ..
- *
- * <h3>Parameters</h3>
- * See the {@link Param} documentation to learn about parameters.
- */
-public abstract class SimpleBenchmark
- implements Benchmark {
- private static final Class<?>[] ARGUMENT_TYPES = { int.class };
-
- private final Map<String, Parameter<?>> parameters;
- private final Map<String, Method> methods;
-
- protected SimpleBenchmark() {
- parameters = Parameter.forClass(getClass());
- methods = createTimedMethods();
-
- if (methods.isEmpty()) {
- throw new ConfigurationException(
- "No benchmarks defined in " + getClass().getName());
- }
- }
-
- protected void setUp() throws Exception {}
-
- protected void tearDown() throws Exception {}
-
- @Override public Set<String> parameterNames() {
- return ImmutableSet.<String>builder()
- .add("benchmark")
- .addAll(parameters.keySet())
- .build();
- }
-
- @Override public Set<String> parameterValues(String parameterName) {
- if ("benchmark".equals(parameterName)) {
- return methods.keySet();
- }
-
- Parameter<?> parameter = parameters.get(parameterName);
- if (parameter == null) {
- throw new IllegalArgumentException();
- }
- try {
- Iterable<?> values = parameter.values();
-
- ImmutableSet.Builder<String> result = ImmutableSet.builder();
- for (Object value : values) {
- result.add(String.valueOf(value));
- }
- return result.build();
- } catch (Exception e) {
- throw new ExceptionFromUserCodeException(e);
- }
- }
-
- @Override public ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues) {
- if (!parameterNames().equals(parameterValues.keySet())) {
- throw new IllegalArgumentException("Invalid parameters specified. Expected "
- + parameterNames() + " but was " + parameterValues.keySet());
- }
-
- String methodName = parameterValues.get("benchmark");
- final Method method = methods.get(methodName);
- if (method == null) {
- throw new IllegalArgumentException("Invalid parameters specified. \"time" + methodName + "\" "
- + "is not a method of this benchmark.");
- }
-
- try {
- @SuppressWarnings({"ClassNewInstance"}) // can throw any Exception, so we catch all Exceptions
- final SimpleBenchmark copyOfSelf = getClass().newInstance();
-
- for (Map.Entry<String, String> entry : parameterValues.entrySet()) {
- String parameterName = entry.getKey();
- if ("benchmark".equals(parameterName)) {
- continue;
- }
-
- Parameter<?> parameter = parameters.get(parameterName);
- Object value = TypeConverter.fromString(entry.getValue(), parameter.getType());
- parameter.set(copyOfSelf, value);
- }
- copyOfSelf.setUp();
-
- return new ConfiguredBenchmark(copyOfSelf) {
- @Override public Object run(int reps) throws Exception {
- try {
- return method.invoke(copyOfSelf, reps);
- } catch (InvocationTargetException e) {
- Throwable cause = e.getCause();
- if (cause instanceof Exception) {
- throw (Exception) cause;
- } else if (cause instanceof Error) {
- throw (Error) cause;
- } else {
- throw e;
- }
- }
- }
-
- @Override public void close() throws Exception {
- copyOfSelf.tearDown();
- }
- };
- } catch (Exception e) {
- throw new ExceptionFromUserCodeException(e);
- }
- }
-
- public Scenario normalizeScenario(Scenario scenario) {
- Map<String, String> variables =
- new LinkedHashMap<String, String>(scenario.getVariables());
- // Make sure the scenario contains method names without the prefixed "time". If
- // it has "time" prefixed, then remove it. Also check whether the user has
- // accidentally put a lower cased letter first, and fix it if necessary.
- String benchmark = variables.get("benchmark");
- Map<String, Method> timedMethods = createTimedMethods();
- if (timedMethods.get(benchmark) == null) {
- // try to upper case first character
- char[] benchmarkChars = benchmark.toCharArray();
- benchmarkChars[0] = Character.toUpperCase(benchmarkChars[0]);
- String upperCasedBenchmark = String.valueOf(benchmarkChars);
- if (timedMethods.get(upperCasedBenchmark) != null) {
- variables.put("benchmark", upperCasedBenchmark);
- } else if (benchmark.startsWith("time")) {
- variables.put("benchmark", benchmark.substring(4));
- }
- }
- return new Scenario(variables);
- }
-
- /**
- * Returns a spec for each benchmark defined in the specified class. The
- * returned specs have no parameter values; those must be added separately.
- */
- private Map<String, Method> createTimedMethods() {
- ImmutableMap.Builder<String, Method> result = ImmutableMap.builder();
- for (Method method : getClass().getDeclaredMethods()) {
- int modifiers = method.getModifiers();
- if (!method.getName().startsWith("time")) {
- continue;
- }
-
- if (!Modifier.isPublic(modifiers)
- || Modifier.isStatic(modifiers)
- || Modifier.isAbstract(modifiers)
- || !Arrays.equals(method.getParameterTypes(), ARGUMENT_TYPES)) {
- throw new ConfigurationException("Timed methods must be public, "
- + "non-static, non-abstract and take a single int parameter. "
- + "But " + method + " violates these requirements.");
- }
-
- result.put(method.getName().substring(4), method);
- }
-
- return result.build();
- }
-
- @Override public Map<String, Integer> getTimeUnitNames() {
- return ImmutableMap.of("ns", 1,
- "us", 1000,
- "ms", 1000000,
- "s", 1000000000);
- }
-
- @Override public double nanosToUnits(double nanos) {
- return nanos;
- }
-
- @Override public Map<String, Integer> getInstanceUnitNames() {
- return ImmutableMap.of(" instances", 1,
- "K instances", 1000,
- "M instances", 1000000,
- "B instances", 1000000000);
- }
-
- @Override public double instancesToUnits(long instances) {
- return instances;
- }
-
- @Override public Map<String, Integer> getMemoryUnitNames() {
- return ImmutableMap.of("B", 1,
- "KiB", 1024,
- "MiB", 1048576,
- "GiB", 1073741824);
- }
-
- @Override public double bytesToUnits(long bytes) {
- return bytes;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/StandardVm.java b/caliper/src/main/java/com/google/caliper/StandardVm.java
deleted file mode 100644
index 3366eb1..0000000
--- a/caliper/src/main/java/com/google/caliper/StandardVm.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-
-final class StandardVm extends Vm {
-
- @Override public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) {
- if (!arguments.getCaptureVmLog()) {
- return ImmutableList.of();
- }
-
- List<String> result = new ArrayList<String>();
- result.add("-verbose:gc");
- result.add("-Xbatch");
- result.add("-XX:+UseSerialGC");
- if (type == MeasurementType.TIME) {
- return Lists.newArrayList("-XX:+PrintCompilation");
- }
-
- return result;
- }
-
- public static String defaultVmName() {
- return "java";
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/TimeMeasurer.java b/caliper/src/main/java/com/google/caliper/TimeMeasurer.java
deleted file mode 100644
index 2925d2d..0000000
--- a/caliper/src/main/java/com/google/caliper/TimeMeasurer.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.caliper.UserException.DoesNotScaleLinearlyException;
-import com.google.caliper.UserException.RuntimeOutOfRangeException;
-import com.google.common.base.Supplier;
-
-/**
- * Measure's the benchmark's per-trial execution time.
- */
-class TimeMeasurer extends Measurer {
-
- private final long warmupNanos;
- private final long runNanos;
-
- /**
- * If the standard deviation of our measurements is within this tolerance, we
- * won't bother to perform additional measurements.
- */
- private static final double SHORT_CIRCUIT_TOLERANCE = 0.01;
-
- private static final int MAX_TRIALS = 10;
-
- TimeMeasurer(long warmupMillis, long runMillis) {
- checkArgument(warmupMillis > 50);
- checkArgument(runMillis > 50);
-
- this.warmupNanos = warmupMillis * 1000000;
- this.runNanos = runMillis * 1000000;
- }
-
- private double warmUp(Supplier<ConfiguredBenchmark> testSupplier) throws Exception {
- long elapsedNanos = 0;
- long netReps = 0;
- int reps = 1;
- boolean definitelyScalesLinearly = false;
-
- /*
- * Run progressively more reps at a time until we cross our warmup
- * threshold. This way any just-in-time compiler will be comfortable running
- * multiple iterations of our measurement method.
- */
- log("[starting warmup]");
- while (elapsedNanos < warmupNanos) {
- long nanos = measureReps(testSupplier.get(), reps);
- elapsedNanos += nanos;
-
- netReps += reps;
- reps *= 2;
-
- // if reps overflowed, that's suspicious! Check that it time scales with reps
- if (reps <= 0) {
- if (!definitelyScalesLinearly) {
- checkScalesLinearly(testSupplier);
- definitelyScalesLinearly = true;
- }
- reps = Integer.MAX_VALUE;
- }
- }
- log("[ending warmup]");
-
- double nanosPerExecution = (double) elapsedNanos / netReps;
- double lowerBound = 0.1;
- double upperBound = 10000000000.0;
- if (!(lowerBound <= nanosPerExecution && nanosPerExecution <= upperBound)) {
- throw new RuntimeOutOfRangeException(nanosPerExecution, lowerBound, upperBound);
- }
-
- return nanosPerExecution;
- }
-
- /**
- * Doing half as much work shouldn't take much more than half as much time. If
- * it does we have a broken benchmark!
- */
- private void checkScalesLinearly(Supplier<ConfiguredBenchmark> testSupplier) throws Exception {
- double half = measureReps(testSupplier.get(), Integer.MAX_VALUE / 2);
- double one = measureReps(testSupplier.get(), Integer.MAX_VALUE);
- if (half / one > 0.75) {
- throw new DoesNotScaleLinearlyException();
- }
- }
-
- /**
- * Measure the nanos per rep for the given test. This code uses an interesting
- * strategy to measure the runtime to minimize execution time when execution
- * time is consistent.
- * <ol>
- * <li>1.0x {@code runMillis} trial is run.
- * <li>0.5x {@code runMillis} trial is run.
- * <li>1.5x {@code runMillis} trial is run.
- * <li>At this point, the standard deviation of these trials is computed. If
- * it is within the threshold, the result is returned.
- * <li>Otherwise trials continue to be executed until either the threshold
- * is satisfied or the maximum number of runs have been executed.
- * </ol>
- *
- * @param testSupplier provides instances of the code under test. A new test
- * is created for each iteration because some benchmarks' performance
- * depends on which memory was allocated. See SetContainsBenchmark for an
- * example.
- */
- @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier)
- throws Exception {
- double estimatedNanosPerRep = warmUp(testSupplier);
-
- log("[measuring nanos per rep with scale 1.00]");
- Measurement measurement100 = measure(testSupplier, 1.00, estimatedNanosPerRep);
- log("[measuring nanos per rep with scale 0.50]");
- Measurement measurement050 = measure(testSupplier, 0.50, measurement100.getRaw());
- log("[measuring nanos per rep with scale 1.50]");
- Measurement measurement150 = measure(testSupplier, 1.50, measurement100.getRaw());
- MeasurementSet measurementSet =
- new MeasurementSet(measurement100, measurement050, measurement150);
-
- for (int i = 3; i < MAX_TRIALS; i++) {
- double threshold = SHORT_CIRCUIT_TOLERANCE * measurementSet.meanRaw();
- if (measurementSet.standardDeviationRaw() < threshold) {
- return measurementSet;
- }
-
- log("[performing additional measurement with scale 1.00]");
- Measurement measurement = measure(testSupplier, 1.00, measurement100.getRaw());
- measurementSet = measurementSet.plusMeasurement(measurement);
- }
-
- return measurementSet;
- }
-
- /**
- * Runs the test method for approximately {@code runNanos * durationScale}
- * nanos and returns a Measurement of the nanos per rep and units per rep.
- */
- private Measurement measure(Supplier<ConfiguredBenchmark> testSupplier,
- double durationScale, double estimatedNanosPerRep) throws Exception {
- int reps = (int) (durationScale * runNanos / estimatedNanosPerRep);
- if (reps == 0) {
- reps = 1;
- }
-
- log("[running trial with " + reps + " reps]");
- ConfiguredBenchmark benchmark = testSupplier.get();
- long elapsedTime = measureReps(benchmark, reps);
- double nanosPerRep = elapsedTime / (double) reps;
- log(String.format("[took %.2f nanoseconds per rep]", nanosPerRep));
- return new Measurement(benchmark.timeUnitNames(), nanosPerRep,
- benchmark.nanosToUnits(nanosPerRep));
- }
-
- /**
- * Returns the total nanos to run {@code reps}.
- */
- private long measureReps(ConfiguredBenchmark benchmark, int reps) throws Exception {
- prepareForTest();
- log(LogConstants.MEASURED_SECTION_STARTING);
- long startNanos = System.nanoTime();
- benchmark.run(reps);
- long endNanos = System.nanoTime();
- log(LogConstants.MEASURED_SECTION_DONE);
- benchmark.close();
- return endNanos - startNanos;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/TypeConverter.java b/caliper/src/main/java/com/google/caliper/TypeConverter.java
deleted file mode 100644
index c3268e0..0000000
--- a/caliper/src/main/java/com/google/caliper/TypeConverter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMap;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.Map;
-
-/**
- * Convert objects to and from Strings.
- */
-final class TypeConverter {
- private TypeConverter() {}
-
- public static Object fromString(String value, Type type) {
- if (type == String.class) {
- return value;
- }
-
- Class<?> c = wrap((Class<?>) type);
- try {
- Method m = c.getMethod("valueOf", String.class);
- m.setAccessible(true); // to permit inner enums, etc.
- return m.invoke(null, value);
- } catch (Exception e) {
- throw new UnsupportedOperationException(
- "Cannot convert " + value + " of type " + type, e);
- }
- }
-
- // safe because both Long.class and long.class are of type Class<Long>
- @SuppressWarnings("unchecked")
- private static <T> Class<T> wrap(Class<T> c) {
- return c.isPrimitive() ? (Class<T>) PRIMITIVES_TO_WRAPPERS.get(c) : c;
- }
-
- private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS
- = new ImmutableMap.Builder<Class<?>, Class<?>>()
- .put(boolean.class, Boolean.class)
- .put(byte.class, Byte.class)
- .put(char.class, Character.class)
- .put(double.class, Double.class)
- .put(float.class, Float.class)
- .put(int.class, Integer.class)
- .put(long.class, Long.class)
- .put(short.class, Short.class)
- .put(void.class, Void.class)
- .build();
-}
diff --git a/caliper/src/main/java/com/google/caliper/UserException.java b/caliper/src/main/java/com/google/caliper/UserException.java
deleted file mode 100644
index a4535a7..0000000
--- a/caliper/src/main/java/com/google/caliper/UserException.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import java.util.Arrays;
-import java.util.Set;
-
-/**
- * Signifies a problem that should be explained in user-friendly terms on the command line, without
- * a confusing stack trace, and optionally followed by a usage summary.
- */
-@SuppressWarnings("serial") // never going to serialize these... right?
-public abstract class UserException extends RuntimeException {
- protected UserException(String error) {
- super(error);
- }
-
- public abstract void display();
-
- // - - - -
-
- public abstract static class ErrorInUsageException extends UserException {
- protected ErrorInUsageException(String error) {
- super(error);
- }
-
- @Override public void display() {
- String message = getMessage();
- if (message != null) {
- System.err.println("Error: " + message);
- }
- Arguments.printUsage();
- }
- }
-
- public abstract static class ErrorInUserCodeException extends UserException {
- private final String remedy;
-
- protected ErrorInUserCodeException(String error, String remedy) {
- super(error);
- this.remedy = remedy;
- }
-
- @Override public void display() {
- System.err.println("Error: " + getMessage());
- System.err.println("Typical Remedy: " + remedy);
- }
- }
-
- // - - - -
-
- // Not technically an error, but works nicely this way anyway
- public static class DisplayUsageException extends ErrorInUsageException {
- public DisplayUsageException() {
- super(null);
- }
- }
-
- public static class IncompatibleArgumentsException extends ErrorInUsageException {
- public IncompatibleArgumentsException(String arg) {
- super("Some arguments passed in are incompatible with: " + arg);
- }
- }
-
- public static class UnrecognizedOptionException extends ErrorInUsageException {
- public UnrecognizedOptionException(String arg) {
- super("Argument not recognized: " + arg);
- }
- }
-
- public static class NoBenchmarkClassException extends ErrorInUsageException {
- public NoBenchmarkClassException() {
- super("No benchmark class specified.");
- }
- }
-
- public static class MultipleBenchmarkClassesException extends ErrorInUsageException {
- public MultipleBenchmarkClassesException(String a, String b) {
- super("Multiple benchmark classes specified: " + Arrays.asList(a, b));
- }
- }
-
- public static class MalformedParameterException extends ErrorInUsageException {
- public MalformedParameterException(String arg) {
- super("Malformed parameter: " + arg);
- }
- }
-
- public static class DuplicateParameterException extends ErrorInUsageException {
- public DuplicateParameterException(String arg) {
- super("Duplicate parameter: " + arg);
- }
- public DuplicateParameterException(Set<String> arg) {
- super("Duplicate parameters: " + arg);
- }
- }
-
- public static class InvalidParameterValueException extends ErrorInUsageException {
- public InvalidParameterValueException(String arg, String value) {
- super("Invalid value \"" + value + "\" for parameter: " + arg);
- }
- }
-
- public static class InvalidTrialsException extends ErrorInUsageException {
- public InvalidTrialsException(String arg) {
- super("Invalid trials: " + arg);
- }
- }
-
- public static class CantCustomizeInProcessVmException extends ErrorInUsageException {
- public CantCustomizeInProcessVmException() {
- super("Can't customize VM when running in process.");
- }
- }
-
- public static class NoSuchClassException extends ErrorInUsageException {
- public NoSuchClassException(String name) {
- super("No class named [" + name + "] was found (check CLASSPATH).");
- }
- }
-
- public static class RuntimeOutOfRangeException extends ErrorInUsageException {
- public RuntimeOutOfRangeException(
- double nanosPerExecution, double lowerBound, double upperBound) {
- super("Runtime " + nanosPerExecution + "ns/rep out of range "
- + lowerBound + "-" + upperBound);
- }
- }
-
- public static class DoesNotScaleLinearlyException extends ErrorInUsageException {
- public DoesNotScaleLinearlyException() {
- super("Doing 2x as much work didn't take 2x as much time! "
- + "Is the JIT optimizing away the body of your benchmark?");
- }
- }
-
- public static class NonConstantMemoryUsage extends ErrorInUsageException {
- public NonConstantMemoryUsage() {
- super("Not all reps of the inner loop allocate the same number of times! "
- + "The reps loop should use a constant number of allocations. "
- + "Are you using the value of reps inside the loop?");
- }
- }
-
- public static class AbstractBenchmarkException extends ErrorInUserCodeException {
- public AbstractBenchmarkException(Class<?> specifiedClass) {
- super("Class [" + specifiedClass.getName() + "] is abstract.", "Specify a concrete class.");
- }
- }
-
- public static class NoParameterlessConstructorException extends ErrorInUserCodeException {
- public NoParameterlessConstructorException(Class<?> specifiedClass) {
- super("Class [" + specifiedClass.getName() + "] has no parameterless constructor.",
- "Remove all constructors or add a parameterless constructor.");
- }
- }
-
- public static class DoesntImplementBenchmarkException extends ErrorInUserCodeException {
- public DoesntImplementBenchmarkException(Class<?> specifiedClass) {
- super("Class [" + specifiedClass + "] does not implement the " + Benchmark.class.getName()
- + " interface.", "Add 'extends " + SimpleBenchmark.class + "' to the class declaration.");
- }
- }
-
- public static class InvalidDebugRepsException extends ErrorInUsageException {
- public InvalidDebugRepsException(String arg) {
- super("Invalid debug reps: " + arg);
- }
- }
-
- // TODO: should remove the caliper stack frames....
- public static class ExceptionFromUserCodeException extends UserException {
- public ExceptionFromUserCodeException(Throwable t) {
- super("An exception was thrown from the benchmark code.");
- initCause(t);
- }
- @Override public void display() {
- System.err.println(getMessage());
- getCause().printStackTrace(System.err);
- }
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Vm.java b/caliper/src/main/java/com/google/caliper/Vm.java
deleted file mode 100644
index 1cc45a9..0000000
--- a/caliper/src/main/java/com/google/caliper/Vm.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableList;
-
-import java.io.File;
-import java.util.List;
-
-class Vm {
- public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) {
- return ImmutableList.of();
- }
-
- /**
- * Returns a process builder to run this VM.
- *
- * @param vmArgs the path to the VM followed by VM arguments.
- * @param applicationArgs arguments to the target process
- */
- public ProcessBuilder newProcessBuilder(File workingDirectory, String classPath,
- ImmutableList<String> vmArgs, String className, ImmutableList<String> applicationArgs) {
- ProcessBuilder result = new ProcessBuilder();
- result.directory(workingDirectory);
- result.command().addAll(vmArgs);
- result.command().add("-cp");
- result.command().add(classPath);
- result.command().add(className);
- result.command().addAll(applicationArgs);
- return result;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/VmFactory.java b/caliper/src/main/java/com/google/caliper/VmFactory.java
deleted file mode 100644
index 408e34c..0000000
--- a/caliper/src/main/java/com/google/caliper/VmFactory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableSet;
-
-import java.util.Arrays;
-import java.util.List;
-
-public final class VmFactory {
- public static ImmutableSet<String> defaultVms() {
- String vmName = DalvikVm.isDalvikVm()
- ? DalvikVm.vmName()
- : StandardVm.defaultVmName();
- return ImmutableSet.of(vmName);
- }
-
- public Vm createVm(Scenario scenario) {
- List<String> vmList = Arrays.asList(scenario.getVariables().get(Scenario.VM_KEY).split("\\s+"));
- Vm vm = null;
- if (!vmList.isEmpty()) {
- if (vmList.get(0).endsWith("app_process")) {
- vm = new DalvikVm();
- } else if (vmList.get(0).endsWith("java")) {
- vm = new StandardVm();
- }
- }
- if (vm == null) {
- vm = new Vm();
- }
- return vm;
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/Xml.java b/caliper/src/main/java/com/google/caliper/Xml.java
deleted file mode 100644
index 2aeebda..0000000
--- a/caliper/src/main/java/com/google/caliper/Xml.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-/**
- * This exists for backwards compatibility with old data, which is stored in XML format.
- * All new data is stored in JSON.
- */
-public final class Xml {
- private static final String DATE_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssz";
- private static final String ENVIRONMENT_ELEMENT_NAME = "environment";
- private static final String RESULT_ELEMENT_NAME = "result";
- private static final String RUN_ELEMENT_NAME = "run";
- private static final String BENCHMARK_ATTRIBUTE = "benchmark";
- private static final String EXECUTED_TIMESTAMP_ATTRIBUTE = "executedTimestamp";
- private static final String OLD_SCENARIO_ELEMENT_NAME = "scenario";
- // for backwards compatibility, use a different name
- private static final String SCENARIO_ELEMENT_NAME = "newScenario";
- private static final String MEASUREMENTS_ELEMENT_NAME = "measurements";
- private static final String TIME_EVENT_LOG_ELEMENT_NAME = "eventLog";
-
- private static Result readResultElement(Element element) throws Exception {
- Environment environment = null;
- Run run = null;
- for (Node topLevelNode : XmlUtils.childrenOf(element)) {
- if (topLevelNode.getNodeName().equals(ENVIRONMENT_ELEMENT_NAME)) {
- Element environmentElement = (Element) topLevelNode;
- environment = readEnvironmentElement(environmentElement);
- } else if (topLevelNode.getNodeName().equals(RUN_ELEMENT_NAME)) {
- run = readRunElement((Element) topLevelNode);
- } else {
- throw new RuntimeException("illegal node name: " + topLevelNode.getNodeName());
- }
- }
-
- if (environment == null || run == null) {
- throw new RuntimeException("missing environment or run elements");
- }
-
- return new Result(run, environment);
- }
-
- private static Environment readEnvironmentElement(Element element) {
- return new Environment(XmlUtils.attributesOf(element));
- }
-
- private static Run readRunElement(Element element) throws Exception {
- String benchmarkName = element.getAttribute(BENCHMARK_ATTRIBUTE);
- String executedDateString = element.getAttribute(EXECUTED_TIMESTAMP_ATTRIBUTE);
- Date executedDate = new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US)
- .parse(executedDateString);
-
- ImmutableMap.Builder<Scenario, ScenarioResult> measurementsBuilder = ImmutableMap.builder();
- for (Node scenarioNode : XmlUtils.childrenOf(element)) {
- Element scenarioElement = (Element) scenarioNode;
- Scenario scenario = new Scenario(XmlUtils.attributesOf(scenarioElement));
- ScenarioResult scenarioResult;
-
- // for backwards compatibility with older runs
- if (scenarioNode.getNodeName().equals(OLD_SCENARIO_ELEMENT_NAME)) {
- MeasurementSet measurement =
- Json.measurementSetFromJson(scenarioElement.getTextContent());
- scenarioResult = new ScenarioResult(measurement, "",
- null, null, null, null);
- } else if (scenarioNode.getNodeName().equals(SCENARIO_ELEMENT_NAME)) {
- MeasurementSet timeMeasurementSet = null;
- String eventLog = null;
- for (Node node : XmlUtils.childrenOf(scenarioElement)) {
- if (node.getNodeName().equals(MEASUREMENTS_ELEMENT_NAME)) {
- timeMeasurementSet = Json.measurementSetFromJson(node.getTextContent());
- } else if (node.getNodeName().equals(TIME_EVENT_LOG_ELEMENT_NAME)) {
- eventLog = node.getTextContent();
- } else {
- throw new RuntimeException("illegal node name: " + node.getNodeName());
- }
- }
- if (timeMeasurementSet == null || eventLog == null) {
- throw new RuntimeException("missing node \"" + MEASUREMENTS_ELEMENT_NAME + "\" or \""
- + TIME_EVENT_LOG_ELEMENT_NAME + "\"");
- }
- // "new Measurement[0]" used instead of empty varargs argument since MeasurementSet has
- // an empty private constructor.
- scenarioResult = new ScenarioResult(timeMeasurementSet, eventLog,
- null, null, null, null);
- } else {
- throw new RuntimeException("illegal node name: " + scenarioNode.getNodeName());
- }
-
- measurementsBuilder.put(scenario, scenarioResult);
- }
-
- return new Run(measurementsBuilder.build(), benchmarkName, executedDate);
- }
-
- /**
- * Creates a result by decoding XML from the specified stream. The XML should
- * be consistent with the format emitted by the now deleted runToXml(Run, OutputStream).
- */
- public static Run runFromXml(InputStream in) {
- try {
- Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
- return readRunElement(document.getDocumentElement());
- } catch (Exception e) {
- throw new IllegalStateException("Malformed XML document", e);
- }
- }
-
- /**
- * Creates an environment by decoding XML from the specified stream. The XML should
- * be consistent with the format emitted by the now deleted
- * environmentToXml(Environment, OutputStream).
- */
- public static Environment environmentFromXml(InputStream in) {
- try {
- Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
- Element environmentElement = document.getDocumentElement();
- return readEnvironmentElement(environmentElement);
- } catch (Exception e) {
- throw new IllegalStateException("Malformed XML document", e);
- }
- }
-
- public static Result resultFromXml(InputStream in) {
- try {
- Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
- return readResultElement(document.getDocumentElement());
- } catch (Exception e) {
- throw new IllegalStateException("Malformed XML document", e);
- }
- }
-
- private Xml() {}
-}
diff --git a/caliper/src/main/java/com/google/caliper/XmlUtils.java b/caliper/src/main/java/com/google/caliper/XmlUtils.java
deleted file mode 100644
index 458e6fa..0000000
--- a/caliper/src/main/java/com/google/caliper/XmlUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-public final class XmlUtils {
- public static ImmutableList<Node> childrenOf(Node node) {
- NodeList children = node.getChildNodes();
- ImmutableList.Builder<Node> result = ImmutableList.builder();
- for (int i = 0, size = children.getLength(); i < size; i++) {
- result.add(children.item(i));
- }
- return result.build();
- }
-
- public static ImmutableMap<String, String> attributesOf(Element element) {
- NamedNodeMap map = element.getAttributes();
- ImmutableMap.Builder<String, String> result = ImmutableMap.builder();
- for (int i = 0, size = map.getLength(); i < size; i++) {
- Attr attr = (Attr) map.item(i);
- result.put(attr.getName(), attr.getValue());
- }
- return result.build();
- }
-
- private XmlUtils() {}
-}
diff --git a/caliper/src/main/java/com/google/caliper/api/AfterRep.java b/caliper/src/main/java/com/google/caliper/api/AfterRep.java
new file mode 100644
index 0000000..628edcc
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/AfterRep.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.annotations.Beta;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Apply this annotation to any method without parameters to have it run after each rep of a
+ * {@link Macrobenchmark}.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+@Beta
+public @interface AfterRep {}
diff --git a/caliper/src/main/java/com/google/caliper/api/BeforeRep.java b/caliper/src/main/java/com/google/caliper/api/BeforeRep.java
new file mode 100644
index 0000000..b4ae203
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/BeforeRep.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.annotations.Beta;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Apply this annotation to any method without parameters to have it run before each rep of a
+ * {@link Macrobenchmark}.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+@Beta
+public @interface BeforeRep {}
diff --git a/caliper/src/main/java/com/google/caliper/api/Footprint.java b/caliper/src/main/java/com/google/caliper/api/Footprint.java
new file mode 100644
index 0000000..0fa7b8c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/Footprint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method whose return value is an object whose total memory footprint is to be
+ * measured.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface Footprint {
+ /**
+ * Optionally ignore instances of the specified types (including subclasses) when measuring. For
+ * example, {@code @Footprint(ignore = Element.class) public Set<Element> set() {...}} would
+ * measure the size of the set while ignoring the size of the elements.
+ */
+ Class<?>[] exclude() default {};
+}
diff --git a/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java b/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java
new file mode 100644
index 0000000..aac4763
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.api;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.annotations.Beta;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Apply this annotation to any method without parameters to have it timed as a macrobenchmark. A
+ * macrobenchmark is roughly defined as any benchmark whose runtime is large enough that the
+ * granularity of the {@linkplain System#nanoTime clock} is not a factor in measurement. Thus, each
+ * repetition of the benchmark code can be timed individually.
+ *
+ * <p>Additionally, since each rep is independently timed, setup and tear down logic can be
+ * performed in between each using the {@link BeforeRep} and {@link AfterRep} annotations
+ * respectively.
+ */
+@Retention(RUNTIME)
+@Target(METHOD)
+@Beta
+public @interface Macrobenchmark {}
diff --git a/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java b/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java
new file mode 100644
index 0000000..1cc0ee0
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+import com.google.caliper.model.Trial;
+
+import java.io.Closeable;
+
+/**
+ * Interface for processing results as they complete. Callers must invoke {@link #close()} after
+ * all trials have been {@linkplain #processTrial processed}.
+ *
+ * <p>Implementations must have a public no argument constructor.
+ */
+public interface ResultProcessor extends Closeable {
+ void processTrial(Trial trial);
+}
diff --git a/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java b/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java
new file mode 100644
index 0000000..90b6fc1
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+/**
+ * Throw this exception from your benchmark class's setUp method, or benchmark method
+ * to indicate that the combination of parameters supplied should not be benchmarked. For example,
+ * while you might want to test <i>most</i> combinations of the parameters {@code size} and {@code
+ * comparator}, you might not want to test the specific combination of {@code size=100000000} and
+ * {@code comparator=reallyExpensiveComparator}.
+ */
+@SuppressWarnings("serial")
+public final class SkipThisScenarioException extends RuntimeException {}
diff --git a/caliper/src/main/java/com/google/caliper/api/VmOptions.java b/caliper/src/main/java/com/google/caliper/api/VmOptions.java
new file mode 100644
index 0000000..db73da8
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/api/VmOptions.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.api;
+
+import com.google.common.annotations.Beta;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation applied to a benchmark that specifies flags to be applied to the VM. These flags
+ * are applied before those specified on the command-line and thus are not guaranteed to be applied.
+ *
+ * <p>This API is likely to change.
+ */
+// TODO(gak): Support platform (e.g. Android and Jvm) specific options.
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Beta
+public @interface VmOptions {
+ String[] value() default {};
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java b/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java
new file mode 100644
index 0000000..f38e09c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+/**
+ * An abstract {@link LogMessageVisitor} with no-op implementations for the visit methods -
+ * provided so that all visitors don't have to override all methods.
+ */
+public abstract class AbstractLogMessageVisitor implements LogMessageVisitor {
+ @Override public void visit(GcLogMessage logMessage) {}
+
+ @Override public void visit(FailureLogMessage logMessage) {}
+
+ @Override public void visit(HotspotLogMessage logMessage) {}
+
+ @Override public void visit(StartMeasurementLogMessage logMessage) {}
+
+ @Override public void visit(StopMeasurementLogMessage logMessage) {}
+
+ @Override public void visit(VmOptionLogMessage logMessage) {}
+
+ @Override public void visit(VmPropertiesLogMessage logMessage) {}
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java b/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java
new file mode 100644
index 0000000..0d6ddea
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import com.google.caliper.util.Parser;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Bindings for {@link Parser parsers} for {@link com.google.caliper.model model} classes.
+ */
+@Module
+public final class BridgeModule {
+ @Provides static Parser<LogMessage> provideLogMessageParser(LogMessageParser parser) {
+ return parser;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java b/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java
new file mode 100644
index 0000000..f063355
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.io.BaseEncoding;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Serializes and deserializes WorkerSpecs as base64 encoded strings so they can be passed on the
+ * command line to the worker.
+ *
+ * <p>Java serialization is a appropriate in this usecase because there are no compatibility
+ * concerns. The messages encoded/decoded by this class are only used to communicate with another
+ * JVM that is running with the same version of the java classes. Also, it should be lighter weight
+ * and faster than other common serialization solutions.
+ */
+public final class CommandLineSerializer {
+ private CommandLineSerializer() {}
+
+ /** Returns the given serializable object as a base64 encoded String. */
+ public static String render(WorkerSpec message) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ try {
+ ObjectOutputStream out = new ObjectOutputStream(bytes);
+ out.writeObject(message);
+ out.close();
+ return BaseEncoding.base64().encode(bytes.toByteArray());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Parses the given base64 encoded string as an object of the given type. */
+ public static WorkerSpec parse(String arg) {
+ try {
+ ByteArrayInputStream bais = new ByteArrayInputStream(BaseEncoding.base64().decode(arg));
+ ObjectInputStream in = new ObjectInputStream(bais);
+ WorkerSpec instance = (WorkerSpec) in.readObject();
+ in.close();
+ checkState(bais.read() == -1,
+ "Message %s contains more than one object.", arg);
+ return instance;
+ } catch (IOException e) {
+ throw new RuntimeException(e); // assertion error?
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("cannot decode message", e);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java
new file mode 100644
index 0000000..4321a67
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Throwables;
+
+import java.io.Serializable;
+
+/**
+ * A message containing information on a failure encountered by the worker JVM.
+ */
+public class FailureLogMessage extends LogMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String stackTrace;
+
+ public FailureLogMessage(Throwable e) {
+ this(Throwables.getStackTraceAsString(e));
+ }
+
+ public FailureLogMessage(String stackTrace) {
+ this.stackTrace = checkNotNull(stackTrace);
+ }
+
+ public String stackTrace() {
+ return stackTrace;
+ }
+
+ @Override
+ public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public int hashCode() {
+ return stackTrace.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof FailureLogMessage) {
+ FailureLogMessage that = (FailureLogMessage) obj;
+ return this.stackTrace.equals(that.stackTrace);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java
new file mode 100644
index 0000000..2d59788
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.caliper.util.ShortDuration;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * A message representing output produced by the JVM when {@code -XX:+PrintGC} is enabled.
+ */
+public final class GcLogMessage extends LogMessage {
+ /**
+ * The type of the garbage collection performed.
+ */
+ public static enum Type {
+ FULL,
+ INCREMENTAL,
+ }
+
+ private final Type type;
+ private final ShortDuration duration;
+
+ GcLogMessage(Type type, ShortDuration duration) {
+ this.type = checkNotNull(type);
+ this.duration = checkNotNull(duration);
+ }
+
+ public Type type() {
+ return type;
+ }
+
+ public ShortDuration duration() {
+ return duration;
+ }
+
+ @Override
+ public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(type, duration);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof GcLogMessage) {
+ GcLogMessage that = (GcLogMessage) obj;
+ return this.type == that.type
+ && this.duration.equals(that.duration);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .addValue(type)
+ .add("duration", duration)
+ .toString();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java
new file mode 100644
index 0000000..0670153
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+/**
+ * A message representing output produced by the JVM when {@code -XX:+PrintCompliation} is enabled.
+ */
+public final class HotspotLogMessage extends LogMessage {
+ HotspotLogMessage() {}
+
+ @Override
+ public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java
new file mode 100644
index 0000000..fc4a2ad
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+/**
+ * A single line of output produced by a {@code Worker}.
+ */
+public abstract class LogMessage {
+ public abstract void accept(LogMessageVisitor visitor);
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java
new file mode 100644
index 0000000..68ab785
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.caliper.util.Parser;
+import com.google.caliper.util.ShortDuration;
+
+import java.math.BigDecimal;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+
+/**
+ * Parses {@link LogMessage} strings.
+ */
+final class LogMessageParser implements Parser<LogMessage> {
+ @Inject LogMessageParser() {}
+
+ private static final Pattern GC_PATTERN =
+ Pattern.compile(".*\\[(?:(Full) )?GC.*(\\d+\\.\\d+) secs\\]");
+ private static final Pattern JIT_PATTERN =
+ Pattern.compile(".*::.*( \\(((\\d+ bytes)|(static))\\))?");
+ private static final Pattern VM_OPTION_PATTERN =
+ Pattern.compile("\\s*(\\w+)\\s+(\\w+)\\s+:?=\\s+([^\\s]*)\\s+\\{([^}]*)\\}\\s*");
+
+ @Override public LogMessage parse(CharSequence text) {
+ // TODO(gak): do this stuff in terms of CharSequence instead of String
+ String string = text.toString();
+ Matcher gcMatcher = GC_PATTERN.matcher(string);
+ if (gcMatcher.matches()) {
+ return new GcLogMessage(
+ "Full".equals(gcMatcher.group(1))
+ ? GcLogMessage.Type.FULL
+ : GcLogMessage.Type.INCREMENTAL,
+ ShortDuration.of(BigDecimal.valueOf(Double.parseDouble(gcMatcher.group(2))), SECONDS));
+ }
+ Matcher jitMatcher = JIT_PATTERN.matcher(string);
+ if (jitMatcher.matches()) {
+ return new HotspotLogMessage();
+ }
+ Matcher vmOptionMatcher = VM_OPTION_PATTERN.matcher(string);
+ if (vmOptionMatcher.matches()) {
+ return new VmOptionLogMessage(vmOptionMatcher.group(2), vmOptionMatcher.group(3));
+ }
+ return null;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java
new file mode 100644
index 0000000..f91bbf1
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+/**
+ * A visitor for the various subclasses of {@link LogMessage}.
+ */
+public interface LogMessageVisitor {
+ void visit(GcLogMessage logMessage);
+ void visit(FailureLogMessage logMessage);
+ void visit(HotspotLogMessage logMessage);
+ void visit(StartMeasurementLogMessage logMessage);
+ void visit(StopMeasurementLogMessage logMessage);
+ void visit(VmOptionLogMessage logMessage);
+ void visit(VmPropertiesLogMessage logMessage);
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java b/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java
new file mode 100644
index 0000000..510d9e6
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.Socket;
+import java.nio.channels.SocketChannel;
+
+/**
+ * A simple tuple for the opened streams of a socket.
+ */
+public final class OpenedSocket {
+ /**
+ * Waits for the channel to connect and returns a new {@link OpenedSocket}.
+ */
+ public static OpenedSocket fromSocket(SocketChannel socket) throws IOException {
+ socket.configureBlocking(true);
+ socket.finishConnect();
+ return fromSocket(socket.socket());
+ }
+ /**
+ * Returns a new {@link OpenedSocket} for the given connected {@link Socket} instance.
+ */
+ public static OpenedSocket fromSocket(Socket socket) throws IOException {
+ // Setting this to true disables Nagle's algorithm (RFC 896) which seeks to decrease packet
+ // overhead by buffering writes while there are packets outstanding (i.e. haven't been ack'd).
+ // This interacts poorly with another TCP feature called 'delayed acks' (RFC 1122) if the
+ // application sends lots of small messages (which we do!). Without this enabled messages sent
+ // by the worker may be delayed by up to the delayed ack timeout (on linux this is 40-500ms,
+ // though in practice I have only observed 40ms). So we need to enable the TCP_NO_DELAY option
+ // here.
+ socket.setTcpNoDelay(true);
+ // N.B. order is important here, constructing an ObjectOutputStream requires writing a header
+ // and constructing an ObjectInputStream requires reading that header. So we always need to
+ // construct the OOS first so we don't deadlock.
+ ObjectOutputStream output = new ObjectOutputStream(getOutputStream(socket));
+ ObjectInputStream input = new ObjectInputStream(getInputStream(socket));
+ return new OpenedSocket(new Reader(input), new Writer(output));
+ }
+
+ private final Reader reader;
+ private final Writer writer;
+
+ private OpenedSocket(Reader reader,
+ Writer objectOutputStream) {
+ this.reader = reader;
+ this.writer = objectOutputStream;
+ }
+
+ public Reader reader() {
+ return reader;
+ }
+
+ public Writer writer() {
+ return writer;
+ }
+
+ /** Reads objects from the socket. */
+ public static final class Reader implements Closeable {
+ private final ObjectInputStream input;
+
+ Reader(ObjectInputStream is) {
+ this.input = is;
+ }
+
+ /** Returns the next object, or {@code null} if we are at EOF. */
+ public Serializable read() throws IOException {
+ try {
+ return (Serializable) checkNotNull(input.readObject());
+ } catch (EOFException eof) {
+ // TODO(lukes): The only 'better' way to handle this would be to use an explicit poison pill
+ // marker in the stream. Otherwise we just have to catch EOFException.
+ return null;
+ } catch (ClassNotFoundException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override public void close() throws IOException {
+ input.close();
+ }
+ }
+
+ /** Writes objects to the socket. */
+ public static final class Writer implements Closeable, Flushable {
+ private final ObjectOutputStream output;
+
+ Writer(ObjectOutputStream output) {
+ this.output = output;
+ }
+
+ /** Returns the next object, or {@code null} if we are at EOF. */
+ public void write(Serializable serializable) throws IOException {
+ output.writeObject(serializable);
+ }
+
+ @Override public void flush() throws IOException {
+ output.flush();
+ }
+
+ @Override public void close() throws IOException {
+ output.close();
+ }
+ }
+
+ /**
+ * Returns an {@link OutputStream} for the socket, but unlike {@link Socket#getOutputStream()}
+ * when you call {@link OutputStream#close() close} it only closes the output end of the socket
+ * instead of the entire socket.
+ */
+ private static OutputStream getOutputStream(final Socket socket) throws IOException {
+ final OutputStream delegate = socket.getOutputStream();
+ return new OutputStream() {
+
+ @Override public void close() throws IOException {
+ delegate.flush();
+ synchronized (socket) {
+ socket.shutdownOutput();
+ if (socket.isInputShutdown()) {
+ socket.close();
+ }
+ }
+ }
+
+ // Boring delegates from here on down
+ @Override public void write(int b) throws IOException {
+ delegate.write(b);
+ }
+
+ @Override public void write(byte[] b) throws IOException {
+ delegate.write(b);
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ delegate.write(b, off, len);
+ }
+
+ @Override public void flush() throws IOException {
+ delegate.flush();
+ }
+ };
+ }
+
+ /**
+ * Returns an {@link InputStream} for the socket, but unlike {@link Socket#getInputStream()}
+ * when you call {@link InputStream#close() close} it only closes the input end of the socket
+ * instead of the entire socket.
+ */
+ private static InputStream getInputStream(final Socket socket) throws IOException {
+ final InputStream delegate = socket.getInputStream();
+ return new InputStream() {
+ @Override public void close() throws IOException {
+ synchronized (socket) {
+ socket.shutdownInput();
+ if (socket.isOutputShutdown()) {
+ socket.close();
+ }
+ }
+ }
+
+ // Boring delegates from here on down
+ @Override public int read() throws IOException {
+ return delegate.read();
+ }
+
+ @Override public int read(byte[] b) throws IOException {
+ return delegate.read(b);
+ }
+
+ @Override public int read(byte[] b, int off, int len) throws IOException {
+ return delegate.read(b, off, len);
+ }
+
+ @Override public long skip(long n) throws IOException {
+ return delegate.skip(n);
+ }
+
+ @Override public int available() throws IOException {
+ return delegate.available();
+ }
+
+ @Override public void mark(int readlimit) {
+ delegate.mark(readlimit);
+ }
+
+ @Override public void reset() throws IOException {
+ delegate.reset();
+ }
+
+ @Override public boolean markSupported() {
+ return delegate.markSupported();
+ }
+ };
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/MeasurementType.java b/caliper/src/main/java/com/google/caliper/bridge/Renderer.java
index 30de69f..f2c9037 100644
--- a/caliper/src/main/java/com/google/caliper/MeasurementType.java
+++ b/caliper/src/main/java/com/google/caliper/bridge/Renderer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc.
+ * Copyright (C) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-package com.google.caliper;
+package com.google.caliper.bridge;
-import com.google.common.annotations.GwtCompatible;
-
-@GwtCompatible
-public enum MeasurementType {
- TIME, INSTANCE, MEMORY, DEBUG
+public interface Renderer<T> {
+ String render(T object);
}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java b/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java
new file mode 100644
index 0000000..114f5f6
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import java.io.Serializable;
+
+/**
+ * A message sent from the runner to the worker to indicate whether or not measuring should
+ * continue.
+ */
+public class ShouldContinueMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final boolean shouldContinue;
+ private final boolean warmupComplete;
+
+ public ShouldContinueMessage(boolean shouldContinue, boolean warmupComplete) {
+ this.shouldContinue = shouldContinue;
+ this.warmupComplete = warmupComplete;
+ }
+
+ public boolean shouldContinue() {
+ return shouldContinue;
+ }
+
+ @Override public int hashCode() {
+ return Boolean.valueOf(shouldContinue).hashCode();
+ }
+
+ @Override public boolean equals(Object obj) {
+ return obj instanceof ShouldContinueMessage
+ && shouldContinue == ((ShouldContinueMessage) obj).shouldContinue;
+ }
+
+ public boolean isWarmupComplete() {
+ return warmupComplete;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java
new file mode 100644
index 0000000..e04dcd5
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import java.io.Serializable;
+
+/**
+ * A message signaling that the timing interval has started in the worker.
+ */
+// TODO(gak): rename in terms of measurement
+public class StartMeasurementLogMessage extends LogMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Override public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override public int hashCode() {
+ return StartMeasurementLogMessage.class.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof StartMeasurementLogMessage;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java
new file mode 100644
index 0000000..7efd9b3
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+/**
+ * A message sent from the worker to the runner immediately after startup to identify the trial
+ * that it is performing.
+ */
+public final class StartupAnnounceMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final UUID trialId;
+
+ public StartupAnnounceMessage(UUID trialId) {
+ this.trialId = checkNotNull(trialId);
+ }
+
+ public UUID trialId() {
+ return trialId;
+ }
+
+ @Override public int hashCode() {
+ return trialId.hashCode();
+ }
+
+ @Override public boolean equals(Object obj) {
+ return obj instanceof StartupAnnounceMessage
+ && trialId.equals(((StartupAnnounceMessage) obj).trialId);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java
new file mode 100644
index 0000000..b93ee50
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import com.google.caliper.model.Measurement;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import java.io.Serializable;
+
+/**
+ * A message signaling that the timing interval has ended in the worker.
+ */
+public class StopMeasurementLogMessage extends LogMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final ImmutableList<Measurement> measurements;
+
+ public StopMeasurementLogMessage(Iterable<Measurement> measurements) {
+ this.measurements = ImmutableList.copyOf(measurements);
+ }
+
+ public ImmutableList<Measurement> measurements() {
+ return measurements;
+ }
+
+ @Override public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(measurements);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof StopMeasurementLogMessage) {
+ StopMeasurementLogMessage that = (StopMeasurementLogMessage) obj;
+ return this.measurements.equals(that.measurements);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java
new file mode 100644
index 0000000..dd8d5cd
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+
+/**
+ * A message representing output produced by the JVM when {@code -XX:+PrintFlagsFinal} is enabled.
+ */
+public final class VmOptionLogMessage extends LogMessage {
+ private final String name;
+ private final String value;
+
+ VmOptionLogMessage(String name, String value) {
+ this.name = checkNotNull(name);
+ this.value = checkNotNull(value);
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java
new file mode 100644
index 0000000..2e63879
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+import java.io.Serializable;
+
+/**
+ * A message containing a selection of {@link System#getProperties() system properties} from the
+ * worker JVM.
+ */
+public class VmPropertiesLogMessage extends LogMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final ImmutableMap<String, String> properties;
+
+ public VmPropertiesLogMessage() {
+ this(ImmutableMap.copyOf(Maps.fromProperties(System.getProperties())));
+ }
+
+ public VmPropertiesLogMessage(ImmutableMap<String, String> properties) {
+ this.properties = checkNotNull(properties);
+ }
+
+ public ImmutableMap<String, String> properties() {
+ return properties;
+ }
+
+ @Override public void accept(LogMessageVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(properties);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof VmPropertiesLogMessage) {
+ VmPropertiesLogMessage that = (VmPropertiesLogMessage) obj;
+ return this.properties.equals(that.properties);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java b/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java
new file mode 100644
index 0000000..022b1dc
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import com.google.caliper.model.BenchmarkSpec;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+/**
+ * This object is sent from the parent process to the child to tell it what to do. If the child
+ * does not do it, it will not get its allowance this week.
+ */
+public final class WorkerSpec implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final UUID trialId;
+ // Should be ? extends Worker but circular build deps prevent that.
+ public final Class<?> workerClass;
+ public final ImmutableMap<String, String> workerOptions;
+ public final BenchmarkSpec benchmarkSpec;
+
+ /**
+ * The names of the benchmark method parameters so that the method can be uniquely identified.
+ */
+ public final ImmutableList<Class<?>> methodParameterClasses;
+ public final int port;
+
+ public WorkerSpec(
+ UUID trialId,
+ Class<?> workerClass,
+ ImmutableMap<String, String> workerOptions,
+ BenchmarkSpec benchmarkSpec,
+ ImmutableList<Class<?>> methodParameterClasses,
+ int port) {
+ this.trialId = trialId;
+ this.workerClass = workerClass;
+ this.workerOptions = workerOptions;
+ this.benchmarkSpec = benchmarkSpec;
+ this.methodParameterClasses = methodParameterClasses;
+ this.port = port;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/bridge/package-info.java b/caliper/src/main/java/com/google/caliper/bridge/package-info.java
new file mode 100644
index 0000000..2d474ee
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/bridge/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Bridges between the runner and the worker.
+ */
+package com.google.caliper.bridge; \ No newline at end of file
diff --git a/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java b/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java
new file mode 100644
index 0000000..3be0ade
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static com.google.caliper.util.Util.subgroupMap;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.VirtualMachineException;
+import com.google.caliper.util.Util;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represents caliper configuration. By default, {@code ~/.caliper/config.properties} and
+ * {@code global-config.properties}.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class CaliperConfig {
+ @VisibleForTesting final ImmutableMap<String, String> properties;
+ private final ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig>
+ resultProcessorConfigs;
+
+ @VisibleForTesting
+ public CaliperConfig(ImmutableMap<String, String> properties)
+ throws InvalidConfigurationException {
+ this.properties = checkNotNull(properties);
+ this.resultProcessorConfigs = findResultProcessorConfigs(subgroupMap(properties, "results"));
+ }
+
+ private static final Pattern CLASS_PROPERTY_PATTERN = Pattern.compile("(\\w+)\\.class");
+
+ private static <T> ImmutableBiMap<String, Class<? extends T>> mapGroupNamesToClasses(
+ ImmutableMap<String, String> groupProperties, Class<T> type)
+ throws InvalidConfigurationException {
+ BiMap<String, Class<? extends T>> namesToClasses = HashBiMap.create();
+ for (Entry<String, String> entry : groupProperties.entrySet()) {
+ Matcher matcher = CLASS_PROPERTY_PATTERN.matcher(entry.getKey());
+ if (matcher.matches() && !entry.getValue().isEmpty()) {
+ try {
+ Class<?> someClass = Util.loadClass(entry.getValue());
+ checkState(type.isAssignableFrom(someClass));
+ @SuppressWarnings("unchecked")
+ Class<? extends T> verifiedClass = (Class<? extends T>) someClass;
+ namesToClasses.put(matcher.group(1), verifiedClass);
+ } catch (ClassNotFoundException e) {
+ throw new InvalidConfigurationException("Cannot find result processor class: "
+ + entry.getValue());
+ }
+ }
+ }
+ return ImmutableBiMap.copyOf(namesToClasses);
+ }
+
+ private static ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig>
+ findResultProcessorConfigs(ImmutableMap<String, String> resultsProperties)
+ throws InvalidConfigurationException {
+ ImmutableBiMap<String, Class<? extends ResultProcessor>> processorToClass =
+ mapGroupNamesToClasses(resultsProperties, ResultProcessor.class);
+ ImmutableMap.Builder<Class<? extends ResultProcessor>, ResultProcessorConfig> builder =
+ ImmutableMap.builder();
+ for (Entry<String, Class<? extends ResultProcessor>> entry : processorToClass.entrySet()) {
+ builder.put(entry.getValue(), getResultProcessorConfig(resultsProperties, entry.getKey()));
+ }
+ return builder.build();
+ }
+
+ public ImmutableMap<String, String> properties() {
+ return properties;
+ }
+
+ /**
+ * Returns the configuration of the current host VM (including the flags used to create it). Any
+ * args specified using {@code vm.args} will also be applied
+ */
+ public VmConfig getDefaultVmConfig(Platform platform) {
+ return new VmConfig.Builder(platform, platform.defaultVmHomeDir())
+ .addAllOptions(platform.inputArguments())
+ // still incorporate vm.args
+ .addAllOptions(getArgs(subgroupMap(properties, "vm")))
+ .build();
+ }
+
+ public VmConfig getVmConfig(Platform platform, String name)
+ throws InvalidConfigurationException {
+ checkNotNull(name);
+ ImmutableMap<String, String> vmGroupMap = subgroupMap(properties, "vm");
+ ImmutableMap<String, String> vmMap = subgroupMap(vmGroupMap, name);
+ File homeDir;
+ try {
+ homeDir = platform.customVmHomeDir(vmGroupMap, name);
+ } catch (VirtualMachineException e) {
+ throw new InvalidConfigurationException(e);
+ }
+ return new VmConfig.Builder(platform, homeDir)
+ .addAllOptions(getArgs(vmGroupMap))
+ .addAllOptions(getArgs(vmMap))
+ .build();
+ }
+
+ private static final Pattern INSTRUMENT_CLASS_PATTERN = Pattern.compile("([^\\.]+)\\.class");
+
+ public ImmutableSet<String> getConfiguredInstruments() {
+ ImmutableSet.Builder<String> resultBuilder = ImmutableSet.builder();
+ for (String key : subgroupMap(properties, "instrument").keySet()) {
+ Matcher matcher = INSTRUMENT_CLASS_PATTERN.matcher(key);
+ if (matcher.matches()) {
+ resultBuilder.add(matcher.group(1));
+ }
+ }
+ return resultBuilder.build();
+ }
+
+ public InstrumentConfig getInstrumentConfig(String name) {
+ checkNotNull(name);
+ ImmutableMap<String, String> instrumentGroupMap = subgroupMap(properties, "instrument");
+ ImmutableMap<String, String> insrumentMap = subgroupMap(instrumentGroupMap, name);
+ @Nullable String className = insrumentMap.get("class");
+ checkArgument(className != null, "no instrument configured named %s", name);
+ return new InstrumentConfig.Builder()
+ .className(className)
+ .addAllOptions(subgroupMap(insrumentMap, "options"))
+ .build();
+ }
+
+ public ImmutableSet<Class<? extends ResultProcessor>> getConfiguredResultProcessors() {
+ return resultProcessorConfigs.keySet();
+ }
+
+ public ResultProcessorConfig getResultProcessorConfig(
+ Class<? extends ResultProcessor> resultProcessorClass) {
+ return resultProcessorConfigs.get(resultProcessorClass);
+ }
+
+ private static ResultProcessorConfig getResultProcessorConfig(
+ ImmutableMap<String, String> resultsProperties, String name) {
+ ImmutableMap<String, String> resultsMap = subgroupMap(resultsProperties, name);
+ return new ResultProcessorConfig.Builder()
+ .className(resultsMap.get("class"))
+ .addAllOptions(subgroupMap(resultsMap, "options"))
+ .build();
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("properties", properties)
+ .toString();
+ }
+
+ private static List<String> getArgs(Map<String, String> properties) {
+ String argsString = Strings.nullToEmpty(properties.get("args"));
+ ImmutableList.Builder<String> args = ImmutableList.builder();
+ StringBuilder arg = new StringBuilder();
+ for (int i = 0; i < argsString.length(); i++) {
+ char c = argsString.charAt(i);
+ switch (c) {
+ case '\\':
+ arg.append(argsString.charAt(++i));
+ break;
+ case ' ':
+ if (arg.length() > 0) {
+ args.add(arg.toString());
+ }
+ arg = new StringBuilder();
+ break;
+ default:
+ arg.append(c);
+ break;
+ }
+ }
+ if (arg.length() > 0) {
+ args.add(arg.toString());
+ }
+ return args.build();
+ }
+
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java b/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java
new file mode 100644
index 0000000..a103cf2
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.util.Util;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteSource;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Loads caliper configuration files and, if necessary, creates new versions from the defaults.
+ */
+@Singleton
+public final class CaliperConfigLoader {
+ private final CaliperOptions options;
+
+ @Inject CaliperConfigLoader(CaliperOptions options) {
+ this.options = options;
+ }
+
+ public CaliperConfig loadOrCreate() throws InvalidConfigurationException {
+ File configFile = options.caliperConfigFile();
+ ImmutableMap<String, String> defaults;
+ try {
+ defaults = Util.loadProperties(
+ Util.resourceSupplier(CaliperConfig.class, "global-config.properties"));
+ } catch (IOException impossible) {
+ throw new AssertionError(impossible);
+ }
+
+ // TODO(kevinb): deal with migration issue from old-style .caliperrc
+
+ if (configFile.exists()) {
+ try {
+ ImmutableMap<String, String> user =
+ Util.loadProperties(Files.asByteSource(configFile));
+ return new CaliperConfig(mergeProperties(options.configProperties(), user, defaults));
+ } catch (IOException keepGoing) {
+ }
+ }
+
+ ByteSource supplier = Util.resourceSupplier(CaliperConfig.class, "default-config.properties");
+ tryCopyIfNeeded(supplier, configFile);
+
+ ImmutableMap<String, String> user;
+ try {
+ user = Util.loadProperties(supplier);
+ } catch (IOException e) {
+ throw new AssertionError(e); // class path must be messed up
+ }
+ return new CaliperConfig(mergeProperties(options.configProperties(), user, defaults));
+ }
+
+ private static ImmutableMap<String, String> mergeProperties(Map<String, String> commandLine,
+ Map<String, String> user,
+ Map<String, String> defaults) {
+ Map<String, String> map = Maps.newHashMap(defaults);
+ map.putAll(user); // overwrite and augment
+ map.putAll(commandLine); // overwrite and augment
+ Iterables.removeIf(map.values(), Predicates.equalTo(""));
+ return ImmutableMap.copyOf(map);
+ }
+
+ private static void tryCopyIfNeeded(ByteSource supplier, File rcFile) {
+ if (!rcFile.exists()) {
+ try {
+ supplier.copyTo(Files.asByteSink(rcFile));
+ } catch (IOException e) {
+ rcFile.delete();
+ }
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/ConfigModule.java b/caliper/src/main/java/com/google/caliper/config/ConfigModule.java
new file mode 100644
index 0000000..62b64bb
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/ConfigModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import dagger.Module;
+import dagger.Provides;
+
+import java.util.logging.LogManager;
+import javax.inject.Singleton;
+
+/**
+ * Bindings for Caliper configuration.
+ */
+@Module
+public final class ConfigModule {
+
+ /**
+ * The {@code doNotRemove} parameter is required here to ensure that the logging configuration
+ * is loaded and used to update the logger settings before the configuration is loaded.
+ */
+ @Provides @Singleton
+ static CaliperConfig provideCaliperConfig(
+ CaliperConfigLoader configLoader,
+ @SuppressWarnings("unused") LoggingConfigLoader doNotRemove)
+ throws InvalidConfigurationException {
+
+ return configLoader.loadOrCreate();
+ }
+
+ @Provides static LogManager provideLogManager() {
+ return LogManager.getLogManager();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java b/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java
new file mode 100644
index 0000000..5715a16
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.model.InstrumentSpec;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * This is the configuration passed to the instrument by the user. This differs from the
+ * {@link InstrumentSpec} in that any number of configurations can yield the same spec
+ * (due to default option values).
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+@Immutable
+public final class InstrumentConfig {
+ private final String className;
+ private final ImmutableMap<String, String> options;
+
+ private InstrumentConfig(Builder builder) {
+ this.className = builder.className;
+ this.options = builder.optionsBuilder.build();
+ }
+
+ public String className() {
+ return className;
+ }
+
+ public ImmutableMap<String, String> options() {
+ return options;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof InstrumentConfig) {
+ InstrumentConfig that = (InstrumentConfig) obj;
+ return this.className.equals(that.className)
+ && this.options.equals(that.options);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(className, options);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("className", className)
+ .add("options", options)
+ .toString();
+ }
+
+ static final class Builder {
+ private String className;
+ private final ImmutableMap.Builder<String, String> optionsBuilder = ImmutableMap.builder();
+
+ public Builder className(String className) {
+ this.className = checkNotNull(className);
+ return this;
+ }
+
+ public Builder instrumentClass(Class<?> insturmentClass) {
+ return className(insturmentClass.getName());
+ }
+
+ public Builder addOption(String option, String value) {
+ optionsBuilder.put(option, value);
+ return this;
+ }
+
+ public Builder addAllOptions(Map<String, String> options) {
+ optionsBuilder.putAll(options);
+ return this;
+ }
+
+ public InstrumentConfig build() {
+ checkState(className != null);
+ return new InstrumentConfig(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java b/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java
new file mode 100644
index 0000000..5d1df97
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import java.io.PrintWriter;
+
+/**
+ * Thrown when an invalid configuration has been specified by the user.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class InvalidConfigurationException extends RuntimeException {
+ public InvalidConfigurationException() {
+ super();
+ }
+
+ public InvalidConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidConfigurationException(String message) {
+ super(message);
+ }
+
+ public InvalidConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public void display(PrintWriter writer) {
+ writer.println(getMessage());
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java b/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java
new file mode 100644
index 0000000..4ee216c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static java.util.logging.Level.SEVERE;
+import static java.util.logging.Level.WARNING;
+
+import com.google.caliper.model.Run;
+import com.google.caliper.options.CaliperDirectory;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.io.Closer;
+
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Loading the logging configuration at {@code ~/.caliper/logging.properties} if present.
+ */
+@Singleton
+final class LoggingConfigLoader {
+ private static final Logger logger = Logger.getLogger(LoggingConfigLoader.class.getName());
+
+ private final File caliperDirectory;
+ private final LogManager logManager;
+ private final Run run;
+
+ @Inject LoggingConfigLoader(@CaliperDirectory File caliperDirectory, LogManager logManager,
+ Run run) {
+ this.caliperDirectory = caliperDirectory;
+ this.logManager = logManager;
+ this.run = run;
+ }
+
+ @Inject void loadLoggingConfig() {
+ File loggingPropertiesFile = new File(caliperDirectory, "logging.properties");
+ if (loggingPropertiesFile.isFile()) {
+ Closer closer = Closer.create();
+ FileInputStream fis = null;
+ try {
+ fis = closer.register(new FileInputStream(loggingPropertiesFile));
+ logManager.readConfiguration(fis);
+ } catch (SecurityException e) {
+ logConfigurationException(e);
+ } catch (IOException e) {
+ logConfigurationException(e);
+ } finally {
+ try {
+ closer.close();
+ } catch (IOException e) {
+ logger.log(SEVERE, "could not close " + loggingPropertiesFile, e);
+ }
+ }
+ logger.info(String.format("Using logging configuration at %s", loggingPropertiesFile));
+ } else {
+ try {
+ maybeLoadDefaultLogConfiguration(LogManager.getLogManager());
+ } catch (SecurityException e) {
+ logConfigurationException(e);
+ } catch (IOException e) {
+ logConfigurationException(e);
+ }
+ }
+ }
+
+ @VisibleForTesting void maybeLoadDefaultLogConfiguration(LogManager logManager)
+ throws SecurityException, IOException {
+ logManager.reset();
+ File logDirectory = new File(caliperDirectory, "log");
+ logDirectory.mkdirs();
+ FileHandler fileHandler = new FileHandler(String.format("%s%c%s.%s.log",
+ logDirectory.getAbsolutePath(), File.separatorChar,
+ ISODateTimeFormat.basicDateTimeNoMillis().print(run.startTime()), run.id()));
+ fileHandler.setEncoding(Charsets.UTF_8.name());
+ fileHandler.setFormatter(new SimpleFormatter());
+ Logger globalLogger = logManager.getLogger("");
+ globalLogger.addHandler(fileHandler);
+ }
+
+ private static void logConfigurationException(Exception e) {
+ logger.log(WARNING, "Could not apply the logging configuration", e);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java b/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java
new file mode 100644
index 0000000..cbaa0eb
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+/**
+ * This is the configuration passed to the {@link com.google.caliper.api.ResultProcessor} by the
+ * user.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public class ResultProcessorConfig {
+ private final String className;
+ private final ImmutableMap<String, String> options;
+
+ private ResultProcessorConfig(Builder builder) {
+ this.className = builder.className;
+ this.options = builder.optionsBuilder.build();
+ }
+
+ public String className() {
+ return className;
+ }
+
+ public ImmutableMap<String, String> options() {
+ return options;
+ }
+
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof ResultProcessorConfig) {
+ ResultProcessorConfig that = (ResultProcessorConfig) obj;
+ return this.className.equals(that.className)
+ && this.options.equals(that.options);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(className, options);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("className", className)
+ .add("options", options)
+ .toString();
+ }
+
+ static final class Builder {
+ private String className;
+ private ImmutableMap.Builder<String, String> optionsBuilder = ImmutableMap.builder();
+
+ public Builder className(String className) {
+ this.className = checkNotNull(className);
+ return this;
+ }
+
+ public Builder addOption(String option, String value) {
+ this.optionsBuilder.put(option, value);
+ return this;
+ }
+
+ public Builder addAllOptions(Map<String, String> options) {
+ this.optionsBuilder.putAll(options);
+ return this;
+ }
+
+ public ResultProcessorConfig build() {
+ return new ResultProcessorConfig(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/config/VmConfig.java b/caliper/src/main/java/com/google/caliper/config/VmConfig.java
new file mode 100644
index 0000000..fcb63ca
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/config/VmConfig.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.caliper.model.VmSpec;
+import com.google.caliper.platform.Platform;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * This is the configuration passed to the VM by the user. This differs from the {@link VmSpec}
+ * in that any number of configurations can yield the same spec (due to default flag values) and any
+ * number of specs can come from a single configuration (due to
+ * <a href="http://www.oracle.com/technetwork/java/ergo5-140223.html">ergonomics</a>).
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class VmConfig {
+ private final Platform platform;
+ private final File vmHome;
+ private final ImmutableList<String> options;
+
+ @GuardedBy("this")
+ private File vmExecutable;
+
+ private VmConfig(Builder builder) {
+ this.platform = builder.platform;
+ this.vmHome = builder.vmHome;
+ this.options = builder.optionsBuilder.build();
+ }
+
+ @VisibleForTesting
+ public VmConfig(File vmHome, Iterable<String> options, File vmExecutable, Platform platform) {
+ this.platform = platform;
+ this.vmHome = checkNotNull(vmHome);
+ this.vmExecutable = checkNotNull(vmExecutable);
+ this.options = ImmutableList.copyOf(options);
+ }
+
+ public File vmHome() {
+ return vmHome;
+ }
+
+ public synchronized File vmExecutable() {
+ if (vmExecutable == null) {
+ vmExecutable = platform.vmExecutable(vmHome);
+ }
+ return vmExecutable;
+ }
+
+ public ImmutableList<String> options() {
+ return options;
+ }
+
+ public String platformName() {
+ return platform.name();
+ }
+
+ public String workerClassPath() {
+ return platform.workerClassPath();
+ }
+
+ public ImmutableSet<String> workerProcessArgs() {
+ return platform.workerProcessArgs();
+ }
+
+ public ImmutableSet<String> commonInstrumentVmArgs() {
+ return platform.commonInstrumentVmArgs();
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof VmConfig) {
+ VmConfig that = (VmConfig) obj;
+ return this.platform.equals(that.platform)
+ && this.vmHome.equals(that.vmHome)
+ && this.options.equals(that.options);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(platform, vmHome, options);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("platform", platform)
+ .add("vmHome", vmHome)
+ .add("options", options)
+ .toString();
+ }
+
+ @VisibleForTesting public static final class Builder {
+ private final Platform platform;
+ private final File vmHome;
+ private final ImmutableList.Builder<String> optionsBuilder = ImmutableList.builder();
+
+
+ public Builder(Platform platform, File vmHome) {
+ this.platform = checkNotNull(platform);
+ this.vmHome = checkNotNull(vmHome);
+ }
+
+ public Builder addOption(String option) {
+ optionsBuilder.add(option);
+ return this;
+ }
+
+ public Builder addAllOptions(Iterable<String> options) {
+ optionsBuilder.addAll(options);
+ return this;
+ }
+
+ public VmConfig build() {
+ return new VmConfig(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java b/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java
new file mode 100644
index 0000000..82937ba
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.caliper.model.ExcludeFromJson;
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+
+/**
+ * An exclusion strategy that excludes elements annotated with {@link ExcludeFromJson}.
+ */
+final class AnnotationExclusionStrategy implements ExclusionStrategy {
+ @Override public boolean shouldSkipField(FieldAttributes f) {
+ return f.getAnnotation(ExcludeFromJson.class) != null;
+ }
+
+ @Override public boolean shouldSkipClass(Class<?> clazz) {
+ return clazz.getAnnotation(ExcludeFromJson.class) != null;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/GsonModule.java b/caliper/src/main/java/com/google/caliper/json/GsonModule.java
new file mode 100644
index 0000000..491dcca
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/GsonModule.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.bind.TypeAdapters;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Provides.Type;
+import org.joda.time.Instant;
+import java.util.Set;
+
+/**
+ * Binds a {@link Gson} instance suitable for serializing and deserializing Caliper
+ * {@linkplain com.google.caliper.model model} objects.
+ */
+@Module
+public final class GsonModule {
+
+ @Provides(type = Type.SET)
+ static TypeAdapterFactory provideImmutableListTypeAdapterFactory() {
+ return new ImmutableListTypeAdatperFactory();
+ }
+
+ @Provides(type = Type.SET)
+ static TypeAdapterFactory provideImmutableMapTypeAdapterFactory() {
+ return new ImmutableMapTypeAdapterFactory();
+ }
+
+ @Provides(type = Type.SET)
+ static TypeAdapterFactory provideNaturallySortedMapTypeAdapterFactory() {
+ return new NaturallySortedMapTypeAdapterFactory();
+ }
+
+ @Provides(type = Type.SET)
+ static TypeAdapterFactory provideImmutableMultimapTypeAdapterFactory() {
+ return new ImmutableMultimapTypeAdapterFactory();
+ }
+
+ @Provides
+ static ExclusionStrategy provideAnnotationExclusionStrategy() {
+ return new AnnotationExclusionStrategy();
+ }
+
+ @Provides(type = Type.SET)
+ static TypeAdapterFactory provideTypeAdapterFactoryForInstant(
+ InstantTypeAdapter typeAdapter) {
+ return TypeAdapters.newFactory(Instant.class, typeAdapter);
+ }
+
+ @Provides static InstantTypeAdapter provideInstantTypeAdapter() {
+ return new InstantTypeAdapter();
+ }
+
+ @Provides static Gson provideGson(Set<TypeAdapterFactory> typeAdapterFactories,
+ ExclusionStrategy exclusionStrategy) {
+ GsonBuilder gsonBuilder = new GsonBuilder().setExclusionStrategies(exclusionStrategy);
+ for (TypeAdapterFactory typeAdapterFactory : typeAdapterFactories) {
+ gsonBuilder.registerTypeAdapterFactory(typeAdapterFactory);
+ }
+ return gsonBuilder.create();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java
new file mode 100644
index 0000000..5a7c925
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Serializes and deserializes {@link ImmutableList} instances using an {@link ArrayList} as an
+ * intermediary.
+ */
+final class ImmutableListTypeAdatperFactory implements TypeAdapterFactory {
+ @SuppressWarnings("unchecked")
+ @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ Type type = typeToken.getType();
+ if (typeToken.getRawType() != ImmutableList.class
+ || !(type instanceof ParameterizedType)) {
+ return null;
+ }
+
+ com.google.common.reflect.TypeToken<ImmutableList<?>> betterToken =
+ (com.google.common.reflect.TypeToken<ImmutableList<?>>)
+ com.google.common.reflect.TypeToken.of(typeToken.getType());
+ final TypeAdapter<ArrayList<?>> arrayListAdapter =
+ (TypeAdapter<ArrayList<?>>) gson.getAdapter(
+ TypeToken.get(betterToken.getSupertype(List.class).getSubtype(ArrayList.class)
+ .getType()));
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ ArrayList<?> arrayList = Lists.newArrayList((List<?>) value);
+ arrayListAdapter.write(out, arrayList);
+ }
+
+ @Override public T read(JsonReader in) throws IOException {
+ ArrayList<?> arrayList = arrayListAdapter.read(in);
+ return (T) ImmutableList.copyOf(arrayList);
+ }
+ };
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java
new file mode 100644
index 0000000..fec1739
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Serializes and deserializes {@link ImmutableMap} instances using a {@link HashMap} as an
+ * intermediary.
+ */
+final class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory {
+ @SuppressWarnings("unchecked")
+ @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ Type type = typeToken.getType();
+ if (typeToken.getRawType() != ImmutableMap.class
+ || !(type instanceof ParameterizedType)) {
+ return null;
+ }
+
+ com.google.common.reflect.TypeToken<ImmutableMap<?, ?>> betterToken =
+ (com.google.common.reflect.TypeToken<ImmutableMap<?, ?>>)
+ com.google.common.reflect.TypeToken.of(typeToken.getType());
+ final TypeAdapter<HashMap<?, ?>> hashMapAdapter =
+ (TypeAdapter<HashMap<?, ?>>) gson.getAdapter(
+ TypeToken.get(betterToken.getSupertype(Map.class).getSubtype(HashMap.class)
+ .getType()));
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ HashMap<?, ?> hashMap = Maps.newHashMap((Map<?, ?>) value);
+ hashMapAdapter.write(out, hashMap);
+ }
+
+ @Override public T read(JsonReader in) throws IOException {
+ HashMap<?, ?> hashMap = hashMapAdapter.read(in);
+ return (T) ImmutableMap.copyOf(hashMap);
+ }
+ };
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java
new file mode 100644
index 0000000..9781388
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.reflect.TypeParameter;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Serializes and deserializes {@link ImmutableMultimap} instances using maps of collections as
+ * intermediaries.
+ */
+final class ImmutableMultimapTypeAdapterFactory implements TypeAdapterFactory {
+ private static <K, V> TypeToken<Map<K, List<V>>> getMapOfListsToken(
+ TypeToken<ListMultimap<K, V>> from) {
+ ParameterizedType rawType = (ParameterizedType) from.getSupertype(ListMultimap.class).getType();
+ @SuppressWarnings("unchecked") // key type is K
+ TypeToken<K> keyType = (TypeToken<K>) TypeToken.of(rawType.getActualTypeArguments()[0]);
+ @SuppressWarnings("unchecked") // value type is V
+ TypeToken<V> valueType = (TypeToken<V>) TypeToken.of(rawType.getActualTypeArguments()[1]);
+ return new TypeToken<Map<K, List<V>>>() {}
+ .where(new TypeParameter<K>() {}, keyType)
+ .where(new TypeParameter<V>() {}, valueType);
+ }
+
+ private static <K, V> TypeToken<Map<K, Set<V>>> getMapOfSetsToken(
+ TypeToken<SetMultimap<K, V>> from) {
+ ParameterizedType rawType = (ParameterizedType) from.getSupertype(SetMultimap.class).getType();
+ @SuppressWarnings("unchecked") // key type is K
+ TypeToken<K> keyType = (TypeToken<K>) TypeToken.of(rawType.getActualTypeArguments()[0]);
+ @SuppressWarnings("unchecked") // value type is V
+ TypeToken<V> valueType = (TypeToken<V>) TypeToken.of(rawType.getActualTypeArguments()[1]);
+ return new TypeToken<Map<K, Set<V>>>() {}
+ .where(new TypeParameter<K>() {}, keyType)
+ .where(new TypeParameter<V>() {}, valueType);
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public <T> TypeAdapter<T> create(Gson gson, com.google.gson.reflect.TypeToken<T> typeToken) {
+ if (ImmutableListMultimap.class.isAssignableFrom(typeToken.getRawType())) {
+ TypeToken<Map<?, List<?>>> mapToken =
+ getMapOfListsToken((TypeToken) TypeToken.of(typeToken.getType()));
+ final TypeAdapter<Map<?, List<?>>> adapter =
+ (TypeAdapter<Map<?, List<?>>>) gson.getAdapter(
+ com.google.gson.reflect.TypeToken.get(mapToken.getType()));
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ ImmutableListMultimap<?, ?> multimap = (ImmutableListMultimap<?, ?>) value;
+ adapter.write(out, (Map) multimap.asMap());
+ }
+
+ @Override public T read(JsonReader in) throws IOException {
+ Map<?, List<?>> value = adapter.read(in);
+ ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder();
+ for (Entry<?, List<?>> entry : value.entrySet()) {
+ builder.putAll(entry.getKey(), entry.getValue());
+ }
+ return (T) builder.build();
+ }
+ };
+ } else if (ImmutableSetMultimap.class.isAssignableFrom(typeToken.getRawType())) {
+ TypeToken<Map<?, Set<?>>> mapToken =
+ getMapOfSetsToken((TypeToken) TypeToken.of(typeToken.getType()));
+ final TypeAdapter<Map<?, Set<?>>> adapter =
+ (TypeAdapter<Map<?, Set<?>>>) gson.getAdapter(
+ com.google.gson.reflect.TypeToken.get(mapToken.getType()));
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ ImmutableSetMultimap<?, ?> multimap = (ImmutableSetMultimap<?, ?>) value;
+ adapter.write(out, (Map) multimap.asMap());
+ }
+
+ @Override public T read(JsonReader in) throws IOException {
+ Map<?, Set<?>> value = adapter.read(in);
+ ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder();
+ for (Entry<?, Set<?>> entry : value.entrySet()) {
+ builder.putAll(entry.getKey(), entry.getValue());
+ }
+ return (T) builder.build();
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java b/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java
new file mode 100644
index 0000000..ae500f7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import org.joda.time.Instant;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+
+/**
+ * Serializes and deserializes {@link Instant} instances.
+ */
+final class InstantTypeAdapter extends TypeAdapter<Instant> {
+ @Override public void write(JsonWriter out, Instant value) throws IOException {
+ out.value(ISODateTimeFormat.dateTime().print(value));
+ }
+
+ @Override public Instant read(JsonReader in) throws IOException {
+ return ISODateTimeFormat.dateTime().parseDateTime(in.nextString()).toInstant();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java
new file mode 100644
index 0000000..92fdb19
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.json;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Serializes and deserializes {@link SortedMap} instances using a {@link TreeMap} with natural
+ * ordering as an intermediary.
+ */
+final class NaturallySortedMapTypeAdapterFactory implements TypeAdapterFactory {
+ @SuppressWarnings("rawtypes")
+ private static final ImmutableSet<Class<? extends SortedMap>> CLASSES =
+ ImmutableSet.of(SortedMap.class, TreeMap.class);
+
+ @SuppressWarnings("unchecked")
+ @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ Type type = typeToken.getType();
+ if (!CLASSES.contains(typeToken.getRawType())
+ || !(type instanceof ParameterizedType)) {
+ return null;
+ }
+
+ com.google.common.reflect.TypeToken<SortedMap<?, ?>> betterToken =
+ (com.google.common.reflect.TypeToken<SortedMap<?, ?>>)
+ com.google.common.reflect.TypeToken.of(typeToken.getType());
+ final TypeAdapter<Map<?, ?>> mapAdapter =
+ (TypeAdapter<Map<?, ?>>) gson.getAdapter(
+ TypeToken.get(betterToken.getSupertype(Map.class).getType()));
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ TreeMap<?, ?> treeMap = Maps.newTreeMap((SortedMap<?, ?>) value);
+ mapAdapter.write(out, treeMap);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override public T read(JsonReader in) throws IOException {
+ TreeMap treeMap = Maps.newTreeMap();
+ treeMap.putAll(mapAdapter.read(in));
+ return (T) treeMap;
+ }
+ };
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/memory/Chain.java b/caliper/src/main/java/com/google/caliper/memory/Chain.java
new file mode 100644
index 0000000..115141e
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/memory/Chain.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.memory;
+
+import com.google.common.base.Preconditions;
+
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * A chain of references, which starts at a root object and leads to a
+ * particular value (either an object or a primitive).
+ */
+public abstract class Chain {
+ private final Object value;
+ private final Chain parent;
+
+ Chain(Chain parent, Object value) {
+ this.parent = parent;
+ this.value = value;
+ }
+
+ static Chain root(Object value) {
+ return new Chain(null, Preconditions.checkNotNull(value)) {
+ @Override
+ public Class<?> getValueType() {
+ return getValue().getClass();
+ }
+ };
+ }
+
+ FieldChain appendField(Field field, Object value) {
+ return new FieldChain(this, Preconditions.checkNotNull(field), value);
+ }
+
+ ArrayIndexChain appendArrayIndex(int arrayIndex, Object value) {
+ return new ArrayIndexChain(this, arrayIndex, value);
+ }
+
+ /**
+ * Returns whether this chain has a parent. This returns false only when
+ * this chain represents the root object itself.
+ */
+ public boolean hasParent() {
+ return parent != null;
+ }
+
+ /**
+ * Returns the parent chain, from which this chain was created.
+ * @throws IllegalStateException if {@code !hasParent()}, then an
+ */
+ public @Nonnull Chain getParent() {
+ Preconditions.checkState(parent != null, "This is the root value, it has no parent");
+ return parent;
+ }
+
+ /**
+ * Returns the value that this chain leads to. If the value is a primitive,
+ * a wrapper object is returned instead.
+ */
+ public @Nullable Object getValue() {
+ return value;
+ }
+
+ public abstract @Nonnull Class<?> getValueType();
+
+ /**
+ * Returns whether the connection of the parent chain and this chain is
+ * through a field (of the getParent().getValue().getClass() class).
+ */
+ public boolean isThroughField() {
+ return false;
+ }
+
+ /**
+ * Returns whether the connection of the parent chain and this chain is
+ * through an array index, i.e. the parent leads to an array, and this
+ * chain leads to an element of that array.
+ */
+ public boolean isThroughArrayIndex() {
+ return false;
+ }
+
+ /**
+ * Returns whether the value of this chain represents a primitive.
+ */
+ public boolean isPrimitive() {
+ return getValueType().isPrimitive();
+ }
+
+ /**
+ * Returns the root object of this chain.
+ */
+ public @Nonnull Object getRoot() {
+ Chain current = this;
+ while (current.hasParent()) {
+ current = current.getParent();
+ }
+ return current.getValue();
+ }
+
+ Deque<Chain> reverse() {
+ Deque<Chain> reverseChain = new ArrayDeque<Chain>(8);
+ Chain current = this;
+ reverseChain.addFirst(current);
+ while (current.hasParent()) {
+ current = current.getParent();
+ reverseChain.addFirst(current);
+ }
+ return reverseChain;
+ }
+
+ @Override public final String toString() {
+ StringBuilder sb = new StringBuilder(32);
+
+ Iterator<Chain> it = reverse().iterator();
+ sb.append(it.next().getValue());
+ while (it.hasNext()) {
+ sb.append("->");
+ Chain current = it.next();
+ if (current.isThroughField()) {
+ sb.append(((FieldChain)current).getField().getName());
+ } else if (current.isThroughArrayIndex()) {
+ sb.append("[").append(((ArrayIndexChain)current).getArrayIndex()).append("]");
+ }
+ }
+ return sb.toString();
+ }
+
+ static class FieldChain extends Chain {
+ private final Field field;
+
+ FieldChain(Chain parent, Field referringField, Object value) {
+ super(parent, value);
+ this.field = referringField;
+ }
+
+ @Override
+ public boolean isThroughField() {
+ return true;
+ }
+
+ @Override
+ public boolean isThroughArrayIndex() {
+ return false;
+ }
+
+ @Override
+ public Class<?> getValueType() {
+ return field.getType();
+ }
+
+ public Field getField() {
+ return field;
+ }
+ }
+
+ static class ArrayIndexChain extends Chain {
+ private final int index;
+
+ ArrayIndexChain(Chain parent, int index, Object value) {
+ super(parent, value);
+ this.index = index;
+ }
+
+ @Override
+ public boolean isThroughField() {
+ return false;
+ }
+
+ @Override
+ public boolean isThroughArrayIndex() {
+ return true;
+ }
+
+ @Override
+ public Class<?> getValueType() {
+ return getParent().getValue().getClass().getComponentType();
+ }
+
+ public int getArrayIndex() {
+ return index;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java b/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java
new file mode 100644
index 0000000..2c4e4cc
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.memory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.EnumSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A depth-first object graph explorer. The traversal starts at a root (an
+ * {@code Object}) and explores any other reachable object (recursively) or
+ * primitive value, excluding static fields from the traversal. The traversal
+ * is controlled by a user-supplied {@link ObjectVisitor}, which decides for
+ * each explored path whether to continue exploration of that path, and it can
+ * also return a value at the end of the traversal.
+ */
+public final class ObjectExplorer {
+ private ObjectExplorer() { }
+
+ /**
+ * Explores an object graph (defined by a root object and whatever is
+ * reachable through it, following non-static fields) while using an
+ * {@link ObjectVisitor} to both control the traversal and return a value.
+ *
+ * <p>Equivalent to {@code exploreObject(rootObject, visitor,
+ * EnumSet.noneOf(Feature.class))}.
+ *
+ * @param <T> the type of the value obtained (after the traversal) by the
+ * ObjectVisitor
+ * @param rootObject an object to be recursively explored
+ * @param visitor a visitor that is notified for each explored path and
+ * decides whether to continue exploration of that path, and constructs a
+ * return value at the end of the exploration
+ * @return whatever value is returned by the visitor at the end of the
+ * traversal
+ * @see ObjectVisitor
+ */
+ public static <T> T exploreObject(Object rootObject, ObjectVisitor<T> visitor) {
+ return exploreObject(rootObject, visitor, EnumSet.noneOf(Feature.class));
+ }
+
+ /**
+ * Explores an object graph (defined by a root object and whatever is
+ * reachable through it, following non-static fields) while using an
+ * {@link ObjectVisitor} to both control the traversal and return a value.
+ *
+ * <p>The {@code features} further customizes the exploration behavior.
+ * In particular:
+ * <ul>
+ * <li>If {@link Feature#VISIT_PRIMITIVES} is contained in features,
+ * the visitor will also be notified about exploration of primitive values.
+ * <li>If {@link Feature#VISIT_NULL} is contained in features, the visitor
+ * will also be notified about exploration of {@code null} values.
+ * </ul>
+ * In both cases above, the return value of
+ * {@link ObjectVisitor#visit(Chain)} is ignored, since neither primitive
+ * values or {@code null} can be further explored.
+ *
+ * @param <T> the type of the value obtained (after the traversal) by the
+ * ObjectVisitor
+ * @param rootObject an object to be recursively explored
+ * @param visitor a visitor that is notified for each explored path
+ * and decides whether to continue exploration of that path, and constructs
+ * a return value at the end of the exploration
+ * @param features a set of desired features that the object exploration should have
+ * @return whatever value is returned by the visitor at the end of the traversal
+ * @see ObjectVisitor
+ */
+ public static <T> T exploreObject(Object rootObject,
+ ObjectVisitor<T> visitor, EnumSet<Feature> features) {
+ Deque<Chain> stack = new ArrayDeque<Chain>(32);
+ if (rootObject != null) stack.push(Chain.root(rootObject));
+
+ while (!stack.isEmpty()) {
+ Chain chain = stack.pop();
+ //the only place where the return value of visit() is considered
+ ObjectVisitor.Traversal traversal = visitor.visit(chain);
+ switch (traversal) {
+ case SKIP: continue;
+ case EXPLORE: break;
+ default: throw new AssertionError();
+ }
+
+ //only nonnull values pushed in the stack
+ @Nonnull Object value = chain.getValue();
+ Class<?> valueClass = value.getClass();
+ if (valueClass.isArray()) {
+ boolean isPrimitive = valueClass.getComponentType().isPrimitive();
+ /*
+ * Since we push paths to explore in a stack, we push references found in the array in
+ * reverse order, so when we pop them, they will be in the array's order.
+ */
+ for (int i = Array.getLength(value) - 1; i >= 0; i--) {
+ Object childValue = Array.get(value, i);
+ if (isPrimitive) {
+ if (features.contains(Feature.VISIT_PRIMITIVES)) {
+ visitor.visit(chain.appendArrayIndex(i, childValue));
+ }
+ } else if (childValue == null) {
+ if (features.contains(Feature.VISIT_NULL)) {
+ visitor.visit(chain.appendArrayIndex(i, childValue));
+ }
+ } else {
+ stack.push(chain.appendArrayIndex(i, childValue));
+ }
+ }
+ } else {
+ /*
+ * Reflection usually provides fields in declaration order. As above in arrays, we push
+ * them to the stack in reverse order, so when we pop them, we get them in the original
+ * (declaration) order.
+ */
+ final Field[] fields = getAllFields(value);
+ for (int j = fields.length - 1; j >= 0; j--) {
+ final Field field = fields[j];
+ Object childValue = null;
+ try {
+ childValue = field.get(value);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ if (childValue == null) { // handling nulls
+ if (features.contains(Feature.VISIT_NULL)) {
+ visitor.visit(chain.appendField(field, childValue));
+ }
+ } else { // handling primitives or references
+ boolean isPrimitive = field.getType().isPrimitive();
+ Chain extendedChain = chain.appendField(field, childValue);
+ if (isPrimitive) {
+ if (features.contains(Feature.VISIT_PRIMITIVES)) {
+ visitor.visit(extendedChain);
+ }
+ } else {
+ stack.push(extendedChain);
+ }
+ }
+ }
+ }
+ }
+ return visitor.result();
+ }
+
+ /**
+ * A stateful predicate that allows exploring an object (the tail of the chain) only once.
+ */
+ static class AtMostOncePredicate implements Predicate<Chain> {
+ private final Set<Object> seen = Collections.newSetFromMap(
+ new IdentityHashMap<Object, Boolean>());
+
+ @Override public boolean apply(Chain chain) {
+ return seen.add(chain.getValue());
+ }
+ }
+
+ static final Predicate<Chain> notEnumFieldsOrClasses = new Predicate<Chain>() {
+ @Override public boolean apply(Chain chain) {
+ return !(Enum.class.isAssignableFrom(chain.getValueType())
+ || chain.getValue() instanceof Class<?>);
+ }
+ };
+
+ static final Function<Chain, Object> chainToObject =
+ new Function<Chain, Object>() {
+ @Override public Object apply(Chain chain) {
+ return chain.getValue();
+ }
+ };
+
+ /**
+ * A cache of {@code Field}s that are accessible for a given {@code Class<?>}.
+ */
+ private static final ConcurrentHashMap<Class<?>, Field[]> clazzFields =
+ new ConcurrentHashMap<Class<?>, Field[]>();
+
+ private static Field[] getAllFields(Object o) {
+ Class<?> clazz = o.getClass();
+ return getAllFields(clazz);
+ }
+
+ /**
+ * Keep a cache of fields per class of interest.
+ *
+ * @param clazz - the {@code Class<?>} to interrogate.
+ * @return An array of fields of the given class.
+ */
+ private static Field[] getAllFields(Class<?> clazz) {
+ Field[] f = clazzFields.get(clazz);
+ if (f == null) {
+ f = computeAllFields(clazz);
+ Field[] u = clazzFields.putIfAbsent(clazz, f);
+ return u == null ? f : u;
+ }
+ return f;
+ }
+
+ private static Field[] computeAllFields(Class<?> clazz) {
+ List<Field> fields = Lists.newArrayListWithCapacity(8);
+ while (clazz != null) {
+ for (Field field : clazz.getDeclaredFields()) {
+ // add only non-static fields
+ if (!Modifier.isStatic(field.getModifiers())) {
+ fields.add(field);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+
+ // all together so there is only one security check
+ Field[] afields = fields.toArray(new Field[fields.size()]);
+ AccessibleObject.setAccessible(afields, true);
+ return afields;
+ }
+
+ /**
+ * Enumeration of features that may be optionally requested for an object
+ * traversal.
+ *
+ * @see ObjectExplorer#exploreObject(Object, ObjectVisitor, EnumSet)
+ */
+ public enum Feature {
+ /**
+ * Null references should be visited.
+ */
+ VISIT_NULL,
+
+ /**
+ * Primitive values should be visited.
+ */
+ VISIT_PRIMITIVES
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java b/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java
new file mode 100644
index 0000000..3cacaeb
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.memory;
+
+import com.google.caliper.memory.ObjectExplorer.Feature;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multiset;
+
+import java.util.EnumSet;
+
+/**
+ * A tool that can qualitatively measure the footprint
+ * ({@literal e.g.}, number of objects, references,
+ * primitives) of a graph structure.
+ */
+public final class ObjectGraphMeasurer {
+ /**
+ * The footprint of an object graph.
+ */
+ public final static class Footprint {
+ private final int objects;
+ private final int nonNullRefs;
+ private final int nullRefs;
+ private final ImmutableMultiset<Class<?>> primitives;
+
+ private static final ImmutableSet<Class<?>> primitiveTypes = ImmutableSet.<Class<?>>of(
+ boolean.class, byte.class, char.class, short.class,
+ int.class, float.class, long.class, double.class);
+
+ /**
+ * Constructs a Footprint, by specifying the number of objects,
+ * references, and primitives (represented as a {@link Multiset}).
+ *
+ * @param objects the number of objects
+ * @param nonNullRefs the number of non-null references
+ * @param nullRefs the number of null references
+ * @param primitives the number of primitives (represented by the
+ * respective primitive classes, e.g. {@code int.class} etc)
+ */
+ public Footprint(int objects, int nonNullRefs, int nullRefs,
+ Multiset<Class<?>> primitives) {
+ Preconditions.checkArgument(objects >= 0, "Negative number of objects");
+ Preconditions.checkArgument(nonNullRefs >= 0, "Negative number of references");
+ Preconditions.checkArgument(nullRefs >= 0, "Negative number of references");
+ Preconditions.checkArgument(primitiveTypes.containsAll(primitives.elementSet()),
+ "Unexpected primitive type");
+ this.objects = objects;
+ this.nonNullRefs = nonNullRefs;
+ this.nullRefs = nullRefs;
+ this.primitives = ImmutableMultiset.copyOf(primitives);
+ }
+
+ /**
+ * Returns the number of objects of this footprint.
+ */
+ public int getObjects() {
+ return objects;
+ }
+
+ /**
+ * Returns the number of non-null references of this footprint.
+ */
+ public int getNonNullReferences() {
+ return nonNullRefs;
+ }
+
+ /**
+ * Returns the number of null references of this footprint.
+ */
+ public int getNullReferences() {
+ return nullRefs;
+ }
+
+ /**
+ * Returns the number of all references (null and non-null) of this footprint.
+ */
+ public int getAllReferences() {
+ return nonNullRefs + nullRefs;
+ }
+
+ /**
+ * Returns the number of primitives of this footprint
+ * (represented by the respective primitive classes,
+ * {@literal e.g.} {@code int.class} etc).
+ */
+ public ImmutableMultiset<Class<?>> getPrimitives() {
+ return primitives;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(getClass().getName(),
+ objects, nonNullRefs, nullRefs, primitives);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Footprint) {
+ Footprint that = (Footprint) o;
+ return this.objects == that.objects
+ && this.nonNullRefs == that.nonNullRefs
+ && this.nullRefs == that.nullRefs
+ && this.primitives.equals(that.primitives);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("Objects", objects)
+ .add("NonNullRefs", nonNullRefs)
+ .add("NullRefs", nullRefs)
+ .add("Primitives", primitives)
+ .toString();
+ }
+ }
+
+ /**
+ * Measures the footprint of the specified object graph.
+ * The object graph is defined by a root object and whatever object can be
+ * reached through that, excluding static fields, {@code Class} objects,
+ * and fields defined in {@code enum}s (all these are considered shared
+ * values, which should not contribute to the cost of any single object
+ * graph).
+ *
+ * <p>Equivalent to {@code measure(rootObject, Predicates.alwaysTrue())}.
+ *
+ * @param rootObject the root object of the object graph
+ * @return the footprint of the object graph
+ */
+ public static Footprint measure(Object rootObject) {
+ return measure(rootObject, Predicates.alwaysTrue());
+ }
+
+ /**
+ * Measures the footprint of the specified object graph.
+ * The object graph is defined by a root object and whatever object can be
+ * reached through that, excluding static fields, {@code Class} objects,
+ * and fields defined in {@code enum}s (all these are considered shared
+ * values, which should not contribute to the cost of any single object
+ * graph), and any object for which the user-provided predicate returns
+ * {@code false}.
+ *
+ * @param rootObject the root object of the object graph
+ * @param objectAcceptor a predicate that returns {@code true} for objects
+ * to be explored (and treated as part of the footprint), or {@code false}
+ * to forbid the traversal to traverse the given object
+ * @return the footprint of the object graph
+ */
+ public static Footprint measure(Object rootObject, Predicate<Object> objectAcceptor) {
+ Preconditions.checkNotNull(objectAcceptor, "predicate");
+
+ Predicate<Chain> completePredicate = Predicates.and(ImmutableList.of(
+ ObjectExplorer.notEnumFieldsOrClasses,
+ new ObjectExplorer.AtMostOncePredicate(),
+ Predicates.compose(objectAcceptor, ObjectExplorer.chainToObject)
+ ));
+
+ return ObjectExplorer.exploreObject(rootObject, new ObjectGraphVisitor(completePredicate),
+ EnumSet.of(Feature.VISIT_PRIMITIVES, Feature.VISIT_NULL));
+ }
+
+ private static class ObjectGraphVisitor implements ObjectVisitor<Footprint> {
+ private int objects;
+ // -1 to account for the root, which has no reference leading to it
+ private int nonNullReferences = -1;
+ private int nullReferences = 0;
+ private final Multiset<Class<?>> primitives = HashMultiset.create();
+ private final Predicate<Chain> predicate;
+
+ ObjectGraphVisitor(Predicate<Chain> predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override public Traversal visit(Chain chain) {
+ if (chain.isPrimitive()) {
+ primitives.add(chain.getValueType());
+ return Traversal.SKIP;
+ } else {
+ if (chain.getValue() == null) {
+ nullReferences++;
+ } else {
+ nonNullReferences++;
+ }
+ }
+ if (predicate.apply(chain) && chain.getValue() != null) {
+ objects++;
+ return Traversal.EXPLORE;
+ }
+ return Traversal.SKIP;
+ }
+
+ @Override public Footprint result() {
+ return new Footprint(objects, nonNullReferences, nullReferences,
+ ImmutableMultiset.copyOf(primitives));
+ }
+ }
+
+ private ObjectGraphMeasurer() {}
+}
diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java b/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java
new file mode 100644
index 0000000..2a1320e
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.memory;
+
+/**
+ * A visitor that controls an object traversal. Implementations
+ * of this interface are passed to {@link ObjectExplorer} exploration methods.
+ *
+ * @param <T> the type of the result that this visitor returns
+ * (can be defined as {@code Void} to denote no result}.
+ *
+ * @see ObjectExplorer
+ */
+public interface ObjectVisitor<T> {
+ /**
+ * Visits an explored value (the whole chain from the root object
+ * leading to the value is provided), and decides whether to continue
+ * the exploration of that value.
+ *
+ * <p>In case the explored value is either primitive or {@code null}
+ * (e.g., if {@code chain.isPrimitive() || chain.getValue() == null}),
+ * the return value is meaningless and is ignored.
+ *
+ * @param chain the chain that leads to the explored value.
+ * @return {@link Traversal#EXPLORE} to denote that the visited object
+ * should be further explored, or {@link Traversal#SKIP} to avoid
+ * exploring it.
+ */
+ Traversal visit(Chain chain);
+
+ /**
+ * Returns an arbitrary value (presumably constructed during the object
+ * graph traversal).
+ */
+ T result();
+
+ /**
+ * Constants that denote how the traversal of a given object (chain)
+ * should continue.
+ */
+ enum Traversal {
+ /**
+ * The visited object should be further explored.
+ */
+ EXPLORE,
+
+ /**
+ * The visited object should not be explored.
+ */
+ SKIP
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java b/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java
new file mode 100644
index 0000000..5ceed02
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that identifies a given method as an "allocation measurement" method. The method
+ * should take a single int parameter; its return value may be any type. Methods with this
+ * annotation may be used with the {@link com.google.caliper.runner.AllocationInstrument}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface AllocationMeasurement {}
diff --git a/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java b/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java
new file mode 100644
index 0000000..bcfdd49
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that identifies a given method as an "arbitrary measurement" method. The method should
+ * take no parameters and return a double, which is the measured value.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ArbitraryMeasurement {
+
+ /**
+ * The units for the value returned by this measurement method.
+ */
+ String units() default "";
+
+ /**
+ * Text description of the quantity measured by this measurement method.
+ */
+ String description() default "";
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java b/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java
new file mode 100644
index 0000000..c2fff4c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Maps;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.PrimitiveSink;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * A specification by which a benchmark method invocation can be uniquely identified.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class BenchmarkSpec implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ static final BenchmarkSpec DEFAULT = new BenchmarkSpec();
+
+ @ExcludeFromJson private int id;
+ private String className;
+ private String methodName;
+ private SortedMap<String, String> parameters;
+ @ExcludeFromJson private int hash;
+
+ private BenchmarkSpec() {
+ this.className = "";
+ this.methodName = "";
+ this.parameters = Maps.newTreeMap();
+ }
+
+ private BenchmarkSpec(Builder builder) {
+ this.className = builder.className;
+ this.methodName = builder.methodName;
+ this.parameters = Maps.newTreeMap(builder.parameters);
+ }
+
+ public String className() {
+ return className;
+ }
+
+ public String methodName() {
+ return methodName;
+ }
+
+ public ImmutableSortedMap<String, String> parameters() {
+ return ImmutableSortedMap.copyOf(parameters);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof BenchmarkSpec) {
+ BenchmarkSpec that = (BenchmarkSpec) obj;
+ return this.className.equals(that.className)
+ && this.methodName.equals(that.methodName)
+ && this.parameters.equals(that.parameters);
+ } else {
+ return false;
+ }
+ }
+
+ private void initHash() {
+ if (hash == 0) {
+ this.hash = getPersistentHashFunction()
+ .hashObject(this, BenchmarkSpecFunnel.INSTANCE).asInt();
+ }
+ }
+
+ @Override public int hashCode() {
+ initHash();
+ return hash;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("className", className)
+ .add("methodName", methodName)
+ .add("parameters", parameters)
+ .toString();
+ }
+
+ enum BenchmarkSpecFunnel implements Funnel<BenchmarkSpec> {
+ INSTANCE;
+
+ @Override public void funnel(BenchmarkSpec from, PrimitiveSink into) {
+ into.putUnencodedChars(from.className)
+ .putUnencodedChars(from.methodName);
+ StringMapFunnel.INSTANCE.funnel(from.parameters, into);
+ }
+ }
+
+ public static final class Builder {
+ private String className;
+ private String methodName;
+ private final SortedMap<String, String> parameters = Maps.newTreeMap();
+
+ public Builder className(String className) {
+ this.className = checkNotNull(className);
+ return this;
+ }
+
+ public Builder methodName(String methodName) {
+ this.methodName = checkNotNull(methodName);
+ return this;
+ }
+
+ public Builder addParameter(String parameterName, String value) {
+ this.parameters.put(checkNotNull(parameterName), checkNotNull(value));
+ return this;
+ }
+
+ public Builder addAllParameters(Map<String, String> parameters) {
+ this.parameters.putAll(parameters);
+ return this;
+ }
+
+ public BenchmarkSpec build() {
+ checkState(className != null);
+ checkState(methodName != null);
+ return new BenchmarkSpec(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/Defaults.java b/caliper/src/main/java/com/google/caliper/model/Defaults.java
new file mode 100644
index 0000000..1765e09
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Defaults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import org.joda.time.Instant;
+
+import java.util.UUID;
+
+/**
+ * Default instances of non-model classes used by the Caliper model. These are present to facilitate
+ * Gson deserialization.
+ */
+final class Defaults {
+ static final UUID UUID = new UUID(0L, 0L);
+ static final Instant INSTANT = new Instant(0L);
+
+ private Defaults() {}
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java b/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java
new file mode 100644
index 0000000..fdf0b95
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a field or type should not be serialized to JSON.
+ */
+@Target({FIELD, TYPE})
+@Retention(RUNTIME)
+public @interface ExcludeFromJson {}
diff --git a/caliper/src/main/java/com/google/caliper/model/Host.java b/caliper/src/main/java/com/google/caliper/model/Host.java
new file mode 100644
index 0000000..ecd098c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Host.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Maps;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.PrimitiveSink;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.logging.Logger;
+
+/**
+ * The performance-informing properties of the host on which a benchmark is run.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class Host {
+ static final Host DEFAULT = new Host();
+ private static final Logger logger = Logger.getLogger(Host.class.getName());
+
+ @ExcludeFromJson private int id;
+ private SortedMap<String, String> properties;
+ @ExcludeFromJson private int hash;
+
+ private Host() {
+ this.properties = Maps.newTreeMap();
+ }
+
+ private Host(Builder builder) {
+ this.properties = Maps.newTreeMap(builder.properties);
+ // eagerly initialize hash to allow for the test-only hash function
+ initHash(builder.hashFunction);
+ }
+
+ public ImmutableSortedMap<String, String> properties() {
+ return ImmutableSortedMap.copyOf(properties);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Host) {
+ Host that = (Host) obj;
+ return this.properties.equals(that.properties);
+ } else {
+ return false;
+ }
+ }
+
+ private void initHash(HashFunction hashFunction) {
+ if (hash == 0) {
+ this.hash = hashFunction.hashObject(this, HostFunnel.INSTANCE).asInt();
+ }
+ }
+
+ private void initHash() {
+ initHash(getPersistentHashFunction());
+ }
+
+ @Override public int hashCode() {
+ initHash();
+ return hash;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("properties", properties)
+ .toString();
+ }
+
+ enum HostFunnel implements Funnel<Host> {
+ INSTANCE;
+
+ @Override public void funnel(Host from, PrimitiveSink into) {
+ StringMapFunnel.INSTANCE.funnel(from.properties, into);
+ }
+ }
+
+ public static final class Builder {
+ private final SortedMap<String, String> properties = Maps.newTreeMap();
+ private HashFunction hashFunction = getPersistentHashFunction();
+
+ public Builder addProperty(String key, String value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ public Builder addAllProperies(Map<String, String> properties) {
+ this.properties.putAll(properties);
+ return this;
+ }
+
+ /**
+ * This only exists for tests to induce hash collisions. Only use this in test code as changing
+ * the hash function will break persisted objects.
+ */
+ @VisibleForTesting public Builder hashFunctionForTesting(HashFunction hashFunction) {
+ logger.warning("somebody is setting the hash function. this should only be used in tests");
+ this.hashFunction = checkNotNull(hashFunction);
+ return this;
+ }
+
+ public Host build() {
+ return new Host(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java b/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java
new file mode 100644
index 0000000..6a7cd77
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Maps;
+
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * A specification by which the application of an instrument can be uniquely identified.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class InstrumentSpec {
+ static final InstrumentSpec DEFAULT = new InstrumentSpec();
+
+ @ExcludeFromJson
+ private int id;
+ private String className;
+ private SortedMap<String, String> options;
+ @ExcludeFromJson
+ private int hash;
+
+ private InstrumentSpec() {
+ this.className = "";
+ this.options = Maps.newTreeMap();
+ }
+
+ private InstrumentSpec(Builder builder) {
+ this.className = builder.className;
+ this.options = Maps.newTreeMap(builder.options);
+ }
+
+ public String className() {
+ return className;
+ }
+
+ public ImmutableSortedMap<String, String> options() {
+ return ImmutableSortedMap.copyOf(options);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof InstrumentSpec) {
+ InstrumentSpec that = (InstrumentSpec) obj;
+ return this.className.equals(that.className)
+ && this.options.equals(that.options);
+ } else {
+ return false;
+ }
+ }
+
+ private void initHash() {
+ if (hash == 0) {
+ this.hash = getPersistentHashFunction()
+ .newHasher()
+ .putUnencodedChars(className)
+ .putObject(options, StringMapFunnel.INSTANCE)
+ .hash().asInt();
+ }
+ }
+
+ @Override public int hashCode() {
+ initHash();
+ return hash;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("className", className)
+ .add("options", options)
+ .toString();
+ }
+
+ public static final class Builder {
+ private String className;
+ private final SortedMap<String, String> options = Maps.newTreeMap();
+
+ public Builder className(String className) {
+ this.className = checkNotNull(className);
+ return this;
+ }
+
+ public Builder instrumentClass(Class<?> insturmentClass) {
+ return className(insturmentClass.getName());
+ }
+
+ public Builder addOption(String option, String value) {
+ this.options.put(option, value);
+ return this;
+ }
+
+ public Builder addAllOptions(Map<String, String> options) {
+ this.options.putAll(options);
+ return this;
+ }
+
+ public InstrumentSpec build() {
+ checkState(className != null);
+ return new InstrumentSpec(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/Measurement.java b/caliper/src/main/java/com/google/caliper/model/Measurement.java
new file mode 100644
index 0000000..8effce7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Measurement.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Multimaps;
+
+import java.io.Serializable;
+
+/**
+ * A single, weighted measurement.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public class Measurement implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public static ImmutableListMultimap<String, Measurement> indexByDescription(
+ Iterable<Measurement> measurements) {
+ return Multimaps.index(measurements, new Function<Measurement, String>() {
+ @Override public String apply(Measurement input) {
+ return input.description;
+ }
+ });
+ }
+
+ @ExcludeFromJson
+ private int id;
+ private Value value;
+ private double weight;
+ private String description;
+
+ private Measurement() {
+ this.value = Value.DEFAULT;
+ this.weight = 0.0;
+ this.description = "";
+ }
+
+ private Measurement(Builder builder) {
+ this.value = builder.value;
+ this.description = builder.description;
+ this.weight = builder.weight;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Measurement) {
+ Measurement that = (Measurement) obj;
+ return this.value.equals(that.value)
+ && this.weight == that.weight
+ && this.description.equals(that.description);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(value, weight, description);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("value", value)
+ .add("weight", weight)
+ .add("description", description)
+ .toString();
+ }
+
+ public Value value() {
+ return value;
+ }
+
+ public double weight() {
+ return weight;
+ }
+
+ public String description() {
+ return description;
+ }
+
+ public static final class Builder {
+ private Value value;
+ private Double weight;
+ private String description;
+
+ public Builder value(Value value) {
+ this.value = checkNotNull(value);
+ return this;
+ }
+
+ public Builder weight(double weight) {
+ checkArgument(weight > 0);
+ this.weight = weight;
+ return this;
+ }
+
+ public Builder description(String description) {
+ this.description = checkNotNull(description);
+ return this;
+ }
+
+ public Measurement build() {
+ checkArgument(value != null);
+ checkArgument(weight != null);
+ checkArgument(description != null);
+ return new Measurement(this);
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/AllTests.java b/caliper/src/main/java/com/google/caliper/model/PersistentHashing.java
index 0e3d1cc..48a3e08 100644
--- a/caliper/src/test/java/com/google/caliper/AllTests.java
+++ b/caliper/src/main/java/com/google/caliper/model/PersistentHashing.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc.
+ * Copyright (C) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,18 @@
* limitations under the License.
*/
-package com.google.caliper;
+package com.google.caliper.model;
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
-public class AllTests {
- public static Test suite() {
- TestSuite suite = new TestSuite();
- suite.addTestSuite(CaliperTest.class);
- suite.addTestSuite(JsonTest.class);
- suite.addTestSuite(MeasurementSetTest.class);
- suite.addTestSuite(ParameterTest.class);
- suite.addTestSuite(WarmupOverflowTest.class);
+/**
+ * A utility class for standardizing the hash function that we're using for persistence.
+ */
+final class PersistentHashing {
+ private PersistentHashing() {}
- return suite;
+ static HashFunction getPersistentHashFunction() {
+ return Hashing.murmur3_32();
}
}
diff --git a/caliper/src/main/java/com/google/caliper/model/Run.java b/caliper/src/main/java/com/google/caliper/model/Run.java
new file mode 100644
index 0000000..eb9733b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Run.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+import org.joda.time.Instant;
+
+import java.util.UUID;
+
+/**
+ * A single invocation of caliper.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class Run {
+ static final Run DEFAULT = new Run();
+
+ private UUID id;
+ private String label;
+ private Instant startTime;
+
+ private Run() {
+ this.id = Defaults.UUID;
+ this.label = "";
+ this.startTime = Defaults.INSTANT;
+ }
+
+ private Run(Builder builder) {
+ this.id = builder.id;
+ this.label = builder.label;
+ this.startTime = builder.startTime;
+ }
+
+ public UUID id() {
+ return id;
+ }
+
+ public String label() {
+ return label;
+ }
+
+ public Instant startTime() {
+ return startTime;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Run) {
+ Run that = (Run) obj;
+ return this.id.equals(that.id)
+ && this.label.equals(that.label)
+ && this.startTime.equals(that.startTime);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(id, label, startTime);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", id)
+ .add("label", label)
+ .add("startTime", startTime)
+ .toString();
+ }
+
+ public static final class Builder {
+ private UUID id;
+ private String label = "";
+ private Instant startTime;
+
+ public Builder(UUID id) {
+ this.id = checkNotNull(id);
+ }
+
+ public Builder label(String label) {
+ this.label = checkNotNull(label);
+ return this;
+ }
+
+ public Builder startTime(Instant startTime) {
+ this.startTime = checkNotNull(startTime);
+ return this;
+ }
+
+ public Run build() {
+ checkState(id != null);
+ checkState(startTime != null);
+ return new Run(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/Scenario.java b/caliper/src/main/java/com/google/caliper/model/Scenario.java
new file mode 100644
index 0000000..414d4ab
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Scenario.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.model.BenchmarkSpec.BenchmarkSpecFunnel;
+import com.google.caliper.model.Host.HostFunnel;
+import com.google.caliper.model.VmSpec.VmSpecFunnel;
+import com.google.common.base.MoreObjects;
+
+/**
+ * The combination of properties whose combination, when measured with a particular instrument,
+ * should produce a repeatable result
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class Scenario {
+ static final Scenario DEFAULT = new Scenario();
+
+ @ExcludeFromJson private int id;
+ private Host host;
+ private VmSpec vmSpec;
+ private BenchmarkSpec benchmarkSpec;
+ // TODO(gak): include data about caliper itself and the code being benchmarked
+ @ExcludeFromJson private int hash;
+
+ private Scenario() {
+ this.host = Host.DEFAULT;
+ this.vmSpec = VmSpec.DEFAULT;
+ this.benchmarkSpec = BenchmarkSpec.DEFAULT;
+ }
+
+ private Scenario(Builder builder) {
+ this.host = builder.host;
+ this.vmSpec = builder.vmSpec;
+ this.benchmarkSpec = builder.benchmarkSpec;
+ }
+
+ public Host host() {
+ return host;
+ }
+
+ public VmSpec vmSpec() {
+ return vmSpec;
+ }
+
+ public BenchmarkSpec benchmarkSpec() {
+ return benchmarkSpec;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Scenario) {
+ Scenario that = (Scenario) obj;
+ return this.host.equals(that.host)
+ && this.vmSpec.equals(that.vmSpec)
+ && this.benchmarkSpec.equals(that.benchmarkSpec);
+ } else {
+ return false;
+ }
+ }
+
+ private void initHash() {
+ if (hash == 0) {
+ this.hash = getPersistentHashFunction()
+ .newHasher()
+ .putObject(host, HostFunnel.INSTANCE)
+ .putObject(vmSpec, VmSpecFunnel.INSTANCE)
+ .putObject(benchmarkSpec, BenchmarkSpecFunnel.INSTANCE)
+ .hash().asInt();
+ }
+ }
+
+ @Override public int hashCode() {
+ initHash();
+ return hash;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("environment", host)
+ .add("vmSpec", vmSpec)
+ .add("benchmarkSpec", benchmarkSpec)
+ .toString();
+ }
+
+ public static final class Builder {
+ private Host host;
+ private VmSpec vmSpec;
+ private BenchmarkSpec benchmarkSpec;
+
+ public Builder host(Host.Builder hostBuilder) {
+ return host(hostBuilder.build());
+ }
+
+ public Builder host(Host host) {
+ this.host = checkNotNull(host);
+ return this;
+ }
+
+ public Builder vmSpec(VmSpec.Builder vmSpecBuilder) {
+ return vmSpec(vmSpecBuilder.build());
+ }
+
+ public Builder vmSpec(VmSpec vmSpec) {
+ this.vmSpec = checkNotNull(vmSpec);
+ return this;
+ }
+
+ public Builder benchmarkSpec(BenchmarkSpec.Builder benchmarkSpecBuilder) {
+ return benchmarkSpec(benchmarkSpecBuilder.build());
+ }
+
+ public Builder benchmarkSpec(BenchmarkSpec benchmarkSpec) {
+ this.benchmarkSpec = checkNotNull(benchmarkSpec);
+ return this;
+ }
+
+ public Scenario build() {
+ checkState(host != null);
+ checkState(vmSpec != null);
+ checkState(benchmarkSpec != null);
+ return new Scenario(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java b/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java
new file mode 100644
index 0000000..ae3f43b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import com.google.common.hash.Funnel;
+import com.google.common.hash.PrimitiveSink;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * A simple funnel that inserts string map entries into a funnel in iteration order.
+ */
+enum StringMapFunnel implements Funnel<Map<String, String>> {
+ INSTANCE;
+
+ @Override
+ public void funnel(Map<String, String> from, PrimitiveSink into) {
+ for (Entry<String, String> entry : from.entrySet()) {
+ into.putUnencodedChars(entry.getKey())
+ .putByte((byte) -1) // separate key and value
+ .putUnencodedChars(entry.getValue());
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/Trial.java b/caliper/src/main/java/com/google/caliper/model/Trial.java
new file mode 100644
index 0000000..08334cf
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Trial.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * An invocation of a single scenario measured with a single instrument and the results thereof.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class Trial { // used to be Result
+ public static final Trial DEFAULT = new Trial();
+
+ private UUID id;
+ private Run run;
+ private InstrumentSpec instrumentSpec;
+ private Scenario scenario;
+ private List<Measurement> measurements;
+
+ private Trial() {
+ this.id = Defaults.UUID;
+ this.run = Run.DEFAULT;
+ this.instrumentSpec = InstrumentSpec.DEFAULT;
+ this.scenario = Scenario.DEFAULT;
+ this.measurements = Lists.newArrayList();
+ }
+
+ private Trial(Builder builder) {
+ this.id = builder.id;
+ this.run = builder.run;
+ this.instrumentSpec = builder.instrumentSpec;
+ this.scenario = builder.scenario;
+ this.measurements = Lists.newArrayList(builder.measurements);
+ }
+
+ public UUID id() {
+ return id;
+ }
+
+ public Run run() {
+ return run;
+ }
+
+ public InstrumentSpec instrumentSpec() {
+ return instrumentSpec;
+ }
+
+ public Scenario scenario() {
+ return scenario;
+ }
+
+ public ImmutableList<Measurement> measurements() {
+ return ImmutableList.copyOf(measurements);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Trial) {
+ Trial that = (Trial) obj;
+ return this.id.equals(that.id)
+ && this.run.equals(that.run)
+ && this.instrumentSpec.equals(that.instrumentSpec)
+ && this.scenario.equals(that.scenario)
+ && this.measurements.equals(that.measurements);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(id, run, instrumentSpec, scenario, measurements);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", id)
+ .add("run", run)
+ .add("instrumentSpec", instrumentSpec)
+ .add("scenario", scenario)
+ .add("measurements", measurements)
+ .toString();
+ }
+
+ public static final class Builder {
+ private final UUID id;
+ private Run run;
+ private InstrumentSpec instrumentSpec;
+ private Scenario scenario;
+ private final List<Measurement> measurements = Lists.newArrayList();
+
+ public Builder(UUID id) {
+ this.id = checkNotNull(id);
+ }
+
+ public Builder run(Run.Builder runBuilder) {
+ return run(runBuilder.build());
+ }
+
+ public Builder run(Run run) {
+ this.run = checkNotNull(run);
+ return this;
+ }
+
+ public Builder instrumentSpec(InstrumentSpec.Builder instrumentSpecBuilder) {
+ return instrumentSpec(instrumentSpecBuilder.build());
+ }
+
+ public Builder instrumentSpec(InstrumentSpec instrumentSpec) {
+ this.instrumentSpec = checkNotNull(instrumentSpec);
+ return this;
+ }
+
+ public Builder scenario(Scenario.Builder scenarioBuilder) {
+ return scenario(scenarioBuilder.build());
+ }
+
+ public Builder scenario(Scenario scenario) {
+ this.scenario = checkNotNull(scenario);
+ return this;
+ }
+
+ public Builder addMeasurement(Measurement.Builder measurementBuilder) {
+ return addMeasurement(measurementBuilder.build());
+ }
+
+ public Builder addMeasurement(Measurement measurement) {
+ this.measurements.add(measurement);
+ return this;
+ }
+
+ public Builder addAllMeasurements(Iterable<Measurement> measurements) {
+ Iterables.addAll(this.measurements, measurements);
+ return this;
+ }
+
+ public Trial build() {
+ checkState(run != null);
+ checkState(instrumentSpec != null);
+ checkState(scenario != null);
+ return new Trial(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/Value.java b/caliper/src/main/java/com/google/caliper/model/Value.java
new file mode 100644
index 0000000..b0daee9
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/Value.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+
+import java.io.Serializable;
+
+/**
+ * A magnitude with units.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public class Value implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ static final Value DEFAULT = new Value();
+
+ public static Value create(double value, String unit) {
+ return new Value(value, checkNotNull(unit));
+ }
+
+ private double magnitude;
+ // TODO(gak): do something smarter than string for units
+ // TODO(gak): give guidelines for how to specify units. E.g. s or seconds
+ private String unit;
+
+ private Value() {
+ this.magnitude = 0.0;
+ this.unit = "";
+ }
+
+ private Value(double value, String unit) {
+ this.magnitude = value;
+ this.unit = unit;
+ }
+
+ public String unit() {
+ return unit;
+ }
+
+ public double magnitude() {
+ return magnitude;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Value) {
+ Value that = (Value) obj;
+ return this.magnitude == that.magnitude
+ && this.unit.equals(that.unit);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(magnitude, unit);
+ }
+
+ @Override public String toString() {
+ return new StringBuilder().append(magnitude).append(unit).toString();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/VmSpec.java b/caliper/src/main/java/com/google/caliper/model/VmSpec.java
new file mode 100644
index 0000000..d4b835b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/VmSpec.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.model;
+
+import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Maps;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.PrimitiveSink;
+
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * A configuration of a virtual machine.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class VmSpec {
+ static final VmSpec DEFAULT = new VmSpec();
+
+ @ExcludeFromJson private int id;
+ private SortedMap<String, String> properties;
+ private SortedMap<String, String> options;
+ @ExcludeFromJson private int hash;
+
+ private VmSpec() {
+ this.properties = Maps.newTreeMap();
+ this.options = Maps.newTreeMap();
+ }
+
+ private VmSpec(Builder builder) {
+ this.properties = Maps.newTreeMap(builder.properties);
+ this.options = Maps.newTreeMap(builder.options);
+ }
+
+ public ImmutableSortedMap<String, String> options() {
+ return ImmutableSortedMap.copyOf(options);
+ }
+
+ public ImmutableSortedMap<String, String> properties() {
+ return ImmutableSortedMap.copyOf(properties);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof VmSpec) {
+ VmSpec that = (VmSpec) obj;
+ return this.properties.equals(that.properties)
+ && this.options.equals(that.options);
+ } else {
+ return false;
+ }
+ }
+
+ private void initHash() {
+ if (hash == 0) {
+ this.hash = getPersistentHashFunction().hashObject(this, VmSpecFunnel.INSTANCE).asInt();
+ }
+ }
+
+ @Override public int hashCode() {
+ initHash();
+ return hash;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("properties", properties)
+ .add("options", options)
+ .toString();
+ }
+
+ enum VmSpecFunnel implements Funnel<VmSpec> {
+ INSTANCE;
+
+ @Override public void funnel(VmSpec from, PrimitiveSink into) {
+ StringMapFunnel.INSTANCE.funnel(from.properties, into);
+ StringMapFunnel.INSTANCE.funnel(from.options, into);
+ }
+ }
+
+ public static final class Builder {
+ private final SortedMap<String, String> properties = Maps.newTreeMap();
+ private final SortedMap<String, String> options = Maps.newTreeMap();
+
+ public Builder addOption(String optionName, String value) {
+ this.options.put(optionName, value);
+ return this;
+ }
+
+ public Builder addAllOptions(Map<String, String> options) {
+ this.options.putAll(options);
+ return this;
+ }
+
+ public Builder addProperty(String property, String value) {
+ this.properties.put(property, value);
+ return this;
+ }
+
+ public Builder addAllProperties(Map<String, String> properties) {
+ this.properties.putAll(properties);
+ return this;
+ }
+
+ public VmSpec build() {
+ return new VmSpec(this);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/model/package-info.java b/caliper/src/main/java/com/google/caliper/model/package-info.java
new file mode 100644
index 0000000..882ca26
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/model/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * These classes model the data that is collected by the caliper {@linkplain
+ * com.google.caliper.runner runner}: the record of which scenarios were tested on which VMs by
+ * which instruments and, most importantly, all the measurements that were observed.
+ *
+ * <p>The primary goal of these classes is to be as easily convertible back and forth to JSON text
+ * as possible. The secondary goal is to be easily persistable in a relational database.
+ */
+package com.google.caliper.model; \ No newline at end of file
diff --git a/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java b/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java
new file mode 100644
index 0000000..30f8d92
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.options;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the directory that caliper should use to store data (--directory). */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+public @interface CaliperDirectory {}
diff --git a/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java b/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java
new file mode 100644
index 0000000..86b96e7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.options;
+
+import com.google.caliper.util.ShortDuration;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+
+import java.io.File;
+
+public interface CaliperOptions {
+ String benchmarkClassName();
+ ImmutableSet<String> benchmarkMethodNames();
+ ImmutableSet<String> vmNames();
+ ImmutableSetMultimap<String, String> userParameters();
+ ImmutableSetMultimap<String, String> vmArguments();
+ ImmutableMap<String, String> configProperties();
+ ImmutableSet<String> instrumentNames();
+ int trialsPerScenario();
+ ShortDuration timeLimit();
+ String runName();
+ boolean printConfiguration();
+ boolean dryRun();
+ File caliperDirectory();
+ File caliperConfigFile();
+}
diff --git a/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java b/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java
new file mode 100644
index 0000000..5e67904
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.options;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.caliper.util.DisplayUsageException;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.Parser;
+import com.google.caliper.util.Parsers;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Primitives;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.List;
+
+// based on r135 of OptionParser.java from vogar
+// NOTE: this class is still pretty messy but will be cleaned up further and possibly offered to
+// Guava.
+
+/**
+ * Parses command line options.
+ *
+ * Strings in the passed-in String[] are parsed left-to-right. Each String is classified as a short
+ * option (such as "-v"), a long option (such as "--verbose"), an argument to an option (such as
+ * "out.txt" in "-f out.txt"), or a non-option positional argument.
+ *
+ * A simple short option is a "-" followed by a short option character. If the option requires an
+ * argument (which is true of any non-boolean option), it may be written as a separate parameter,
+ * but need not be. That is, "-f out.txt" and "-fout.txt" are both acceptable.
+ *
+ * It is possible to specify multiple short options after a single "-" as long as all (except
+ * possibly the last) do not require arguments.
+ *
+ * A long option begins with "--" followed by several characters. If the option requires an
+ * argument, it may be written directly after the option name, separated by "=", or as the next
+ * argument. (That is, "--file=out.txt" or "--file out.txt".)
+ *
+ * A boolean long option '--name' automatically gets a '--no-name' companion. Given an option
+ * "--flag", then, "--flag", "--no-flag", "--flag=true" and "--flag=false" are all valid, though
+ * neither "--flag true" nor "--flag false" are allowed (since "--flag" by itself is sufficient, the
+ * following "true" or "false" is interpreted separately). You can use "yes" and "no" as synonyms
+ * for "true" and "false".
+ *
+ * Each String not starting with a "-" and not a required argument of a previous option is a
+ * non-option positional argument, as are all successive Strings. Each String after a "--" is a
+ * non-option positional argument.
+ *
+ * The fields corresponding to options are updated as their options are processed. Any remaining
+ * positional arguments are returned as an ImmutableList<String>.
+ *
+ * Here's a simple example:
+ *
+ * // This doesn't need to be a separate class, if your application doesn't warrant it. //
+ * Non-@Option fields will be ignored. class Options {
+ *
+ * @Option(names = { "-q", "--quiet" }) boolean quiet = false;
+ *
+ * // Boolean options require a long name if it's to be possible to explicitly turn them off. //
+ * Here the user can use --no-color.
+ * @Option(names = { "--color" }) boolean color = true;
+ * @Option(names = { "-m", "--mode" }) String mode = "standard; // Supply a default just by setting
+ * the field.
+ * @Option(names = { "-p", "--port" }) int portNumber = 8888;
+ *
+ * // There's no need to offer a short name for rarely-used options.
+ * @Option(names = { "--timeout" }) double timeout = 1.0;
+ * @Option(names = { "-o", "--output-file" }) String outputFile;
+ *
+ * }
+ *
+ * See also:
+ *
+ * the getopt(1) man page Python's "optparse" module (http://docs.python.org/library/optparse.html)
+ * the POSIX "Utility Syntax Guidelines" (http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap12.html#tag_12_02)
+ * the GNU "Standards for Command Line Interfaces" (http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces)
+ */
+final class CommandLineParser<T> {
+ /**
+ * Annotates a field or method in an options class to signify that parsed values should be
+ * injected.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.METHOD})
+ public @interface Option {
+ /**
+ * The names for this option, such as { "-h", "--help" }. Names must start with one or two '-'s.
+ * An option must have at least one name.
+ */
+ String[] value();
+ }
+
+ /**
+ * Annotates a single method in an options class to receive any "leftover" arguments. The method
+ * must accept {@code ImmutableList<String>} or a supertype. The method will be invoked even if
+ * the list is empty.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.METHOD})
+ public @interface Leftovers {}
+
+ public static <T> CommandLineParser<T> forClass(Class<? extends T> c) {
+ return new CommandLineParser<T>(c);
+ }
+
+ private final InjectionMap injectionMap;
+ private T injectee;
+
+ // TODO(kevinb): make a helper object that can be mutated during processing
+ private final List<PendingInjection> pendingInjections = Lists.newArrayList();
+
+ /**
+ * Constructs a new command-line parser that will inject values into {@code injectee}.
+ *
+ * @throws IllegalArgumentException if {@code injectee} contains multiple options using the same
+ * name
+ */
+ private CommandLineParser(Class<? extends T> c) {
+ this.injectionMap = InjectionMap.forClass(c);
+ }
+
+ /**
+ * Parses the command-line arguments 'args', setting the @Option fields of the 'optionSource'
+ * provided to the constructor. Returns a list of the positional arguments left over after
+ * processing all options.
+ */
+ public void parseAndInject(String[] args, T injectee) throws InvalidCommandException {
+ this.injectee = injectee;
+ pendingInjections.clear();
+ Iterator<String> argsIter = Iterators.forArray(args);
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+
+ while (argsIter.hasNext()) {
+ String arg = argsIter.next();
+ if (arg.equals("--")) {
+ break; // "--" marks the end of options and the beginning of positional arguments.
+ } else if (arg.startsWith("--")) {
+ parseLongOption(arg, argsIter);
+ } else if (arg.startsWith("-")) {
+ parseShortOptions(arg, argsIter);
+ } else {
+ builder.add(arg);
+ // allow positional arguments to mix with options since many linux commands do
+ }
+ }
+
+ for (PendingInjection pi : pendingInjections) {
+ pi.injectableOption.inject(pi.value, injectee);
+ }
+
+ ImmutableList<String> leftovers = builder.addAll(argsIter).build();
+ invokeMethod(injectee, injectionMap.leftoversMethod, leftovers);
+ }
+
+ // Private stuff from here on down
+
+ private abstract static class InjectableOption {
+ abstract boolean isBoolean();
+ abstract void inject(String valueText, Object injectee) throws InvalidCommandException;
+ boolean delayedInjection() {
+ return false;
+ }
+ }
+
+ private static class InjectionMap {
+ public static InjectionMap forClass(Class<?> injectedClass) {
+ ImmutableMap.Builder<String, InjectableOption> builder = ImmutableMap.builder();
+
+ InjectableOption helpOption = new InjectableOption() {
+ @Override boolean isBoolean() {
+ return true;
+ }
+ @Override void inject(String valueText, Object injectee) throws DisplayUsageException {
+ throw new DisplayUsageException();
+ }
+ };
+ builder.put("-h", helpOption);
+ builder.put("--help", helpOption);
+
+ Method leftoverMethod = null;
+
+ for (Field field : injectedClass.getDeclaredFields()) {
+ checkArgument(!field.isAnnotationPresent(Leftovers.class),
+ "Sorry, @Leftovers only works for methods at present"); // TODO(kevinb)
+ Option option = field.getAnnotation(Option.class);
+ if (option != null) {
+ InjectableOption injectable = FieldOption.create(field);
+ for (String optionName : option.value()) {
+ builder.put(optionName, injectable);
+ }
+ }
+ }
+ for (Method method : injectedClass.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(Leftovers.class)) {
+ checkArgument(!isStaticOrAbstract(method),
+ "@Leftovers method cannot be static or abstract");
+ checkArgument(!method.isAnnotationPresent(Option.class),
+ "method has both @Option and @Leftovers");
+ checkArgument(leftoverMethod == null, "Two methods have @Leftovers");
+
+ method.setAccessible(true);
+ leftoverMethod = method;
+
+ // TODO: check type is a supertype of ImmutableList<String>
+ }
+ Option option = method.getAnnotation(Option.class);
+ if (option != null) {
+ InjectableOption injectable = MethodOption.create(method);
+ for (String optionName : option.value()) {
+ builder.put(optionName, injectable);
+ }
+ }
+ }
+
+ ImmutableMap<String, InjectableOption> optionMap = builder.build();
+ return new InjectionMap(optionMap, leftoverMethod);
+ }
+
+ final ImmutableMap<String, InjectableOption> optionMap;
+ final Method leftoversMethod;
+
+ InjectionMap(ImmutableMap<String, InjectableOption> optionMap, Method leftoversMethod) {
+ this.optionMap = optionMap;
+ this.leftoversMethod = leftoversMethod;
+ }
+
+ InjectableOption getInjectableOption(String optionName) throws InvalidCommandException {
+ InjectableOption injectable = optionMap.get(optionName);
+ if (injectable == null) {
+ throw new InvalidCommandException("Invalid option: %s", optionName);
+ }
+ return injectable;
+ }
+ }
+
+ private static class FieldOption extends InjectableOption {
+ private static InjectableOption create(Field field) {
+ field.setAccessible(true);
+ Type type = field.getGenericType();
+
+ if (type instanceof Class) {
+ return new FieldOption(field, (Class<?>) type);
+ }
+ throw new IllegalArgumentException("can't inject parameterized types etc.");
+ }
+
+ private Field field;
+ private boolean isBoolean;
+ private Parser<?> parser;
+
+ private FieldOption(Field field, Class<?> c) {
+ this.field = field;
+ this.isBoolean = c == boolean.class || c == Boolean.class;
+ try {
+ this.parser = Parsers.conventionalParser(Primitives.wrap(c));
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("No suitable String-conversion method");
+ }
+ }
+
+ @Override boolean isBoolean() {
+ return isBoolean;
+ }
+
+ @Override void inject(String valueText, Object injectee) throws InvalidCommandException {
+ Object value = convert(parser, valueText);
+ try {
+ field.set(injectee, value);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ }
+ }
+ }
+
+ private static class MethodOption extends InjectableOption {
+ private static InjectableOption create(Method method) {
+ checkArgument(!isStaticOrAbstract(method),
+ "@Option methods cannot be static or abstract");
+ Class<?>[] classes = method.getParameterTypes();
+ checkArgument(classes.length == 1, "Method does not have exactly one argument: " + method);
+ return new MethodOption(method, classes[0]);
+ }
+
+ private Method method;
+ private boolean isBoolean;
+ private Parser<?> parser;
+
+ private MethodOption(Method method, Class<?> c) {
+ this.method = method;
+ this.isBoolean = c == boolean.class || c == Boolean.class;
+ try {
+ this.parser = Parsers.conventionalParser(Primitives.wrap(c));
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("No suitable String-conversion method");
+ }
+
+ method.setAccessible(true);
+ }
+
+ @Override boolean isBoolean() {
+ return isBoolean;
+ }
+
+ @Override boolean delayedInjection() {
+ return true;
+ }
+
+ @Override void inject(String valueText, Object injectee) throws InvalidCommandException {
+ invokeMethod(injectee, method, convert(parser, valueText));
+ }
+ }
+
+ private static Object convert(Parser<?> parser, String valueText) throws InvalidCommandException {
+ Object value;
+ try {
+ value = parser.parse(valueText);
+ } catch (ParseException e) {
+ throw new InvalidCommandException("wrong datatype: " + e.getMessage());
+ }
+ return value;
+ }
+
+ private void parseLongOption(String arg, Iterator<String> args) throws InvalidCommandException {
+ String name = arg.replaceFirst("^--no-", "--");
+ String value = null;
+
+ // Support "--name=value" as well as "--name value".
+ int equalsIndex = name.indexOf('=');
+ if (equalsIndex != -1) {
+ value = name.substring(equalsIndex + 1);
+ name = name.substring(0, equalsIndex);
+ }
+
+ InjectableOption injectable = injectionMap.getInjectableOption(name);
+
+ if (value == null) {
+ value = injectable.isBoolean()
+ ? Boolean.toString(!arg.startsWith("--no-"))
+ : grabNextValue(args, name);
+ }
+ injectNowOrLater(injectable, value);
+ }
+
+ private void injectNowOrLater(InjectableOption injectable, String value)
+ throws InvalidCommandException {
+ if (injectable.delayedInjection()) {
+ pendingInjections.add(new PendingInjection(injectable, value));
+ } else {
+ injectable.inject(value, injectee);
+ }
+ }
+
+ private static class PendingInjection {
+ InjectableOption injectableOption;
+ String value;
+
+ private PendingInjection(InjectableOption injectableOption, String value) {
+ this.injectableOption = injectableOption;
+ this.value = value;
+ }
+ }
+
+ // Given boolean options a and b, and non-boolean option f, we want to allow:
+ // -ab
+ // -abf out.txt
+ // -abfout.txt
+ // (But not -abf=out.txt --- POSIX doesn't mention that either way, but GNU expressly forbids it.)
+
+ private void parseShortOptions(String arg, Iterator<String> args) throws InvalidCommandException {
+ for (int i = 1; i < arg.length(); ++i) {
+ String name = "-" + arg.charAt(i);
+ InjectableOption injectable = injectionMap.getInjectableOption(name);
+
+ String value;
+ if (injectable.isBoolean()) {
+ value = "true";
+ } else {
+ // We need a value. If there's anything left, we take the rest of this "short option".
+ if (i + 1 < arg.length()) {
+ value = arg.substring(i + 1);
+ i = arg.length() - 1; // delayed "break"
+
+ // otherwise the next arg
+ } else {
+ value = grabNextValue(args, name);
+ }
+ }
+ injectNowOrLater(injectable, value);
+ }
+ }
+
+ private static void invokeMethod(Object injectee, Method method, Object value)
+ throws InvalidCommandException {
+ try {
+ method.invoke(injectee, value);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ Throwables.propagateIfPossible(cause, InvalidCommandException.class);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String grabNextValue(Iterator<String> args, String name)
+ throws InvalidCommandException {
+ if (args.hasNext()) {
+ return args.next();
+ } else {
+ throw new InvalidCommandException("option '" + name + "' requires an argument");
+ }
+ }
+
+ private static boolean isStaticOrAbstract(Method method) {
+ int modifiers = method.getModifiers();
+ return Modifier.isStatic(modifiers) || Modifier.isAbstract(modifiers);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/options/OptionsModule.java b/caliper/src/main/java/com/google/caliper/options/OptionsModule.java
new file mode 100644
index 0000000..9647475
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/OptionsModule.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.options;
+
+import com.google.caliper.util.InvalidCommandException;
+
+import dagger.Module;
+import dagger.Provides;
+
+import java.io.File;
+
+import javax.inject.Singleton;
+
+/**
+ * Bindings for Caliper command line options.
+ */
+@Module
+public final class OptionsModule {
+
+ private static final String[] EMPTY_ARGS = new String[] {};
+
+ private final String[] args;
+
+ private boolean requireBenchmarkClassName;
+
+ /**
+ * Return a module that will provide access to configuration options and the name of the
+ * benchmark class.
+ *
+ * @param args the arguments from which the configuration options and the benchmark class name
+ * are parsed; must have one non-option value that is the benchmark class name.
+ */
+ public static OptionsModule withBenchmarkClass(String [] args) {
+ return new OptionsModule(args, true);
+ }
+
+ /**
+ * Return a module that will provide access to configuration options without the name of the
+ * benchmark class.
+ *
+ * @param args the arguments from which the configuration options are parsed; it must have no
+ * non-option values.
+ */
+ public static OptionsModule withoutBenchmarkClass(String [] args) {
+ return new OptionsModule(args, false);
+ }
+
+ /**
+ * Return a module that will provide access to the default configuration options.
+ */
+ public static OptionsModule defaultOptionsModule() {
+ return new OptionsModule(EMPTY_ARGS, false);
+ }
+
+ public OptionsModule(String[] args, boolean requireBenchmarkClassName) {
+ this.args = args.clone(); // defensive copy, just in case
+ this.requireBenchmarkClassName = requireBenchmarkClassName;
+ }
+
+ @Provides
+ @Singleton
+ CaliperOptions provideOptions() throws InvalidCommandException {
+ return ParsedOptions.from(args, requireBenchmarkClassName);
+ }
+
+ @Provides @CaliperDirectory static File provideCaliperDirectory(CaliperOptions options) {
+ return options.caliperDirectory();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java b/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java
new file mode 100644
index 0000000..43dd8f7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.caliper.options.CommandLineParser.Leftovers;
+import com.google.caliper.options.CommandLineParser.Option;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.ShortDuration;
+import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+final class ParsedOptions implements CaliperOptions {
+
+ public static ParsedOptions from(String[] args, boolean requireBenchmarkClassName)
+ throws InvalidCommandException {
+ ParsedOptions options = new ParsedOptions(requireBenchmarkClassName);
+
+ CommandLineParser<ParsedOptions> parser = CommandLineParser.forClass(ParsedOptions.class);
+ try {
+ parser.parseAndInject(args, options);
+ } catch (InvalidCommandException e) {
+ e.setUsage(USAGE);
+ throw e;
+ }
+ return options;
+ }
+
+ /**
+ * True if the benchmark class name is expected as the last argument, false if it is not allowed.
+ */
+ private final boolean requireBenchmarkClassName;
+
+ private ParsedOptions(boolean requireBenchmarkClassName) {
+ this.requireBenchmarkClassName = requireBenchmarkClassName;
+ }
+
+ // --------------------------------------------------------------------------
+ // Dry run -- simple boolean, needs to be checked in some methods
+ // --------------------------------------------------------------------------
+
+ @Option({"-n", "--dry-run"})
+ private boolean dryRun;
+
+ @Override public boolean dryRun() {
+ return dryRun;
+ }
+
+ private void dryRunIncompatible(String optionName) throws InvalidCommandException {
+ // This only works because CLP does field injection before method injection
+ if (dryRun) {
+ throw new InvalidCommandException("Option not available in dry-run mode: " + optionName);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Delimiter -- injected early so methods can use it
+ // --------------------------------------------------------------------------
+
+ @Option({"-d", "--delimiter"})
+ private String delimiter = ",";
+
+ private ImmutableSet<String> split(String string) {
+ return ImmutableSet.copyOf(Splitter.on(delimiter).split(string));
+ }
+
+ // --------------------------------------------------------------------------
+ // Benchmark method names to run
+ // --------------------------------------------------------------------------
+
+ private ImmutableSet<String> benchmarkNames = ImmutableSet.of();
+
+ @Option({"-b", "--benchmark"})
+ private void setBenchmarkNames(String benchmarksString) {
+ benchmarkNames = split(benchmarksString);
+ }
+
+ @Override public ImmutableSet<String> benchmarkMethodNames() {
+ return benchmarkNames;
+ }
+
+ // --------------------------------------------------------------------------
+ // Print configuration?
+ // --------------------------------------------------------------------------
+
+ @Option({"-p", "--print-config"})
+ private boolean printConfiguration = false;
+
+ @Override public boolean printConfiguration() {
+ return printConfiguration;
+ }
+
+ // --------------------------------------------------------------------------
+ // Trials
+ // --------------------------------------------------------------------------
+
+ private int trials = 1;
+
+ @Option({"-t", "--trials"})
+ private void setTrials(int trials) throws InvalidCommandException {
+ dryRunIncompatible("trials");
+ if (trials < 1) {
+ throw new InvalidCommandException("trials must be at least 1: " + trials);
+ }
+ this.trials = trials;
+ }
+
+ @Override public int trialsPerScenario() {
+ return trials;
+ }
+
+ // --------------------------------------------------------------------------
+ // Time limit
+ // --------------------------------------------------------------------------
+
+ private ShortDuration runTime = ShortDuration.of(5, MINUTES);
+
+ @Option({"-l", "--time-limit"})
+ private void setTimeLimit(String timeLimitString) throws InvalidCommandException {
+ try {
+ this.runTime = ShortDuration.valueOf(timeLimitString);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidCommandException("Invalid time limit: " + timeLimitString);
+ }
+ }
+
+ @Override public ShortDuration timeLimit() {
+ return runTime;
+ }
+
+ // --------------------------------------------------------------------------
+ // Run name
+ // --------------------------------------------------------------------------
+
+ private String runName = "";
+
+ @Option({"-r", "--run-name"})
+ private void setRunName(String runName) {
+ this.runName = checkNotNull(runName);
+ }
+
+ @Override public String runName() {
+ return runName;
+ }
+
+ // --------------------------------------------------------------------------
+ // VM specifications
+ // --------------------------------------------------------------------------
+
+ private ImmutableSet<String> vmNames = ImmutableSet.of();
+
+ @Option({"-m", "--vm"})
+ private void setVms(String vmsString) throws InvalidCommandException {
+ dryRunIncompatible("vm");
+ vmNames = split(vmsString);
+ }
+
+ @Override public ImmutableSet<String> vmNames() {
+ return vmNames;
+ }
+
+ // --------------------------------------------------------------------------
+ // Measuring instruments to use
+ // --------------------------------------------------------------------------
+
+ private static final ImmutableSet<String> DEFAULT_INSTRUMENT_NAMES =
+ new ImmutableSet.Builder<String>()
+ .add("allocation")
+ .add("runtime")
+ .build();
+
+ private ImmutableSet<String> instrumentNames = DEFAULT_INSTRUMENT_NAMES;
+
+ @Option({"-i", "--instrument"})
+ private void setInstruments(String instrumentsString) {
+ instrumentNames = split(instrumentsString);
+ }
+
+ @Override public ImmutableSet<String> instrumentNames() {
+ return instrumentNames;
+ }
+
+// --------------------------------------------------------------------------
+ // Benchmark parameters
+ // --------------------------------------------------------------------------
+
+ private Multimap<String, String> mutableUserParameters = ArrayListMultimap.create();
+
+ @Option("-D")
+ private void addParameterSpec(String nameAndValues) throws InvalidCommandException {
+ addToMultimap(nameAndValues, mutableUserParameters);
+ }
+
+ @Override public ImmutableSetMultimap<String, String> userParameters() {
+ // de-dup values, but keep in order
+ return new ImmutableSetMultimap.Builder<String, String>()
+ .orderKeysBy(Ordering.natural())
+ .putAll(mutableUserParameters)
+ .build();
+ }
+
+ // --------------------------------------------------------------------------
+ // VM arguments
+ // --------------------------------------------------------------------------
+
+ private Multimap<String, String> mutableVmArguments = ArrayListMultimap.create();
+
+ @Option("-J")
+ private void addVmArgumentsSpec(String nameAndValues) throws InvalidCommandException {
+ dryRunIncompatible("-J");
+ addToMultimap(nameAndValues, mutableVmArguments);
+ }
+
+ @Override public ImmutableSetMultimap<String, String> vmArguments() {
+ // de-dup values, but keep in order
+ return new ImmutableSetMultimap.Builder<String, String>()
+ .orderKeysBy(Ordering.natural())
+ .putAll(mutableVmArguments)
+ .build();
+ }
+
+ // --------------------------------------------------------------------------
+ // VM arguments
+ // --------------------------------------------------------------------------
+
+ private final Map<String, String> mutableConfigPropertes = Maps.newHashMap();
+
+ @Option("-C")
+ private void addConfigProperty(String nameAndValue) throws InvalidCommandException {
+ List<String> tokens = splitProperty(nameAndValue);
+ mutableConfigPropertes.put(tokens.get(0), tokens.get(1));
+ }
+
+ @Override public ImmutableMap<String, String> configProperties() {
+ return ImmutableMap.copyOf(mutableConfigPropertes);
+ }
+
+ // --------------------------------------------------------------------------
+ // Location of .caliper
+ // --------------------------------------------------------------------------
+
+ private File caliperDirectory = new File(System.getProperty("user.home"), ".caliper");
+
+ @Option({"--directory"})
+ private void setCaliperDirectory(String path) {
+ caliperDirectory = new File(path);
+ }
+
+ @Override public File caliperDirectory() {
+ return caliperDirectory;
+ }
+
+ // --------------------------------------------------------------------------
+ // Location of config.properties
+ // --------------------------------------------------------------------------
+
+ private Optional<File> caliperConfigFile = Optional.absent();
+
+ @Option({"-c", "--config"})
+ private void setCaliperConfigFile(String filename) {
+ caliperConfigFile = Optional.of(new File(filename));
+ }
+
+ @Override public File caliperConfigFile() {
+ return caliperConfigFile.or(new File(caliperDirectory, "config.properties"));
+ }
+
+
+ // --------------------------------------------------------------------------
+ // Leftover - benchmark class name
+ // --------------------------------------------------------------------------
+
+ private String benchmarkClassName;
+
+ @Leftovers
+ private void setLeftovers(ImmutableList<String> leftovers) throws InvalidCommandException {
+ if (requireBenchmarkClassName) {
+ if (leftovers.isEmpty()) {
+ throw new InvalidCommandException("No benchmark class specified");
+ }
+ if (leftovers.size() > 1) {
+ throw new InvalidCommandException("Extra stuff, expected only class name: " + leftovers);
+ }
+ this.benchmarkClassName = leftovers.get(0);
+ } else {
+ if (!leftovers.isEmpty()) {
+ throw new InvalidCommandException(
+ "Extra stuff, did not expect non-option arguments: " + leftovers);
+ }
+ }
+ }
+
+ @Override public String benchmarkClassName() {
+ return benchmarkClassName;
+ }
+
+ // --------------------------------------------------------------------------
+ // Helper methods
+ // --------------------------------------------------------------------------
+
+ private static List<String> splitProperty(String propertyString) throws InvalidCommandException {
+ List<String> tokens = ImmutableList.copyOf(Splitter.on('=').limit(2).split(propertyString));
+ if (tokens.size() != 2) {
+ throw new InvalidCommandException("no '=' found in: " + propertyString);
+ }
+ return tokens;
+ }
+
+ private void addToMultimap(String nameAndValues, Multimap<String, String> multimap)
+ throws InvalidCommandException {
+ List<String> tokens = splitProperty(nameAndValues);
+ String name = tokens.get(0);
+ String values = tokens.get(1);
+
+ if (multimap.containsKey(name)) {
+ throw new InvalidCommandException("multiple parameter sets for: " + name);
+ }
+ multimap.putAll(name, split(values));
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("benchmarkClassName", this.benchmarkClassName())
+ .add("benchmarkMethodNames", this.benchmarkMethodNames())
+ .add("benchmarkParameters", this.userParameters())
+ .add("dryRun", this.dryRun())
+ .add("instrumentNames", this.instrumentNames())
+ .add("vms", this.vmNames())
+ .add("vmArguments", this.vmArguments())
+ .add("trials", this.trialsPerScenario())
+ .add("printConfig", this.printConfiguration())
+ .add("delimiter", this.delimiter)
+ .add("caliperConfigFile", this.caliperConfigFile)
+ .toString();
+ }
+
+ // --------------------------------------------------------------------------
+ // Usage
+ // --------------------------------------------------------------------------
+
+ // TODO(kevinb): kinda nice if CommandLineParser could autogenerate most of this...
+ // TODO(kevinb): a test could actually check that we don't exceed 79 columns.
+ private static final ImmutableList<String> USAGE = ImmutableList.of(
+ "Usage:",
+ " java com.google.caliper.runner.CaliperMain <benchmark_class_name> [options...]",
+ "",
+ "Options:",
+ " -h, --help print this message",
+ " -n, --dry-run instead of measuring, execute a single rep for each scenario",
+ " in-process",
+ " -b, --benchmark comma-separated list of benchmark methods to run; 'foo' is",
+ " an alias for 'timeFoo' (default: all found in class)",
+ " -m, --vm comma-separated list of VMs to test on; possible values are",
+ " configured in Caliper's configuration file (default:",
+ " whichever VM caliper itself is running in, only)",
+ " -i, --instrument comma-separated list of measuring instruments to use; possible ",
+ " values are configured in Caliper's configuration file ",
+ " (default: \"" + Joiner.on(",").join(DEFAULT_INSTRUMENT_NAMES) + "\")",
+ " -t, --trials number of independent trials to peform per benchmark scenario; ",
+ " a positive integer (default: 1)",
+ " -l, --time-limit maximum length of time allowed for a single trial; use 0 to allow ",
+ " trials to run indefinitely. (default: 30s) ",
+ " -r, --run-name a user-friendly string used to identify the run",
+ " -p, --print-config print the effective configuration that will be used by Caliper",
+ " -d, --delimiter separator used in options that take multiple values (default: ',')",
+ " -c, --config location of Caliper's configuration file (default:",
+ " $HOME/.caliper/config.properties)",
+ " --directory location of Caliper's configuration and data directory ",
+ " (default: $HOME/.caliper)",
+ "",
+ " -Dparam=val1,val2,...",
+ " Specifies the values to inject into the 'param' field of the benchmark",
+ " class; if multiple values or parameters are specified in this way, caliper",
+ " will try all possible combinations.",
+ "",
+ // commented out until this flag is fixed
+ // " -JdisplayName='vm arg list choice 1,vm arg list choice 2,...'",
+ // " Specifies alternate sets of VM arguments to pass. As with any variable,",
+ // " caliper will test all possible combinations. Example:",
+ // " -Jmemory='-Xms32m -Xmx32m,-Xms512m -Xmx512m'",
+ // "",
+ " -CconfigProperty=value",
+ " Specifies a value for any property that could otherwise be specified in ",
+ " $HOME/.caliper/config.properties. Properties specified on the command line",
+ " will override those specified in the file.",
+ "",
+ "See http://code.google.com/p/caliper/wiki/CommandLineOptions for more details.",
+ "");
+}
diff --git a/caliper/src/main/java/com/google/caliper/options/package-info.java b/caliper/src/main/java/com/google/caliper/options/package-info.java
new file mode 100644
index 0000000..b0bba1f
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/options/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * Contains code relating to parsing and managing command line options. This is distinct from
+ * configuration ({@code ~/.caliper/config.properties}), which lives in the
+ * {@link com.google.caliper.config} package.
+ */
+package com.google.caliper.options;
diff --git a/caliper/src/main/java/com/google/caliper/platform/Platform.java b/caliper/src/main/java/com/google/caliper/platform/Platform.java
new file mode 100644
index 0000000..44b6cd2
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/Platform.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * An abstraction of the platform within which caliper (both the scheduler and the actual workers)
+ * will run.
+ */
+public abstract class Platform {
+
+ private final Platform.Type platformType;
+
+ public Platform(Type platformType) {
+ this.platformType = checkNotNull(platformType);
+ }
+
+ /**
+ * Get the executable for the virtual machine for this platform.
+ *
+ * @param vmHome the home directory of the virtual machine, allows testing across multiple vms on
+ * the same platform in one go.
+ */
+ public abstract File vmExecutable(File vmHome);
+
+ /**
+ * Additional virtual machine arguments common to all instruments that are passed to a worker.
+ */
+ public abstract ImmutableSet<String> commonInstrumentVmArgs();
+
+ /**
+ * The name of the platform type.
+ */
+ public String name() {
+ return platformType.name;
+ }
+
+ /**
+ * Additional arguments that should be passed to a worker.
+ */
+ public abstract ImmutableSet<String> workerProcessArgs();
+
+ /**
+ * The class path that should be used to run a worker..
+ */
+ public abstract String workerClassPath();
+
+ /**
+ * Checks to see whether the specific class is supported on this platform.
+ *
+ * <p>This checks to see whether {@link SupportedPlatform} specifies a {@link Type} that
+ * matches this platform.
+ *
+ * @param clazz the class to check.
+ * @return true if it is supported, false otherwise.
+ */
+ public boolean supports(Class<?> clazz) {
+ SupportedPlatform annotation = clazz.getAnnotation(SupportedPlatform.class);
+ if (annotation == null) {
+ // Support must be explicitly declared.
+ return false;
+ }
+
+ Platform.Type[] types = annotation.value();
+ for (Type type : types) {
+ if (type.equals(platformType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the input arguments for the current running JVM.
+ */
+ public abstract Collection<String> inputArguments();
+
+ /**
+ * Selects the names of properties that will be used to populate the
+ * {@link com.google.caliper.model.VmSpec} for a specific run.
+ */
+ public abstract Predicate<String> vmPropertiesToRetain();
+
+ /**
+ * Checks that the vm options are appropriate for this platform, throws an exception if they are
+ * not.
+ */
+ public abstract void checkVmProperties(Map<String, String> options);
+
+ /**
+ * Get the default vm home directory.
+ */
+ public File defaultVmHomeDir() {
+ // Currently both supported platforms use java.home property to refer to the 'home' directory
+ // of the vm, in the case of Android it is the directory containing the dalvikvm executable.
+ return new File(System.getProperty("java.home"));
+ }
+
+ /**
+ * Get the home directory of a custom virtual machine.
+ * @param vmGroupMap the configuration properties for all VMs, may contain default properties that
+ * apply to all VMs.
+ * @param vmConfigName the name of the VM within the configuration, used to access VM specific
+ * properties from the {@code vmGroupMap}.
+ * @throws VirtualMachineException if there was a problem with the VM, either the configuration
+ * or the file system.
+ */
+ public abstract File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName)
+ throws VirtualMachineException;
+
+ /**
+ * The type of platforms supported.
+ */
+ public enum Type {
+ DALVIK("Dalvik"),
+ JVM("Java");
+
+ private final String name;
+
+ Type(String name) {
+ this.name = name;
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java b/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java
new file mode 100644
index 0000000..e3ac741
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates the platforms supported by the annotated type.
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface SupportedPlatform {
+ Platform.Type[] value();
+}
diff --git a/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java b/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java
new file mode 100644
index 0000000..b241212
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform;
+
+/**
+ * Thrown when a problem was found with a custom VM configuration.
+ */
+public class VirtualMachineException extends Exception {
+
+ public VirtualMachineException(String message) {
+ super(message);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java
new file mode 100644
index 0000000..45c5b00
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform.dalvik;
+
+import com.google.common.base.Optional;
+
+import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Singleton;
+
+/**
+ * Provider of a {@link DalvikPlatform} instance.
+ *
+ * <p>The {@link DalvikPlatform} is optional as it is only available when running on Android.
+ */
+@Module
+public final class DalvikModule {
+
+ @Provides
+ @Singleton
+ public static Optional<DalvikPlatform> provideOptionalPlatform() {
+ if (System.getProperty("java.specification.name").equals("Dalvik Core Library")) {
+ return Optional.of(new DalvikPlatform());
+ } else {
+ return Optional.absent();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java
new file mode 100644
index 0000000..9a971d3
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform.dalvik;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.platform.Platform;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An abstraction of the Dalvik (aka Android) platform.
+ *
+ * <p>Although this talks about dalvik it actually works with ART too.
+ */
+public final class DalvikPlatform extends Platform {
+
+ public DalvikPlatform() {
+ super(Type.DALVIK);
+ }
+
+ @Override
+ public File vmExecutable(File vmHome) {
+ // TODO(user): Allow the 32/64 version of dalvik to be selected rather than the default
+ // standard configurations of Android systems and windows.
+ File bin = new File(vmHome, "bin");
+ Preconditions.checkState(bin.exists() && bin.isDirectory(),
+ "Could not find %s under android root %s", bin, vmHome);
+ String executableName = "dalvikvm";
+ File dalvikvm = new File(bin, executableName);
+ if (!dalvikvm.exists() || dalvikvm.isDirectory()) {
+ throw new IllegalStateException(
+ String.format("Cannot find %s binary in %s", executableName, bin));
+ }
+
+ return dalvikvm;
+ }
+
+ @Override
+ public ImmutableSet<String> commonInstrumentVmArgs() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public ImmutableSet<String> workerProcessArgs() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public String workerClassPath() {
+ // TODO(user): Find a way to get the class path programmatically from the class loader.
+ return System.getProperty("java.class.path");
+ }
+
+ @Override
+ public Collection<String> inputArguments() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Predicate<String> vmPropertiesToRetain() {
+ return Predicates.alwaysFalse();
+ }
+
+ @Override
+ public void checkVmProperties(Map<String, String> options) {
+ checkState(options.isEmpty());
+ }
+
+ @Override
+ public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) {
+ // TODO(user): Should probably use this to support specifying dalvikvm32/dalvikvm64
+ // and maybe even app_process.
+ throw new UnsupportedOperationException(
+ "Running with a custom Dalvik VM is not currently supported");
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java b/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java
new file mode 100644
index 0000000..8963638
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform.jvm;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * Provides a class path containing all of the jars present on the local machine that are referenced
+ * by a given {@link ClassLoader}.
+ */
+final class EffectiveClassPath {
+ private EffectiveClassPath() {}
+
+ private static final String PATH_SEPARATOR = System.getProperty("path.separator");
+ private static final String JAVA_CLASS_PATH = System.getProperty("java.class.path");
+
+ static String getClassPathForClassLoader(ClassLoader classLoader) {
+ // Order of JAR files may have some significance. Try to preserve it.
+ LinkedHashSet<File> files = new LinkedHashSet<File>();
+ for (String entry : Splitter.on(PATH_SEPARATOR).split(JAVA_CLASS_PATH)) {
+ files.add(new File(entry));
+ }
+ files.addAll(getClassPathFiles(classLoader));
+
+ return Joiner.on(PATH_SEPARATOR).join(files);
+ }
+
+ private static Set<File> getClassPathFiles(ClassLoader classLoader) {
+ ImmutableSet.Builder<File> files = ImmutableSet.builder();
+ @Nullable ClassLoader parent = classLoader.getParent();
+ if (parent != null) {
+ files.addAll(getClassPathFiles(parent));
+ }
+ if (classLoader instanceof URLClassLoader) {
+ URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+ for (URL url : urlClassLoader.getURLs()) {
+ try {
+ files.add(new File(url.toURI()));
+ } catch (URISyntaxException e) {
+ // skip it then
+ } catch (IllegalArgumentException e) {
+ // skip it then
+ }
+ }
+ }
+ return files.build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/ConfigurationException.java b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmModule.java
index c4a35ec..6490374 100644
--- a/caliper/src/main/java/com/google/caliper/ConfigurationException.java
+++ b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,19 @@
* limitations under the License.
*/
-package com.google.caliper;
+package com.google.caliper.platform.jvm;
+
+import dagger.Module;
+import dagger.Provides;
/**
- * Thrown upon occurrence of a configuration error.
+ * Provider of the default {@link JvmPlatform}, this is assumed to always be available.
*/
-final class ConfigurationException extends RuntimeException {
-
- ConfigurationException(String s) {
- super(s);
- }
+@Module
+public final class JvmModule {
- ConfigurationException(Throwable cause) {
- super(cause);
+ @Provides
+ public static JvmPlatform provideJvmPlatform() {
+ return new JvmPlatform();
}
-
- private static final long serialVersionUID = 0;
}
diff --git a/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java
new file mode 100644
index 0000000..549cdd9
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.platform.jvm;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.Thread.currentThread;
+
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.VirtualMachineException;
+import com.google.caliper.util.Util;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * An abstraction of a standard Java Virtual Machine platform.
+ */
+public final class JvmPlatform extends Platform {
+
+ /**
+ * Some default JVM args to keep worker VMs somewhat predictable.
+ */
+ @VisibleForTesting
+ public static final ImmutableSet<String> INSTRUMENT_JVM_ARGS = ImmutableSet.of(
+ // do compilation serially
+ "-Xbatch",
+ // make sure compilation doesn't run in parallel with itself
+ "-XX:CICompilerCount=1",
+ // ensure the parallel garbage collector
+ "-XX:+UseParallelGC",
+ // generate classes or don't, but do it immediately
+ "-Dsun.reflect.inflationThreshold=0");
+
+ private static final ImmutableSet<String> WORKER_PROCESS_ARGS = ImmutableSet.of(
+ "-XX:+PrintFlagsFinal",
+ "-XX:+PrintCompilation",
+ "-XX:+PrintGC");
+
+
+ private static final Predicate<String> PROPERTIES_TO_RETAIN = new Predicate<String>() {
+ @Override public boolean apply(String input) {
+ return input.startsWith("java.vm")
+ || input.startsWith("java.runtime")
+ || input.equals("java.version")
+ || input.equals("java.vendor")
+ || input.equals("sun.reflect.noInflation")
+ || input.equals("sun.reflect.inflationThreshold");
+ }
+ };
+
+ public JvmPlatform() {
+ super(Type.JVM);
+ }
+
+ @Override
+ public File vmExecutable(File javaHome) {
+ // TODO(gak): support other platforms. This currently supports finding the java executable on
+ // standard configurations of unix systems and windows.
+ File bin = new File(javaHome, "bin");
+ Preconditions.checkState(bin.exists() && bin.isDirectory(),
+ "Could not find %s under java home %s", bin, javaHome);
+ File jvm = new File(bin, "java");
+ if (!jvm.exists() || jvm.isDirectory()) {
+ jvm = new File(bin, "java.exe");
+ if (!jvm.exists() || jvm.isDirectory()) {
+ throw new IllegalStateException(
+ String.format("Cannot find java binary in %s, looked for java and java.exe", bin));
+ }
+ }
+
+ return jvm;
+ }
+
+ @Override
+ public ImmutableSet<String> commonInstrumentVmArgs() {
+ return INSTRUMENT_JVM_ARGS;
+ }
+
+ @Override
+ public ImmutableSet<String> workerProcessArgs() {
+ return WORKER_PROCESS_ARGS;
+ }
+
+ @Override
+ public String workerClassPath() {
+ return getClassPath();
+ }
+
+ private static String getClassPath() {
+ // Use the effective class path in case this is being invoked in an isolated class loader
+ String classpath =
+ EffectiveClassPath.getClassPathForClassLoader(currentThread().getContextClassLoader());
+ return classpath;
+ }
+
+ @Override
+ public Collection<String> inputArguments() {
+ return Collections2.filter(ManagementFactory.getRuntimeMXBean().getInputArguments(),
+ new Predicate<String>() {
+ @Override
+ public boolean apply(String input) {
+ // Exclude the -agentlib:jdwp param which configures the socket debugging protocol.
+ // If this is set in the parent VM we do not want it to be inherited by the child
+ // VM. If it is, the child will die immediately on startup because it will fail to
+ // bind to the debug port (because the parent VM is already bound to it).
+ return !input.startsWith("-agentlib:jdwp");
+ }
+ });
+ }
+
+ @Override
+ public Predicate<String> vmPropertiesToRetain() {
+ return PROPERTIES_TO_RETAIN;
+ }
+
+ @Override
+ public void checkVmProperties(Map<String, String> options) {
+ checkState(!options.isEmpty());
+ }
+
+ @Override
+ public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName)
+ throws VirtualMachineException {
+ // Configuration can either be:
+ // vm.<vmConfigName>.home = <homeDir>
+ // or
+ // vm.baseDirectory = <baseDir>
+ // homeDir = <baseDir>/<vmConfigName>
+ ImmutableMap<String, String> vmMap = Util.subgroupMap(vmGroupMap, vmConfigName);
+ return getJdkHomeDir(vmGroupMap.get("baseDirectory"), vmMap.get("home"), vmConfigName);
+ }
+
+ // TODO(gak): check that the directory seems to be a jdk home (with a java binary and all of that)
+ // TODO(gak): make this work with different directory layouts. I'm looking at you OS X...
+ public static File getJdkHomeDir(@Nullable String baseDirectoryPath,
+ @Nullable String homeDirPath, String vmConfigName)
+ throws VirtualMachineException {
+ if (homeDirPath == null) {
+ File baseDirectory = getBaseDirectory(baseDirectoryPath);
+ File homeDir = new File(baseDirectory, vmConfigName);
+ checkConfiguration(homeDir.isDirectory(), "%s is not a directory", homeDir);
+ return homeDir;
+ } else {
+ File potentialHomeDir = new File(homeDirPath);
+ if (potentialHomeDir.isAbsolute()) {
+ checkConfiguration(potentialHomeDir.isDirectory(), "%s is not a directory",
+ potentialHomeDir);
+ return potentialHomeDir;
+ } else {
+ File baseDirectory = getBaseDirectory(baseDirectoryPath);
+ File homeDir = new File(baseDirectory, homeDirPath);
+ checkConfiguration(homeDir.isDirectory(), "%s is not a directory", potentialHomeDir);
+ return homeDir;
+ }
+ }
+ }
+
+ private static File getBaseDirectory(@Nullable String baseDirectoryPath)
+ throws VirtualMachineException {
+ if (baseDirectoryPath == null) {
+ throw new VirtualMachineException(
+ "must set either a home directory or a base directory");
+ } else {
+ File baseDirectory = new File(baseDirectoryPath);
+ checkConfiguration(baseDirectory.isAbsolute(), "base directory cannot be a relative path");
+ checkConfiguration(baseDirectory.isDirectory(), "base directory must be a directory");
+ return baseDirectory;
+ }
+ }
+
+ private static void checkConfiguration(boolean check, String message)
+ throws VirtualMachineException {
+ if (!check) {
+ throw new VirtualMachineException(message);
+ }
+ }
+
+ private static void checkConfiguration(boolean check, String messageFormat, Object... args)
+ throws VirtualMachineException {
+ if (!check) {
+ throw new VirtualMachineException(String.format(messageFormat, args));
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java b/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java
new file mode 100644
index 0000000..7469117
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagateIfInstanceOf;
+import static java.util.logging.Level.SEVERE;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.api.SkipThisScenarioException;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.SupportedPlatform;
+import com.google.caliper.worker.MacrobenchmarkAllocationWorker;
+import com.google.caliper.worker.MicrobenchmarkAllocationWorker;
+import com.google.caliper.worker.Worker;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.monitoring.runtime.instrumentation.AllocationInstrumenter;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.logging.Logger;
+
+/**
+ * {@link Instrument} that watches the memory allocations in an invocation of the
+ * benchmark method and reports some statistic. The benchmark method must accept a
+ * single int argument 'reps', which is the number of times to execute the guts of
+ * the benchmark method, and it must be public and non-static.
+ *
+ * <p>Note that the allocation instruments reports a "worst case" for allocation in that it reports
+ * the bytes and objects allocated in interpreted mode (no JIT).
+ */
+@SupportedPlatform(Platform.Type.JVM)
+public final class AllocationInstrument extends Instrument {
+ private static final String ALLOCATION_AGENT_JAR_OPTION = "allocationAgentJar";
+ /**
+ * If this option is set to {@code true} then every individual allocation will be tracked and
+ * logged. This will also increase the detail of certain error messages.
+ */
+ private static final String TRACK_ALLOCATIONS_OPTION = "trackAllocations";
+ private static final Logger logger = Logger.getLogger(AllocationInstrument.class.getName());
+
+ @Override
+ public boolean isBenchmarkMethod(Method method) {
+ return method.isAnnotationPresent(Benchmark.class) || BenchmarkMethods.isTimeMethod(method);
+ }
+
+ @Override
+ public Instrumentation createInstrumentation(Method benchmarkMethod)
+ throws InvalidBenchmarkException {
+ checkNotNull(benchmarkMethod);
+ checkArgument(isBenchmarkMethod(benchmarkMethod));
+ try {
+ switch (BenchmarkMethods.Type.of(benchmarkMethod)) {
+ case MACRO:
+ return new MacroAllocationInstrumentation(benchmarkMethod);
+ case MICRO:
+ case PICO:
+ return new MicroAllocationInstrumentation(benchmarkMethod);
+ default:
+ throw new AssertionError("unknown type");
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBenchmarkException("Benchmark methods must have no arguments or accept "
+ + "a single int or long parameter: %s", benchmarkMethod.getName());
+ }
+ }
+
+ private final class MicroAllocationInstrumentation extends Instrumentation {
+ MicroAllocationInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override
+ public void dryRun(Object benchmark) throws UserCodeException {
+ // execute the benchmark method, but don't try to take any measurements, because this JVM
+ // may not have the allocation instrumenter agent.
+ try {
+ benchmarkMethod.invoke(benchmark, 1);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ propagateIfInstanceOf(userException, SkipThisScenarioException.class);
+ throw new UserCodeException(userException);
+ }
+ }
+
+ @Override public ImmutableMap<String, String> workerOptions() {
+ return ImmutableMap.of(TRACK_ALLOCATIONS_OPTION, options.get(TRACK_ALLOCATIONS_OPTION));
+ }
+
+ @Override
+ public Class<? extends Worker> workerClass() {
+ return MicrobenchmarkAllocationWorker.class;
+ }
+
+ @Override
+ MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ return new Instrument.DefaultMeasurementCollectingVisitor(
+ ImmutableSet.of("bytes", "objects"));
+ }
+ }
+
+ @Override public TrialSchedulingPolicy schedulingPolicy() {
+ // Assuming there is enough memory it should be fine to run these in parallel.
+ return TrialSchedulingPolicy.PARALLEL;
+ }
+
+ private final class MacroAllocationInstrumentation extends Instrumentation {
+ MacroAllocationInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override
+ public void dryRun(Object benchmark) throws InvalidBenchmarkException {
+ // execute the benchmark method, but don't try to take any measurements, because this JVM
+ // may not have the allocation instrumenter agent.
+ try {
+ benchmarkMethod.invoke(benchmark);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ propagateIfInstanceOf(userException, SkipThisScenarioException.class);
+ throw new UserCodeException(userException);
+ }
+ }
+
+ @Override public ImmutableMap<String, String> workerOptions() {
+ return ImmutableMap.of(TRACK_ALLOCATIONS_OPTION, options.get(TRACK_ALLOCATIONS_OPTION));
+ }
+
+ @Override
+ public Class<? extends Worker> workerClass() {
+ return MacrobenchmarkAllocationWorker.class;
+ }
+
+ @Override
+ MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ return new Instrument.DefaultMeasurementCollectingVisitor(
+ ImmutableSet.of("bytes", "objects"));
+ }
+ }
+
+ @Override
+ public ImmutableSet<String> instrumentOptions() {
+ return ImmutableSet.of(ALLOCATION_AGENT_JAR_OPTION, TRACK_ALLOCATIONS_OPTION);
+ }
+
+ private static Optional<File> findAllocationInstrumentJarOnClasspath() throws IOException {
+ ImmutableSet<File> jarFiles = JarFinder.findJarFiles(
+ Thread.currentThread().getContextClassLoader(),
+ ClassLoader.getSystemClassLoader());
+ for (File file : jarFiles) {
+ JarFile jarFile = null;
+ try {
+ jarFile = new JarFile(file);
+ Manifest manifest = jarFile.getManifest();
+ if ((manifest != null)
+ && AllocationInstrumenter.class.getName().equals(
+ manifest.getMainAttributes().getValue("Premain-Class"))) {
+ return Optional.of(file);
+ }
+ } finally {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ }
+ }
+ return Optional.absent();
+ }
+
+ /**
+ * This instrument's worker requires the allocationinstrumenter agent jar, specified
+ * on the worker VM's command line with "-javaagent:[jarfile]".
+ */
+ @Override ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) {
+ String agentJar = options.get(ALLOCATION_AGENT_JAR_OPTION);
+ if (Strings.isNullOrEmpty(agentJar)) {
+ try {
+ Optional<File> instrumentJar = findAllocationInstrumentJarOnClasspath();
+ // TODO(gak): bundle up the allocation jar and unpack it if it's not on the classpath
+ if (instrumentJar.isPresent()) {
+ agentJar = instrumentJar.get().getAbsolutePath();
+ }
+ } catch (IOException e) {
+ logger.log(SEVERE,
+ "An exception occurred trying to locate the allocation agent jar on the classpath", e);
+ }
+ }
+ if (Strings.isNullOrEmpty(agentJar) || !new File(agentJar).exists()) {
+ throw new IllegalStateException("Can't find required allocationinstrumenter agent jar");
+ }
+ // Add microbenchmark args to minimize differences in the output
+ return new ImmutableSet.Builder<String>()
+ .addAll(super.getExtraCommandLineArgs(vmConfig))
+ // we just run in interpreted mode to ensure that intrinsics don't break the instrumentation
+ .add("-Xint")
+ .add("-javaagent:" + agentJar)
+ // Some environments rename files and use symlinks to improve resource caching,
+ // if the agent jar path is actually a symlink it will prevent the agent from finding itself
+ // and adding itself to the bootclasspath, so we do it manually here.
+ .add("-Xbootclasspath/a:" + agentJar)
+ .build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java b/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java
new file mode 100644
index 0000000..2845cd8
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.caliper.runner.CommonInstrumentOptions.GC_BEFORE_EACH_OPTION;
+import static com.google.common.base.Throwables.propagateIfInstanceOf;
+
+import com.google.caliper.api.SkipThisScenarioException;
+import com.google.caliper.bridge.AbstractLogMessageVisitor;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.model.ArbitraryMeasurement;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.SupportedPlatform;
+import com.google.caliper.util.Util;
+import com.google.caliper.worker.ArbitraryMeasurementWorker;
+import com.google.caliper.worker.Worker;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Instrument for taking an arbitrary measurement. When using this instrument, the benchmark code
+ * itself returns the value. See {@link ArbitraryMeasurement}.
+ */
+@SupportedPlatform(Platform.Type.JVM)
+public final class ArbitraryMeasurementInstrument extends Instrument {
+ @Override public boolean isBenchmarkMethod(Method method) {
+ return method.isAnnotationPresent(ArbitraryMeasurement.class);
+ }
+
+ @Override
+ public Instrumentation createInstrumentation(Method benchmarkMethod)
+ throws InvalidBenchmarkException {
+ if (benchmarkMethod.getParameterTypes().length != 0) {
+ throw new InvalidBenchmarkException(
+ "Arbitrary measurement methods should take no parameters: " + benchmarkMethod.getName());
+ }
+
+ if (benchmarkMethod.getReturnType() != double.class) {
+ throw new InvalidBenchmarkException(
+ "Arbitrary measurement methods must have a return type of double: "
+ + benchmarkMethod.getName());
+ }
+
+ // Static technically doesn't hurt anything, but it's just the completely wrong idea
+ if (Util.isStatic(benchmarkMethod)) {
+ throw new InvalidBenchmarkException(
+ "Arbitrary measurement methods must not be static: " + benchmarkMethod.getName());
+ }
+
+ if (!Util.isPublic(benchmarkMethod)) {
+ throw new InvalidBenchmarkException(
+ "Arbitrary measurement methods must be public: " + benchmarkMethod.getName());
+ }
+
+ return new ArbitraryMeasurementInstrumentation(benchmarkMethod);
+ }
+
+ @Override public TrialSchedulingPolicy schedulingPolicy() {
+ // We could allow it here but in general it would depend on the particular measurement so it
+ // should probably be configured by the user. For now we just disable it.
+ return TrialSchedulingPolicy.SERIAL;
+ }
+
+ private final class ArbitraryMeasurementInstrumentation extends Instrumentation {
+ protected ArbitraryMeasurementInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override
+ public void dryRun(Object benchmark) throws InvalidBenchmarkException {
+ try {
+ benchmarkMethod.invoke(benchmark);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ propagateIfInstanceOf(userException, SkipThisScenarioException.class);
+ throw new UserCodeException(userException);
+ }
+ }
+
+ @Override
+ public Class<? extends Worker> workerClass() {
+ return ArbitraryMeasurementWorker.class;
+ }
+
+ @Override public ImmutableMap<String, String> workerOptions() {
+ return ImmutableMap.of(GC_BEFORE_EACH_OPTION, options.get(GC_BEFORE_EACH_OPTION));
+ }
+
+ @Override
+ MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ return new SingleMeasurementCollectingVisitor();
+ }
+ }
+
+ @Override
+ public ImmutableSet<String> instrumentOptions() {
+ return ImmutableSet.of(GC_BEFORE_EACH_OPTION);
+ }
+
+ private static final class SingleMeasurementCollectingVisitor extends AbstractLogMessageVisitor
+ implements MeasurementCollectingVisitor {
+ Optional<Measurement> measurement = Optional.absent();
+
+ @Override
+ public boolean isDoneCollecting() {
+ return measurement.isPresent();
+ }
+
+ @Override
+ public boolean isWarmupComplete() {
+ return true;
+ }
+
+ @Override
+ public ImmutableList<Measurement> getMeasurements() {
+ return ImmutableList.copyOf(measurement.asSet());
+ }
+
+ @Override
+ public void visit(StopMeasurementLogMessage logMessage) {
+ this.measurement = Optional.of(Iterables.getOnlyElement(logMessage.measurements()));
+ }
+
+ @Override
+ public ImmutableList<String> getMessages() {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java
new file mode 100644
index 0000000..2aa6e1a
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagateIfInstanceOf;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+import com.google.caliper.api.SkipThisScenarioException;
+import com.google.caliper.api.VmOptions;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.Reflection;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An instance of this type represents a user-provided class. It manages creating, setting up and
+ * destroying instances of that class.
+ */
+final class BenchmarkClass {
+ private static final Logger logger = Logger.getLogger(BenchmarkClass.class.getName());
+
+ static BenchmarkClass forClass(Class<?> theClass) throws InvalidBenchmarkException {
+ return new BenchmarkClass(theClass);
+ }
+
+ final Class<?> theClass;
+ private final ParameterSet userParameters;
+ private final ImmutableSet<String> benchmarkFlags;
+
+ private BenchmarkClass(Class<?> theClass) throws InvalidBenchmarkException {
+ this.theClass = checkNotNull(theClass);
+
+ if (!theClass.getSuperclass().equals(Object.class)) {
+ throw new InvalidBenchmarkException(
+ "%s must not extend any class other than %s. Prefer composition.",
+ theClass, Object.class);
+ }
+
+ if (Modifier.isAbstract(theClass.getModifiers())) {
+ throw new InvalidBenchmarkException("Class '%s' is abstract", theClass);
+ }
+
+ // TODO: check for nested, non-static classes (non-abstract, but no constructor?)
+ // this will fail later anyway (no way to declare parameterless nested constr., but
+ // maybe signal this better?
+
+ this.userParameters = ParameterSet.create(theClass, Param.class);
+
+ this.benchmarkFlags = getVmOptions(theClass);
+ }
+
+ ImmutableSet<Method> beforeExperimentMethods() {
+ return Reflection.getAnnotatedMethods(theClass, BeforeExperiment.class);
+ }
+
+ ImmutableSet<Method> afterExperimentMethods() {
+ return Reflection.getAnnotatedMethods(theClass, AfterExperiment.class);
+ }
+
+ public ParameterSet userParameters() {
+ return userParameters;
+ }
+
+ public ImmutableSet<String> vmOptions() {
+ return benchmarkFlags;
+ }
+
+ // TODO(gak): use these methods in the worker as well
+ public void setUpBenchmark(Object benchmarkInstance) throws UserCodeException {
+ boolean setupSuccess = false;
+ try {
+ callSetUp(benchmarkInstance);
+ setupSuccess = true;
+ } finally {
+ // If setUp fails, we should call tearDown. If this method throws an exception, we
+ // need to call tearDown from here, because no one else has the reference to the
+ // Benchmark.
+ if (!setupSuccess) {
+ try {
+ callTearDown(benchmarkInstance);
+ } catch (UserCodeException e) {
+ // The exception thrown during setUp shouldn't be lost, as it's probably more
+ // important to the user.
+ logger.log(
+ Level.INFO,
+ "in @AfterExperiment methods called because @BeforeExperiment methods failed",
+ e);
+ }
+ }
+ }
+ }
+
+ public void cleanup(Object benchmark) throws UserCodeException {
+ callTearDown(benchmark);
+ }
+
+ @VisibleForTesting Class<?> benchmarkClass() {
+ return theClass;
+ }
+
+ public String name() {
+ return theClass.getName();
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof BenchmarkClass) {
+ BenchmarkClass that = (BenchmarkClass) obj;
+ return this.theClass.equals(that.theClass);
+ } else {
+ return false;
+ }
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(theClass);
+ }
+
+ @Override public String toString() {
+ return name();
+ }
+
+ private void callSetUp(Object benchmark) throws UserCodeException {
+ for (Method method : beforeExperimentMethods()) {
+ try {
+ method.invoke(benchmark);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ propagateIfInstanceOf(e.getCause(), SkipThisScenarioException.class);
+ throw new UserCodeException(
+ "Exception thrown from a @BeforeExperiment method", e.getCause());
+ }
+ }
+ }
+
+ private void callTearDown(Object benchmark) throws UserCodeException {
+ for (Method method : afterExperimentMethods()) {
+ try {
+ method.invoke(benchmark);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ propagateIfInstanceOf(e.getCause(), SkipThisScenarioException.class);
+ throw new UserCodeException(
+ "Exception thrown from an @AfterExperiment method", e.getCause());
+ }
+ }
+ }
+
+ private static ImmutableSet<String> getVmOptions(Class<?> benchmarkClass) {
+ VmOptions annotation = benchmarkClass.getAnnotation(VmOptions.class);
+ return (annotation == null)
+ ? ImmutableSet.<String>of()
+ : ImmutableSet.copyOf(annotation.value());
+ }
+
+ void validateParameters(ImmutableSetMultimap<String, String> parameters)
+ throws InvalidCommandException {
+ for (String paramName : parameters.keySet()) {
+ Parameter parameter = userParameters.get(paramName);
+ if (parameter == null) {
+ throw new InvalidCommandException("unrecognized parameter: " + paramName);
+ }
+ try {
+ parameter.validate(parameters.get(paramName));
+ } catch (InvalidBenchmarkException e) {
+ // TODO(kevinb): this is weird.
+ throw new InvalidCommandException(e.getMessage());
+ }
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java
new file mode 100644
index 0000000..a7c777d
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.config.ConfigModule;
+import com.google.caliper.options.OptionsModule;
+import com.google.caliper.util.OutputModule;
+import com.google.common.collect.ImmutableSet;
+
+import dagger.Component;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javax.inject.Singleton;
+
+/**
+ * Determines whether a class contains one or more benchmarks or not.
+ *
+ * <p>Useful for tools that need to check whether a class contains benchmarks before running them
+ * by calling an appropriate method on {@link CaliperMain}.
+ */
+// This should be considered part of the public API alongside {@link CaliperMain}.
+public final class BenchmarkClassChecker {
+
+ /**
+ * Create a new instance of {@link BenchmarkClassChecker}.
+ *
+ * @param arguments a list of command line arguments for Caliper, can include any of the
+ * options supported by Caliper.
+ * @return a new instance of {@link BenchmarkClassChecker}.
+ */
+ public static BenchmarkClassChecker create(List<String> arguments) {
+ return new BenchmarkClassChecker(arguments);
+ }
+
+ /**
+ * The set of {@link Instrument instruments} that are used to determine whether a class has any
+ * methods suitable for benchmarking.
+ */
+ private final ImmutableSet<Instrument> instruments;
+
+ private BenchmarkClassChecker(List<String> arguments) {
+ String[] args = arguments.toArray(new String[arguments.size()]);
+ InstrumentProvider instrumentProvider = DaggerBenchmarkClassChecker_InstrumentProvider.builder()
+ .optionsModule(OptionsModule.withoutBenchmarkClass(args))
+ .outputModule(new OutputModule(new PrintWriter(System.out), new PrintWriter(System.err)))
+ .build();
+
+ instruments = instrumentProvider.instruments();
+ }
+
+ /**
+ * Check to see whether the supplied class contains at least one benchmark method that can be run
+ * by caliper.
+ *
+ * @param theClass the class that may contain one or more benchmark methods.
+ * @return true if the class does contain a benchmark method, false otherwise.
+ */
+ public boolean isBenchmark(Class<?> theClass) {
+ for (Method method : theClass.getDeclaredMethods()) {
+ for (Instrument instrument : instruments) {
+ if (instrument.isBenchmarkMethod(method)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Singleton
+ @Component(modules = {
+ ConfigModule.class,
+ ExperimentingRunnerModule.class,
+ OptionsModule.class,
+ OutputModule.class,
+ PlatformModule.class,
+ RunnerModule.class,
+ })
+ /**
+ * Provides the set of supported {@link Instrument instruments}.
+ */
+ interface InstrumentProvider {
+ ImmutableSet<Instrument> instruments();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java
new file mode 100644
index 0000000..35b76be
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.runner.Running.AfterExperimentMethods;
+import com.google.caliper.runner.Running.BeforeExperimentMethods;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+
+import java.lang.reflect.Method;
+import javax.inject.Singleton;
+
+/**
+ * Binds objects related to a benchmark class.
+ */
+// TODO(gak): move more of benchmark class into this module
+@Module
+public final class BenchmarkClassModule {
+
+ @Provides
+ @Singleton
+ static BenchmarkClass provideBenchmarkClass(@Running.BenchmarkClass Class<?> benchmarkClassObject)
+ throws InvalidBenchmarkException {
+ return BenchmarkClass.forClass(benchmarkClassObject);
+ }
+
+ @Provides
+ @BeforeExperimentMethods
+ static ImmutableSet<Method> provideBeforeExperimentMethods(
+ BenchmarkClass benchmarkClass) {
+ return benchmarkClass.beforeExperimentMethods();
+ }
+
+ @Provides
+ @AfterExperimentMethods
+ static ImmutableSet<Method> provideAfterExperimentMethods(
+ BenchmarkClass benchmarkClass) {
+ return benchmarkClass.afterExperimentMethods();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java
new file mode 100644
index 0000000..548d272
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.Param;
+import com.google.caliper.util.Parser;
+import com.google.caliper.util.Parsers;
+import com.google.common.collect.ImmutableSortedMap;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.text.ParseException;
+
+import javax.inject.Inject;
+
+/**
+ * Responsible for creating instances of the benchmark class.
+ */
+final class BenchmarkCreator {
+
+ private static final String BENCHMARK_NO_PUBLIC_DEFAULT_CONSTRUCTOR =
+ "Benchmark %s does not have a publicly visible default constructor";
+
+ private final Class<?> benchmarkClass;
+ private final ImmutableSortedMap<String, String> parameters;
+ private final Constructor<?> benchmarkClassCtor;
+
+ @Inject
+ BenchmarkCreator(
+ @Running.BenchmarkClass Class<?> benchmarkClass,
+ @Running.Benchmark ImmutableSortedMap<String, String> parameters) {
+ this.benchmarkClass = benchmarkClass;
+ this.benchmarkClassCtor = findDefaultConstructor(benchmarkClass);
+ this.parameters = parameters;
+ }
+
+ private static Constructor<?> findDefaultConstructor(Class<?> benchmarkClass) {
+ Constructor<?> defaultConstructor = null;
+ for (Constructor<?> constructor : benchmarkClass.getDeclaredConstructors()) {
+ if (constructor.getParameterTypes().length == 0) {
+ defaultConstructor = constructor;
+ defaultConstructor.setAccessible(true);
+ break;
+ }
+ }
+ if (defaultConstructor == null) {
+ throw new UserCodeException(
+ String.format(BENCHMARK_NO_PUBLIC_DEFAULT_CONSTRUCTOR, benchmarkClass), null);
+ }
+ return defaultConstructor;
+ }
+
+ Object createBenchmarkInstance() {
+ Object instance;
+ try {
+ instance = benchmarkClassCtor.newInstance();
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ throw new UserCodeException(userException);
+ }
+
+ // Inject values for the user parameters.
+ for (Field field : benchmarkClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(Param.class)) {
+ try {
+ field.setAccessible(true);
+ Parser<?> parser = Parsers.conventionalParser(field.getType());
+ field.set(instance, parser.parse(parameters.get(field.getName())));
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ throw new AssertionError("types have been checked");
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("already set access");
+ }
+ }
+ }
+
+ return instance;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java
new file mode 100644
index 0000000..f109b50
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.util.Util;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Utilities for working with methods annotated by {@link Benchmark}.
+ */
+final class BenchmarkMethods {
+ private static final Class<?>[] MACROBENCHMARK_PARAMS = new Class<?>[] {};
+ private static final Class<?>[] MICROBENCHMARK_PARAMS = new Class<?>[] {int.class};
+ private static final Class<?>[] PICOBENCHMARK_PARAMS = new Class<?>[] {long.class};
+
+ private BenchmarkMethods() {}
+
+ enum Type {
+ MACRO,
+ MICRO,
+ PICO;
+
+ static Type of(Method benchmarkMethod) {
+ Class<?>[] parameterTypes = benchmarkMethod.getParameterTypes();
+ if (Arrays.equals(parameterTypes, MACROBENCHMARK_PARAMS)) {
+ return MACRO;
+ } else if (Arrays.equals(parameterTypes, MICROBENCHMARK_PARAMS)) {
+ return MICRO;
+ } else if (Arrays.equals(parameterTypes, PICOBENCHMARK_PARAMS)) {
+ return PICO;
+ } else {
+ throw new IllegalArgumentException("invalid method parameters: " + benchmarkMethod);
+ }
+ }
+ }
+
+ /**
+ * Several instruments look for benchmark methods like {@code timeBlah(int reps)}; this is the
+ * centralized code that identifies such methods.
+ *
+ * <p>This method does not check the correctness of the argument types.
+ */
+ static boolean isTimeMethod(Method method) {
+ return method.getName().startsWith("time") && Util.isPublic(method);
+ }
+
+ /**
+ * For instruments that use {@link #isTimeMethod} to identify their methods, this method checks
+ * the {@link Method} appropriately.
+ */
+ static Method checkTimeMethod(Method timeMethod) throws InvalidBenchmarkException {
+ checkArgument(isTimeMethod(timeMethod));
+ Class<?>[] parameterTypes = timeMethod.getParameterTypes();
+ if (!Arrays.equals(parameterTypes, new Class<?>[] {int.class})
+ && !Arrays.equals(parameterTypes, new Class<?>[] {long.class})) {
+ throw new InvalidBenchmarkException(
+ "Microbenchmark methods must accept a single int parameter: " + timeMethod.getName());
+ }
+
+ // Static technically doesn't hurt anything, but it's just the completely wrong idea
+ if (Util.isStatic(timeMethod)) {
+ throw new InvalidBenchmarkException(
+ "Microbenchmark methods must not be static: " + timeMethod.getName());
+ }
+ return timeMethod;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java
new file mode 100644
index 0000000..679c2bd
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the parameters applied to a benchmark. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface BenchmarkParameters {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java b/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java
new file mode 100644
index 0000000..c81e407
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.collect.ObjectArrays.concat;
+
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.options.OptionsModule;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.OutputModule;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.util.concurrent.ServiceManager;
+
+import java.io.PrintWriter;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+
+/**
+ * Primary entry point for the caliper benchmark runner application; run with {@code --help} for
+ * details. This class's only purpose is to take care of anything that's specific to command-line
+ * invocation and then hand off to {@code CaliperRun}. That is, a hypothetical GUI benchmark runner
+ * might still use {@code CaliperRun} but would skip using this class.
+ */
+public final class CaliperMain {
+ /**
+ * Your benchmark classes can implement main() like this: <pre> {@code
+ *
+ * public static void main(String[] args) {
+ * CaliperMain.main(MyBenchmark.class, args);
+ * }}</pre>
+ *
+ * Note that this method does invoke {@link System#exit} when it finishes. Consider {@link
+ * #exitlessMain} if you don't want that.
+ *
+ * <p>Measurement is handled in a subprocess, so it will not use {@code benchmarkClass} itself;
+ * the class is provided here only as a shortcut for specifying the full class <i>name</i>. The
+ * class that gets loaded later could be completely different.
+ */
+ public static void main(Class<?> benchmarkClass, String[] args) {
+ main(concat(args, benchmarkClass.getName()));
+ }
+
+ /**
+ * Entry point for the caliper benchmark runner application; run with {@code --help} for details.
+ */
+ public static void main(String[] args) {
+ PrintWriter stdout = new PrintWriter(System.out, true);
+ PrintWriter stderr = new PrintWriter(System.err, true);
+ int code = 1; // pessimism!
+
+ try {
+ exitlessMain(args, stdout, stderr);
+ code = 0;
+
+ } catch (InvalidCommandException e) {
+ e.display(stderr);
+ code = e.exitCode();
+
+ } catch (InvalidBenchmarkException e) {
+ e.display(stderr);
+
+ } catch (InvalidConfigurationException e) {
+ e.display(stderr);
+
+ } catch (Throwable t) {
+ t.printStackTrace(stderr);
+ stdout.println();
+ stdout.println("An unexpected exception has been thrown by the caliper runner.");
+ stdout.println("Please see https://sites.google.com/site/caliperusers/issues");
+ }
+
+ stdout.flush();
+ stderr.flush();
+ System.exit(code);
+ }
+
+ private static final String LEGACY_ENV = "USE_LEGACY_CALIPER";
+
+ public static void exitlessMain(String[] args, PrintWriter stdout, PrintWriter stderr)
+ throws InvalidCommandException, InvalidBenchmarkException, InvalidConfigurationException {
+ @Nullable String legacyCaliperEnv = System.getenv(LEGACY_ENV);
+ if (!Strings.isNullOrEmpty(legacyCaliperEnv)) {
+ System.err.println("Legacy Caliper is no more. " + LEGACY_ENV + " has no effect.");
+ }
+ try {
+ MainComponent mainComponent = DaggerMainComponent.builder()
+ .optionsModule(OptionsModule.withBenchmarkClass(args))
+ .outputModule(new OutputModule(stdout, stderr))
+ .build();
+ CaliperOptions options = mainComponent.getCaliperOptions();
+ if (options.printConfiguration()) {
+ stdout.println("Configuration:");
+ ImmutableSortedMap<String, String> sortedProperties =
+ ImmutableSortedMap.copyOf(mainComponent.getCaliperConfig().properties());
+ for (Entry<String, String> entry : sortedProperties.entrySet()) {
+ stdout.printf(" %s = %s%n", entry.getKey(), entry.getValue());
+ }
+ }
+ // check that the parameters are valid
+ mainComponent.getBenchmarkClass().validateParameters(options.userParameters());
+ ServiceManager serviceManager = mainComponent.getServiceManager();
+ serviceManager.startAsync().awaitHealthy();
+ try {
+ CaliperRun run = mainComponent.getCaliperRun();
+ run.run(); // throws IBE
+ } finally {
+ try {
+ // We have some shutdown logic to ensure that files are cleaned up so give it a chance to
+ // run
+ serviceManager.stopAsync().awaitStopped(10, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ // That's fine
+ }
+ }
+ } finally {
+ // courtesy flush
+ stderr.flush();
+ stdout.flush();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java b/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java
new file mode 100644
index 0000000..2555016
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * A single invocation of caliper.
+ */
+public interface CaliperRun {
+ void run() throws InvalidBenchmarkException;
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java b/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java
new file mode 100644
index 0000000..4998601
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * Instrument option identifiers shared between instruments.
+ */
+final class CommonInstrumentOptions {
+ private CommonInstrumentOptions() {}
+
+ static final String MEASUREMENTS_OPTION = "measurements";
+ static final String GC_BEFORE_EACH_OPTION = "gcBeforeEach";
+ static final String WARMUP_OPTION = "warmup";
+ static final String MAX_WARMUP_WALL_TIME_OPTION = "maxWarmupWallTime";
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java b/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java
new file mode 100644
index 0000000..a308c58
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.model.BenchmarkSpec;
+import com.google.caliper.model.InstrumentSpec;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Scenario;
+import com.google.caliper.model.Trial;
+import com.google.caliper.model.VmSpec;
+import com.google.caliper.util.Stdout;
+import com.google.common.base.Function;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Sets;
+
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.stat.descriptive.rank.Percentile;
+
+import java.io.Closeable;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Prints a brief summary of the results collected. It does not contain the measurements themselves
+ * as that is the responsibility of the webapp.
+ */
+final class ConsoleOutput implements Closeable {
+ private final PrintWriter stdout;
+
+ private final Set<InstrumentSpec> instrumentSpecs = Sets.newHashSet();
+ private final Set<VmSpec> vmSpecs = Sets.newHashSet();
+ private final Set<BenchmarkSpec> benchmarkSpecs = Sets.newHashSet();
+ private int numMeasurements = 0;
+ private int trialsCompleted = 0;
+ private final int numberOfTrials;
+ private final Stopwatch stopwatch;
+
+
+ ConsoleOutput(@Stdout PrintWriter stdout, int numberOfTrials, Stopwatch stopwatch) {
+ this.stdout = stdout;
+ this.numberOfTrials = numberOfTrials;
+ this.stopwatch = stopwatch;
+ }
+
+ /**
+ * Prints a short message when we observe a trial failure.
+ */
+ void processFailedTrial(TrialFailureException e) {
+ trialsCompleted++;
+ // TODO(lukes): it would be nice to print which trial failed. Consider adding Experiment data
+ // to the TrialFailureException.
+ stdout.println(
+ "ERROR: Trial failed to complete (its results will not be included in the run):\n"
+ + " " + e.getMessage());
+ stdout.flush();
+ }
+
+ /**
+ * Prints a summary of a successful trial result.
+ */
+ void processTrial(TrialResult result) {
+ trialsCompleted++;
+ stdout.printf("Trial Report (%d of %d):%n Experiment %s%n",
+ trialsCompleted, numberOfTrials, result.getExperiment());
+ if (!result.getTrialMessages().isEmpty()) {
+ stdout.println(" Messages:");
+ for (String message : result.getTrialMessages()) {
+ stdout.print(" ");
+ stdout.println(message);
+ }
+ }
+ Trial trial = result.getTrial();
+ ImmutableListMultimap<String, Measurement> measurementsIndex =
+ new ImmutableListMultimap.Builder<String, Measurement>()
+ .orderKeysBy(Ordering.natural())
+ .putAll(Multimaps.index(trial.measurements(), new Function<Measurement, String>() {
+ @Override public String apply(Measurement input) {
+ return input.description();
+ }
+ }))
+ .build();
+ stdout.println(" Results:");
+ for (Entry<String, Collection<Measurement>> entry : measurementsIndex.asMap().entrySet()) {
+ Collection<Measurement> measurements = entry.getValue();
+ ImmutableSet<String> units = FluentIterable.from(measurements)
+ .transform(new Function<Measurement, String>() {
+ @Override public String apply(Measurement input) {
+ return input.value().unit();
+ }
+ }).toSet();
+ double[] weightedValues = new double[measurements.size()];
+ int i = 0;
+ for (Measurement measurement : measurements) {
+ weightedValues[i] = measurement.value().magnitude() / measurement.weight();
+ i++;
+ }
+ Percentile percentile = new Percentile();
+ percentile.setData(weightedValues);
+ DescriptiveStatistics descriptiveStatistics = new DescriptiveStatistics(weightedValues);
+ String unit = Iterables.getOnlyElement(units);
+ stdout.printf(
+ " %s%s: min=%.2f, 1st qu.=%.2f, median=%.2f, mean=%.2f, 3rd qu.=%.2f, max=%.2f%n",
+ entry.getKey(), unit.isEmpty() ? "" : "(" + unit + ")",
+ descriptiveStatistics.getMin(), percentile.evaluate(25),
+ percentile.evaluate(50), descriptiveStatistics.getMean(),
+ percentile.evaluate(75), descriptiveStatistics.getMax());
+ }
+
+ instrumentSpecs.add(trial.instrumentSpec());
+ Scenario scenario = trial.scenario();
+ vmSpecs.add(scenario.vmSpec());
+ benchmarkSpecs.add(scenario.benchmarkSpec());
+ numMeasurements += trial.measurements().size();
+ }
+
+ @Override public void close() {
+ if (trialsCompleted == numberOfTrials) { // if we finished all the trials
+ stdout.printf("Collected %d measurements from:%n", numMeasurements);
+ stdout.printf(" %d instrument(s)%n", instrumentSpecs.size());
+ stdout.printf(" %d virtual machine(s)%n", vmSpecs.size());
+ stdout.printf(" %d benchmark(s)%n", benchmarkSpecs.size());
+ stdout.println();
+ stdout.format("Execution complete: %s.%n", stopwatch.stop());
+ stdout.flush();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java b/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java
new file mode 100644
index 0000000..798e724
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.model.Host;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * An instance of this class is responsible for returning a Map that describes the environment:
+ * JVM version, os details, etc.
+ */
+final class EnvironmentGetter {
+ Host getHost() {
+ return new Host.Builder()
+ .addAllProperies(getProperties())
+ .build();
+ }
+
+ private Map<String, String> getProperties() {
+ TreeMap<String, String> propertyMap = Maps.newTreeMap();
+
+ Map<String, String> sysProps = Maps.fromProperties(System.getProperties());
+
+ // Sometimes java.runtime.version is more descriptive than java.version
+ String version = sysProps.get("java.version");
+ String alternateVersion = sysProps.get("java.runtime.version");
+ if (alternateVersion != null && alternateVersion.length() > version.length()) {
+ version = alternateVersion;
+ }
+ propertyMap.put("host.availableProcessors",
+ Integer.toString(Runtime.getRuntime().availableProcessors()));
+
+ String osName = sysProps.get("os.name");
+ propertyMap.put("os.name", osName);
+ propertyMap.put("os.version", sysProps.get("os.version"));
+ propertyMap.put("os.arch", sysProps.get("os.arch"));
+
+ if (osName.equals("Linux")) {
+ getLinuxEnvironment(propertyMap);
+ }
+
+ return propertyMap;
+ }
+
+ private void getLinuxEnvironment(Map<String, String> propertyMap) {
+ // the following probably doesn't work on ALL linux
+ Multimap<String, String> cpuInfo = propertiesFromLinuxFile("/proc/cpuinfo");
+ propertyMap.put("host.cpus", Integer.toString(cpuInfo.get("processor").size()));
+ String s = "cpu cores";
+ propertyMap.put("host.cpu.cores", describe(cpuInfo, s));
+ propertyMap.put("host.cpu.names", describe(cpuInfo, "model name"));
+ propertyMap.put("host.cpu.cachesize", describe(cpuInfo, "cache size"));
+
+ Multimap<String, String> memInfo = propertiesFromLinuxFile("/proc/meminfo");
+ // TODO redo memInfo.toString() so we don't get square brackets
+ propertyMap.put("host.memory.physical", memInfo.get("MemTotal").toString());
+ propertyMap.put("host.memory.swap", memInfo.get("SwapTotal").toString());
+ }
+
+ private static String describe(Multimap<String, String> cpuInfo, String s) {
+ Collection<String> strings = cpuInfo.get(s);
+ // TODO redo the ImmutableMultiset.toString() call so we don't get square brackets
+ return (strings.size() == 1)
+ ? strings.iterator().next()
+ : ImmutableMultiset.copyOf(strings).toString();
+ }
+
+ /**
+ * Returns the key/value pairs from the specified properties-file like file.
+ * Unlike standard Java properties files, {@code reader} is allowed to list
+ * the same property multiple times. Comments etc. are unsupported.
+ *
+ * <p>If there's any problem reading the file's contents, we'll return an
+ * empty Multimap.
+ */
+ private static Multimap<String, String> propertiesFromLinuxFile(String file) {
+ try {
+ List<String> lines = Files.readLines(new File(file), Charset.defaultCharset());
+ ImmutableMultimap.Builder<String, String> result = ImmutableMultimap.builder();
+ for (String line : lines) {
+ // TODO(schmoe): replace with Splitter (in Guava release 10)
+ String[] parts = line.split("\\s*\\:\\s*", 2);
+ if (parts.length == 2) {
+ result.put(parts[0], parts[1]);
+ }
+ }
+ return result.build();
+ } catch (IOException e) {
+ // If there's any problem reading the file, just return an empty multimap.
+ return ImmutableMultimap.of();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/Experiment.java b/caliper/src/main/java/com/google/caliper/runner/Experiment.java
new file mode 100644
index 0000000..f0a877f
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/Experiment.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSortedMap;
+
+import java.util.Map;
+
+/**
+ * A single "premise" for making benchmark measurements: which class and method to invoke, which VM
+ * to use, which choices for user parameters and vmArguments to fill in and which instrument to use
+ * to measure. A caliper run will compute all possible scenarios using
+ * {@link FullCartesianExperimentSelector}, and will run one or more trials of each.
+ */
+final class Experiment {
+ private final Instrumentation instrumentation;
+ private final VirtualMachine vm;
+ private final ImmutableSortedMap<String, String> userParameters;
+
+ Experiment(
+ Instrumentation instrumentation,
+ Map<String, String> userParameters,
+ VirtualMachine vm) {
+ this.instrumentation = checkNotNull(instrumentation);
+ this.userParameters = ImmutableSortedMap.copyOf(userParameters);
+ this.vm = checkNotNull(vm);
+ }
+
+ Instrumentation instrumentation() {
+ return instrumentation;
+ }
+
+ ImmutableSortedMap<String, String> userParameters() {
+ return userParameters;
+ }
+
+ VirtualMachine vm() {
+ return vm;
+ }
+
+ @Override public boolean equals(Object object) {
+ if (object instanceof Experiment) {
+ Experiment that = (Experiment) object;
+ return this.instrumentation.equals(that.instrumentation)
+ && this.vm.equals(that.vm)
+ && this.userParameters.equals(that.userParameters);
+ }
+ return false;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(instrumentation, vm, userParameters);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper("")
+ .add("instrument", instrumentation.instrument())
+ .add("benchmarkMethod", instrumentation.benchmarkMethod.getName())
+ .add("vm", vm.name)
+ .add("parameters", userParameters)
+ .toString();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java
new file mode 100644
index 0000000..174c5f5
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.caliper.runner.Running.Benchmark;
+
+import dagger.Subcomponent;
+
+/**
+ * Provides access to the benchmark instance to use for the experiment.
+ */
+@Subcomponent(modules = ExperimentModule.class)
+public interface ExperimentComponent {
+ @Benchmark Object getBenchmarkInstance();
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java
new file mode 100644
index 0000000..79711a1
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.caliper.runner.Running.Benchmark;
+import static com.google.caliper.runner.Running.BenchmarkMethod;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.caliper.bridge.WorkerSpec;
+import com.google.caliper.util.Util;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import dagger.Module;
+import dagger.Provides;
+
+import java.lang.reflect.Method;
+
+/**
+ * A module that binds data specific to a single experiment.
+ */
+@Module
+public final class ExperimentModule {
+ private final ImmutableSortedMap<String, String> parameters;
+ private final Method benchmarkMethod;
+
+ private ExperimentModule(
+ Method benchmarkMethod,
+ ImmutableSortedMap<String, String> parameters) {
+ this.parameters = checkNotNull(parameters);
+ this.benchmarkMethod = checkNotNull(benchmarkMethod);
+ }
+
+ public static ExperimentModule forExperiment(Experiment experiment) {
+ Method benchmarkMethod = experiment.instrumentation().benchmarkMethod();
+ return new ExperimentModule(
+ benchmarkMethod,
+ experiment.userParameters());
+ }
+
+ public static ExperimentModule forWorkerSpec(WorkerSpec spec)
+ throws ClassNotFoundException {
+ Class<?> benchmarkClass = Util.loadClass(spec.benchmarkSpec.className());
+ Method benchmarkMethod = findBenchmarkMethod(benchmarkClass, spec.benchmarkSpec.methodName(),
+ spec.methodParameterClasses);
+ benchmarkMethod.setAccessible(true);
+ return new ExperimentModule(benchmarkMethod, spec.benchmarkSpec.parameters());
+ }
+
+ @Provides
+ @Benchmark
+ static Object provideRunningBenchmark(BenchmarkCreator creator) {
+ return creator.createBenchmarkInstance();
+ }
+
+ @Provides
+ @BenchmarkMethod
+ String provideRunningBenchmarkMethodName() {
+ return benchmarkMethod.getName();
+ }
+
+ @Provides
+ @BenchmarkMethod
+ Method provideRunningBenchmarkMethod() {
+ return benchmarkMethod;
+ }
+
+ @Provides
+ @Benchmark
+ ImmutableSortedMap<String, String> provideUserParameters() {
+ return parameters;
+ }
+
+ private static Method findBenchmarkMethod(Class<?> benchmark, String methodName,
+ ImmutableList<Class<?>> methodParameterClasses) {
+ Class<?>[] params = methodParameterClasses.toArray(new Class<?>[methodParameterClasses.size()]);
+ try {
+ return benchmark.getDeclaredMethod(methodName, params);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (SecurityException e) {
+ // assertion error?
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java
new file mode 100644
index 0000000..fe32057
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+
+public interface ExperimentSelector {
+ ImmutableSet<Instrument> instruments();
+ ImmutableSet<VirtualMachine> vms();
+ ImmutableSetMultimap<String, String> userParameters();
+
+ // The important method
+ ImmutableSet<Experiment> selectExperiments();
+
+ String selectionType();
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java
new file mode 100644
index 0000000..5214193
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.logging.Level.WARNING;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.api.SkipThisScenarioException;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.util.Stdout;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Throwables;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Queues;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * An execution of each {@link Experiment} for the configured number of trials.
+ */
+@VisibleForTesting
+public final class ExperimentingCaliperRun implements CaliperRun {
+
+ private static final Logger logger = Logger.getLogger(ExperimentingCaliperRun.class.getName());
+
+ private static final FutureFallback<Object> FALLBACK_TO_NULL = new FutureFallback<Object>() {
+ final ListenableFuture<Object> nullFuture = Futures.immediateFuture(null);
+ @Override public ListenableFuture<Object> create(Throwable t) throws Exception {
+ return nullFuture;
+ }
+ };
+
+ private final MainComponent mainComponent;
+ private final CaliperOptions options;
+ private final PrintWriter stdout;
+ private final BenchmarkClass benchmarkClass;
+ private final ImmutableSet<Instrument> instruments;
+ private final ImmutableSet<ResultProcessor> resultProcessors;
+ private final ExperimentSelector selector;
+ private final Provider<ListeningExecutorService> executorProvider;
+
+ @Inject @VisibleForTesting
+ public ExperimentingCaliperRun(
+ MainComponent mainComponent,
+ CaliperOptions options,
+ @Stdout PrintWriter stdout,
+ BenchmarkClass benchmarkClass,
+ ImmutableSet<Instrument> instruments,
+ ImmutableSet<ResultProcessor> resultProcessors,
+ ExperimentSelector selector,
+ Provider<ListeningExecutorService> executorProvider) {
+ this.mainComponent = mainComponent;
+ this.options = options;
+ this.stdout = stdout;
+ this.benchmarkClass = benchmarkClass;
+ this.instruments = instruments;
+ this.resultProcessors = resultProcessors;
+ this.selector = selector;
+ this.executorProvider = executorProvider;
+ }
+
+ @Override
+ public void run() throws InvalidBenchmarkException {
+ ImmutableSet<Experiment> allExperiments = selector.selectExperiments();
+ // TODO(lukes): move this standard-out handling into the ConsoleOutput class?
+ stdout.println("Experiment selection: ");
+ stdout.println(" Benchmark Methods: " + FluentIterable.from(allExperiments)
+ .transform(new Function<Experiment, String>() {
+ @Override public String apply(Experiment experiment) {
+ return experiment.instrumentation().benchmarkMethod().getName();
+ }
+ }).toSet());
+ stdout.println(" Instruments: " + FluentIterable.from(selector.instruments())
+ .transform(new Function<Instrument, String>() {
+ @Override public String apply(Instrument instrument) {
+ return instrument.name();
+ }
+ }));
+ stdout.println(" User parameters: " + selector.userParameters());
+ stdout.println(" Virtual machines: " + FluentIterable.from(selector.vms())
+ .transform(
+ new Function<VirtualMachine, String>() {
+ @Override public String apply(VirtualMachine vm) {
+ return vm.name;
+ }
+ }));
+ stdout.println(" Selection type: " + selector.selectionType());
+ stdout.println();
+
+ if (allExperiments.isEmpty()) {
+ throw new InvalidBenchmarkException(
+ "There were no experiments to be performed for the class %s using the instruments %s",
+ benchmarkClass.benchmarkClass().getSimpleName(), instruments);
+ }
+
+ stdout.format("This selection yields %s experiments.%n", allExperiments.size());
+ stdout.flush();
+
+ // always dry run first.
+ ImmutableSet<Experiment> experimentsToRun = dryRun(allExperiments);
+ if (experimentsToRun.size() != allExperiments.size()) {
+ stdout.format("%d experiments were skipped.%n",
+ allExperiments.size() - experimentsToRun.size());
+ }
+
+ if (experimentsToRun.isEmpty()) {
+ throw new InvalidBenchmarkException("All experiments were skipped.");
+ }
+
+ if (options.dryRun()) {
+ return;
+ }
+
+ stdout.flush();
+
+ int totalTrials = experimentsToRun.size() * options.trialsPerScenario();
+ Stopwatch stopwatch = Stopwatch.createStarted();
+ List<ScheduledTrial> trials = createScheduledTrials(experimentsToRun, totalTrials);
+
+ final ListeningExecutorService executor = executorProvider.get();
+ List<ListenableFuture<TrialResult>> pendingTrials = scheduleTrials(trials, executor);
+ ConsoleOutput output = new ConsoleOutput(stdout, totalTrials, stopwatch);
+ try {
+ // Process results as they complete.
+ for (ListenableFuture<TrialResult> trialFuture : inCompletionOrder(pendingTrials)) {
+ try {
+ TrialResult result = trialFuture.get();
+ output.processTrial(result);
+ for (ResultProcessor resultProcessor : resultProcessors) {
+ resultProcessor.processTrial(result.getTrial());
+ }
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof TrialFailureException) {
+ output.processFailedTrial((TrialFailureException) e.getCause());
+ } else {
+ for (ListenableFuture<?> toCancel : pendingTrials) {
+ toCancel.cancel(true);
+ }
+ throw Throwables.propagate(e.getCause());
+ }
+ } catch (InterruptedException e) {
+ // be responsive to interruption, cancel outstanding work and exit
+ for (ListenableFuture<?> toCancel : pendingTrials) {
+ // N.B. TrialRunLoop is responsive to interruption.
+ toCancel.cancel(true);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+ } finally {
+ executor.shutdown();
+ output.close();
+ }
+
+ for (ResultProcessor resultProcessor : resultProcessors) {
+ try {
+ resultProcessor.close();
+ } catch (IOException e) {
+ logger.log(WARNING, "Could not close a result processor: " + resultProcessor, e);
+ }
+ }
+ }
+
+ /**
+ * Schedule all the trials.
+ *
+ * <p>This method arranges all the {@link ScheduledTrial trials} to run according to their
+ * scheduling criteria. The executor instance is responsible for enforcing max parallelism.
+ */
+ private List<ListenableFuture<TrialResult>> scheduleTrials(List<ScheduledTrial> trials,
+ final ListeningExecutorService executor) {
+ List<ListenableFuture<TrialResult>> pendingTrials = Lists.newArrayList();
+ List<ScheduledTrial> serialTrials = Lists.newArrayList();
+ for (final ScheduledTrial scheduledTrial : trials) {
+ if (scheduledTrial.policy() == TrialSchedulingPolicy.PARALLEL) {
+ pendingTrials.add(executor.submit(scheduledTrial.trialTask()));
+ } else {
+ serialTrials.add(scheduledTrial);
+ }
+ }
+ // A future representing the completion of all prior tasks. Futures.successfulAsList allows us
+ // to ignore failure.
+ ListenableFuture<?> previous = Futures.successfulAsList(pendingTrials);
+ for (final ScheduledTrial scheduledTrial : serialTrials) {
+ // each of these trials can only start after all prior trials have finished, so we use
+ // Futures.transform to force the sequencing.
+ ListenableFuture<TrialResult> current =
+ Futures.transform(
+ previous,
+ new AsyncFunction<Object, TrialResult>() {
+ @Override public ListenableFuture<TrialResult> apply(Object ignored) {
+ return executor.submit(scheduledTrial.trialTask());
+ }
+ });
+ pendingTrials.add(current);
+ // ignore failure of the prior task.
+ previous = Futures.withFallback(current, FALLBACK_TO_NULL);
+ }
+ return pendingTrials;
+ }
+
+ /** Returns all the ScheduledTrials for this run. */
+ private List<ScheduledTrial> createScheduledTrials(ImmutableSet<Experiment> experimentsToRun,
+ int totalTrials) {
+ List<ScheduledTrial> trials = Lists.newArrayListWithCapacity(totalTrials);
+ /** This is 1-indexed because it's only used for display to users. E.g. "Trial 1 of 27" */
+ int trialNumber = 1;
+ for (int i = 0; i < options.trialsPerScenario(); i++) {
+ for (Experiment experiment : experimentsToRun) {
+ try {
+ TrialScopeComponent trialScopeComponent = mainComponent.newTrialComponent(
+ new TrialModule(UUID.randomUUID(), trialNumber, experiment));
+
+ trials.add(trialScopeComponent.getScheduledTrial());
+ } finally {
+ trialNumber++;
+ }
+ }
+ }
+ return trials;
+ }
+
+ /**
+ * Attempts to run each given scenario once, in the current VM. Returns a set of all of the
+ * scenarios that didn't throw a {@link SkipThisScenarioException}.
+ */
+ ImmutableSet<Experiment> dryRun(Iterable<Experiment> experiments)
+ throws InvalidBenchmarkException {
+ ImmutableSet.Builder<Experiment> builder = ImmutableSet.builder();
+ for (Experiment experiment : experiments) {
+ try {
+ ExperimentComponent experimentComponent =
+ mainComponent.newExperimentComponent(ExperimentModule.forExperiment(experiment));
+ Object benchmark = experimentComponent.getBenchmarkInstance();
+ benchmarkClass.setUpBenchmark(benchmark);
+ try {
+ experiment.instrumentation().dryRun(benchmark);
+ builder.add(experiment);
+ } finally {
+ // discard 'benchmark' now; the worker will have to instantiate its own anyway
+ benchmarkClass.cleanup(benchmark);
+ }
+ } catch (SkipThisScenarioException innocuous) {}
+ }
+ return builder.build();
+ }
+
+ public static <T> ImmutableList<ListenableFuture<T>> inCompletionOrder(
+ Iterable<? extends ListenableFuture<? extends T>> futures) {
+ final ConcurrentLinkedQueue<SettableFuture<T>> delegates = Queues.newConcurrentLinkedQueue();
+ ImmutableList.Builder<ListenableFuture<T>> listBuilder = ImmutableList.builder();
+ for (final ListenableFuture<? extends T> future : futures) {
+ SettableFuture<T> delegate = SettableFuture.create();
+ // Must make sure to add the delegate to the queue first in case the future is already done
+ delegates.add(delegate);
+ future.addListener(new Runnable() {
+ @Override public void run() {
+ SettableFuture<T> delegate = delegates.remove();
+ try {
+ delegate.set(Uninterruptibles.getUninterruptibly(future));
+ } catch (ExecutionException e) {
+ delegate.setException(e.getCause());
+ } catch (CancellationException e) {
+ delegate.cancel(true);
+ }
+ }
+ }, MoreExecutors.directExecutor());
+ listBuilder.add(delegate);
+ }
+ return listBuilder.build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java
new file mode 100644
index 0000000..7abcbd9
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.InstrumentConfig;
+import com.google.caliper.model.Host;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.ShortDuration;
+import com.google.caliper.util.Stderr;
+import com.google.caliper.util.Util;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Ordering;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Service;
+
+import dagger.MapKey;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Provides.Type;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+/**
+ * Configures a {@link CaliperRun} that performs experiments.
+ */
+@Module
+final class ExperimentingRunnerModule {
+ private static final String RUNNER_MAX_PARALLELISM_OPTION = "runner.maxParallelism";
+
+ @Provides(type = Type.SET)
+ static Service provideServerSocketService(ServerSocketService impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.SET)
+ static Service provideTrialOutputFactoryService(TrialOutputFactoryService impl) {
+ return impl;
+ }
+
+ @Provides
+ static TrialOutputFactory provideTrialOutputFactory(TrialOutputFactoryService impl) {
+ return impl;
+ }
+
+ @Provides
+ static ExperimentSelector provideExperimentSelector(FullCartesianExperimentSelector impl) {
+ return impl;
+ }
+
+ @Provides
+ static ListeningExecutorService provideExecutorService(CaliperConfig config) {
+ int poolSize = Integer.parseInt(config.properties().get(RUNNER_MAX_PARALLELISM_OPTION));
+ return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(poolSize));
+ }
+
+ @LocalPort
+ @Provides
+ static int providePortNumber(ServerSocketService serverSocketService) {
+ return serverSocketService.getPort();
+ }
+
+ /**
+ * Specifies the {@link Class} object to use as a key in the map of available
+ * {@link ResultProcessor result processors} passed to
+ * {@link #provideResultProcessors(CaliperConfig, Map)}.
+ */
+ @MapKey(unwrapValue = true)
+ public @interface ResultProcessorClassKey {
+ Class<? extends ResultProcessor> value();
+ }
+
+ @Provides(type = Type.MAP)
+ @ResultProcessorClassKey(OutputFileDumper.class)
+ static ResultProcessor provideOutputFileDumper(OutputFileDumper impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @ResultProcessorClassKey(HttpUploader.class)
+ static ResultProcessor provideHttpUploader(HttpUploader impl) {
+ return impl;
+ }
+
+ @Provides static ImmutableSet<ResultProcessor> provideResultProcessors(
+ CaliperConfig config,
+ Map<Class<? extends ResultProcessor>, Provider<ResultProcessor>> availableProcessors) {
+ ImmutableSet.Builder<ResultProcessor> builder = ImmutableSet.builder();
+ for (Class<? extends ResultProcessor> processorClass : config.getConfiguredResultProcessors()) {
+ Provider<ResultProcessor> resultProcessorProvider = availableProcessors.get(processorClass);
+ ResultProcessor resultProcessor = resultProcessorProvider == null
+ ? ResultProcessorCreator.createResultProcessor(processorClass)
+ : resultProcessorProvider.get();
+ builder.add(resultProcessor);
+ }
+ return builder.build();
+ }
+
+ @Provides static UUID provideUuid() {
+ return UUID.randomUUID();
+ }
+
+ @Provides @BenchmarkParameters
+ static ImmutableSetMultimap<String, String> provideBenchmarkParameters(
+ BenchmarkClass benchmarkClass, CaliperOptions options) throws InvalidBenchmarkException {
+ return benchmarkClass.userParameters().fillInDefaultsFor(options.userParameters());
+ }
+
+ @Provides @Singleton
+ static Host provideHost(EnvironmentGetter environmentGetter) {
+ return environmentGetter.getHost();
+ }
+
+ @Provides @Singleton
+ static EnvironmentGetter provideEnvironmentGetter() {
+ return new EnvironmentGetter();
+ }
+
+ /**
+ * Specifies the {@link Class} object to use as a key in the map of available
+ * {@link Instrument instruments} passed to {@link #provideInstruments},
+ */
+ @MapKey(unwrapValue = true)
+ public @interface InstrumentClassKey {
+ Class<? extends Instrument> value();
+ }
+
+ @Provides(type = Type.MAP)
+ @InstrumentClassKey(ArbitraryMeasurementInstrument.class)
+ static Instrument provideArbitraryMeasurementInstrument() {
+ return new ArbitraryMeasurementInstrument();
+ }
+
+ @Provides(type = Type.MAP)
+ @InstrumentClassKey(AllocationInstrument.class)
+ static Instrument provideAllocationInstrument() {
+ return new AllocationInstrument();
+ }
+
+ @Provides(type = Type.MAP)
+ @InstrumentClassKey(RuntimeInstrument.class)
+ static Instrument provideRuntimeInstrument(
+ @NanoTimeGranularity ShortDuration nanoTimeGranularity) {
+ return new RuntimeInstrument(nanoTimeGranularity);
+ }
+
+ @Provides
+ static ImmutableSet<Instrument> provideInstruments(
+ CaliperOptions options,
+ final CaliperConfig config,
+ Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments,
+ Platform platform,
+ @Stderr PrintWriter stderr)
+ throws InvalidCommandException {
+
+ ImmutableSet.Builder<Instrument> builder = ImmutableSet.builder();
+ ImmutableSet<String> configuredInstruments = config.getConfiguredInstruments();
+ for (final String instrumentName : options.instrumentNames()) {
+ if (!configuredInstruments.contains(instrumentName)) {
+ throw new InvalidCommandException("%s is not a configured instrument (%s). "
+ + "use --print-config to see the configured instruments.",
+ instrumentName, configuredInstruments);
+ }
+ final InstrumentConfig instrumentConfig = config.getInstrumentConfig(instrumentName);
+ String className = instrumentConfig.className();
+ try {
+ Class<? extends Instrument> clazz =
+ Util.lenientClassForName(className).asSubclass(Instrument.class);
+ Provider<Instrument> instrumentProvider = availableInstruments.get(clazz);
+ if (instrumentProvider == null) {
+ throw new InvalidInstrumentException("Instrument %s not supported", className);
+ }
+
+ // Make sure that the instrument is supported on the platform.
+ if (platform.supports(clazz)) {
+ Instrument instrument = instrumentProvider.get();
+ InstrumentInjectorModule injectorModule =
+ new InstrumentInjectorModule(instrumentConfig, instrumentName);
+ InstrumentComponent instrumentComponent = DaggerInstrumentComponent.builder()
+ .instrumentInjectorModule(injectorModule)
+ .build();
+ instrumentComponent.injectInstrument(instrument);
+ builder.add(instrument);
+ } else {
+ stderr.format("Instrument %s not supported on %s, ignoring\n",
+ className, platform.name());
+ }
+ } catch (ClassNotFoundException e) {
+ throw new InvalidCommandException("Cannot find instrument class '%s'", className);
+ }
+ }
+ return builder.build();
+ }
+
+ @Provides @Singleton static NanoTimeGranularityTester provideNanoTimeGranularityTester() {
+ return new NanoTimeGranularityTester();
+ }
+
+ @Provides @Singleton @NanoTimeGranularity static ShortDuration provideNanoTimeGranularity(
+ NanoTimeGranularityTester tester) {
+ return tester.testNanoTimeGranularity();
+ }
+
+ @Provides static ImmutableSet<Instrumentation> provideInstrumentations(CaliperOptions options,
+ BenchmarkClass benchmarkClass, ImmutableSet<Instrument> instruments)
+ throws InvalidBenchmarkException {
+ ImmutableSet.Builder<Instrumentation> builder = ImmutableSet.builder();
+ ImmutableSet<String> benchmarkMethodNames = options.benchmarkMethodNames();
+ Set<String> unusedBenchmarkNames = new HashSet<String>(benchmarkMethodNames);
+ for (Instrument instrument : instruments) {
+ for (Method method : findAllBenchmarkMethods(benchmarkClass.benchmarkClass(), instrument)) {
+ if (benchmarkMethodNames.isEmpty() || benchmarkMethodNames.contains(method.getName())) {
+ builder.add(instrument.createInstrumentation(method));
+ unusedBenchmarkNames.remove(method.getName());
+ }
+ }
+ }
+ if (!unusedBenchmarkNames.isEmpty()) {
+ throw new InvalidBenchmarkException(
+ "Invalid benchmark method(s) specified in options: " + unusedBenchmarkNames);
+ }
+ return builder.build();
+ }
+
+ private static ImmutableSortedSet<Method> findAllBenchmarkMethods(Class<?> benchmarkClass,
+ Instrument instrument) throws InvalidBenchmarkException {
+ ImmutableSortedSet.Builder<Method> result = ImmutableSortedSet.orderedBy(
+ Ordering.natural().onResultOf(new Function<Method, String>() {
+ @Override public String apply(Method method) {
+ return method.getName();
+ }
+ }));
+ Set<String> benchmarkMethodNames = new HashSet<String>();
+ Set<String> overloadedMethodNames = new TreeSet<String>();
+ for (Method method : benchmarkClass.getDeclaredMethods()) {
+ if (instrument.isBenchmarkMethod(method)) {
+ method.setAccessible(true);
+ result.add(method);
+ if (!benchmarkMethodNames.add(method.getName())) {
+ overloadedMethodNames.add(method.getName());
+ }
+ }
+ }
+ if (!overloadedMethodNames.isEmpty()) {
+ throw new InvalidBenchmarkException(
+ "Overloads are disallowed for benchmark methods, found overloads of %s in benchmark %s",
+ overloadedMethodNames,
+ benchmarkClass);
+ }
+ return result.build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java b/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java
new file mode 100644
index 0000000..a972ca4
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * A set of {@link Experiment experiments} constructed by taking all possible combinations of
+ * instruments, benchmark methods, user parameters, VM specs and VM arguments.
+ */
+public final class FullCartesianExperimentSelector implements ExperimentSelector {
+ private ImmutableSet<Instrumentation> instrumentations;
+ private final ImmutableSet<VirtualMachine> vms;
+ private final ImmutableSetMultimap<String, String> userParameters;
+
+ @Inject FullCartesianExperimentSelector(
+ ImmutableSet<Instrumentation> instrumentations,
+ ImmutableSet<VirtualMachine> vms,
+ @BenchmarkParameters ImmutableSetMultimap<String, String> userParameters) {
+ this.instrumentations = instrumentations;
+ this.vms = vms;
+ this.userParameters = userParameters;
+ }
+
+ // TODO(gak): put this someplace more sensible
+ @Override public ImmutableSet<Instrument> instruments() {
+ return FluentIterable.from(instrumentations)
+ .transform(new Function<Instrumentation, Instrument>() {
+ @Override public Instrument apply(Instrumentation input) {
+ return input.instrument();
+ }
+ })
+ .toSet();
+ }
+
+ @Override public ImmutableSet<VirtualMachine> vms() {
+ return vms;
+ }
+
+ @Override public ImmutableSetMultimap<String, String> userParameters() {
+ return userParameters;
+ }
+
+ @Override public ImmutableSet<Experiment> selectExperiments() {
+ List<Experiment> experiments = Lists.newArrayList();
+ for (Instrumentation instrumentation : instrumentations) {
+ for (VirtualMachine vm : vms) {
+ for (List<String> userParamsChoice : cartesian(userParameters)) {
+ ImmutableMap<String, String> theseUserParams =
+ zip(userParameters.keySet(), userParamsChoice);
+ experiments.add(new Experiment(instrumentation, theseUserParams, vm));
+ }
+ }
+ }
+ return ImmutableSet.copyOf(experiments);
+ }
+
+ protected static <T> Set<List<T>> cartesian(SetMultimap<String, T> multimap) {
+ @SuppressWarnings({"unchecked", "rawtypes"}) // promised by spec
+ ImmutableMap<String, Set<T>> paramsAsMap = (ImmutableMap) multimap.asMap();
+ return Sets.cartesianProduct(paramsAsMap.values().asList());
+ }
+
+ protected static <K, V> ImmutableMap<K, V> zip(Set<K> keys, Collection<V> values) {
+ ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
+
+ Iterator<K> keyIterator = keys.iterator();
+ Iterator<V> valueIterator = values.iterator();
+
+ while (keyIterator.hasNext() && valueIterator.hasNext()) {
+ builder.put(keyIterator.next(), valueIterator.next());
+ }
+
+ if (keyIterator.hasNext() || valueIterator.hasNext()) {
+ throw new AssertionError(); // I really screwed up, then.
+ }
+ return builder.build();
+ }
+
+ @Override public String selectionType() {
+ return "Full cartesian product";
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java b/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java
new file mode 100644
index 0000000..2f99a50
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.util.Stdout;
+import com.google.gson.Gson;
+
+import com.sun.jersey.api.client.Client;
+
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * A {@link ResultProcessor} implementation that publishes results to the webapp using an HTTP
+ * request.
+ */
+public class HttpUploader extends ResultsUploader {
+ @Inject HttpUploader(@Stdout PrintWriter stdout, Gson gson, CaliperConfig config)
+ throws InvalidConfigurationException {
+ super(stdout, gson, Client.create(), config.getResultProcessorConfig(HttpUploader.class));
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/Instrument.java b/caliper/src/main/java/com/google/caliper/runner/Instrument.java
new file mode 100644
index 0000000..0d6bc73
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/Instrument.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.caliper.bridge.AbstractLogMessageVisitor;
+import com.google.caliper.bridge.LogMessageVisitor;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.model.InstrumentSpec;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.worker.Worker;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+
+import java.lang.reflect.Method;
+
+import javax.inject.Inject;
+
+public abstract class Instrument {
+ protected ImmutableMap<String, String> options;
+ private String name = getClass().getSimpleName();
+
+ @Inject void setOptions(@InstrumentOptions ImmutableMap<String, String> options) {
+ this.options = ImmutableMap.copyOf(
+ Maps.filterKeys(options, Predicates.in(instrumentOptions())));
+ }
+
+ @Inject void setInstrumentName(@InstrumentName String name) {
+ this.name = name;
+ }
+
+ String name() {
+ return name;
+ }
+
+ @Override public String toString() {
+ return name();
+ }
+
+ public abstract boolean isBenchmarkMethod(Method method);
+
+ public abstract Instrumentation createInstrumentation(Method benchmarkMethod)
+ throws InvalidBenchmarkException;
+
+ /**
+ * Indicates that trials using this instrument can be run in parallel with other trials.
+ */
+ public abstract TrialSchedulingPolicy schedulingPolicy();
+
+ /**
+ * The application of an instrument to a particular benchmark method.
+ */
+ // TODO(gak): consider passing in Instrument explicitly for DI
+ public abstract class Instrumentation {
+ protected Method benchmarkMethod;
+
+ protected Instrumentation(Method benchmarkMethod) {
+ this.benchmarkMethod = checkNotNull(benchmarkMethod);
+ }
+
+ Instrument instrument() {
+ return Instrument.this;
+ }
+
+ Method benchmarkMethod() {
+ return benchmarkMethod;
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Instrumentation) {
+ Instrumentation that = (Instrumentation) obj;
+ return Instrument.this.equals(that.instrument())
+ && this.benchmarkMethod.equals(that.benchmarkMethod);
+ }
+ return super.equals(obj);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hashCode(Instrument.this, benchmarkMethod);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(Instrumentation.class)
+ .add("instrument", Instrument.this)
+ .add("benchmarkMethod", benchmarkMethod)
+ .toString();
+ }
+
+ public abstract void dryRun(Object benchmark) throws InvalidBenchmarkException;
+
+ public abstract Class<? extends Worker> workerClass();
+
+ /**
+ * Return the subset of options (and possibly a transformation thereof) to be used in the
+ * worker. Returns all instrument options by default.
+ */
+ public ImmutableMap<String, String> workerOptions() {
+ return options;
+ }
+
+ abstract MeasurementCollectingVisitor getMeasurementCollectingVisitor();
+ }
+
+ public final ImmutableMap<String, String> options() {
+ return options;
+ }
+
+ final InstrumentSpec getSpec() {
+ return new InstrumentSpec.Builder()
+ .instrumentClass(getClass())
+ .addAllOptions(options())
+ .build();
+ }
+
+ /**
+ * Defines the list of options applicable to this instrument. Implementations that use options
+ * will need to override this method.
+ */
+ protected ImmutableSet<String> instrumentOptions() {
+ return ImmutableSet.of();
+ }
+
+ /**
+ * Returns some arguments that should be added to the command line when invoking
+ * this instrument's worker.
+ *
+ * @param vmConfig the configuration for the VM on which this is running.
+ */
+ ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) {
+ return vmConfig.commonInstrumentVmArgs();
+ }
+
+ interface MeasurementCollectingVisitor extends LogMessageVisitor {
+ boolean isDoneCollecting();
+ boolean isWarmupComplete();
+ ImmutableList<Measurement> getMeasurements();
+ /**
+ * Returns all the messages created while collecting measurments.
+ *
+ * <p>A message is some piece of user visible data that should be displayed to the user along
+ * with the trial results.
+ *
+ * <p>TODO(lukes): should we model these as anything more than strings. These messages already
+ * have a concept of 'level' based on the prefix.
+ */
+ ImmutableList<String> getMessages();
+ }
+
+ /**
+ * A default implementation of {@link MeasurementCollectingVisitor} that collects measurements for
+ * pre-specified descriptions.
+ */
+ protected static final class DefaultMeasurementCollectingVisitor
+ extends AbstractLogMessageVisitor implements MeasurementCollectingVisitor {
+ static final int DEFAULT_NUMBER_OF_MEASUREMENTS = 9;
+ final ImmutableSet<String> requiredDescriptions;
+ final ListMultimap<String, Measurement> measurementsByDescription;
+ final int requiredMeasurements;
+
+ DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions) {
+ this(requiredDescriptions, DEFAULT_NUMBER_OF_MEASUREMENTS);
+ }
+
+ DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions,
+ int requiredMeasurements) {
+ this.requiredDescriptions = requiredDescriptions;
+ checkArgument(!requiredDescriptions.isEmpty());
+ this.requiredMeasurements = requiredMeasurements;
+ checkArgument(requiredMeasurements > 0);
+ this.measurementsByDescription =
+ ArrayListMultimap.create(requiredDescriptions.size(), requiredMeasurements);
+ }
+
+ @Override public void visit(StopMeasurementLogMessage logMessage) {
+ for (Measurement measurement : logMessage.measurements()) {
+ measurementsByDescription.put(measurement.description(), measurement);
+ }
+ }
+
+ @Override public boolean isDoneCollecting() {
+ for (String description : requiredDescriptions) {
+ if (measurementsByDescription.get(description).size() < requiredMeasurements) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isWarmupComplete() {
+ return true;
+ }
+
+ @Override public ImmutableList<Measurement> getMeasurements() {
+ return ImmutableList.copyOf(measurementsByDescription.values());
+ }
+
+ @Override public ImmutableList<String> getMessages() {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java
new file mode 100644
index 0000000..55157e7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import dagger.Component;
+
+/**
+ * Component to use to inject values into an {@link Instrument}.
+ */
+@Component(modules = InstrumentInjectorModule.class)
+interface InstrumentComponent {
+
+ void injectInstrument(Instrument instrument);
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java
new file mode 100644
index 0000000..dc159e3
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.config.InstrumentConfig;
+import com.google.common.collect.ImmutableMap;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * The set of bindings available for injecting into {@link Instrument} instances.
+ */
+@Module
+final class InstrumentInjectorModule {
+
+ private final InstrumentConfig instrumentConfig;
+
+ private final String instrumentName;
+
+ InstrumentInjectorModule(InstrumentConfig instrumentConfig, String instrumentName) {
+ this.instrumentConfig = instrumentConfig;
+ this.instrumentName = instrumentName;
+ }
+
+ @Provides
+ InstrumentConfig provideInstrumentConfig() {
+ return instrumentConfig;
+ }
+
+ @Provides
+ @InstrumentOptions
+ static ImmutableMap<String, String> provideInstrumentOptions(InstrumentConfig config) {
+ return config.options();
+ }
+
+ @Provides
+ @InstrumentName
+ String provideInstrumentName() {
+ return instrumentName;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java
new file mode 100644
index 0000000..bef02c7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the name used to configure an instrument. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface InstrumentName {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java
new file mode 100644
index 0000000..d6f0681
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the options applied to an instrument. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface InstrumentOptions {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java b/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java
new file mode 100644
index 0000000..71aaec2
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+@SuppressWarnings("serial")
+public class InvalidBenchmarkException extends RuntimeException {
+ public InvalidBenchmarkException(String message, Object... args) {
+ super(String.format(message, fixArgs(args)));
+ }
+
+ private static Object[] fixArgs(Object[] args) {
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] instanceof Class) {
+ args[i] = ((Class<?>) args[i]).getSimpleName();
+ }
+ }
+ return args;
+ }
+
+ public void display(PrintWriter writer) {
+ writer.println(getMessage());
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java b/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java
new file mode 100644
index 0000000..83db165
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+@SuppressWarnings("serial")
+public class InvalidInstrumentException extends RuntimeException {
+ public InvalidInstrumentException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public void display(PrintWriter writer) {
+ printStackTrace(writer);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/JarFinder.java b/caliper/src/main/java/com/google/caliper/runner/JarFinder.java
new file mode 100644
index 0000000..174ef81
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/JarFinder.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2013 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.ClassPath;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+
+/**
+ * Scans the source of a {@link ClassLoader} and finds all jar files. This is a modified version
+ * of {@link ClassPath} that finds jars instead of resources.
+ */
+final class JarFinder {
+ private static final Logger logger = Logger.getLogger(JarFinder.class.getName());
+
+ /** Separator for the Class-Path manifest attribute value in jar files. */
+ private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR =
+ Splitter.on(' ').omitEmptyStrings();
+
+ /**
+ * Returns a list of jar files reachable from the given class loaders.
+ *
+ * <p>Currently only {@link URLClassLoader} and only {@code file://} urls are supported.
+ *
+ * @throws IOException if the attempt to read class path resources (jar files or directories)
+ * failed.
+ */
+ public static ImmutableSet<File> findJarFiles(ClassLoader first, ClassLoader... rest)
+ throws IOException {
+ Scanner scanner = new Scanner();
+ Map<URI, ClassLoader> map = Maps.newLinkedHashMap();
+ for (ClassLoader classLoader : Lists.asList(first, rest)) {
+ map.putAll(getClassPathEntries(classLoader));
+ }
+ for (Map.Entry<URI, ClassLoader> entry : map.entrySet()) {
+ scanner.scan(entry.getKey(), entry.getValue());
+ }
+ return scanner.jarFiles();
+ }
+
+ @VisibleForTesting static ImmutableMap<URI, ClassLoader> getClassPathEntries(
+ ClassLoader classloader) {
+ Map<URI, ClassLoader> entries = Maps.newLinkedHashMap();
+ // Search parent first, since it's the order ClassLoader#loadClass() uses.
+ ClassLoader parent = classloader.getParent();
+ if (parent != null) {
+ entries.putAll(getClassPathEntries(parent));
+ }
+ if (classloader instanceof URLClassLoader) {
+ URLClassLoader urlClassLoader = (URLClassLoader) classloader;
+ for (URL entry : urlClassLoader.getURLs()) {
+ URI uri;
+ try {
+ uri = entry.toURI();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ if (!entries.containsKey(uri)) {
+ entries.put(uri, classloader);
+ }
+ }
+ }
+ return ImmutableMap.copyOf(entries);
+ }
+
+ @VisibleForTesting static final class Scanner {
+ private final ImmutableSet.Builder<File> jarFiles = new ImmutableSet.Builder<File>();
+ private final Set<URI> scannedUris = Sets.newHashSet();
+
+ ImmutableSet<File> jarFiles() {
+ return jarFiles.build();
+ }
+
+ void scan(URI uri, ClassLoader classloader) throws IOException {
+ if (uri.getScheme().equals("file") && scannedUris.add(uri)) {
+ scanFrom(new File(uri), classloader);
+ }
+ }
+
+ @VisibleForTesting void scanFrom(File file, ClassLoader classloader)
+ throws IOException {
+ if (!file.exists()) {
+ return;
+ }
+ if (file.isDirectory()) {
+ scanDirectory(file, classloader);
+ } else {
+ scanJar(file, classloader);
+ }
+ }
+
+ private void scanDirectory(File directory, ClassLoader classloader) {
+ scanDirectory(directory, classloader, "");
+ }
+
+ private void scanDirectory(
+ File directory, ClassLoader classloader, String packagePrefix) {
+ for (File file : directory.listFiles()) {
+ String name = file.getName();
+ if (file.isDirectory()) {
+ scanDirectory(file, classloader, packagePrefix + name + "/");
+ }
+ // do we need to look for jars here?
+ }
+ }
+
+ private void scanJar(File file, ClassLoader classloader) throws IOException {
+ JarFile jarFile;
+ try {
+ jarFile = new JarFile(file);
+ } catch (IOException e) {
+ // Not a jar file
+ return;
+ }
+ jarFiles.add(file);
+ try {
+ for (URI uri : getClassPathFromManifest(file, jarFile.getManifest())) {
+ scan(uri, classloader);
+ }
+ } finally {
+ try {
+ jarFile.close();
+ } catch (IOException ignored) {}
+ }
+ }
+
+ /**
+ * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
+ * to <a
+ * href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
+ * JAR File Specification</a>. If {@code manifest} is null, it means the jar file has no
+ * manifest, and an empty set will be returned.
+ */
+ @VisibleForTesting static ImmutableSet<URI> getClassPathFromManifest(
+ File jarFile, @Nullable Manifest manifest) {
+ if (manifest == null) {
+ return ImmutableSet.of();
+ }
+ ImmutableSet.Builder<URI> builder = ImmutableSet.builder();
+ String classpathAttribute = manifest.getMainAttributes()
+ .getValue(Attributes.Name.CLASS_PATH.toString());
+ if (classpathAttribute != null) {
+ for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
+ URI uri;
+ try {
+ uri = getClassPathEntry(jarFile, path);
+ } catch (URISyntaxException e) {
+ // Ignore bad entry
+ logger.warning("Invalid Class-Path entry: " + path);
+ continue;
+ }
+ builder.add(uri);
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Returns the absolute uri of the Class-Path entry value as specified in
+ * <a
+ * href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
+ * JAR File Specification</a>. Even though the specification only talks about relative urls,
+ * absolute urls are actually supported too (for example, in Maven surefire plugin).
+ */
+ @VisibleForTesting static URI getClassPathEntry(File jarFile, String path)
+ throws URISyntaxException {
+ URI uri = new URI(path);
+ return uri.isAbsolute()
+ ? uri
+ : new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/LocalPort.java b/caliper/src/main/java/com/google/caliper/runner/LocalPort.java
new file mode 100644
index 0000000..08f7314
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/LocalPort.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the port to which the worker has bound. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface LocalPort {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/MainComponent.java b/caliper/src/main/java/com/google/caliper/runner/MainComponent.java
new file mode 100644
index 0000000..22bcd75
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/MainComponent.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.bridge.BridgeModule;
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.ConfigModule;
+import com.google.caliper.json.GsonModule;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.options.OptionsModule;
+import com.google.caliper.util.OutputModule;
+import com.google.common.util.concurrent.ServiceManager;
+
+import dagger.Component;
+
+import javax.inject.Singleton;
+
+/**
+ * The main component used when running caliper.
+ */
+@Singleton
+@Component(modules = {
+ BenchmarkClassModule.class,
+ BridgeModule.class,
+ ConfigModule.class,
+ ExperimentingRunnerModule.class,
+ GsonModule.class,
+ MainModule.class,
+ OptionsModule.class,
+ OutputModule.class,
+ PlatformModule.class,
+ RunnerModule.class,
+ ServiceModule.class,
+})
+interface MainComponent {
+
+ BenchmarkClass getBenchmarkClass();
+
+ CaliperConfig getCaliperConfig();
+
+ CaliperOptions getCaliperOptions();
+
+ CaliperRun getCaliperRun();
+
+ ServiceManager getServiceManager();
+
+ TrialScopeComponent newTrialComponent(TrialModule trialModule);
+
+ ExperimentComponent newExperimentComponent(ExperimentModule experimentModule);
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/MainModule.java b/caliper/src/main/java/com/google/caliper/runner/MainModule.java
new file mode 100644
index 0000000..86ab6f9
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/MainModule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.Util;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Singleton;
+
+/**
+ * Provides bindings to integrate other modules into the {@link MainComponent}.
+ */
+@Module
+class MainModule {
+
+ private static Class<?> benchmarkClassForName(String className)
+ throws InvalidCommandException, UserCodeException {
+ try {
+ return Util.lenientClassForName(className);
+ } catch (ClassNotFoundException e) {
+ throw new InvalidCommandException("Benchmark class not found: " + className);
+ } catch (ExceptionInInitializerError e) {
+ throw new UserCodeException(
+ "Exception thrown while initializing class '" + className + "'", e.getCause());
+ } catch (NoClassDefFoundError e) {
+ throw new UserCodeException("Unable to load " + className, e);
+ }
+ }
+
+ @Provides
+ @Singleton
+ @Running.BenchmarkClass
+ static Class<?> provideBenchmarkClass(CaliperOptions options) {
+ return benchmarkClassForName(options.benchmarkClassName());
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java
new file mode 100644
index 0000000..737b0c7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the granularity of {@link System#nanoTime()}. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface NanoTimeGranularity {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java
new file mode 100644
index 0000000..ee135bf
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.math.RoundingMode.CEILING;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.caliper.util.ShortDuration;
+import com.google.common.base.Ticker;
+import com.google.common.math.LongMath;
+
+/**
+ * A utility that calculates the finest granularity that can be expected from subsequent calls to
+ * {@link System#nanoTime()}. Note that this utility necessarily invokes {@link System#nanoTime()}
+ * directly rather than using {@link Ticker} because the extra indirection might cause additional
+ * overhead.
+ */
+final class NanoTimeGranularityTester {
+ private static final int TRIALS = 1000;
+
+ ShortDuration testNanoTimeGranularity() {
+ long total = 0L;
+ for (int i = 0; i < TRIALS; i++) {
+ long first = System.nanoTime();
+ long second = System.nanoTime();
+ long third = System.nanoTime();
+ long fourth = System.nanoTime();
+ long fifth = System.nanoTime();
+ long sixth = System.nanoTime();
+ long seventh = System.nanoTime();
+ long eighth = System.nanoTime();
+ long ninth = System.nanoTime();
+ total += second - first;
+ total += third - second;
+ total += fourth - third;
+ total += fifth - fourth;
+ total += sixth - fifth;
+ total += seventh - sixth;
+ total += eighth - seventh;
+ total += ninth - eighth;
+ }
+ return ShortDuration.of(LongMath.divide(total, TRIALS * 8, CEILING), NANOSECONDS);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java b/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java
new file mode 100644
index 0000000..6a9e892
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.logging.Level.SEVERE;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.config.ResultProcessorConfig;
+import com.google.caliper.model.Run;
+import com.google.caliper.model.Trial;
+import com.google.caliper.options.CaliperDirectory;
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.stream.JsonWriter;
+
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+/**
+ * {@link ResultProcessor} that dumps the output data to a file in JSON format. By default, the
+ * output will be dumped to a file called
+ * {@code ~/.caliper/results/[benchmark classname].[timestamp].json}; if it exists and is a file,
+ * the file will be overwritten. The location can be overridden as either a file or a directory
+ * using either the {@code file} or {@code dir} options respectively.
+ */
+final class OutputFileDumper implements ResultProcessor {
+ private static final Logger logger = Logger.getLogger(OutputFileDumper.class.getName());
+
+ private final Run run;
+ private final Gson gson;
+ private final File resultFile;
+ private final File workFile;
+
+ private Optional<JsonWriter> writer = Optional.absent();
+
+ @Inject OutputFileDumper(Run run,
+ BenchmarkClass benchmarkClass,
+ Gson gson,
+ CaliperConfig caliperConfig,
+ @CaliperDirectory File caliperDirectory) throws InvalidConfigurationException {
+ this.run = run;
+ ResultProcessorConfig config = caliperConfig.getResultProcessorConfig(OutputFileDumper.class);
+ if (config.options().containsKey("file")) {
+ this.resultFile = new File(config.options().get("file"));
+ logger.finer("found an output file in the configuration");
+ } else if (config.options().containsKey("dir")) {
+ File dir = new File(config.options().get("dir"));
+ if (dir.isFile()) {
+ throw new InvalidConfigurationException("specified a directory, but it's a file");
+ }
+ this.resultFile = new File(dir, createFileName(benchmarkClass.name()));
+ logger.finer("found an output directory in the configuration");
+ } else {
+ this.resultFile =
+ new File(new File(caliperDirectory, "results"), createFileName(benchmarkClass.name()));
+ logger.fine("found no configuration");
+ }
+ logger.fine(String.format("using %s for results", resultFile));
+ this.gson = gson;
+ this.workFile = new File(resultFile.getPath() + ".tmp");
+ }
+
+ private String createFileName(String benchmarkName) {
+ return String.format("%s.%s.json", benchmarkName, createTimestamp());
+ }
+
+ private String createTimestamp() {
+ return ISODateTimeFormat.dateTimeNoMillis().print(run.startTime());
+ }
+
+ @Override public void processTrial(Trial trial) {
+ if (!writer.isPresent()) {
+ try {
+ Files.createParentDirs(workFile);
+ JsonWriter writer =
+ new JsonWriter(new OutputStreamWriter(new FileOutputStream(workFile), Charsets.UTF_8));
+ writer.setIndent(" "); // always pretty print
+ writer.beginArray();
+ this.writer = Optional.of(writer);
+ } catch (IOException e) {
+ logger.log(SEVERE, String.format(
+ "An error occured writing trial %s. Results in %s will be incomplete.", trial.id(),
+ resultFile), e);
+ }
+ }
+ if (writer.isPresent()) {
+ gson.toJson(trial, Trial.class, writer.get());
+ }
+ }
+
+ @Override public void close() throws IOException {
+ if (writer.isPresent()) {
+ writer.get().endArray().close();
+ }
+ if (workFile.exists()) {
+ Files.move(workFile, resultFile);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/Parameter.java b/caliper/src/main/java/com/google/caliper/runner/Parameter.java
new file mode 100644
index 0000000..25d9484
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/Parameter.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.Param;
+import com.google.caliper.util.Parser;
+import com.google.caliper.util.Parsers;
+import com.google.caliper.util.Util;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Primitives;
+
+import java.lang.reflect.Field;
+import java.text.ParseException;
+
+/**
+ * Represents an injectable parameter, marked with one of @Param, @VmParam. Has nothing to do with
+ * particular choices of <i>values</i> for this parameter (except that it knows how to find the
+ * <i>default</i> values).
+ */
+public final class Parameter {
+ public static Parameter create(Field field) throws InvalidBenchmarkException {
+ return new Parameter(field);
+ }
+
+ private final Field field;
+ private final Parser<?> parser;
+ private final ImmutableList<String> defaults;
+
+ public Parameter(Field field) throws InvalidBenchmarkException {
+ if (Util.isStatic(field)) {
+ throw new InvalidBenchmarkException("Parameter field '%s' must not be static",
+ field.getName());
+ }
+ if (RESERVED_NAMES.contains(field.getName())) {
+ throw new InvalidBenchmarkException("Class '%s' uses reserved parameter name '%s'",
+ field.getDeclaringClass(), field.getName());
+ }
+
+ this.field = field;
+ field.setAccessible(true);
+
+ Class<?> type = Primitives.wrap(field.getType());
+ try {
+ this.parser = Parsers.conventionalParser(type);
+ } catch (NoSuchMethodException e) {
+ throw new InvalidBenchmarkException("Type '%s' of parameter field '%s' has no recognized "
+ + "String-converting method; see <TODO> for details", type, field.getName());
+ }
+
+ this.defaults = findDefaults(field);
+ validate(defaults);
+ }
+
+ void validate(ImmutableCollection<String> values) throws InvalidBenchmarkException {
+ for (String valueAsString : values) {
+ try {
+ parser.parse(valueAsString);
+ } catch (ParseException e) {
+ throw new InvalidBenchmarkException(
+ "Cannot convert value '%s' to type '%s': %s",
+ valueAsString, field.getType(), e.getMessage());
+ }
+ }
+ }
+
+ static final ImmutableSet<String> RESERVED_NAMES = ImmutableSet.of(
+ "benchmark",
+ "environment",
+ "instrument",
+ "measurement", // e.g. runtime, allocation, etc.
+ "run",
+ "trial", // currently unused, but we might need it
+ "vm");
+
+ String name() {
+ return field.getName();
+ }
+
+ ImmutableList<String> defaults() {
+ return defaults;
+ }
+
+ void inject(Object benchmark, String value) {
+ try {
+ Object o = parser.parse(value);
+ field.set(benchmark, o);
+ } catch (ParseException impossible) {
+ // already validated both defaults and command-line
+ throw new AssertionError(impossible);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ }
+ }
+
+ private static ImmutableList<String> findDefaults(Field field) {
+ String[] defaultsAsStrings = field.getAnnotation(Param.class).value();
+ if (defaultsAsStrings.length > 0) {
+ return ImmutableList.copyOf(defaultsAsStrings);
+ }
+
+ Class<?> type = field.getType();
+ if (type == boolean.class) {
+ return ALL_BOOLEANS;
+ }
+
+ if (type.isEnum()) {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ for (Object enumConstant : type.getEnumConstants()) {
+ builder.add(enumConstant.toString());
+ }
+ return builder.build();
+ }
+ return ImmutableList.of();
+ }
+
+ private static final ImmutableList<String> ALL_BOOLEANS = ImmutableList.of("true", "false");
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java b/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java
new file mode 100644
index 0000000..640e52f
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Sets;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents all the injectable parameter fields of a single kind (@Param or @VmParam) found in a
+ * benchmark class. Has nothing to do with particular choices of <i>values</i> for these parameters
+ * (except that it knows how to find the <i>default</i> values).
+ */
+public final class ParameterSet {
+ public static ParameterSet create(Class<?> theClass, Class<? extends Annotation> annotationClass)
+ throws InvalidBenchmarkException {
+ // deterministic order, not reflection order
+ ImmutableMap.Builder<String, Parameter> parametersBuilder =
+ ImmutableSortedMap.naturalOrder();
+
+ for (Field field : theClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(annotationClass)) {
+ Parameter parameter = Parameter.create(field);
+ parametersBuilder.put(field.getName(), parameter);
+ }
+ }
+ return new ParameterSet(parametersBuilder.build());
+ }
+
+ final ImmutableMap<String, Parameter> map;
+
+ private ParameterSet(ImmutableMap<String, Parameter> map) {
+ this.map = map;
+ }
+
+ public Set<String> names() {
+ return map.keySet();
+ }
+
+ public Parameter get(String name) {
+ return map.get(name);
+ }
+
+ public ImmutableSetMultimap<String, String> fillInDefaultsFor(
+ ImmutableSetMultimap<String, String> explicitValues) throws InvalidBenchmarkException {
+ ImmutableSetMultimap.Builder<String, String> combined = ImmutableSetMultimap.builder();
+
+ // For user parameters, this'll actually be the same as fromClass.keySet(), since any extras
+ // given at the command line are treated as errors; for VM parameters this is not the case.
+ for (String name : Sets.union(map.keySet(), explicitValues.keySet())) {
+ Parameter parameter = map.get(name);
+ ImmutableCollection<String> values = explicitValues.containsKey(name)
+ ? explicitValues.get(name)
+ : parameter.defaults();
+
+ combined.putAll(name, values);
+ if (values.isEmpty()) {
+ throw new InvalidBenchmarkException("ERROR: No default value provided for " + name);
+ }
+ }
+ return combined.orderKeysBy(Ordering.natural()).build();
+ }
+
+ public void injectAll(Object benchmark, Map<String, String> actualValues) {
+ for (Parameter parameter : map.values()) {
+ String value = actualValues.get(parameter.name());
+ parameter.inject(benchmark, value);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java b/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java
new file mode 100644
index 0000000..2e61989
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.dalvik.DalvikModule;
+import com.google.caliper.platform.dalvik.DalvikPlatform;
+import com.google.caliper.platform.jvm.JvmModule;
+import com.google.caliper.platform.jvm.JvmPlatform;
+import com.google.common.base.Optional;
+
+import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Provider;
+
+/**
+ * Provider of a {@link Platform} instance appropriate for the current platform.
+ */
+@Module(includes = {JvmModule.class, DalvikModule.class})
+public final class PlatformModule {
+
+ /**
+ * Chooses the {@link DalvikPlatform} if available, otherwise uses the default
+ * {@link JvmPlatform}.
+ */
+ @Provides
+ static Platform providePlatform(
+ Optional<DalvikPlatform> optionalDalvikPlatform,
+ Provider<JvmPlatform> jvmPlatformProvider) {
+ if (optionalDalvikPlatform.isPresent()) {
+ return optionalDalvikPlatform.get();
+ } else {
+ return jvmPlatformProvider.get();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java b/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java
new file mode 100644
index 0000000..7b814d0
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+
+
+/**
+ * An exception created on the runner with the same stack trace as one thrown on the worker that
+ * reports the actual exception class and message in its message.
+ */
+final class ProxyWorkerException extends RuntimeException {
+ ProxyWorkerException(String stackTrace) {
+ super(formatMesssage(stackTrace));
+ }
+
+ private static String formatMesssage(String stackTrace) {
+ StringBuilder builder = new StringBuilder(stackTrace.length() + 512)
+ .append("An exception occurred in a worker process. The stack trace is as follows:\n\t");
+ Joiner.on("\n\t").appendTo(builder, Splitter.on('\n').split(stackTrace));
+ return builder.toString();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java b/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java
new file mode 100644
index 0000000..fc278c5
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.api.ResultProcessor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Responsible for creating instances of configured {@link ResultProcessor}.
+ */
+final class ResultProcessorCreator {
+
+ public static final String NO_PUBLIC_DEFAULT_CONSTRUCTOR =
+ "ResultProcessor %s not supported as it does not have a public default constructor";
+
+ private ResultProcessorCreator() {
+ }
+
+ static ResultProcessor createResultProcessor(Class<? extends ResultProcessor> processorClass) {
+ ResultProcessor resultProcessor;
+
+ try {
+ Constructor<? extends ResultProcessor> constructor = processorClass.getConstructor();
+ resultProcessor = constructor.newInstance();
+ } catch (NoSuchMethodException e) {
+ throw new UserCodeException(String.format(NO_PUBLIC_DEFAULT_CONSTRUCTOR, processorClass), e);
+ } catch (InvocationTargetException e) {
+ throw new UserCodeException("ResultProcessor %s could not be instantiated", e.getCause());
+ } catch (InstantiationException e) {
+ throw new UserCodeException("ResultProcessor %s could not be instantiated", e);
+ } catch (IllegalAccessException e) {
+ throw new UserCodeException("ResultProcessor %s could not be instantiated", e);
+ }
+
+ return resultProcessor;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java b/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java
new file mode 100644
index 0000000..8e654d5
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.logging.Level.SEVERE;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.config.ResultProcessorConfig;
+import com.google.caliper.model.Trial;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.Gson;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+
+/**
+ * {@link ResultProcessor} implementation that uploads the JSON-serialized results to the Caliper
+ * webapp.
+ */
+abstract class ResultsUploader implements ResultProcessor {
+ private static final Logger logger = Logger.getLogger(ResultsUploader.class.getName());
+ private static final String POST_PATH = "/data/trials";
+ private static final String RESULTS_PATH_PATTERN = "/runs/%s";
+
+ private final PrintWriter stdout;
+ private final Client client;
+ private final Gson gson;
+ private final Optional<UUID> apiKey;
+ private final Optional<URI> uploadUri;
+ private Optional<UUID> runId = Optional.absent();
+ private boolean failure = false;
+
+ ResultsUploader(PrintWriter stdout, Gson gson, Client client,
+ ResultProcessorConfig resultProcessorConfig) throws InvalidConfigurationException {
+ this.stdout = stdout;
+ this.client = client;
+ this.gson = gson;
+ @Nullable String apiKeyString = resultProcessorConfig.options().get("key");
+ Optional<UUID> apiKey = Optional.absent();
+ if (Strings.isNullOrEmpty(apiKeyString)) {
+ logger.info("No api key specified. Uploading results anonymously.");
+ } else {
+ try {
+ apiKey = Optional.of(UUID.fromString(apiKeyString));
+ } catch (IllegalArgumentException e) {
+ throw new InvalidConfigurationException(String.format(
+ "The specified API key (%s) is not valid. API keys are UUIDs and should look like %s.",
+ apiKeyString, new UUID(0L, 0L)));
+ }
+ }
+ this.apiKey = apiKey;
+
+ @Nullable String urlString = resultProcessorConfig.options().get("url");
+ if (Strings.isNullOrEmpty(urlString)) {
+ logger.info("No upload URL was specified. Results will not be uploaded.");
+ this.uploadUri = Optional.absent();
+ } else {
+ try {
+ this.uploadUri = Optional.of(new URI(urlString).resolve(POST_PATH));
+ } catch (URISyntaxException e) {
+ throw new InvalidConfigurationException(urlString + " is an invalid upload url", e);
+ }
+ }
+ }
+
+ @Override public final void processTrial(Trial trial) {
+ if (uploadUri.isPresent()) {
+ WebResource resource = client.resource(uploadUri.get());
+ if (apiKey.isPresent()) {
+ resource = resource.queryParam("key", apiKey.get().toString());
+ }
+ boolean threw = true;
+ try {
+ // TODO(gak): make the json part happen automagically
+ resource.type(APPLICATION_JSON_TYPE).post(gson.toJson(ImmutableList.of(trial)));
+ // only set the run id if a result has been successfully uploaded
+ runId = Optional.of(trial.run().id());
+ threw = false;
+ } catch (ClientHandlerException e) {
+ logUploadFailure(trial, e);
+ } catch (UniformInterfaceException e) {
+ logUploadFailure(trial, e);
+ logger.fine("Failed upload response: " + e.getResponse().getStatus());
+ } finally {
+ failure |= threw;
+ }
+ }
+ }
+
+ private static void logUploadFailure(Trial trial, Exception e) {
+ logger.log(SEVERE, String.format(
+ "Could not upload trial %s. Consider uploading it manually.", trial.id()), e);
+ }
+
+ @Override public final void close() {
+ if (uploadUri.isPresent()) {
+ if (runId.isPresent()) {
+ stdout.printf("Results have been uploaded. View them at: %s%n",
+ uploadUri.get().resolve(String.format(RESULTS_PATH_PATTERN, runId.get())));
+ }
+ if (failure) {
+ // TODO(gak): implement some retry
+ stdout.println("Some trials failed to upload. Consider uploading them manually.");
+ }
+ } else {
+ logger.fine("No upload URL was provided, so results were not uploaded.");
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java b/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java
new file mode 100644
index 0000000..a55fb03
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.model.Run;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.platform.Platform;
+import com.google.common.collect.ImmutableSet;
+
+import dagger.Module;
+import dagger.Provides;
+
+import org.joda.time.Instant;
+
+import java.util.UUID;
+
+import javax.inject.Singleton;
+
+/**
+ * A Dagger module that configures bindings common to all {@link CaliperRun} implementations.
+ */
+// TODO(gak): throwing providers for all of the things that throw
+@Module
+final class RunnerModule {
+ @Provides
+ static ImmutableSet<VirtualMachine> provideVirtualMachines(
+ CaliperOptions options,
+ CaliperConfig config,
+ Platform platform)
+ throws InvalidConfigurationException {
+ ImmutableSet<String> vmNames = options.vmNames();
+ ImmutableSet.Builder<VirtualMachine> builder = ImmutableSet.builder();
+ if (vmNames.isEmpty()) {
+ builder.add(new VirtualMachine("default", config.getDefaultVmConfig(platform)));
+ } else {
+ for (String vmName : vmNames) {
+ VmConfig vmConfig = config.getVmConfig(platform, vmName);
+ builder.add(new VirtualMachine(vmName, vmConfig));
+ }
+ }
+ return builder.build();
+ }
+
+ @Provides
+ static Instant provideInstant() {
+ return Instant.now();
+ }
+
+ @Provides static CaliperRun provideCaliperRun(ExperimentingCaliperRun experimentingCaliperRun) {
+ return experimentingCaliperRun;
+ }
+
+ @Provides @Singleton
+ static Run provideRun(UUID uuid, CaliperOptions caliperOptions, Instant startTime) {
+ return new Run.Builder(uuid).label(caliperOptions.runName()).startTime(startTime).build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/Running.java b/caliper/src/main/java/com/google/caliper/runner/Running.java
new file mode 100644
index 0000000..0ff4c7e
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/Running.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * A collection of annotations for bindings pertaining to the currently running experiment.
+ */
+public class Running {
+ private Running() {}
+
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @Qualifier
+ public @interface Benchmark {}
+
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @Qualifier
+ public @interface BenchmarkMethod {}
+
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @Qualifier
+ public @interface BenchmarkClass {}
+
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @Qualifier
+ public @interface BeforeExperimentMethods {}
+
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @Qualifier
+ public @interface AfterExperimentMethods {}
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java b/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java
new file mode 100644
index 0000000..3187dfd
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.caliper.runner.CommonInstrumentOptions.GC_BEFORE_EACH_OPTION;
+import static com.google.caliper.runner.CommonInstrumentOptions.MAX_WARMUP_WALL_TIME_OPTION;
+import static com.google.caliper.runner.CommonInstrumentOptions.MEASUREMENTS_OPTION;
+import static com.google.caliper.runner.CommonInstrumentOptions.WARMUP_OPTION;
+import static com.google.caliper.util.Reflection.getAnnotatedMethods;
+import static com.google.caliper.util.Util.isStatic;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Throwables.propagateIfInstanceOf;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.api.AfterRep;
+import com.google.caliper.api.BeforeRep;
+import com.google.caliper.api.Macrobenchmark;
+import com.google.caliper.api.SkipThisScenarioException;
+import com.google.caliper.bridge.AbstractLogMessageVisitor;
+import com.google.caliper.bridge.GcLogMessage;
+import com.google.caliper.bridge.HotspotLogMessage;
+import com.google.caliper.bridge.StartMeasurementLogMessage;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.SupportedPlatform;
+import com.google.caliper.util.ShortDuration;
+import com.google.caliper.worker.MacrobenchmarkWorker;
+import com.google.caliper.worker.RuntimeWorker;
+import com.google.caliper.worker.Worker;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+/**
+ * The instrument responsible for measuring the runtime of {@link Benchmark} methods.
+ */
+@SupportedPlatform({Platform.Type.JVM, Platform.Type.DALVIK})
+class RuntimeInstrument extends Instrument {
+ private static final String SUGGEST_GRANULARITY_OPTION = "suggestGranularity";
+ private static final String TIMING_INTERVAL_OPTION = "timingInterval";
+ private static final int DRY_RUN_REPS = 1;
+
+ private static final Logger logger = Logger.getLogger(RuntimeInstrument.class.getName());
+
+ private final ShortDuration nanoTimeGranularity;
+
+ @Inject
+ RuntimeInstrument(@NanoTimeGranularity ShortDuration nanoTimeGranularity) {
+ this.nanoTimeGranularity = nanoTimeGranularity;
+ }
+
+ @Override
+ public boolean isBenchmarkMethod(Method method) {
+ return method.isAnnotationPresent(Benchmark.class)
+ || BenchmarkMethods.isTimeMethod(method)
+ || method.isAnnotationPresent(Macrobenchmark.class);
+ }
+
+ @Override
+ protected ImmutableSet<String> instrumentOptions() {
+ return ImmutableSet.of(
+ WARMUP_OPTION, MAX_WARMUP_WALL_TIME_OPTION, TIMING_INTERVAL_OPTION, MEASUREMENTS_OPTION,
+ GC_BEFORE_EACH_OPTION, SUGGEST_GRANULARITY_OPTION);
+ }
+
+ @Override
+ public Instrumentation createInstrumentation(Method benchmarkMethod)
+ throws InvalidBenchmarkException {
+ checkNotNull(benchmarkMethod);
+ checkArgument(isBenchmarkMethod(benchmarkMethod));
+ if (isStatic(benchmarkMethod)) {
+ throw new InvalidBenchmarkException("Benchmark methods must not be static: %s",
+ benchmarkMethod.getName());
+ }
+ try {
+ switch (BenchmarkMethods.Type.of(benchmarkMethod)) {
+ case MACRO:
+ return new MacrobenchmarkInstrumentation(benchmarkMethod);
+ case MICRO:
+ return new MicrobenchmarkInstrumentation(benchmarkMethod);
+ case PICO:
+ return new PicobenchmarkInstrumentation(benchmarkMethod);
+ default:
+ throw new AssertionError("unknown type");
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBenchmarkException("Benchmark methods must have no arguments or accept "
+ + "a single int or long parameter: %s", benchmarkMethod.getName());
+ }
+ }
+
+ private class MacrobenchmarkInstrumentation extends Instrumentation {
+ MacrobenchmarkInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override
+ public void dryRun(Object benchmark) throws UserCodeException {
+ ImmutableSet<Method> beforeRepMethods =
+ getAnnotatedMethods(benchmarkMethod.getDeclaringClass(), BeforeRep.class);
+ ImmutableSet<Method> afterRepMethods =
+ getAnnotatedMethods(benchmarkMethod.getDeclaringClass(), AfterRep.class);
+ try {
+ for (Method beforeRepMethod : beforeRepMethods) {
+ beforeRepMethod.invoke(benchmark);
+ }
+ try {
+ benchmarkMethod.invoke(benchmark);
+ } finally {
+ for (Method afterRepMethod : afterRepMethods) {
+ afterRepMethod.invoke(benchmark);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ propagateIfInstanceOf(userException, SkipThisScenarioException.class);
+ throw new UserCodeException(userException);
+ }
+ }
+
+ @Override
+ public Class<? extends Worker> workerClass() {
+ return MacrobenchmarkWorker.class;
+ }
+
+ @Override
+ MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ return new SingleInvocationMeasurementCollector(
+ Integer.parseInt(options.get(MEASUREMENTS_OPTION)),
+ ShortDuration.valueOf(options.get(WARMUP_OPTION)),
+ ShortDuration.valueOf(options.get(MAX_WARMUP_WALL_TIME_OPTION)),
+ nanoTimeGranularity);
+ }
+ }
+
+ @Override public TrialSchedulingPolicy schedulingPolicy() {
+ // Runtime measurements are currently believed to be too sensitive to system performance to
+ // allow parallel runners.
+ // TODO(lukes): investigate this, on a multicore system it seems like we should be able to
+ // allow some parallelism without corrupting results.
+ return TrialSchedulingPolicy.SERIAL;
+ }
+
+ private abstract class RuntimeInstrumentation extends Instrumentation {
+ RuntimeInstrumentation(Method method) {
+ super(method);
+ }
+
+ @Override public void dryRun(Object benchmark) throws UserCodeException {
+ try {
+ benchmarkMethod.invoke(benchmark, DRY_RUN_REPS);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible);
+ } catch (InvocationTargetException e) {
+ Throwable userException = e.getCause();
+ propagateIfInstanceOf(userException, SkipThisScenarioException.class);
+ throw new UserCodeException(userException);
+ }
+ }
+
+ @Override public ImmutableMap<String, String> workerOptions() {
+ return ImmutableMap.of(
+ TIMING_INTERVAL_OPTION + "Nanos", toNanosString(TIMING_INTERVAL_OPTION),
+ GC_BEFORE_EACH_OPTION, options.get(GC_BEFORE_EACH_OPTION));
+ }
+
+ private String toNanosString(String optionName) {
+ return String.valueOf(
+ ShortDuration.valueOf(options.get(optionName)).to(NANOSECONDS));
+ }
+
+ @Override MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ return new RepBasedMeasurementCollector(
+ getMeasurementsPerTrial(),
+ ShortDuration.valueOf(options.get(WARMUP_OPTION)),
+ ShortDuration.valueOf(options.get(MAX_WARMUP_WALL_TIME_OPTION)),
+ Boolean.parseBoolean(options.get(SUGGEST_GRANULARITY_OPTION)),
+ nanoTimeGranularity);
+ }
+ }
+
+ private class MicrobenchmarkInstrumentation extends RuntimeInstrumentation {
+ MicrobenchmarkInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override public Class<? extends Worker> workerClass() {
+ return RuntimeWorker.Micro.class;
+ }
+ }
+
+ private int getMeasurementsPerTrial() {
+ @Nullable
+ String measurementsString = options.get(MEASUREMENTS_OPTION);
+ int measurementsPerTrial =
+ (measurementsString == null) ? 1 : Integer.parseInt(measurementsString);
+ // TODO(gak): fail faster
+ checkState(measurementsPerTrial > 0);
+ return measurementsPerTrial;
+ }
+
+ private class PicobenchmarkInstrumentation extends RuntimeInstrumentation {
+ PicobenchmarkInstrumentation(Method benchmarkMethod) {
+ super(benchmarkMethod);
+ }
+
+ @Override public Class<? extends Worker> workerClass() {
+ return RuntimeWorker.Pico.class;
+ }
+ }
+
+ private abstract static class RuntimeMeasurementCollector extends AbstractLogMessageVisitor
+ implements MeasurementCollectingVisitor {
+ final int targetMeasurements;
+ final ShortDuration warmup;
+ final ShortDuration maxWarmupWallTime;
+ final List<Measurement> measurements = Lists.newArrayList();
+ ShortDuration elapsedWarmup = ShortDuration.zero();
+ boolean measuring = false;
+ boolean invalidateMeasurements = false;
+ boolean notifiedAboutGc = false;
+ boolean notifiedAboutJit = false;
+ boolean notifiedAboutMeasuringJit = false;
+ Stopwatch timeSinceStartOfTrial = Stopwatch.createUnstarted();
+ final List<String> messages = Lists.newArrayList();
+ final ShortDuration nanoTimeGranularity;
+
+ RuntimeMeasurementCollector(
+ int targetMeasurements,
+ ShortDuration warmup,
+ ShortDuration maxWarmupWallTime,
+ ShortDuration nanoTimeGranularity) {
+ this.targetMeasurements = targetMeasurements;
+ this.warmup = warmup;
+ this.maxWarmupWallTime = maxWarmupWallTime;
+ this.nanoTimeGranularity = nanoTimeGranularity;
+ }
+
+ @Override
+ public void visit(GcLogMessage logMessage) {
+ if (measuring && isWarmupComplete() && !notifiedAboutGc) {
+ gcWhileMeasuring();
+ notifiedAboutGc = true;
+ }
+ }
+
+ abstract void gcWhileMeasuring();
+
+ @Override
+ public void visit(HotspotLogMessage logMessage) {
+ if (isWarmupComplete()) {
+ if (measuring && notifiedAboutMeasuringJit) {
+ hotspotWhileMeasuring();
+ notifiedAboutMeasuringJit = true;
+ } else if (notifiedAboutJit) {
+ hotspotWhileNotMeasuring();
+ notifiedAboutJit = true;
+ }
+ }
+ }
+
+ abstract void hotspotWhileMeasuring();
+
+ abstract void hotspotWhileNotMeasuring();
+
+ @Override
+ public void visit(StartMeasurementLogMessage logMessage) {
+ checkState(!measuring);
+ measuring = true;
+ if (!timeSinceStartOfTrial.isRunning()) {
+ timeSinceStartOfTrial.start();
+ }
+ }
+
+ @Override
+ public void visit(StopMeasurementLogMessage logMessage) {
+ checkState(measuring);
+ ImmutableList<Measurement> newMeasurements = logMessage.measurements();
+ if (!isWarmupComplete()) {
+ for (Measurement measurement : newMeasurements) {
+ // TODO(gak): eventually we will need to resolve different units
+ checkArgument("ns".equals(measurement.value().unit()));
+ elapsedWarmup = elapsedWarmup.plus(
+ ShortDuration.of(BigDecimal.valueOf(measurement.value().magnitude()), NANOSECONDS));
+ validateMeasurement(measurement);
+ }
+ } else {
+ if (!measuredWarmupDurationReached()) {
+ messages.add(String.format(
+ "WARNING: Warmup was interrupted because it took longer than %s of wall-clock time. "
+ + "%s was spent in the benchmark method for warmup "
+ + "(normal warmup duration should be %s).",
+ maxWarmupWallTime, elapsedWarmup, warmup));
+ }
+
+ if (invalidateMeasurements) {
+ logger.fine(String.format("Discarding %s as they were marked invalid.", newMeasurements));
+ } else {
+ this.measurements.addAll(newMeasurements);
+ }
+ }
+ invalidateMeasurements = false;
+ measuring = false;
+ }
+
+ abstract void validateMeasurement(Measurement measurement);
+
+ @Override
+ public ImmutableList<Measurement> getMeasurements() {
+ return ImmutableList.copyOf(measurements);
+ }
+
+ boolean measuredWarmupDurationReached() {
+ return elapsedWarmup.compareTo(warmup) >= 0;
+ }
+
+ @Override
+ public boolean isWarmupComplete() {
+ // Fast macro-benchmarks (up to tens of ms) need lots of measurements to reach 10s of
+ // measured warmup time. Because of the per-measurement overhead of running @BeforeRep and
+ // @AfterRep, warmup can take very long.
+ //
+ // To prevent this, we enforce a cap on the wall-clock time here.
+ return measuredWarmupDurationReached()
+ || timeSinceStartOfTrial.elapsed(MILLISECONDS) > maxWarmupWallTime.to(MILLISECONDS);
+ }
+
+ @Override
+ public boolean isDoneCollecting() {
+ return measurements.size() >= targetMeasurements;
+ }
+
+ @Override
+ public ImmutableList<String> getMessages() {
+ return ImmutableList.copyOf(messages);
+ }
+ }
+
+ private static final class RepBasedMeasurementCollector extends RuntimeMeasurementCollector {
+ final boolean suggestGranularity;
+ boolean notifiedAboutGranularity = false;
+
+ RepBasedMeasurementCollector(
+ int measurementsPerTrial,
+ ShortDuration warmup,
+ ShortDuration maxWarmupWallTime,
+ boolean suggestGranularity,
+ ShortDuration nanoTimeGranularity) {
+ super(measurementsPerTrial, warmup, maxWarmupWallTime, nanoTimeGranularity);
+ this.suggestGranularity = suggestGranularity;
+ }
+
+ @Override
+ void gcWhileMeasuring() {
+ invalidateMeasurements = true;
+ messages.add("ERROR: GC occurred during timing. Measurements were discarded.");
+ }
+
+ @Override
+ void hotspotWhileMeasuring() {
+ invalidateMeasurements = true;
+ messages.add(
+ "ERROR: Hotspot compilation occurred during timing: warmup is likely insufficent. "
+ + "Measurements were discarded.");
+ }
+
+ @Override
+ void hotspotWhileNotMeasuring() {
+ messages.add(
+ "WARNING: Hotspot compilation occurred after warmup, but outside of timing. "
+ + "Results may be affected. Run with --verbose to see which method was compiled.");
+ }
+
+ @Override
+ void validateMeasurement(Measurement measurement) {
+ if (suggestGranularity) {
+ double nanos = measurement.value().magnitude() / measurement.weight();
+ if (!notifiedAboutGranularity && ((nanos / 1000) > nanoTimeGranularity.to(NANOSECONDS))) {
+ notifiedAboutGranularity = true;
+ ShortDuration reasonableUpperBound = nanoTimeGranularity.times(1000);
+ messages.add(String.format("INFO: This experiment does not require a microbenchmark. "
+ + "The granularity of the timer (%s) is less than 0.1%% of the measured runtime. "
+ + "If all experiments for this benchmark have runtimes greater than %s, "
+ + "consider the macrobenchmark instrument.", nanoTimeGranularity,
+ reasonableUpperBound));
+ }
+ }
+ }
+ }
+
+ private static final class SingleInvocationMeasurementCollector
+ extends RuntimeMeasurementCollector {
+
+ SingleInvocationMeasurementCollector(
+ int measurementsPerTrial,
+ ShortDuration warmup,
+ ShortDuration maxWarmupWallTime,
+ ShortDuration nanoTimeGranularity) {
+ super(measurementsPerTrial, warmup, maxWarmupWallTime, nanoTimeGranularity);
+ }
+
+ @Override
+ void gcWhileMeasuring() {
+ messages.add("WARNING: GC occurred during timing. "
+ + "Depending on the scope of the benchmark, this might significantly impact results. "
+ + "Consider running with a larger heap size.");
+ }
+
+ @Override
+ void hotspotWhileMeasuring() {
+ messages.add("WARNING: Hotspot compilation occurred during timing. "
+ + "Depending on the scope of the benchmark, this might significantly impact results. "
+ + "Consider running with a longer warmup.");
+ }
+
+ @Override
+ void hotspotWhileNotMeasuring() {
+ messages.add(
+ "WARNING: Hotspot compilation occurred after warmup, but outside of timing. "
+ + "Depending on the scope of the benchmark, this might significantly impact results. "
+ + "Consider running with a longer warmup.");
+ }
+
+ @Override
+ void validateMeasurement(Measurement measurement) {
+ double nanos = measurement.value().magnitude() / measurement.weight();
+ if ((nanos / 1000) < nanoTimeGranularity.to(NANOSECONDS)) {
+ ShortDuration runtime = ShortDuration.of(BigDecimal.valueOf(nanos), NANOSECONDS);
+ throw new TrialFailureException(String.format(
+ "This experiment requires a microbenchmark. "
+ + "The granularity of the timer (%s) "
+ + "is greater than 0.1%% of the measured runtime (%s). "
+ + "Use the microbenchmark instrument for accurate measurements.",
+ nanoTimeGranularity, runtime));
+ }
+ }
+ }
+}
+
diff --git a/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java b/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java
new file mode 100644
index 0000000..9caea04
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * A {@link ShutdownHookRegistrar} that delegates to {@link Runtime}.
+ */
+class RuntimeShutdownHookRegistrar implements ShutdownHookRegistrar {
+ @Override public void addShutdownHook(Thread hook) {
+ Runtime.getRuntime().addShutdownHook(hook);
+ }
+
+ @Override public boolean removeShutdownHook(Thread hook) {
+ return Runtime.getRuntime().removeShutdownHook(hook);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java b/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java
new file mode 100644
index 0000000..fda696b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+/**
+ * A ScheduledTrial is a simple pair of a {@link TrialRunLoop} and a
+ * {@link TrialSchedulingPolicy}.
+ */
+@TrialScoped final class ScheduledTrial {
+ private final TrialRunLoop runLoop;
+ private final Experiment experiment;
+ private final TrialSchedulingPolicy policy;
+
+ @Inject ScheduledTrial(Experiment experiment, TrialRunLoop runLoop,
+ TrialSchedulingPolicy policy) {
+ this.runLoop = runLoop;
+ this.experiment = experiment;
+ this.policy = policy;
+ }
+
+ TrialSchedulingPolicy policy() {
+ return policy;
+ }
+
+ Experiment experiment() {
+ return experiment;
+ }
+
+ Callable<TrialResult> trialTask() {
+ return runLoop;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java b/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java
new file mode 100644
index 0000000..ed93dd5
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.StartupAnnounceMessage;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.AbstractExecutionThreadService;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.Service;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A {@link Service} that manages a {@link ServerSocket}.
+ *
+ * <p> This service provides two pieces of functionality:
+ * <ol>
+ * <li>It adapts {@link ServerSocket#accept()} to a {@link ListenableFuture} of an opened socket.
+ * <li>It demultiplexes incoming connections based on a {@link StartupAnnounceMessage} that is
+ * sent over the socket.
+ * </ol>
+ *
+ * <p>The {@linkplain State states} of this service are as follows:
+ * <ul>
+ * <li>{@linkplain State#NEW NEW} : Idle state, the {@link ServerSocket} is not open yet.
+ * <li>{@linkplain State#STARTING STARTING} : {@link ServerSocket} is opened
+ * <li>{@linkplain State#RUNNING RUNNING} : We are continuously accepting and parsing connections
+ * from the socket.
+ * <li>{@linkplain State#STOPPING STOPPING} : The server socket is closing and all pending
+ * connection requests are terminated, connection requests will fail immediately.
+ * <li>{@linkplain State#TERMINATED TERMINATED} : Idle state, the socket is closed.
+ * <li>{@linkplain State#FAILED FAILED} : The service will transition to failed if it encounters
+ * any errors while accepting connections or reading from connections.
+ * </ul>
+ *
+ * <p>Note to future self. There have been a few attempts to make it so that it is no longer
+ * necessary to dedicate a thread to this service (basically turn it into an AbstractIdleService).
+ * The general idea has been to make callers to getConnection invoke accept, here is why it didn't
+ * work.
+ * <ul>
+ * <li>If you make getConnection a blocking method that calls accept until it finds the
+ * connection with its id, then there is no way to deal with connections that never arrive.
+ * For example, if the worker crashes before connecting then the thread calling accept will
+ * block forever waiting for it. The only way to unblock a thread stuck on accept() is to
+ * close the socket (this holds for ServerSocketChannels and normal ServerSockets), but we
+ * cannot do that in this case because the socket is a shared resource.
+ * <li>If you make getConnection a non-blocking, polling based method then you expose yourself
+ * to potential deadlocks (due to missed signals) depending on what thread you poll from.
+ * If the polling thread is any of the threads that are involved with processing messages
+ * from the worker I believe there to be a deadlock risk. Basically, if the worker sends
+ * messages over its output streams and then calls Socket.connect, and no printing to stdout
+ * or stderr occurs while connecting. Then if the runner polls, but misses the connection
+ * and then tries to read again, it will deadlock.
+ * </ul>
+ */
+@Singleton
+final class ServerSocketService extends AbstractExecutionThreadService {
+ private enum Source { REQUEST, ACCEPT}
+
+ private final Lock lock = new ReentrantLock();
+
+ /**
+ * Contains futures that have either only been accepted or requested. Once both occur they are
+ * removed from this map.
+ */
+ @GuardedBy("lock")
+ private final Map<UUID, SettableFuture<OpenedSocket>> halfFinishedConnections = Maps.newHashMap();
+
+ /**
+ * Contains the history of connections so we can ensure that each id is only accepted once and
+ * requested once.
+ */
+ @GuardedBy("lock")
+ private final SetMultimap<Source, UUID> connectionState = Multimaps.newSetMultimap(
+ Maps.<Source, Collection<UUID>>newEnumMap(Source.class),
+ new Supplier<Set<UUID>>(){
+ @Override public Set<UUID> get() {
+ return Sets.newHashSet();
+ }
+ });
+
+ private ServerSocket serverSocket;
+
+ @Inject ServerSocketService() {}
+
+ int getPort() {
+ awaitRunning();
+ checkState(serverSocket != null, "Socket has not been opened yet");
+ return serverSocket.getLocalPort();
+ }
+
+ /**
+ * Returns a {@link ListenableFuture} for an open connection corresponding to the given id.
+ *
+ * <p>N.B. calling this method 'consumes' the connection and as such calling it twice with the
+ * same id will not work, the second future returned will never complete. Similarly calling it
+ * with an id that does not correspond to a worker trying to connect will also fail.
+ */
+ public ListenableFuture<OpenedSocket> getConnection(UUID id) {
+ checkState(isRunning(), "You can only get connections from a running service: %s", this);
+ return getConnectionImpl(id, Source.REQUEST);
+ }
+
+ @Override protected void startUp() throws Exception {
+ serverSocket = new ServerSocket(0 /* bind to any available port */);
+ }
+
+ @Override protected void run() throws Exception {
+ while (isRunning()) {
+ Socket socket;
+ try {
+ socket = serverSocket.accept();
+ } catch (SocketException e) {
+ // we were closed
+ return;
+ }
+ OpenedSocket openedSocket = OpenedSocket.fromSocket(socket);
+
+ UUID id = ((StartupAnnounceMessage) openedSocket.reader().read()).trialId();
+ // N.B. you should not call set with the lock held, to prevent same thread executors from
+ // running with the lock.
+ getConnectionImpl(id, Source.ACCEPT).set(openedSocket);
+ }
+ }
+
+ /**
+ * Returns a {@link SettableFuture} from the map of connections.
+ *
+ * <p>This method has the following properties:
+ * <ul>
+ * <li>If the id is present in {@link #connectionState}, this will throw an
+ * {@link IllegalStateException}.
+ * <li>The id and source are recorded in {@link #connectionState}
+ * <li>If the future is already in {@link #halfFinishedConnections}, it is removed and
+ * returned.
+ * <li>If the future is not in {@link #halfFinishedConnections}, a new {@link SettableFuture}
+ * is added and then returned.
+ *
+ * <p>These features together ensure that each connection can only be accepted once, only
+ * requested once and once both have happened it will be removed from
+ * {@link #halfFinishedConnections}.
+ */
+ private SettableFuture<OpenedSocket> getConnectionImpl(UUID id, Source source) {
+ lock.lock();
+ try {
+ checkState(connectionState.put(source, id), "Connection for %s has already been %s",
+ id, source);
+ SettableFuture<OpenedSocket> future = halfFinishedConnections.get(id);
+ if (future == null) {
+ future = SettableFuture.create();
+ halfFinishedConnections.put(id, future);
+ } else {
+ halfFinishedConnections.remove(id);
+ }
+ return future;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override protected void triggerShutdown() {
+ try {
+ serverSocket.close();
+ } catch (IOException e) {
+ // best effort...
+ }
+ }
+
+ @Override protected void shutDown() throws Exception {
+ serverSocket.close();
+ // Now we have either been asked to stop or have failed with some kind of exception, we want to
+ // notify all pending requests, so if there are any references outside of this class they will
+ // notice.
+ lock.lock();
+ try {
+ for (SettableFuture<OpenedSocket> future : halfFinishedConnections.values()) {
+ future.setException(new Exception("The socket has been closed"));
+ }
+ halfFinishedConnections.clear();
+ connectionState.clear();
+ } finally {
+ lock.unlock();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java b/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java
new file mode 100644
index 0000000..92f179d
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.common.util.concurrent.Service;
+import com.google.common.util.concurrent.ServiceManager;
+import dagger.Module;
+import dagger.Provides;
+
+import java.util.Set;
+import javax.inject.Singleton;
+
+/** Configures the {@link ServiceManager}. */
+@Module
+final class ServiceModule {
+ @Provides
+ @Singleton
+ static ServiceManager provideServiceManager(Set<Service> services) {
+ return new ServiceManager(services);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java b/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java
new file mode 100644
index 0000000..b0f8a75
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * A simple interface for registering and deregistering shutdown hooks.
+ */
+interface ShutdownHookRegistrar {
+ /**
+ * Adds a hook to run at process shutdown.
+ *
+ * <p>See {@link Runtime#addShutdownHook(Thread)}.
+ */
+ void addShutdownHook(Thread hook);
+ /**
+ * Removes a shutdown hook that was previously registered via {@link #addShutdownHook(Thread)}.
+ *
+ * <p>See {@link Runtime#removeShutdownHook(Thread)}.
+ */
+ boolean removeShutdownHook(Thread hook);
+}
+
diff --git a/caliper/src/main/java/com/google/caliper/runner/StreamService.java b/caliper/src/main/java/com/google/caliper/runner/StreamService.java
new file mode 100644
index 0000000..a5852d0
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/StreamService.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.bridge.LogMessage;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.runner.StreamService.StreamItem.Kind;
+import com.google.caliper.util.Parser;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Queues;
+import com.google.common.io.Closeables;
+import com.google.common.io.LineReader;
+import com.google.common.util.concurrent.AbstractService;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Service; // for javadoc
+import com.google.common.util.concurrent.Service.State; // for javadoc
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.text.ParseException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+/**
+ * A {@link Service} that establishes a connection over a socket to a process and then allows
+ * multiplexed access to the processes' line oriented output over the socket and the standard
+ * process streams (stdout and stderr) as well as allowing data to be written over the socket.
+ *
+ * <p>The {@linkplain State states} of this service are as follows:
+ * <ul>
+ * <li>{@linkplain State#NEW NEW} : Idle state, no reading or writing is allowed.
+ * <li>{@linkplain State#STARTING STARTING} : Streams are being opened
+ * <li>{@linkplain State#RUNNING RUNNING} : At least one stream is still open or the writer has
+ * not been closed yet.
+ * <li>{@linkplain State#STOPPING STOPPING} : All streams have closed but some threads may still
+ * be running.
+ * <li>{@linkplain State#TERMINATED TERMINATED} : Idle state, all streams are closed
+ * <li>{@linkplain State#FAILED FAILED} : The service will transition to failed if it encounters
+ * any errors while reading from or writing to the streams, service failure will also cause
+ * the worker process to be forcibly shutdown and {@link #readItem(long, TimeUnit)},
+ * {@link #closeWriter()} and {@link #sendMessage(Serializable)} will start throwing
+ * IllegalStateExceptions.
+ * </ul>
+ */
+@TrialScoped final class StreamService extends AbstractService {
+ /** How long to wait for a process that should be exiting to actually exit. */
+ private static final int SHUTDOWN_WAIT_MILLIS = 10;
+
+ private static final Logger logger = Logger.getLogger(StreamService.class.getName());
+ private static final StreamItem TIMEOUT_ITEM = new StreamItem(Kind.TIMEOUT, null);
+
+ /** The final item that will be sent down the stream. */
+ static final StreamItem EOF_ITEM = new StreamItem(Kind.EOF, null);
+
+ private final ListeningExecutorService streamExecutor = MoreExecutors.listeningDecorator(
+ Executors.newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true).build()));
+ private final BlockingQueue<StreamItem> outputQueue = Queues.newLinkedBlockingQueue();
+ private final WorkerProcess worker;
+ private volatile Process process;
+ private final Parser<LogMessage> logMessageParser;
+ private final TrialOutputLogger trialOutput;
+
+ /**
+ * This represents the number of open streams from the users perspective. i.e. can you still
+ * write to the socket and read items.
+ *
+ * <p>This is decremented when either the socket is closed for writing or the EOF_ITEM has been
+ * read by the user.
+ */
+ private final AtomicInteger openStreams = new AtomicInteger();
+
+ /**
+ * Used to track how many read streams are open so we can correctly set the EOF_ITEM onto the
+ * queue.
+ */
+ private final AtomicInteger runningReadStreams = new AtomicInteger();
+ private OpenedSocket.Writer socketWriter;
+
+ @Inject StreamService(WorkerProcess worker,
+ Parser<LogMessage> logMessageParser,
+ TrialOutputLogger trialOutput) {
+ this.worker = worker;
+ this.logMessageParser = logMessageParser;
+ this.trialOutput = trialOutput;
+ }
+
+ @Override protected void doStart() {
+ try {
+ // TODO(lukes): write the commandline to the trial output file?
+ process = worker.startWorker();
+ } catch (IOException e) {
+ notifyFailed(e);
+ return;
+ }
+ // Failsafe kill the process and the executor service.
+ // If the process has already exited cleanly, this will be a no-op.
+ addListener(new Listener() {
+ @Override public void starting() {}
+ @Override public void running() {}
+ @Override public void stopping(State from) {}
+ @Override public void terminated(State from) {
+ cleanup();
+ }
+ @Override public void failed(State from, Throwable failure) {
+ cleanup();
+ }
+
+ void cleanup() {
+ streamExecutor.shutdown();
+ process.destroy();
+ try {
+ streamExecutor.awaitTermination(10, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ streamExecutor.shutdownNow();
+ }
+ }, MoreExecutors.directExecutor());
+ // You may be thinking as you read this "Yo dawg, what if IOExceptions rain from the sky?"
+ // If a stream we are reading from throws an IOException then we fail the entire Service. This
+ // will cause the worker to be killed (if its not dead already) and the various StreamReaders to
+ // be interrupted (eventually).
+
+ // use the default charset because worker streams will use the default for output
+ Charset processCharset = Charset.defaultCharset();
+ runningReadStreams.addAndGet(2);
+ openStreams.addAndGet(1);
+ streamExecutor.submit(
+ threadRenaming("worker-stderr",
+ new StreamReader("stderr",
+ new InputStreamReader(process.getErrorStream(), processCharset))));
+ streamExecutor.submit(
+ threadRenaming("worker-stdout",
+ new StreamReader("stdout",
+ new InputStreamReader(process.getInputStream(), processCharset))));
+ worker.socketFuture().addListener(
+ new Runnable() {
+ @Override public void run() {
+ try {
+ OpenedSocket openedSocket =
+ Uninterruptibles.getUninterruptibly(worker.socketFuture());
+ logger.fine("successfully opened the pipe from the worker");
+ socketWriter = openedSocket.writer();
+ runningReadStreams.addAndGet(1);
+ openStreams.addAndGet(1);
+ streamExecutor.submit(threadRenaming("worker-socket",
+ new SocketStreamReader(openedSocket.reader())));
+ } catch (ExecutionException e) {
+ notifyFailed(e.getCause());
+ }
+ }
+ },
+ MoreExecutors.directExecutor());
+ notifyStarted();
+ }
+
+ /**
+ * Reads a {@link StreamItem} from one of the streams waiting for one to become available if
+ * necessary.
+ */
+ StreamItem readItem(long timeout, TimeUnit unit) throws InterruptedException {
+ checkState(isRunning(), "Cannot read items from a %s StreamService", state());
+ StreamItem line = outputQueue.poll(timeout, unit);
+ if (line == EOF_ITEM) {
+ closeStream();
+ }
+ return (line == null) ? TIMEOUT_ITEM : line;
+ }
+
+ /**
+ * Write a line of data to the worker process over the socket.
+ *
+ * <p>N.B. Writing data via {@link #sendMessage(Serializable)} is only valid once the underlying
+ * socket has been opened. This should be fine assuming that socket writes are only in response
+ * to socket reads (which is currently the case), so there is no way that a write could happen
+ * prior to the socket being opened.
+ */
+ void sendMessage(Serializable message) throws IOException {
+ checkState(isRunning(), "Cannot read items from a %s StreamService", state());
+ checkState(socketWriter != null, "Attempted to write to the socket before it was opened.");
+ try {
+ socketWriter.write(message);
+ // We need to flush since this is a back and forth lockstep protocol, buffering can cause
+ // deadlock!
+ socketWriter.flush();
+ } catch (IOException e) {
+ Closeables.close(socketWriter, true);
+ notifyFailed(e);
+ throw e;
+ }
+ }
+
+ /** Closes the socket writer. */
+ void closeWriter() throws IOException {
+ checkState(isRunning(), "Cannot read items from a %s StreamService", state());
+ checkState(socketWriter != null, "Attempted to close the socket before it was opened.");
+ try {
+ socketWriter.close();
+ } catch (IOException e) {
+ notifyFailed(e);
+ throw e;
+ }
+ closeStream();
+ }
+
+ @Override protected void doStop() {
+ if (openStreams.get() > 0) {
+ // This means stop was called on us externally and we are still reading/writing, just log a
+ // warning and do nothing
+ logger.warning("Attempting to stop the stream service with streams still open");
+ }
+ final ListenableFuture<Integer> processFuture = streamExecutor.submit(new Callable<Integer>() {
+ @Override public Integer call() throws Exception {
+ return process.waitFor();
+ }
+ });
+ // Experimentally, even with well behaved processes there is some time between when all streams
+ // are closed as part of process shutdown and when the process has exited. So to not fail
+ // flakily when shutting down normally we need to do a timed wait
+ streamExecutor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean threw = true;
+ try {
+ if (processFuture.get(SHUTDOWN_WAIT_MILLIS, TimeUnit.MILLISECONDS) == 0) {
+ notifyStopped();
+ } else {
+ notifyFailed(
+ new Exception("Process failed to stop cleanly. Exit code: " + process.waitFor()));
+ }
+ threw = false;
+ } finally {
+ processFuture.cancel(true); // we don't need it anymore
+ if (threw) {
+ process.destroy();
+ notifyFailed(
+ new Exception("Process failed to stop cleanly and was forcibly killed. Exit code: "
+ + process.waitFor()));
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ private void closeStream() {
+ if (openStreams.decrementAndGet() == 0) {
+ stopAsync();
+ }
+ }
+
+ private void closeReadStream() {
+ if (runningReadStreams.decrementAndGet() == 0) {
+ outputQueue.add(EOF_ITEM);
+ }
+ }
+
+ /** An item read from one of the streams. */
+ static class StreamItem {
+ enum Kind {
+ /** This indicates that it is the last item. */
+ EOF,
+ /** This indicates that reading the item timed out. */
+ TIMEOUT,
+ /** This indicates that this item has content. */
+ DATA;
+ }
+
+ @Nullable private final LogMessage logMessage;
+ private final Kind kind;
+
+ private StreamItem(LogMessage line) {
+ this(Kind.DATA, checkNotNull(line));
+ }
+
+ private StreamItem(Kind state, @Nullable LogMessage logMessage) {
+ this.logMessage = logMessage;
+ this.kind = state;
+ }
+
+ /** Returns the content. This is only valid if {@link #kind()} return {@link Kind#DATA}. */
+ LogMessage content() {
+ checkState(kind == Kind.DATA, "Only data lines have content: %s", this);
+ return logMessage;
+ }
+
+ Kind kind() {
+ return kind;
+ }
+
+ @Override public String toString() {
+ ToStringHelper helper = MoreObjects.toStringHelper(StreamItem.class);
+ if (kind == Kind.DATA) {
+ helper.addValue(logMessage);
+ } else {
+ helper.addValue(kind);
+ }
+ return helper.toString();
+ }
+ }
+
+ /** Returns a callable that renames the the thread that the given callable runs in. */
+ private static <T> Callable<T> threadRenaming(final String name, final Callable<T> callable) {
+ checkNotNull(name);
+ checkNotNull(callable);
+ return new Callable<T>() {
+ @Override public T call() throws Exception {
+ Thread currentThread = Thread.currentThread();
+ String oldName = currentThread.getName();
+ currentThread.setName(name);
+ try {
+ return callable.call();
+ } finally {
+ currentThread.setName(oldName);
+ }
+ }
+ };
+ }
+
+ /**
+ * A background task that reads lines of text from a {@link Reader} and puts them onto a
+ * {@link BlockingQueue}.
+ */
+ private final class StreamReader implements Callable<Void> {
+ final Reader reader;
+ final String streamName;
+
+ StreamReader(String streamName, Reader reader) {
+ this.streamName = streamName;
+ this.reader = reader;
+ }
+
+ @Override public Void call() throws IOException, InterruptedException, ParseException {
+ LineReader lineReader = new LineReader(reader);
+ boolean threw = true;
+ try {
+ String line;
+ while ((line = lineReader.readLine()) != null) {
+ trialOutput.log(streamName, line);
+ LogMessage logMessage = logMessageParser.parse(line);
+ if (logMessage != null) {
+ outputQueue.put(new StreamItem(logMessage));
+ }
+ }
+ threw = false;
+ } catch (Exception e) {
+ notifyFailed(e);
+ } finally {
+ closeReadStream();
+ Closeables.close(reader, threw);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * A background task that reads lines of text from a {@link OpenedSocket.Reader} and puts them
+ * onto a {@link BlockingQueue}.
+ */
+ private final class SocketStreamReader implements Callable<Void> {
+ final OpenedSocket.Reader reader;
+
+ SocketStreamReader(OpenedSocket.Reader reader) {
+ this.reader = reader;
+ }
+
+ @Override public Void call() throws IOException, InterruptedException, ParseException {
+ boolean threw = true;
+ try {
+ Object obj;
+ while ((obj = reader.read()) != null) {
+ if (obj instanceof String) {
+ log(obj.toString());
+ continue;
+ }
+ LogMessage message = (LogMessage) obj;
+ if (message instanceof StopMeasurementLogMessage) {
+ // TODO(lukes): how useful are these messages? They seem like leftover debugging info
+ for (Measurement measurement : ((StopMeasurementLogMessage) message).measurements()) {
+ log(String.format("I got a result! %s: %f%s%n",
+ measurement.description(),
+ measurement.value().magnitude() / measurement.weight(),
+ measurement.value().unit()));
+ }
+ }
+ outputQueue.put(new StreamItem(message));
+ }
+ threw = false;
+ } catch (Exception e) {
+ notifyFailed(e);
+ } finally {
+ closeReadStream();
+ Closeables.close(reader, threw);
+ }
+ return null;
+ }
+
+ private void log(String text) {
+ trialOutput.log("socket", text);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java b/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java
new file mode 100644
index 0000000..923750b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * An exception representing the failure of an individual trial. Throwing this exception will
+ * invalidate the trial, but allow the run to continue. Both the runner and individual instruments
+ * are free to throw this exception.
+ *
+ * <p>The exception message is used to convey the nature of the failure to the user.
+ */
+final class TrialFailureException extends RuntimeException {
+ public TrialFailureException(String message) {
+ super(message);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialId.java b/caliper/src/main/java/com/google/caliper/runner/TrialId.java
new file mode 100644
index 0000000..9a7c6fe
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialId.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation for the current trial id. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface TrialId {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialModule.java b/caliper/src/main/java/com/google/caliper/runner/TrialModule.java
new file mode 100644
index 0000000..73d0044
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialModule.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.bridge.LogMessage;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.model.BenchmarkSpec;
+import com.google.caliper.model.Host;
+import com.google.caliper.model.Run;
+import com.google.caliper.model.Scenario;
+import com.google.caliper.model.Trial;
+import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor;
+import com.google.caliper.util.Parser;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Module;
+import dagger.Provides;
+
+import java.util.UUID;
+
+/**
+ * Configuration for a {@link TrialRunLoop}.
+ */
+@Module
+final class TrialModule {
+
+ private final UUID trialId;
+ private final int trialNumber;
+ private final Experiment experiment;
+
+ TrialModule(UUID trialId, int trialNumber, Experiment experiment) {
+ this.trialId = trialId;
+ this.trialNumber = trialNumber;
+ this.experiment = experiment;
+ }
+
+ @TrialScoped
+ @Provides
+ @TrialId
+ UUID provideTrialId() {
+ return trialId;
+ }
+
+ @TrialScoped
+ @Provides
+ @TrialNumber
+ int provideTrialNumber() {
+ return trialNumber;
+ }
+
+ @TrialScoped
+ @Provides
+ Experiment provideExperiment() {
+ return experiment;
+ }
+
+ @TrialScoped
+ @Provides
+ static BenchmarkSpec provideBenchmarkSpec(Experiment experiment) {
+ return new BenchmarkSpec.Builder()
+ .className(experiment.instrumentation().benchmarkMethod().getDeclaringClass().getName())
+ .methodName(experiment.instrumentation().benchmarkMethod().getName())
+ .addAllParameters(experiment.userParameters())
+ .build();
+ }
+
+ @Provides
+ @TrialScoped
+ static ListenableFuture<OpenedSocket> provideTrialSocket(
+ @TrialId UUID trialId,
+ ServerSocketService serverSocketService) {
+ return serverSocketService.getConnection(trialId);
+ }
+
+ @Provides
+ static MeasurementCollectingVisitor provideMeasurementCollectingVisitor(Experiment experiment) {
+ return experiment.instrumentation().getMeasurementCollectingVisitor();
+ }
+
+ @Provides
+ @TrialScoped
+ static TrialSchedulingPolicy provideTrialSchedulingPolicy(Experiment experiment) {
+ return experiment.instrumentation().instrument().schedulingPolicy();
+ }
+
+ @Provides
+ @TrialScoped
+ static StreamService provideStreamService(
+ WorkerProcess worker,
+ Parser<LogMessage> logMessageParser,
+ TrialOutputLogger trialOutput) {
+ return new StreamService(worker, logMessageParser, trialOutput);
+ }
+
+ // TODO(user): make this a singleton in a higher level module.
+ @Provides
+ @TrialScoped
+ static ShutdownHookRegistrar provideShutdownHook() {
+ return new RuntimeShutdownHookRegistrar();
+ }
+
+ @Provides static TrialResultFactory provideTrialFactory(
+ @TrialId final UUID trialId,
+ final Run run,
+ final Host host,
+ final Experiment experiment,
+ final BenchmarkSpec benchmarkSpec) {
+ return new TrialResultFactory() {
+ @Override public TrialResult newTrialResult(
+ VmDataCollectingVisitor dataCollectingVisitor,
+ MeasurementCollectingVisitor measurementCollectingVisitor) {
+ checkState(measurementCollectingVisitor.isDoneCollecting());
+ // TODO(lukes): should the trial messages be part of the Trial datastructure? It seems like
+ // the web UI could make use of them.
+ return new TrialResult(
+ new Trial.Builder(trialId)
+ .run(run)
+ .instrumentSpec(experiment.instrumentation().instrument().getSpec())
+ .scenario(new Scenario.Builder()
+ .host(host)
+ .vmSpec(dataCollectingVisitor.vmSpec())
+ .benchmarkSpec(benchmarkSpec))
+ .addAllMeasurements(measurementCollectingVisitor.getMeasurements())
+ .build(),
+ experiment,
+ measurementCollectingVisitor.getMessages());
+ }
+ };
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java b/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java
new file mode 100644
index 0000000..1437749
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** Binding annotation the trial number, used for debugging purposes. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface TrialNumber {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java
new file mode 100644
index 0000000..588a330
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+
+/**
+ * A factory for trial log files.
+ *
+ * <p>The log files may be configured to be deleted on exit of the runner process. If the files
+ * should not be deleted then call {@link #persistFile(File)} to ensure that they survive.
+ */
+interface TrialOutputFactory {
+
+ /** A simple tuple of a {@link File} and a {@link PrintWriter} for writing to that file. */
+ final class FileAndWriter {
+ final File file;
+ final PrintWriter writer;
+
+ FileAndWriter(File file, PrintWriter writer) {
+ this.file = file;
+ this.writer = writer;
+ }
+ }
+
+ /** Returns the file to write trial output to. */
+ FileAndWriter getTrialOutputFile(int trialNumber) throws FileNotFoundException;
+
+ /**
+ * Ensures that the given file will not be deleted after the run. The file provided must be equal
+ * to a file returned by {@link #getTrialOutputFile(int)}.
+ */
+ void persistFile(File f);
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java
new file mode 100644
index 0000000..31175dd
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.caliper.model.Run;
+import com.google.caliper.options.CaliperOptions;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.AbstractIdleService;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A {@link TrialOutputFactory} implemented as a service that manages a directory either under
+ * {@code /tmp} or in a user configured directory.
+ *
+ * <p>If there is a user configured directory, then no files will be deleted on service shutdown.
+ * Otherwise the only way to ensure that the log files survive service shutdown is to explicitly
+ * call {@link #persistFile(File)} with each file that should not be deleted.
+ */
+@Singleton
+final class TrialOutputFactoryService
+ extends AbstractIdleService implements TrialOutputFactory {
+ private static final String LOG_DIRECTORY_PROPERTY = "worker.output";
+
+ private final CaliperOptions options;
+ private final Run run;
+
+ @GuardedBy("this")
+ private final Set<String> toDelete = new LinkedHashSet<String>();
+
+ @GuardedBy("this")
+ private File directory;
+
+ @GuardedBy("this")
+ private boolean persistFiles;
+
+ @Inject TrialOutputFactoryService(Run run, CaliperOptions options) {
+ this.run = run;
+ this.options = options;
+ }
+
+ /** Returns the file to write trial output to. */
+ @Override public FileAndWriter getTrialOutputFile(int trialNumber) throws FileNotFoundException {
+ File dir;
+ synchronized (this) {
+ if (directory == null) {
+ throw new RuntimeException(
+ String.format("The output manager %s has not been started yet", this));
+ }
+ dir = directory;
+ }
+ File trialFile = new File(dir, String.format("trial-%d.log", trialNumber));
+ synchronized (this) {
+ if (!persistFiles) {
+ toDelete.add(trialFile.getPath());
+ }
+ }
+ return new FileAndWriter(trialFile,
+ new PrintWriter(
+ new BufferedWriter(
+ new OutputStreamWriter(
+ new FileOutputStream(trialFile),
+ Charsets.UTF_8))));
+ }
+
+ /**
+ * Ensures that the given file will not be deleted on exit of the JVM, possibly by copying to a
+ * new file.
+ */
+ @Override public synchronized void persistFile(File f) {
+ if (!persistFiles) {
+ checkArgument(toDelete.remove(f.getPath()), "%s was not created by the output manager", f);
+ }
+ }
+
+ @Override protected synchronized void startUp() throws Exception {
+ File directory;
+ String dirName = options.configProperties().get(LOG_DIRECTORY_PROPERTY);
+ boolean persistFiles = true;
+ if (dirName != null) {
+ directory = new File(dirName);
+ if (!directory.exists()) {
+ if (!directory.mkdirs()) {
+ throw new Exception(
+ String.format("Unable to create directory %s indicated by property %s",
+ dirName, LOG_DIRECTORY_PROPERTY));
+ }
+ } else if (!directory.isDirectory()) {
+ throw new Exception(
+ String.format("Configured directory %s indicated by property %s is not a directory",
+ dirName, LOG_DIRECTORY_PROPERTY));
+ }
+ // The directory exists and is a directory
+ directory = new File(directory, String.format("run-%s", run.id()));
+ if (!directory.mkdir()) {
+ throw new Exception("Unable to create a run directory " + directory);
+ }
+ } else {
+ // If none is configured then we don't care, just make a temp dir
+ // TODO(lukes): it would be nice to use jdk7 java.nio.file.Files.createTempDir() which allows
+ // us to specify a name, but caliper is still on jdk6.
+ directory = Files.createTempDir();
+ persistFiles = false;
+ }
+ this.directory = directory;
+ this.persistFiles = persistFiles;
+ }
+
+ @Override protected synchronized void shutDown() throws Exception {
+ if (!persistFiles) {
+ // This is best effort, files to be deleted are already in a tmp directory.
+ for (String f : toDelete) {
+ new File(f).delete();
+ }
+ // This will only succeed if the directory is empty which is what we want.
+ directory.delete();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java
new file mode 100644
index 0000000..1803fab
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.runner.TrialOutputFactory.FileAndWriter;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.UUID;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.inject.Inject;
+
+/**
+ * A logger to write trial output to a file.
+ */
+@TrialScoped final class TrialOutputLogger implements Closeable {
+ @GuardedBy("this")
+ private File file;
+
+ @GuardedBy("this")
+ private PrintWriter writer;
+
+ private final int trialNumber;
+ private final Experiment experiment;
+ private final UUID trialId;
+ private final TrialOutputFactory outputManager;
+
+ @Inject TrialOutputLogger(TrialOutputFactory outputManager, @TrialNumber int trialNumber,
+ @TrialId UUID trialId, Experiment experiment) {
+ this.outputManager = outputManager;
+ this.trialNumber = trialNumber;
+ this.trialId = trialId;
+ this.experiment = experiment;
+ }
+
+ /** Opens the trial output file. */
+ synchronized void open() throws IOException {
+ if (writer == null) {
+ FileAndWriter fileAndWriter = outputManager.getTrialOutputFile(trialNumber);
+ file = fileAndWriter.file;
+ writer = fileAndWriter.writer;
+ }
+ }
+
+ /**
+ * Ensures that the writer has been opened. also creates a happens-before edge that ensures that
+ * writer is visible (and non-null) after a non-exceptional return from this method.
+ */
+ private synchronized void checkOpened() {
+ checkState(writer != null, "The logger is not open");
+ }
+
+ /** Prints header information to the file. */
+ synchronized void printHeader() {
+ checkOpened();
+ // make the file self describing
+ // TODO(lukes): we could print the command line here. The user wouldn't be able to run it again
+ // since there would be no runner sending continue messages, but it might be useful to debug
+ // classpath issues.
+ writer.println("Trial Number: " + trialNumber);
+ writer.println("Trial Id: " + trialId);
+ writer.println("Experiment: " + experiment);
+ writer.println();
+ }
+
+ /**
+ * Logs a line of output to the logger.
+ *
+ * @param source The source of the line (e.g. 'stderr')
+ * @param line The output
+ */
+ synchronized void log(String source, String line) {
+ checkOpened();
+ writer.printf("[%s] %s%n", source, line);
+ }
+
+ @Override public synchronized void close() {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+ /** Marks the log file so that it will not be deleted at the end of the benchmark. */
+ synchronized void ensureFileIsSaved() {
+ checkOpened();
+ outputManager.persistFile(file);
+ }
+
+ /** Returns the log file path. */
+ synchronized File trialOutputFile() {
+ checkOpened();
+ return file;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialResult.java b/caliper/src/main/java/com/google/caliper/runner/TrialResult.java
new file mode 100644
index 0000000..a870b27
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.model.Trial;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A simple tuple of the data
+ */
+final class TrialResult {
+ private final Trial trial;
+ private final Experiment experiment;
+ private final ImmutableList<String> trialMessages;
+
+ TrialResult(Trial trial, Experiment experiment, ImmutableList<String> trialMessages) {
+ this.trial = trial;
+ this.experiment = experiment;
+ this.trialMessages = trialMessages;
+ }
+
+ Experiment getExperiment() {
+ return experiment;
+ }
+
+ Trial getTrial() {
+ return trial;
+ }
+
+ ImmutableList<String> getTrialMessages() {
+ return trialMessages;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java b/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java
new file mode 100644
index 0000000..2f8e2a2
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.runner;
+
+import com.google.caliper.model.Trial;
+import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor;
+
+/**
+ * A factory for producing {@link TrialResult TrialResults} based on data collected from visitors.
+ */
+interface TrialResultFactory {
+ /** Returns a new {@link Trial}. */
+ TrialResult newTrialResult(VmDataCollectingVisitor vmData,
+ MeasurementCollectingVisitor measurementData);
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java b/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java
new file mode 100644
index 0000000..587f3d2
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.caliper.bridge.LogMessage;
+import com.google.caliper.bridge.ShouldContinueMessage;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.model.Trial;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor;
+import com.google.caliper.runner.StreamService.StreamItem;
+import com.google.caliper.util.ShortDuration;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.Service.State;
+
+import org.joda.time.Duration;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+/**
+ * The main data gather control loop for a Trial.
+ *
+ * <p>This class starts the worker process, reads all the data from it and constructs the
+ * {@link Trial} while enforcing the trial timeout.
+ */
+@TrialScoped class TrialRunLoop implements Callable<TrialResult> {
+ private static final Logger logger = Logger.getLogger(TrialRunLoop.class.getName());
+
+ /** The time that the worker has to clean up after an experiment. */
+ private static final Duration WORKER_CLEANUP_DURATION = Duration.standardSeconds(2);
+
+ private final CaliperOptions options;
+ private final StreamService streamService;
+ private final TrialResultFactory trialFactory;
+
+ // TODO(lukes): The VmDataCollectingVisitor should be able to tell us when it has collected all
+ // its data.
+ private final VmDataCollectingVisitor dataCollectingVisitor;
+ private final Stopwatch trialStopwatch = Stopwatch.createUnstarted();
+ private final MeasurementCollectingVisitor measurementCollectingVisitor;
+ private final TrialOutputLogger trialOutput;
+
+ @Inject TrialRunLoop(
+ MeasurementCollectingVisitor measurementCollectingVisitor,
+ CaliperOptions options,
+ TrialResultFactory trialFactory,
+ TrialOutputLogger trialOutput,
+ StreamService streamService,
+ VmDataCollectingVisitor dataCollectingVisitor) {
+ this.options = options;
+ this.trialFactory = trialFactory;
+ this.streamService = streamService;
+ this.measurementCollectingVisitor = measurementCollectingVisitor;
+ this.trialOutput = trialOutput;
+ this.dataCollectingVisitor = dataCollectingVisitor;
+ }
+
+ @Override public TrialResult call() throws TrialFailureException, IOException {
+ if (streamService.state() != State.NEW) {
+ throw new IllegalStateException("You can only invoke the run loop once");
+ }
+ trialOutput.open();
+ trialOutput.printHeader();
+ streamService.startAsync().awaitRunning();
+ try {
+ long timeLimitNanos = getTrialTimeLimitTrialNanos();
+ boolean doneCollecting = false;
+ boolean done = false;
+ while (!done) {
+ StreamItem item;
+ try {
+ item = streamService.readItem(
+ timeLimitNanos - trialStopwatch.elapsed(NANOSECONDS),
+ NANOSECONDS);
+ } catch (InterruptedException e) {
+ trialOutput.ensureFileIsSaved();
+ // Someone has asked us to stop (via Futures.cancel?).
+ if (doneCollecting) {
+ logger.log(Level.WARNING, "Trial cancelled before completing normally (but after "
+ + "collecting sufficient data). Inspect {0} to see any worker output",
+ trialOutput.trialOutputFile());
+ done = true;
+ break;
+ }
+ // We were asked to stop but we didn't actually finish (the normal case). Fail the trial.
+ throw new TrialFailureException(
+ String.format("Trial cancelled. Inspect %s to see any worker output.",
+ trialOutput.trialOutputFile()));
+ }
+ switch (item.kind()) {
+ case DATA:
+ LogMessage logMessage = item.content();
+ logMessage.accept(measurementCollectingVisitor);
+ logMessage.accept(dataCollectingVisitor);
+ if (!doneCollecting && measurementCollectingVisitor.isDoneCollecting()) {
+ doneCollecting = true;
+ // We have received all the measurements we need and are about to tell the worker to
+ // shut down. At this point the worker should shutdown soon, but we don't want to
+ // wait too long, so decrease the time limit so that we wait no more than
+ // WORKER_CLEANUP_DURATION.
+ long cleanupTimeNanos = MILLISECONDS.toNanos(WORKER_CLEANUP_DURATION.getMillis());
+ // TODO(lukes): Does the min operation make sense here? should we just use the
+ // cleanupTimeNanos?
+ timeLimitNanos = trialStopwatch.elapsed(NANOSECONDS) + cleanupTimeNanos;
+ }
+ // If it is a stop measurement message we need to tell the worker to either stop or keep
+ // going with a WorkerContinueMessage. This needs to be done after the
+ // measurementCollecting visitor sees the message so that isDoneCollection will be up to
+ // date.
+ if (logMessage instanceof StopMeasurementLogMessage) {
+ // TODO(lukes): this is a blocking write, perhaps we should perform it in a non
+ // blocking manner to keep this thread only blocking in one place. This would
+ // complicate error handling, but may increase performance since it would free this
+ // thread up to handle other messages
+ streamService.sendMessage(
+ new ShouldContinueMessage(
+ !doneCollecting,
+ measurementCollectingVisitor.isWarmupComplete()));
+ if (doneCollecting) {
+ streamService.closeWriter();
+ }
+ }
+ break;
+ case EOF:
+ // We consider EOF to be synonymous with worker shutdown
+ if (!doneCollecting) {
+ trialOutput.ensureFileIsSaved();
+ throw new TrialFailureException(String.format("The worker exited without producing "
+ + "data. It has likely crashed. Inspect %s to see any worker output.",
+ trialOutput.trialOutputFile()));
+ }
+ done = true;
+ break;
+ case TIMEOUT:
+ trialOutput.ensureFileIsSaved();
+ if (doneCollecting) {
+ // Should this be an error?
+ logger.log(Level.WARNING, "Worker failed to exit cleanly within the alloted time. "
+ + "Inspect {0} to see any worker output", trialOutput.trialOutputFile());
+ done = true;
+ } else {
+ throw new TrialFailureException(String.format(
+ "Trial exceeded the total allowable runtime (%s). "
+ + "The limit may be adjusted using the --time-limit flag. Inspect %s to "
+ + "see any worker output",
+ options.timeLimit(), trialOutput.trialOutputFile()));
+ }
+ break;
+ default:
+ throw new AssertionError("Impossible item: " + item);
+ }
+ }
+ return trialFactory.newTrialResult(dataCollectingVisitor, measurementCollectingVisitor);
+ } catch (Throwable e) {
+ Throwables.propagateIfInstanceOf(e, TrialFailureException.class);
+ // This is some failure that is not a TrialFailureException, let the exception propagate but
+ // log the filename for the user.
+ trialOutput.ensureFileIsSaved();
+ logger.severe(
+ String.format(
+ "Unexpected error while executing trial. Inspect %s to see any worker output.",
+ trialOutput.trialOutputFile()));
+ throw Throwables.propagate(e);
+ } finally {
+ trialStopwatch.reset();
+ streamService.stopAsync();
+ trialOutput.close();
+ }
+ }
+
+ private long getTrialTimeLimitTrialNanos() {
+ ShortDuration timeLimit = options.timeLimit();
+ if (ShortDuration.zero().equals(timeLimit)) {
+ return Long.MAX_VALUE;
+ }
+ return timeLimit.to(NANOSECONDS);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java b/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java
new file mode 100644
index 0000000..604ca9c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+/**
+ * The scheduling policy for a particular trial.
+ *
+ * <p>TODO(lukes): Currently this is extremely simple. Trials can be scheduled in parallel with
+ * other trials or not. In the future, this should use some kind of cost modeling.
+ */
+enum TrialSchedulingPolicy {
+ PARALLEL,
+ SERIAL;
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java b/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java
new file mode 100644
index 0000000..5f23549
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import dagger.Subcomponent;
+
+/**
+ * Component for creating {@link ScheduledTrial} in the {@link TrialScoped}.
+ */
+@TrialScoped
+@Subcomponent(modules = {TrialModule.class})
+interface TrialScopeComponent {
+ ScheduledTrial getScheduledTrial();
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java b/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java
new file mode 100644
index 0000000..81a465a
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for the TrialScope.
+ *
+ * <p>Apply this to binding for which there can only be one per trial.
+ */
+@Target({ TYPE, METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Scope
+@interface TrialScoped {}
diff --git a/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java b/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java
new file mode 100644
index 0000000..1aa6337
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import java.io.PrintWriter;
+
+/**
+ * Signifies that the user's benchmark code threw an exception.
+ */
+@SuppressWarnings("serial")
+public class UserCodeException extends InvalidBenchmarkException {
+ public UserCodeException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+
+ public UserCodeException(Throwable cause) {
+ this("An exception was thrown from the benchmark code", cause);
+ }
+
+ @Override public void display(PrintWriter writer) {
+ printStackTrace(writer);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java b/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java
new file mode 100644
index 0000000..f48020b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.config.VmConfig;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * A named virtual machine configuration.
+ */
+final class VirtualMachine {
+ final String name;
+ final VmConfig config;
+
+ VirtualMachine(String name, VmConfig config) {
+ this.name = name;
+ this.config = config;
+ }
+
+ @Override public boolean equals(Object object) {
+ if (object instanceof VirtualMachine) {
+ VirtualMachine that = (VirtualMachine) object;
+ return this.name.equals(that.name)
+ && this.config.equals(that.config);
+ }
+ return false;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(name, config);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", name)
+ .add("config", config)
+ .toString();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java b/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java
new file mode 100644
index 0000000..3b56d53
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.bridge.AbstractLogMessageVisitor;
+import com.google.caliper.bridge.FailureLogMessage;
+import com.google.caliper.bridge.VmOptionLogMessage;
+import com.google.caliper.bridge.VmPropertiesLogMessage;
+import com.google.caliper.model.VmSpec;
+import com.google.caliper.platform.Platform;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+import javax.inject.Inject;
+
+/** An {@link AbstractLogMessageVisitor} that collects data about JVM properties and options. */
+@TrialScoped
+final class VmDataCollectingVisitor extends AbstractLogMessageVisitor {
+ private final ImmutableMap.Builder<String, String> vmOptionsBuilder = ImmutableMap.builder();
+ private final Platform platform;
+ private Optional<ImmutableMap<String, String>> vmProperties = Optional.absent();
+
+ @Inject VmDataCollectingVisitor(Platform platform) {
+ this.platform = platform;
+ }
+
+ /**
+ * Returns a {@link VmSpec} based on the data gathered by this visitor.
+ *
+ * @throws IllegalStateException if not all the data has been gathered.
+ */
+ VmSpec vmSpec() {
+ ImmutableMap<String, String> options = vmOptionsBuilder.build();
+ platform.checkVmProperties(options);
+ return new VmSpec.Builder()
+ .addAllProperties(vmProperties.get())
+ .addAllOptions(options)
+ .build();
+ }
+
+ @Override
+ public void visit(FailureLogMessage logMessage) {
+ throw new ProxyWorkerException(logMessage.stackTrace());
+ }
+
+ @Override
+ public void visit(VmOptionLogMessage logMessage) {
+ vmOptionsBuilder.put(logMessage.name(), logMessage.value());
+ }
+
+ @Override
+ public void visit(VmPropertiesLogMessage logMessage) {
+ vmProperties = Optional.of(ImmutableMap.copyOf(
+ Maps.filterKeys(logMessage.properties(), platform.vmPropertiesToRetain())));
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java b/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java
new file mode 100644
index 0000000..144a21c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.bridge.CommandLineSerializer;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.WorkerSpec;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.model.BenchmarkSpec;
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.caliper.worker.WorkerMain;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.inject.Inject;
+
+/**
+ * A representation of an unstarted worker.
+ *
+ * <p>A worker is a sub process that runs a benchmark trial. Specifically it is a JVM running
+ * {@link com.google.caliper.worker.WorkerMain}. Because of this we can make certain assumptions
+ * about its behavior, including but not limited to:
+ *
+ * <ul>
+ * <li>The worker will connect back to us over a socket connection and send us UTF-8 json
+ * messages in a line oriented protocol.
+ * <li>TODO(lukes,gak): This is probably as good a place as any to specify the entire protocol.
+ * </ul>
+ */
+@TrialScoped final class WorkerProcess {
+ private static final Logger logger = Logger.getLogger(WorkerProcess.class.getName());
+
+ @GuardedBy("this")
+ private Process worker;
+ private final ProcessBuilder workerBuilder;
+ private final ShutdownHookRegistrar shutdownHookRegistrar;
+ private final ListenableFuture<OpenedSocket> openedSocket;
+ private final UUID trialId;
+
+ @VisibleForTesting WorkerProcess(ProcessBuilder workerBuilder,
+ UUID trialId,
+ ListenableFuture<OpenedSocket> openedSocket,
+ ShutdownHookRegistrar shutdownHookRegistrar) {
+ this.trialId = trialId;
+ this.workerBuilder = workerBuilder;
+ this.openedSocket = openedSocket;
+ this.shutdownHookRegistrar = shutdownHookRegistrar;
+ }
+
+ @Inject WorkerProcess(@TrialId UUID trialId,
+ ListenableFuture<OpenedSocket> openedSocket,
+ Experiment experiment,
+ BenchmarkSpec benchmarkSpec,
+ @LocalPort int localPort,
+ BenchmarkClass benchmarkClass,
+ ShutdownHookRegistrar shutdownHookRegistrar) {
+ this.trialId = trialId;
+ this.workerBuilder =
+ buildProcess(trialId, experiment, benchmarkSpec, localPort, benchmarkClass);
+ this.openedSocket = openedSocket;
+ this.shutdownHookRegistrar = shutdownHookRegistrar;
+ }
+
+ ListenableFuture<OpenedSocket> socketFuture() {
+ return openedSocket;
+ }
+
+ /**
+ * Returns a {@link Process} representing this worker. The process will be started if it hasn't
+ * already.
+ */
+ synchronized Process startWorker() throws IOException {
+ if (worker == null) {
+ final Process delegate = workerBuilder.start();
+ final Thread shutdownHook = new Thread("worker-shutdown-hook-" + trialId) {
+ @Override public void run() {
+ delegate.destroy();
+ }
+ };
+ shutdownHookRegistrar.addShutdownHook(shutdownHook);
+ worker = new Process() {
+ @Override public OutputStream getOutputStream() {
+ return delegate.getOutputStream();
+ }
+
+ @Override public InputStream getInputStream() {
+ return delegate.getInputStream();
+ }
+
+ @Override public InputStream getErrorStream() {
+ return delegate.getErrorStream();
+ }
+
+ @Override public int waitFor() throws InterruptedException {
+ int waitFor = delegate.waitFor();
+ shutdownHookRegistrar.removeShutdownHook(shutdownHook);
+ return waitFor;
+ }
+
+ @Override public int exitValue() {
+ int exitValue = delegate.exitValue();
+ // if it hasn't thrown, the process is done
+ shutdownHookRegistrar.removeShutdownHook(shutdownHook);
+ return exitValue;
+ }
+
+ @Override public void destroy() {
+ delegate.destroy();
+ shutdownHookRegistrar.removeShutdownHook(shutdownHook);
+ }
+ };
+ }
+ return worker;
+ }
+
+ @VisibleForTesting static ProcessBuilder buildProcess(
+ UUID trialId,
+ Experiment experiment,
+ BenchmarkSpec benchmarkSpec,
+ int localPort,
+ BenchmarkClass benchmarkClass) {
+ // TODO(lukes): it would be nice to split this method into a few smaller more targeted methods
+ Instrumentation instrumentation = experiment.instrumentation();
+ Instrument instrument = instrumentation.instrument();
+ WorkerSpec request = new WorkerSpec(
+ trialId,
+ instrumentation.workerClass(),
+ instrumentation.workerOptions(),
+ benchmarkSpec,
+ ImmutableList.copyOf(instrumentation.benchmarkMethod.getParameterTypes()),
+ localPort);
+
+ ProcessBuilder processBuilder = new ProcessBuilder().redirectErrorStream(false);
+
+ List<String> args = processBuilder.command();
+
+ VirtualMachine vm = experiment.vm();
+ VmConfig vmConfig = vm.config;
+ args.addAll(getJvmArgs(vm, benchmarkClass));
+
+ Iterable<String> instrumentJvmOptions = instrument.getExtraCommandLineArgs(vmConfig);
+ logger.fine(String.format("Instrument(%s) Java args: %s", instrument.getClass().getName(),
+ instrumentJvmOptions));
+ Iterables.addAll(args, instrumentJvmOptions);
+
+ // last to ensure that they're always applied
+ args.addAll(vmConfig.workerProcessArgs());
+
+ args.add(WorkerMain.class.getName());
+ args.add(CommandLineSerializer.render(request));
+
+ logger.finest(String.format("Full JVM (%s) args: %s", vm.name, args));
+ return processBuilder;
+ }
+
+ @VisibleForTesting static List<String> getJvmArgs(
+ VirtualMachine vm,
+ BenchmarkClass benchmarkClass) {
+
+ VmConfig vmConfig = vm.config;
+ String platformName = vmConfig.platformName();
+
+ List<String> args = Lists.newArrayList();
+ String jdkPath = vmConfig.vmExecutable().getAbsolutePath();
+ args.add(jdkPath);
+ logger.fine(String.format("%s(%s) Path: %s", platformName, vm.name, jdkPath));
+
+ ImmutableList<String> jvmOptions = vmConfig.options();
+ args.addAll(jvmOptions);
+ logger.fine(String.format("%s(%s) args: %s", platformName, vm.name, jvmOptions));
+
+ ImmutableSet<String> benchmarkJvmOptions = benchmarkClass.vmOptions();
+ args.addAll(benchmarkJvmOptions);
+ logger.fine(String.format("Benchmark(%s) %s args: %s", benchmarkClass.name(), platformName,
+ benchmarkJvmOptions));
+
+ String classPath = vmConfig.workerClassPath();
+ Collections.addAll(args, "-cp", classPath);
+ logger.finer(String.format("Class path: %s", classPath));
+ return args;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java b/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java
new file mode 100644
index 0000000..c412c55
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.util;
+
+import java.io.PrintWriter;
+
+/**
+ * Exception used to abort command-line processing because the user has asked for help (using either
+ * --help or -h).
+ */
+@SuppressWarnings("serial") // who would serialize a command-line parsing error?
+public final class DisplayUsageException extends InvalidCommandException {
+ public DisplayUsageException() {
+ super("(User asked for --help. This message should not appear anywhere.)");
+ }
+
+ @Override public void display(PrintWriter writer) {
+ displayUsage(writer);
+ }
+
+ @Override public int exitCode() {
+ return 0;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java b/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java
deleted file mode 100644
index 40293bf..0000000
--- a/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper.util;
-
-import com.google.gson.JsonParser;
-
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * Reads a stream containing inline JSON objects. Each JSON object is prefixed
- * by a marker string and suffixed by a newline character.
- */
-public final class InterleavedReader implements Closeable {
-
- /**
- * The length of the scratch buffer to search for markers in. Also acts as an
- * upper bound on the length of returned strings. Not used as an I/O buffer.
- */
- private static final int BUFFER_LENGTH = 80;
-
- private final String marker;
- private final BufferedReader reader;
- private final JsonParser jsonParser = new JsonParser();
-
- public static final String DEFAULT_MARKER = "//ZxJ/";
-
- public InterleavedReader(Reader reader) {
- this(DEFAULT_MARKER, reader);
- }
-
- public InterleavedReader(String marker, Reader reader) {
- if (marker.length() > BUFFER_LENGTH) {
- throw new IllegalArgumentException("marker.length() > BUFFER_LENGTH");
- }
- this.marker = marker;
- this.reader = reader instanceof BufferedReader
- ? (BufferedReader) reader
- : new BufferedReader(reader);
- }
-
- /**
- * Returns the next value in the stream: either a String, a JsonElement, or
- * null to indicate the end of the stream. Callers should use instanceof to
- * inspect the return type.
- */
- public Object read() throws IOException {
- char[] buffer = new char[BUFFER_LENGTH];
- reader.mark(BUFFER_LENGTH);
- int count = 0;
- int textEnd;
-
- while (true) {
- int r = reader.read(buffer, count, buffer.length - count);
-
- if (r == -1) {
- // the input is exhausted; return the remaining characters
- textEnd = count;
- break;
- }
-
- count += r;
- int possibleMarker = findPossibleMarker(buffer, count);
-
- if (possibleMarker != 0) {
- // return the characters that precede the marker
- textEnd = possibleMarker;
- break;
- }
-
- if (count < marker.length()) {
- // the buffer contains only the prefix of a marker so we must read more
- continue;
- }
-
- // we've read a marker so return the value that follows
- reader.reset();
- String json = reader.readLine().substring(marker.length());
- return jsonParser.parse(json);
- }
-
- if (count == 0) {
- return null;
- }
-
- // return characters
- reader.reset();
- count = reader.read(buffer, 0, textEnd);
- return new String(buffer, 0, count);
- }
-
- @Override public void close() throws IOException {
- reader.close();
- }
-
- /**
- * Returns the index of marker in {@code chars}, stopping at {@code limit}.
- * Should the chars end with a prefix of marker, the offset of that prefix
- * is returned.
- */
- int findPossibleMarker(char[] chars, int limit) {
- search:
- for (int i = 0; true; i++) {
- for (int m = 0; m < marker.length() && i + m < limit; m++) {
- if (chars[i + m] != marker.charAt(m)) {
- continue search;
- }
- }
- return i;
- }
- }
-}
diff --git a/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java b/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java
new file mode 100644
index 0000000..990ca7b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Exception that signifies that the <i>user</i> has given an invalid argument string.
+ */
+@SuppressWarnings("serial") // who would serialize a command-line parsing error?
+public class InvalidCommandException extends RuntimeException {
+ private ImmutableList<String> usage;
+
+ public InvalidCommandException(String message, Object... args) {
+ super(String.format(message, args));
+ }
+
+ public void setUsage(List<String> usage) {
+ this.usage = ImmutableList.copyOf(usage);
+ }
+
+ public void display(PrintWriter writer) {
+ writer.println(getMessage());
+ if (usage != null) {
+ writer.println();
+ displayUsage(writer);
+ }
+ }
+
+ protected final void displayUsage(PrintWriter writer) {
+ for (String line : usage) {
+ writer.println(line);
+ }
+ }
+
+ public int exitCode() {
+ return 1;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/OutputModule.java b/caliper/src/main/java/com/google/caliper/util/OutputModule.java
new file mode 100644
index 0000000..8220436
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/OutputModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.Module;
+import dagger.Provides;
+
+import java.io.PrintWriter;
+
+/**
+ * A module that binds {@link PrintWriter} instances for {@link Stdout} and {@link Stderr}.
+ */
+@Module
+public final class OutputModule {
+ public static OutputModule system() {
+ return new OutputModule(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
+ }
+
+ private final PrintWriter stdout;
+ private final PrintWriter stderr;
+
+ public OutputModule(PrintWriter stdout, PrintWriter stderr) {
+ this.stdout = checkNotNull(stdout);
+ this.stderr = checkNotNull(stderr);
+ }
+
+ @Provides @Stdout PrintWriter provideStdoutWriter() {
+ return stdout;
+ }
+
+ @Provides @Stderr PrintWriter provideStderr() {
+ return stderr;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/Parser.java b/caliper/src/main/java/com/google/caliper/util/Parser.java
new file mode 100644
index 0000000..0ab0d9e
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Parser.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import java.text.ParseException;
+
+// TODO(kevinb): release common.text.Parser in Guava then nuke this
+public interface Parser<T> {
+ T parse(CharSequence text) throws ParseException;
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/Parsers.java b/caliper/src/main/java/com/google/caliper/util/Parsers.java
new file mode 100644
index 0000000..3e56f1a
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Parsers.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Primitives;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.ParseException;
+import java.util.List;
+
+public class Parsers {
+ public static final Parser<String> IDENTITY = new Parser<String>() {
+ @Override public String parse(CharSequence in) {
+ return in.toString();
+ }
+ };
+
+ private static final List<String> CONVERSION_METHOD_NAMES =
+ ImmutableList.of("fromString", "decode", "valueOf");
+
+ /**
+ * Parser that tries, in this order:
+ * <ul>
+ * <li>ResultType.fromString(String)
+ * <li>ResultType.decode(String)
+ * <li>ResultType.valueOf(String)
+ * <li>new ResultType(String)
+ * </ul>
+ */
+ public static <T> Parser<T> conventionalParser(Class<T> resultType)
+ throws NoSuchMethodException {
+ if (resultType == String.class) {
+ @SuppressWarnings("unchecked") // T == String
+ Parser<T> identity = (Parser<T>) IDENTITY;
+ return identity;
+ }
+
+ final Class<T> wrappedResultType = Primitives.wrap(resultType);
+
+ for (String methodName : CONVERSION_METHOD_NAMES) {
+ try {
+ final Method method = wrappedResultType.getDeclaredMethod(methodName, String.class);
+
+ if (Util.isStatic(method) && wrappedResultType.isAssignableFrom(method.getReturnType())) {
+ method.setAccessible(true); // to permit inner enums, etc.
+ return new InvokingParser<T>() {
+ @Override protected T invoke(String input) throws Exception {
+ return wrappedResultType.cast(method.invoke(null, input));
+ }
+ };
+ }
+ } catch (Exception tryAgain) {
+ }
+ }
+
+ final Constructor<T> constr = wrappedResultType.getDeclaredConstructor(String.class);
+ constr.setAccessible(true);
+ return new InvokingParser<T>() {
+ @Override protected T invoke(String input) throws Exception {
+ return wrappedResultType.cast(constr.newInstance(input));
+ }
+ };
+ }
+
+ abstract static class InvokingParser<T> implements Parser<T> {
+ @Override public T parse(CharSequence input) throws ParseException {
+ try {
+ return invoke(input.toString());
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ String desc = firstNonNull(cause.getMessage(), cause.getClass().getSimpleName());
+ throw newParseException(desc, cause);
+ } catch (Exception e) {
+ throw newParseException("Unknown parsing problem", e);
+ }
+ }
+
+ protected abstract T invoke(String input) throws Exception;
+ }
+
+ public static ParseException newParseException(String message, Throwable cause) {
+ ParseException pe = newParseException(message);
+ pe.initCause(cause);
+ return pe;
+ }
+
+ public static ParseException newParseException(String message) {
+ return new ParseException(message, 0);
+ }
+
+ private static <T> T firstNonNull(T first, T second) {
+ return (first != null) ? first : second;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/Reflection.java b/caliper/src/main/java/com/google/caliper/util/Reflection.java
new file mode 100644
index 0000000..4b17d8d
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Reflection.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * A utility class for common reflection operations in Caliper.
+ */
+public final class Reflection {
+ private Reflection() {}
+
+ public static ImmutableSet<Method> getAnnotatedMethods(Class<?> clazz,
+ Class<? extends Annotation> annotationClass) {
+ Method[] methods = clazz.getDeclaredMethods();
+ ImmutableSet.Builder<Method> builder = ImmutableSet.builder();
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(annotationClass)) {
+ method.setAccessible(true);
+ builder.add(method);
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/ShortDuration.java b/caliper/src/main/java/com/google/caliper/util/ShortDuration.java
new file mode 100644
index 0000000..34d9a2b
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/ShortDuration.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.primitives.Longs;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represents a nonnegative duration from 0 to 100 days, with picosecond precision.
+ * Contrast with Joda-Time's duration class, which has only millisecond precision but can
+ * represent durations of millions of years.
+ */
+public abstract class ShortDuration implements Comparable<ShortDuration> {
+ // Factories
+
+ public static ShortDuration of(long duration, TimeUnit unit) {
+ if (duration == 0) {
+ return ZERO;
+ }
+ checkArgument(duration >= 0, "negative duration: %s", duration);
+ checkArgument(duration <= MAXES.get(unit),
+ "ShortDuration cannot exceed 100 days: %s %s", duration, unit);
+ long nanos = TimeUnit.NANOSECONDS.convert(duration, unit);
+ return new PositiveShortDuration(nanos * 1000);
+ }
+
+ public static ShortDuration of(BigDecimal duration, TimeUnit unit) {
+ // convert to picoseconds first, to minimize rounding
+ BigDecimal picos = duration.multiply(ONE_IN_PICOS.get(unit));
+ return ofPicos(toLong(picos, RoundingMode.HALF_UP));
+ }
+
+ public static ShortDuration valueOf(String s) {
+ if ("0".equals(s)) {
+ return ZERO;
+ }
+ Matcher matcher = PATTERN.matcher(s);
+ checkArgument(matcher.matches(), "Invalid ShortDuration: %s", s);
+
+ BigDecimal value = new BigDecimal(matcher.group(1));
+ String abbrev = matcher.group(2);
+ TimeUnit unit = ABBREV_TO_UNIT.get(abbrev);
+ checkArgument(unit != null, "Unrecognized time unit: %s", abbrev);
+
+ return of(value, unit);
+ }
+
+ public static ShortDuration zero() {
+ return ZERO;
+ }
+
+ // fortunately no abbreviation starts with 'e', so this should work
+ private static final Pattern PATTERN = Pattern.compile("^([0-9.eE+-]+) ?(\\S+)$");
+
+ private static ShortDuration ofPicos(long picos) {
+ if (picos == 0) {
+ return ZERO;
+ }
+ checkArgument(picos > 0);
+ return new PositiveShortDuration(picos);
+ }
+
+ // TODO(kevinb): we sure seem to convert back and forth with BigDecimal a lot.
+ // Why not just *make* this a BigDecimal?
+ final long picos;
+
+ ShortDuration(long picos) {
+ this.picos = picos;
+ }
+
+ public long toPicos() {
+ return picos;
+ }
+
+ public long to(TimeUnit unit) {
+ return to(unit, RoundingMode.HALF_UP);
+ }
+
+ public abstract long to(TimeUnit unit, RoundingMode roundingMode);
+
+ /*
+ * In Guava, this will probably implement an interface called Quantity, and the following methods
+ * will come from there, so they won't have to be defined here.
+ */
+
+ /**
+ * Returns an instance of this type that represents the sum of this value and {@code
+ * addend}.
+ */
+ public abstract ShortDuration plus(ShortDuration addend);
+
+ /**
+ * Returns an instance of this type that represents the difference of this value and
+ * {@code subtrahend}.
+ */
+ public abstract ShortDuration minus(ShortDuration subtrahend);
+
+ /**
+ * Returns an instance of this type that represents the product of this value and the
+ * integral value {@code multiplicand}.
+ */
+ public abstract ShortDuration times(long multiplicand);
+
+ /**
+ * Returns an instance of this type that represents the product of this value and {@code
+ * multiplicand}, rounded according to {@code roundingMode} if necessary.
+ *
+ * <p>If this class represents an amount that is "continuous" rather than discrete, the
+ * implementation of this method may simply ignore the rounding mode.
+ */
+ public abstract ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode);
+
+ /**
+ * Returns an instance of this type that represents this value divided by the integral
+ * value {@code divisor}, rounded according to {@code roundingMode} if necessary.
+ *
+ * <p>If this class represents an amount that is "continuous" rather than discrete, the
+ * implementation of this method may simply ignore the rounding mode.
+ */
+ public abstract ShortDuration dividedBy(long divisor, RoundingMode roundingMode);
+
+ /**
+ * Returns an instance of this type that represents this value divided by {@code
+ * divisor}, rounded according to {@code roundingMode} if necessary.
+ *
+ * <p>If this class represents an amount that is "continuous" rather than discrete, the
+ * implementation of this method may simply ignore the rounding mode.
+ */
+ public abstract ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode);
+
+ // Zero
+
+ private static ShortDuration ZERO = new ShortDuration(0) {
+ @Override public long to(TimeUnit unit, RoundingMode roundingMode) {
+ return 0;
+ }
+ @Override public ShortDuration plus(ShortDuration addend) {
+ return addend;
+ }
+ @Override public ShortDuration minus(ShortDuration subtrahend) {
+ checkArgument(this == subtrahend);
+ return this;
+ }
+ @Override public ShortDuration times(long multiplicand) {
+ return this;
+ }
+ @Override public ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode) {
+ return this;
+ }
+ @Override public ShortDuration dividedBy(long divisor, RoundingMode roundingMode) {
+ return dividedBy(new BigDecimal(divisor), roundingMode);
+ }
+ @Override public ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode) {
+ checkArgument(divisor.compareTo(BigDecimal.ZERO) != 0);
+ return this;
+ }
+ @Override public int compareTo(ShortDuration that) {
+ if (this == that) {
+ return 0;
+ }
+ checkNotNull(that);
+ return -1;
+ }
+ @Override public boolean equals(@Nullable Object that) {
+ return this == that;
+ }
+ @Override public int hashCode() {
+ return 0;
+ }
+ @Override public String toString() {
+ return "0s";
+ }
+ };
+
+ // Non-zero
+
+ private static class PositiveShortDuration extends ShortDuration {
+ private PositiveShortDuration(long picos) {
+ super(picos);
+ checkArgument(picos > 0);
+ }
+
+ @Override public long to(TimeUnit unit, RoundingMode roundingMode) {
+ BigDecimal divisor = ONE_IN_PICOS.get(unit);
+ return toLong(new BigDecimal(picos).divide(divisor), roundingMode);
+ }
+
+ @Override public ShortDuration plus(ShortDuration addend) {
+ return new PositiveShortDuration(picos + addend.picos);
+ }
+
+ @Override public ShortDuration minus(ShortDuration subtrahend) {
+ return ofPicos(picos - subtrahend.picos);
+ }
+
+ @Override public ShortDuration times(long multiplicand) {
+ if (multiplicand == 0) {
+ return ZERO;
+ }
+ checkArgument(multiplicand >= 0, "negative multiplicand: %s", multiplicand);
+ checkArgument(multiplicand <= Long.MAX_VALUE / picos,
+ "product of %s and %s would overflow", this, multiplicand);
+ return new PositiveShortDuration(picos * multiplicand);
+ }
+
+ @Override public ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode) {
+ BigDecimal product = BigDecimal.valueOf(picos).multiply(multiplicand);
+ return ofPicos(toLong(product, roundingMode));
+ }
+
+ @Override public ShortDuration dividedBy(long divisor, RoundingMode roundingMode) {
+ return dividedBy(new BigDecimal(divisor), roundingMode);
+ }
+
+ @Override public ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode) {
+ BigDecimal product = BigDecimal.valueOf(picos).divide(divisor, roundingMode);
+ return ofPicos(product.longValueExact());
+ }
+
+ @Override public int compareTo(ShortDuration that) {
+ return Longs.compare(this.picos, that.picos);
+ }
+
+ @Override public boolean equals(Object object) {
+ if (object instanceof PositiveShortDuration) {
+ PositiveShortDuration that = (PositiveShortDuration) object;
+ return this.picos == that.picos;
+ }
+ return false;
+ }
+
+ @Override public int hashCode() {
+ return Longs.hashCode(picos);
+ }
+
+ @Override public String toString() {
+ TimeUnit bestUnit = TimeUnit.NANOSECONDS;
+ for (TimeUnit unit : TimeUnit.values()) {
+ if (picosIn(unit) > picos) {
+ break;
+ }
+ bestUnit = unit;
+ }
+ BigDecimal divisor = ONE_IN_PICOS.get(bestUnit);
+
+ return new BigDecimal(picos).divide(divisor, ROUNDER) + preferredAbbrev(bestUnit);
+ }
+
+ private static final MathContext ROUNDER = new MathContext(4);
+ }
+
+ // Private parts
+
+ private static String preferredAbbrev(TimeUnit bestUnit) {
+ return ABBREVIATIONS.get(bestUnit).get(0);
+ }
+
+ private static final ImmutableListMultimap<TimeUnit, String> ABBREVIATIONS =
+ createAbbreviations();
+
+ private static ImmutableListMultimap<TimeUnit, String> createAbbreviations() {
+ ImmutableListMultimap.Builder<TimeUnit, String> builder = ImmutableListMultimap.builder();
+ builder.putAll(TimeUnit.NANOSECONDS, "ns", "nanos");
+ builder.putAll(TimeUnit.MICROSECONDS, "\u03bcs" /*μs*/, "us", "micros");
+ builder.putAll(TimeUnit.MILLISECONDS, "ms", "millis");
+ builder.putAll(TimeUnit.SECONDS, "s", "sec");
+
+ // Do the rest in a JDK5-safe way
+ TimeUnit[] allUnits = TimeUnit.values();
+ if (allUnits.length >= 7) {
+ builder.putAll(allUnits[4], "m", "min");
+ builder.putAll(allUnits[5], "h", "hr");
+ builder.putAll(allUnits[6], "d");
+ }
+
+ for (TimeUnit unit : TimeUnit.values()) {
+ builder.put(unit, Ascii.toLowerCase(unit.name()));
+ }
+ return builder.build();
+ }
+
+ private static final Map<String, TimeUnit> ABBREV_TO_UNIT = createAbbrevToUnitMap();
+
+ private static Map<String, TimeUnit> createAbbrevToUnitMap() {
+ ImmutableMap.Builder<String, TimeUnit> builder = ImmutableMap.builder();
+ for (Map.Entry<TimeUnit, String> entry : ABBREVIATIONS.entries()) {
+ builder.put(entry.getValue(), entry.getKey());
+ }
+ return builder.build();
+ }
+
+ private static final Map<TimeUnit, BigDecimal> ONE_IN_PICOS = createUnitToPicosMap();
+
+ private static Map<TimeUnit, BigDecimal> createUnitToPicosMap() {
+ Map<TimeUnit, BigDecimal> map = Maps.newEnumMap(TimeUnit.class);
+ for (TimeUnit unit : TimeUnit.values()) {
+ map.put(unit, new BigDecimal(picosIn(unit)));
+ }
+ return Collections.unmodifiableMap(map);
+ }
+
+ private static final Map<TimeUnit, Long> MAXES = createMaxesMap();
+
+ private static Map<TimeUnit, Long> createMaxesMap() {
+ Map<TimeUnit, Long> map = Maps.newEnumMap(TimeUnit.class);
+ for (TimeUnit unit : TimeUnit.values()) {
+ // Max is 100 days
+ map.put(unit, unit.convert(100L * 24 * 60 * 60, TimeUnit.SECONDS));
+ }
+ return Collections.unmodifiableMap(map);
+ }
+
+ private static long toLong(BigDecimal bd, RoundingMode roundingMode) {
+ // setScale does not really mutate the BigDecimal
+ return bd.setScale(0, roundingMode).longValueExact();
+ }
+
+ private static long picosIn(TimeUnit unit) {
+ return unit.toNanos(1000);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/util/Stderr.java b/caliper/src/main/java/com/google/caliper/util/Stderr.java
new file mode 100644
index 0000000..7aa9e1f
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Stderr.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** A binding annotation for standard err. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+public @interface Stderr {}
diff --git a/caliper/src/main/java/com/google/caliper/util/Stdout.java b/caliper/src/main/java/com/google/caliper/util/Stdout.java
new file mode 100644
index 0000000..26acf6d
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Stdout.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/** A binding annotation for standard out. */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+public @interface Stdout {}
diff --git a/caliper/src/main/java/com/google/caliper/util/Util.java b/caliper/src/main/java/com/google/caliper/util/Util.java
new file mode 100644
index 0000000..a3dc3f7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/util/Util.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteSource;
+import com.google.common.io.Closer;
+import com.google.common.io.Resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public final class Util {
+ private Util() {}
+
+ // Users have no idea that nested classes are identified with '$', not '.', so if class lookup
+ // fails try replacing the last . with $.
+ public static Class<?> lenientClassForName(String className) throws ClassNotFoundException {
+ try {
+ return loadClass(className);
+ } catch (ClassNotFoundException ignored) {
+ // try replacing the last dot with a $, in case that helps
+ // example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1
+ // amusingly, the $ character means three different things in this one line alone
+ String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1");
+ return loadClass(newName);
+ }
+ }
+
+ /**
+ * Search for a class by name.
+ *
+ * @param className the name of the class.
+ * @return the class.
+ * @throws ClassNotFoundException if the class could not be found.
+ */
+ public static Class<?> loadClass(String className) throws ClassNotFoundException {
+ // Use the thread context class loader. This is necessary because in some configurations, e.g.
+ // when run from a single JAR containing caliper and all its dependencies the caliper JAR
+ // ends up on the boot class path of the Worker and so needs to the use thread context class
+ // loader to load classes provided by the user.
+ return Class.forName(className, true, Thread.currentThread().getContextClassLoader());
+ }
+
+ public static ImmutableMap<String, String> loadProperties(ByteSource is) throws IOException {
+ Properties props = new Properties();
+ Closer closer = Closer.create();
+ InputStream in = closer.register(is.openStream());
+ try {
+ props.load(in);
+ } finally {
+ closer.close();
+ }
+ return Maps.fromProperties(props);
+ }
+
+ public static ByteSource resourceSupplier(final Class<?> c, final String name) {
+ return Resources.asByteSource(c.getResource(name));
+ }
+
+ private static <T> ImmutableMap<String, T> prefixedSubmap(
+ Map<String, T> props, String prefix) {
+ ImmutableMap.Builder<String, T> submapBuilder = ImmutableMap.builder();
+ for (Map.Entry<String, T> entry : props.entrySet()) {
+ String name = entry.getKey();
+ if (name.startsWith(prefix)) {
+ submapBuilder.put(name.substring(prefix.length()), entry.getValue());
+ }
+ }
+ return submapBuilder.build();
+ }
+
+ /**
+ * Returns a map containing only those entries whose key starts with {@code <groupName>.}.
+ *
+ * <p>The keys in the returned map have had their {@code <groupName>.} prefix removed.
+ *
+ * <p>e.g. If given a map that contained {@code group.key1 -> value1, key2 -> value2} and a
+ * {@code groupName} of {@code group} it would produce a map containing {@code key1 -> value1}.
+ */
+ public static ImmutableMap<String, String> subgroupMap(
+ Map<String, String> map, String groupName) {
+ return prefixedSubmap(map, groupName + ".");
+ }
+
+ public static boolean isPublic(Member member) {
+ return Modifier.isPublic(member.getModifiers());
+ }
+
+ public static boolean isStatic(Member member) {
+ return Modifier.isStatic(member.getModifiers());
+ }
+
+ private static final long FORCE_GC_TIMEOUT_SECS = 2;
+
+ public static void forceGc() {
+ System.gc();
+ System.runFinalization();
+ final CountDownLatch latch = new CountDownLatch(1);
+ new Object() {
+ @Override protected void finalize() {
+ latch.countDown();
+ }
+ };
+ System.gc();
+ System.runFinalization();
+ try {
+ latch.await(FORCE_GC_TIMEOUT_SECS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public static <T> ImmutableBiMap<T, String> assignNames(Set<T> items) {
+ ImmutableList<T> itemList = ImmutableList.copyOf(items);
+ ImmutableBiMap.Builder<T, String> itemNamesBuilder = ImmutableBiMap.builder();
+ for (int i = 0; i < itemList.size(); i++) {
+ itemNamesBuilder.put(itemList.get(i), generateUniqueName(i));
+ }
+ return itemNamesBuilder.build();
+ }
+
+ private static String generateUniqueName(int index) {
+ if (index < 26) {
+ return String.valueOf((char) ('A' + index));
+ } else {
+ return generateUniqueName(index / 26 - 1) + generateUniqueName(index % 26);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java
new file mode 100644
index 0000000..96fe5e1
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.monitoring.runtime.instrumentation.Sampler;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+
+/**
+ * An {@link AllocationRecorder} that records the number and cumulative size of allocation.
+ */
+final class AggregateAllocationsRecorder extends AllocationRecorder {
+ private final AtomicInteger allocationCount = new AtomicInteger();
+ private final AtomicLong allocationSize = new AtomicLong();
+ private volatile boolean recording = false;
+
+ private final Sampler sampler = new Sampler() {
+ @Override public void sampleAllocation(int arrayCount, String desc, Object newObj,
+ long size) {
+ if (recording) {
+ allocationCount.getAndIncrement();
+ allocationSize.getAndAdd(size);
+ }
+ }
+ };
+
+ @Inject AggregateAllocationsRecorder() {
+ com.google.monitoring.runtime.instrumentation.AllocationRecorder.addSampler(sampler);
+ }
+
+ @Override protected void doStartRecording() {
+ checkState(!recording, "startRecording called, but we were already recording.");
+ allocationCount.set(0);
+ allocationSize.set(0);
+ recording = true;
+ }
+
+ @Override public AllocationStats stopRecording(int reps) {
+ checkState(recording, "stopRecording called, but we were not recording.");
+ recording = false;
+ return new AllocationStats(allocationCount.get(), allocationSize.get(), reps);
+ }
+} \ No newline at end of file
diff --git a/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java
new file mode 100644
index 0000000..ae9baef
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Arrays.asList;
+
+import com.google.caliper.runner.Running;
+import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.monitoring.runtime.instrumentation.Sampler;
+
+import javax.inject.Inject;
+
+/**
+ * An {@link AllocationRecorder} that records every allocation and its location.
+ *
+ * <p>This recorder is enabled via the {@code trackAllocations} worker option.
+ */
+final class AllAllocationsRecorder extends AllocationRecorder {
+ private final Class<?> benchmarkClass;
+ private final String benchmarkMethodName;
+ private volatile boolean recording = false;
+ private final ConcurrentHashMultiset<Allocation> allocations = ConcurrentHashMultiset.create();
+
+ private final Sampler sampler = new Sampler() {
+ @Override public void sampleAllocation(int arrayCount, String desc, Object newObj,
+ long size) {
+ if (recording) {
+ if (arrayCount != -1) {
+ desc = desc + "[" + arrayCount + "]";
+ }
+ // The first item is this line, the second is in AllocationRecorder and the
+ // one before that is the allocating line, so we start at index 2.
+ // We want to grab all lines until we get into the benchmark method.
+ StackTraceElement[] stackTrace = new Exception().getStackTrace();
+ int startIndex = 2;
+ int endIndex = 2;
+ for (int i = startIndex; i < stackTrace.length; i++) {
+ StackTraceElement element = stackTrace[i];
+ if (element.getClassName().startsWith(
+ AllAllocationsRecorder.class.getPackage().getName())) {
+ // Don't track locations up into the worker code, or originating within the worker
+ // code.
+ break;
+ }
+ endIndex = i;
+ if (element.getClassName().equals(benchmarkClass.getName())
+ && element.getMethodName().equals(benchmarkMethodName)) {
+ // stop logging at the method under test
+ break;
+ }
+ }
+ allocations.add(
+ new Allocation(desc, size, asList(stackTrace).subList(startIndex, endIndex + 1)));
+ }
+ }
+ };
+
+ @Inject AllAllocationsRecorder(@Running.BenchmarkClass Class<?> benchmarkClass,
+ @Running.BenchmarkMethod String benchmarkMethodName) {
+ this.benchmarkClass = benchmarkClass;
+ this.benchmarkMethodName = benchmarkMethodName;
+ com.google.monitoring.runtime.instrumentation.AllocationRecorder.addSampler(sampler);
+ }
+
+ @Override protected void doStartRecording() {
+ checkState(!recording, "startRecording called, but we were already recording.");
+ allocations.clear();
+ recording = true;
+ }
+
+ @Override public AllocationStats stopRecording(int reps) {
+ checkState(recording, "stopRecording called, but we were not recording.");
+ recording = false;
+ return new AllocationStats(allocations, reps);
+ }
+} \ No newline at end of file
diff --git a/caliper/src/main/java/com/google/caliper/worker/Allocation.java b/caliper/src/main/java/com/google/caliper/worker/Allocation.java
new file mode 100644
index 0000000..d855dc0
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/Allocation.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Data about a particular allocation performed by a benchmark. This tracks a human readable
+ * description of the allocation (e.g. 'int[23]', 'java.lang.Integer', or 'java.util.ArrayList'),
+ * the total size of the allocation in bytes and the location, which is a stringified stack trace of
+ * the allocation.
+ */
+final class Allocation {
+ // While particular lists of STEs can have a lot of variety within a benchmark there don't tend
+ // to be many individually unique STEs. This can save a lot of memory.
+ // Within a benchmark the code paths should be fairly uniform so it should be safe to just store
+ // these forever.
+ private static final Interner<StackTraceElement> steInterner = Interners.newWeakInterner();
+ private static final Interner<String> descriptionInterner = Interners.newWeakInterner();
+
+ /** Returns the sum of the {@link #size sizes} of the allocations. */
+ static long getTotalSize(Collection<Allocation> allocations) {
+ long totalSize = 0;
+ for (Allocation allocation : allocations) {
+ totalSize += allocation.size;
+ }
+ return totalSize;
+ }
+
+ private final String description;
+ private final long size;
+ private final ImmutableList<StackTraceElement> location;
+
+ Allocation(String description, long size, List<StackTraceElement> location) {
+ this.description = descriptionInterner.intern(description);
+ this.size = size;
+ ImmutableList.Builder<StackTraceElement> locationBuilder = ImmutableList.builder();
+ for (StackTraceElement ste : location) {
+ locationBuilder.add(steInterner.intern(ste));
+ }
+ this.location = locationBuilder.build();
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (obj instanceof Allocation) {
+ Allocation other = (Allocation) obj;
+ return other.description.equals(description)
+ && other.size == size
+ && other.location.equals(location);
+
+ }
+ return false;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(description, size, location);
+ }
+
+ @Override public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(description).append(" (").append(size).append(" bytes)\n\tat ");
+ Joiner.on("\n\tat ").appendTo(builder, location);
+ return builder.toString();
+ }
+} \ No newline at end of file
diff --git a/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java
new file mode 100644
index 0000000..3996e09
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+/**
+ * An object that records all allocations that occur between {@link #startRecording()}
+ * and {@link #stopRecording(int)}.
+ *
+ * <p>This object can accurately track allocations made from multiple threads but is only
+ * expected to have {@link #startRecording()} and {@link #stopRecording(int)} called by a single
+ * thread.
+ */
+abstract class AllocationRecorder {
+ private boolean firstTime = true;
+
+ /**
+ * Clears the prior state and starts a new recording.
+ *
+ * @throws IllegalStateException if the recording infrastructure is misconfigured.
+ */
+ final void startRecording() {
+ if (firstTime) {
+ Object obj;
+ doStartRecording();
+ obj = new Object();
+ AllocationStats stats = stopRecording(1);
+ if (stats.getAllocationCount() != 1 || stats.getAllocationSize() < 1) {
+ throw new IllegalStateException(
+ String.format("The allocation recording infrastructure appears to be broken. "
+ + "Expected to find exactly one allocation of a java/lang/Object instead found %s",
+ stats));
+ }
+ firstTime = false;
+ }
+ doStartRecording();
+ }
+
+ /** Clears the prior state and starts a new recording. */
+ protected abstract void doStartRecording();
+
+ /**
+ * Stops recording allocations and saves all the allocation data recorded since the previous call
+ * to {@link #startRecording()} to an {@link AllocationStats} object.
+ *
+ * @param reps The number of reps that the previous set of allocation represents.
+ */
+ abstract AllocationStats stopRecording(int reps);
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java b/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java
new file mode 100644
index 0000000..af3634c
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Value;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Multiset.Entry;
+import com.google.common.collect.Multisets;
+
+import java.text.DecimalFormat;
+import java.util.Collection;
+
+/**
+ * A set of statistics about the allocations performed by a benchmark method.
+ */
+final class AllocationStats {
+ private final int allocationCount;
+ private final long allocationSize;
+ private final int reps;
+ private final ImmutableMultiset<Allocation> allocations;
+
+ /**
+ * Constructs a new {@link AllocationStats} with the given number of allocations
+ * ({@code allocationCount}), cumulative size of the allocations ({@code allocationSize}) and the
+ * number of {@code reps} passed to the benchmark method.
+ */
+ AllocationStats(int allocationCount, long allocationSize, int reps) {
+ this(allocationCount, allocationSize, reps, ImmutableMultiset.<Allocation>of());
+ }
+
+ /**
+ * Constructs a new {@link AllocationStats} with the given allocations and the number of
+ * {@code reps} passed to the benchmark method.
+ */
+ AllocationStats(Collection<Allocation> allocations, int reps) {
+ this(allocations.size(), Allocation.getTotalSize(allocations), reps,
+ ImmutableMultiset.copyOf(allocations));
+ }
+
+ private AllocationStats(int allocationCount, long allocationSize, int reps,
+ Multiset<Allocation> allocations) {
+ checkArgument(allocationCount >= 0, "allocationCount (%s) was negative", allocationCount);
+ this.allocationCount = allocationCount;
+ checkArgument(allocationSize >= 0, "allocationSize (%s) was negative", allocationSize);
+ this.allocationSize = allocationSize;
+ checkArgument(reps >= 0, "reps (%s) was negative", reps);
+ this.reps = reps;
+ this.allocations = Multisets.copyHighestCountFirst(allocations);
+ }
+
+ int getAllocationCount() {
+ return allocationCount;
+ }
+
+ long getAllocationSize() {
+ return allocationSize;
+ }
+
+ /**
+ * Computes and returns the difference between this measurement and the given
+ * {@code baseline} measurement. The {@code baseline} measurement must have a lower weight
+ * (fewer reps) than this measurement.
+ */
+ AllocationStats minus(AllocationStats baseline) {
+ for (Entry<Allocation> entry : baseline.allocations.entrySet()) {
+ int superCount = allocations.count(entry.getElement());
+ if (superCount < entry.getCount()) {
+ throw new IllegalStateException(
+ String.format("Your benchmark appears to have non-deterministic allocation behavior. "
+ + "Observed %d instance(s) of %s in the baseline but only %d in the actual "
+ + "measurement",
+ entry.getCount(),
+ entry.getElement(),
+ superCount));
+ }
+ }
+ try {
+ return new AllocationStats(allocationCount - baseline.allocationCount,
+ allocationSize - baseline.allocationSize,
+ reps - baseline.reps,
+ Multisets.difference(allocations, baseline.allocations));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(String.format(
+ "Your benchmark appears to have non-deterministic allocation behavior. The difference "
+ + "between the baseline %s and the measurement %s is invalid. Consider enabling "
+ + "instrument.allocation.options.trackAllocations to get a more specific error message.",
+ baseline, this), e);
+ }
+ }
+
+ /**
+ * Computes and returns the difference between this measurement and the given
+ * {@code baseline} measurement. Unlike {@link #minus(AllocationStats)} this does not have to
+ * be a super set of the baseline.
+ */
+ public Delta delta(AllocationStats baseline) {
+ return new Delta(
+ allocationCount - baseline.allocationCount,
+ allocationSize - baseline.allocationSize,
+ reps - baseline.reps,
+ Multisets.difference(allocations, baseline.allocations),
+ Multisets.difference(baseline.allocations, allocations));
+ }
+
+ /**
+ * Returns a list of {@link Measurement measurements} based on this collection of stats.
+ */
+ ImmutableList<Measurement> toMeasurements() {
+ for (Entry<Allocation> entry : allocations.entrySet()) {
+ double allocsPerRep = ((double) entry.getCount()) / reps;
+ System.out.printf("Allocated %f allocs per rep of %s%n", allocsPerRep, entry.getElement());
+ }
+ return ImmutableList.of(
+ new Measurement.Builder()
+ .value(Value.create(allocationCount, ""))
+ .description("objects")
+ .weight(reps)
+ .build(),
+ new Measurement.Builder()
+ .value(Value.create(allocationSize, "B"))
+ .weight(reps)
+ .description("bytes")
+ .build());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof AllocationStats) {
+ AllocationStats that = (AllocationStats) obj;
+ return allocationCount == that.allocationCount
+ && allocationSize == that.allocationSize
+ && reps == that.reps
+ && Objects.equal(allocations, that.allocations);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(allocationCount, allocationSize, reps, allocations);
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("allocationCount", allocationCount)
+ .add("allocationSize", allocationSize)
+ .add("reps", reps)
+ .add("allocations", allocations)
+ .toString();
+ }
+
+ /**
+ * The delta between two different sets of statistics.
+ */
+ static final class Delta {
+ private final int count;
+ private final long size;
+ private final int reps;
+ private final Multiset<Allocation> additions;
+ private final Multiset<Allocation> removals;
+
+ Delta(
+ int count,
+ long size,
+ int reps,
+ Multiset<Allocation> additions,
+ Multiset<Allocation> removals) {
+ this.count = count;
+ this.size = size;
+ this.reps = reps;
+ this.additions = additions;
+ this.removals = removals;
+ }
+
+ /**
+ * Returns the long formatted with a leading +/- sign
+ */
+ private static String formatWithLeadingSign(long n) {
+ return n > 0 ? "+" + n : "" + n;
+ }
+
+ @Override public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("count", formatWithLeadingSign(count))
+ .add("size", formatWithLeadingSign(size))
+ .add("reps", formatWithLeadingSign(reps))
+ .add("additions", additions)
+ .add("removals", removals)
+ .toString();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java b/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java
new file mode 100644
index 0000000..75466f8
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.model.ArbitraryMeasurement;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Value;
+import com.google.caliper.runner.Running.Benchmark;
+import com.google.caliper.runner.Running.BenchmarkMethod;
+import com.google.caliper.util.Util;
+import com.google.common.collect.ImmutableSet;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Worker for arbitrary measurements.
+ */
+public final class ArbitraryMeasurementWorker extends Worker {
+ private final Options options;
+ private final String unit;
+ private final String description;
+
+ @Inject ArbitraryMeasurementWorker(
+ @Benchmark Object benchmark,
+ @BenchmarkMethod Method method,
+ @WorkerOptions Map<String, String> workerOptions) {
+ super(benchmark, method);
+ this.options = new Options(workerOptions);
+ ArbitraryMeasurement annotation = method.getAnnotation(ArbitraryMeasurement.class);
+ this.unit = annotation.units();
+ this.description = annotation.description();
+ }
+
+ @Override public void preMeasure(boolean inWarmup) throws Exception {
+ if (options.gcBeforeEach && !inWarmup) {
+ Util.forceGc();
+ }
+ }
+
+ @Override public Iterable<Measurement> measure() throws Exception {
+ double measured = (Double) benchmarkMethod.invoke(benchmark);
+ return ImmutableSet.of(new Measurement.Builder()
+ .value(Value.create(measured, unit))
+ .weight(1)
+ .description(description)
+ .build());
+ }
+
+ private static class Options {
+ final boolean gcBeforeEach;
+
+ Options(Map<String, String> options) {
+ this.gcBeforeEach = Boolean.parseBoolean(options.get("gcBeforeEach"));
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java
new file mode 100644
index 0000000..45af4f7
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.model.Measurement;
+import com.google.caliper.runner.Running.Benchmark;
+import com.google.caliper.runner.Running.BenchmarkMethod;
+import com.google.common.collect.ImmutableList;
+
+import java.lang.reflect.Method;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link Worker} for the {@code AllocationInstrument}. This class invokes the benchmark method
+ * a few times, with varying numbers of reps, and computes the number of object allocations and the
+ * total size of those allocations.
+ */
+public final class MacrobenchmarkAllocationWorker extends Worker {
+ private final AllocationRecorder recorder;
+
+ @Inject MacrobenchmarkAllocationWorker(@Benchmark Object benchmark,
+ @BenchmarkMethod Method method, AllocationRecorder recorder) {
+ super(benchmark, method);
+ this.recorder = recorder;
+ }
+
+ @Override public void bootstrap() throws Exception {
+ // do one initial measurement and throw away its results
+ measureAllocations(benchmark, benchmarkMethod);
+ }
+
+ @Override public ImmutableList<Measurement> measure() throws Exception {
+ return measureAllocations(benchmark, benchmarkMethod).toMeasurements();
+ }
+
+ private AllocationStats measureAllocations(Object benchmark, Method method) throws Exception {
+ recorder.startRecording();
+ method.invoke(benchmark);
+ return recorder.stopRecording(1);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java
new file mode 100644
index 0000000..5960084
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import static com.google.caliper.util.Reflection.getAnnotatedMethods;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.caliper.api.AfterRep;
+import com.google.caliper.api.BeforeRep;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Value;
+import com.google.caliper.runner.Running.Benchmark;
+import com.google.caliper.runner.Running.BenchmarkMethod;
+import com.google.caliper.util.Util;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableSet;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link Worker} implementation for macrobenchmarks.
+ */
+public class MacrobenchmarkWorker extends Worker {
+ private final Stopwatch stopwatch;
+ private final ImmutableSet<Method> beforeRepMethods;
+ private final ImmutableSet<Method> afterRepMethods;
+ private final boolean gcBeforeEach;
+
+ @Inject MacrobenchmarkWorker(@Benchmark Object benchmark, @BenchmarkMethod Method method,
+ Ticker ticker, @WorkerOptions Map<String, String> workerOptions) {
+ super(benchmark, method);
+ this.stopwatch = Stopwatch.createUnstarted(ticker);
+ this.beforeRepMethods =
+ getAnnotatedMethods(benchmark.getClass(), BeforeRep.class);
+ this.afterRepMethods =
+ getAnnotatedMethods(benchmark.getClass(), AfterRep.class);
+ this.gcBeforeEach = Boolean.parseBoolean(workerOptions.get("gcBeforeEach"));
+ }
+
+ @Override public void preMeasure(boolean inWarmup) throws Exception {
+ for (Method beforeRepMethod : beforeRepMethods) {
+ beforeRepMethod.invoke(benchmark);
+ }
+ if (gcBeforeEach && !inWarmup) {
+ Util.forceGc();
+ }
+ }
+
+ @Override public Iterable<Measurement> measure() throws Exception {
+ stopwatch.start();
+ benchmarkMethod.invoke(benchmark);
+ long nanos = stopwatch.stop().elapsed(NANOSECONDS);
+ stopwatch.reset();
+ return ImmutableSet.of(new Measurement.Builder()
+ .description("runtime")
+ .weight(1)
+ .value(Value.create(nanos, "ns"))
+ .build());
+ }
+
+ @Override public void postMeasure() throws Exception {
+ for (Method afterRepMethod : afterRepMethods) {
+ afterRepMethod.invoke(benchmark);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java b/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java
new file mode 100644
index 0000000..a4797b0
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.model.Measurement;
+import com.google.caliper.runner.Running.Benchmark;
+import com.google.caliper.runner.Running.BenchmarkMethod;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link Worker} for the {@code AllocationInstrument}. This class invokes the benchmark method
+ * a few times, with varying numbers of reps, and computes the number of object allocations and the
+ * total size of those allocations.
+ */
+public final class MicrobenchmarkAllocationWorker extends Worker {
+ // TODO(gak): make this or something like this an option
+ private static final int WARMUP_REPS = 10;
+ private static final int MAX_REPS = 100;
+
+ /**
+ * The number of consecutive measurement runs that must have matching allocations during the warm
+ * up in order for the method to be determined to be deterministic.
+ */
+ private static final int DETERMINISTIC_BENCHMARK_THRESHOLD = 2;
+
+ /**
+ * The maximum number of warm up measurements to take before determining that the test is
+ * non-deterministic.
+ */
+ private static final int DETERMINISTIC_MEASUREMENT_COUNT = DETERMINISTIC_BENCHMARK_THRESHOLD + 3;
+
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ private final Random random;
+ private final AllocationRecorder recorder;
+
+ @Inject MicrobenchmarkAllocationWorker(@Benchmark Object benchmark,
+ @BenchmarkMethod Method method, AllocationRecorder recorder, Random random) {
+ super(benchmark, method);
+ this.random = random;
+ this.recorder = recorder;
+ }
+
+ @Override public void bootstrap() throws Exception {
+ // do some initial measurements and throw away the results. this warms up the bootstrap method
+ // itself and also the method invocation path for calling that method.
+
+ // warm up the loop in the benchmark method.
+ measureAllocations(benchmark, benchmarkMethod, WARMUP_REPS);
+
+ // verify that the benchmark is deterministic in terms of the measured allocations.
+ verifyBenchmarkIsDeterministic();
+ }
+
+ /**
+ * Verify the determinism of the benchmark method.
+ *
+ * <p>The method invocation path, i.e. the code that the JVM executes to invoke the method, can
+ * vary depending on how many times it is run with a corresponding effect on the allocations
+ * measured. The invocations performed by this method should be sufficient to cause the JVM to
+ * settle on a single path for invoking the benchmark method and so cause identical allocations
+ * for each subsequent invocation. If tests start to fail with lots of non-deterministic
+ * allocation errors then it's possible that additional invocations are required in which case
+ * the value of {@link #DETERMINISTIC_BENCHMARK_THRESHOLD} should be increased.
+ */
+ private void verifyBenchmarkIsDeterministic() throws Exception {
+ // keep track of all the statistics generated while warming up the method invocation path.
+ List<AllocationStats> history = new ArrayList<AllocationStats>();
+
+ // warm up the method invocation path by calling the benchmark multiple times with 0 reps.
+ AllocationStats baseline = null;
+ int matchingSequenceLength = 1;
+ for (int i = 0; i < DETERMINISTIC_MEASUREMENT_COUNT; ++i) {
+ AllocationStats stats = measureAllocations(benchmark, benchmarkMethod, 0);
+ history.add(stats);
+ if (stats.equals(baseline)) {
+ // if consecutive measurements with the same allocation characteristics reaches the
+ // threshold then treat the benchmark as being deterministic.
+ if (++matchingSequenceLength == DETERMINISTIC_BENCHMARK_THRESHOLD) {
+ return;
+ }
+ } else {
+ matchingSequenceLength = 1;
+ baseline = stats;
+ }
+ }
+
+ // the baseline allocations did not settle down and so are probably non-deterministic.
+ StringBuilder builder = new StringBuilder(100);
+ AllocationStats previous = null;
+ for (AllocationStats allocationStats : history) {
+ if (previous == null) {
+ builder.append(LINE_SEPARATOR).append(" ").append(allocationStats);
+ } else {
+ AllocationStats.Delta delta = allocationStats.delta(previous);
+ builder.append(LINE_SEPARATOR).append(" ").append(delta);
+ }
+ previous = allocationStats;
+ }
+ throw new IllegalStateException(String.format(
+ "Your benchmark appears to have non-deterministic allocation behavior. "
+ + "During the warm up process there was no consecutive sequence of %d runs with"
+ + " identical allocations. The allocation history is:%s",
+ DETERMINISTIC_BENCHMARK_THRESHOLD, builder));
+ }
+
+ @Override public Iterable<Measurement> measure() throws Exception {
+ AllocationStats baseline = measureAllocations(benchmark, benchmarkMethod, 0);
+ // [1, MAX_REPS]
+ int measurementReps = random.nextInt(MAX_REPS) + 1;
+ AllocationStats measurement = measureAllocations(benchmark, benchmarkMethod, measurementReps);
+ return measurement.minus(baseline).toMeasurements();
+ }
+
+ private AllocationStats measureAllocations(
+ Object benchmark, Method method, int reps) throws Exception {
+ // do the Integer boxing and the creation of the Object[] outside of the record block, so that
+ // our internal allocations aren't counted in the benchmark's allocations.
+ Object[] args = {reps};
+ recorder.startRecording();
+ method.invoke(benchmark, args);
+ return recorder.stopRecording(reps);
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java b/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java
new file mode 100644
index 0000000..7d96229
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Value;
+import com.google.caliper.runner.InvalidBenchmarkException;
+import com.google.caliper.runner.Running.Benchmark;
+import com.google.caliper.runner.Running.BenchmarkMethod;
+import com.google.caliper.util.ShortDuration;
+import com.google.caliper.util.Util;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableSet;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Random;
+
+import javax.inject.Inject;
+
+/**
+ * A {@link Worker} base class for micro and pico benchmarks.
+ */
+public abstract class RuntimeWorker extends Worker {
+ @VisibleForTesting static final int INITIAL_REPS = 100;
+
+ protected final Random random;
+ protected final Ticker ticker;
+ protected final Options options;
+ private long totalReps;
+ private long totalNanos;
+ private long nextReps;
+
+ RuntimeWorker(Object benchmark,
+ Method method, Random random, Ticker ticker,
+ Map<String, String> workerOptions) {
+ super(benchmark, method);
+ this.random = random;
+ // TODO(gak): investigate whether or not we can use Stopwatch
+ this.ticker = ticker;
+ this.options = new Options(workerOptions);
+ }
+
+ @Override public void bootstrap() throws Exception {
+ totalReps = INITIAL_REPS;
+ totalNanos = invokeTimeMethod(INITIAL_REPS);
+ }
+
+ @Override public void preMeasure(boolean inWarmup) throws Exception {
+ nextReps = calculateTargetReps(totalReps, totalNanos, options.timingIntervalNanos,
+ random.nextGaussian());
+ if (options.gcBeforeEach && !inWarmup) {
+ Util.forceGc();
+ }
+ }
+
+ @Override public Iterable<Measurement> measure() throws Exception {
+ long nanos = invokeTimeMethod(nextReps);
+ Measurement measurement = new Measurement.Builder()
+ .description("runtime")
+ .value(Value.create(nanos, "ns"))
+ .weight(nextReps)
+ .build();
+
+ totalReps += nextReps;
+ totalNanos += nanos;
+ return ImmutableSet.of(measurement);
+ }
+
+ abstract long invokeTimeMethod(long reps) throws Exception;
+
+ /**
+ * Returns a random number of reps based on a normal distribution around the estimated number of
+ * reps for the timing interval. The distribution used has a standard deviation of one fifth of
+ * the estimated number of reps.
+ */
+ @VisibleForTesting static long calculateTargetReps(long reps, long nanos, long targetNanos,
+ double gaussian) {
+ double targetReps = (((double) reps) / nanos) * targetNanos;
+ return Math.max(1L, Math.round((gaussian * (targetReps / 5)) + targetReps));
+ }
+
+ /**
+ * A {@link Worker} for micro benchmarks.
+ */
+ public static final class Micro extends RuntimeWorker {
+ @Inject Micro(@Benchmark Object benchmark,
+ @BenchmarkMethod Method method, Random random, Ticker ticker,
+ @WorkerOptions Map<String, String> workerOptions) {
+ super(benchmark, method, random, ticker, workerOptions);
+ }
+
+ @Override long invokeTimeMethod(long reps) throws Exception {
+ int intReps = (int) reps;
+ if (reps != intReps) {
+ throw new InvalidBenchmarkException("%s.%s takes an int for reps, "
+ + "but requires a greater number to fill the given timing interval (%s). "
+ + "If this is expected (the benchmarked code is very fast), use a long parameter."
+ + "Otherwise, check your benchmark for errors.",
+ benchmark.getClass(), benchmarkMethod.getName(),
+ ShortDuration.of(options.timingIntervalNanos, NANOSECONDS));
+ }
+ long before = ticker.read();
+ benchmarkMethod.invoke(benchmark, intReps);
+ return ticker.read() - before;
+ }
+ }
+
+ /**
+ * A {@link Worker} for pico benchmarks.
+ */
+ public static final class Pico extends RuntimeWorker {
+ @Inject Pico(@Benchmark Object benchmark,
+ @BenchmarkMethod Method method, Random random, Ticker ticker,
+ @WorkerOptions Map<String, String> workerOptions) {
+ super(benchmark, method, random, ticker, workerOptions);
+ }
+
+ @Override long invokeTimeMethod(long reps) throws Exception {
+ long before = ticker.read();
+ benchmarkMethod.invoke(benchmark, reps);
+ return ticker.read() - before;
+ }
+ }
+
+ private static final class Options {
+ long timingIntervalNanos;
+ boolean gcBeforeEach;
+
+ Options(Map<String, String> optionMap) {
+ this.timingIntervalNanos = Long.parseLong(optionMap.get("timingIntervalNanos"));
+ this.gcBeforeEach = Boolean.parseBoolean(optionMap.get("gcBeforeEach"));
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/Worker.java b/caliper/src/main/java/com/google/caliper/worker/Worker.java
new file mode 100644
index 0000000..d0f5c08
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/Worker.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.caliper.worker;
+
+import com.google.caliper.model.Measurement;
+import com.google.caliper.runner.Running.AfterExperimentMethods;
+import com.google.caliper.runner.Running.BeforeExperimentMethods;
+import com.google.common.collect.ImmutableSet;
+
+import java.lang.reflect.Method;
+
+import javax.inject.Inject;
+
+/**
+ * A {@link Worker} collects measurements on behalf of a particular Instrument.
+ */
+public abstract class Worker {
+ @Inject
+ @BeforeExperimentMethods
+ ImmutableSet<Method> beforeExperimentMethods;
+
+ @Inject
+ @AfterExperimentMethods
+ ImmutableSet<Method> afterExperimentMethods;
+
+ protected final Method benchmarkMethod;
+ protected final Object benchmark;
+
+ protected Worker(Object benchmark, Method method) {
+ this.benchmark = benchmark;
+ this.benchmarkMethod = method;
+ }
+
+ /** Initializes the benchmark object. */
+ final void setUpBenchmark() throws Exception {
+ for (Method method : beforeExperimentMethods) {
+ method.invoke(benchmark);
+ }
+ }
+
+ /** Called once before all measurements but after benchmark setup. */
+ public void bootstrap() throws Exception {}
+
+ /**
+ * Called immediately before {@link #measure()}.
+ *
+ * @param inWarmup whether we are in warmup, or taking real measurements. Used by
+ * some implementations to skip forcing GC to make warmup faster.
+ */
+ public void preMeasure(boolean inWarmup) throws Exception {}
+
+ /** Called immediately after {@link #measure()}. */
+ public void postMeasure() throws Exception {}
+
+ /** Template method for workers that produce multiple measurements. */
+ public abstract Iterable<Measurement> measure() throws Exception;
+
+ /** Tears down the benchmark object. */
+ final void tearDownBenchmark() throws Exception {
+ for (Method method : afterExperimentMethods) {
+ method.invoke(benchmark);
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java b/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java
new file mode 100644
index 0000000..40b11da
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.bridge.BridgeModule;
+import com.google.caliper.runner.BenchmarkClassModule;
+import com.google.caliper.runner.ExperimentModule;
+import dagger.Component;
+import javax.inject.Singleton;
+
+/**
+ * Creates {@link Worker} for an {@link com.google.caliper.runner.Experiment}.
+ */
+@Singleton
+@Component(modules = {
+ BenchmarkClassModule.class,
+ BridgeModule.class,
+ ExperimentModule.class,
+ WorkerModule.class
+})
+interface WorkerComponent {
+ Worker getWorker();
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java b/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java
new file mode 100644
index 0000000..7b903d4
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.bridge.FailureLogMessage;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.ShouldContinueMessage;
+import com.google.caliper.bridge.StartMeasurementLogMessage;
+import com.google.caliper.bridge.StartupAnnounceMessage;
+import com.google.caliper.bridge.StopMeasurementLogMessage;
+import com.google.caliper.bridge.VmPropertiesLogMessage;
+import com.google.caliper.model.Measurement;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.UUID;
+
+/** The worker's interface for communicating with the runner. */
+final class WorkerEventLog implements Closeable {
+ private final OpenedSocket.Writer writer;
+ private final OpenedSocket.Reader reader;
+
+ WorkerEventLog(OpenedSocket socket) {
+ this.writer = socket.writer();
+ this.reader = socket.reader();
+ }
+
+ void notifyWorkerStarted(UUID trialId) throws IOException {
+ writer.write(new StartupAnnounceMessage(trialId));
+ writer.write(new VmPropertiesLogMessage());
+ writer.flush();
+ }
+
+ void notifyBootstrapPhaseStarting() throws IOException {
+ writer.write("Bootstrap phase starting.");
+ writer.flush();
+ }
+
+ void notifyMeasurementPhaseStarting() throws IOException {
+ writer.write("Measurement phase starting (includes warmup and actual measurement).");
+ writer.flush();
+ }
+
+ void notifyMeasurementStarting() throws IOException {
+ writer.write("About to measure.");
+ writer.write(new StartMeasurementLogMessage());
+ writer.flush();
+ }
+
+ /**
+ * Report the measurements and wait for it to be ack'd by the runner. Returns a message received
+ * from the runner, which lets us know whether to continue measuring and whether we're in the
+ * warmup or measurement phase.
+ */
+ ShouldContinueMessage notifyMeasurementEnding(Iterable<Measurement> measurements) throws
+ IOException {
+ writer.write(new StopMeasurementLogMessage(measurements));
+ writer.flush();
+ return (ShouldContinueMessage) reader.read();
+ }
+
+ void notifyFailure(Exception e) throws IOException {
+ writer.write(new FailureLogMessage(e));
+ writer.flush();
+ }
+
+ @Override public void close() throws IOException {
+ try {
+ reader.close();
+ } finally {
+ writer.close();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java b/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java
new file mode 100644
index 0000000..34ec674
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.bridge.CommandLineSerializer;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.ShouldContinueMessage;
+import com.google.caliper.bridge.WorkerSpec;
+import com.google.caliper.runner.ExperimentModule;
+import com.google.common.net.InetAddresses;
+
+import java.net.InetSocketAddress;
+import java.nio.channels.SocketChannel;
+
+/**
+ * This class is invoked as a subprocess by the Caliper runner parent process; it re-stages
+ * the benchmark and hands it off to the instrument's worker.
+ */
+public final class WorkerMain {
+ private WorkerMain() {}
+
+ public static void main(String[] args) throws Exception {
+ // TODO(lukes): instead of parsing the spec from the command line pass the port number on the
+ // command line and then receive the spec from the socket. This way we can start JVMs prior
+ // to starting experiments and thus get better experiment latency.
+ WorkerSpec request = CommandLineSerializer.parse(args[0]);
+ // nonblocking connect so we can interleave the system call with injector creation.
+ SocketChannel channel = SocketChannel.open();
+ channel.configureBlocking(false);
+ channel.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), request.port));
+
+ WorkerComponent workerComponent = DaggerWorkerComponent.builder()
+ .experimentModule(ExperimentModule.forWorkerSpec(request))
+ .workerModule(new WorkerModule(request))
+ .build();
+ Worker worker = workerComponent.getWorker();
+ WorkerEventLog log = new WorkerEventLog(OpenedSocket.fromSocket(channel));
+
+ log.notifyWorkerStarted(request.trialId);
+ try {
+ worker.setUpBenchmark();
+ log.notifyBootstrapPhaseStarting();
+ worker.bootstrap();
+ log.notifyMeasurementPhaseStarting();
+ boolean keepMeasuring = true;
+ boolean isInWarmup = true;
+ while (keepMeasuring) {
+ worker.preMeasure(isInWarmup);
+ log.notifyMeasurementStarting();
+ try {
+ ShouldContinueMessage message = log.notifyMeasurementEnding(worker.measure());
+ keepMeasuring = message.shouldContinue();
+ isInWarmup = !message.isWarmupComplete();
+ } finally {
+ worker.postMeasure();
+ }
+ }
+ } catch (Exception e) {
+ log.notifyFailure(e);
+ } finally {
+ System.out.flush(); // ?
+ worker.tearDownBenchmark();
+ log.close();
+ }
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java b/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java
new file mode 100644
index 0000000..1f7e201
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.worker;
+
+import com.google.caliper.Param;
+import com.google.caliper.bridge.WorkerSpec;
+import com.google.caliper.runner.Running;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.Util;
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableMap;
+import dagger.MapKey;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Provides.Type;
+
+import java.util.Map;
+import java.util.Random;
+
+import javax.inject.Provider;
+
+/**
+ * Binds classes necessary for the worker. Also manages the injection of {@link Param parameters}
+ * from the {@link WorkerSpec} into the benchmark.
+ *
+ * <p>TODO(gak): Ensure that each worker only has bindings for the objects it needs and not the
+ * objects required by different workers. (i.e. don't bind a Ticker if the worker is an allocation
+ * worker).
+ */
+@Module
+final class WorkerModule {
+ private final Class<? extends Worker> workerClass;
+ private final ImmutableMap<String, String> workerOptions;
+
+ private final Class<?> benchmarkClassObject;
+
+ WorkerModule(WorkerSpec workerSpec) throws ClassNotFoundException {
+ this.workerClass = workerSpec.workerClass.asSubclass(Worker.class);
+ this.workerOptions = workerSpec.workerOptions;
+
+ benchmarkClassObject = Util.loadClass(workerSpec.benchmarkSpec.className());
+ }
+
+ @Provides
+ @Running.BenchmarkClass
+ Class<?> provideBenchmarkClassObject() {
+ return benchmarkClassObject;
+ }
+
+ @Provides
+ Worker provideWorker(Map<Class<? extends Worker>, Provider<Worker>> availableWorkers) {
+ Provider<Worker> workerProvider = availableWorkers.get(workerClass);
+ if (workerProvider == null) {
+ throw new InvalidCommandException("%s is not a supported worker (%s).",
+ workerClass, availableWorkers);
+ }
+ return workerProvider.get();
+ }
+
+ /**
+ * Specifies the {@link Class} object to use as a key in the map of available
+ * {@link Worker workers} passed to {@link #provideWorker(Map)}.
+ */
+ @MapKey(unwrapValue = true)
+ public @interface WorkerClassKey {
+ Class<? extends Worker> value();
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(ArbitraryMeasurementWorker.class)
+ static Worker provideArbitraryMeasurementWorker(ArbitraryMeasurementWorker impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(MicrobenchmarkAllocationWorker.class)
+ static Worker provideMicrobenchmarkAllocationWorker(MicrobenchmarkAllocationWorker impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(MacrobenchmarkWorker.class)
+ static Worker provideMacrobenchmarkWorker(MacrobenchmarkWorker impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(MacrobenchmarkAllocationWorker.class)
+ static Worker provideMacrobenchmarkAllocationWorker(MacrobenchmarkAllocationWorker impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(RuntimeWorker.Micro.class)
+ static Worker provideRuntimeWorkerMicro(RuntimeWorker.Micro impl) {
+ return impl;
+ }
+
+ @Provides(type = Type.MAP)
+ @WorkerClassKey(RuntimeWorker.Pico.class)
+ static Worker provideRuntimeWorkerPico(RuntimeWorker.Pico impl) {
+ return impl;
+ }
+
+ @Provides
+ static Ticker provideTicker() {
+ return Ticker.systemTicker();
+ }
+
+ @Provides
+ AllocationRecorder provideAllocationRecorder(
+ Provider<AllAllocationsRecorder> allAllocationsRecorderProvider,
+ Provider<AggregateAllocationsRecorder> aggregateAllocationsRecorderProvider) {
+
+ return Boolean.valueOf(workerOptions.get("trackAllocations"))
+ ? allAllocationsRecorderProvider.get()
+ : aggregateAllocationsRecorderProvider.get();
+ }
+
+ @Provides
+ static Random provideRandom() {
+ return new Random();
+ }
+
+ @Provides
+ @WorkerOptions
+ Map<String, String> provideWorkerOptions() {
+ return workerOptions;
+ }
+}
diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java b/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java
new file mode 100644
index 0000000..bc93b18
--- /dev/null
+++ b/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.worker;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER, METHOD})
+@Qualifier
+@interface WorkerOptions {}
diff --git a/caliper/src/main/resources/com/google/caliper/config/default-config.properties b/caliper/src/main/resources/com/google/caliper/config/default-config.properties
new file mode 100644
index 0000000..ee5b3ee
--- /dev/null
+++ b/caliper/src/main/resources/com/google/caliper/config/default-config.properties
@@ -0,0 +1,14 @@
+# Caliper config file
+# Run with --print-config to see all of the options being applied
+
+# INSTRUMENT CONFIG
+# instrument.micro.options.warmup=10s
+# instrument.micro.options.timingInterval=500ms
+# instrument.micro.options.reportedIntervals=7
+# instrument.micro.options.maxRuntime=10s
+
+# VM CONFIG
+vm.args=-Xmx3g -Xms3g
+
+# See the Caliper webapp to get a key so you can associate results with your account
+results.upload.options.key=
diff --git a/caliper/src/main/resources/com/google/caliper/config/global-config.properties b/caliper/src/main/resources/com/google/caliper/config/global-config.properties
new file mode 100644
index 0000000..dd741f0
--- /dev/null
+++ b/caliper/src/main/resources/com/google/caliper/config/global-config.properties
@@ -0,0 +1,90 @@
+# Caliper global config file
+# Users' ~/.caliper/config settings may override these
+
+######################
+# VM CONFIGURATION
+######################
+
+# This directory can be automatically prepended to non-absolute VM paths
+vm.baseDirectory=/usr/local/buildtools/java
+
+# Standard vm parameter options.
+vm.args=
+
+# Common configurations
+
+vm.jdk-32-client.home=jdk-32
+vm.jdk-32-client.args=-d32 -client
+
+vm.jdk-32-server.home=jdk-32
+vm.jdk-32-server.args=-d32 -server
+
+vm.jdk-64-compressed.home=jdk-64
+vm.jdk-64-compressed.args=-d64 -XX:+UseCompressedOops
+
+vm.jdk-64-uncompressed.home=jdk-64
+vm.jdk-64-uncompressed.args=-d64 -XX:-UseCompressedOops
+
+
+######################
+# INSTRUMENT CONFIG
+######################
+
+# To define new instrument configurations, provide an "instrument.<name>.class" property
+# pointing to a concrete class that extends com.google.caliper.runner.Instrument, and add
+# whichever other options it supports using "instrument.<name>.<optionName>=<value>".
+
+# Instrument "runtime"
+instrument.runtime.class=com.google.caliper.runner.RuntimeInstrument
+
+# Do not report any measurements from before this minimum time has elapsed
+instrument.runtime.options.warmup=10s
+# Interrupt warmup when it has been running for this much wall-clock time,
+# even if the measured warmup time (above) hasn't been reached. This prevents fast benchmarks
+# with high per-measurement overhead (e.g. long @BeforeRep and @AfterRep methods)
+# from taking too long to warm up.
+instrument.runtime.options.maxWarmupWallTime=10m
+
+# Caliper chooses rep counts such that the total timing interval comes out near this value.
+# Higher values take longer, but are more precise (less vulnerable to fixed costs)
+instrument.runtime.options.timingInterval=500ms
+
+# Caliper ultimately records only the final N measurements, where N is this value.
+instrument.runtime.options.measurements=9
+
+# Run GC before every measurement?
+instrument.runtime.options.gcBeforeEach=true
+
+# Whether or not to make suggestions about whether a benchmark should be a pico/micro/macro
+# benchmark. Note that this will not effect errors that result from benchmarks that are unable to
+# take proper measurements due to granularity issues.
+instrument.runtime.options.suggestGranularity=true
+
+# Instrument "arbitrary"
+instrument.arbitrary.class=com.google.caliper.runner.ArbitraryMeasurementInstrument
+
+# Run GC before every measurement?
+instrument.arbitrary.options.gcBeforeEach=false
+
+# Instrument "allocation"
+instrument.allocation.class=com.google.caliper.runner.AllocationInstrument
+
+# Track and log a summary of every individual allocation. This enables better error messages for
+# buggy benchmarks and prints detailed reports of allocation behavior in verbose mode. N.B. This
+# can increase the memory usage of the allocation worker significantly, so it is not recommended
+# for benchmarks that do a lot of allocation.
+instrument.allocation.options.trackAllocations=false
+
+
+# Sets the maximum number of trials that can run in parallel.
+runner.maxParallelism=2
+
+######################
+# RESULTS PROCESSORS
+######################
+
+results.file.class=com.google.caliper.runner.OutputFileDumper
+
+results.upload.class=com.google.caliper.runner.HttpUploader
+
+results.upload.options.url=https://microbenchmarks.appspot.com/
diff --git a/caliper/src/test/java/com/google/caliper/CaliperTest.java b/caliper/src/test/java/com/google/caliper/CaliperTest.java
deleted file mode 100644
index 18857ba..0000000
--- a/caliper/src/test/java/com/google/caliper/CaliperTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.base.Supplier;
-import java.util.Map;
-import java.util.Set;
-import junit.framework.TestCase;
-
-public final class CaliperTest extends TestCase {
-
- /**
- * Test we detect and fail when benchmarks don't scale properly.
- * @throws Exception
- */
- public void testBenchmarkScalesNonLinearly() throws Exception {
- TimeMeasurer timeMeasurer = new TimeMeasurer(1000, 1000);
- try {
- timeMeasurer.run(new NonLinearTimedRunnable());
- fail();
- } catch (UserException.DoesNotScaleLinearlyException e) {
- }
- }
-
- private static class NonLinearTimedRunnable extends ConfiguredBenchmark
- implements Supplier<ConfiguredBenchmark> {
- private NonLinearTimedRunnable() {
- super(new NoOpBenchmark());
- }
-
- @Override public ConfiguredBenchmark get() {
- return this;
- }
-
- @Override public Object run(int reps) throws Exception {
- return null; // broken! doesn't loop reps times.
- }
-
- @Override public void close() throws Exception {}
- }
-
- private static class NoOpBenchmark implements Benchmark {
- @Override public Set<String> parameterNames() {
- return null;
- }
-
- @Override public Set<String> parameterValues(String parameterName) {
- return null;
- }
-
- @Override public ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues) {
- return null;
- }
-
- @Override public Map<String, Integer> getTimeUnitNames() {
- return null;
- }
-
- @Override public Map<String, Integer> getInstanceUnitNames() {
- return null;
- }
-
- @Override public Map<String, Integer> getMemoryUnitNames() {
- return null;
- }
-
- @Override public double nanosToUnits(double nanos) {
- return 0;
- }
-
- @Override public double instancesToUnits(long instances) {
- return 0;
- }
-
- @Override public double bytesToUnits(long bytes) {
- return 0;
- }
- }
-}
diff --git a/caliper/src/test/java/com/google/caliper/JsonTest.java b/caliper/src/test/java/com/google/caliper/JsonTest.java
deleted file mode 100644
index cee02b0..0000000
--- a/caliper/src/test/java/com/google/caliper/JsonTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMap;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Map;
-import junit.framework.TestCase;
-
-public final class JsonTest extends TestCase {
-
- public void testJsonSerialization() {
- Result original = newSampleResult();
- String json = Json.getGsonInstance().toJson(original, Result.class);
- Result reserialized = Json.getGsonInstance().fromJson(json, Result.class);
- assertEquals(original, reserialized);
- }
-
- /**
- * Caliper's JSON files used to include dates specific to the host machine's
- * locale. http://code.google.com/p/caliper/issues/detail?id=113
- */
- public void testJsonSerializationWithFancyLocale() {
- Result original = newSampleResult();
-
- // serialize in one locale...
- Locale defaultLocale = Locale.getDefault();
- Locale.setDefault(Locale.ITALY);
- String json;
- try {
- json = Json.getGsonInstance().toJson(original, Result.class);
- } finally {
- Locale.setDefault(defaultLocale);
- }
-
- // deserialize in another
- Result reserialized = Json.getGsonInstance().fromJson(json, Result.class);
- assertEquals(original, reserialized);
- }
-
- private Result newSampleResult() {
- Map<String,Integer> units = ImmutableMap.of("ns", 1);
- MeasurementSet timeMeasurements = new MeasurementSet(new Measurement(units, 2.0, 2.0));
- Date executedDate = new Date(0);
- Scenario scenario = new Scenario(ImmutableMap.of("benchmark", "Foo"));
- ScenarioResult scenarioResult = new ScenarioResult(
- timeMeasurements, "log", null, null, null, null);
- Run run = new Run(ImmutableMap.of(scenario, scenarioResult), "foo.FooBenchmark", executedDate);
- Environment environment = new Environment(ImmutableMap.of("os.name", "Linux"));
- return new Result(run, environment);
- }
-}
diff --git a/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java b/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java
deleted file mode 100644
index f6f7cbb..0000000
--- a/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Ordering;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import junit.framework.TestCase;
-
-public class MeasurementSetTest extends TestCase {
-
- Ordering<Measurement> MEASUREMENT_BY_NANOS = new Ordering<Measurement>() {
- @Override public int compare(Measurement a, Measurement b) {
- return Double.compare(a.getRaw(), b.getRaw());
- }
- };
-
- public void testIncompatibleMeasurements() {
- Measurement[] measurements = new Measurement[2];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("triplens", 1), 3.8, 7.6);
- try {
- new MeasurementSet(measurements);
- fail("illegal argument exception not thrown");
- } catch (IllegalArgumentException e) {
- // success
- }
- }
-
- public void testIncompatibleAddedMeasurements() {
- Measurement[] measurements = new Measurement[1];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- try {
- measurementSet.plusMeasurement(new Measurement(ImmutableMap.of("triplens", 1), 3.8, 7.6));
- fail("illegal argument exception not thrown");
- } catch (IllegalArgumentException e) {
- // success
- }
- }
-
- public void testSize() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals(3, measurementSet.size());
-
- Measurement[] measurements2 = new Measurement[4];
- measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4);
- MeasurementSet measurementSet2 =
- new MeasurementSet(measurements2);
- assertEquals(4, measurementSet2.size());
- }
-
- public void testPlusMeasurement() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
-
- Measurement[] measurements2 = new Measurement[4];
- measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4);
- MeasurementSet measurementSet2 =
- new MeasurementSet(measurements2);
-
- MeasurementSet measurementSet3 = measurementSet.plusMeasurement(measurements2[3]);
-
- assertDoubleListsEquals(measurementSet2.getMeasurementsRaw(),
- measurementSet3.getMeasurementsRaw(), 0.0000001);
- assertDoubleListsEquals(measurementSet2.getMeasurementUnits(),
- measurementSet3.getMeasurementUnits(), 0.0000001);
- assertEquals(measurementSet2.getUnitNames(), measurementSet3.getUnitNames());
-
- List<Measurement> measurementList1 =
- MEASUREMENT_BY_NANOS.sortedCopy(measurementSet2.getMeasurements());
- List<Measurement> measurementList2 =
- MEASUREMENT_BY_NANOS.sortedCopy(measurementSet3.getMeasurements());
- assertEquals(measurementList1.size(), measurementList2.size());
- for (int i = 0; i < measurementList1.size(); i++) {
- assertEquals(measurementList1.get(i).getRaw(),
- measurementList2.get(i).getRaw());
- assertEquals(measurementList1.get(i).getProcessed(),
- measurementList2.get(i).getProcessed());
- }
- }
-
- public void testMedian() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals(2.3, measurementSet.medianRaw(), 0.00000001);
- assertEquals(4.6, measurementSet.medianUnits(), 0.00000001);
-
- Measurement[] measurements2 = new Measurement[4];
- measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4);
- MeasurementSet measurementSet2 =
- new MeasurementSet(measurements2);
- assertEquals((2.3 + 3.8) / 2, measurementSet2.medianRaw(), 0.00000001);
- assertEquals((4.6 + 7.6) / 2, measurementSet2.medianUnits(), 0.00000001);
- }
-
- public void testMean() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals((1.1 + 3.8 + 2.3) / 3, measurementSet.meanRaw(), 0.00000001);
- assertEquals((2.2 + 7.6 + 4.6) / 3, measurementSet.meanUnits(), 0.00000001);
-
- Measurement[] measurements2 = new Measurement[4];
- measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4);
- MeasurementSet measurementSet2 =
- new MeasurementSet(measurements2);
- assertEquals((1.1 + 2.3 + 3.8 + 7.2) / 4, measurementSet2.meanRaw(), 0.00000001);
- assertEquals((2.2 + 4.6 + 7.6 + 14.4) / 4, measurementSet2.meanUnits(), 0.00000001);
- }
-
- public void testStandardDeviation() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals(1.35277, measurementSet.standardDeviationRaw(), 0.00001);
- assertEquals(2.70555, measurementSet.standardDeviationUnits(), 0.00001);
- }
-
- public void testMax() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals(3.8, measurementSet.maxRaw(), 0.00000001);
- assertEquals(7.6, measurementSet.maxUnits(), 0.00000001);
- }
-
- public void testMin() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- assertEquals(1.1, measurementSet.minRaw(), 0.00000001);
- assertEquals(2.2, measurementSet.minUnits(), 0.00000001);
- }
-
- public void testJsonRoundtrip() {
- Measurement[] measurements = new Measurement[3];
- measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2);
- measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6);
- measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6);
- MeasurementSet measurementSet = new MeasurementSet(measurements);
- MeasurementSet roundTripped =
- Json.measurementSetFromJson(Json.measurementSetToJson(measurementSet));
- assertDoubleListsEquals(measurementSet.getMeasurementsRaw(),
- roundTripped.getMeasurementsRaw(), 0.00000001);
- assertDoubleListsEquals(measurementSet.getMeasurementUnits(),
- roundTripped.getMeasurementUnits(), 0.00000001);
- assertEquals(measurementSet.getUnitNames(), roundTripped.getUnitNames());
- }
-
- @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
- public void testFromLegacyString() {
- MeasurementSet measurementSet = Json.measurementSetFromJson("122.0 133.0 144.0");
- assertDoubleListsEquals(Arrays.asList(122.0, 133.0, 144.0),
- measurementSet.getMeasurementsRaw(), 0.00000001);
- assertDoubleListsEquals(Arrays.asList(122.0, 133.0, 144.0),
- measurementSet.getMeasurementUnits(), 0.00000001);
- assertEquals(ImmutableMap.of("ns", 1, "us", 1000, "ms", 1000000, "s", 1000000000),
- measurementSet.getUnitNames());
- }
-
- private void assertDoubleListsEquals(List<Double> expected, List<Double> actual, double epsilon) {
- assertEquals(expected.size(), actual.size());
- Collections.sort(expected);
- Collections.sort(actual);
- for (int i = 0; i < expected.size(); i++) {
- assertEquals(expected.get(i), actual.get(i), epsilon);
- }
- }
-}
diff --git a/caliper/src/test/java/com/google/caliper/ParameterTest.java b/caliper/src/test/java/com/google/caliper/ParameterTest.java
deleted file mode 100644
index 9063261..0000000
--- a/caliper/src/test/java/com/google/caliper/ParameterTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-
-public class ParameterTest extends TestCase {
-
- public static class A extends SimpleBenchmark {
- @Param({"value1", "value2"}) String param;
- }
-
- public void testFromAnnotation() throws Exception {
- Map<String,Parameter<?>> map = Parameter.forClass(A.class);
- Parameter<?> p = map.get("param");
- assertEquals("param", p.getName());
- assertEquals(String.class, p.getType());
-
- checkParameterValues(A.class, "value1", "value2");
- }
-
- public enum Foo { VALUE1, VALUE2 }
-
- public static class H extends SimpleBenchmark {
- @Param Foo param;
- }
-
- public void testAllEnums() throws Exception {
- checkParameterValues(H.class, Foo.VALUE1, Foo.VALUE2);
- }
-
- public static class I extends SimpleBenchmark {
- @Param boolean param;
- }
-
- public void testBoolean() throws Exception {
- checkParameterValues(I.class, true, false);
- }
-
- private static void checkParameterValues(Class<? extends SimpleBenchmark> bClass,
- Object... expected) throws Exception {
- Map<String,Parameter<?>> map = Parameter.forClass(bClass);
- assertEquals(1, map.size());
- Parameter<?> p = map.get("param");
- List<Object> values = ImmutableList.copyOf(p.values());
- assertEquals(Arrays.asList(expected), values);
- }
-}
diff --git a/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java b/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java
deleted file mode 100644
index 85e59af..0000000
--- a/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import com.google.caliper.UserException.DoesNotScaleLinearlyException;
-import com.google.common.util.concurrent.SimpleTimeLimiter;
-import com.google.common.util.concurrent.TimeLimiter;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import junit.framework.TestCase;
-
-/**
- * Test exposing an issue where any warmup that completes enough executions to reach
- * Integer.MAX_VALUE reps (either because the benchmark code is optimized away or because the
- * warmupMillis are long enough compared to the benchmark execution time).
- */
-public class WarmupOverflowTest extends TestCase {
- private TimeLimiter timeLimiter;
-
- @Override public void setUp() {
- timeLimiter = new SimpleTimeLimiter(Executors.newSingleThreadExecutor());
- }
-
- public void testOptimizedAwayBenchmarkDoesNotTakeTooLongToRun() throws Exception {
- try {
- timeLimiter.callWithTimeout(new Callable<Void>() {
- @Override public Void call() throws Exception {
- InProcessRunner runner = new InProcessRunner();
- runner.run(OptimizedAwayBenchmark.class.getName(), "--warmupMillis", "3000",
- "--measurementType", "TIME");
- return null;
- }
- }, 90, TimeUnit.SECONDS, false);
- } catch (DoesNotScaleLinearlyException expected) {
- }
- }
-
- public void testLongWarmupMillisDoesNotTakeTooLongToRun() throws Exception {
- timeLimiter.callWithTimeout(new Callable<Void>() {
- @Override public Void call() throws Exception {
- InProcessRunner runner = new InProcessRunner();
- runner.run(RelativelyFastBenchmark.class.getName(), "--warmupMillis", "8000",
- "--runMillis", "51", "--measurementType", "TIME");
- return null;
- }
- }, 90, TimeUnit.SECONDS, false);
- }
-
- public static class OptimizedAwayBenchmark extends SimpleBenchmark {
- public void timeIsNullOrEmpty(int reps) {
- for (int i = 0; i < reps; i++) {
- // do nothing!
- }
- }
- }
-
- public static class RelativelyFastBenchmark extends SimpleBenchmark {
- public long timeSqrt(int reps) {
- long result = 0;
- for(int i = 0; i < reps; i++) {
- result += Math.sqrt(81);
- }
- return result;
- }
- }
-}
diff --git a/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java b/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java
new file mode 100644
index 0000000..292bc94
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import com.sun.management.HotSpotDiagnosticMXBean;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+
+/**
+ * A simple class that invokes {@link System#gc()} over and over to generate some GC log messages.
+ */
+public final class GcLogMessageGenerator {
+ public static void main(String[] args) throws IOException {
+ checkGcLogging();
+ for (int i = 0; i < 100; i++) {
+ System.gc();
+ }
+ }
+
+ private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
+
+ private static void checkGcLogging() throws IOException {
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(
+ server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class);
+ if (!bean.getVMOption("PrintGC").getValue().equals(Boolean.TRUE.toString())) {
+ System.err.println("This is only useful if you run with -XX:+PrintGC");
+ System.exit(1);
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java b/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java
new file mode 100644
index 0000000..46370d5
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.bridge;
+
+import static com.google.caliper.bridge.GcLogMessage.Type.FULL;
+import static com.google.caliper.bridge.GcLogMessage.Type.INCREMENTAL;
+import static com.google.common.base.Charsets.UTF_8;
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.caliper.util.ShortDuration;
+import com.google.common.io.Resources;
+
+import dagger.Component;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Tests {@link LogMessageParser}.
+ */
+@RunWith(JUnit4.class)
+
+public class LogMessageParserTest {
+ @Inject LogMessageParser parser;
+
+ @Component(modules = BridgeModule.class)
+ interface LogMessageParserComponent {
+ void inject(LogMessageParserTest test);
+ }
+
+ @Before public void setUp() {
+ DaggerLogMessageParserTest_LogMessageParserComponent.create().inject(this);
+ }
+
+ @Test public void gcPatten_jdk6() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk6-gc.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof GcLogMessage);
+ }
+ }
+
+ @Test public void gcPatten_jdk7() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk7-gc.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof GcLogMessage);
+ }
+ }
+
+ @Test public void gcMessageData() {
+ assertEquals(new GcLogMessage(INCREMENTAL, ShortDuration.of(1232, MICROSECONDS)),
+ parser.parse("[GC 987K->384K(62848K), 0.0012320 secs]"));
+ assertEquals(new GcLogMessage(FULL, ShortDuration.of(5455, MICROSECONDS)),
+ parser.parse("[Full GC 384K->288K(62848K), 0.0054550 secs]"));
+ assertEquals(new GcLogMessage(INCREMENTAL, ShortDuration.of(1424, MICROSECONDS)),
+ parser.parse(
+ "2013-02-11T20:15:26.706-0600: 0.098: [GC 1316K->576K(62848K), 0.0014240 secs]"));
+ assertEquals(new GcLogMessage(FULL, ShortDuration.of(4486, MICROSECONDS)),
+ parser.parse(
+ "2013-02-11T20:15:26.708-0600: 0.099: [Full GC 576K->486K(62848K), 0.0044860 secs]"));
+ }
+
+ @Test public void jitPattern_jdk6() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk6-compilation.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof HotspotLogMessage);
+ }
+ }
+
+ @Test public void jitPattern_jdk7() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk7-compilation.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof HotspotLogMessage);
+ }
+ }
+
+ @Test public void vmOptionPattern_jdk6() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk6-flags.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof VmOptionLogMessage);
+ }
+ }
+
+ @Test public void vmOptionPattern_jdk7() throws Exception {
+ List<String> lines = Resources.readLines(
+ Resources.getResource(LogMessageParserTest.class, "jdk7-flags.txt"), UTF_8);
+ for (String line : lines) {
+ assertTrue(parser.parse(line) instanceof VmOptionLogMessage);
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java b/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java
new file mode 100644
index 0000000..317d1e5
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import com.google.caliper.options.CaliperOptions;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Tests {@link CaliperConfigLoader}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+
+public class CaliperConfigLoaderTest {
+ @Mock CaliperOptions optionsMock;
+
+ private File tempConfigFile;
+
+ @Before public void createTempUserProperties() throws IOException {
+ tempConfigFile = File.createTempFile("caliper-config-test", "properties");
+ tempConfigFile.deleteOnExit();
+ Properties userProperties = new Properties();
+ userProperties.put("some.property", "franklin");
+ FileOutputStream fs = new FileOutputStream(tempConfigFile);
+ userProperties.store(fs, null);
+ fs.close();
+ }
+
+ @After public void deleteTempUserProperties() {
+ tempConfigFile.delete();
+ }
+
+ @Test public void loadOrCreate_configFileExistsNoOverride() throws Exception {
+ when(optionsMock.caliperConfigFile()).thenReturn(tempConfigFile);
+ when(optionsMock.configProperties()).thenReturn(ImmutableMap.<String, String>of());
+ CaliperConfigLoader loader = new CaliperConfigLoader(optionsMock);
+ CaliperConfig config = loader.loadOrCreate();
+ assertEquals("franklin", config.properties.get("some.property"));
+ }
+
+ @Test public void loadOrCreate_configFileExistsWithOverride() throws Exception {
+ when(optionsMock.caliperConfigFile()).thenReturn(tempConfigFile);
+ when(optionsMock.configProperties()).thenReturn(ImmutableMap.of(
+ "some.property", "tacos"));
+ CaliperConfigLoader loader = new CaliperConfigLoader(optionsMock);
+ CaliperConfig config = loader.loadOrCreate();
+ assertEquals("tacos", config.properties.get("some.property"));
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java b/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java
new file mode 100644
index 0000000..05bbe32
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.model.Trial;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.jvm.JvmPlatform;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+
+/**
+ * Tests {@link CaliperConfig}.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+@RunWith(JUnit4.class)
+public class CaliperConfigTest {
+ @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+ private Platform platform = new JvmPlatform();
+
+ @Test public void getDefaultVmConfig() throws Exception {
+ CaliperConfig configuration = new CaliperConfig(
+ ImmutableMap.of("vm.args", "-very -special=args"));
+ VmConfig defaultVmConfig = configuration.getDefaultVmConfig(platform);
+ assertEquals(new File(System.getProperty("java.home")), defaultVmConfig.vmHome());
+ ImmutableList<String> expectedArgs = new ImmutableList.Builder<String>()
+ .addAll(ManagementFactory.getRuntimeMXBean().getInputArguments())
+ .add("-very")
+ .add("-special=args")
+ .build();
+ assertEquals(expectedArgs, defaultVmConfig.options());
+ }
+
+ @Test public void getVmConfig_baseDirectoryAndName() throws Exception {
+ File tempBaseDir = folder.newFolder();
+ File jdkHome = new File(tempBaseDir, "test");
+ jdkHome.mkdir();
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "vm.baseDirectory", tempBaseDir.getAbsolutePath()));
+ assertEquals(new VmConfig.Builder(platform, jdkHome).build(),
+ configuration.getVmConfig(platform, "test"));
+ }
+
+ @Test public void getVmConfig_baseDirectoryAndHome() throws Exception {
+ File tempBaseDir = folder.newFolder();
+ File jdkHome = new File(tempBaseDir, "test-home");
+ jdkHome.mkdir();
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "vm.baseDirectory", tempBaseDir.getAbsolutePath(),
+ "vm.test.home", "test-home"));
+ assertEquals(new VmConfig.Builder(platform, jdkHome).build(),
+ configuration.getVmConfig(platform, "test"));
+ }
+
+ @Test public void getVmConfig() throws Exception {
+ File jdkHome = folder.newFolder();
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "vm.args", "-a -b -c",
+ "vm.test.home", jdkHome.getAbsolutePath(),
+ "vm.test.args", " -d -e "));
+ assertEquals(
+ new VmConfig.Builder(platform, jdkHome)
+ .addOption("-a")
+ .addOption("-b")
+ .addOption("-c")
+ .addOption("-d")
+ .addOption("-e")
+ .build(),
+ configuration.getVmConfig(platform, "test"));
+ }
+
+ @Test public void getVmConfig_escapedSpacesInArgs() throws Exception {
+ File jdkHome = folder.newFolder();
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "vm.args", "-a=string\\ with\\ spa\\ces -b -c",
+ "vm.test.home", jdkHome.getAbsolutePath()));
+ assertEquals(
+ new VmConfig.Builder(platform, jdkHome)
+ .addOption("-a=string with spaces")
+ .addOption("-b")
+ .addOption("-c")
+ .build(),
+ configuration.getVmConfig(platform, "test"));
+ }
+
+ @Test public void getInstrumentConfig() throws Exception {
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "instrument.test.class", "test.ClassName",
+ "instrument.test.options.a", "1",
+ "instrument.test.options.b", "excited b b excited"));
+ assertEquals(
+ new InstrumentConfig.Builder()
+ .className("test.ClassName")
+ .addOption("a", "1")
+ .addOption("b", "excited b b excited")
+ .build(),
+ configuration.getInstrumentConfig("test"));
+ }
+
+ @Test public void getInstrumentConfig_notConfigured() throws Exception {
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "instrument.test.options.a", "1",
+ "instrument.test.options.b", "excited b b excited"));
+ try {
+ configuration.getInstrumentConfig("test");
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test public void getConfiguredInstruments() throws Exception {
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "instrument.test.class", "test.ClassName",
+ "instrument.test2.class", "test.ClassName",
+ "instrument.test3.options.a", "1",
+ "instrument.test4.class", "test.ClassName",
+ "instrument.test4.options.b", "excited b b excited"));
+ assertEquals(ImmutableSet.of("test", "test2", "test4"),
+ configuration.getConfiguredInstruments());
+ }
+
+ @Test public void getConfiguredResultProcessors() throws Exception {
+ assertEquals(ImmutableSet.of(),
+ new CaliperConfig(ImmutableMap.<String, String>of()).getConfiguredResultProcessors());
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "results.test.class", TestResultProcessor.class.getName()));
+ assertEquals(ImmutableSet.of(TestResultProcessor.class),
+ configuration.getConfiguredResultProcessors());
+ }
+
+ @Test public void getResultProcessorConfig() throws Exception {
+ CaliperConfig configuration = new CaliperConfig(ImmutableMap.of(
+ "results.test.class", TestResultProcessor.class.getName(),
+ "results.test.options.g", "ak",
+ "results.test.options.c", "aliper"));
+ assertEquals(
+ new ResultProcessorConfig.Builder()
+ .className(TestResultProcessor.class.getName())
+ .addOption("g", "ak")
+ .addOption("c", "aliper")
+ .build(),
+ configuration.getResultProcessorConfig(TestResultProcessor.class));
+ }
+
+ private static final class TestResultProcessor implements ResultProcessor {
+ @Override public void close() {}
+
+ @Override public void processTrial(Trial trial) {}
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java b/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java
new file mode 100644
index 0000000..e4320ef
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.config;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static java.util.logging.Level.INFO;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.caliper.model.Run;
+import com.google.common.io.Files;
+
+import org.joda.time.Instant;
+import org.joda.time.format.ISODateTimeFormat;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+import java.util.logging.FileHandler;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+/**
+ * Tests {@link LoggingConfigLoader}.
+ */
+
+@RunWith(MockitoJUnitRunner.class)
+public class LoggingConfigLoaderTest {
+ @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+ @Mock LogManager logManager;
+ @Mock Logger logger;
+ @Captor ArgumentCaptor<Handler> handlerCaptor;
+
+ private LoggingConfigLoader loader;
+ private UUID runId = UUID.randomUUID();
+ private Instant startTime = new Instant();
+ private File caliperDirectory;
+
+ @Before public void setUp() throws IOException {
+ this.caliperDirectory = folder.newFolder();
+ this.loader = new LoggingConfigLoader(caliperDirectory, logManager, new Run.Builder(runId)
+ .label("fake run")
+ .startTime(startTime)
+ .build());
+ }
+
+ @Test public void testLoadDefaultLogConfiguration()
+ throws SecurityException, IOException {
+ when(logManager.getLogger("")).thenReturn(logger);
+ loader.maybeLoadDefaultLogConfiguration(logManager);
+ verify(logManager).reset();
+ verify(logger).addHandler(handlerCaptor.capture());
+ FileHandler fileHandler = (FileHandler) handlerCaptor.getValue();
+ assertEquals(UTF_8.name(), fileHandler.getEncoding());
+ assertTrue(fileHandler.getFormatter() instanceof SimpleFormatter);
+ fileHandler.publish(new LogRecord(INFO, "some message"));
+ File logFile = new File(new File(caliperDirectory, "log"),
+ ISODateTimeFormat.basicDateTimeNoMillis().print(startTime) + "." + runId + ".log");
+ assertTrue(logFile.isFile());
+ assertTrue(Files.toString(logFile, UTF_8).contains("some message"));
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java b/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java
new file mode 100644
index 0000000..7998866
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.config;
+
+import static org.junit.Assert.assertTrue;
+
+import com.google.caliper.platform.jvm.JvmPlatform;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+
+/**
+ * Tests {@link VmConfig}.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+@RunWith(JUnit4.class)
+public class VmConfigTest {
+
+
+ @Test
+ public void testExecutable() {
+ File javaExecutable =
+ new VmConfig.Builder(new JvmPlatform(), new File(System.getProperty("java.home")))
+ .build()
+ .vmExecutable();
+ assertTrue("Could not find: " + javaExecutable, javaExecutable.exists());
+ assertTrue(javaExecutable + " is not a file", javaExecutable.isFile());
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java b/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java
new file mode 100644
index 0000000..5888f9c
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.memory;
+
+import com.google.caliper.memory.ObjectGraphMeasurer.Footprint;
+import com.google.common.collect.ImmutableMultiset;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for ObjectGraphMeasurer.
+ */
+@RunWith(JUnit4.class)
+public class ObjectGraphMeasurerTest extends TestCase {
+ enum DummyEnum {
+ VALUE;
+ }
+ static final Object oneEnumField = new Object() {
+ @SuppressWarnings("unused") DummyEnum enumField = DummyEnum.VALUE;
+ };
+
+ // enums are treated as statics (and ignored)
+ @Test public void testEnum() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneEnumField);
+ assertEquals(new Footprint(1, 1, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static final Object oneClassField = new Object() {
+ @SuppressWarnings("unused") Class<?> clazz = Object.class;
+ };
+
+ // Class instances are treated as statics (and ignored)
+ @Test public void testClass() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneClassField);
+ assertEquals(new ObjectGraphMeasurer.Footprint(1, 1, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static final Object oneObjectField = new Object() {
+ @SuppressWarnings("unused") Object objectField = new Object();
+ };
+
+ @Test public void testObject() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneObjectField);
+ assertEquals(new ObjectGraphMeasurer.Footprint(2, 1, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static final Object withCycle = new Object() {
+ Object[] array = new Object[1];
+ {
+ array[0] = this;
+ }
+ };
+
+ @Test public void testCycle() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(withCycle);
+ assertEquals(new ObjectGraphMeasurer.Footprint(2, 2, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static final Object multiplePathsToObject = new Object() {
+ Object object = new Object();
+ @SuppressWarnings("unused") Object ref1 = object;
+ @SuppressWarnings("unused") Object ref2 = object;
+ };
+
+ @Test public void testMultiplePathsToObject() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(multiplePathsToObject);
+ assertEquals(new ObjectGraphMeasurer.Footprint(2, 3, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static final Object multiplePathsToClass = new Object() {
+ Object object = Object.class;
+ @SuppressWarnings("unused") Object ref1 = object;
+ @SuppressWarnings("unused") Object ref2 = object;
+ };
+
+ @Test public void testMultiplePathsToClass() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(multiplePathsToClass);
+ assertEquals(new ObjectGraphMeasurer.Footprint(1, 3, 0, NO_PRIMITIVES), footprint);
+ }
+
+ static class WithStaticField {
+ static WithStaticField INSTANCE = new WithStaticField();
+ }
+
+ @Test public void testStaticFields() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(new WithStaticField());
+ assertEquals(new ObjectGraphMeasurer.Footprint(1, 0, 0, NO_PRIMITIVES), footprint);
+ }
+
+ @SuppressWarnings("unused") // unused test fields
+ static final Object oneNullOneNonNull = new Object() {
+ Object nonNull1 = new Object();
+ Object nonNull2 = nonNull1;
+ Object null1 = null;
+ Object null2 = null;
+ Object null3 = null;
+ };
+
+ @Test public void testNullField() {
+ ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneNullOneNonNull);
+ assertEquals(new ObjectGraphMeasurer.Footprint(2, 2, 3, NO_PRIMITIVES), footprint);
+ }
+
+ private static final ImmutableMultiset<Class<?>> NO_PRIMITIVES = ImmutableMultiset.of();
+}
diff --git a/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java b/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java
new file mode 100644
index 0000000..46d678a
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.options;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.util.DisplayUsageException;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.caliper.util.ShortDuration;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(JUnit4.class)
+
+public class ParsedOptionsTest {
+ private File tempDir;
+
+ @Before public void setUp() throws IOException {
+ tempDir = Files.createTempDir();
+ makeTestVmTree(tempDir);
+ }
+
+ @After public void tearDown() throws IOException {
+ if (tempDir != null) {
+ Runtime.getRuntime().exec(new String[] {"rm", "-rf", tempDir.getCanonicalPath()});
+ }
+ }
+
+ private static void makeTestVmTree(File baseDir) throws IOException {
+ File bin = new File(baseDir, "testVm/bin");
+ bin.mkdirs();
+ File java = new File(bin, "java");
+ Files.touch(java);
+ }
+
+ @Test public void testNoOptions_RequireBenchmarkClassName() {
+ try {
+ ParsedOptions.from(new String[] {}, true);
+ fail();
+ } catch (InvalidCommandException expected) {
+ assertEquals("No benchmark class specified", expected.getMessage());
+ }
+ }
+
+ @Test public void testTooManyArguments_RequireBenchmarkClassName() {
+ try {
+ ParsedOptions.from(new String[] {"a", "b"}, true);
+ fail();
+ } catch (InvalidCommandException expected) {
+ assertEquals("Extra stuff, expected only class name: [a, b]", expected.getMessage());
+ }
+ }
+
+ @Test public void testTooManyArguments_DoNotRequireBenchmarkClassName() {
+ try {
+ ParsedOptions.from(new String[] {"a", "b"}, false);
+ fail();
+ } catch (InvalidCommandException expected) {
+ assertEquals("Extra stuff, did not expect non-option arguments: [a, b]",
+ expected.getMessage());
+ }
+ }
+
+ @Test public void testHelp() throws InvalidCommandException {
+ try {
+ ParsedOptions.from(new String[] {"--help"}, true);
+ fail();
+ } catch (DisplayUsageException expected) {
+ }
+ }
+
+ @Test public void testDefaults_RequireBenchmarkClassName() throws InvalidCommandException {
+ CaliperOptions options = ParsedOptions.from(new String[] {CLASS_NAME}, true);
+
+ assertEquals(CLASS_NAME, options.benchmarkClassName());
+ checkDefaults(options);
+ }
+
+ @Test public void testDefaults_DoNotRequireBenchmarkClassName() throws InvalidCommandException {
+ CaliperOptions options = ParsedOptions.from(new String[] {}, false);
+
+ assertNull(options.benchmarkClassName());
+ checkDefaults(options);
+ }
+
+ private void checkDefaults(CaliperOptions options) {
+ assertTrue(options.benchmarkMethodNames().isEmpty());
+ assertFalse(options.dryRun());
+ ImmutableSet<String> expectedInstruments = new ImmutableSet.Builder<String>()
+ .add("allocation")
+ .add("runtime")
+ .build();
+ assertEquals(expectedInstruments, options.instrumentNames());
+ assertEquals(1, options.trialsPerScenario());
+ assertTrue(options.userParameters().isEmpty());
+ assertFalse(options.printConfiguration());
+ assertTrue(options.vmArguments().isEmpty());
+ assertEquals(0, options.vmNames().size());
+ }
+
+ @Test public void testKitchenSink() throws InvalidCommandException {
+ String[] args = {
+ "--benchmark=foo;bar;qux",
+ "--instrument=testInstrument",
+ "--directory=/path/to/some/dir",
+ "--trials=2",
+ "--time-limit=15s",
+ "-Dx=a;b;c",
+ "-Dy=b;d",
+ "-Csome.property=value",
+ "-Csome.other.property=other-value",
+ "--print-config",
+ "-JmemoryMax=-Xmx32m;-Xmx64m",
+ "--vm=testVm",
+ "--delimiter=;",
+ CLASS_NAME,
+ };
+ CaliperOptions options = ParsedOptions.from(args, true);
+
+ assertEquals(CLASS_NAME, options.benchmarkClassName());
+ assertEquals(ImmutableSet.of("foo", "bar", "qux"), options.benchmarkMethodNames());
+ assertFalse(options.dryRun());
+ assertEquals(ImmutableSet.of("testInstrument"), options.instrumentNames());
+ assertEquals(new File("/path/to/some/dir"), options.caliperDirectory());
+ assertEquals(2, options.trialsPerScenario());
+ assertEquals(ShortDuration.of(15, SECONDS), options.timeLimit());
+ assertEquals(ImmutableSetMultimap.of("x", "a", "x", "b", "x", "c", "y", "b", "y", "d"),
+ options.userParameters());
+ assertEquals(ImmutableMap.of("some.property", "value", "some.other.property", "other-value"),
+ options.configProperties());
+ assertTrue(options.printConfiguration());
+ assertEquals(ImmutableSetMultimap.of("memoryMax", "-Xmx32m", "memoryMax", "-Xmx64m"),
+ options.vmArguments());
+
+ String vmName = Iterables.getOnlyElement(options.vmNames());
+ assertEquals("testVm", vmName);
+ }
+
+ public static class FakeBenchmark {}
+
+ private static final String CLASS_NAME = FakeBenchmark.class.getName();
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java
new file mode 100644
index 0000000..0186aa0
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Trial;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.jvm.JvmPlatform;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Tests {@link AllocationInstrument}.
+ */
+
+@RunWith(JUnit4.class)
+public class AllocationInstrumentTest {
+
+ @Rule public CaliperTestWatcher runner = new CaliperTestWatcher();
+
+ @Test public void getExtraCommandLineArgs() throws Exception {
+ AllocationInstrument instrument = new AllocationInstrument();
+ File fakeJar = File.createTempFile("fake", "jar");
+ fakeJar.deleteOnExit();
+ instrument.setOptions(ImmutableMap.of("allocationAgentJar", fakeJar.getAbsolutePath()));
+ ImmutableSet<String> expected = new ImmutableSet.Builder<String>()
+ .addAll(JvmPlatform.INSTRUMENT_JVM_ARGS)
+ .add("-Xint")
+ .add("-javaagent:" + fakeJar.getAbsolutePath())
+ .add("-Xbootclasspath/a:" + fakeJar.getAbsolutePath())
+ .add("-Dsun.reflect.inflationThreshold=0")
+ .build();
+ Platform platform = new JvmPlatform();
+ VmConfig vmConfig = new VmConfig.Builder(platform, new File(System.getProperty("java.home")))
+ .build();
+ assertEquals(expected, instrument.getExtraCommandLineArgs(vmConfig));
+ fakeJar.delete();
+ }
+
+ @Test
+ public void intrinsics() throws Exception {
+ runner.forBenchmark(ArrayListGrowthBenchmark.class)
+ .instrument("allocation")
+ .run();
+ Trial trial = Iterables.getOnlyElement(runner.trials());
+ ImmutableListMultimap<String, Measurement> measurementsByDescription =
+ Measurement.indexByDescription(trial.measurements());
+ // 14 objects and 1960 bytes are the known values for growing an ArrayList from 1 element to 100
+ // elements
+ for (Measurement objectMeasurement : measurementsByDescription.get("objects")) {
+ assertEquals(14.0, objectMeasurement.value().magnitude() / objectMeasurement.weight(), 0.001);
+ }
+ for (Measurement byteMeasurement : measurementsByDescription.get("bytes")) {
+ assertEquals(1960.0, byteMeasurement.value().magnitude() / byteMeasurement.weight(), 0.001);
+ }
+ }
+
+ public static class TestBenchmark {
+ List<Object> list = Lists.newLinkedList();
+ @Benchmark public int compressionSize(int reps) {
+ for (int i = 0; i < reps; i++) {
+ list.add(new Object());
+ }
+ int hashCode = list.hashCode();
+ list.clear();
+ return hashCode;
+ }
+ }
+
+ public static class ArrayListGrowthBenchmark {
+ @BeforeExperiment void warmUp() {
+ // ensure that hotspot has compiled this code
+ benchmarkGrowth(100000);
+ }
+
+ @Benchmark void benchmarkGrowth(int reps) {
+ for (int i = 0; i < reps; i++) {
+ List<String> list = Lists.newArrayListWithCapacity(1);
+ for (int j = 0; j < 100; j++) {
+ list.add("");
+ }
+ }
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java
new file mode 100644
index 0000000..1e7f98c
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.caliper.model.ArbitraryMeasurement;
+import com.google.caliper.model.Measurement;
+import com.google.caliper.model.Value;
+import com.google.common.collect.Iterables;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integration tests for the {@link ArbitraryMeasurementInstrument}
+ */
+@RunWith(JUnit4.class)
+public class ArbitraryMeasurmentInstrumentTest {
+ @Rule public CaliperTestWatcher runner = new CaliperTestWatcher();
+
+ @Test
+
+ public void testSuccess() throws Exception {
+ runner.forBenchmark(TestBenchmark.class)
+ .instrument("arbitrary")
+ .run();
+ Measurement measurement = Iterables.getOnlyElement(
+ Iterables.getOnlyElement(runner.trials()).measurements());
+ Measurement expected = new Measurement.Builder()
+ .description("fake measurment")
+ .weight(1)
+ .value(Value.create(1.0, "hz"))
+ .build();
+ assertEquals(expected, measurement);
+ }
+
+ public static class TestBenchmark {
+ @ArbitraryMeasurement(units = "hz", description = "fake measurment")
+ public double compressionSize() {
+ return 1.0;
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java b/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java
new file mode 100644
index 0000000..3312088
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.common.collect.Lists;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+/**
+ * Integration tests for misbehaving benchmarks.
+ */
+@RunWith(JUnit4.class)
+public class BadUserCodeTest {
+ @Rule public CaliperTestWatcher runner = new CaliperTestWatcher();
+
+ @Test
+
+ public void testExceptionInInit() throws Exception {
+ try {
+ runner.forBenchmark(ExceptionInInitBenchmark.class).run();
+ fail();
+ } catch (UserCodeException expected) {}
+ }
+
+ private static void throwSomeUserException() {
+ throw new RuntimeException();
+ }
+
+ static class ExceptionInInitBenchmark {
+ static {
+ throwSomeUserException();
+ }
+
+ @Benchmark void timeSomething(int reps) {
+ fail("" + reps);
+ }
+ }
+
+ @Test
+
+ public void testExceptionInConstructor() throws Exception {
+ try {
+ runner.forBenchmark(ExceptionInConstructorBenchmark.class).run();
+ fail();
+ } catch (UserCodeException expected) {}
+ }
+
+ static class ExceptionInConstructorBenchmark {
+ ExceptionInConstructorBenchmark() {
+ throw new RuntimeException();
+ }
+
+ @Benchmark void timeSomething(int reps) {
+ fail("" + reps);
+ }
+ }
+
+ @Test
+
+ public void testExceptionInMethod() throws Exception {
+ try {
+ runner.forBenchmark(ExceptionInMethodBenchmark.class).run();
+ fail();
+ } catch (UserCodeException expected) {}
+ }
+
+ static class ExceptionInMethodBenchmark {
+ @Benchmark void timeSomething(@SuppressWarnings("unused") int reps) {
+ throw new RuntimeException();
+ }
+ }
+
+ @Test
+
+ public void testExceptionInMethod_notInDryRun() throws Exception {
+ try {
+ runner.forBenchmark(ExceptionLateInMethodBenchmark.class).run();
+ fail();
+ } catch (ProxyWorkerException expected) {
+ assertTrue(expected.getMessage().contains(ExceptionLateInMethodBenchmark.class.getName()));
+ }
+ }
+
+ static class ExceptionLateInMethodBenchmark {
+ @Benchmark void timeSomething(int reps) {
+ if (reps > 1) {
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ @Test
+
+ public void testExceptionInSetUp() throws Exception {
+ try {
+ runner.forBenchmark(ExceptionInSetUpBenchmark.class).run();
+ fail();
+ } catch (UserCodeException expected) {}
+ }
+
+ static class ExceptionInSetUpBenchmark {
+ @BeforeExperiment void setUp() {
+ throw new RuntimeException();
+ }
+
+ @Benchmark void timeSomething(int reps) {
+ fail("" + reps);
+ }
+ }
+
+ @Test
+
+ public void testNonDeterministicAllocation_noTrackAllocations() throws Exception {
+ try {
+ runner.forBenchmark(NonDeterministicAllocationBenchmark.class)
+ .instrument("allocation")
+ .options("-Cinstrument.allocation.options.trackAllocations=" + false)
+ .run();
+ fail();
+ } catch (ProxyWorkerException expected) {
+ String message = "Your benchmark appears to have non-deterministic allocation behavior";
+ assertTrue("Expected " + expected.getMessage() + " to contain " + message,
+ expected.getMessage().contains(message));
+ }
+ }
+
+ @Test
+
+ public void testNonDeterministicAllocation_trackAllocations() throws Exception {
+ try {
+ runner.forBenchmark(NonDeterministicAllocationBenchmark.class)
+ .instrument("allocation")
+ .options("-Cinstrument.allocation.options.trackAllocations=" + true)
+ .run();
+ fail();
+ } catch (ProxyWorkerException expected) {
+ String message = "Your benchmark appears to have non-deterministic allocation behavior";
+ assertTrue("Expected " + expected.getMessage() + " to contain " + message,
+ expected.getMessage().contains(message));
+ }
+ }
+
+ /** The number of allocations is non deterministic because it depends on static state. */
+ static class NonDeterministicAllocationBenchmark {
+ static int timeCount = 0;
+ // We dump items into this list so the jit cannot remove the allocations
+ static List<Object> list = Lists.newArrayList();
+ @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) {
+ timeCount++;
+ if (timeCount % 2 == 0) {
+ list.add(new Object());
+ return list.hashCode();
+ }
+ return this.hashCode();
+ }
+ }
+
+ @Test
+
+ public void testComplexNonDeterministicAllocation_noTrackAllocations() throws Exception {
+ // Without trackAllocations enabled this kind of non-determinism cannot be detected.
+ runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class)
+ .instrument("allocation")
+ .options("-Cinstrument.allocation.options.trackAllocations=" + false)
+ .run();
+ }
+
+ @Test
+
+ public void testComplexNonDeterministicAllocation_trackAllocations() throws Exception {
+ try {
+ runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class)
+ .instrument("allocation")
+ .options("-Cinstrument.allocation.options.trackAllocations=" + true)
+ .run();
+ } catch (ProxyWorkerException expected) {
+ String message = "Your benchmark appears to have non-deterministic allocation behavior";
+ assertTrue("Expected " + expected.getMessage() + " to contain " + message,
+ expected.getMessage().contains(message));
+ }
+ }
+
+ /** Benchmark allocates the same number of things each time but in a different way. */
+ static class ComplexNonDeterministicAllocationBenchmark {
+ static int timeCount = 0;
+ @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) {
+ // We dump items into this list so the jit cannot remove the allocations
+ List<Object> list = Lists.newArrayList();
+ timeCount++;
+ if (timeCount % 2 == 0) {
+ list.add(new Object());
+ } else {
+ list.add(new Object());
+ }
+ return list.hashCode();
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java
new file mode 100644
index 0000000..66305d2
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java
@@ -0,0 +1,63 @@
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.api.Macrobenchmark;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Tests {@link BenchmarkClassChecker}.
+ */
+
+@RunWith(JUnit4.class)
+public class BenchmarkClassCheckerTest {
+
+ private BenchmarkClassChecker benchmarkClassChecker =
+ BenchmarkClassChecker.create(Collections.<String>emptyList());
+
+ @Test
+ public void testNoBenchmarkMethods() {
+ assertFalse(benchmarkClassChecker.isBenchmark(Object.class));
+ }
+
+ @Test
+ public void testBenchmarkAnnotatedMethod() {
+ assertTrue(benchmarkClassChecker.isBenchmark(BenchmarkAnnotatedMethod.class));
+ }
+
+ public static class BenchmarkAnnotatedMethod {
+ @Benchmark void benchmarkMethod() {}
+ }
+
+ @Test
+ public void testMacroBenchmarkAnnotatedMethod() {
+ assertTrue(benchmarkClassChecker.isBenchmark(MacroBenchmarkAnnotatedMethod.class));
+ }
+
+ @Test
+ public void testMacroBenchmarkAnnotatedMethod_NoSuitableInstrument() {
+ benchmarkClassChecker = BenchmarkClassChecker.create(Arrays.asList("-i", "allocation"));
+ assertFalse(benchmarkClassChecker.isBenchmark(MacroBenchmarkAnnotatedMethod.class));
+ }
+
+ public static class MacroBenchmarkAnnotatedMethod {
+ @Macrobenchmark void macrobenchmarkMethod() {}
+ }
+
+ @Test
+ public void testTimeMethod() {
+ assertTrue(benchmarkClassChecker.isBenchmark(TimeMethod.class));
+ }
+
+ public static class TimeMethod {
+ public void timeMethod() {}
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java
new file mode 100644
index 0000000..dcc6113
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link BenchmarkClass}.
+ */
+@RunWith(JUnit4.class)
+public class BenchmarkClassTest {
+ @Test public void beforeMeasurementMethods_AnnotatedBenchmark() throws Exception {
+ assertEquals(
+ ImmutableSet.of(
+ MyBenchmark.class.getDeclaredMethod("before1"),
+ MyBenchmark.class.getDeclaredMethod("before2")),
+ BenchmarkClass.forClass(MyBenchmark.class).beforeExperimentMethods());
+ }
+
+ @Test public void afterMeasurementMethods_AnnotatedBenchmark() throws Exception {
+ assertEquals(
+ ImmutableSet.of(
+ MyBenchmark.class.getDeclaredMethod("after1"),
+ MyBenchmark.class.getDeclaredMethod("after2")),
+ BenchmarkClass.forClass(MyBenchmark.class).afterExperimentMethods());
+ }
+
+ @Test public void forClass_inheritenceThrows() throws Exception {
+ try {
+ BenchmarkClass.forClass(MalformedBenhcmark.class);
+ fail();
+ } catch (InvalidBenchmarkException expected) {}
+ }
+
+ static class MyBenchmark {
+ @BeforeExperiment void before1() {}
+ @BeforeExperiment void before2() {}
+ @AfterExperiment void after1() {}
+ @AfterExperiment void after2() {}
+ }
+
+ static class MalformedBenhcmark extends MyBenchmark {}
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java
new file mode 100644
index 0000000..c1b2bf6
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.Param;
+import com.google.common.collect.ImmutableSortedMap;
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link BenchmarkCreator}
+ */
+@RunWith(JUnit4.class)
+public class BenchmarkCreatorTest extends TestCase {
+
+ @Test
+ public void publicDefaultConstructorNoParamBenchmark() {
+ BenchmarkCreator creator = new BenchmarkCreator(PublicDefaultConstructorNoParamBenchmark.class,
+ ImmutableSortedMap.<String, String>of());
+
+ Object benchmarkInstance = creator.createBenchmarkInstance();
+ assertTrue(benchmarkInstance instanceof PublicDefaultConstructorNoParamBenchmark);
+ }
+
+ public static class PublicDefaultConstructorNoParamBenchmark {
+ }
+
+ @Test
+ public void publicDefaultConstructorWithParamBenchmark() {
+ BenchmarkCreator creator = new BenchmarkCreator(
+ PublicDefaultConstructorWithParamBenchmark.class,
+ ImmutableSortedMap.of("byteField", "1", "intField", "2", "stringField", "string"));
+
+ Object benchmarkInstance = creator.createBenchmarkInstance();
+ assertTrue(benchmarkInstance instanceof PublicDefaultConstructorWithParamBenchmark);
+ PublicDefaultConstructorWithParamBenchmark benchmark =
+ (PublicDefaultConstructorWithParamBenchmark) benchmarkInstance;
+ assertEquals(1, benchmark.byteField);
+ assertEquals(2, benchmark.intField);
+ assertEquals("string", benchmark.stringField);
+ }
+
+ public static class PublicDefaultConstructorWithParamBenchmark {
+ @Param
+ byte byteField;
+
+ @Param
+ int intField;
+
+ @Param
+ String stringField;
+ }
+
+ @Test
+ public void publicNoSuitableConstructorBenchmark() {
+ try {
+ new BenchmarkCreator(
+ PublicNoSuitableConstructorBenchmark.class,
+ ImmutableSortedMap.<String, String>of());
+ } catch (UserCodeException e) {
+ assertEquals("Benchmark class "
+ + PublicNoSuitableConstructorBenchmark.class.getName()
+ + " does not have a publicly visible default constructor", e.getMessage());
+ }
+ }
+
+ public static class PublicNoSuitableConstructorBenchmark {
+ @Param
+ byte byteField;
+
+ @Param
+ int intField;
+
+ @Param
+ String stringField;
+
+ public PublicNoSuitableConstructorBenchmark(
+ byte byteField, int intField, String stringField) {
+ this.byteField = byteField;
+ this.intField = intField;
+ this.stringField = stringField;
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java b/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java
new file mode 100644
index 0000000..63f0044
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.model.Trial;
+import com.google.caliper.util.InvalidCommandException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A {@link TestWatcher} that can be used to configure a run of caliper.
+ *
+ * <p>Provides common test configuration utilities and redirects output to a buffer and only dumps
+ * it during a failure.
+ *
+ * <p>TODO(lukes,gak): This is a bad name since it isn't just watching the tests, it is helping you
+ * run the tests.
+ */
+public final class CaliperTestWatcher extends TestWatcher {
+ // N.B. StringWriter is internally synchronized and is safe to write to from multiple threads.
+ private StringWriter stdout;
+ private final StringWriter stderr = new StringWriter();
+ private File workerOutput;
+
+ private String instrument;
+ private Class<?> benchmarkClass;
+ private List<String> extraOptions = Lists.newArrayList();
+
+ CaliperTestWatcher forBenchmark(Class<?> benchmarkClass) {
+ this.benchmarkClass = benchmarkClass;
+ return this;
+ }
+
+ CaliperTestWatcher instrument(String instrument) {
+ this.instrument = instrument;
+ return this;
+ }
+
+ CaliperTestWatcher options(String... extraOptions) {
+ this.extraOptions = Arrays.asList(extraOptions);
+ return this;
+ }
+
+ void run() throws InvalidCommandException, InvalidBenchmarkException,
+ InvalidConfigurationException {
+ checkState(benchmarkClass != null, "You must configure a benchmark!");
+ workerOutput = Files.createTempDir();
+ // configure a custom dir so the files aren't deleted when CaliperMain returns
+ List<String> options = Lists.newArrayList(
+ "-Cworker.output=" + workerOutput.getPath(),
+ "-Cresults.file.class=",
+ "-Cresults.upload.class=" + InMemoryResultsUploader.class.getName());
+ if (instrument != null) {
+ options.add("-i");
+ options.add(instrument);
+ }
+ options.addAll(extraOptions);
+ options.add(benchmarkClass.getName());
+ this.stdout = new StringWriter();
+ CaliperMain.exitlessMain(
+ options.toArray(new String[0]),
+ new PrintWriter(stdout, true),
+ new PrintWriter(stderr, true));
+ }
+
+ @Override protected void finished(Description description) {
+ if (workerOutput != null) {
+ for (File f : workerOutput.listFiles()) {
+ f.delete();
+ }
+ workerOutput.delete();
+ }
+ }
+
+ @Override protected void failed(Throwable e, Description description) {
+ // don't log if run was never called.
+ if (stdout != null) {
+ System.err.println("Caliper failed with the following output (stdout):\n"
+ + stdout.toString() + "stderr:\n" + stderr.toString());
+ }
+ }
+
+ ImmutableList<Trial> trials() {
+ return InMemoryResultsUploader.trials();
+ }
+
+ public StringWriter getStderr() {
+ return stderr;
+ }
+
+ public StringWriter getStdout() {
+ return stdout;
+ }
+
+ File workerOutputDirectory() {
+ return workerOutput;
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java b/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java
new file mode 100644
index 0000000..a941f56
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.options.CaliperOptions;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.SupportedPlatform;
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.caliper.worker.Worker;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.lang.reflect.Method;
+
+/**
+ * Tests {@link ExperimentingRunnerModule}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ExperimentingRunnerModuleTest {
+ private ExperimentingRunnerModule module = new ExperimentingRunnerModule();
+ private Instrument instrumentA = new FakeInstrument();
+ private Instrument instrumentB = new FakeInstrument();
+
+ @Mock CaliperOptions options;
+
+ private Method methodA;
+ private Method methodB;
+ private Method methodC;
+
+ @Before public void setUp() throws Exception {
+ methodA = TestBenchmark.class.getDeclaredMethod("a");
+ methodB = TestBenchmark.class.getDeclaredMethod("b");
+ methodC = TestBenchmark.class.getDeclaredMethod("c");
+ }
+
+ @Test public void provideInstrumentations_noNames() throws Exception {
+ when(options.benchmarkMethodNames()).thenReturn(ImmutableSet.<String>of());
+ assertEquals(
+ new ImmutableSet.Builder<Instrumentation>()
+ .add(instrumentA.createInstrumentation(methodA))
+ .add(instrumentA.createInstrumentation(methodB))
+ .add(instrumentA.createInstrumentation(methodC))
+ .add(instrumentB.createInstrumentation(methodA))
+ .add(instrumentB.createInstrumentation(methodB))
+ .add(instrumentB.createInstrumentation(methodC))
+ .build(),
+ module.provideInstrumentations(options,
+ BenchmarkClass.forClass(TestBenchmark.class),
+ ImmutableSet.of(instrumentA, instrumentB)));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test public void provideInstrumentations_withNames() throws Exception {
+ when(options.benchmarkMethodNames()).thenReturn(ImmutableSet.of("b"),
+ ImmutableSet.of("a", "c"));
+ assertEquals(
+ new ImmutableSet.Builder<Instrumentation>()
+ .add(instrumentA.createInstrumentation(methodB))
+ .add(instrumentB.createInstrumentation(methodB))
+ .build(),
+ module.provideInstrumentations(options,
+ BenchmarkClass.forClass(TestBenchmark.class),
+ ImmutableSet.of(instrumentA, instrumentB)));
+ assertEquals(
+ new ImmutableSet.Builder<Instrumentation>()
+ .add(instrumentA.createInstrumentation(methodA))
+ .add(instrumentA.createInstrumentation(methodC))
+ .add(instrumentB.createInstrumentation(methodA))
+ .add(instrumentB.createInstrumentation(methodC))
+ .build(),
+ module.provideInstrumentations(options,
+ BenchmarkClass.forClass(TestBenchmark.class),
+ ImmutableSet.of(instrumentA, instrumentB)));
+ }
+
+ @Test public void provideInstrumentations_withInvalidName() {
+ when(options.benchmarkMethodNames()).thenReturn(
+ ImmutableSet.of("a", "c", "bad"));
+ try {
+ module.provideInstrumentations(options,
+ BenchmarkClass.forClass(TestBenchmark.class),
+ ImmutableSet.of(instrumentA, instrumentB));
+ fail("should have thrown for invalid benchmark method name");
+ } catch (Exception expected) {
+ assertTrue(expected.getMessage().contains("[bad]"));
+ }
+ }
+
+ static final class TestBenchmark {
+ @Benchmark void a() {}
+ @Benchmark void b() {}
+ @Benchmark void c() {}
+ }
+
+ @SupportedPlatform(Platform.Type.JVM)
+ static final class FakeInstrument extends Instrument {
+ @Override public boolean isBenchmarkMethod(Method method) {
+ return true;
+ }
+
+ @Override
+ public Instrumentation createInstrumentation(Method benchmarkMethod)
+ throws InvalidBenchmarkException {
+ return new Instrumentation(benchmarkMethod) {
+ @Override
+ public Class<? extends Worker> workerClass() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void dryRun(Object benchmark) throws InvalidBenchmarkException {}
+
+ @Override
+ MeasurementCollectingVisitor getMeasurementCollectingVisitor() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override public TrialSchedulingPolicy schedulingPolicy() {
+ return TrialSchedulingPolicy.SERIAL;
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java b/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java
new file mode 100644
index 0000000..309a462
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import com.google.caliper.bridge.LogMessage;
+import com.google.caliper.bridge.LogMessageVisitor;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.config.CaliperConfig;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.platform.Platform;
+import com.google.caliper.platform.jvm.JvmPlatform;
+import com.google.caliper.util.Util;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * A collection of Simple java executables and a helper method for creating process builders for
+ * them.
+ */
+final class FakeWorkers {
+
+ @GuardedBy("FakeWorkers.class")
+ private static VirtualMachine jvm;
+
+ /**
+ * Try to find the currently executing jvm binary, N.B. This isn't guaranteed to be cross
+ * platform.
+ */
+ private static synchronized VirtualMachine init() {
+ if (jvm == null) {
+ try {
+ Platform platform = new JvmPlatform();
+ jvm = new VirtualMachine("default",
+ new CaliperConfig(ImmutableMap.<String, String>of()).getDefaultVmConfig(platform));
+ } catch (InvalidConfigurationException e) {
+ throw new RuntimeException();
+ }
+ }
+ return jvm;
+ }
+
+ /**
+ * Returns a ProcessBuilder that attempts to invoke the given class as main in a JVM configured
+ * with a classpath equivalent to the currently executing JVM.
+ */
+ static ProcessBuilder createProcessBuilder(Class<?> mainClass, String ...mainArgs) {
+ VirtualMachine jvm = init();
+ List<String> args;
+ try {
+ args = WorkerProcess.getJvmArgs(jvm, BenchmarkClass.forClass(mainClass));
+ } catch (InvalidBenchmarkException e) {
+ throw new RuntimeException(e);
+ }
+ args.add(mainClass.getName());
+ Collections.addAll(args, mainArgs);
+ return new ProcessBuilder().command(args);
+ }
+
+ public static VirtualMachine getVirtualMachine() {
+ return init();
+ }
+
+ /**
+ * A simple main method that will sleep for the number of milliseconds specified in the first
+ * argument.
+ */
+ static final class Sleeper {
+ public static void main(String[] args) throws NumberFormatException, InterruptedException {
+ Thread.sleep(Long.parseLong(args[0]));
+ }
+ }
+
+ /**
+ * A simple main method that exits immediately with the code provided by the first argument
+ */
+ static final class Exit {
+ public static void main(String[] args) {
+ System.exit(Integer.parseInt(args[0]));
+ }
+ }
+
+ /**
+ * A simple main method that exits immediately with the code provided by the first argument
+ */
+ static final class CloseAndSleep {
+ public static void main(String[] args) throws IOException, InterruptedException {
+ System.err.close();
+ System.in.close();
+ System.out.close();
+ new CountDownLatch(1).await(); // wait forever
+ }
+ }
+
+ /**
+ * Prints alternating arguments to standard out and standard error.
+ */
+ static final class PrintClient {
+ public static void main(String[] args) {
+ for (int i = 0; i < args.length; i++) {
+ if (i % 2 == 0) {
+ System.out.println(args[i]);
+ System.out.flush();
+ } else {
+ System.err.println(args[i]);
+ System.err.flush();
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints alternating arguments to standard out and standard error.
+ */
+ @VisibleForTesting
+ static final class LoadBenchmarkClass {
+
+ public static void main(String[] args) throws ClassNotFoundException {
+ String benchmarkClassName = args[0];
+ Util.loadClass(benchmarkClassName);
+ }
+ }
+
+ static final class DummyLogMessage extends LogMessage implements Serializable {
+ private final String content;
+
+ DummyLogMessage(String content) {
+ this.content = content;
+ }
+
+ @Override public void accept(LogMessageVisitor visitor) {}
+
+ @Override public String toString() {
+ return content;
+ }
+
+ @Override public boolean equals(Object obj) {
+ return obj instanceof DummyLogMessage && ((DummyLogMessage) obj).content.equals(content);
+ }
+
+ @Override public int hashCode() {
+ return content.hashCode();
+ }
+ }
+
+ /**
+ * Connects to a socket on localhost on the port provided as the first argument and echos all
+ * data.
+ *
+ * <p>Once the connection has been closed it prints the remaining args to stdout
+ */
+ static final class SocketEchoClient {
+ public static void main(String[] args) throws Exception {
+ int port = Integer.parseInt(args[0]);
+ OpenedSocket openedSocket = OpenedSocket.fromSocket(
+ new Socket(InetAddress.getLocalHost(), port));
+ OpenedSocket.Reader reader = openedSocket.reader();
+ OpenedSocket.Writer writer = openedSocket.writer();
+ writer.write(new DummyLogMessage("start"));
+ writer.flush();
+ Serializable obj;
+ while ((obj = reader.read()) != null) {
+ writer.write(obj);
+ writer.flush();
+ }
+ writer.close();
+ reader.close();
+ for (int i = 1; i < args.length; i++) {
+ System.out.println(args[i]);
+ System.out.flush();
+ }
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java b/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java
new file mode 100644
index 0000000..075a6e2
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.model.Trial;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * A {@link ResultProcessor} that collects all trials in a static list for easy inspection by tests.
+ */
+public class InMemoryResultsUploader implements ResultProcessor {
+ static ImmutableList<Trial> trials() {
+ return ImmutableList.copyOf(trials);
+ }
+
+ private static List<Trial> trials;
+ private boolean isClosed;
+
+ @Inject public InMemoryResultsUploader() {
+ trials = Lists.newArrayList();
+ }
+
+ @Override public void close() throws IOException {
+ checkState(!isClosed);
+ isClosed = true;
+ }
+
+ @Override public void processTrial(Trial trial) {
+ checkState(!isClosed);
+ trials.add(trial);
+ }
+} \ No newline at end of file
diff --git a/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java b/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java
new file mode 100644
index 0000000..80a1330
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.google.caliper.config.InvalidConfigurationException;
+import com.google.caliper.util.InvalidCommandException;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Unit test covering common user mistakes in benchmark classes.
+ */
+@RunWith(JUnit4.class)
+
+public class MalformedBenchmarksTest {
+ // Put the expected messages together here, which may promote some kind of
+ // consistency in their wording. :)
+
+ private static final String ABSTRACT =
+ "Class '%s' is abstract";
+ private static final String NO_CONSTRUCTOR =
+ "Benchmark class %s does not have a publicly visible default constructor";
+ private static final String NO_METHODS =
+ "There were no experiments to be performed for the class %s using the instruments " +
+ "[allocation, runtime]";
+ private static final String STATIC_BENCHMARK =
+ "Benchmark methods must not be static: timeIt";
+ private static final String WRONG_ARGUMENTS =
+ "Benchmark methods must have no arguments or accept a single int or long parameter: timeIt";
+ private static final String STATIC_PARAM =
+ "Parameter field 'oops' must not be static";
+ private static final String RESERVED_PARAM =
+ "Class '%s' uses reserved parameter name 'vm'";
+ private static final String NO_CONVERSION = "Type 'Object' of parameter field 'oops' "
+ + "has no recognized String-converting method; see <TODO> for details";
+ private static final String CONVERT_FAILED = // granted this one's a little weird (and brittle)
+ "Cannot convert value 'oops' to type 'int': For input string: \"oops\"";
+
+ @Test public void abstractBenchmark() throws Exception {
+ expectException(ABSTRACT, AbstractBenchmark.class);
+ }
+ abstract static class AbstractBenchmark {}
+
+ @Test public void noSuitableConstructor() throws Exception {
+ expectException(String.format(NO_CONSTRUCTOR, BadConstructorBenchmark.class.getName()),
+ BadConstructorBenchmark.class);
+ }
+
+ @SuppressWarnings("unused")
+ static class BadConstructorBenchmark {
+ BadConstructorBenchmark(String damnParam) {}
+ @Benchmark void timeIt(int reps) {}
+ }
+
+ @Test public void noBenchmarkMethods() throws Exception {
+ expectException(NO_METHODS, NoMethodsBenchmark.class);
+ }
+
+ @SuppressWarnings("unused")
+ static class NoMethodsBenchmark {
+ void timeIt(int reps) {} // not annotated
+ }
+
+ @Test public void staticBenchmarkMethod() throws Exception {
+ expectException(STATIC_BENCHMARK, StaticBenchmarkMethodBenchmark.class);
+ }
+
+ @SuppressWarnings("unused")
+ static class StaticBenchmarkMethodBenchmark {
+ @Benchmark public static void timeIt(int reps) {}
+ }
+
+ @Test public void wrongSignature() throws Exception {
+ expectException(WRONG_ARGUMENTS, BoxedParamBenchmark.class);
+ expectException(WRONG_ARGUMENTS, ExtraParamBenchmark.class);
+ }
+
+ @SuppressWarnings("unused")
+ static class BoxedParamBenchmark {
+ @Benchmark void timeIt(Integer reps) {}
+ }
+
+ @SuppressWarnings("unused")
+ static class ExtraParamBenchmark {
+ @Benchmark void timeIt(int reps, int what) {}
+ }
+
+ @Test public void hasBenchmarkOverloads() throws Exception {
+ // N.B. baz is fine since although it has an overload, its overload is not a benchmark method.
+ expectException(
+ "Overloads are disallowed for benchmark methods, found overloads of [bar, foo] in "
+ + "benchmark OverloadsAnnotatedBenchmark",
+ OverloadsAnnotatedBenchmark.class);
+ }
+
+ @SuppressWarnings("unused")
+ static class OverloadsAnnotatedBenchmark {
+ @Benchmark public void foo(long reps) {}
+ @Benchmark public void foo(int reps) {}
+ @Benchmark public void bar(long reps) {}
+ @Benchmark public void bar(int reps) {}
+ @Benchmark public void baz(int reps) {}
+ public void baz(long reps, boolean thing) {}
+ public void baz(long reps) {}
+ }
+
+ @Test public void staticParam() throws Exception {
+ expectException(STATIC_PARAM, StaticParamBenchmark.class);
+ }
+ static class StaticParamBenchmark {
+ @Param static String oops;
+ }
+
+ @Test public void reservedParameterName() throws Exception {
+ expectException(RESERVED_PARAM, ReservedParamBenchmark.class);
+ }
+ static class ReservedParamBenchmark {
+ @Param String vm;
+ }
+
+ @Test public void unparsableParamType() throws Exception {
+ expectException(NO_CONVERSION, UnparsableParamTypeBenchmark.class);
+ }
+ static class UnparsableParamTypeBenchmark {
+ @Param Object oops;
+ }
+
+ @Test public void unparsableParamDefault() throws Exception {
+ expectException(CONVERT_FAILED, UnparsableParamDefaultBenchmark.class);
+ }
+ static class UnparsableParamDefaultBenchmark {
+ @Param({"1", "2", "oops"}) int number;
+ }
+
+ // end of tests
+
+ private void expectException(String expectedMessageFmt, Class<?> benchmarkClass)
+ throws InvalidCommandException, InvalidConfigurationException {
+ try {
+ CaliperMain.exitlessMain(
+ new String[] {"--instrument=allocation,runtime", "--dry-run", benchmarkClass.getName()},
+ new PrintWriter(new StringWriter()), new PrintWriter(new StringWriter()));
+ fail("no exception thrown");
+ } catch (InvalidBenchmarkException e) {
+ try {
+ String expectedMessageText =
+ String.format(expectedMessageFmt, benchmarkClass.getSimpleName());
+ assertEquals(expectedMessageText, e.getMessage());
+
+ // don't swallow our real stack trace
+ } catch (AssertionFailedError afe) {
+ afe.initCause(e);
+ throw afe;
+ }
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java b/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java
new file mode 100644
index 0000000..d88ee39
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.api.ResultProcessor;
+import com.google.caliper.model.Trial;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import java.io.IOException;
+
+/**
+ * Unit test to ensure that {@link ResultProcessorCreator} works properly.
+ */
+@RunWith(JUnit4.class)
+public class ResultProcessorCreatorTest {
+
+ public static final String NOT_SUPPORTED =
+ "ResultProcessor %s not supported as it does not have a public default constructor";
+
+ @Test
+ public void testNotPublicConstructor() {
+ try {
+ ResultProcessorCreator.createResultProcessor(NoPublicConstructorResultProcessor.class);
+ fail("Did not fail on non-public constructor");
+ } catch (UserCodeException e) {
+ assertEquals(String.format(NOT_SUPPORTED, NoPublicConstructorResultProcessor.class),
+ e.getMessage());
+ }
+ }
+
+ public static class NoPublicConstructorResultProcessor implements ResultProcessor {
+
+ NoPublicConstructorResultProcessor() {
+ }
+
+ @Override
+ public void processTrial(Trial trial) {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }
+
+ @Test
+ public void testPublicButNotDefaultConstructor() {
+ try {
+ ResultProcessorCreator.createResultProcessor(
+ PublicButNotDefaultDefaultConstructorResultProcessor.class);
+ fail("Did not fail on public but not default constructor");
+ } catch (UserCodeException e) {
+ assertEquals(
+ String.format(NOT_SUPPORTED, PublicButNotDefaultDefaultConstructorResultProcessor.class),
+ e.getMessage());
+ }
+ }
+
+ public static class PublicButNotDefaultDefaultConstructorResultProcessor
+ implements ResultProcessor {
+
+ public PublicButNotDefaultDefaultConstructorResultProcessor(
+ @SuppressWarnings("UnusedParameters") int i) {
+ }
+
+ @Override
+ public void processTrial(Trial trial) {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }
+
+ @Test
+ public void testPublicConstructor() {
+ ResultProcessor processor =
+ ResultProcessorCreator.createResultProcessor(PublicDefaultConstructorResultProcessor.class);
+ assertTrue(processor instanceof PublicDefaultConstructorResultProcessor);
+ }
+
+ public static class PublicDefaultConstructorResultProcessor implements ResultProcessor {
+
+ @Override
+ public void processTrial(Trial trial) {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java
new file mode 100644
index 0000000..e960c03
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.api.BeforeRep;
+import com.google.caliper.api.Macrobenchmark;
+import com.google.caliper.runner.Instrument.Instrumentation;
+import com.google.caliper.util.ShortDuration;
+import com.google.caliper.worker.MacrobenchmarkWorker;
+import com.google.caliper.worker.RuntimeWorker;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link RuntimeInstrument}.
+ */
+@RunWith(JUnit4.class)
+public class RuntimeInstrumentTest {
+ @Rule public CaliperTestWatcher runner = new CaliperTestWatcher();
+
+ private RuntimeInstrument instrument;
+
+ @Before public void createInstrument() {
+ this.instrument = new RuntimeInstrument(ShortDuration.of(100, NANOSECONDS));
+ }
+
+ @Test public void isBenchmarkMethod() {
+ assertEquals(
+ ImmutableSet.of("macrobenchmark", "microbenchmark", "picobenchmark", "integerParam"),
+ FluentIterable.from(Arrays.asList(RuntimeBenchmark.class.getDeclaredMethods()))
+ .filter(new Predicate<Method>() {
+ @Override public boolean apply(Method input) {
+ return instrument.isBenchmarkMethod(input);
+ }
+ })
+ .transform(new Function<Method, String>() {
+ @Override public String apply(Method input) {
+ return input.getName();
+ }
+ })
+ .toSet());
+ }
+
+ @Test public void createInstrumentation_macrobenchmark() throws Exception {
+ Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("macrobenchmark");
+ Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
+ assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
+ assertEquals(instrument, instrumentation.instrument());
+ assertEquals(MacrobenchmarkWorker.class, instrumentation.workerClass());
+ }
+
+ @Test public void createInstrumentation_microbenchmark() throws Exception {
+ Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("microbenchmark", int.class);
+ Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
+ assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
+ assertEquals(instrument, instrumentation.instrument());
+ assertEquals(RuntimeWorker.Micro.class, instrumentation.workerClass());
+ }
+
+ @Test public void createInstrumentation_picobenchmark() throws Exception {
+ Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("picobenchmark", long.class);
+ Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
+ assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
+ assertEquals(instrument, instrumentation.instrument());
+ assertEquals(RuntimeWorker.Pico.class, instrumentation.workerClass());
+ }
+
+ @Test public void createInstrumentation_badParam() throws Exception {
+ Method benchmarkMethod =
+ RuntimeBenchmark.class.getDeclaredMethod("integerParam", Integer.class);
+ try {
+ instrument.createInstrumentation(benchmarkMethod);
+ fail();
+ } catch (InvalidBenchmarkException expected) {}
+ }
+
+ @Test public void createInstrumentation_notAMacrobenchmark() throws Exception {
+ Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("notAMacrobenchmark");
+ try {
+ instrument.createInstrumentation(benchmarkMethod);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test public void createInstrumentationnotAMicrobenchmark() throws Exception {
+ Method benchmarkMethod =
+ RuntimeBenchmark.class.getDeclaredMethod("notAMicrobenchmark", int.class);
+ try {
+ instrument.createInstrumentation(benchmarkMethod);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test public void createInstrumentation_notAPicobenchmark() throws Exception {
+ Method benchmarkMethod =
+ RuntimeBenchmark.class.getDeclaredMethod("notAPicobenchmark", long.class);
+ try {
+ instrument.createInstrumentation(benchmarkMethod);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @SuppressWarnings("unused")
+ private static final class RuntimeBenchmark {
+ @Benchmark void macrobenchmark() {}
+ @Benchmark void microbenchmark(int reps) {}
+ @Benchmark void picobenchmark(long reps) {}
+
+ @Benchmark void integerParam(Integer oops) {}
+
+ void notAMacrobenchmark() {}
+ void notAMicrobenchmark(int reps) {}
+ void notAPicobenchmark(long reps) {}
+ }
+
+ private double relativeDifference(double a, double b) {
+ return Math.abs(a - b) / ((a + b) / 2.0);
+ }
+
+ static final class TestBenchmark {
+ @Benchmark long pico(long reps) {
+ long dummy = 0;
+ for (long i = 0; i < reps; i++) {
+ dummy += spin();
+ }
+ return dummy;
+ }
+
+ @Benchmark long micro(int reps) {
+ long dummy = 0;
+ for (int i = 0; i < reps; i++) {
+ dummy += spin();
+ }
+ return dummy;
+ }
+
+ @Macrobenchmark long macro() {
+ return spin();
+ }
+ }
+
+ // busy spin for 10ms and return the elapsed time. N.B. we busy spin instead of sleeping so
+ // that we aren't put at the mercy (and variance) of the thread scheduler.
+ private static long spin() {
+ long remainingNanos = TimeUnit.MILLISECONDS.toNanos(10);
+ long start = System.nanoTime();
+ long elapsed;
+ while ((elapsed = System.nanoTime() - start) < remainingNanos) {}
+ return elapsed;
+ }
+
+ @Test
+
+ public void gcBeforeEachOptionIsHonored() throws Exception {
+ runBenchmarkWithKnownHeap(true);
+ // The GC error will only be avoided if gcBeforeEach is true, and
+ // honored by the MacrobenchmarkWorker.
+ assertFalse("No GC warning should be printed to stderr",
+ runner.getStdout().toString().contains("WARNING: GC occurred during timing."));
+ }
+
+ @Test
+
+ public void gcBeforeEachOptionIsReallyNecessary() throws Exception {
+ // Verifies that we indeed get a GC warning if gcBeforeEach = false.
+ runBenchmarkWithKnownHeap(false);
+ assertTrue("A GC warning should be printed to stderr if gcBeforeEach isn't honored",
+ runner.getStdout().toString().contains("WARNING: GC occurred during timing."));
+ }
+
+ private void runBenchmarkWithKnownHeap(boolean gcBeforeEach) throws Exception {
+ runner.forBenchmark(BenchmarkThatAllocatesALot.class)
+ .instrument("runtime")
+ .options(
+ "-Cvm.args=-Xmx512m",
+ "-Cinstrument.runtime.options.measurements=10",
+ "-Cinstrument.runtime.options.gcBeforeEach=" + gcBeforeEach,
+ "--time-limit=30s")
+ .run();
+ }
+
+ static final class BenchmarkThatAllocatesALot {
+ @Benchmark
+ int benchmarkMethod() {
+ // Any larger and the GC doesn't manage to make enough space, resulting in
+ // OOMErrors in both test cases above.
+ long[] array = new long[32 * 1024 * 1024];
+ return array.length;
+ }
+ }
+
+ @Test
+
+ public void maxWarmupWallTimeOptionIsHonored() throws Exception {
+ runner.forBenchmark(MacroBenchmarkWithLongBeforeRep.class)
+ .instrument("runtime")
+ .options(
+ "-Cinstrument.runtime.options.maxWarmupWallTime=100ms",
+ "--time-limit=10s")
+ .run();
+
+ assertTrue(
+ "The maxWarmupWallTime should trigger an interruption of warmup and a warning "
+ + "should be printed to stderr",
+ runner.getStdout().toString().contains(
+ "WARNING: Warmup was interrupted "
+ + "because it took longer than 100ms of wall-clock time."));
+ }
+
+ static final class MacroBenchmarkWithLongBeforeRep {
+ @BeforeRep
+ public void beforeRepMuchLongerThanBenchmark() {
+ Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
+ }
+
+ @Benchmark
+ long prettyFastMacroBenchmark() {
+ return spin();
+ }
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java b/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java
new file mode 100644
index 0000000..8add40b
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.bridge.StartupAnnounceMessage;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Tests for {@link ServerSocketService}.
+ */
+@RunWith(JUnit4.class)
+
+public class ServerSocketServiceTest {
+
+ private final ServerSocketService service = new ServerSocketService();
+ private int port;
+
+ @Before public void startService() {
+ service.startAsync().awaitRunning();
+ port = service.getPort();
+ }
+
+ @After public void stopService() {
+ service.stopAsync().awaitTerminated();
+ }
+
+ @Test public void getConnectionId_requestComesInFirst() throws Exception {
+ UUID id = UUID.randomUUID();
+ ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id);
+ assertFalse(pendingServerConnection.isDone());
+ OpenedSocket clientSocket = openConnectionAndIdentify(id);
+ // Assert that the ends are hooked up to each other
+ assertEndsConnected(clientSocket, pendingServerConnection.get());
+ }
+
+ @Test public void getConnectionIdTwice_acceptComesFirst() throws Exception {
+ UUID id = UUID.randomUUID();
+ OpenedSocket clientSocket = openConnectionAndIdentify(id);
+
+ ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id);
+ // wait for the service to fully initialize the connection
+ OpenedSocket serverSocket = pendingServerConnection.get();
+ assertEndsConnected(clientSocket, serverSocket);
+ try {
+ // the second request is an error
+ service.getConnection(id).get();
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test public void getConnectionStoppedService() throws Exception {
+ UUID id = UUID.randomUUID();
+ ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id);
+ assertFalse(pendingServerConnection.isDone());
+ service.stopAsync().awaitTerminated();
+ assertTrue(pendingServerConnection.isDone());
+
+ try {
+ pendingServerConnection.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertEquals("The socket has been closed", e.getCause().getMessage());
+ }
+
+ try {
+ service.getConnection(UUID.randomUUID());
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ private OpenedSocket openClientConnection() throws IOException {
+ return OpenedSocket.fromSocket(new Socket(InetAddress.getLoopbackAddress(), port));
+ }
+
+ /**
+ * Opens a connection to the service and identifies itself using the id.
+ */
+ private OpenedSocket openConnectionAndIdentify(UUID id) throws IOException {
+ OpenedSocket clientSocket = openClientConnection();
+ OpenedSocket.Writer writer = clientSocket.writer();
+ writer.write(new StartupAnnounceMessage(id));
+ writer.flush();
+ return clientSocket;
+ }
+
+ private void assertEndsConnected(OpenedSocket clientSocket, OpenedSocket serverSocket)
+ throws IOException {
+ serverSocket.writer().write("hello client!");
+ serverSocket.writer().flush(); // necessary to prevent deadlock
+ assertEquals("hello client!", clientSocket.reader().read());
+
+ clientSocket.writer().write("hello server!");
+ clientSocket.writer().flush(); // ditto
+ assertEquals("hello server!", serverSocket.reader().read());
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java b/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java
new file mode 100644
index 0000000..f20e72a
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.runner;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.bridge.LogMessage;
+import com.google.caliper.bridge.OpenedSocket;
+import com.google.caliper.runner.FakeWorkers.DummyLogMessage;
+import com.google.caliper.runner.StreamService.StreamItem;
+import com.google.caliper.runner.StreamService.StreamItem.Kind;
+import com.google.caliper.util.Parser;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Service.Listener;
+import com.google.common.util.concurrent.Service.State;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.text.ParseException;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link StreamService}.
+ */
+@RunWith(JUnit4.class)
+
+public class StreamServiceTest {
+
+ private ServerSocket serverSocket;
+ private final StringWriter writer = new StringWriter();
+ private final PrintWriter stdout = new PrintWriter(writer, true);
+ private final Parser<LogMessage> parser = new Parser<LogMessage>() {
+ @Override public LogMessage parse(final CharSequence text) throws ParseException {
+ return new DummyLogMessage(text.toString());
+ }
+ };
+
+ private StreamService service;
+ private final CountDownLatch terminalLatch = new CountDownLatch(1);
+ private static final int TRIAL_NUMBER = 3;
+
+ @Before public void setUp() throws IOException {
+ serverSocket = new ServerSocket(0);
+ }
+
+ @After public void closeSocket() throws IOException {
+ serverSocket.close();
+ }
+
+ @After public void stopService() {
+ if (service != null && service.state() != State.FAILED && service.state() != State.TERMINATED) {
+ service.stopAsync().awaitTerminated();
+ }
+ }
+
+ @Test public void testReadOutput() throws Exception {
+ makeService(FakeWorkers.PrintClient.class, "foo", "bar");
+ service.startAsync().awaitRunning();
+ StreamItem item1 = readItem();
+ assertEquals(Kind.DATA, item1.kind());
+ Set<String> lines = Sets.newHashSet();
+ lines.add(item1.content().toString());
+ StreamItem item2 = readItem();
+ assertEquals(Kind.DATA, item2.kind());
+ lines.add(item2.content().toString());
+ assertEquals(Sets.newHashSet("foo", "bar"), lines);
+ assertEquals(State.RUNNING, service.state());
+ StreamItem item3 = readItem();
+ assertEquals(Kind.EOF, item3.kind());
+ awaitStopped(100, TimeUnit.MILLISECONDS);
+ assertTerminated();
+ }
+
+ @Test public void failingProcess() throws Exception {
+ makeService(FakeWorkers.Exit.class, "1");
+ service.startAsync().awaitRunning();
+ assertEquals(Kind.EOF, readItem().kind());
+ awaitStopped(100, TimeUnit.MILLISECONDS);
+ assertEquals(State.FAILED, service.state());
+ }
+
+ @Test public void processDoesntExit() throws Exception {
+ // close all fds and then sleep
+ makeService(FakeWorkers.CloseAndSleep.class);
+ service.startAsync().awaitRunning();
+ assertEquals(Kind.EOF, readItem().kind());
+ awaitStopped(200, TimeUnit.MILLISECONDS); // we
+ assertEquals(State.FAILED, service.state());
+ }
+
+ @Test public void testSocketInputOutput() throws Exception {
+ int localport = serverSocket.getLocalPort();
+ // read from the socket and echo it back
+ makeService(FakeWorkers.SocketEchoClient.class, Integer.toString(localport));
+
+ service.startAsync().awaitRunning();
+ assertEquals(new DummyLogMessage("start"), readItem().content());
+ service.sendMessage(new DummyLogMessage("hello socket world"));
+ assertEquals(new DummyLogMessage("hello socket world"), readItem().content());
+ service.closeWriter();
+ assertEquals(State.RUNNING, service.state());
+ StreamItem nextItem = readItem();
+ assertEquals("Expected EOF " + nextItem, Kind.EOF, nextItem.kind());
+ awaitStopped(100, TimeUnit.MILLISECONDS);
+ assertTerminated();
+ }
+
+ @Test public void testSocketClosesBeforeProcess() throws Exception {
+ int localport = serverSocket.getLocalPort();
+ // read from the socket and echo it back
+ makeService(FakeWorkers.SocketEchoClient.class, Integer.toString(localport), "foo");
+ service.startAsync().awaitRunning();
+ assertEquals(new DummyLogMessage("start"), readItem().content());
+ service.sendMessage(new DummyLogMessage("hello socket world"));
+ assertEquals(new DummyLogMessage("hello socket world"), readItem().content());
+ service.closeWriter();
+
+ assertEquals("foo", readItem().content().toString());
+
+ assertEquals(State.RUNNING, service.state());
+ assertEquals(Kind.EOF, readItem().kind());
+ awaitStopped(100, TimeUnit.MILLISECONDS);
+ assertTerminated();
+ }
+
+ @Test public void failsToAcceptConnection() throws Exception {
+ serverSocket.close(); // This will force serverSocket.accept to throw a SocketException
+ makeService(FakeWorkers.Sleeper.class, Long.toString(TimeUnit.MINUTES.toMillis(10)));
+ try {
+ service.startAsync().awaitRunning();
+ fail();
+ } catch (IllegalStateException expected) {}
+ assertEquals(SocketException.class, service.failureCause().getClass());
+ }
+
+ /** Reads an item, asserting that there was no timeout. */
+ private StreamItem readItem() throws InterruptedException {
+ StreamItem item = service.readItem(10, TimeUnit.SECONDS);
+ assertNotSame("Timed out while reading item from worker", Kind.TIMEOUT, item.kind());
+ return item;
+ }
+
+ /**
+ * Wait for the service to reach a terminal state without calling stop.
+ */
+ private void awaitStopped(long time, TimeUnit unit) throws InterruptedException {
+ assertTrue(terminalLatch.await(time, unit));
+ }
+
+ private void assertTerminated() {
+ State state = service.state();
+ if (state != State.TERMINATED) {
+ if (state == State.FAILED) {
+ throw new AssertionError(service.failureCause());
+ }
+ fail("Expected service to be terminated but was: " + state);
+ }
+ }
+
+ @SuppressWarnings("resource")
+ private void makeService(Class<?> main, String ...args) {
+ checkState(service == null, "You can only make one StreamService per test");
+ UUID trialId = UUID.randomUUID();
+ TrialOutputLogger trialOutput = new TrialOutputLogger(new TrialOutputFactory() {
+ @Override public FileAndWriter getTrialOutputFile(int trialNumber)
+ throws FileNotFoundException {
+ checkArgument(trialNumber == TRIAL_NUMBER);
+ return new FileAndWriter(new File("/tmp/not-a-file"), stdout);
+ }
+
+ @Override public void persistFile(File f) {
+ throw new UnsupportedOperationException();
+ }
+
+ }, TRIAL_NUMBER, trialId, null /* experiment */);
+ try {
+ // normally the TrialRunLoop opens/closes the logger
+ trialOutput.open();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ service = new StreamService(
+ new WorkerProcess(FakeWorkers.createProcessBuilder(main, args),
+ trialId,
+ getSocketFuture(),
+ new RuntimeShutdownHookRegistrar()),
+ parser,
+ trialOutput);
+ service.addListener(new Listener() {
+ @Override public void starting() {}
+ @Override public void running() {}
+ @Override public void stopping(State from) {}
+ @Override public void terminated(State from) {
+ terminalLatch.countDown();
+ }
+ @Override public void failed(State from, Throwable failure) {
+ terminalLatch.countDown();
+ }
+ }, MoreExecutors.directExecutor());
+ }
+
+ private ListenableFuture<OpenedSocket> getSocketFuture() {
+ ListenableFutureTask<OpenedSocket> openSocketTask = ListenableFutureTask.create(
+ new Callable<OpenedSocket>() {
+ @Override
+ public OpenedSocket call() throws Exception {
+ return OpenedSocket.fromSocket(serverSocket.accept());
+ }
+ });
+ // N.B. this thread will block on serverSocket.accept until a connection is accepted or the
+ // socket is closed, so no matter what this thread will die with the test.
+ Thread opener = new Thread(openSocketTask, "SocketOpener");
+ opener.setDaemon(true);
+ opener.start();
+ return openSocketTask;
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java b/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java
new file mode 100644
index 0000000..397f7d8
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.runner;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.config.VmConfig;
+import com.google.caliper.model.BenchmarkSpec;
+import com.google.caliper.platform.jvm.JvmPlatform;
+import com.google.caliper.worker.WorkerMain;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Tests {@link WorkerProcess}.
+ *
+ * <p>TODO(lukes,gak): write more tests for how our specs get turned into commandlines
+ */
+
+@RunWith(JUnit4.class)
+public class WorkerProcessTest {
+ private static final int PORT_NUMBER = 4004;
+ private static final UUID TRIAL_ID = UUID.randomUUID();
+
+ private static class MockRegistrar implements ShutdownHookRegistrar {
+ Set<Thread> hooks = Sets.newHashSet();
+ @Override public void addShutdownHook(Thread hook) {
+ hooks.add(hook);
+ }
+ @Override public boolean removeShutdownHook(Thread hook) {
+ return hooks.remove(hook);
+ }
+ }
+
+ private final MockRegistrar registrar = new MockRegistrar();
+ private BenchmarkClass benchmarkClass;
+
+ @Before public void setUp() throws InvalidBenchmarkException {
+ benchmarkClass = BenchmarkClass.forClass(TestBenchmark.class);
+ }
+
+ @Test public void simpleArgsTest() throws Exception {
+ Method method = TestBenchmark.class.getDeclaredMethods()[0];
+ AllocationInstrument allocationInstrument = new AllocationInstrument();
+ allocationInstrument.setOptions(ImmutableMap.of("trackAllocations", "true"));
+ VmConfig vmConfig = new VmConfig(
+ new File("foo"),
+ Arrays.asList("--doTheHustle"),
+ new File("java"),
+ new JvmPlatform());
+ Experiment experiment = new Experiment(
+ allocationInstrument.createInstrumentation(method),
+ ImmutableMap.<String, String>of(),
+ new VirtualMachine("foo-jvm", vmConfig));
+ BenchmarkSpec spec = new BenchmarkSpec.Builder()
+ .className(TestBenchmark.class.getName())
+ .methodName(method.getName())
+ .build();
+ ProcessBuilder builder = createProcess(experiment, spec);
+ List<String> commandLine = builder.command();
+ assertEquals(new File("java").getAbsolutePath(), commandLine.get(0));
+ assertEquals("--doTheHustle", commandLine.get(1)); // vm specific flags come next
+ assertEquals("-cp", commandLine.get(2)); // then the classpath
+ // should we assert on classpath contents?
+ ImmutableSet<String> extraCommandLineArgs =
+ allocationInstrument.getExtraCommandLineArgs(vmConfig);
+ assertEquals(extraCommandLineArgs.asList(),
+ commandLine.subList(4, 4 + extraCommandLineArgs.size()));
+ int index = 4 + extraCommandLineArgs.size();
+ assertEquals("-XX:+PrintFlagsFinal", commandLine.get(index));
+ assertEquals("-XX:+PrintCompilation", commandLine.get(++index));
+ assertEquals("-XX:+PrintGC", commandLine.get(++index));
+ assertEquals(WorkerMain.class.getName(), commandLine.get(++index));
+ // followed by worker args...
+ }
+
+ @Test public void shutdownHook_waitFor() throws Exception {
+ Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker();
+ assertEquals("worker-shutdown-hook-" + TRIAL_ID,
+ Iterables.getOnlyElement(registrar.hooks).getName());
+ worker.waitFor();
+ assertTrue(registrar.hooks.isEmpty());
+ }
+
+ @Test public void shutdownHook_exitValueThrows() throws Exception {
+ Process worker = createWorkerProcess(
+ FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker();
+ try {
+ Thread hook = Iterables.getOnlyElement(registrar.hooks);
+ assertEquals("worker-shutdown-hook-" + TRIAL_ID, hook.getName());
+ try {
+ worker.exitValue();
+ fail();
+ } catch (IllegalThreadStateException expected) {}
+ assertTrue(registrar.hooks.contains(hook));
+ } finally {
+ worker.destroy(); // clean up
+ }
+ }
+
+ @Test public void shutdownHook_exitValue() throws Exception {
+ Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker();
+ while (true) {
+ try {
+ worker.exitValue();
+ assertTrue(registrar.hooks.isEmpty());
+ break;
+ } catch (IllegalThreadStateException e) {
+ Thread.sleep(10); // keep polling
+ }
+ }
+ }
+
+ @Test public void shutdownHook_destroy() throws Exception {
+ Process worker = createWorkerProcess(
+ FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker();
+ worker.destroy();
+ assertTrue(registrar.hooks.isEmpty());
+ }
+
+ static final class TestBenchmark {
+ @Benchmark long thing(long reps) {
+ long dummy = 0;
+ for (long i = 0; i < reps; i++) {
+ dummy += new Long(dummy).hashCode();
+ }
+ return dummy;
+ }
+ }
+
+ private ProcessBuilder createProcess(Experiment experiment, BenchmarkSpec benchmarkSpec) {
+ return WorkerProcess.buildProcess(TRIAL_ID, experiment, benchmarkSpec, PORT_NUMBER,
+ benchmarkClass);
+ }
+
+ private WorkerProcess createWorkerProcess(Class<?> main, String ...args) {
+ return new WorkerProcess(FakeWorkers.createProcessBuilder(main, args),
+ TRIAL_ID,
+ null,
+ registrar);
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java b/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java
new file mode 100644
index 0000000..4ae875b
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LinearTranslationTest {
+ private static final double CLOSE_ENOUGH = 1.0E-13;
+
+ @Test public void linearTranslation() {
+ LinearTranslation ctof = new LinearTranslation(0, 32, 100, 212);
+ assertEquals(32, ctof.translate(0), CLOSE_ENOUGH);
+ assertEquals(212, ctof.translate(100), CLOSE_ENOUGH);
+ assertEquals(98.6, ctof.translate(37), CLOSE_ENOUGH);
+ assertEquals(-40, ctof.translate(-40), CLOSE_ENOUGH);
+
+ LinearTranslation reversed = new LinearTranslation(5, 42, 69, 0);
+ assertEquals(-21, reversed.translate(101), CLOSE_ENOUGH);
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java b/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java
new file mode 100644
index 0000000..0c32fc8
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public class ShortDurationTest {
+ @Test public void valueOf() {
+ assertEquals(ShortDuration.zero(), ShortDuration.valueOf("0"));
+ testIt(0, "0ns", "0s");
+ testIt(0, "0 ns", "0s");
+ testIt(0, "0nanos", "0s");
+ testIt(0, "0nanoseconds", "0s");
+ testIt(0, "0 ms", "0s");
+ testIt(0, "0us", "0s");
+ testIt(0, "1e-12 ms", "0s");
+ testIt(1, "0.501 ns", "0.501ns");
+ testIt(1000, "1 \u03bcs", "1\u03bcs");
+ // testIt(500, "0.5us", "500ns");
+ // testIt(499, "0.499000000000000000000000000000001us", "499ns");
+ // testIt(500, "0.49995 us", "500ns");
+ // testIt(60480000000000L, "0.7 days", "16.80h");
+ // testIt(Long.MAX_VALUE, "106751.99116730064591 days", "106751.99d");
+ }
+
+ @Test public void tooLongForALong() {
+ try {
+ ShortDuration.valueOf("106751.99116730064592 days");
+ fail();
+ } catch (ArithmeticException expected) {
+ }
+ }
+
+ private static void testIt(long i, String s, String p) {
+ ShortDuration d = ShortDuration.valueOf(s);
+ assertEquals(i, d.to(TimeUnit.NANOSECONDS));
+ assertEquals(p, d.toString());
+ }
+}
diff --git a/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java b/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java
new file mode 100644
index 0000000..0d75d3b
--- /dev/null
+++ b/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.caliper.worker;
+
+import static com.google.caliper.worker.RuntimeWorker.INITIAL_REPS;
+import static com.google.caliper.worker.RuntimeWorker.calculateTargetReps;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+
+import com.google.caliper.util.ShortDuration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.math.BigDecimal;
+
+/**
+ * Tests {@link RuntimeWorker}.
+ */
+@RunWith(JUnit4.class)
+public class RuntimeWorkerTest {
+ private static final ShortDuration TIMING_INTERVAL = ShortDuration.of(100, MILLISECONDS);
+
+ @Test public void testCalculateTargetReps_tinyBenchmark() {
+ // this is one cycle on a 5GHz machine
+ ShortDuration oneCycle = ShortDuration.of(new BigDecimal("2.0e-10"), SECONDS);
+ long targetReps = calculateTargetReps(INITIAL_REPS,
+ oneCycle.times(INITIAL_REPS).to(NANOSECONDS), TIMING_INTERVAL.to(NANOSECONDS), 0.0);
+ long expectedReps = TIMING_INTERVAL.toPicos() / oneCycle.toPicos();
+ assertEquals(expectedReps, targetReps);
+ }
+
+ @Test public void testCalculateTargetReps_hugeBenchmark() {
+ long targetReps =
+ calculateTargetReps(INITIAL_REPS, HOURS.toNanos(1), TIMING_INTERVAL.to(NANOSECONDS), 0.0);
+ assertEquals(1, targetReps);
+ }
+
+ @Test public void testCalculateTargetReps_applyRandomness() {
+ long targetReps = calculateTargetReps(INITIAL_REPS, MILLISECONDS.toNanos(100),
+ TIMING_INTERVAL.to(NANOSECONDS), 0.5);
+ assertEquals(110, targetReps);
+ }
+}
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt
new file mode 100644
index 0000000..2227fb1
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt
@@ -0,0 +1,288 @@
+ 1 java.lang.String::hashCode (60 bytes)
+ 2 java.lang.String::lastIndexOf (156 bytes)
+ 3 java.lang.String::indexOf (151 bytes)
+ 4 sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (553 bytes)
+ 5 java.io.UnixFileSystem::normalize (75 bytes)
+ 6 sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes)
+ 7 java.lang.String::indexOf (166 bytes)
+ 1% sun.net.www.ParseUtil::encodePath @ 29 (336 bytes)
+ 8 java.lang.String::equals (88 bytes)
+ 9 sun.net.www.ParseUtil::encodePath (336 bytes)
+ 10 java.lang.String::charAt (33 bytes)
+ 11 java.util.Arrays::binarySearch0 (72 bytes)
+ 12 com.google.common.base.CharMatcher$10::matches (17 bytes)
+ 13 java.util.Arrays::binarySearch (9 bytes)
+ 2% com.google.common.base.CharMatcher::setBits @ 3 (28 bytes)
+ 14 com.google.common.base.CharMatcher::setBits (28 bytes)
+ 15 java.lang.Object::<init> (1 bytes)
+ 16 java.util.zip.ZipFile::ensureOpen (37 bytes)
+ 17 java.util.zip.ZipFile::access$300 (5 bytes)
+ 18 java.util.zip.ZipFile::access$100 (5 bytes)
+ 19 java.util.HashMap::indexFor (6 bytes)
+ 20 java.util.HashMap::hash (23 bytes)
+ 21 java.lang.String::<init> (20 bytes)
+ 22 java.lang.String::substring (83 bytes)
+ 23 java.util.HashMap::get (79 bytes)
+ 24 java.lang.String::lastIndexOf (12 bytes)
+--- n java.util.zip.ZipFile::freeEntry (static)
+--- n java.util.zip.ZipEntry::initFields
+ 25 java.util.zip.ZipFile::access$800 (6 bytes)
+ 26 java.util.jar.JarFile$JarFileEntry::<init> (11 bytes)
+ 27 java.util.jar.JarEntry::<init> (6 bytes)
+--- n java.util.zip.ZipFile::getNextEntry (static)
+ 28 java.util.zip.ZipEntry::<init> (102 bytes)
+ 29 java.util.jar.JarFile$1::hasMoreElements (10 bytes)
+ 30 ! java.util.zip.ZipFile$2::hasMoreElements (41 bytes)
+ 31 sun.misc.URLClassPath::getResourceMapKey (55 bytes)
+ 32 java.util.jar.JarFile$1::nextElement (5 bytes)
+ 33 java.util.jar.JarFile$1::nextElement (26 bytes)
+ 34 java.util.zip.ZipFile$2::nextElement (5 bytes)
+ 35 ! java.util.zip.ZipFile$2::nextElement (211 bytes)
+ 36 java.util.zip.ZipFile::access$400 (6 bytes)
+ 37 java.util.zip.ZipEntry::<init> (43 bytes)
+ 38 sun.misc.URLClassPath$JarLoader::addJarEntriesToEntryMap (202 bytes)
+ 39 java.util.ArrayList::get (11 bytes)
+ 40 java.util.ArrayList::rangeCheck (22 bytes)
+ 41 java.util.ArrayList::elementData (7 bytes)
+ 23 made not entrant (2) java.util.HashMap::get (79 bytes)
+ 42 java.lang.String::replace (142 bytes)
+ 43 java.util.Properties$LineReader::readLine (452 bytes)
+ 44 java.lang.String::startsWith (78 bytes)
+--- n java.lang.System::arraycopy (static)
+ 45 java.util.HashMap::get (79 bytes)
+ 46 java.io.DataOutputStream::writeUTF (435 bytes)
+ 47 ! sun.reflect.generics.parser.SignatureParser::current (40 bytes)
+ 48 java.io.DataOutputStream::incCount (20 bytes)
+ 49 s java.io.ByteArrayOutputStream::write (55 bytes)
+ 50 java.lang.CharacterData::of (120 bytes)
+ 51 java.lang.CharacterDataLatin1::getProperties (11 bytes)
+ 52 java.lang.String::getChars (66 bytes)
+ 53 java.lang.Math::min (11 bytes)
+ 54 java.lang.AbstractStringBuilder::append (40 bytes)
+ 55 java.util.jar.Manifest$FastInputStream::readLine (167 bytes)
+ 56 net.sf.cglib.asm.ByteVector::putUTF8 (394 bytes)
+ 57 net.sf.cglib.asm.Type::a (253 bytes)
+ 58 s java.lang.StringBuffer::append (8 bytes)
+ 59 net.sf.cglib.asm.Type::a (214 bytes)
+ 60 net.sf.cglib.asm.Type::getArgumentTypes (131 bytes)
+ 61 java.util.Arrays::copyOfRange (63 bytes)
+ 62 java.lang.AbstractStringBuilder::append (60 bytes)
+ 63 java.lang.String::<init> (72 bytes)
+ 64 java.lang.Character::isWhitespace (5 bytes)
+ 65 java.lang.Character::isWhitespace (9 bytes)
+ 66 java.lang.CharacterDataLatin1::isWhitespace (23 bytes)
+ 67 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes)
+ 68 java.lang.StringBuilder::append (8 bytes)
+ 43 made not entrant (2) java.util.Properties$LineReader::readLine (452 bytes)
+ 69 java.lang.StringBuilder::append (8 bytes)
+ 3% java.util.Properties$LineReader::readLine @ 21 (452 bytes)
+ 70 java.util.HashMap::put (126 bytes)
+ 71 sun.reflect.generics.parser.SignatureParser::advance (37 bytes)
+ 72 java.util.zip.ZStreamRef::address (5 bytes)
+ 73 java.lang.String::compareTo (150 bytes)
+ 74 sun.misc.MetaIndex::mayContain (51 bytes)
+ 75 java.lang.String::startsWith (7 bytes)
+ 23 made zombie (2) java.util.HashMap::get (79 bytes)
+ 76 java.lang.AbstractStringBuilder::<init> (12 bytes)
+ 77 java.util.ArrayList$Itr::hasNext (20 bytes)
+ 78 java.util.zip.InflaterInputStream::ensureOpen (18 bytes)
+ 79 java.util.HashMap::transfer (83 bytes)
+ 80 java.util.ArrayList$Itr::checkForComodification (23 bytes)
+ 81 java.util.ArrayList$Itr::next (66 bytes)
+ 82 sun.reflect.ClassFileAssembler::emitByte (11 bytes)
+ 83 sun.reflect.ByteVectorImpl::add (38 bytes)
+ 84 java.util.ArrayList::ensureCapacity (58 bytes)
+ 85 java.util.ArrayList::add (29 bytes)
+ 86 java.util.HashMap$HashIterator::nextEntry (99 bytes)
+ 87 java.lang.Class::searchMethods (90 bytes)
+ 88 java.util.HashMap$HashIterator::<init> (63 bytes)
+ 89 java.lang.reflect.AccessibleObject::<init> (5 bytes)
+ 43 made zombie (2) java.util.Properties$LineReader::readLine (452 bytes)
+ 90 sun.reflect.ReflectionFactory::langReflectAccess (15 bytes)
+ 91 java.lang.Class::copyMethods (36 bytes)
+ 92 java.lang.reflect.ReflectAccess::copyMethod (5 bytes)
+ 93 java.lang.reflect.Method::copy (67 bytes)
+ 94 java.lang.reflect.Method::<init> (68 bytes)
+ 95 sun.reflect.ReflectionFactory::copyMethod (10 bytes)
+ 96 java.lang.System::getSecurityManager (4 bytes)
+ 97 ! com.sun.jersey.core.reflection.ReflectionHelper::findMethodOnClass (94 bytes)
+ 98 java.util.HashMap$HashIterator::hasNext (13 bytes)
+ 99 java.lang.StringBuilder::toString (17 bytes)
+100 java.lang.reflect.Method::equals (107 bytes)
+101 java.lang.ref.SoftReference::get (18 bytes)
+--- n java.lang.Object::clone
+102 s java.lang.reflect.Method::declaredAnnotations (39 bytes)
+103 java.lang.Class::clearCachesOnClassRedefinition (70 bytes)
+104 java.lang.Class::checkInitted (19 bytes)
+105 com.sun.jersey.core.reflection.AnnotatedMethod::hasParameterAnnotations (80 bytes)
+--- n java.lang.Class::getClassLoader0
+106 java.lang.Class$MethodArray::addIfNotPresent (47 bytes)
+107 java.lang.Class::getMethod0 (97 bytes)
+--- n java.lang.String::intern
+108 java.lang.Class::getName (20 bytes)
+109 java.util.Collections$EmptyMap::get (2 bytes)
+110 java.lang.reflect.Method::getAnnotation (26 bytes)
+111 java.util.HashMap$KeyIterator::next (8 bytes)
+110 made not entrant (2) java.lang.reflect.Method::getAnnotation (26 bytes)
+112 java.lang.Class::privateGetDeclaredMethods (119 bytes)
+--- n java.lang.Class::getInterfaces
+113 com.sun.jersey.core.reflection.AnnotatedMethod::hasMethodAnnotations (43 bytes)
+114 java.lang.StringBuilder::<init> (7 bytes)
+115 java.util.AbstractCollection::<init> (5 bytes)
+116 java.lang.Character::toLowerCase (9 bytes)
+117 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
+--- n java.lang.Class::isInterface
+118 java.lang.Class::argumentTypesToString (78 bytes)
+119 java.lang.Class::checkMemberAccess (78 bytes)
+--- n java.lang.Class::getSuperclass
+120 sun.reflect.generics.tree.SimpleClassTypeSignature::make (11 bytes)
+121 sun.reflect.generics.tree.SimpleClassTypeSignature::<init> (20 bytes)
+122 sun.reflect.generics.parser.SignatureParser::parseClassTypeSignatureSuffix (53 bytes)
+123 sun.reflect.generics.parser.SignatureParser::parseSimpleClassTypeSignature (71 bytes)
+124 java.util.HashMap$Entry::<init> (26 bytes)
+125 sun.reflect.generics.visitor.Reifier::visitClassTypeSignature (381 bytes)
+126 java.util.Properties$LineReader::readLine (452 bytes)
+127 java.util.Properties::loadConvert (505 bytes)
+128 sun.reflect.ClassFileAssembler::emitConstantPoolUTF8 (50 bytes)
+129 ! sun.reflect.UTF8::encode (189 bytes)
+130 sun.reflect.UTF8::utf8Length (81 bytes)
+131 java.util.AbstractList::<init> (10 bytes)
+132 java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+133 java.util.regex.Matcher::search (109 bytes)
+134 java.util.regex.Pattern$Curly::match0 (174 bytes)
+132 made not entrant (2) java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+135 java.lang.Character::charCount (12 bytes)
+136 java.lang.Character::isHighSurrogate (18 bytes)
+137 java.lang.String::codePointAt (44 bytes)
+138 java.lang.Character::codePointAtImpl (41 bytes)
+139 java.util.regex.Pattern$Start::match (109 bytes)
+140 com.google.common.io.LineBuffer::add (201 bytes)
+141 java.util.regex.Pattern$Curly::match (86 bytes)
+142 java.util.zip.Inflater::ensureOpen (47 bytes)
+143 java.util.regex.Pattern::has (15 bytes)
+144 java.util.regex.Matcher::reset (83 bytes)
+145 java.util.regex.Pattern$Node::match (27 bytes)
+146 java.util.regex.Pattern$CharProperty::match (56 bytes)
+147 java.lang.Character::codePointAt (51 bytes)
+148 java.util.regex.Pattern$Ctype::isSatisfiedBy (24 bytes)
+149 java.util.regex.ASCII::isType (15 bytes)
+150 java.util.regex.ASCII::getType (17 bytes)
+151 java.util.regex.Pattern$Slice::match (79 bytes)
+152 java.util.regex.Pattern$Dot::isSatisfiedBy (34 bytes)
+153 java.util.regex.Matcher::match (109 bytes)
+154 java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+155 java.util.regex.Pattern$Single::isSatisfiedBy (14 bytes)
+--- n java.lang.Thread::currentThread (static)
+156 com.google.common.collect.AbstractIndexedListIterator::hasNext (17 bytes)
+157 java.lang.String::indexOf (7 bytes)
+ 70 made not entrant (2) java.util.HashMap::put (126 bytes)
+133 made not entrant (2) java.util.regex.Matcher::search (109 bytes)
+158 java.util.regex.Matcher::search (109 bytes)
+146 made not entrant (2) java.util.regex.Pattern$CharProperty::match (56 bytes)
+159 java.lang.String::toUpperCase (442 bytes)
+160 java.lang.Character::toUpperCaseEx (30 bytes)
+161 java.lang.CharacterDataLatin1::toUpperCaseEx (71 bytes)
+110 made zombie (2) java.lang.reflect.Method::getAnnotation (26 bytes)
+162 java.nio.Buffer::position (43 bytes)
+163 java.nio.charset.CoderResult::isUnderflow (13 bytes)
+164 java.nio.Buffer::limit (62 bytes)
+165 java.nio.Buffer::<init> (121 bytes)
+166 java.nio.charset.CoderResult::isOverflow (14 bytes)
+167 java.nio.Buffer::hasRemaining (17 bytes)
+168 java.nio.CharBuffer::hasArray (20 bytes)
+169 java.nio.ByteBuffer::hasArray (20 bytes)
+170 ! java.nio.CharBuffer::wrap (20 bytes)
+171 java.nio.HeapCharBuffer::<init> (14 bytes)
+172 java.nio.CharBuffer::<init> (22 bytes)
+173 ! java.nio.charset.CharsetEncoder::encode (285 bytes)
+174 sun.nio.cs.UTF_8$Encoder::encodeLoop (28 bytes)
+175 java.nio.Buffer::remaining (10 bytes)
+176 sun.nio.cs.StreamEncoder::ensureOpen (18 bytes)
+177 sun.nio.cs.StreamEncoder::implWrite (156 bytes)
+178 ! sun.nio.cs.StreamEncoder::write (78 bytes)
+179 com.google.gson.stream.JsonWriter::string (365 bytes)
+180 java.io.OutputStreamWriter::write (9 bytes)
+181 sun.nio.cs.StreamEncoder::write (17 bytes)
+179 made not entrant (2) com.google.gson.stream.JsonWriter::string (365 bytes)
+182 com.google.gson.stream.JsonWriter::string (365 bytes)
+183 java.io.StringWriter::write (11 bytes)
+184 s java.lang.StringBuffer::append (8 bytes)
+ 49 made not entrant (2) java.io.ByteArrayOutputStream::write (55 bytes)
+185 sun.security.util.Cache$EqualByteArray::hashCode (57 bytes)
+186 java.lang.Math::max (11 bytes)
+ 70 made zombie (2) java.util.HashMap::put (126 bytes)
+187 s java.io.ByteArrayInputStream::read (36 bytes)
+188 java.math.BigInteger::stripLeadingZeroBytes (132 bytes)
+189 s java.io.ByteArrayInputStream::available (10 bytes)
+132 made zombie (2) java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+190 sun.security.util.DerInputStream::available (8 bytes)
+191 java.io.ByteArrayInputStream::mark (9 bytes)
+192 java.io.DataInputStream::readUTF (501 bytes)
+193 sun.security.util.DerInputStream::getLength (111 bytes)
+194 sun.security.util.ObjectIdentifier::checkValidOid (115 bytes)
+195 sun.security.util.DerInputStream::getByte (12 bytes)
+196 sun.security.util.ObjectIdentifier::initFromEncoding (206 bytes)
+197 sun.security.util.ObjectIdentifier::getComponent (73 bytes)
+198 sun.security.util.ObjectIdentifier::equals (69 bytes)
+199 sun.security.util.ObjectIdentifier::hashCode (35 bytes)
+200 sun.security.util.DerInputStream::<init> (19 bytes)
+201 ! java.security.cert.Certificate::hashCode (34 bytes)
+202 java.lang.String::toLowerCase (436 bytes)
+203 java.lang.Character::toLowerCase (6 bytes)
+204 java.util.HashMap::addEntry (58 bytes)
+205 s java.io.ByteArrayOutputStream::write (55 bytes)
+206 java.io.BufferedInputStream::getBufIfOpen (21 bytes)
+207 s java.io.BufferedInputStream::read (49 bytes)
+208 java.io.DataInputStream::readChar (40 bytes)
+ 4% sun.text.normalizer.NormalizerDataReader::read @ 12 (86 bytes)
+208 made not entrant (2) java.io.DataInputStream::readChar (40 bytes)
+209 java.io.DataInputStream::readChar (40 bytes)
+210 java.math.BigInteger::mulAdd (81 bytes)
+ 5% com.sun.crypto.provider.AESCrypt::<clinit> @ 724 (1577 bytes)
+ 50 made not entrant (2) java.lang.CharacterData::of (120 bytes)
+ 64 made not entrant (2) java.lang.Character::isWhitespace (5 bytes)
+ 65 made not entrant (2) java.lang.Character::isWhitespace (9 bytes)
+ 67 made not entrant (2) sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes)
+116 made not entrant (2) java.lang.Character::toLowerCase (9 bytes)
+160 made not entrant (2) java.lang.Character::toUpperCaseEx (30 bytes)
+203 made not entrant (2) java.lang.Character::toLowerCase (6 bytes)
+159 made not entrant (2) java.lang.String::toUpperCase (442 bytes)
+202 made not entrant (2) java.lang.String::toLowerCase (436 bytes)
+211 java.lang.CharacterData::of (120 bytes)
+212 java.lang.String::toLowerCase (436 bytes)
+213 java.lang.String::toUpperCase (442 bytes)
+201 made not entrant (2) java.security.cert.Certificate::hashCode (34 bytes)
+198 made not entrant (2) sun.security.util.ObjectIdentifier::equals (69 bytes)
+214 java.lang.Character::toUpperCaseEx (30 bytes)
+146 made zombie (2) java.util.regex.Pattern$CharProperty::match (56 bytes)
+133 made zombie (2) java.util.regex.Matcher::search (109 bytes)
+179 made zombie (2) com.google.gson.stream.JsonWriter::string (365 bytes)
+215 java.lang.Character::toLowerCase (9 bytes)
+216 java.lang.Character::isWhitespace (5 bytes)
+217 java.lang.Character::isWhitespace (9 bytes)
+218 sun.text.normalizer.NormalizerImpl::decompose (680 bytes)
+219 ! sun.security.x509.AVA::toRFC2253CanonicalString (484 bytes)
+220 java.lang.String::regionMatches (157 bytes)
+221 sun.security.provider.SHA::implCompress (491 bytes)
+--- n sun.misc.Unsafe::getInt
+ 49 made zombie (2) java.io.ByteArrayOutputStream::write (55 bytes)
+ 6% com.sun.crypto.provider.ARCFOURCipher::crypt @ 15 (129 bytes)
+208 made zombie (2) java.io.DataInputStream::readChar (40 bytes)
+222 com.sun.crypto.provider.ARCFOURCipher::crypt (129 bytes)
+223 java.lang.Integer::reverseBytes (26 bytes)
+ 45 made not entrant (2) java.util.HashMap::get (79 bytes)
+ 50 made zombie (2) java.lang.CharacterData::of (120 bytes)
+224 java.util.HashMap$EntryIterator::next (5 bytes)
+225 java.util.HashMap$EntryIterator::next (5 bytes)
+ 64 made zombie (2) java.lang.Character::isWhitespace (5 bytes)
+ 65 made zombie (2) java.lang.Character::isWhitespace (9 bytes)
+ 67 made zombie (2) sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes)
+116 made zombie (2) java.lang.Character::toLowerCase (9 bytes)
+226 java.util.HashMap$Entry::hashCode (38 bytes)
+227 java.util.AbstractMap::hashCode (41 bytes)
+160 made zombie (2) java.lang.Character::toUpperCaseEx (30 bytes)
+198 made zombie (2) sun.security.util.ObjectIdentifier::equals (69 bytes)
+203 made zombie (2) java.lang.Character::toLowerCase (6 bytes)
+201 made zombie (2) java.security.cert.Certificate::hashCode (34 bytes)
+159 made zombie (2) java.lang.String::toUpperCase (442 bytes)
+202 made zombie (2) java.lang.String::toLowerCase (436 bytes) \ No newline at end of file
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt
new file mode 100644
index 0000000..8a94e73
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt
@@ -0,0 +1,770 @@
+ bool AHRByDeathCollectTimeRatio = false {product}
+ bool AHRByMinorPauseTimeMajorFreq = false {product}
+ bool AHRByPromoToAllocRatio = false {product}
+ bool AHRBySurvivorAge = false {product}
+uintx AHRIncrementSize = 5242880 {product}
+uintx AHRMaxDeathCollectTimeRatio = 55 {product}
+uintx AHRMaxMinorInvocationsPerMajor = 30 {product}
+uintx AHRMaxMinorPauseTimeMillis = 100 {product}
+uintx AHRMaxPromoToAllocRatio = 25 {product}
+uintx AHRMaxRatio = 100 {product}
+uintx AHRMaxSize = 104857600 {product}
+uintx AHRMaxSurvivorAge = 10 {product}
+uintx AHRMinDeathCollectTimeRatio = 50 {product}
+uintx AHRMinMinorInvocationsPerMajor = 5 {product}
+uintx AHRMinMinorPauseTimeMillis = 150 {product}
+uintx AHRMinPromoToAllocRatio = 2 {product}
+uintx AHRMinSurvivorAge = 2 {product}
+ bool AdaptiveHeapRebalance = false {product}
+uintx AdaptivePermSizeWeight = 20 {product}
+uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
+uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
+uintx AdaptiveSizePausePolicy = 0 {product}
+uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product}
+uintx AdaptiveSizePolicyInitializingSteps = 20 {product}
+uintx AdaptiveSizePolicyOutputInterval = 0 {product}
+uintx AdaptiveSizePolicyWeight = 10 {product}
+uintx AdaptiveSizeThroughPutPolicy = 0 {product}
+uintx AdaptiveTimeWeight = 25 {product}
+ bool AdjustConcurrency = false {product}
+ bool AggressiveOpts = false {product}
+ intx AliasLevel = 3 {product}
+ intx AllocatePrefetchDistance = 192 {product}
+ intx AllocatePrefetchInstr = 0 {product}
+ intx AllocatePrefetchLines = 4 {product}
+ intx AllocatePrefetchStepSize = 64 {product}
+ intx AllocatePrefetchStyle = 1 {product}
+ bool AllowJNIEnvProxy = false {product}
+ bool AllowParallelDefineClass = false {product}
+ bool AllowUserSignalHandlers = false {product}
+ bool AlwaysActAsServerClassMachine = false {product}
+ bool AlwaysCompileLoopMethods = false {product}
+ intx AlwaysInflate = 0 {product}
+ bool AlwaysLockClassLoader = false {product}
+ bool AlwaysPreTouch = false {product}
+ bool AlwaysRestoreFPU = false {product}
+ bool AlwaysTenure = false {product}
+ bool AnonymousClasses = false {product}
+ bool AssertOnSuspendWaitFailure = false {product}
+ intx Atomics = 0 {product}
+ intx AutoBoxCacheMax = 128 {C2 product}
+uintx AutoGCSelectPauseMillis = 5000 {product}
+ intx BCEATraceLevel = 0 {product}
+ intx BackEdgeThreshold = 100000 {pd product}
+ bool BackgroundCompilation = true {pd product}
+uintx BaseFootPrintEstimate = 268435456 {product}
+ intx BiasedLockingBulkRebiasThreshold = 20 {product}
+ intx BiasedLockingBulkRevokeThreshold = 40 {product}
+ intx BiasedLockingDecayTime = 25000 {product}
+ intx BiasedLockingStartupDelay = 4000 {product}
+ bool BindCMSThreadToCPU = false {diagnostic}
+ bool BindGCTaskThreadsToCPUs = false {product}
+ bool BlockLayoutByFrequency = true {C2 product}
+ intx BlockLayoutMinDiamondPercentage = 20 {C2 product}
+ bool BlockLayoutRotateLoops = true {C2 product}
+ bool BlockOffsetArrayUseUnallocatedBlock = true {product}
+ bool BranchOnRegister = false {C2 product}
+ bool BytecodeVerificationLocal = false {product}
+ bool BytecodeVerificationRemote = true {product}
+ intx CICompilerCount = 2 {product}
+ bool CICompilerCountPerCPU = false {product}
+ bool CITime = false {product}
+ bool CMSAbortSemantics = false {product}
+uintx CMSAbortablePrecleanMinWorkPerIteration = 100 {product}
+ intx CMSAbortablePrecleanWaitMillis = 100 {product}
+uintx CMSBitMapYieldQuantum = 10485760 {product}
+uintx CMSBootstrapOccupancy = 50 {product}
+ bool CMSClassUnloadingEnabled = false {product}
+uintx CMSClassUnloadingMaxInterval = 0 {product}
+ bool CMSCleanOnEnter = true {product}
+ bool CMSCompactWhenClearAllSoftRefs = true {product}
+uintx CMSConcMarkMultiple = 32 {product}
+ bool CMSConcurrentMTEnabled = true {product}
+uintx CMSCoordinatorYieldSleepCount = 10 {product}
+ bool CMSDumpAtPromotionFailure = false {product}
+uintx CMSExpAvgFactor = 50 {product}
+ bool CMSExtrapolateSweep = false {product}
+uintx CMSFullGCsBeforeCompaction = 0 {product}
+uintx CMSIncrementalDutyCycle = 10 {product}
+uintx CMSIncrementalDutyCycleMin = 0 {product}
+ bool CMSIncrementalMode = false {product}
+uintx CMSIncrementalOffset = 0 {product}
+ bool CMSIncrementalPacing = true {product}
+uintx CMSIncrementalSafetyFactor = 10 {product}
+uintx CMSIndexedFreeListReplenish = 4 {product}
+ intx CMSInitiatingOccupancyFraction = -1 {product}
+ intx CMSInitiatingOccupancyFractionDelta = -1 {product}
+ intx CMSInitiatingPermOccupancyFraction = -1 {product}
+ intx CMSInitiatingPermOccupancyFractionDelta = -1 {product}
+ intx CMSInitiatingRamLimitFraction = -1 {product}
+ intx CMSIsTooFullPercentage = 98 {product}
+double CMSLargeCoalSurplusPercent = {product}
+double CMSLargeSplitSurplusPercent = {product}
+ bool CMSLoopWarn = false {product}
+uintx CMSMaxAbortablePrecleanLoops = 0 {product}
+ intx CMSMaxAbortablePrecleanTime = 5000 {product}
+uintx CMSOldPLABMax = 1024 {product}
+uintx CMSOldPLABMin = 16 {product}
+uintx CMSOldPLABNumRefills = 4 {product}
+uintx CMSOldPLABReactivityCeiling = 10 {product}
+uintx CMSOldPLABReactivityFactor = 2 {product}
+ bool CMSOldPLABResizeQuicker = false {product}
+uintx CMSOldPLABToleranceFactor = 4 {product}
+ bool CMSPLABRecordAlways = true {product}
+uintx CMSParPromoteBlocksToClaim = 16 {product}
+ bool CMSParallelRemarkEnabled = true {product}
+ bool CMSParallelSurvivorRemarkEnabled = true {product}
+ bool CMSPermGenPrecleaningEnabled = true {product}
+uintx CMSPrecleanDenominator = 3 {product}
+uintx CMSPrecleanIter = 3 {product}
+uintx CMSPrecleanNumerator = 2 {product}
+ bool CMSPrecleanRefLists1 = true {product}
+ bool CMSPrecleanRefLists2 = false {product}
+ bool CMSPrecleanSurvivors1 = false {product}
+ bool CMSPrecleanSurvivors2 = true {product}
+uintx CMSPrecleanThreshold = 1000 {product}
+ bool CMSPrecleaningEnabled = true {product}
+ bool CMSPrintChunksInDump = false {product}
+ bool CMSPrintObjectsInDump = false {product}
+uintx CMSRemarkVerifyVariant = 1 {product}
+ bool CMSReplenishIntermediate = true {product}
+uintx CMSRescanMultiple = 32 {product}
+uintx CMSRevisitStackSize = 1048576 {product}
+uintx CMSSamplingGrain = 16384 {product}
+ bool CMSScavengeBeforeRemark = false {product}
+uintx CMSScheduleRemarkEdenPenetration = 50 {product}
+uintx CMSScheduleRemarkEdenSizeThreshold = 2097152 {product}
+uintx CMSScheduleRemarkSamplingRatio = 5 {product}
+double CMSSmallCoalSurplusPercent = {product}
+double CMSSmallSplitSurplusPercent = {product}
+ bool CMSSplitIndexedFreeListBlocks = true {product}
+ intx CMSTriggerPermRatio = 80 {product}
+ intx CMSTriggerRatio = 80 {product}
+ bool CMSUseGCOverheadLimit = true {product}
+ bool CMSUseOldDefaults = false {product}
+ intx CMSWaitDuration = 2000 {product}
+uintx CMSWorkQueueDrainThreshold = 10 {product}
+ bool CMSYield = true {product}
+uintx CMSYieldSleepCount = 0 {product}
+ intx CMSYoungGenPerWorker = 16777216 {product}
+uintx CMS_FLSPadding = 1 {product}
+uintx CMS_FLSWeight = 75 {product}
+uintx CMS_SweepPadding = 1 {product}
+uintx CMS_SweepTimerThresholdMillis = 10 {product}
+uintx CMS_SweepWeight = 75 {product}
+uintx CPUForCMSThread = 0 {diagnostic}
+ bool CheckJNICalls = false {product}
+ bool ClassUnloading = true {product}
+ intx ClearFPUAtPark = 0 {product}
+ bool ClipInlining = true {product}
+uintx CodeCacheExpansionSize = 32768 {pd product}
+uintx CodeCacheFlushingMinimumFreeSpace = 1536000 {product}
+uintx CodeCacheMinimumFreeSpace = 512000 {product}
+ bool CollectGen0First = false {product}
+ bool CompactFields = true {product}
+ intx CompilationPolicyChoice = 0 {product}
+ intx CompilationRepeat = 0 {C1 product}
+ccstrlist CompileCommand = {product}
+ccstr CompileCommandFile = {product}
+ccstrlist CompileOnly = {product}
+ intx CompileThreshold = 10000 {pd product}
+ bool CompilerThreadHintNoPreempt = true {product}
+ intx CompilerThreadPriority = -1 {product}
+ intx CompilerThreadStackSize = 0 {pd product}
+uintx ConcGCThreads = 0 {product}
+ intx ConditionalMoveLimit = 3 {C2 pd product}
+ bool ConvertSleepToYield = true {pd product}
+ bool ConvertYieldToSleep = false {product}
+ bool CycleTime = false {product}
+ bool DTraceAllocProbes = false {product}
+ bool DTraceMethodProbes = false {product}
+ bool DTraceMonitorProbes = false {product}
+ bool DeallocateHeapPages = true {product}
+ bool DeallocateStackPages = true {product}
+uintx DeallocateStackPagesMinIntervalMs = 100 {product}
+ bool DebugContinuation = false {diagnostic}
+ bool DebugInlinedCalls = true {diagnostic}
+ bool DebugNonSafepoints = false {diagnostic}
+uintx DefaultMaxRAMFraction = 4 {product}
+ intx DefaultThreadPriority = -1 {product}
+ bool DeferInitialCardMark = false {diagnostic}
+ intx DeferPollingPageLoopCount = -1 {product}
+ intx DeferThrSuspendLoopCount = 4000 {product}
+ bool DeoptimizeRandom = false {product}
+ bool DisableAttachMechanism = false {product}
+ bool DisableExplicitGC = false {product}
+ccstrlist DisableIntrinsic = {diagnostic}
+ bool DisplayVMOutput = true {diagnostic}
+ bool DisplayVMOutputToStderr = false {product}
+ bool DisplayVMOutputToStdout = false {product}
+ bool DoEscapeAnalysis = true {C2 product}
+ intx DominatorSearchLimit = 1000 {C2 diagnostic}
+ bool DontCompileHugeMethods = true {product}
+ bool DontYieldALot = false {pd product}
+ bool DumpSharedSpaces = false {product}
+ bool EagerXrunInit = false {product}
+ intx EliminateAllocationArraySizeLimit = 64 {C2 product}
+ bool EliminateAllocations = true {C2 product}
+ bool EliminateAutoBox = false {C2 diagnostic}
+ bool EliminateLocks = true {C2 product}
+ intx EmitSync = 0 {product}
+uintx ErgoHeapSizeLimit = 0 {product}
+ccstr ErrorFile = {product}
+ bool EstimateArgEscape = true {product}
+ intx EventLogLength = 2000 {product}
+ bool ExplicitGCInvokesConcurrent = false {product}
+ bool ExplicitGCInvokesConcurrentAndUnloadsClasses = false {product}
+ bool ExtendedDTraceProbes = false {product}
+ bool FLSAlwaysCoalesceLarge = false {product}
+uintx FLSCoalescePolicy = 2 {product}
+double FLSLargestBlockCoalesceProximity = {product}
+ bool FLSVerifyAllHeapReferences = false {diagnostic}
+ bool FLSVerifyIndexTable = false {diagnostic}
+ bool FLSVerifyLists = false {diagnostic}
+ bool FailOverToOldVerifier = true {product}
+ bool FastCardTableScan = false {product}
+ bool FastTLABRefill = true {product}
+ intx FenceInstruction = 0 {product}
+ intx FieldsAllocationStyle = 1 {product}
+ bool FilterSpuriousWakeups = true {product}
+ bool ForceFullGCJVMTIEpilogues = false {product}
+ bool ForceNUMA = false {product}
+ bool ForceSharedSpaces = false {product}
+ bool ForceTimeHighResolution = false {product}
+ intx FreqInlineSize = 325 {pd product}
+ bool FullProfileOnReInterpret = true {diagnostic}
+ intx G1ConcRefinementGreenZone = 0 {product}
+ intx G1ConcRefinementRedZone = 0 {product}
+ intx G1ConcRefinementServiceIntervalMillis = 300 {product}
+uintx G1ConcRefinementThreads = 0 {product}
+ intx G1ConcRefinementThresholdStep = 0 {product}
+ intx G1ConcRefinementYellowZone = 0 {product}
+ intx G1ConfidencePercent = 50 {product}
+uintx G1HeapRegionSize = 0 {product}
+ intx G1MarkRegionStackSize = 1048576 {product}
+ bool G1PrintHeapRegions = false {diagnostic}
+ intx G1RSetRegionEntries = 0 {product}
+uintx G1RSetScanBlockSize = 64 {product}
+ intx G1RSetSparseRegionEntries = 0 {product}
+ intx G1RSetUpdatingPauseTimePercent = 10 {product}
+ intx G1ReservePercent = 10 {product}
+ intx G1SATBBufferSize = 1024 {product}
+ bool G1SummarizeConcMark = false {diagnostic}
+ bool G1SummarizeRSetStats = false {diagnostic}
+ intx G1SummarizeRSetStatsPeriod = 0 {diagnostic}
+ bool G1SummarizeZFStats = false {diagnostic}
+ bool G1TraceConcRefinement = false {diagnostic}
+ intx G1UpdateBufferSize = 256 {product}
+ bool G1UseAdaptiveConcRefinement = true {product}
+uintx GCDrainStackTargetSize = 64 {product}
+uintx GCHeapFreeLimit = 2 {product}
+ bool GCLockerInvokesConcurrent = false {product}
+ bool GCOverheadReporting = false {product}
+ intx GCOverheadReportingPeriodMS = 100 {product}
+ bool GCParallelVerificationEnabled = true {diagnostic}
+uintx GCPauseIntervalMillis = 0 {product}
+uintx GCTaskTimeStampEntries = 200 {product}
+uintx GCTimeLimit = 98 {product}
+uintx GCTimeRatio = 99 {product}
+ bool GoogleAgent = true {product}
+ccstr GoogleAgentFlags = {product}
+ bool GoogleAgentWS = false {product}
+ccstr GoogleAgentWSFlags = {product}
+uintx GoogleGCHeapFreeLimitPolicy = 2 {product}
+ bool GoogleHeapInstrumentation = false {product}
+ bool GoogleHeapMonitor = true {product}
+ bool GoogleInheritAltSigStack = false {product}
+uintx GoogleLogRotationSize = 0 {diagnostic}
+uintx GoogleMaxGarbageHeapzTraces = 200 {product}
+ bool GoogleScoping = false {product}
+uintx GoogleSoftRefLRUPolicy = 0 {product}
+uintx GoogleSoftRefLRUPolicyExcludedMB = 0 {product}
+uintx GoogleUseConstantOMProvision = 0 {product}
+ bool GoogleUseLibunwind = false {pd product}
+ bool GoogleUseSSEForVM = false {product}
+ccstr HPILibPath = {product}
+ bool HandlePromotionFailure = true {product}
+uintx HeapBaseMinAddress = 2147483648 {pd product}
+ bool HeapDumpAfterFullGC = false {manageable}
+ bool HeapDumpBeforeFullGC = false {manageable}
+ bool HeapDumpOnOutOfMemoryError = false {manageable}
+ccstr HeapDumpPath = {manageable}
+uintx HeapFirstMaximumCompactionCount = 3 {product}
+uintx HeapMaximumCompactionInterval = 20 {product}
+ bool HoistFinalLoads = false {product}
+ bool IgnoreUnrecognizedVMOptions = false {product}
+uintx InitialCodeCacheSize = 2359296 {pd product}
+ bool InitialCompileFast = false {diagnostic}
+ bool InitialCompileReallyFast = false {diagnostic}
+uintx InitialHeapSize := 67108864 {product}
+uintx InitialRAMFraction = 64 {product}
+uintx InitialSurvivorRatio = 8 {product}
+ intx InitialTenuringThreshold = 7 {product}
+uintx InitiatingHeapOccupancyPercent = 45 {product}
+ bool Inline = true {product}
+ bool InlineMethodsWithNullUnloadedTypesInSignature = true {product}
+ intx InlineSmallCode = 1000 {pd product}
+ bool InsertMemBarAfterArraycopy = true {C2 product}
+ intx InteriorEntryAlignment = 4 {C2 pd product}
+ intx InterpreterProfilePercentage = 33 {product}
+ bool JNIDetachReleasesMonitors = true {product}
+ bool JavaMonitorsInStackTrace = true {product}
+ intx JavaPriority10_To_OSPriority = -1 {product}
+ intx JavaPriority1_To_OSPriority = -1 {product}
+ intx JavaPriority2_To_OSPriority = -1 {product}
+ intx JavaPriority3_To_OSPriority = -1 {product}
+ intx JavaPriority4_To_OSPriority = -1 {product}
+ intx JavaPriority5_To_OSPriority = -1 {product}
+ intx JavaPriority6_To_OSPriority = -1 {product}
+ intx JavaPriority7_To_OSPriority = -1 {product}
+ intx JavaPriority8_To_OSPriority = -1 {product}
+ intx JavaPriority9_To_OSPriority = -1 {product}
+ bool LIRFillDelaySlots = false {C1 pd product}
+uintx LargePageHeapSizeThreshold = 134217728 {product}
+ccstr LargePageMountPoint = {product}
+uintx LargePageSizeInBytes = 0 {product}
+ bool LazyBootClassLoader = true {product}
+ bool LinkWellKnownClasses = false {diagnostic}
+ bool LogAHR = false {product}
+ bool LogCompilation = false {diagnostic}
+ccstr LogFile = {diagnostic}
+ bool LogGCOverheadLimit = false {product}
+ bool LogVMOutput = false {diagnostic}
+ intx LoopOptsCount = 43 {C2 product}
+ intx LoopUnrollLimit = 50 {C2 pd product}
+ intx LoopUnrollMin = 4 {C2 product}
+ bool LoopUnswitching = true {C2 product}
+ intx MallocVerifyInterval = 0 {diagnostic}
+ intx MallocVerifyStart = 0 {diagnostic}
+ bool ManagementServer = false {product}
+uintx MarkStackSize = 32768 {product}
+uintx MarkStackSizeMax = 4194304 {product}
+ intx MarkSweepAlwaysCompactCount = 4 {product}
+uintx MarkSweepDeadRatio = 5 {product}
+ intx MaxBCEAEstimateLevel = 5 {product}
+ intx MaxBCEAEstimateSize = 150 {product}
+ intx MaxDirectMemorySize = -1 {product}
+ bool MaxFDLimit = true {product}
+uintx MaxGCMinorPauseMillis = 4294967295 {product}
+uintx MaxGCPauseMillis = 4294967295 {product}
+uintx MaxHeapFreeRatio = 70 {product}
+uintx MaxHeapSize := 1073741824 {product}
+ intx MaxInlineLevel = 9 {product}
+ intx MaxInlineSize = 35 {product}
+ intx MaxJavaStackTraceDepth = 1024 {product}
+ intx MaxJumpTableSize = 65000 {C2 product}
+ intx MaxJumpTableSparseness = 5 {C2 product}
+ intx MaxLabelRootDepth = 1100 {C2 product}
+uintx MaxLiveObjectEvacuationRatio = 100 {product}
+ intx MaxLoopPad = 11 {C2 product}
+uintx MaxNewSize = 4294901760 {product}
+ intx MaxNodeLimit = 65000 {C2 product}
+uintx MaxPermHeapExpansion = 4194304 {product}
+uintx MaxPermSize = 67108864 {pd product}
+uint64_t MaxRAM = 0 {pd product}
+uintx MaxRAMFraction = 4 {product}
+ intx MaxRecursiveInlineLevel = 1 {product}
+ intx MaxTenuringThreshold = 15 {product}
+ intx MaxTrivialSize = 6 {product}
+ bool MethodFlushing = true {product}
+ intx MethodHandlePushLimit = 3 {diagnostic}
+ intx MinCodeCacheFlushingInterval = 30 {product}
+uintx MinHeapDeltaBytes = 131072 {product}
+uintx MinHeapFreeRatio = 40 {product}
+ intx MinInliningThreshold = 250 {product}
+ intx MinJumpTableSize = 18 {C2 product}
+uintx MinPermHeapExpansion = 262144 {product}
+uintx MinRAMFraction = 2 {product}
+uintx MinSurvivorRatio = 3 {product}
+uintx MinTLABSize = 2048 {product}
+ bool MixedModeThreadDump = true {product}
+ intx MonitorBound = 0 {product}
+ bool MonitorInUseLists = false {product}
+ intx MultiArrayExpandLimit = 6 {C2 product}
+ bool MustCallLoadClassInternal = false {product}
+ intx NUMAChunkResizeWeight = 20 {product}
+ intx NUMAPageScanRate = 256 {product}
+ intx NUMASpaceResizeRate = 1073741824 {product}
+ bool NUMAStats = false {product}
+ intx NativeMonitorFlags = 0 {product}
+ intx NativeMonitorSpinLimit = 20 {product}
+ intx NativeMonitorTimeout = -1 {product}
+ bool NeedsDeoptSuspend = false {pd product}
+ bool NeverActAsServerClassMachine = false {pd product}
+ bool NeverTenure = false {product}
+ intx NewRatio = 2 {product}
+uintx NewSize = 1048576 {product}
+uintx NewSizeThreadIncrease = 4096 {pd product}
+ intx NmethodSweepCheckInterval = 5 {product}
+ intx NmethodSweepFraction = 4 {product}
+ intx NodeLimitFudgeFactor = 1000 {C2 product}
+ intx NumberOfLoopInstrToAlign = 4 {C2 product}
+ bool ObjLifeTracking = false {product}
+uintx ObjLifeTrackingCpuFraction = 100 {product}
+uintx ObjLifetimeHistoBucketCount = 1024 {product}
+ bool ObjLifetimeHistoExportVarPerBucket = false {product}
+uintx OldPLABSize = 1024 {product}
+uintx OldPLABWeight = 50 {product}
+uintx OldSize = 4194304 {product}
+ bool OmitStackTraceInFastThrow = true {product}
+ccstrlist OnError = {product}
+ccstrlist OnOutOfMemoryError = {product}
+ intx OnStackReplacePercentage = 140 {pd product}
+ bool OptimizeFill = false {C2 product}
+ bool OptimizeMethodHandles = true {diagnostic}
+ bool OptimizeStringConcat = false {C2 product}
+ bool OptoBundling = false {C2 pd product}
+ intx OptoLoopAlignment = 16 {pd product}
+ bool OptoScheduling = false {C2 pd product}
+uintx PLABWeight = 75 {product}
+ bool PSChunkLargeArrays = true {product}
+ bool PSResizeByFreeRatio = false {product}
+ bool PSResizeByFreeRatioWithSystemGC = false {product}
+ intx ParGCArrayScanChunk = 50 {product}
+ intx ParGCCardsPerStrideChunk = 256 {diagnostic}
+uintx ParGCDesiredObjsFromOverflowList = 20 {product}
+ intx ParGCStridesPerThread = 2 {diagnostic}
+ bool ParGCTrimOverflow = true {product}
+ bool ParGCUseLocalOverflow = false {product}
+ intx ParallelGCBufferWastePct = 10 {product}
+ bool ParallelGCRetainPLAB = true {product}
+uintx ParallelGCThreads := 10 {product}
+ bool ParallelGCVerbose = false {product}
+uintx ParallelOldDeadWoodLimiterMean = 50 {product}
+uintx ParallelOldDeadWoodLimiterStdDev = 80 {product}
+ bool ParallelRefProcBalancingEnabled = true {product}
+ bool ParallelRefProcEnabled = false {product}
+ bool PartialPeelAtUnsignedTests = true {C2 product}
+ bool PartialPeelLoop = true {C2 product}
+ intx PartialPeelNewPhiDelta = 0 {C2 product}
+ bool PauseAtStartup = false {diagnostic}
+ccstr PauseAtStartupFile = {diagnostic}
+uintx PausePadding = 1 {product}
+ intx PerBytecodeRecompilationCutoff = 200 {product}
+ intx PerBytecodeTrapLimit = 4 {product}
+ intx PerMethodRecompilationCutoff = 400 {product}
+ intx PerMethodTrapLimit = 100 {product}
+ bool PerfAllowAtExitRegistration = false {product}
+ bool PerfBypassFileSystemCheck = false {product}
+ intx PerfDataMemorySize = 32768 {product}
+ intx PerfDataSamplingInterval = 50 {product}
+ccstr PerfDataSaveFile = {product}
+ bool PerfDataSaveToFile = false {product}
+ bool PerfDisableSharedMem = false {product}
+ intx PerfMaxStringConstLength = 1024 {product}
+uintx PermGenPadding = 3 {product}
+uintx PermMarkSweepDeadRatio = 20 {product}
+uintx PermSize = 16777216 {pd product}
+ bool PostSpinYield = true {product}
+ intx PreBlockSpin = 10 {product}
+ intx PreInflateSpin = 10 {pd product}
+ bool PreSpinYield = false {product}
+ bool PreferInterpreterNativeStubs = false {pd product}
+ intx PrefetchCopyIntervalInBytes = -1 {product}
+ intx PrefetchFieldsAhead = -1 {product}
+ intx PrefetchScanIntervalInBytes = -1 {product}
+ bool PreserveAllAnnotations = false {product}
+uintx PreserveMarkStackSize = 1024 {product}
+uintx PretenureSizeThreshold = 0 {product}
+ bool PrintAdapterHandlers = false {diagnostic}
+ bool PrintAdaptiveSizePolicy = false {product}
+ bool PrintAssembly = false {diagnostic}
+ccstr PrintAssemblyOptions = {diagnostic}
+ bool PrintBiasedLockingStatistics = false {diagnostic}
+ bool PrintCMSInitiationCause = false {product}
+ bool PrintCMSInitiationStatistics = false {product}
+ intx PrintCMSStatistics = 0 {product}
+ bool PrintCardTableStats = false {product}
+ bool PrintClassHistogram = false {manageable}
+ bool PrintClassHistogramAfterFullGC = false {manageable}
+ bool PrintClassHistogramBeforeFullGC = false {manageable}
+ bool PrintCommandLineFlags = false {product}
+ bool PrintCompilation = false {product}
+ bool PrintCompressedOopsMode = false {diagnostic}
+ bool PrintConcurrentLocks = false {manageable}
+ bool PrintDTraceDOF = false {diagnostic}
+ intx PrintFLSCensus = 0 {product}
+ intx PrintFLSStatistics = 0 {product}
+ bool PrintFlagsFinal := true {product}
+ bool PrintFlagsInitial = false {product}
+ bool PrintGC = false {manageable}
+ bool PrintGCApplicationConcurrentTime = false {product}
+ bool PrintGCApplicationStoppedTime = false {product}
+ bool PrintGCDateStamps = false {manageable}
+ bool PrintGCDetails = false {manageable}
+ bool PrintGCTaskTimeStamps = false {product}
+ bool PrintGCTimeStamps = false {manageable}
+ bool PrintHeapAtGC = false {product rw}
+ bool PrintHeapAtGCExtended = false {product rw}
+ bool PrintHeapAtSIGBREAK = true {product}
+ bool PrintInlining = false {diagnostic}
+ bool PrintInterpreter = false {diagnostic}
+ bool PrintIntrinsics = false {diagnostic}
+ bool PrintJNIGCStalls = false {product}
+ bool PrintJNIResolving = false {product}
+ bool PrintNMethods = false {diagnostic}
+ bool PrintNativeNMethods = false {diagnostic}
+ bool PrintOldPLAB = false {product}
+ bool PrintOopAddress = false {product}
+ bool PrintPLAB = false {product}
+ bool PrintParallelOldGCPhaseTimes = false {product}
+ bool PrintPreciseBiasedLockingStatistics = false {C2 diagnostic}
+ bool PrintPromotionFailure = false {product}
+ bool PrintReferenceGC = false {product}
+ bool PrintRevisitStats = false {product}
+ bool PrintSafepointStatistics = false {product}
+ intx PrintSafepointStatisticsCount = 300 {product}
+ intx PrintSafepointStatisticsTimeout = -1 {product}
+ bool PrintSharedSpaces = false {product}
+ bool PrintSignatureHandlers = false {diagnostic}
+ bool PrintStubCode = false {diagnostic}
+ bool PrintTLAB = false {product}
+ bool PrintTenuringDistribution = false {product}
+ bool PrintVMOptions = false {product}
+ bool PrintVMQWaitTime = false {product}
+uintx ProcessDistributionStride = 4 {product}
+ bool ProfileInterpreter = true {pd product}
+ bool ProfileIntervals = false {product}
+ intx ProfileIntervalsTicks = 100 {product}
+ intx ProfileMaturityPercentage = 20 {product}
+ bool ProfileVM = false {product}
+ bool ProfilerPrintByteCodeStatistics = false {product}
+ bool ProfilerRecordPC = false {product}
+uintx PromotedPadding = 3 {product}
+ intx QueuedAllocationWarningCount = 0 {product}
+uintx RamLimit = 0 {product}
+ bool RangeCheckElimination = true {product}
+ intx ReadPrefetchInstr = 0 {product}
+ intx ReadSpinIterations = 100 {product}
+ bool ReassociateInvariants = true {C2 product}
+ bool ReduceBulkZeroing = true {C2 product}
+ bool ReduceFieldZeroing = true {C2 product}
+ bool ReduceInitialCardMarks = true {C2 product}
+ bool ReduceSignalUsage = false {product}
+ intx RefDiscoveryPolicy = 0 {product}
+ bool ReflectionWrapResolutionErrors = true {product}
+ bool RegisterFinalizersAtInit = true {product}
+ bool RelaxAccessControlCheck = false {product}
+ bool RequireSharedSpaces = false {product}
+uintx ReservedCodeCacheSize = 50331648 {pd product}
+ bool ResizeOldPLAB = true {product}
+ bool ResizePLAB = true {product}
+ bool ResizeTLAB = true {pd product}
+ bool RestoreMXCSROnJNICalls = false {product}
+ bool RewriteBytecodes = true {pd product}
+ bool RewriteFrequentPairs = true {pd product}
+ intx SafepointPollOffset = 256 {C1 pd product}
+ intx SafepointSpinBeforeYield = 2000 {product}
+ bool SafepointTimeout = false {product}
+ intx SafepointTimeoutDelay = 10000 {product}
+ bool ScavengeBeforeFullGC = true {product}
+ intx ScavengeRootsInCode = 0 {diagnostic}
+ intx SelfDestructTimer = 0 {product}
+ bool SerializeVMOutput = true {diagnostic}
+uintx SharedDummyBlockSize = 536870912 {product}
+uintx SharedMiscCodeSize = 4194304 {product}
+uintx SharedMiscDataSize = 4194304 {product}
+ bool SharedOptimizeColdStart = true {diagnostic}
+uintx SharedReadOnlySize = 10485760 {product}
+uintx SharedReadWriteSize = 12582912 {product}
+ bool SharedSkipVerify = false {diagnostic}
+ bool ShowMessageBoxOnError = false {product}
+ intx SoftRefLRUPolicyMSPerMB = 1000 {product}
+ bool SplitIfBlocks = true {product}
+ intx StackRedPages = 1 {pd product}
+ intx StackShadowPages = 3 {pd product}
+ bool StackTraceInThrowable = true {product}
+ intx StackYellowPages = 2 {pd product}
+ bool StartAttachListener = false {product}
+ bool StartSuspended = false {product}
+ intx StarvationMonitorInterval = 200 {product}
+ bool StressLdcRewrite = false {product}
+ bool StressTieredRuntime = false {product}
+ bool SuppressFatalErrorMessage = false {product}
+uintx SurvivorPadding = 3 {product}
+ intx SurvivorRatio = 8 {product}
+ intx SuspendRetryCount = 50 {product}
+ intx SuspendRetryDelay = 5 {product}
+ intx SyncFlags = 0 {product}
+ccstr SyncKnobs = {product}
+ intx SyncVerbose = 0 {product}
+uintx TLABAllocationWeight = 35 {product}
+uintx TLABRefillWasteFraction = 64 {product}
+uintx TLABSize = 0 {product}
+ bool TLABStats = true {product}
+uintx TLABWasteIncrement = 4 {product}
+uintx TLABWasteTargetPercent = 1 {product}
+ intx TargetPLABWastePct = 10 {product}
+ intx TargetSurvivorRatio = 50 {product}
+uintx TenuredGenerationSizeIncrement = 20 {product}
+uintx TenuredGenerationSizeSupplement = 80 {product}
+uintx TenuredGenerationSizeSupplementDecay = 2 {product}
+ intx ThreadPriorityPolicy = 0 {product}
+ bool ThreadPriorityVerbose = false {product}
+uintx ThreadSafetyMargin = 52428800 {product}
+ intx ThreadStackSize = 320 {pd product}
+uintx ThresholdTolerance = 10 {product}
+ intx Tier1BytecodeLimit = 10 {product}
+ intx Tier1FreqInlineSize = 35 {C2 product}
+ intx Tier1Inline = 0 {C2 product}
+ intx Tier1LoopOptsCount = 0 {C2 product}
+ intx Tier1MaxInlineSize = 8 {C2 product}
+ bool Tier1OptimizeVirtualCallProfiling = true {C1 product}
+ bool Tier1ProfileBranches = true {C1 product}
+ bool Tier1ProfileCalls = true {C1 product}
+ bool Tier1ProfileCheckcasts = true {C1 product}
+ bool Tier1ProfileInlinedCalls = true {C1 product}
+ bool Tier1ProfileVirtualCalls = true {C1 product}
+ bool Tier1UpdateMethodData = true {product}
+ intx Tier2BackEdgeThreshold = 100000 {pd product}
+ intx Tier2CompileThreshold = 10000 {pd product}
+ intx Tier3BackEdgeThreshold = 100000 {pd product}
+ intx Tier3CompileThreshold = 20000 {pd product}
+ intx Tier4BackEdgeThreshold = 100000 {pd product}
+ intx Tier4CompileThreshold = 40000 {pd product}
+ bool TieredCompilation = false {pd product}
+ bool TimeLinearScan = false {C1 product}
+ bool TraceBiasedLocking = false {product}
+ bool TraceClassLoading = false {product rw}
+ bool TraceClassLoadingPreorder = false {product}
+ bool TraceClassResolution = false {product}
+ bool TraceClassUnloading = false {product rw}
+ bool TraceCompileTriggered = false {diagnostic}
+ bool TraceGen0Time = false {product}
+ bool TraceGen1Time = false {product}
+ccstr TraceJVMTI = {product}
+ bool TraceJVMTIObjectTagging = false {diagnostic}
+ bool TraceLoaderConstraints = false {product rw}
+ bool TraceMonitorInflation = false {product}
+ bool TraceNMethodInstalls = false {diagnostic}
+ bool TraceOSRBreakpoint = false {diagnostic}
+ bool TraceParallelOldGCTasks = false {product}
+ intx TraceRedefineClasses = 0 {product}
+ bool TraceRedundantCompiles = false {diagnostic}
+ bool TraceSafepointCleanupTime = false {product}
+ bool TraceSuperWord = false {C2 product}
+ bool TraceSuspendWaitFailures = false {product}
+ bool TraceTriggers = false {diagnostic}
+ intx TrackedInitializationLimit = 50 {C2 product}
+ intx TypeProfileMajorReceiverPercent = 90 {product}
+ intx TypeProfileWidth = 2 {product}
+ intx UnguardOnExecutionViolation = 0 {product}
+ bool UnlockDiagnosticVMOptions = true {diagnostic}
+ bool UnsyncloadClass = false {diagnostic}
+ bool Use486InstrsOnly = false {product}
+ bool UseAdaptiveGCBoundary = false {product}
+ bool UseAdaptiveGenerationSizePolicyAtMajorCollection = true {product}
+ bool UseAdaptiveGenerationSizePolicyAtMinorCollection = true {product}
+ bool UseAdaptiveNUMAChunkSizing = true {product}
+ bool UseAdaptiveSizeDecayMajorGCCost = true {product}
+ bool UseAdaptiveSizePolicy = true {product}
+ bool UseAdaptiveSizePolicyFootprintGoal = true {product}
+ bool UseAdaptiveSizePolicyWithSystemGC = false {product}
+ bool UseAddressNop = true {product}
+ bool UseAltSigs = false {product}
+ bool UseAutoGCSelectPolicy = false {product}
+ bool UseBiasedLocking = true {product}
+ bool UseBimorphicInlining = true {C2 product}
+ bool UseBoundThreads = true {product}
+ bool UseCMSBestFit = true {product}
+ bool UseCMSCollectionPassing = true {product}
+ bool UseCMSCompactAtFullCollection = true {product}
+ bool UseCMSInitiatingOccupancyOnly = false {product}
+ bool UseCodeCacheFlushing = false {product}
+ bool UseCompiler = true {product}
+ bool UseCompilerSafepoints = true {product}
+ bool UseConcMarkSweepGC = false {product}
+ bool UseCountLeadingZerosInstruction = false {product}
+ bool UseCounterDecay = true {product}
+ bool UseDivMod = true {C2 product}
+ bool UseFPUForSpilling = false {C2 product}
+ bool UseFastAccessorMethods = true {product}
+ bool UseFastEmptyMethods = true {product}
+ bool UseFastJNIAccessors = true {product}
+ bool UseG1GC = false {product}
+ bool UseGCOverheadLimit = true {product}
+ bool UseGCTaskAffinity = false {product}
+ bool UseHeavyMonitors = false {product}
+ bool UseIncDec = true {diagnostic}
+ bool UseInlineCaches = true {product}
+ bool UseInterpreter = true {product}
+ bool UseJumpTables = true {C2 product}
+ bool UseLWPSynchronization = true {product}
+ bool UseLargePages = false {pd product}
+ bool UseLargePagesIndividualAllocation = false {pd product}
+ bool UseLinuxPosixThreadCPUClocks = false {product}
+ bool UseLoopCounter = true {product}
+ bool UseLoopPredicate = true {C2 product}
+ bool UseMaximumCompactionOnSystemGC = true {product}
+ bool UseMembar = false {product}
+ bool UseNUMA = false {product}
+ bool UseNewCode = false {diagnostic}
+ bool UseNewCode2 = false {diagnostic}
+ bool UseNewCode3 = false {diagnostic}
+ bool UseNewFeature1 = false {C1 product}
+ bool UseNewFeature2 = false {C1 product}
+ bool UseNewFeature3 = false {C1 product}
+ bool UseNewFeature4 = false {C1 product}
+ bool UseNewLongLShift = false {product}
+ bool UseNiagaraInstrs = false {product}
+ bool UseOSErrorReporting = false {pd product}
+ bool UseOldInlining = true {C2 product}
+ bool UseOnStackReplacement = true {pd product}
+ bool UseOnlyInlinedBimorphic = true {C2 product}
+ bool UseOprofile = false {product}
+ bool UseOptoBiasInlining = true {C2 product}
+ bool UsePSAdaptiveSurvivorSizePolicy = true {product}
+ bool UseParNewGC = false {product}
+ bool UseParallelDensePrefixUpdate = true {product}
+ bool UseParallelGC := true {product}
+ bool UseParallelOldGC = false {product}
+ bool UseParallelOldGCCompacting = true {product}
+ bool UseParallelOldGCDensePrefix = true {product}
+ bool UsePerfData = true {product}
+ bool UsePopCountInstruction = true {product}
+ intx UseSSE = 4 {product}
+ bool UseSSE42Intrinsics = true {product}
+ bool UseSeparateVSpacesInYoungGen = true {product}
+ bool UseSerialGC = false {product}
+ bool UseSharedSpaces = false {product}
+ bool UseSignalChaining = true {product}
+ bool UseSpinning = false {product}
+ bool UseSplitVerifier = true {product}
+ bool UseStoreImmI16 = false {product}
+ bool UseStringCache = false {product}
+ bool UseSuperWord = true {C2 product}
+ bool UseTLAB = true {pd product}
+ bool UseThreadPriorities = true {pd product}
+ bool UseTypeProfile = true {product}
+ bool UseUnalignedLoadStores = true {product}
+ bool UseVMInterruptibleIO = true {product}
+ bool UseVectoredExceptions = false {pd product}
+ bool UseXMMForArrayCopy = true {product}
+ bool UseXmmI2D = false {product}
+ bool UseXmmI2F = false {product}
+ bool UseXmmLoadAndClearUpper = true {product}
+ bool UseXmmRegToRegMoveAll = true {product}
+ bool VMThreadHintNoPreempt = false {product}
+ intx VMThreadPriority = -1 {product}
+ intx VMThreadStackSize = 512 {pd product}
+ intx ValueMapInitialSize = 11 {C1 product}
+ intx ValueMapMaxLoopSize = 8 {C1 product}
+ intx ValueSearchLimit = 1000 {C2 product}
+ bool VerifyAfterGC = false {diagnostic}
+ bool VerifyBeforeExit = false {diagnostic}
+ bool VerifyBeforeGC = false {diagnostic}
+ bool VerifyBeforeIteration = false {diagnostic}
+ bool VerifyDuringGC = false {diagnostic}
+ intx VerifyGCLevel = 0 {diagnostic}
+uintx VerifyGCStartAt = 0 {diagnostic}
+ bool VerifyMergedCPBytecodes = true {product}
+ bool VerifyMethodHandles = false {diagnostic}
+ bool VerifyObjectStartArray = true {diagnostic}
+ bool VerifyRememberedSets = false {diagnostic}
+ intx WorkAroundNPTLTimedWaitHang = 1 {product}
+uintx YoungGenerationSizeIncrement = 20 {product}
+uintx YoungGenerationSizeSupplement = 80 {product}
+uintx YoungGenerationSizeSupplementDecay = 8 {product}
+uintx YoungPLABSize = 4096 {product}
+ bool ZeroTLAB = false {product}
+ intx hashCode = 0 {product} \ No newline at end of file
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt
new file mode 100644
index 0000000..fd9d07c
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt
@@ -0,0 +1,200 @@
+[GC 987K->384K(62848K), 0.0012320 secs]
+[Full GC 384K->288K(62848K), 0.0054550 secs]
+[GC 288K->288K(62848K), 0.0004450 secs]
+[Full GC 288K->288K(62848K), 0.0049580 secs]
+[GC 288K->288K(62848K), 0.0004590 secs]
+[Full GC 288K->288K(62848K), 0.0048240 secs]
+[GC 288K->288K(62848K), 0.0005700 secs]
+[Full GC 288K->288K(62848K), 0.0063250 secs]
+[GC 288K->288K(62848K), 0.0003540 secs]
+[Full GC 288K->288K(62848K), 0.0048210 secs]
+[GC 288K->288K(62848K), 0.0003700 secs]
+[Full GC 288K->288K(62848K), 0.0049430 secs]
+[GC 288K->288K(62848K), 0.0004250 secs]
+[Full GC 288K->288K(62848K), 0.0047700 secs]
+[GC 288K->288K(62848K), 0.0003550 secs]
+[Full GC 288K->288K(62848K), 0.0047090 secs]
+[GC 288K->288K(62848K), 0.0003850 secs]
+[Full GC 288K->288K(62848K), 0.0047220 secs]
+[GC 288K->288K(62848K), 0.0003830 secs]
+[Full GC 288K->288K(62848K), 0.0047330 secs]
+[GC 288K->288K(62848K), 0.0004230 secs]
+[Full GC 288K->288K(62848K), 0.0047130 secs]
+[GC 288K->288K(62848K), 0.0003140 secs]
+[Full GC 288K->288K(62848K), 0.0047360 secs]
+[GC 288K->288K(62848K), 0.0003540 secs]
+[Full GC 288K->288K(62848K), 0.0047210 secs]
+[GC 288K->288K(62848K), 0.0003610 secs]
+[Full GC 288K->288K(62848K), 0.0047280 secs]
+[GC 288K->288K(62848K), 0.0004640 secs]
+[Full GC 288K->288K(62848K), 0.0046920 secs]
+[GC 288K->288K(62848K), 0.0004700 secs]
+[Full GC 288K->288K(62848K), 0.0047080 secs]
+[GC 288K->288K(62848K), 0.0003220 secs]
+[Full GC 288K->288K(62848K), 0.0047020 secs]
+[GC 288K->288K(62848K), 0.0004100 secs]
+[Full GC 288K->288K(62848K), 0.0047860 secs]
+[GC 288K->288K(62848K), 0.0004340 secs]
+[Full GC 288K->288K(62848K), 0.0047310 secs]
+[GC 288K->288K(62848K), 0.0003980 secs]
+[Full GC 288K->288K(62848K), 0.0047440 secs]
+[GC 288K->288K(62848K), 0.0004540 secs]
+[Full GC 288K->288K(62848K), 0.0047260 secs]
+[GC 288K->288K(62848K), 0.0003340 secs]
+[Full GC 288K->288K(62848K), 0.0047130 secs]
+[GC 288K->288K(62848K), 0.0003420 secs]
+[Full GC 288K->288K(62848K), 0.0046830 secs]
+[GC 288K->288K(62848K), 0.0003560 secs]
+[Full GC 288K->288K(62848K), 0.0047030 secs]
+[GC 288K->288K(62848K), 0.0003440 secs]
+[Full GC 288K->288K(62848K), 0.0047340 secs]
+[GC 288K->288K(62848K), 0.0004980 secs]
+[Full GC 288K->288K(62848K), 0.0048440 secs]
+[GC 288K->288K(62848K), 0.0003500 secs]
+[Full GC 288K->288K(62848K), 0.0048000 secs]
+[GC 288K->288K(62848K), 0.0004090 secs]
+[Full GC 288K->288K(62848K), 0.0048140 secs]
+[GC 288K->288K(62848K), 0.0004960 secs]
+[Full GC 288K->288K(62848K), 0.0048130 secs]
+[GC 288K->288K(62848K), 0.0003140 secs]
+[Full GC 288K->288K(62848K), 0.0047990 secs]
+[GC 288K->288K(62848K), 0.0003350 secs]
+[Full GC 288K->288K(62848K), 0.0048460 secs]
+[GC 288K->288K(62848K), 0.0002190 secs]
+[Full GC 288K->288K(62848K), 0.0047700 secs]
+[GC 288K->288K(62848K), 0.0003200 secs]
+[Full GC 288K->288K(62848K), 0.0047690 secs]
+[GC 288K->288K(62848K), 0.0003220 secs]
+[Full GC 288K->288K(62848K), 0.0047490 secs]
+[GC 288K->288K(62848K), 0.0003210 secs]
+[Full GC 288K->288K(62848K), 0.0047590 secs]
+[GC 288K->288K(62848K), 0.0002560 secs]
+[Full GC 288K->288K(62848K), 0.0047250 secs]
+[GC 288K->288K(62848K), 0.0003560 secs]
+[Full GC 288K->288K(62848K), 0.0047430 secs]
+[GC 288K->288K(62848K), 0.0002860 secs]
+[Full GC 288K->288K(62848K), 0.0047320 secs]
+[GC 288K->288K(62848K), 0.0003470 secs]
+[Full GC 288K->288K(62848K), 0.0047370 secs]
+[GC 288K->288K(62848K), 0.0003020 secs]
+[Full GC 288K->288K(62848K), 0.0047140 secs]
+[GC 288K->288K(62848K), 0.0002670 secs]
+[Full GC 288K->288K(62848K), 0.0047510 secs]
+[GC 288K->288K(62848K), 0.0003510 secs]
+[Full GC 288K->288K(62848K), 0.0047140 secs]
+[GC 288K->288K(62848K), 0.0002680 secs]
+[Full GC 288K->288K(62848K), 0.0047100 secs]
+[GC 288K->288K(62848K), 0.0002390 secs]
+[Full GC 288K->288K(62848K), 0.0047820 secs]
+[GC 288K->288K(62848K), 0.0002780 secs]
+[Full GC 288K->288K(62848K), 0.0047480 secs]
+[GC 288K->288K(62848K), 0.0002590 secs]
+[Full GC 288K->288K(62848K), 0.0048950 secs]
+[GC 288K->288K(62848K), 0.0004100 secs]
+[Full GC 288K->288K(62848K), 0.0047580 secs]
+[GC 288K->288K(62848K), 0.0003630 secs]
+[Full GC 288K->288K(62848K), 0.0047230 secs]
+[GC 288K->288K(62848K), 0.0003200 secs]
+[Full GC 288K->288K(62848K), 0.0047490 secs]
+[GC 288K->288K(62848K), 0.0002940 secs]
+[Full GC 288K->288K(62848K), 0.0047300 secs]
+[GC 288K->288K(62848K), 0.0016660 secs]
+[Full GC 288K->288K(62848K), 0.0052520 secs]
+[GC 288K->288K(62848K), 0.0003090 secs]
+[Full GC 288K->288K(62848K), 0.0051710 secs]
+[GC 288K->288K(62848K), 0.0003220 secs]
+[Full GC 288K->288K(62848K), 0.0050270 secs]
+[GC 288K->288K(62848K), 0.0003390 secs]
+[Full GC 288K->288K(62848K), 0.0048450 secs]
+[GC 288K->288K(62848K), 0.0003830 secs]
+[Full GC 288K->288K(62848K), 0.0047870 secs]
+[GC 288K->288K(62848K), 0.0003800 secs]
+[Full GC 288K->288K(62848K), 0.0051750 secs]
+[GC 288K->288K(62848K), 0.0004380 secs]
+[Full GC 288K->288K(62848K), 0.0052900 secs]
+[GC 288K->288K(62848K), 0.0003550 secs]
+[Full GC 288K->288K(62848K), 0.0053350 secs]
+[GC 288K->288K(62848K), 0.0003090 secs]
+[Full GC 288K->288K(62848K), 0.0053060 secs]
+[GC 288K->288K(62848K), 0.0003030 secs]
+[Full GC 288K->288K(62848K), 0.0052720 secs]
+[GC 288K->288K(62848K), 0.0003220 secs]
+[Full GC 288K->288K(62848K), 0.0053230 secs]
+[GC 288K->288K(62848K), 0.0003610 secs]
+[Full GC 288K->288K(62848K), 0.0053970 secs]
+[GC 288K->288K(62848K), 0.0003590 secs]
+[Full GC 288K->288K(62848K), 0.0053740 secs]
+[GC 288K->288K(62848K), 0.0003610 secs]
+[Full GC 288K->288K(62848K), 0.0054130 secs]
+[GC 288K->288K(62848K), 0.0004750 secs]
+[Full GC 288K->288K(62848K), 0.0053560 secs]
+[GC 288K->288K(62848K), 0.0003510 secs]
+[Full GC 288K->288K(62848K), 0.0053140 secs]
+[GC 288K->288K(62848K), 0.0004350 secs]
+[Full GC 288K->288K(62848K), 0.0053260 secs]
+[GC 288K->288K(62848K), 0.0004010 secs]
+[Full GC 288K->288K(62848K), 0.0054420 secs]
+[GC 288K->288K(62848K), 0.0004300 secs]
+[Full GC 288K->288K(62848K), 0.0053740 secs]
+[GC 288K->288K(62848K), 0.0003610 secs]
+[Full GC 288K->288K(62848K), 0.0070060 secs]
+[GC 288K->288K(62848K), 0.0003240 secs]
+[Full GC 288K->288K(62848K), 0.0067830 secs]
+[GC 288K->288K(62848K), 0.0003500 secs]
+[Full GC 288K->288K(62848K), 0.0068030 secs]
+[GC 288K->288K(62848K), 0.0003260 secs]
+[Full GC 288K->288K(62848K), 0.0066850 secs]
+[GC 288K->288K(62848K), 0.0004600 secs]
+[Full GC 288K->288K(62848K), 0.0067520 secs]
+[GC 288K->288K(62848K), 0.0003550 secs]
+[Full GC 288K->288K(62848K), 0.0067010 secs]
+[GC 288K->288K(62848K), 0.0004590 secs]
+[Full GC 288K->288K(62848K), 0.0069550 secs]
+[GC 288K->288K(62848K), 0.0004450 secs]
+[Full GC 288K->288K(62848K), 0.0068770 secs]
+[GC 288K->288K(62848K), 0.0005830 secs]
+[Full GC 288K->288K(62848K), 0.0067410 secs]
+[GC 288K->288K(62848K), 0.0004850 secs]
+[Full GC 288K->288K(62848K), 0.0067660 secs]
+[GC 288K->288K(62848K), 0.0004270 secs]
+[Full GC 288K->288K(62848K), 0.0067770 secs]
+[GC 288K->288K(62848K), 0.0004140 secs]
+[Full GC 288K->288K(62848K), 0.0067090 secs]
+[GC 288K->288K(62848K), 0.0004360 secs]
+[Full GC 288K->288K(62848K), 0.0067760 secs]
+[GC 288K->288K(62848K), 0.0005740 secs]
+[Full GC 288K->288K(62848K), 0.0067000 secs]
+[GC 288K->288K(62848K), 0.0004730 secs]
+[Full GC 288K->288K(62848K), 0.0066820 secs]
+[GC 288K->288K(62848K), 0.0003490 secs]
+[Full GC 288K->288K(62848K), 0.0067010 secs]
+[GC 288K->288K(62848K), 0.0004610 secs]
+[Full GC 288K->288K(62848K), 0.0067730 secs]
+[GC 288K->288K(62848K), 0.0005560 secs]
+[Full GC 288K->288K(62848K), 0.0067720 secs]
+[GC 288K->288K(62848K), 0.0004240 secs]
+[Full GC 288K->288K(62848K), 0.0066300 secs]
+[GC 288K->288K(62848K), 0.0005200 secs]
+[Full GC 288K->288K(62848K), 0.0069920 secs]
+[GC 288K->288K(62848K), 0.0004240 secs]
+[Full GC 288K->288K(62848K), 0.0076550 secs]
+[GC 617K->304K(62848K), 0.0004960 secs]
+[Full GC 304K->288K(62848K), 0.0076840 secs]
+[GC 617K->304K(62848K), 0.0003870 secs]
+[Full GC 304K->288K(62848K), 0.0077080 secs]
+[GC 288K->288K(62848K), 0.0004140 secs]
+[Full GC 288K->288K(62848K), 0.0073410 secs]
+[GC 288K->288K(62848K), 0.0004640 secs]
+[Full GC 288K->288K(62848K), 0.0066710 secs]
+[GC 288K->288K(62848K), 0.0004830 secs]
+[Full GC 288K->288K(62848K), 0.0066390 secs]
+[GC 288K->288K(62848K), 0.0006970 secs]
+[Full GC 288K->288K(62848K), 0.0066070 secs]
+[GC 288K->288K(62848K), 0.0003500 secs]
+[Full GC 288K->288K(62848K), 0.0065460 secs]
+[GC 288K->288K(62848K), 0.0004160 secs]
+[Full GC 288K->288K(62848K), 0.0065420 secs]
+[GC 288K->288K(62848K), 0.0003710 secs]
+[Full GC 288K->288K(62848K), 0.0066060 secs]
+[GC 288K->288K(62848K), 0.0003510 secs]
+[Full GC 288K->288K(62848K), 0.0065420 secs] \ No newline at end of file
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt
new file mode 100644
index 0000000..02acb05
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt
@@ -0,0 +1,352 @@
+ 91 1 b java.lang.String::hashCode (67 bytes)
+ 106 2 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes)
+ 130 3 b java.lang.String::lastIndexOf (68 bytes)
+ 132 4 b java.lang.String::indexOf (87 bytes)
+ 136 5 b java.io.UnixFileSystem::normalize (75 bytes)
+ 138 1 % b java.io.UnixFileSystem::normalize @ 10 (75 bytes)
+ 142 6 b sun.nio.cs.UTF_8$Encoder::encode (361 bytes)
+ 149 2 % b sun.nio.cs.UTF_8$Encoder::encode @ 20 (361 bytes)
+ 162 2 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made not entrant
+ 165 7 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes)
+ 185 8 b sun.net.www.ParseUtil::encodePath (336 bytes)
+ 200 9 b java.lang.String::indexOf (166 bytes)
+ 236 10 b java.lang.String::equals (88 bytes)
+ 253 11 b java.util.Arrays::binarySearch0 (72 bytes)
+ 255 12 b com.google.common.base.CharMatcher$10::matches (17 bytes)
+ 256 13 b java.util.Arrays::binarySearch (9 bytes)
+ 257 3 % b com.google.common.base.CharMatcher::setBits @ 3 (28 bytes)
+made not compilable com.google.common.base.CharMatcher::matches
+ 263 14 b com.google.common.base.CharMatcher::setBits (28 bytes)
+ 363 15 b java.lang.Object::<init> (1 bytes)
+ 365 16 b java.lang.String::charAt (33 bytes)
+ 483 17 b java.lang.String::length (5 bytes)
+ 487 18 n java.util.zip.ZipFile::getEntryBytes (0 bytes) (static)
+ 524 19 b java.util.zip.ZipFile::ensureOpen (37 bytes)
+ 525 20 b java.util.zip.ZipFile::access$400 (5 bytes)
+ 527 21 b java.util.zip.ZipFile::access$200 (5 bytes)
+ 527 22 b java.util.zip.ZipFile::access$300 (5 bytes)
+ 534 23 n java.lang.System::arraycopy (0 bytes) (static)
+ 550 24 b java.lang.Math::min (11 bytes)
+ 557 25 b java.util.HashMap::indexFor (6 bytes)
+ 559 26 b java.util.HashMap::hash (23 bytes)
+ 561 27 b java.lang.String::<init> (20 bytes)
+ 563 28 b java.lang.String::substring (83 bytes)
+ 566 29 b java.util.HashMap::get (79 bytes)
+ 569 30 b java.util.Arrays::copyOfRange (63 bytes)
+ 573 31 b java.lang.String::<init> (72 bytes)
+ 577 32 b java.lang.String::lastIndexOf (12 bytes)
+ 588 33 n java.util.zip.ZipFile::getEntrySize (0 bytes) (static)
+ 588 34 b java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes)
+ 589 35 n java.util.zip.ZipFile::getEntryCSize (0 bytes) (static)
+ 589 36 n java.util.zip.ZipFile::getEntryMethod (0 bytes) (static)
+ 589 37 n java.util.zip.ZipFile::freeEntry (0 bytes) (static)
+ 590 38 b java.util.zip.ZipFile::getZipEntry (245 bytes)
+made not compilable java.nio.charset.Charset::newDecoder
+ 612 39 b java.util.zip.ZipEntry::<init> (43 bytes)
+ 613 40 n java.util.zip.ZipFile::getEntryFlag (0 bytes) (static)
+ 613 41 b java.util.zip.ZipCoder::isUTF8 (5 bytes)
+ 613 42 n java.util.zip.ZipFile::getEntryTime (0 bytes) (static)
+ 613 43 n java.util.zip.ZipFile::getEntryCrc (0 bytes) (static)
+ 613 44 b java.util.zip.ZipFile::access$1000 (6 bytes)
+ 614 45 b java.util.jar.JarFile$JarFileEntry::<init> (11 bytes)
+ 615 46 b java.util.jar.JarEntry::<init> (6 bytes)
+ 615 47 b java.util.zip.ZipEntry::<init> (115 bytes)
+ 616 48 b sun.misc.URLClassPath::getResourceMapKey (55 bytes)
+ 621 49 b java.util.jar.JarFile$1::hasMoreElements (10 bytes)
+ 623 50 !b java.util.zip.ZipFile$1::hasMoreElements (41 bytes)
+ 624 51 b java.util.zip.ZipEntry::getName (5 bytes)
+ 625 52 b java.util.jar.JarFile$1::nextElement (5 bytes)
+ 629 53 b java.util.jar.JarFile$1::nextElement (26 bytes)
+ 633 54 b java.util.zip.ZipFile$1::nextElement (5 bytes)
+ 636 55 !b java.util.zip.ZipFile$1::nextElement (212 bytes)
+ 639 56 b java.util.zip.ZipFile::access$500 (6 bytes)
+ 639 57 n java.util.zip.ZipFile::getNextEntry (0 bytes) (static)
+ 639 58 b java.util.zip.ZipFile::access$900 (7 bytes)
+ 640 59 b java.util.ArrayList::size (5 bytes)
+ 641 60 b sun.misc.URLClassPath$JarLoader::addJarEntriesToEntryMap (202 bytes)
+ 664 61 b java.util.ArrayList::get (11 bytes)
+ 664 62 b java.util.ArrayList::rangeCheck (22 bytes)
+ 665 63 b java.util.ArrayList::elementData (7 bytes)
+ 707 29 java.util.HashMap::get (79 bytes) made not entrant
+ 719 64 b java.lang.String::replace (142 bytes)
+ 749 65 b java.lang.String::startsWith (78 bytes)
+ 865 66 b java.util.HashMap::get (79 bytes)
+ 867 67 b java.io.DataOutputStream::writeUTF (435 bytes)
+ 913 68 b java.lang.String::getChars (66 bytes)
+ 917 69 !b sun.reflect.generics.parser.SignatureParser::current (40 bytes)
+ 918 70 b java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
+ 922 71 b java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
+ 922 72 b java.io.DataOutputStream::incCount (20 bytes)
+ 923 73 s b java.io.ByteArrayOutputStream::write (32 bytes)
+ 933 74 b java.lang.CharacterData::of (120 bytes)
+ 934 75 b java.lang.CharacterDataLatin1::getProperties (11 bytes)
+ 967 76 b java.util.concurrent.ConcurrentSkipListMap::findPredecessor (121 bytes)
+ 979 77 b java.lang.Integer::compareTo (9 bytes)
+ 980 78 b java.lang.Integer::compareTo (12 bytes)
+ 980 79 b java.lang.Integer::compare (20 bytes)
+ 1030 80 b java.lang.AbstractStringBuilder::append (29 bytes)
+ 1046 81 b java.util.jar.Manifest$FastInputStream::readLine (167 bytes)
+ 1073 82 b net.sf.cglib.asm.ByteVector::putUTF8 (394 bytes)
+ 1081 83 b net.sf.cglib.asm.Type::a (253 bytes)
+ 1087 84 s b java.lang.StringBuffer::append (8 bytes)
+ 1091 85 b net.sf.cglib.asm.Type::a (214 bytes)
+ 1116 86 b java.util.ArrayList::access$100 (5 bytes)
+ 1118 87 b java.lang.AbstractStringBuilder::append (48 bytes)
+ 1127 88 b net.sf.cglib.asm.Type::getArgumentTypes (131 bytes)
+ 1154 89 b sun.misc.MetaIndex::mayContain (51 bytes)
+ 1181 90 b sun.reflect.generics.parser.SignatureParser::advance (37 bytes)
+ 1189 91 b java.lang.Character::isWhitespace (5 bytes)
+ 1189 92 b java.lang.Character::isWhitespace (9 bytes)
+ 1190 93 b java.lang.CharacterDataLatin1::isWhitespace (23 bytes)
+ 1190 94 b sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes)
+ 1253 95 b java.util.Properties$LineReader::readLine (452 bytes)
+made not compilable java.io.Reader::read
+ 1287 96 b java.util.zip.ZStreamRef::address (5 bytes)
+ 1314 97 b java.lang.StringBuilder::append (8 bytes)
+ 1323 98 n java.lang.Thread::currentThread (0 bytes) (static)
+ 1327 99 b java.lang.String::compareTo (150 bytes)
+ 1346 100 b sun.misc.URLClassPath::access$100 (5 bytes)
+ 1353 101 b java.util.HashMap::put (126 bytes)
+ 1377 102 b java.lang.AbstractStringBuilder::<init> (12 bytes)
+ 1396 103 b java.util.zip.InflaterInputStream::ensureOpen (18 bytes)
+ 1399 104 b java.util.concurrent.ConcurrentSkipListMap::findNode (108 bytes)
+ 1407 105 b java.util.HashMap::transfer (83 bytes)
+ 1413 106 b sun.reflect.ClassFileAssembler::emitByte (11 bytes)
+ 1414 107 b sun.reflect.ByteVectorImpl::add (38 bytes)
+ 1422 108 b java.util.HashMap$HashIterator::nextEntry (99 bytes)
+ 1425 109 b java.lang.reflect.Method::getName (5 bytes)
+ 1428 110 n java.lang.Object::hashCode (0 bytes)
+ 1443 111 b java.lang.String::startsWith (7 bytes)
+ 1452 112 b java.lang.ref.Reference::get (5 bytes)
+ 1453 113 b java.lang.Class::searchMethods (90 bytes)
+ 1463 114 b java.util.ArrayList$Itr::hasNext (20 bytes)
+ 1463 115 b java.util.HashMap$HashIterator::<init> (63 bytes)
+ 1473 116 b java.util.ArrayList::ensureCapacityInternal (26 bytes)
+ 1478 117 b java.util.ArrayList::add (29 bytes)
+ 1484 118 n java.lang.Object::clone (0 bytes)
+ 1489 119 b java.lang.reflect.AccessibleObject::<init> (5 bytes)
+ 1490 120 b sun.reflect.ReflectionFactory::langReflectAccess (15 bytes)
+ 1493 121 b java.lang.Class::copyMethods (36 bytes)
+ 1501 122 b java.util.ArrayList$Itr::checkForComodification (23 bytes)
+ 1503 123 b java.util.Arrays::hashCode (56 bytes)
+ 1505 124 b java.lang.reflect.ReflectAccess::copyMethod (5 bytes)
+ 1507 125 b java.lang.reflect.Method::copy (67 bytes)
+ 1508 126 b java.lang.reflect.Method::<init> (68 bytes)
+ 1510 127 b java.lang.reflect.Method::getDeclaringClass (5 bytes)
+ 1521 128 b java.lang.reflect.Method::getParameterTypes (14 bytes)
+ 1524 129 !b com.sun.jersey.core.reflection.ReflectionHelper::findMethodOnClass (94 bytes)
+ 1545 130 b java.lang.System::getSecurityManager (4 bytes)
+ 1546 131 b java.util.ArrayList$Itr::next (66 bytes)
+ 1547 132 b java.util.ArrayList::access$200 (5 bytes)
+ 1553 133 b java.util.HashMap$HashIterator::hasNext (13 bytes)
+ 1555 134 b java.util.HashMap$Entry::<init> (26 bytes)
+ 1558 135 b java.lang.Integer::valueOf (54 bytes)
+ 1560 136 b java.lang.ref.SoftReference::get (29 bytes)
+ 1565 137 s b java.lang.reflect.Method::declaredAnnotations (39 bytes)
+ 1569 138 b java.lang.StringBuilder::toString (17 bytes)
+ 1574 139 b java.lang.Class::clearCachesOnClassRedefinition (70 bytes)
+ 1580 140 b java.util.AbstractCollection::<init> (5 bytes)
+ 1581 141 b java.lang.Class::checkInitted (19 bytes)
+ 1595 142 b com.sun.jersey.core.reflection.AnnotatedMethod::hasParameterAnnotations (80 bytes)
+ 1604 143 n java.lang.Class::getClassLoader0 (0 bytes)
+ 1607 144 b java.lang.Class::getMethod0 (97 bytes)
+ 1634 145 b java.util.HashMap$Entry::getKey (5 bytes)
+ 1634 146 b java.util.Collections$EmptyMap::get (2 bytes)
+ 1636 147 n java.lang.String::intern (0 bytes)
+ 1636 148 b java.lang.reflect.Method::getAnnotation (26 bytes)
+ 1640 148 java.lang.reflect.Method::getAnnotation (26 bytes) made not entrant
+ 1641 149 b java.util.HashMap$KeyIterator::next (8 bytes)
+ 1646 150 b java.lang.reflect.Method::getReturnType (5 bytes)
+ 1647 151 b java.lang.Class::getName (21 bytes)
+ 1658 152 n java.lang.Class::getInterfaces (0 bytes)
+ 1659 153 b java.util.AbstractList::<init> (10 bytes)
+ 1660 154 b com.sun.jersey.core.reflection.AnnotatedMethod::hasMethodAnnotations (43 bytes)
+ 1676 155 b java.lang.Character::toLowerCase (9 bytes)
+ 1677 156 b java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
+ 1688 157 b java.util.concurrent.locks.AbstractQueuedSynchronizer::getState (5 bytes)
+ 1695 158 b java.lang.Class::argumentTypesToString (78 bytes)
+ 1701 2 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made zombie
+ 1701 29 java.util.HashMap::get (79 bytes) made zombie
+ 1702 159 b java.lang.Class::checkMemberAccess (78 bytes)
+ 1704 160 b sun.reflect.generics.parser.SignatureParser::parsePackageNameAndSimpleClassTypeSignature (139 bytes)
+ 1734 161 s!b sun.misc.URLClassPath::getLoader (182 bytes)
+ 1772 162 b java.util.Properties::loadConvert (505 bytes)
+ 1789 163 b sun.reflect.ClassFileAssembler::emitConstantPoolUTF8 (50 bytes)
+ 1809 164 !b sun.reflect.UTF8::encode (189 bytes)
+ 1818 165 b sun.reflect.UTF8::utf8Length (81 bytes)
+ 1841 166 b sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (543 bytes)
+ 1869 167 b java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+made not compilable java.util.regex.Pattern$CharProperty::isSatisfiedBy
+ 1882 168 b java.util.regex.Matcher::search (109 bytes)
+ 2023 169 b java.lang.Character::charCount (12 bytes)
+ 2023 170 b java.lang.Character::isHighSurrogate (18 bytes)
+ 2024 171 b java.lang.String::codePointAt (44 bytes)
+ 2025 172 b java.lang.Character::codePointAtImpl (41 bytes)
+ 2036 173 b java.nio.Buffer::checkIndex (22 bytes)
+ 2036 174 b java.nio.DirectLongBufferU::ix (10 bytes)
+ 2040 175 b java.util.concurrent.locks.AbstractOwnableSynchronizer::setExclusiveOwnerThread (6 bytes)
+ 2041 176 b sun.misc.URLClassPath::getResource (79 bytes)
+made not compilable java.net.URLStreamHandler::openConnection
+ 2059 176 sun.misc.URLClassPath::getResource (79 bytes) made not entrant
+ 2063 177 b com.google.common.io.LineBuffer::add (201 bytes)
+made not compilable com.google.common.io.LineBuffer::handleLine
+ 2073 178 b java.util.regex.Pattern::has (15 bytes)
+ 2074 179 b java.util.ArrayList::<init> (44 bytes)
+ 2080 180 b java.net.URL::getHost (5 bytes)
+ 2089 168 java.util.regex.Matcher::search (109 bytes) made not entrant
+ 2089 167 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) made not entrant
+ 2124 181 b java.util.HashMap::addEntry (58 bytes)
+ 2140 182 n java.lang.System::nanoTime (0 bytes) (static)
+ 2145 183 b java.util.zip.Inflater::ensureOpen (47 bytes)
+ 2204 184 b java.lang.String::trim (87 bytes)
+ 3019 185 b java.lang.String::indexOf (7 bytes)
+ 3023 186 b java.util.regex.Pattern$Curly::match0 (174 bytes)
+ 3030 187 b java.util.regex.Matcher::reset (83 bytes)
+ 3035 188 b java.util.regex.Pattern$Node::match (27 bytes)
+ 3036 148 java.lang.reflect.Method::getAnnotation (26 bytes) made zombie
+ 3037 189 b java.util.regex.Pattern$BmpCharProperty::match (50 bytes)
+ 3039 190 b java.util.regex.Pattern$Ctype::isSatisfiedBy (24 bytes)
+ 3039 191 b java.util.regex.ASCII::isType (15 bytes)
+ 3040 192 b java.util.regex.ASCII::getType (17 bytes)
+ 3042 193 b java.util.regex.Pattern$CharProperty::match (56 bytes)
+ 3045 194 b java.util.regex.Pattern$Slice::match (79 bytes)
+ 3052 195 b java.util.regex.Matcher::match (109 bytes)
+ 3062 196 b java.util.regex.Pattern$Curly::match (86 bytes)
+ 3074 197 b com.google.common.collect.AbstractIndexedListIterator::hasNext (17 bytes)
+ 3078 198 b java.util.concurrent.ConcurrentHashMap$Segment::rehash (262 bytes)
+ 3094 101 java.util.HashMap::put (126 bytes) made not entrant
+ 15172 199 b java.util.concurrent.locks.AbstractOwnableSynchronizer::getExclusiveOwnerThread (5 bytes)
+ 17427 200 n sun.misc.Unsafe::compareAndSwapInt (0 bytes)
+ 17431 193 java.util.regex.Pattern$CharProperty::match (56 bytes) made not entrant
+ 17436 201 b java.lang.String::toUpperCase (442 bytes)
+ 17484 202 !b sun.misc.URLClassPath$JarLoader::getResource (91 bytes)
+ 17518 203 b java.nio.Buffer::position (43 bytes)
+ 17519 167 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) made zombie
+ 17519 176 sun.misc.URLClassPath::getResource (79 bytes) made zombie
+ 17519 168 java.util.regex.Matcher::search (109 bytes) made zombie
+ 17519 204 b java.nio.ByteBuffer::arrayOffset (35 bytes)
+ 17520 205 b java.nio.CharBuffer::arrayOffset (35 bytes)
+ 17524 206 b java.nio.Buffer::position (5 bytes)
+ 17524 207 b sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (489 bytes)
+ 17531 208 b java.nio.charset.CoderResult::isUnderflow (13 bytes)
+ 17539 209 b java.nio.Buffer::limit (62 bytes)
+ 17540 210 b java.nio.Buffer::<init> (121 bytes)
+ 17541 211 b java.nio.Buffer::remaining (10 bytes)
+ 17542 212 b java.nio.charset.CoderResult::isOverflow (14 bytes)
+ 17542 213 b java.nio.Buffer::hasRemaining (17 bytes)
+ 17542 214 b java.nio.CharBuffer::hasArray (20 bytes)
+ 17543 215 b java.nio.ByteBuffer::hasArray (20 bytes)
+ 17543 216 !b java.nio.CharBuffer::wrap (20 bytes)
+ 17545 217 b java.nio.HeapCharBuffer::<init> (14 bytes)
+ 17546 218 b java.nio.CharBuffer::<init> (22 bytes)
+ 17547 219 b sun.nio.cs.StreamEncoder::ensureOpen (18 bytes)
+ 17547 220 b sun.nio.cs.StreamEncoder::implWrite (156 bytes)
+ 17556 221 !b java.nio.charset.CharsetEncoder::encode (285 bytes)
+ 17561 222 b sun.nio.cs.UTF_8$Encoder::encodeLoop (28 bytes)
+ 17562 223 !b sun.nio.cs.StreamEncoder::write (78 bytes)
+ 17565 224 b com.google.gson.stream.JsonWriter::string (365 bytes)
+ 17577 225 b sun.nio.cs.StreamEncoder::write (37 bytes)
+ 17588 224 com.google.gson.stream.JsonWriter::string (365 bytes) made not entrant
+ 17589 226 b com.google.gson.stream.JsonWriter::string (365 bytes)
+ 17618 227 b java.lang.String::toLowerCase (477 bytes)
+ 17637 10 java.lang.String::equals (88 bytes) made not entrant
+ 17641 73 s java.io.ByteArrayOutputStream::write (32 bytes) made not entrant
+ 17656 7 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made not entrant
+ 17656 34 java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes) made not entrant
+ 17656 38 java.util.zip.ZipFile::getZipEntry (245 bytes) made not entrant
+ 17658 228 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes)
+ 17672 229 b java.lang.StringBuilder::<init> (7 bytes)
+ 17680 230 b java.math.BigInteger::destructiveMulAdd (150 bytes)
+ 17685 101 java.util.HashMap::put (126 bytes) made zombie
+ 17685 231 b java.util.regex.Pattern$Single::isSatisfiedBy (14 bytes)
+ 17687 232 b java.lang.Integer::parseInt (261 bytes)
+ 17693 233 b java.lang.Character::digit (6 bytes)
+ 17693 234 b java.lang.Character::digit (10 bytes)
+ 17694 235 b java.lang.CharacterDataLatin1::digit (91 bytes)
+ 17698 236 b sun.security.util.Cache$EqualByteArray::hashCode (57 bytes)
+ 17721 237 b java.lang.String::equals (88 bytes)
+ 17733 238 b java.math.BigInteger::stripLeadingZeroBytes (132 bytes)
+ 17740 239 s b java.io.ByteArrayInputStream::read (36 bytes)
+ 17749 240 s b java.io.ByteArrayInputStream::available (10 bytes)
+ 17756 241 b java.io.ByteArrayInputStream::mark (9 bytes)
+ 17768 242 b sun.security.util.DerInputStream::getLength (111 bytes)
+made not compilable java.io.InputStream::read
+ 17774 243 b java.util.Arrays::copyOf (19 bytes)
+ 17781 244 b java.io.DataInputStream::readUTF (501 bytes)
+ 17822 245 b sun.security.util.ObjectIdentifier::check (78 bytes)
+ 17829 246 s b java.io.ByteArrayOutputStream::write (32 bytes)
+ 17854 247 b sun.security.util.DerInputStream::<init> (19 bytes)
+ 17858 248 b sun.security.util.DerInputStream::available (8 bytes)
+ 17863 249 !b java.security.cert.Certificate::hashCode (34 bytes)
+ 17889 202 ! sun.misc.URLClassPath$JarLoader::getResource (91 bytes) made not entrant
+ 17919 250 b java.util.Arrays::equals (54 bytes)
+ 17930 251 b java.util.Arrays::hashCode (44 bytes)
+ 17936 252 b java.math.BigInteger::mulAdd (81 bytes)
+ 17957 195 java.util.regex.Matcher::match (109 bytes) made not entrant
+ 17965 4 % b com.sun.crypto.provider.AESCrypt::<clinit> @ 724 (1577 bytes)
+ 18073 232 java.lang.Integer::parseInt (261 bytes) made not entrant
+ 18142 233 java.lang.Character::digit (6 bytes) made not entrant
+ 18142 74 java.lang.CharacterData::of (120 bytes) made not entrant
+ 18142 91 java.lang.Character::isWhitespace (5 bytes) made not entrant
+ 18142 94 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) made not entrant
+ 18142 92 java.lang.Character::isWhitespace (9 bytes) made not entrant
+ 18142 155 java.lang.Character::toLowerCase (9 bytes) made not entrant
+ 18142 201 java.lang.String::toUpperCase (442 bytes) made not entrant
+ 18142 234 java.lang.Character::digit (10 bytes) made not entrant
+ 18142 227 java.lang.String::toLowerCase (477 bytes) made not entrant
+ 18143 253 b java.lang.Character::toUpperCaseEx (30 bytes)
+ 18145 10 java.lang.String::equals (88 bytes) made zombie
+ 18148 254 b java.io.BufferedInputStream::getBufIfOpen (21 bytes)
+ 18148 255 s b java.io.BufferedInputStream::read (49 bytes)
+ 18151 256 b java.io.DataInputStream::readChar (40 bytes)
+ 18158 193 java.util.regex.Pattern$CharProperty::match (56 bytes) made zombie
+ 18158 224 com.google.gson.stream.JsonWriter::string (365 bytes) made zombie
+ 18158 5 % b sun.text.normalizer.NormalizerDataReader::read @ 11 (83 bytes)
+ 18165 256 java.io.DataInputStream::readChar (40 bytes) made not entrant
+ 18169 257 b java.io.DataInputStream::readInt (72 bytes)
+ 18180 258 b java.io.DataInputStream::readChar (40 bytes)
+ 18192 259 b java.lang.StringBuilder::append (8 bytes)
+ 18203 260 b java.lang.CharacterData::of (120 bytes)
+ 18206 261 b java.lang.Character::toLowerCase (6 bytes)
+made not compilable java.lang.CharacterData::toLowerCase
+ 18208 262 b java.lang.String::toLowerCase (477 bytes)
+ 18229 263 b java.lang.String::toUpperCase (442 bytes)
+ 18251 264 b java.lang.Character::isWhitespace (5 bytes)
+made not compilable java.lang.CharacterData::isWhitespace
+ 18251 265 b java.lang.Character::isWhitespace (9 bytes)
+ 18255 266 b sun.text.normalizer.NormalizerBase::normalize (223 bytes)
+ 18262 267 !b sun.security.x509.AVA::toRFC2253CanonicalString (484 bytes)
+made not compilable java.nio.charset.spi.CharsetProvider::charsetForName
+ 18334 268 b java.lang.String::regionMatches (157 bytes)
+made not compilable java.lang.CharacterData::toUpperCase
+ 18347 7 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made zombie
+ 18347 34 java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes) made zombie
+ 18347 73 s java.io.ByteArrayOutputStream::write (32 bytes) made zombie
+ 18347 38 java.util.zip.ZipFile::getZipEntry (245 bytes) made zombie
+ 18348 232 java.lang.Integer::parseInt (261 bytes) made zombie
+ 18348 249 ! java.security.cert.Certificate::hashCode (34 bytes) made not entrant
+ 18357 269 b sun.security.provider.SHA::implCompress (491 bytes)
+ 18429 6 % b com.sun.crypto.provider.ARCFOURCipher::crypt @ 15 (129 bytes)
+ 18435 270 n sun.misc.Unsafe::getInt (0 bytes)
+ 18436 271 b java.lang.Integer::reverseBytes (26 bytes)
+ 18436 272 b com.sun.crypto.provider.ARCFOURCipher::crypt (129 bytes)
+ 18442 202 ! sun.misc.URLClassPath$JarLoader::getResource (91 bytes) made zombie
+ 18442 195 java.util.regex.Matcher::match (109 bytes) made zombie
+ 49381 233 java.lang.Character::digit (6 bytes) made zombie
+ 49381 74 java.lang.CharacterData::of (120 bytes) made zombie
+ 49381 91 java.lang.Character::isWhitespace (5 bytes) made zombie
+ 49381 94 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) made zombie
+ 49381 92 java.lang.Character::isWhitespace (9 bytes) made zombie
+ 49381 155 java.lang.Character::toLowerCase (9 bytes) made zombie
+ 49381 161 s! sun.misc.URLClassPath::getLoader (182 bytes) made not entrant
+ 49381 201 java.lang.String::toUpperCase (442 bytes) made zombie
+ 49381 234 java.lang.Character::digit (10 bytes) made zombie
+ 49381 227 java.lang.String::toLowerCase (477 bytes) made zombie
+ 49382 273 b sun.misc.URLClassPath$LoaderSearchCursor::nextLoader (211 bytes)
+ 49406 66 java.util.HashMap::get (79 bytes) made not entrant
+ 49420 274 b java.util.HashMap$EntryIterator::next (5 bytes)
+ 49422 275 b java.util.HashMap$EntryIterator::next (5 bytes)
+ 49434 276 b java.util.HashMap$Entry::hashCode (38 bytes)
+ 49436 256 java.io.DataInputStream::readChar (40 bytes) made zombie
+ 49436 277 b java.util.AbstractMap::hashCode (43 bytes)
+made not compilable java.util.AbstractMap::entrySet \ No newline at end of file
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt
new file mode 100644
index 0000000..a09cf57
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt
@@ -0,0 +1,832 @@
+ bool AHRByDeathCollectTimeRatio = false {product}
+ bool AHRByMinorPauseTimeMajorFreq = false {product}
+ bool AHRByPromoToAllocRatio = false {product}
+ bool AHRBySurvivorAge = false {product}
+ uintx AHRIncrementSize = 5242880 {product}
+ uintx AHRMaxDeathCollectTimeRatio = 55 {product}
+ uintx AHRMaxMinorInvocationsPerMajor = 30 {product}
+ uintx AHRMaxMinorPauseTimeMillis = 100 {product}
+ uintx AHRMaxPromoToAllocRatio = 25 {product}
+ uintx AHRMaxRatio = 100 {product}
+ uintx AHRMaxSize = 104857600 {product}
+ uintx AHRMaxSurvivorAge = 10 {product}
+ uintx AHRMinDeathCollectTimeRatio = 50 {product}
+ uintx AHRMinMinorInvocationsPerMajor = 5 {product}
+ uintx AHRMinMinorPauseTimeMillis = 150 {product}
+ uintx AHRMinPromoToAllocRatio = 2 {product}
+ uintx AHRMinSurvivorAge = 2 {product}
+ bool AdaptiveHeapRebalance = false {product}
+ uintx AdaptivePermSizeWeight = 20 {product}
+ uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
+ uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
+ uintx AdaptiveSizePausePolicy = 0 {product}
+ uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product}
+ uintx AdaptiveSizePolicyInitializingSteps = 20 {product}
+ uintx AdaptiveSizePolicyOutputInterval = 0 {product}
+ uintx AdaptiveSizePolicyWeight = 10 {product}
+ uintx AdaptiveSizeThroughPutPolicy = 0 {product}
+ uintx AdaptiveTimeWeight = 25 {product}
+ bool AdjustConcurrency = false {product}
+ bool AggressiveOpts = false {product}
+ intx AliasLevel = 3 {product}
+ intx AllocateInstancePrefetchLines = 1 {product}
+ intx AllocatePrefetchDistance = 192 {product}
+ intx AllocatePrefetchInstr = 0 {product}
+ intx AllocatePrefetchLines = 4 {product}
+ intx AllocatePrefetchStepSize = 64 {product}
+ intx AllocatePrefetchStyle = 1 {product}
+ bool AllowJNIEnvProxy = false {product}
+ bool AllowParallelDefineClass = false {product}
+ bool AllowUserSignalHandlers = false {product}
+ bool AlwaysActAsServerClassMachine = false {product}
+ bool AlwaysCompileLoopMethods = false {product}
+ intx AlwaysInflate = 0 {product}
+ bool AlwaysLockClassLoader = false {product}
+ bool AlwaysPreTouch = false {product}
+ bool AlwaysRestoreFPU = false {product}
+ bool AlwaysTenure = false {product}
+ bool AnonymousClasses = false {product}
+ uintx ArraycopyDstPrefetchDistance = 0 {product}
+ uintx ArraycopySrcPrefetchDistance = 0 {product}
+ bool AssertOnSuspendWaitFailure = false {product}
+ intx Atomics = 0 {product}
+ intx AutoBoxCacheMax = 128 {C2 product}
+ uintx AutoGCSelectPauseMillis = 5000 {product}
+ intx BCEATraceLevel = 0 {product}
+ intx BackEdgeThreshold = 100000 {pd product}
+ bool BackgroundCompilation = true {pd product}
+ uintx BaseFootPrintEstimate = 268435456 {product}
+ intx BiasedLockingBulkRebiasThreshold = 20 {product}
+ intx BiasedLockingBulkRevokeThreshold = 40 {product}
+ intx BiasedLockingDecayTime = 25000 {product}
+ intx BiasedLockingStartupDelay = 4000 {product}
+ bool BindCMSThreadToCPU = false {diagnostic}
+ bool BindGCTaskThreadsToCPUs = false {product}
+ intx BlockCopyLowLimit = 2048 {product}
+ bool BlockLayoutByFrequency = true {C2 product}
+ intx BlockLayoutMinDiamondPercentage = 20 {C2 product}
+ bool BlockLayoutRotateLoops = true {C2 product}
+ bool BlockOffsetArrayUseUnallocatedBlock = false {diagnostic}
+ intx BlockZeroingLowLimit = 2048 {product}
+ bool BranchOnRegister = false {C2 product}
+ bool BytecodeVerificationLocal = false {product}
+ bool BytecodeVerificationRemote = true {product}
+ bool C1OptimizeVirtualCallProfiling = true {C1 product}
+ bool C1ProfileBranches = true {C1 product}
+ bool C1ProfileCalls = true {C1 product}
+ bool C1ProfileCheckcasts = true {C1 product}
+ bool C1ProfileInlinedCalls = true {C1 product}
+ bool C1ProfileVirtualCalls = true {C1 product}
+ bool C1UpdateMethodData = true {C1 product}
+ intx CICompilerCount = 2 {product}
+ bool CICompilerCountPerCPU = false {product}
+ bool CITime = false {product}
+ bool CMSAbortSemantics = false {product}
+ uintx CMSAbortablePrecleanMinWorkPerIteration = 100 {product}
+ intx CMSAbortablePrecleanWaitMillis = 100 {manageable}
+ uintx CMSBitMapYieldQuantum = 10485760 {product}
+ uintx CMSBootstrapOccupancy = 50 {product}
+ bool CMSClassUnloadingEnabled = false {product}
+ uintx CMSClassUnloadingMaxInterval = 0 {product}
+ bool CMSCleanOnEnter = true {product}
+ bool CMSCompactWhenClearAllSoftRefs = true {product}
+ uintx CMSConcMarkMultiple = 32 {product}
+ bool CMSConcurrentMTEnabled = true {product}
+ uintx CMSCoordinatorYieldSleepCount = 10 {product}
+ bool CMSDumpAtPromotionFailure = false {product}
+ bool CMSEdenChunksRecordAlways = false {product}
+ uintx CMSExpAvgFactor = 50 {product}
+ bool CMSExtrapolateSweep = false {product}
+ uintx CMSFullGCsBeforeCompaction = 0 {product}
+ uintx CMSIncrementalDutyCycle = 10 {product}
+ uintx CMSIncrementalDutyCycleMin = 0 {product}
+ bool CMSIncrementalMode = false {product}
+ uintx CMSIncrementalOffset = 0 {product}
+ bool CMSIncrementalPacing = true {product}
+ uintx CMSIncrementalSafetyFactor = 10 {product}
+ uintx CMSIndexedFreeListReplenish = 4 {product}
+ intx CMSInitiatingOccupancyFraction = -1 {product}
+ intx CMSInitiatingOccupancyFractionDelta = -1 {product}
+ intx CMSInitiatingPermOccupancyFraction = -1 {product}
+ intx CMSInitiatingPermOccupancyFractionDelta = -1 {product}
+ intx CMSInitiatingRamLimitFraction = -1 {product}
+ intx CMSIsTooFullPercentage = 98 {product}
+ double CMSLargeCoalSurplusPercent = 0.950000 {product}
+ double CMSLargeSplitSurplusPercent = 1.000000 {product}
+ bool CMSLoopWarn = false {product}
+ uintx CMSMaxAbortablePrecleanLoops = 0 {product}
+ intx CMSMaxAbortablePrecleanTime = 5000 {product}
+ uintx CMSOldPLABMax = 1024 {product}
+ uintx CMSOldPLABMin = 16 {product}
+ uintx CMSOldPLABNumRefills = 4 {product}
+ uintx CMSOldPLABReactivityCeiling = 10 {product}
+ uintx CMSOldPLABReactivityFactor = 2 {product}
+ bool CMSOldPLABResizeQuicker = false {product}
+ uintx CMSOldPLABToleranceFactor = 4 {product}
+ bool CMSPLABRecordAlways = true {product}
+ uintx CMSParPromoteBlocksToClaim = 16 {product}
+ bool CMSParallelInitialMarkEnabled = false {product}
+ bool CMSParallelRemarkEnabled = true {product}
+ uintx CMSParallelSTWFullGCHeapRegionSize = 1048576 {product}
+ bool CMSParallelSurvivorRemarkEnabled = true {product}
+ bool CMSPermGenPrecleaningEnabled = true {product}
+ uintx CMSPrecleanDenominator = 3 {product}
+ uintx CMSPrecleanIter = 3 {product}
+ uintx CMSPrecleanNumerator = 2 {product}
+ bool CMSPrecleanRefLists1 = true {product}
+ bool CMSPrecleanRefLists2 = false {product}
+ bool CMSPrecleanSurvivors1 = false {product}
+ bool CMSPrecleanSurvivors2 = true {product}
+ uintx CMSPrecleanThreshold = 1000 {product}
+ bool CMSPrecleaningEnabled = true {product}
+ bool CMSPrintChunksInDump = false {product}
+ bool CMSPrintEdenSurvivorChunks = false {product}
+ bool CMSPrintObjectsInDump = false {product}
+ uintx CMSRemarkVerifyVariant = 1 {product}
+ bool CMSReplenishIntermediate = true {product}
+ uintx CMSRescanMultiple = 32 {product}
+ uintx CMSRevisitStackSize = 1048576 {product}
+ uintx CMSSamplingGrain = 16384 {product}
+ bool CMSScavengeBeforeRemark = false {product}
+ uintx CMSScheduleRemarkEdenPenetration = 50 {product}
+ uintx CMSScheduleRemarkEdenSizeThreshold = 2097152 {product}
+ uintx CMSScheduleRemarkSamplingRatio = 5 {product}
+ double CMSSmallCoalSurplusPercent = 1.050000 {product}
+ double CMSSmallSplitSurplusPercent = 1.100000 {product}
+ bool CMSSplitIndexedFreeListBlocks = true {product}
+ intx CMSTriggerPermRatio = 80 {product}
+ intx CMSTriggerRatio = 80 {product}
+ bool CMSUseGCOverheadLimit = true {product}
+ intx CMSWaitDuration = 2000 {manageable}
+ uintx CMSWorkQueueDrainThreshold = 10 {product}
+ bool CMSYield = true {product}
+ uintx CMSYieldSleepCount = 0 {product}
+ intx CMSYoungGenPerWorker = 67108864 {pd product}
+ uintx CMS_FLSPadding = 1 {product}
+ uintx CMS_FLSWeight = 75 {product}
+ uintx CMS_SweepPadding = 1 {product}
+ uintx CMS_SweepTimerThresholdMillis = 10 {product}
+ uintx CMS_SweepWeight = 75 {product}
+ uintx CPUForCMSThread = 0 {diagnostic}
+ bool CheckJNICalls = false {product}
+ bool ClassUnloading = true {product}
+ intx ClearFPUAtPark = 0 {product}
+ bool ClipInlining = true {product}
+ uintx CodeCacheExpansionSize = 32768 {pd product}
+ uintx CodeCacheFlushingMinimumFreeSpace = 1536000 {product}
+ uintx CodeCacheMinimumFreeSpace = 512000 {product}
+ bool CollectGen0First = false {product}
+ bool CompactFields = true {product}
+ intx CompilationPolicyChoice = 0 {product}
+ intx CompilationRepeat = 0 {C1 product}
+ccstrlist CompileCommand = {product}
+ ccstr CompileCommandFile = {product}
+ccstrlist CompileOnly = {product}
+ intx CompileThreshold = 10000 {pd product}
+ bool CompilerThreadHintNoPreempt = true {product}
+ intx CompilerThreadPriority = -1 {product}
+ intx CompilerThreadStackSize = 0 {pd product}
+ uintx ConcGCThreads = 0 {product}
+ intx ConditionalMoveLimit = 3 {C2 pd product}
+ bool ConvertSleepToYield = true {pd product}
+ bool ConvertYieldToSleep = false {product}
+ bool CreateMinidumpOnCrash = false {product}
+ bool CycleTime = false {product}
+ bool DTraceAllocProbes = false {product}
+ bool DTraceMethodProbes = false {product}
+ bool DTraceMonitorProbes = false {product}
+ bool DeallocateHeapPages = true {product}
+ bool DeallocateStackPages = true {product}
+ uintx DeallocateStackPagesMinIntervalMs = 100 {product}
+ bool DebugContinuation = false {diagnostic}
+ bool DebugInlinedCalls = true {diagnostic}
+ bool DebugNonSafepoints = false {diagnostic}
+ uintx DefaultMaxRAMFraction = 4 {product}
+ intx DefaultThreadPriority = -1 {product}
+ bool DeferInitialCardMark = false {diagnostic}
+ intx DeferPollingPageLoopCount = -1 {product}
+ intx DeferThrSuspendLoopCount = 4000 {product}
+ bool DeoptimizeRandom = false {product}
+ bool DisableAttachMechanism = false {product}
+ bool DisableExplicitGC = false {product}
+ccstrlist DisableIntrinsic = {diagnostic}
+ bool DisplayVMOutput = true {diagnostic}
+ bool DisplayVMOutputToStderr = false {product}
+ bool DisplayVMOutputToStdout = false {product}
+ bool DoEscapeAnalysis = true {C2 product}
+ intx DominatorSearchLimit = 1000 {C2 diagnostic}
+ bool DontCompileHugeMethods = true {product}
+ bool DontYieldALot = false {pd product}
+ bool DumpSharedSpaces = false {product}
+ bool EagerXrunInit = false {product}
+ intx EliminateAllocationArraySizeLimit = 64 {C2 product}
+ bool EliminateAllocations = true {C2 product}
+ bool EliminateAutoBox = false {C2 diagnostic}
+ bool EliminateLocks = true {C2 product}
+ intx EmitSync = 0 {product}
+ bool EnableInvokeDynamic = true {diagnostic}
+ uintx ErgoHeapSizeLimit = 0 {product}
+ ccstr ErrorFile = {product}
+ ccstr ErrorReportServer = {product}
+ bool EstimateArgEscape = true {product}
+ intx EventLogLength = 2000 {product}
+ bool ExplicitGCInvokesConcurrent = false {product}
+ bool ExplicitGCInvokesConcurrentAndUnloadsClasses = false {product}
+ bool ExtendedDTraceProbes = false {product}
+ bool FLSAlwaysCoalesceLarge = false {product}
+ uintx FLSCoalescePolicy = 2 {product}
+ double FLSLargestBlockCoalesceProximity = 0.990000 {product}
+ bool FLSVerifyAllHeapReferences = false {diagnostic}
+ bool FLSVerifyIndexTable = false {diagnostic}
+ bool FLSVerifyLists = false {diagnostic}
+ bool FailOverToOldVerifier = true {product}
+ bool FastCardTableScan = true {product}
+ bool FastTLABRefill = true {product}
+ intx FenceInstruction = 0 {product}
+ intx FieldsAllocationStyle = 1 {product}
+ bool FilterSpuriousWakeups = true {product}
+ bool ForceNUMA = false {product}
+ bool ForceTimeHighResolution = false {product}
+ intx FreqInlineSize = 325 {pd product}
+ bool FullProfileOnReInterpret = true {diagnostic}
+ double G1ConcMarkStepDurationMillis = 10.000000 {product}
+ intx G1ConcRefinementGreenZone = 0 {product}
+ intx G1ConcRefinementRedZone = 0 {product}
+ intx G1ConcRefinementServiceIntervalMillis = 300 {product}
+ uintx G1ConcRefinementThreads = 0 {product}
+ intx G1ConcRefinementThresholdStep = 0 {product}
+ intx G1ConcRefinementYellowZone = 0 {product}
+ intx G1ConfidencePercent = 50 {product}
+ uintx G1HeapRegionSize = 0 {product}
+ intx G1MarkRegionStackSize = 1048576 {product}
+ bool G1PrintHeapRegions = false {diagnostic}
+ bool G1PrintRegionLivenessInfo = false {diagnostic}
+ intx G1RSetRegionEntries = 0 {product}
+ uintx G1RSetScanBlockSize = 64 {product}
+ intx G1RSetSparseRegionEntries = 0 {product}
+ intx G1RSetUpdatingPauseTimePercent = 10 {product}
+ intx G1RefProcDrainInterval = 10 {product}
+ uintx G1ReservePercent = 10 {product}
+ uintx G1SATBBufferEnqueueingThresholdPercent = 60 {product}
+ intx G1SATBBufferSize = 1024 {product}
+ bool G1SummarizeConcMark = false {diagnostic}
+ bool G1SummarizeRSetStats = false {diagnostic}
+ intx G1SummarizeRSetStatsPeriod = 0 {diagnostic}
+ bool G1TraceConcRefinement = false {diagnostic}
+ intx G1UpdateBufferSize = 256 {product}
+ bool G1UseAdaptiveConcRefinement = true {product}
+ uintx GCDrainStackTargetSize = 64 {product}
+ uintx GCHeapFreeLimit = 2 {product}
+ uintx GCLockerEdenExpansionPercent = 5 {product}
+ bool GCLockerInvokesConcurrent = false {product}
+ uintx GCLogFileSize = 0 {product}
+ bool GCOverheadReporting = false {product}
+ intx GCOverheadReportingPeriodMS = 100 {product}
+ bool GCParallelVerificationEnabled = true {diagnostic}
+ uintx GCPauseIntervalMillis = 0 {product}
+ uintx GCTaskTimeStampEntries = 200 {product}
+ uintx GCTimeLimit = 98 {product}
+ uintx GCTimeRatio = 99 {product}
+ bool GoogleAdjustGCThreads = false {product}
+ bool GoogleAgent = true {product}
+ ccstr GoogleAgentFlags = {product}
+ bool GoogleAgentWS = false {product}
+ ccstr GoogleAgentWSFlags = {product}
+ bool GoogleAlignInterpreterCalls = true {product}
+ bool GoogleDetailOmittedStackTrace = true {product}
+ uintx GoogleGCHeapFreeLimitPolicy = 2 {product}
+ bool GoogleGetMethodsInOrder = false {diagnostic}
+ bool GoogleGetMethodsReverse = false {diagnostic}
+ bool GoogleHeapInstrumentation = false {product}
+ bool GoogleHeapMonitor = true {product}
+ bool GoogleHeapSamplingInstrumentation = false {product}
+ bool GoogleInheritAltSigStack = false {product}
+ uintx GoogleLogRotationSize = 0 {diagnostic}
+ uintx GoogleMaxGarbageHeapzTraces = 200 {product}
+ bool GoogleScoping = false {product}
+ uintx GoogleSoftRefLRUPolicy = 0 {product}
+ uintx GoogleSoftRefLRUPolicyExcludedMB = 0 {product}
+ uintx GoogleUseConstantOMProvision = 0 {product}
+ bool GoogleUseLibunwind = false {pd product}
+ bool GoogleUseSSEForVM = false {product}
+ intx GuaranteedSafepointInterval = 1000 {diagnostic}
+ ccstr HPILibPath = {product}
+ uintx HeapBaseMinAddress = 2147483648 {pd product}
+ bool HeapDumpAfterFullGC = false {manageable}
+ bool HeapDumpBeforeFullGC = false {manageable}
+ bool HeapDumpOnOutOfMemoryError = false {manageable}
+ ccstr HeapDumpPath = {manageable}
+ uintx HeapFirstMaximumCompactionCount = 3 {product}
+ uintx HeapMaximumCompactionInterval = 20 {product}
+ bool HoistFinalLoads = false {product}
+ bool IgnoreUnrecognizedVMOptions = false {product}
+ uintx InitialCodeCacheSize = 2359296 {pd product}
+ bool InitialCompileFast = false {diagnostic}
+ bool InitialCompileReallyFast = false {diagnostic}
+ uintx InitialHeapSize := 67108864 {product}
+ uintx InitialRAMFraction = 64 {product}
+ uintx InitialSurvivorRatio = 8 {product}
+ intx InitialTenuringThreshold = 7 {product}
+ uintx InitiatingHeapOccupancyPercent = 45 {product}
+ bool Inline = true {product}
+ bool InlineGetCurrentThreadAllocatedMemory = false {product}
+ bool InlineMethodsWithNullUnloadedTypesInSignature = true {product}
+ intx InlineSmallCode = 1000 {pd product}
+ bool InsertMemBarAfterArraycopy = true {C2 product}
+ intx InteriorEntryAlignment = 4 {C2 pd product}
+ intx InterpreterProfilePercentage = 33 {product}
+ bool JNIDetachReleasesMonitors = true {product}
+ bool JavaMonitorsInStackTrace = true {product}
+ intx JavaPriority10_To_OSPriority = -1 {product}
+ intx JavaPriority1_To_OSPriority = -1 {product}
+ intx JavaPriority2_To_OSPriority = -1 {product}
+ intx JavaPriority3_To_OSPriority = -1 {product}
+ intx JavaPriority4_To_OSPriority = -1 {product}
+ intx JavaPriority5_To_OSPriority = -1 {product}
+ intx JavaPriority6_To_OSPriority = -1 {product}
+ intx JavaPriority7_To_OSPriority = -1 {product}
+ intx JavaPriority8_To_OSPriority = -1 {product}
+ intx JavaPriority9_To_OSPriority = -1 {product}
+ bool LIRFillDelaySlots = false {C1 pd product}
+ uintx LargePageHeapSizeThreshold = 134217728 {product}
+ ccstr LargePageMountPoint = {product}
+ uintx LargePageSizeInBytes = 0 {product}
+ bool LazyBootClassLoader = true {product}
+ bool LinkWellKnownClasses = false {diagnostic}
+ bool LogAHR = false {product}
+ bool LogCMSParallelSTWFullGC = false {product}
+ bool LogCompilation = false {diagnostic}
+ ccstr LogFile = {diagnostic}
+ bool LogGCOverheadLimit = false {product}
+ bool LogVMOutput = false {diagnostic}
+ bool LoopLimitCheck = true {C2 diagnostic}
+ intx LoopOptsCount = 43 {C2 product}
+ intx LoopUnrollLimit = 50 {C2 pd product}
+ intx LoopUnrollMin = 4 {C2 product}
+ bool LoopUnswitching = true {C2 product}
+ intx MallocVerifyInterval = 0 {diagnostic}
+ intx MallocVerifyStart = 0 {diagnostic}
+ bool ManagementServer = false {product}
+ uintx MarkStackSize = 32768 {product}
+ uintx MarkStackSizeMax = 4194304 {product}
+ intx MarkSweepAlwaysCompactCount = 4 {product}
+ uintx MarkSweepDeadRatio = 5 {product}
+ intx MaxBCEAEstimateLevel = 5 {product}
+ intx MaxBCEAEstimateSize = 150 {product}
+ intx MaxDirectMemorySize = -1 {product}
+ bool MaxFDLimit = true {product}
+ uintx MaxGCMinorPauseMillis = 4294967295 {product}
+ uintx MaxGCPauseMillis = 4294967295 {product}
+ uintx MaxHeapFreeRatio = 70 {product}
+ uintx MaxHeapSize := 1073741824 {product}
+ intx MaxInlineLevel = 9 {product}
+ intx MaxInlineSize = 35 {product}
+ intx MaxJavaStackTraceDepth = 1024 {product}
+ intx MaxJumpTableSize = 65000 {C2 product}
+ intx MaxJumpTableSparseness = 5 {C2 product}
+ intx MaxLabelRootDepth = 1100 {C2 product}
+ intx MaxLoopPad = 11 {C2 product}
+ uintx MaxNewSize = 4294901760 {product}
+ intx MaxNodeLimit = 65000 {C2 product}
+ uintx MaxPermHeapExpansion = 4194304 {product}
+ uintx MaxPermSize = 67108864 {pd product}
+ uint64_t MaxRAM = 0 {pd product}
+ uintx MaxRAMFraction = 4 {product}
+ intx MaxRecursiveInlineLevel = 1 {product}
+ intx MaxTenuringThreshold = 15 {product}
+ intx MaxTrivialSize = 6 {product}
+ bool MethodFlushing = true {product}
+ intx MethodHandlePushLimit = 3 {diagnostic}
+ intx MinCodeCacheFlushingInterval = 30 {product}
+ uintx MinHeapDeltaBytes = 131072 {product}
+ uintx MinHeapFreeRatio = 40 {product}
+ intx MinInliningThreshold = 250 {product}
+ intx MinJumpTableSize = 18 {C2 product}
+ uintx MinPermHeapExpansion = 262144 {product}
+ uintx MinRAMFraction = 2 {product}
+ uintx MinSurvivorRatio = 3 {product}
+ uintx MinTLABSize = 2048 {product}
+ bool MixedModeThreadDump = true {product}
+ intx MonitorBound = 0 {product}
+ bool MonitorInUseLists = false {product}
+ intx MultiArrayExpandLimit = 6 {C2 product}
+ bool MustCallLoadClassInternal = false {product}
+ intx NUMAChunkResizeWeight = 20 {product}
+ uintx NUMAInterleaveGranularity = 2097152 {product}
+ intx NUMAPageScanRate = 256 {product}
+ intx NUMASpaceResizeRate = 1073741824 {product}
+ bool NUMAStats = false {product}
+ intx NativeMonitorFlags = 0 {product}
+ intx NativeMonitorSpinLimit = 20 {product}
+ intx NativeMonitorTimeout = -1 {product}
+ bool NeedsDeoptSuspend = false {pd product}
+ bool NeverActAsServerClassMachine = false {pd product}
+ bool NeverTenure = false {product}
+ intx NewRatio = 2 {product}
+ uintx NewSize = 1048576 {product}
+ uintx NewSizeThreadIncrease = 4096 {pd product}
+ intx NmethodSweepCheckInterval = 5 {product}
+ intx NmethodSweepFraction = 4 {product}
+ intx NodeLimitFudgeFactor = 1000 {C2 product}
+ uintx NumberOfGCLogFiles = 0 {product}
+ intx NumberOfLoopInstrToAlign = 4 {C2 product}
+ bool ObjLifeTracking = false {product}
+ uintx ObjLifeTrackingCpuFraction = 100 {product}
+ uintx ObjLifetimeHistoBucketCount = 1024 {product}
+ bool ObjLifetimeHistoExportVarPerBucket = false {product}
+ uintx OldPLABSize = 1024 {product}
+ uintx OldPLABWeight = 50 {product}
+ uintx OldSize = 4194304 {product}
+ bool OmitStackTraceInFastThrow = true {product}
+ccstrlist OnError = {product}
+ccstrlist OnOutOfMemoryError = {product}
+ intx OnStackReplacePercentage = 140 {pd product}
+ bool OptimizeFill = false {C2 product}
+ bool OptimizeMethodHandles = true {diagnostic}
+ bool OptimizeStringConcat = false {C2 product}
+ bool OptoBundling = false {C2 pd product}
+ intx OptoLoopAlignment = 16 {pd product}
+ bool OptoScheduling = false {C2 pd product}
+ uintx PLABWeight = 75 {product}
+ bool PSChunkLargeArrays = true {product}
+ bool PSResizeByFreeRatio = false {product}
+ bool PSResizeByFreeRatioWithSystemGC = false {product}
+ intx ParGCArrayScanChunk = 50 {product}
+ intx ParGCCardsPerStrideChunk = 256 {diagnostic}
+ uintx ParGCDesiredObjsFromOverflowList = 20 {product}
+ intx ParGCStridesPerThread = 2 {diagnostic}
+ bool ParGCTrimOverflow = true {product}
+ bool ParGCUseLocalOverflow = false {product}
+ intx ParallelGCBufferWastePct = 10 {product}
+ bool ParallelGCRetainPLAB = false {diagnostic}
+ uintx ParallelGCThreads := 10 {product}
+ bool ParallelGCVerbose = false {product}
+ uintx ParallelOldDeadWoodLimiterMean = 50 {product}
+ uintx ParallelOldDeadWoodLimiterStdDev = 80 {product}
+ bool ParallelRefProcBalancingEnabled = true {product}
+ bool ParallelRefProcEnabled = false {product}
+ bool PartialPeelAtUnsignedTests = true {C2 product}
+ bool PartialPeelLoop = true {C2 product}
+ intx PartialPeelNewPhiDelta = 0 {C2 product}
+ bool PauseAtExit = false {diagnostic}
+ bool PauseAtStartup = false {diagnostic}
+ ccstr PauseAtStartupFile = {diagnostic}
+ uintx PausePadding = 1 {product}
+ intx PerBytecodeRecompilationCutoff = 200 {product}
+ intx PerBytecodeTrapLimit = 4 {product}
+ intx PerMethodRecompilationCutoff = 400 {product}
+ intx PerMethodTrapLimit = 100 {product}
+ bool PerfAllowAtExitRegistration = false {product}
+ bool PerfBypassFileSystemCheck = false {product}
+ intx PerfDataMemorySize = 32768 {product}
+ intx PerfDataSamplingInterval = 50 {product}
+ ccstr PerfDataSaveFile = {product}
+ bool PerfDataSaveToFile = false {product}
+ bool PerfDisableSharedMem = false {product}
+ intx PerfMaxStringConstLength = 1024 {product}
+ uintx PermGenPadding = 3 {product}
+ uintx PermMarkSweepDeadRatio = 20 {product}
+ uintx PermSize = 16777216 {pd product}
+ bool PostSpinYield = true {product}
+ intx PreBlockSpin = 10 {product}
+ intx PreInflateSpin = 10 {pd product}
+ bool PreSpinYield = false {product}
+ bool PreferInterpreterNativeStubs = false {pd product}
+ intx PrefetchCopyIntervalInBytes = -1 {product}
+ intx PrefetchFieldsAhead = -1 {product}
+ intx PrefetchScanIntervalInBytes = -1 {product}
+ bool PreserveAllAnnotations = false {product}
+ uintx PreserveMarkStackSize = 1024 {product}
+ uintx PretenureSizeThreshold = 0 {product}
+ bool PrintAdapterHandlers = false {diagnostic}
+ bool PrintAdaptiveSizePolicy = false {product}
+ bool PrintAssembly = false {diagnostic}
+ ccstr PrintAssemblyOptions = {diagnostic}
+ bool PrintBiasedLockingStatistics = false {diagnostic}
+ bool PrintCMSInitiationCause = false {product}
+ bool PrintCMSInitiationStatistics = false {product}
+ intx PrintCMSStatistics = 0 {product}
+ bool PrintCardTableStats = false {product}
+ bool PrintClassHistogram = false {manageable}
+ bool PrintClassHistogramAfterFullGC = false {manageable}
+ bool PrintClassHistogramBeforeFullGC = false {manageable}
+ bool PrintClassLoadingDateStamps = false {product}
+ bool PrintClassLoadingTimeStamps = false {product}
+ bool PrintCommandLineFlags = false {product}
+ bool PrintCompilation = false {product}
+ bool PrintCompressedOopsMode = false {diagnostic}
+ bool PrintConcurrentLocks = false {manageable}
+ bool PrintDTraceDOF = false {diagnostic}
+ intx PrintFLSCensus = 0 {product}
+ intx PrintFLSStatistics = 0 {product}
+ bool PrintFlagsFinal := true {product}
+ bool PrintFlagsInitial = false {product}
+ bool PrintFullGCPhaseTimes = false {product}
+ bool PrintGC = false {manageable}
+ bool PrintGCApplicationConcurrentTime = false {product}
+ bool PrintGCApplicationStoppedTime = false {product}
+ bool PrintGCDateStamps = false {manageable}
+ bool PrintGCDetails = false {manageable}
+ bool PrintGCTaskTimeStamps = false {product}
+ bool PrintGCTimeStamps = false {manageable}
+ bool PrintHeapAtGC = false {product rw}
+ bool PrintHeapAtGCExtended = false {product rw}
+ bool PrintHeapAtSIGBREAK = true {product}
+ bool PrintInlining = false {diagnostic}
+ bool PrintInterpreter = false {diagnostic}
+ bool PrintIntrinsics = false {diagnostic}
+ bool PrintJNIGCStalls = false {product}
+ bool PrintJNIResolving = false {product}
+ bool PrintMethodHandleStubs = false {diagnostic}
+ bool PrintNMethods = false {diagnostic}
+ bool PrintNativeNMethods = false {diagnostic}
+ bool PrintOldPLAB = false {product}
+ bool PrintOopAddress = false {product}
+ bool PrintPLAB = false {product}
+ bool PrintParallelOldGCPhaseTimes = false {product}
+ bool PrintPreciseBiasedLockingStatistics = false {C2 diagnostic}
+ bool PrintPromotionFailure = false {product}
+ bool PrintReferenceGC = false {product}
+ bool PrintRevisitStats = false {product}
+ bool PrintSafepointStatistics = false {product}
+ intx PrintSafepointStatisticsCount = 300 {product}
+ intx PrintSafepointStatisticsTimeout = -1 {product}
+ bool PrintSharedSpaces = false {product}
+ bool PrintSignatureHandlers = false {diagnostic}
+ bool PrintStubCode = false {diagnostic}
+ bool PrintTLAB = false {product}
+ bool PrintTenuringDistribution = false {product}
+ bool PrintTieredEvents = false {product}
+ bool PrintVMOptions = false {product}
+ bool PrintVMQWaitTime = false {product}
+ bool PrintWarnings = true {product}
+ uintx ProcessDistributionStride = 4 {product}
+ bool ProfileDynamicTypes = true {diagnostic}
+ bool ProfileInterpreter = true {pd product}
+ bool ProfileIntervals = false {product}
+ intx ProfileIntervalsTicks = 100 {product}
+ intx ProfileMaturityPercentage = 20 {product}
+ bool ProfileVM = false {product}
+ bool ProfilerPrintByteCodeStatistics = false {product}
+ bool ProfilerRecordPC = false {product}
+ uintx PromotedPadding = 3 {product}
+ intx QueuedAllocationWarningCount = 0 {product}
+ uintx RamLimit = 0 {product}
+ bool RangeCheckElimination = true {product}
+ bool RangeLimitCheck = true {C2 diagnostic}
+ intx ReadPrefetchInstr = 0 {product}
+ intx ReadSpinIterations = 100 {product}
+ bool ReassociateInvariants = true {C2 product}
+ bool ReduceBulkZeroing = true {C2 product}
+ bool ReduceFieldZeroing = true {C2 product}
+ bool ReduceInitialCardMarks = true {C2 product}
+ bool ReduceSignalUsage = false {product}
+ intx RefDiscoveryPolicy = 0 {product}
+ bool ReflectionWrapResolutionErrors = true {product}
+ bool RegisterFinalizersAtInit = true {product}
+ bool RelaxAccessControlCheck = false {product}
+ bool RequireSharedSpaces = false {product}
+ uintx ReservedCodeCacheSize = 50331648 {pd product}
+ bool ResizeOldPLAB = true {product}
+ bool ResizePLAB = true {product}
+ bool ResizeTLAB = true {pd product}
+ bool RestoreMXCSROnJNICalls = false {product}
+ bool RewriteBytecodes = true {pd product}
+ bool RewriteFrequentPairs = true {pd product}
+ intx SafepointPollOffset = 256 {C1 pd product}
+ intx SafepointSpinBeforeYield = 2000 {product}
+ bool SafepointTimeout = false {product}
+ intx SafepointTimeoutDelay = 10000 {product}
+ bool ScavengeBeforeFullGC = true {product}
+ intx ScavengeRootsInCode = 1 {diagnostic}
+ intx SelfDestructTimer = 0 {product}
+ bool SerializeVMOutput = true {diagnostic}
+ bool ShareCMSMarkBitMapWithParalleSTWFullGC = true {product}
+ uintx SharedDummyBlockSize = 536870912 {product}
+ uintx SharedMiscCodeSize = 4194304 {product}
+ uintx SharedMiscDataSize = 4194304 {product}
+ bool SharedOptimizeColdStart = true {diagnostic}
+ uintx SharedReadOnlySize = 10485760 {product}
+ uintx SharedReadWriteSize = 12582912 {product}
+ bool SharedSkipVerify = false {diagnostic}
+ bool ShowMessageBoxOnError = false {product}
+ intx SoftRefLRUPolicyMSPerMB = 1000 {product}
+ bool SplitIfBlocks = true {product}
+ intx StackRedPages = 1 {pd product}
+ intx StackShadowPages = 3 {pd product}
+ bool StackTraceInThrowable = true {product}
+ intx StackYellowPages = 2 {pd product}
+ bool StartAttachListener = false {product}
+ bool StartSuspended = false {product}
+ intx StarvationMonitorInterval = 200 {product}
+ bool StressLdcRewrite = false {product}
+ bool StressTieredRuntime = false {product}
+ uintx StringTableSize = 1009 {product}
+ bool SuppressFatalErrorMessage = false {product}
+ uintx SurvivorPadding = 3 {product}
+ intx SurvivorRatio = 8 {product}
+ intx SuspendRetryCount = 50 {product}
+ intx SuspendRetryDelay = 5 {product}
+ intx SyncFlags = 0 {product}
+ ccstr SyncKnobs = {product}
+ intx SyncVerbose = 0 {product}
+ uintx TLABAllocationWeight = 35 {product}
+ uintx TLABRefillWasteFraction = 64 {product}
+ uintx TLABSize = 0 {product}
+ bool TLABStats = true {product}
+ uintx TLABWasteIncrement = 4 {product}
+ uintx TLABWasteTargetPercent = 1 {product}
+ intx TargetPLABWastePct = 10 {product}
+ intx TargetSurvivorRatio = 50 {product}
+ uintx TenuredGenerationSizeIncrement = 20 {product}
+ uintx TenuredGenerationSizeSupplement = 80 {product}
+ uintx TenuredGenerationSizeSupplementDecay = 2 {product}
+ intx ThreadPriorityPolicy = 0 {product}
+ bool ThreadPriorityVerbose = false {product}
+ uintx ThreadSafetyMargin = 52428800 {product}
+ intx ThreadStackSize = 320 {pd product}
+ uintx ThresholdTolerance = 10 {product}
+ intx Tier0BackedgeNotifyFreqLog = 10 {product}
+ intx Tier0InvokeNotifyFreqLog = 7 {product}
+ intx Tier0ProfilingStartPercentage = 200 {product}
+ intx Tier1FreqInlineSize = 35 {C2 product}
+ intx Tier1Inline = 0 {C2 product}
+ intx Tier1LoopOptsCount = 0 {C2 product}
+ intx Tier1MaxInlineSize = 8 {C2 product}
+ intx Tier2BackEdgeThreshold = 0 {product}
+ intx Tier2BackedgeNotifyFreqLog = 14 {product}
+ intx Tier2CompileThreshold = 0 {product}
+ intx Tier2InvokeNotifyFreqLog = 11 {product}
+ intx Tier3BackEdgeThreshold = 7000 {product}
+ intx Tier3BackedgeNotifyFreqLog = 13 {product}
+ intx Tier3CompileThreshold = 2000 {product}
+ intx Tier3DelayOff = 2 {product}
+ intx Tier3DelayOn = 5 {product}
+ intx Tier3InvocationThreshold = 200 {product}
+ intx Tier3InvokeNotifyFreqLog = 10 {product}
+ intx Tier3LoadFeedback = 5 {product}
+ intx Tier3MinInvocationThreshold = 100 {product}
+ intx Tier4BackEdgeThreshold = 40000 {product}
+ intx Tier4CompileThreshold = 15000 {product}
+ intx Tier4InvocationThreshold = 5000 {product}
+ intx Tier4LoadFeedback = 3 {product}
+ intx Tier4MinInvocationThreshold = 600 {product}
+ bool TieredCompilation = false {pd product}
+ intx TieredCompileTaskTimeout = 50 {product}
+ intx TieredRateUpdateMaxTime = 25 {product}
+ intx TieredRateUpdateMinTime = 1 {product}
+ intx TieredStopAtLevel = 4 {product}
+ bool TimeLinearScan = false {C1 product}
+ bool TraceBiasedLocking = false {product}
+ bool TraceClassLoading = false {product rw}
+ bool TraceClassLoadingPreorder = false {product}
+ bool TraceClassResolution = false {product}
+ bool TraceClassUnloading = false {product rw}
+ bool TraceCompileTriggered = false {diagnostic}
+ bool TraceGen0Time = false {product}
+ bool TraceGen1Time = false {product}
+ ccstr TraceJVMTI = {product}
+ bool TraceJVMTIObjectTagging = false {diagnostic}
+ bool TraceLoaderConstraints = false {product rw}
+ bool TraceMonitorInflation = false {product}
+ bool TraceNMethodInstalls = false {diagnostic}
+ bool TraceOSRBreakpoint = false {diagnostic}
+ bool TraceParallelOldGCTasks = false {product}
+ intx TraceRedefineClasses = 0 {product}
+ bool TraceRedundantCompiles = false {diagnostic}
+ bool TraceSafepointCleanupTime = false {product}
+ bool TraceSuperWord = false {C2 product}
+ bool TraceSuspendWaitFailures = false {product}
+ bool TraceTriggers = false {diagnostic}
+ intx TrackedInitializationLimit = 50 {C2 product}
+ bool TransmitErrorReport = false {product}
+ intx TypeProfileMajorReceiverPercent = 90 {product}
+ intx TypeProfileWidth = 2 {product}
+ intx UnguardOnExecutionViolation = 0 {product}
+ bool UnlinkSymbolsALot = false {product}
+ bool UnlockDiagnosticVMOptions = true {diagnostic}
+ bool UnrollLimitCheck = true {C2 diagnostic}
+ bool UnsyncloadClass = false {diagnostic}
+ bool Use486InstrsOnly = false {product}
+ bool UseAdaptiveGCBoundary = false {product}
+ bool UseAdaptiveGenerationSizePolicyAtMajorCollection = true {product}
+ bool UseAdaptiveGenerationSizePolicyAtMinorCollection = true {product}
+ bool UseAdaptiveNUMAChunkSizing = true {product}
+ bool UseAdaptiveSizeDecayMajorGCCost = true {product}
+ bool UseAdaptiveSizePolicy = true {product}
+ bool UseAdaptiveSizePolicyFootprintGoal = true {product}
+ bool UseAdaptiveSizePolicyWithSystemGC = false {product}
+ bool UseAddressNop = true {product}
+ bool UseAltSigs = false {product}
+ bool UseAutoGCSelectPolicy = false {product}
+ bool UseBiasedLocking = true {product}
+ bool UseBimorphicInlining = true {C2 product}
+ bool UseBlockCopy = false {product}
+ bool UseBlockZeroing = false {product}
+ bool UseBoundThreads = true {product}
+ bool UseCBCond = false {product}
+ bool UseCMSBestFit = true {product}
+ bool UseCMSCollectionPassing = true {product}
+ bool UseCMSCompactAtFullCollection = true {product}
+ bool UseCMSInitiatingOccupancyOnly = false {product}
+ bool UseCMSParallelSTWFullGC = false {product}
+ bool UseCodeCacheFlushing = false {product}
+ bool UseCompiler = true {product}
+ bool UseCompilerSafepoints = true {product}
+ bool UseConcMarkSweepGC = false {product}
+ bool UseCondCardMark = false {product}
+ bool UseCountLeadingZerosInstruction = false {product}
+ bool UseCounterDecay = true {product}
+ bool UseDivMod = true {C2 product}
+ bool UseFPUForSpilling = false {C2 product}
+ bool UseFastAccessorMethods = false {product}
+ bool UseFastEmptyMethods = false {product}
+ bool UseFastJNIAccessors = true {product}
+ bool UseG1GC = false {product}
+ bool UseGCLogFileRotation = false {product}
+ bool UseGCOverheadLimit = true {product}
+ bool UseGCTaskAffinity = false {product}
+ bool UseHeavyMonitors = false {product}
+ bool UseHugeTLBFS = false {product}
+ bool UseIncDec = true {diagnostic}
+ bool UseInlineCaches = true {product}
+ bool UseInterpreter = true {product}
+ bool UseJumpTables = true {C2 product}
+ bool UseLWPSynchronization = true {product}
+ bool UseLargePages = false {pd product}
+ bool UseLargePagesIndividualAllocation = false {pd product}
+ bool UseLinuxPosixThreadCPUClocks = false {product}
+ bool UseLoopCounter = true {product}
+ bool UseLoopPredicate = true {C2 product}
+ bool UseMaximumCompactionOnSystemGC = true {product}
+ bool UseMembar = false {pd product}
+ bool UseNUMA = false {product}
+ bool UseNUMAInterleaving = false {product}
+ bool UseNewCode = false {diagnostic}
+ bool UseNewCode2 = false {diagnostic}
+ bool UseNewCode3 = false {diagnostic}
+ bool UseNewLongLShift = false {product}
+ bool UseNiagaraInstrs = false {product}
+ bool UseOSErrorReporting = false {pd product}
+ bool UseOldInlining = true {C2 product}
+ bool UseOnStackReplacement = true {pd product}
+ bool UseOnlyInlinedBimorphic = true {C2 product}
+ bool UseOprofile = false {product}
+ bool UseOptoBiasInlining = true {C2 product}
+ bool UsePPCLWSYNC = true {product}
+ bool UsePSAdaptiveSurvivorSizePolicy = true {product}
+ bool UseParNewGC = false {product}
+ bool UseParallelGC := true {product}
+ bool UseParallelOldGC = false {product}
+ bool UsePerfData = true {product}
+ bool UsePopCountInstruction = true {product}
+ bool UseRDPCForConstantTableBase = false {C2 product}
+ bool UseRicochetFrames = true {diagnostic}
+ bool UseSHM = false {product}
+ intx UseSSE = 4 {product}
+ bool UseSSE42Intrinsics = true {product}
+ bool UseSeparateVSpacesInYoungGen = true {product}
+ bool UseSerialGC = false {product}
+ bool UseSharedSpaces = false {product}
+ bool UseSignalChaining = true {product}
+ bool UseSpinning = false {product}
+ bool UseSplitVerifier = true {product}
+ bool UseStoreImmI16 = false {product}
+ bool UseStringCache = false {product}
+ bool UseSuperWord = true {C2 product}
+ bool UseTLAB = true {pd product}
+ bool UseThreadPriorities = true {pd product}
+ bool UseTypeProfile = true {product}
+ bool UseUnalignedLoadStores = true {product}
+ intx UseVIS = 99 {product}
+ bool UseVMInterruptibleIO = false {product}
+ bool UseVectoredExceptions = false {pd product}
+ bool UseXMMForArrayCopy = true {product}
+ bool UseXmmI2D = false {product}
+ bool UseXmmI2F = false {product}
+ bool UseXmmLoadAndClearUpper = true {product}
+ bool UseXmmRegToRegMoveAll = true {product}
+ bool VMThreadHintNoPreempt = false {product}
+ intx VMThreadPriority = -1 {product}
+ intx VMThreadStackSize = 512 {pd product}
+ intx ValueMapInitialSize = 11 {C1 product}
+ intx ValueMapMaxLoopSize = 8 {C1 product}
+ intx ValueSearchLimit = 1000 {C2 product}
+ bool VerifyAfterGC = false {diagnostic}
+ bool VerifyBeforeExit = false {diagnostic}
+ bool VerifyBeforeGC = false {diagnostic}
+ bool VerifyBeforeIteration = false {diagnostic}
+ bool VerifyDuringGC = false {diagnostic}
+ intx VerifyGCLevel = 0 {diagnostic}
+ uintx VerifyGCStartAt = 0 {diagnostic}
+ bool VerifyMergedCPBytecodes = true {product}
+ bool VerifyMethodHandles = false {diagnostic}
+ bool VerifyObjectStartArray = true {diagnostic}
+ bool VerifyRememberedSets = false {diagnostic}
+ intx WorkAroundNPTLTimedWaitHang = 1 {product}
+ uintx YoungGenerationSizeIncrement = 20 {product}
+ uintx YoungGenerationSizeSupplement = 80 {product}
+ uintx YoungGenerationSizeSupplementDecay = 8 {product}
+ uintx YoungPLABSize = 4096 {product}
+ bool ZeroTLAB = false {product}
+ intx hashCode = 0 {product} \ No newline at end of file
diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt
new file mode 100644
index 0000000..b3afcf1
--- /dev/null
+++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt
@@ -0,0 +1,200 @@
+2013-02-11T20:15:26.706-0600: 0.098: [GC 1316K->576K(62848K), 0.0014240 secs]
+2013-02-11T20:15:26.708-0600: 0.099: [Full GC 576K->486K(62848K), 0.0044860 secs]
+2013-02-11T20:15:26.713-0600: 0.104: [GC 486K->486K(62848K), 0.0005000 secs]
+2013-02-11T20:15:26.713-0600: 0.105: [Full GC 486K->486K(62848K), 0.0039840 secs]
+2013-02-11T20:15:26.717-0600: 0.109: [GC 486K->486K(62848K), 0.0007650 secs]
+2013-02-11T20:15:26.718-0600: 0.110: [Full GC 486K->486K(62848K), 0.0038350 secs]
+2013-02-11T20:15:26.722-0600: 0.113: [GC 486K->486K(62848K), 0.0005430 secs]
+2013-02-11T20:15:26.723-0600: 0.114: [Full GC 486K->486K(62848K), 0.0045480 secs]
+2013-02-11T20:15:26.727-0600: 0.119: [GC 486K->486K(62848K), 0.0003950 secs]
+2013-02-11T20:15:26.728-0600: 0.119: [Full GC 486K->486K(62848K), 0.0036570 secs]
+2013-02-11T20:15:26.731-0600: 0.123: [GC 486K->486K(62848K), 0.0005850 secs]
+2013-02-11T20:15:26.732-0600: 0.123: [Full GC 486K->486K(62848K), 0.0036650 secs]
+2013-02-11T20:15:26.736-0600: 0.127: [GC 486K->486K(62848K), 0.0004640 secs]
+2013-02-11T20:15:26.736-0600: 0.128: [Full GC 486K->486K(62848K), 0.0035990 secs]
+2013-02-11T20:15:26.740-0600: 0.131: [GC 486K->486K(62848K), 0.0005500 secs]
+2013-02-11T20:15:26.740-0600: 0.132: [Full GC 486K->486K(62848K), 0.0038150 secs]
+2013-02-11T20:15:26.744-0600: 0.136: [GC 486K->486K(62848K), 0.0004460 secs]
+2013-02-11T20:15:26.745-0600: 0.136: [Full GC 486K->486K(62848K), 0.0036000 secs]
+2013-02-11T20:15:26.748-0600: 0.140: [GC 486K->486K(62848K), 0.0003120 secs]
+2013-02-11T20:15:26.749-0600: 0.140: [Full GC 486K->486K(62848K), 0.0035820 secs]
+2013-02-11T20:15:26.752-0600: 0.144: [GC 486K->486K(62848K), 0.0003340 secs]
+2013-02-11T20:15:26.753-0600: 0.144: [Full GC 486K->486K(62848K), 0.0036170 secs]
+2013-02-11T20:15:26.756-0600: 0.148: [GC 486K->486K(62848K), 0.0004370 secs]
+2013-02-11T20:15:26.757-0600: 0.148: [Full GC 486K->486K(62848K), 0.0036000 secs]
+2013-02-11T20:15:26.761-0600: 0.152: [GC 486K->486K(62848K), 0.0003760 secs]
+2013-02-11T20:15:26.761-0600: 0.152: [Full GC 486K->486K(62848K), 0.0035950 secs]
+2013-02-11T20:15:26.765-0600: 0.156: [GC 486K->486K(62848K), 0.0004000 secs]
+2013-02-11T20:15:26.765-0600: 0.157: [Full GC 486K->486K(62848K), 0.0035760 secs]
+2013-02-11T20:15:26.769-0600: 0.160: [GC 486K->486K(62848K), 0.0003480 secs]
+2013-02-11T20:15:26.769-0600: 0.161: [Full GC 486K->486K(62848K), 0.0035710 secs]
+2013-02-11T20:15:26.773-0600: 0.164: [GC 486K->486K(62848K), 0.0003370 secs]
+2013-02-11T20:15:26.773-0600: 0.164: [Full GC 486K->486K(62848K), 0.0035910 secs]
+2013-02-11T20:15:26.777-0600: 0.168: [GC 486K->486K(62848K), 0.0003840 secs]
+2013-02-11T20:15:26.777-0600: 0.169: [Full GC 486K->486K(62848K), 0.0035760 secs]
+2013-02-11T20:15:26.781-0600: 0.172: [GC 486K->486K(62848K), 0.0003560 secs]
+2013-02-11T20:15:26.781-0600: 0.173: [Full GC 486K->486K(62848K), 0.0035920 secs]
+2013-02-11T20:15:26.785-0600: 0.176: [GC 486K->486K(62848K), 0.0003520 secs]
+2013-02-11T20:15:26.785-0600: 0.177: [Full GC 486K->486K(62848K), 0.0035650 secs]
+2013-02-11T20:15:26.789-0600: 0.180: [GC 486K->486K(62848K), 0.0004280 secs]
+2013-02-11T20:15:26.789-0600: 0.181: [Full GC 486K->486K(62848K), 0.0035460 secs]
+2013-02-11T20:15:26.793-0600: 0.184: [GC 815K->502K(62848K), 0.0003340 secs]
+2013-02-11T20:15:26.793-0600: 0.185: [Full GC 502K->486K(62848K), 0.0037010 secs]
+2013-02-11T20:15:26.797-0600: 0.188: [GC 486K->486K(62848K), 0.0004180 secs]
+2013-02-11T20:15:26.797-0600: 0.189: [Full GC 486K->486K(62848K), 0.0035310 secs]
+2013-02-11T20:15:26.801-0600: 0.193: [GC 486K->486K(62848K), 0.0003840 secs]
+2013-02-11T20:15:26.801-0600: 0.193: [Full GC 486K->486K(62848K), 0.0035550 secs]
+2013-02-11T20:15:26.805-0600: 0.197: [GC 486K->486K(62848K), 0.0003240 secs]
+2013-02-11T20:15:26.805-0600: 0.197: [Full GC 486K->486K(62848K), 0.0035560 secs]
+2013-02-11T20:15:26.809-0600: 0.201: [GC 486K->486K(62848K), 0.0002990 secs]
+2013-02-11T20:15:26.809-0600: 0.201: [Full GC 486K->486K(62848K), 0.0035600 secs]
+2013-02-11T20:15:26.813-0600: 0.204: [GC 486K->486K(62848K), 0.0002760 secs]
+2013-02-11T20:15:26.813-0600: 0.205: [Full GC 486K->486K(62848K), 0.0035470 secs]
+2013-02-11T20:15:26.817-0600: 0.208: [GC 486K->486K(62848K), 0.0002980 secs]
+2013-02-11T20:15:26.817-0600: 0.209: [Full GC 486K->486K(62848K), 0.0035440 secs]
+2013-02-11T20:15:26.821-0600: 0.212: [GC 486K->486K(62848K), 0.0002770 secs]
+2013-02-11T20:15:26.821-0600: 0.213: [Full GC 486K->486K(62848K), 0.0035510 secs]
+2013-02-11T20:15:26.825-0600: 0.216: [GC 486K->486K(62848K), 0.0002340 secs]
+2013-02-11T20:15:26.825-0600: 0.216: [Full GC 486K->486K(62848K), 0.0035280 secs]
+2013-02-11T20:15:26.828-0600: 0.220: [GC 486K->486K(62848K), 0.0002440 secs]
+2013-02-11T20:15:26.829-0600: 0.220: [Full GC 486K->486K(62848K), 0.0035330 secs]
+2013-02-11T20:15:26.832-0600: 0.224: [GC 486K->486K(62848K), 0.0003140 secs]
+2013-02-11T20:15:26.833-0600: 0.224: [Full GC 486K->486K(62848K), 0.0035600 secs]
+2013-02-11T20:15:26.836-0600: 0.228: [GC 486K->486K(62848K), 0.0002780 secs]
+2013-02-11T20:15:26.837-0600: 0.228: [Full GC 486K->486K(62848K), 0.0035240 secs]
+2013-02-11T20:15:26.840-0600: 0.232: [GC 486K->486K(62848K), 0.0003130 secs]
+2013-02-11T20:15:26.840-0600: 0.232: [Full GC 486K->486K(62848K), 0.0035350 secs]
+2013-02-11T20:15:26.844-0600: 0.236: [GC 815K->502K(62848K), 0.0002610 secs]
+2013-02-11T20:15:26.844-0600: 0.236: [Full GC 502K->487K(62848K), 0.0035270 secs]
+2013-02-11T20:15:26.848-0600: 0.239: [GC 487K->487K(62848K), 0.0002930 secs]
+2013-02-11T20:15:26.848-0600: 0.240: [Full GC 487K->487K(62848K), 0.0035470 secs]
+2013-02-11T20:15:26.852-0600: 0.243: [GC 487K->487K(62848K), 0.0002730 secs]
+2013-02-11T20:15:26.852-0600: 0.244: [Full GC 487K->487K(62848K), 0.0035470 secs]
+2013-02-11T20:15:26.856-0600: 0.247: [GC 487K->487K(62848K), 0.0003420 secs]
+2013-02-11T20:15:26.856-0600: 0.248: [Full GC 487K->487K(62848K), 0.0035390 secs]
+2013-02-11T20:15:26.860-0600: 0.251: [GC 487K->487K(62848K), 0.0002320 secs]
+2013-02-11T20:15:26.860-0600: 0.252: [Full GC 487K->487K(62848K), 0.0035050 secs]
+2013-02-11T20:15:26.864-0600: 0.255: [GC 487K->487K(62848K), 0.0002880 secs]
+2013-02-11T20:15:26.864-0600: 0.255: [Full GC 487K->487K(62848K), 0.0035190 secs]
+2013-02-11T20:15:26.867-0600: 0.259: [GC 487K->487K(62848K), 0.0002650 secs]
+2013-02-11T20:15:26.868-0600: 0.259: [Full GC 487K->487K(62848K), 0.0034950 secs]
+2013-02-11T20:15:26.871-0600: 0.263: [GC 487K->487K(62848K), 0.0003850 secs]
+2013-02-11T20:15:26.872-0600: 0.263: [Full GC 487K->487K(62848K), 0.0035450 secs]
+2013-02-11T20:15:26.875-0600: 0.267: [GC 487K->487K(62848K), 0.0003630 secs]
+2013-02-11T20:15:26.876-0600: 0.267: [Full GC 487K->487K(62848K), 0.0035380 secs]
+2013-02-11T20:15:26.879-0600: 0.271: [GC 487K->487K(62848K), 0.0002800 secs]
+2013-02-11T20:15:26.879-0600: 0.271: [Full GC 487K->487K(62848K), 0.0035340 secs]
+2013-02-11T20:15:26.883-0600: 0.275: [GC 487K->487K(62848K), 0.0002580 secs]
+2013-02-11T20:15:26.883-0600: 0.275: [Full GC 487K->487K(62848K), 0.0035550 secs]
+2013-02-11T20:15:26.887-0600: 0.278: [GC 487K->487K(62848K), 0.0003880 secs]
+2013-02-11T20:15:26.887-0600: 0.279: [Full GC 487K->487K(62848K), 0.0035690 secs]
+2013-02-11T20:15:26.891-0600: 0.283: [GC 487K->487K(62848K), 0.0003040 secs]
+2013-02-11T20:15:26.891-0600: 0.283: [Full GC 487K->487K(62848K), 0.0035720 secs]
+2013-02-11T20:15:26.895-0600: 0.286: [GC 487K->487K(62848K), 0.0002750 secs]
+2013-02-11T20:15:26.895-0600: 0.287: [Full GC 487K->487K(62848K), 0.0035700 secs]
+2013-02-11T20:15:26.899-0600: 0.290: [GC 816K->503K(62848K), 0.0002830 secs]
+2013-02-11T20:15:26.899-0600: 0.291: [Full GC 503K->487K(62848K), 0.0037630 secs]
+2013-02-11T20:15:26.903-0600: 0.295: [GC 487K->487K(62848K), 0.0002890 secs]
+2013-02-11T20:15:26.903-0600: 0.295: [Full GC 487K->487K(62848K), 0.0037330 secs]
+2013-02-11T20:15:26.907-0600: 0.299: [GC 487K->487K(62848K), 0.0002580 secs]
+2013-02-11T20:15:26.907-0600: 0.299: [Full GC 487K->487K(62848K), 0.0035480 secs]
+2013-02-11T20:15:26.911-0600: 0.303: [GC 816K->503K(62848K), 0.0003540 secs]
+2013-02-11T20:15:26.911-0600: 0.303: [Full GC 503K->487K(62848K), 0.0035610 secs]
+2013-02-11T20:15:26.915-0600: 0.307: [GC 487K->487K(62848K), 0.0004160 secs]
+2013-02-11T20:15:26.915-0600: 0.307: [Full GC 487K->487K(62848K), 0.0035390 secs]
+2013-02-11T20:15:26.919-0600: 0.311: [GC 816K->519K(62848K), 0.0003650 secs]
+2013-02-11T20:15:26.920-0600: 0.311: [Full GC 519K->487K(62848K), 0.0035430 secs]
+2013-02-11T20:15:26.923-0600: 0.315: [GC 487K->487K(62848K), 0.0003340 secs]
+2013-02-11T20:15:26.924-0600: 0.315: [Full GC 487K->487K(62848K), 0.0035600 secs]
+2013-02-11T20:15:26.927-0600: 0.319: [GC 816K->503K(62848K), 0.0003750 secs]
+2013-02-11T20:15:26.928-0600: 0.319: [Full GC 503K->487K(62848K), 0.0035330 secs]
+2013-02-11T20:15:26.931-0600: 0.323: [GC 487K->487K(62848K), 0.0003790 secs]
+2013-02-11T20:15:26.932-0600: 0.323: [Full GC 487K->487K(62848K), 0.0035490 secs]
+2013-02-11T20:15:26.935-0600: 0.327: [GC 816K->503K(62848K), 0.0002930 secs]
+2013-02-11T20:15:26.936-0600: 0.327: [Full GC 503K->487K(62848K), 0.0035400 secs]
+2013-02-11T20:15:26.939-0600: 0.331: [GC 487K->487K(62848K), 0.0002870 secs]
+2013-02-11T20:15:26.939-0600: 0.331: [Full GC 487K->487K(62848K), 0.0035630 secs]
+2013-02-11T20:15:26.943-0600: 0.335: [GC 487K->487K(62848K), 0.0003790 secs]
+2013-02-11T20:15:26.943-0600: 0.335: [Full GC 487K->487K(62848K), 0.0035950 secs]
+2013-02-11T20:15:26.947-0600: 0.339: [GC 816K->503K(62848K), 0.0003640 secs]
+2013-02-11T20:15:26.948-0600: 0.339: [Full GC 503K->487K(62848K), 0.0038120 secs]
+2013-02-11T20:15:26.951-0600: 0.343: [GC 487K->487K(62848K), 0.0003520 secs]
+2013-02-11T20:15:26.952-0600: 0.343: [Full GC 487K->487K(62848K), 0.0035580 secs]
+2013-02-11T20:15:26.955-0600: 0.347: [GC 487K->487K(62848K), 0.0003620 secs]
+2013-02-11T20:15:26.956-0600: 0.347: [Full GC 487K->487K(62848K), 0.0035290 secs]
+2013-02-11T20:15:26.959-0600: 0.351: [GC 487K->487K(62848K), 0.0003630 secs]
+2013-02-11T20:15:26.960-0600: 0.351: [Full GC 487K->487K(62848K), 0.0035080 secs]
+2013-02-11T20:15:26.963-0600: 0.355: [GC 487K->487K(62848K), 0.0003310 secs]
+2013-02-11T20:15:26.964-0600: 0.355: [Full GC 487K->482K(62848K), 0.0038060 secs]
+2013-02-11T20:15:26.968-0600: 0.359: [GC 482K->482K(62848K), 0.0003650 secs]
+2013-02-11T20:15:26.968-0600: 0.359: [Full GC 482K->482K(62848K), 0.0035340 secs]
+2013-02-11T20:15:26.972-0600: 0.363: [GC 482K->482K(62848K), 0.0003300 secs]
+2013-02-11T20:15:26.972-0600: 0.363: [Full GC 482K->482K(62848K), 0.0035340 secs]
+2013-02-11T20:15:26.976-0600: 0.367: [GC 482K->482K(62848K), 0.0003220 secs]
+2013-02-11T20:15:26.976-0600: 0.367: [Full GC 482K->482K(62848K), 0.0035230 secs]
+2013-02-11T20:15:26.979-0600: 0.371: [GC 482K->482K(62848K), 0.0003220 secs]
+2013-02-11T20:15:26.980-0600: 0.371: [Full GC 482K->482K(62848K), 0.0035200 secs]
+2013-02-11T20:15:26.983-0600: 0.375: [GC 482K->482K(62848K), 0.0003030 secs]
+2013-02-11T20:15:26.984-0600: 0.375: [Full GC 482K->482K(62848K), 0.0035370 secs]
+2013-02-11T20:15:26.987-0600: 0.379: [GC 482K->482K(62848K), 0.0003230 secs]
+2013-02-11T20:15:26.988-0600: 0.379: [Full GC 482K->482K(62848K), 0.0035150 secs]
+2013-02-11T20:15:26.991-0600: 0.383: [GC 482K->482K(62848K), 0.0003050 secs]
+2013-02-11T20:15:26.992-0600: 0.383: [Full GC 482K->482K(62848K), 0.0035080 secs]
+2013-02-11T20:15:26.995-0600: 0.387: [GC 482K->482K(62848K), 0.0003320 secs]
+2013-02-11T20:15:26.995-0600: 0.387: [Full GC 482K->482K(62848K), 0.0035060 secs]
+2013-02-11T20:15:26.999-0600: 0.391: [GC 482K->482K(62848K), 0.0002520 secs]
+2013-02-11T20:15:26.999-0600: 0.391: [Full GC 482K->482K(62848K), 0.0035190 secs]
+2013-02-11T20:15:27.003-0600: 0.394: [GC 482K->482K(62848K), 0.0003050 secs]
+2013-02-11T20:15:27.003-0600: 0.395: [Full GC 482K->482K(62848K), 0.0035430 secs]
+2013-02-11T20:15:27.007-0600: 0.398: [GC 482K->482K(62848K), 0.0002800 secs]
+2013-02-11T20:15:27.007-0600: 0.399: [Full GC 482K->482K(62848K), 0.0035210 secs]
+2013-02-11T20:15:27.011-0600: 0.402: [GC 482K->482K(62848K), 0.0003130 secs]
+2013-02-11T20:15:27.011-0600: 0.403: [Full GC 482K->482K(62848K), 0.0035530 secs]
+2013-02-11T20:15:27.015-0600: 0.406: [GC 482K->482K(62848K), 0.0004020 secs]
+2013-02-11T20:15:27.015-0600: 0.407: [Full GC 482K->482K(62848K), 0.0035340 secs]
+2013-02-11T20:15:27.019-0600: 0.410: [GC 482K->482K(62848K), 0.0003310 secs]
+2013-02-11T20:15:27.019-0600: 0.411: [Full GC 482K->482K(62848K), 0.0035390 secs]
+2013-02-11T20:15:27.023-0600: 0.414: [GC 482K->482K(62848K), 0.0003630 secs]
+2013-02-11T20:15:27.023-0600: 0.414: [Full GC 482K->482K(62848K), 0.0035500 secs]
+2013-02-11T20:15:27.027-0600: 0.418: [GC 482K->482K(62848K), 0.0003430 secs]
+2013-02-11T20:15:27.027-0600: 0.418: [Full GC 482K->482K(62848K), 0.0035220 secs]
+2013-02-11T20:15:27.031-0600: 0.422: [GC 482K->482K(62848K), 0.0003260 secs]
+2013-02-11T20:15:27.031-0600: 0.422: [Full GC 482K->482K(62848K), 0.0035230 secs]
+2013-02-11T20:15:27.034-0600: 0.426: [GC 482K->482K(62848K), 0.0003650 secs]
+2013-02-11T20:15:27.035-0600: 0.426: [Full GC 482K->482K(62848K), 0.0035010 secs]
+2013-02-11T20:15:27.038-0600: 0.430: [GC 482K->482K(62848K), 0.0003000 secs]
+2013-02-11T20:15:27.039-0600: 0.430: [Full GC 482K->482K(62848K), 0.0035090 secs]
+2013-02-11T20:15:27.042-0600: 0.434: [GC 482K->482K(62848K), 0.0004570 secs]
+2013-02-11T20:15:27.043-0600: 0.434: [Full GC 482K->482K(62848K), 0.0036010 secs]
+2013-02-11T20:15:27.046-0600: 0.438: [GC 482K->482K(62848K), 0.0004570 secs]
+2013-02-11T20:15:27.047-0600: 0.438: [Full GC 482K->482K(62848K), 0.0036090 secs]
+2013-02-11T20:15:27.051-0600: 0.442: [GC 482K->482K(62848K), 0.0003740 secs]
+2013-02-11T20:15:27.051-0600: 0.442: [Full GC 482K->482K(62848K), 0.0036140 secs]
+2013-02-11T20:15:27.055-0600: 0.446: [GC 482K->482K(62848K), 0.0003630 secs]
+2013-02-11T20:15:27.055-0600: 0.446: [Full GC 482K->482K(62848K), 0.0036080 secs]
+2013-02-11T20:15:27.059-0600: 0.450: [GC 482K->482K(62848K), 0.0003770 secs]
+2013-02-11T20:15:27.059-0600: 0.451: [Full GC 482K->482K(62848K), 0.0036060 secs]
+2013-02-11T20:15:27.063-0600: 0.454: [GC 482K->482K(62848K), 0.0003410 secs]
+2013-02-11T20:15:27.063-0600: 0.455: [Full GC 482K->482K(62848K), 0.0036300 secs]
+2013-02-11T20:15:27.067-0600: 0.458: [GC 482K->482K(62848K), 0.0002990 secs]
+2013-02-11T20:15:27.067-0600: 0.459: [Full GC 482K->482K(62848K), 0.0036160 secs]
+2013-02-11T20:15:27.071-0600: 0.462: [GC 482K->482K(62848K), 0.0003970 secs]
+2013-02-11T20:15:27.071-0600: 0.463: [Full GC 482K->482K(62848K), 0.0036660 secs]
+2013-02-11T20:15:27.075-0600: 0.466: [GC 482K->482K(62848K), 0.0003160 secs]
+2013-02-11T20:15:27.075-0600: 0.467: [Full GC 482K->482K(62848K), 0.0036080 secs]
+2013-02-11T20:15:27.079-0600: 0.470: [GC 482K->482K(62848K), 0.0003140 secs]
+2013-02-11T20:15:27.079-0600: 0.471: [Full GC 482K->482K(62848K), 0.0035740 secs]
+2013-02-11T20:15:27.083-0600: 0.474: [GC 482K->482K(62848K), 0.0003450 secs]
+2013-02-11T20:15:27.083-0600: 0.475: [Full GC 482K->482K(62848K), 0.0036240 secs]
+2013-02-11T20:15:27.087-0600: 0.478: [GC 482K->482K(62848K), 0.0004670 secs]
+2013-02-11T20:15:27.087-0600: 0.479: [Full GC 482K->482K(62848K), 0.0037790 secs]
+2013-02-11T20:15:27.091-0600: 0.483: [GC 482K->482K(62848K), 0.0002690 secs]
+2013-02-11T20:15:27.091-0600: 0.483: [Full GC 482K->482K(62848K), 0.0036290 secs]
+2013-02-11T20:15:27.095-0600: 0.487: [GC 482K->482K(62848K), 0.0003340 secs]
+2013-02-11T20:15:27.096-0600: 0.487: [Full GC 482K->482K(62848K), 0.0035950 secs]
+2013-02-11T20:15:27.099-0600: 0.491: [GC 482K->482K(62848K), 0.0002480 secs]
+2013-02-11T20:15:27.099-0600: 0.491: [Full GC 482K->482K(62848K), 0.0035940 secs]
+2013-02-11T20:15:27.103-0600: 0.495: [GC 482K->482K(62848K), 0.0003510 secs]
+2013-02-11T20:15:27.104-0600: 0.495: [Full GC 482K->482K(62848K), 0.0037060 secs]
+2013-02-11T20:15:27.107-0600: 0.499: [GC 482K->482K(62848K), 0.0003680 secs]
+2013-02-11T20:15:27.108-0600: 0.499: [Full GC 482K->482K(62848K), 0.0035920 secs] \ No newline at end of file
diff --git a/examples/pom.xml b/examples/pom.xml
index 7352d7b..0e904cb 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -1,19 +1,57 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2011 Google Inc.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>com.google.caliper</groupId>
- <artifactId>caliper-examples</artifactId>
- <packaging>jar</packaging>
- <version>0.5-rc1</version>
- <inceptionYear>2009</inceptionYear>
- <name>Caliper Examples</name>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
- <version>5</version>
+ <version>7</version>
</parent>
- <url>http://code.google.com/p/caliper/</url>
+
+ <groupId>com.google.caliper</groupId>
+ <artifactId>caliper-examples</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>Caliper Examples</name>
<description>Caliper Examples</description>
+
+ <url>http://code.google.com/p/caliper/</url>
+
+ <inceptionYear>2009</inceptionYear>
+
+ <organization>
+ <name>Google Inc.</name>
+ <url>http://www.google.com</url>
+ </organization>
+
+ <developers>
+ <developer>
+ <name>Gregory Kick</name>
+ <organization>Google Inc.</organization>
+ </developer>
+ <developer>
+ <name>Jesse Wilson</name>
+ </developer>
+ </developers>
+
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
@@ -21,24 +59,23 @@
<distribution>repo</distribution>
</license>
</licenses>
+
<scm>
<connection>scm:git:http://code.google.com/p/caliper/examples</connection>
<developerConnection>scm:git:git:http://code.google.com/p/caliper/examples</developerConnection>
<url>http://caliper.codegoogle.com/svn/trunk/examples</url>
</scm>
+
<issueManagement>
<system>Google Code Issue Tracking</system>
<url>http://code.google.com/p/caliper/issues/list</url>
</issueManagement>
- <organization>
- <name>Google, Inc.</name>
- <url>http://www.google.com</url>
- </organization>
+
<dependencies>
<dependency>
<groupId>com.google.caliper</groupId>
<artifactId>caliper</artifactId>
- <version>0.5-rc1</version>
+ <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -47,13 +84,14 @@
<scope>test</scope>
</dependency>
</dependencies>
+
<build>
<defaultGoal>package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
+ <version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
@@ -62,7 +100,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
- <version>2.8</version>
+ <version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
@@ -72,17 +110,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
- <version>2.1</version>
+ <version>2.5.1</version>
<configuration>
<arguments>-DenableCiProfile=true</arguments>
</configuration>
</plugin>
</plugins>
</build>
- <developers>
- <developer>
- <name>Jesse Wilson</name>
- <organization>Google Inc.</organization>
- </developer>
- </developers>
+
</project>
diff --git a/examples/src/main/java/examples/ArraySortBenchmark.java b/examples/src/main/java/examples/ArraySortBenchmark.java
index f42390f..81be2a6 100644
--- a/examples/src/main/java/examples/ArraySortBenchmark.java
+++ b/examples/src/main/java/examples/ArraySortBenchmark.java
@@ -16,16 +16,17 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+
import java.util.Arrays;
import java.util.Random;
/**
* Measures sorting on different distributions of integers.
*/
-public class ArraySortBenchmark extends SimpleBenchmark {
+public class ArraySortBenchmark {
@Param({"10", "100", "1000", "10000"}) private int length;
@@ -34,12 +35,12 @@ public class ArraySortBenchmark extends SimpleBenchmark {
private int[] values;
private int[] copy;
- @Override protected void setUp() throws Exception {
+ @BeforeExperiment void setUp() throws Exception {
values = distribution.create(length);
copy = new int[length];
}
- public void timeSort(int reps) {
+ @Benchmark void sort(int reps) {
for (int i = 0; i < reps; i++) {
System.arraycopy(values, 0, copy, 0, values.length);
Arrays.sort(copy);
@@ -95,8 +96,4 @@ public class ArraySortBenchmark extends SimpleBenchmark {
abstract int[] create(int length);
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(ArraySortBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/BitSetBenchmark.java b/examples/src/main/java/examples/BitSetBenchmark.java
index 3154b01..98cdcc8 100644
--- a/examples/src/main/java/examples/BitSetBenchmark.java
+++ b/examples/src/main/java/examples/BitSetBenchmark.java
@@ -16,8 +16,10 @@
package examples;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+
import java.util.BitSet;
import java.util.Random;
@@ -44,17 +46,17 @@ import java.util.Random;
* BaselineIteration 68 XX|||||||||||||||||
* </pre>
*
- * <p>Initially things look simple. The {@link #timeSetBitSetX64(int)} benchmark
- * takes approximately twice as long as {@link #timeSetMaskX64(int)}. However
+ * <p>Initially things look simple. The {@link #setBitSetX64(int)} benchmark
+ * takes approximately twice as long as {@link #setMaskX64(int)}. However
* the inner loops in these benchmarks have almost no content, so a more
* 'real world' benchmark was devised in an attempt to back up these results.
*
- * <p>The {@link #timeCharsToMask(int)} and {@link #timeCharsToBitSet(int)}
+ * <p>The {@link #charsToMask(int)} and {@link #charsToBitSet(int)}
* benchmarks convert a simple char[] of '1's and '0's to a corresponding BitSet
* or bit mask. These also processes 64 bits per iteration and so appears to be
* doing the same amount of work as the first benchmarks.
*
- * <p>Additionally the {@link BitSetBenchmark#timeBaselineIteration(int)}
+ * <p>Additionally the {@link BitSetBenchmark#baselineIteration(int)}
* benchmark attempts to measure the raw cost of looping through and reading the
* source data.
*
@@ -83,11 +85,11 @@ import java.util.Random;
* <p><b>2:</b>Overly simplistic benchmarks can give a very false impression of
* performance.
*/
-public class BitSetBenchmark extends SimpleBenchmark {
+public class BitSetBenchmark {
private BitSet bitSet;
private char[] bitString;
- @Override protected void setUp() throws Exception {
+ @BeforeExperiment void setUp() throws Exception {
bitSet = new BitSet(64);
bitString = new char[64];
Random r = new Random();
@@ -99,7 +101,7 @@ public class BitSetBenchmark extends SimpleBenchmark {
/**
* This benchmark attempts to measure performance of {@link BitSet#set}.
*/
- public int timeSetBitSetX64(int reps) {
+ @Benchmark int setBitSetX64(int reps) {
long count = 64L * reps;
for (int i = 0; i < count; i++) {
bitSet.set(i & 0x3F, true);
@@ -110,7 +112,7 @@ public class BitSetBenchmark extends SimpleBenchmark {
/**
* This benchmark attempts to measure performance of direct bit-manipulation.
*/
- public long timeSetMaskX64(int reps) {
+ @Benchmark long setMaskX64(int reps) {
long count = 64L * reps;
long bitMask = 0L;
for (int i = 0; i < count; i++) {
@@ -122,9 +124,9 @@ public class BitSetBenchmark extends SimpleBenchmark {
/**
* This benchmark parses a char[] of 1's and 0's into a BitSet. Results from
* this benchmark should be comparable with those from
- * {@link #timeCharsToMask(int)}.
+ * {@link #charsToMask(int)}.
*/
- public String timeCharsToBitSet(int reps) {
+ @Benchmark String charsToBitSet(int reps) {
/*
* This benchmark now measures the complete parsing of a char[] rather than
* a single invocation of {@link BitSet#set}. However this fine because
@@ -141,9 +143,9 @@ public class BitSetBenchmark extends SimpleBenchmark {
/**
* This benchmark parses a char[] of 1's and 0's into a bit mask. Results from
* this benchmark should be comparable with those from
- * {@link #timeCharsToBitSet(int)}.
+ * {@link #charsToBitSet(int)}.
*/
- public long timeCharsToMask(int reps) {
+ @Benchmark long charsToMask(int reps) {
/*
* Comparing results we see a far more realistic sounding result whereby
* using a bit mask is a little over 4x faster than using BitSet.
@@ -164,12 +166,12 @@ public class BitSetBenchmark extends SimpleBenchmark {
/**
* This benchmark attempts to measure the baseline cost of both
- * {@link #timeCharsToBitSet(int)} and {@link #timeCharsToMask(int)}.
+ * {@link #charsToBitSet(int)} and {@link #charsToMask(int)}.
* It does this by unconditionally summing the character values of the char[].
* This is as close to a no-op case as we can expect to get without unwanted
* over-optimization.
*/
- public long timeBaselineIteration(int reps) {
+ @Benchmark long baselineIteration(int reps) {
int badHash = 0;
for (int i = 0; i < reps; i++) {
for (int n = 0; n < bitString.length; n++) {
@@ -178,9 +180,4 @@ public class BitSetBenchmark extends SimpleBenchmark {
}
return badHash;
}
-
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- Runner.main(BitSetBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/CharacterBenchmark.java b/examples/src/main/java/examples/CharacterBenchmark.java
index 1e013af..82c4439 100644
--- a/examples/src/main/java/examples/CharacterBenchmark.java
+++ b/examples/src/main/java/examples/CharacterBenchmark.java
@@ -16,15 +16,15 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
/**
* Tests various Character methods, intended for testing multiple
* implementations against each other.
*/
-public class CharacterBenchmark extends SimpleBenchmark {
+public class CharacterBenchmark {
@Param private CharacterSet characterSet;
@@ -32,7 +32,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
private char[] chars;
- @Override protected void setUp() throws Exception {
+ @BeforeExperiment void setUp() throws Exception {
this.chars = characterSet.chars;
}
@@ -51,7 +51,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
// A fake benchmark to give us a baseline.
- public boolean timeIsSpace(int reps) {
+ @Benchmark boolean isSpace(int reps) {
boolean dummy = false;
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
@@ -69,7 +69,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
return dummy;
}
- public void timeDigit(int reps) {
+ @Benchmark void digit(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -85,7 +85,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeGetNumericValue(int reps) {
+ @Benchmark void getNumericValue(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -101,7 +101,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsDigit(int reps) {
+ @Benchmark void isDigit(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -117,7 +117,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsIdentifierIgnorable(int reps) {
+ @Benchmark void isIdentifierIgnorable(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -133,7 +133,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsJavaIdentifierPart(int reps) {
+ @Benchmark void isJavaIdentifierPart(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -149,7 +149,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsJavaIdentifierStart(int reps) {
+ @Benchmark void isJavaIdentifierStart(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -165,7 +165,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsLetter(int reps) {
+ @Benchmark void isLetter(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -181,7 +181,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsLetterOrDigit(int reps) {
+ @Benchmark void isLetterOrDigit(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -197,7 +197,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsLowerCase(int reps) {
+ @Benchmark void isLowerCase(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -213,7 +213,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsSpaceChar(int reps) {
+ @Benchmark void isSpaceChar(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -229,7 +229,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsUpperCase(int reps) {
+ @Benchmark void isUpperCase(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -245,7 +245,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeIsWhitespace(int reps) {
+ @Benchmark void isWhitespace(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -261,7 +261,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeToLowerCase(int reps) {
+ @Benchmark void toLowerCase(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -277,7 +277,7 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
- public void timeToUpperCase(int reps) {
+ @Benchmark void toUpperCase(int reps) {
if (overload == Overload.CHAR) {
for (int i = 0; i < reps; ++i) {
for (int ch = 0; ch < 65536; ++ch) {
@@ -292,9 +292,4 @@ public class CharacterBenchmark extends SimpleBenchmark {
}
}
}
-
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- Runner.main(CharacterBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/CompressionSizeBenchmark.java b/examples/src/main/java/examples/CompressionSizeBenchmark.java
index 90ddd39..d4fed5e 100644
--- a/examples/src/main/java/examples/CompressionSizeBenchmark.java
+++ b/examples/src/main/java/examples/CompressionSizeBenchmark.java
@@ -16,10 +16,9 @@
package examples;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.api.Benchmark;
import com.google.caliper.model.ArbitraryMeasurement;
-import com.google.caliper.runner.CaliperMain;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -30,7 +29,7 @@ import java.util.zip.Deflater;
/**
* Example "arbitrary measurement" benchmark.
*/
-public class CompressionSizeBenchmark extends Benchmark {
+public class CompressionSizeBenchmark {
@Param({
"this string will compress badly",
@@ -50,7 +49,7 @@ public class CompressionSizeBenchmark extends Benchmark {
compressionLevelMap.put("huffmanOnly", Deflater.HUFFMAN_ONLY);
}
- public long timeSimpleCompression(int reps) {
+ @Benchmark long simpleCompression(int reps) {
long dummy = 0;
for (int i = 0; i < reps; i++) {
dummy += compress(toCompress.getBytes()).length;
@@ -83,8 +82,4 @@ public class CompressionSizeBenchmark extends Benchmark {
}
return bos.toByteArray();
}
-
- public static void main(String[] args) {
- CaliperMain.main(CompressionSizeBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/ContainsBenchmark.java b/examples/src/main/java/examples/ContainsBenchmark.java
index 01bb8c6..26d2eb9 100644
--- a/examples/src/main/java/examples/ContainsBenchmark.java
+++ b/examples/src/main/java/examples/ContainsBenchmark.java
@@ -16,20 +16,19 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Random;
import java.util.Set;
-public class ContainsBenchmark extends SimpleBenchmark {
+public class ContainsBenchmark {
@Param({"0", "25", "50", "75", "100"}) private int percentNulls;
@Param({"100", "1000", "10000"}) private int containsPerRep;
@@ -39,7 +38,7 @@ public class ContainsBenchmark extends SimpleBenchmark {
/** twenty-five percent nulls */
private final List<Object> queries = new ArrayList<Object>();
- @Override protected void setUp() {
+ @BeforeExperiment void setUp() {
set.addAll(Arrays.asList("str1", "str2", "str3", "str4"));
int nullThreshold = percentNulls * containsPerRep / 100;
for (int i = 0; i < nullThreshold; i++) {
@@ -51,28 +50,11 @@ public class ContainsBenchmark extends SimpleBenchmark {
Collections.shuffle(queries, new Random(0));
}
- @Override public Map<String, Integer> getTimeUnitNames() {
- Map<String, Integer> unitNames = new HashMap<String, Integer>();
- unitNames.put("ns/contains", 1);
- unitNames.put("us/contains", 1000);
- unitNames.put("ms/contains", 1000000);
- unitNames.put("s/contains", 1000000000);
- return unitNames;
- }
-
- @Override public double nanosToUnits(double nanos) {
- return nanos / containsPerRep;
- }
-
- public void timeContains(int reps) {
+ @Benchmark void contains(int reps) {
for (int i = 0; i < reps; i++) {
for (Object query : queries) {
set.contains(query);
}
}
}
-
- public static void main(String[] args) {
- Runner.main(ContainsBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/CopyArrayBenchmark.java b/examples/src/main/java/examples/CopyArrayBenchmark.java
index fed0950..543db5b 100644
--- a/examples/src/main/java/examples/CopyArrayBenchmark.java
+++ b/examples/src/main/java/examples/CopyArrayBenchmark.java
@@ -16,9 +16,9 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
import java.util.Arrays;
import java.util.Random;
@@ -44,7 +44,7 @@ import java.util.Random;
* memory (boolean arrays count as byte arrays!).
* </ul>
*/
-public class CopyArrayBenchmark extends SimpleBenchmark {
+public class CopyArrayBenchmark {
public enum Strategy {
CLONE {
@Override Object[] copy(Object[] array) {
@@ -252,7 +252,7 @@ public class CopyArrayBenchmark extends SimpleBenchmark {
long[] longArray;
short[] shortArray;
- @Override protected void setUp() {
+ @BeforeExperiment void setUp() {
objectArray = new Object[size];
booleanArray = new boolean[size];
byteArray = new byte[size];
@@ -271,86 +271,82 @@ public class CopyArrayBenchmark extends SimpleBenchmark {
byteArray[i] = (byte) num;
charArray[i] = (char) num;
doubleArray[i] = num;
- floatArray[i] = (float) num;
+ floatArray[i] = num;
intArray[i] = num;
longArray[i] = num;
shortArray[i] = (short) num;
}
}
- public int timeObjects(int reps) {
+ @Benchmark int objects(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(objectArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(objectArray));
}
return dummy;
}
- public int timeBooleans(int reps) {
+ @Benchmark int booleans(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(booleanArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(booleanArray));
}
return dummy;
}
- public int timeBytes(int reps) {
+ @Benchmark int bytes(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(byteArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(byteArray));
}
return dummy;
}
- public int timeChars(int reps) {
+ @Benchmark int chars(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(charArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(charArray));
}
return dummy;
}
- public int timeDoubles(int reps) {
+ @Benchmark int doubles(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(doubleArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(doubleArray));
}
return dummy;
}
- public int timeFloats(int reps) {
+ @Benchmark int floats(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(floatArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(floatArray));
}
return dummy;
}
- public int timeInts(int reps) {
+ @Benchmark int ints(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(intArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(intArray));
}
return dummy;
}
- public int timeLongs(int reps) {
+ @Benchmark int longs(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(longArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(longArray));
}
return dummy;
}
- public int timeShorts(int reps) {
+ @Benchmark int shorts(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
- dummy += strategy.copy(shortArray).hashCode();
+ dummy += System.identityHashCode(strategy.copy(shortArray));
}
return dummy;
}
-
- public static void main(String[] args) {
- Runner.main(CopyArrayBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/DemoBenchmark.java b/examples/src/main/java/examples/DemoBenchmark.java
index 3b7e2dd..3313eee 100644
--- a/examples/src/main/java/examples/DemoBenchmark.java
+++ b/examples/src/main/java/examples/DemoBenchmark.java
@@ -16,23 +16,24 @@
package examples;
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.api.Benchmark;
import com.google.caliper.api.SkipThisScenarioException;
-import com.google.caliper.api.VmParam;
-import com.google.caliper.runner.CaliperMain;
+import com.google.caliper.api.VmOptions;
import com.google.caliper.util.ShortDuration;
import java.math.BigDecimal;
-public class DemoBenchmark extends Benchmark {
+@VmOptions("-server")
+public class DemoBenchmark {
@Param({"abc", "def", "xyz"}) String string;
@Param({"1", "2"}) int number;
@Param Foo foo;
@Param({"0.00", "123.45"}) BigDecimal money;
@Param({"1ns", "2 minutes"}) ShortDuration duration;
- @VmParam({"-Xmx32m", "-Xmx1g"}) String memoryMax;
enum Foo {
FOO, BAR, BAZ, QUX;
@@ -42,13 +43,13 @@ public class DemoBenchmark extends Benchmark {
// System.out.println("I should not do this.");
}
- @Override protected void setUp() throws Exception {
+ @BeforeExperiment void setUp() throws Exception {
if (string.equals("abc") && number == 1) {
throw new SkipThisScenarioException();
}
}
- public int timeSomething(int reps) {
+ @Benchmark int something(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
dummy += i;
@@ -56,7 +57,7 @@ public class DemoBenchmark extends Benchmark {
return dummy;
}
- public int timeSomethingElse(int reps) {
+ @Benchmark int somethingElse(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
dummy -= i;
@@ -64,11 +65,7 @@ public class DemoBenchmark extends Benchmark {
return dummy;
}
- @Override protected void tearDown() throws Exception {
+ @AfterExperiment void tearDown() throws Exception {
// System.out.println("Hey, I'm tearing up the joint.");
}
-
- public static void main(String[] args) {
- CaliperMain.main(DemoBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/DoubleToStringBenchmark.java b/examples/src/main/java/examples/DoubleToStringBenchmark.java
index 291ec84..d28f9b0 100644
--- a/examples/src/main/java/examples/DoubleToStringBenchmark.java
+++ b/examples/src/main/java/examples/DoubleToStringBenchmark.java
@@ -16,17 +16,13 @@
package examples;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
-
-import java.util.Arrays;
-import java.util.List;
/**
* Measures the various ways the JDK converts doubles to strings.
*/
-public class DoubleToStringBenchmark extends SimpleBenchmark {
+public class DoubleToStringBenchmark {
@Param Method method;
public enum Method {
@@ -83,7 +79,7 @@ public class DoubleToStringBenchmark extends SimpleBenchmark {
@Param Value value;
- public int timePrimitive(int reps) {
+ @Benchmark int primitive(int reps) {
double d = value.value;
int dummy = 0;
for (int i = 0; i < reps; i++) {
@@ -92,7 +88,7 @@ public class DoubleToStringBenchmark extends SimpleBenchmark {
return dummy;
}
- public int timeWrapper(int reps) {
+ @Benchmark int wrapper(int reps) {
Double d = value.value;
int dummy = 0;
for (int i = 0; i < reps; i++) {
@@ -100,8 +96,4 @@ public class DoubleToStringBenchmark extends SimpleBenchmark {
}
return dummy;
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(DoubleToStringBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/DoubleToStringBenchmark2.java b/examples/src/main/java/examples/DoubleToStringBenchmark2.java
index 8e6c69a..3a6da5b 100644
--- a/examples/src/main/java/examples/DoubleToStringBenchmark2.java
+++ b/examples/src/main/java/examples/DoubleToStringBenchmark2.java
@@ -16,14 +16,13 @@
package examples;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
/**
* Measures the various ways the JDK converts doubles to strings.
*/
-public class DoubleToStringBenchmark2 extends SimpleBenchmark {
+public class DoubleToStringBenchmark2 {
@Param boolean useWrapper;
enum Value {
@@ -41,7 +40,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark {
@Param Value value;
- public int timeToString(int reps) {
+ @Benchmark int toString(int reps) {
int dummy = 0;
if (useWrapper) {
Double d = value.d;
@@ -57,7 +56,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark {
return dummy;
}
- public int timeStringValueOf(int reps) {
+ @Benchmark int stringValueOf(int reps) {
int dummy = 0;
if (useWrapper) {
Double d = value.d;
@@ -73,7 +72,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark {
return dummy;
}
- public int timeStringFormat(int reps) {
+ @Benchmark int stringFormat(int reps) {
int dummy = 0;
if (useWrapper) {
Double d = value.d;
@@ -89,7 +88,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark {
return dummy;
}
- public int timeQuoteTrick(int reps) {
+ @Benchmark int quoteTrick(int reps) {
int dummy = 0;
if (useWrapper) {
Double d = value.d;
@@ -104,8 +103,4 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark {
}
return dummy;
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(DoubleToStringBenchmark2.class, args);
- }
}
diff --git a/examples/src/main/java/examples/EnumSetContainsBenchmark.java b/examples/src/main/java/examples/EnumSetContainsBenchmark.java
index b232514..10660a9 100644
--- a/examples/src/main/java/examples/EnumSetContainsBenchmark.java
+++ b/examples/src/main/java/examples/EnumSetContainsBenchmark.java
@@ -16,16 +16,17 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+
import java.util.EnumSet;
import java.util.Set;
/**
* Measures EnumSet#contains().
*/
-public class EnumSetContainsBenchmark extends SimpleBenchmark {
+public class EnumSetContainsBenchmark {
@Param private SetMaker setMaker;
@@ -75,18 +76,14 @@ public class EnumSetContainsBenchmark extends SimpleBenchmark {
private Set<?> set;
private Object[] testValues;
- @Override protected void setUp() {
+ @BeforeExperiment void setUp() {
this.set = setMaker.newSet();
this.testValues = setMaker.testValues();
}
- public void timeContains(int reps) {
+ @Benchmark void contains(int reps) {
for (int i = 0; i < reps; i++) {
set.contains(testValues[i % testValues.length]);
}
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(EnumSetContainsBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java b/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java
index a11b1bd..7c4bc79 100644
--- a/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java
+++ b/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java
@@ -16,9 +16,7 @@
package examples;
-import com.google.caliper.SimpleBenchmark;
-import com.google.caliper.runner.CaliperMain;
-
+import com.google.caliper.Benchmark;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
@@ -28,49 +26,43 @@ import java.util.Locale;
* Benchmarks creation and cloning various expensive objects.
*/
@SuppressWarnings({"ResultOfObjectAllocationIgnored"}) // TODO: should fix!
-public class ExpensiveObjectsBenchmark extends SimpleBenchmark {
- public void timeNewDecimalFormatSymbols(int reps) {
+public class ExpensiveObjectsBenchmark {
+ @Benchmark void newDecimalFormatSymbols(int reps) {
for (int i = 0; i < reps; ++i) {
new DecimalFormatSymbols(Locale.US);
}
}
- public void timeClonedDecimalFormatSymbols(int reps) {
+ @Benchmark void clonedDecimalFormatSymbols(int reps) {
DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
for (int i = 0; i < reps; ++i) {
dfs.clone();
}
}
- public void timeNewNumberFormat(int reps) {
+ @Benchmark void newNumberFormat(int reps) {
for (int i = 0; i < reps; ++i) {
NumberFormat.getInstance(Locale.US);
}
}
- public void timeClonedNumberFormat(int reps) {
+ @Benchmark void clonedNumberFormat(int reps) {
NumberFormat nf = NumberFormat.getInstance(Locale.US);
for (int i = 0; i < reps; ++i) {
nf.clone();
}
}
- public void timeNewSimpleDateFormat(int reps) {
+ @Benchmark void newSimpleDateFormat(int reps) {
for (int i = 0; i < reps; ++i) {
new SimpleDateFormat();
}
}
- public void timeClonedSimpleDateFormat(int reps) {
+ @Benchmark void clonedSimpleDateFormat(int reps) {
SimpleDateFormat sdf = new SimpleDateFormat();
for (int i = 0; i < reps; ++i) {
sdf.clone();
}
}
-
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- CaliperMain.main(ExpensiveObjectsBenchmark.class, args);
-// Runner.main(ExpensiveObjectsBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/FormatterBenchmark.java b/examples/src/main/java/examples/FormatterBenchmark.java
index b4a0541..93ed936 100644
--- a/examples/src/main/java/examples/FormatterBenchmark.java
+++ b/examples/src/main/java/examples/FormatterBenchmark.java
@@ -16,36 +16,37 @@
package examples;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+import com.google.caliper.Benchmark;
import java.util.Formatter;
/**
* Compares Formatter against hand-written StringBuilder code.
*/
-public class FormatterBenchmark extends SimpleBenchmark {
- public void timeFormatter_NoFormatting(int reps) {
+public class FormatterBenchmark {
+ @Benchmark void formatter_NoFormatting(int reps) {
for (int i = 0; i < reps; i++) {
Formatter f = new Formatter();
f.format("this is a reasonably short string that doesn't actually need any formatting");
+ f.close();
}
}
- public void timeStringBuilder_NoFormatting(int reps) {
+ @Benchmark void stringBuilder_NoFormatting(int reps) {
for (int i = 0; i < reps; i++) {
StringBuilder sb = new StringBuilder();
sb.append("this is a reasonably short string that doesn't actually need any formatting");
}
}
- public void timeFormatter_OneInt(int reps) {
+ @Benchmark void formatter_OneInt(int reps) {
for (int i = 0; i < reps; i++) {
Formatter f = new Formatter();
f.format("this is a reasonably short string that has an int %d in it", i);
+ f.close();
}
}
- public void timeStringBuilder_OneInt(int reps) {
+ @Benchmark void stringBuilder_OneInt(int reps) {
for (int i = 0; i < reps; i++) {
StringBuilder sb = new StringBuilder();
sb.append("this is a reasonably short string that has an int ");
@@ -54,14 +55,15 @@ public class FormatterBenchmark extends SimpleBenchmark {
}
}
- public void timeFormatter_OneString(int reps) {
+ @Benchmark void formatter_OneString(int reps) {
for (int i = 0; i < reps; i++) {
Formatter f = new Formatter();
f.format("this is a reasonably short string that has a string %s in it", "hello");
+ f.close();
}
}
- public void timeStringBuilder_OneString(int reps) {
+ @Benchmark void stringBuilder_OneString(int reps) {
for (int i = 0; i < reps; i++) {
StringBuilder sb = new StringBuilder();
sb.append("this is a reasonably short string that has a string ");
@@ -69,8 +71,4 @@ public class FormatterBenchmark extends SimpleBenchmark {
sb.append(" in it");
}
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(FormatterBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/IntModBenchmark.java b/examples/src/main/java/examples/IntModBenchmark.java
index 55a119c..bed415f 100644
--- a/examples/src/main/java/examples/IntModBenchmark.java
+++ b/examples/src/main/java/examples/IntModBenchmark.java
@@ -16,17 +16,16 @@
package examples;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+import com.google.caliper.Benchmark;
/**
* Measures several candidate implementations for mod().
*/
@SuppressWarnings("SameParameterValue")
-public class IntModBenchmark extends SimpleBenchmark {
+public class IntModBenchmark {
private static final int M = (1 << 16) - 1;
- public int timeConditional(int reps) {
+ @Benchmark int conditional(int reps) {
int dummy = 5;
for (int i = 0; i < reps; i++) {
dummy += Integer.MAX_VALUE + conditionalMod(dummy, M);
@@ -39,7 +38,7 @@ public class IntModBenchmark extends SimpleBenchmark {
return r < 0 ? r + m : r;
}
- public int timeDoubleRemainder(int reps) {
+ @Benchmark int doubleRemainder(int reps) {
int dummy = 5;
for (int i = 0; i < reps; i++) {
dummy += Integer.MAX_VALUE + doubleRemainderMod(dummy, M);
@@ -52,7 +51,7 @@ public class IntModBenchmark extends SimpleBenchmark {
return (int) ((a % m + (long) m) % m);
}
- public int timeRightShiftingMod(int reps) {
+ @Benchmark int rightShiftingMod(int reps) {
int dummy = 5;
for (int i = 0; i < reps; i++) {
dummy += Integer.MAX_VALUE + rightShiftingMod(dummy, M);
@@ -66,7 +65,7 @@ public class IntModBenchmark extends SimpleBenchmark {
return (int) (r + (r >> 63 & m));
}
- public int timeLeftShiftingMod(int reps) {
+ @Benchmark int leftShiftingMod(int reps) {
int dummy = 5;
for (int i = 0; i < reps; i++) {
dummy += Integer.MAX_VALUE + leftShiftingMod(dummy, M);
@@ -79,16 +78,11 @@ public class IntModBenchmark extends SimpleBenchmark {
return (int) ((a + ((long) m << 32)) % m);
}
- public int timeWrongMod(int reps) {
+ @Benchmark int wrongMod(int reps) {
int dummy = 5;
for (int i = 0; i < reps; i++) {
dummy += Integer.MAX_VALUE + dummy % M;
}
return dummy;
}
-
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- Runner.main(IntModBenchmark.class, args);
- }
-} \ No newline at end of file
+}
diff --git a/examples/src/main/java/examples/ListIterationBenchmark.java b/examples/src/main/java/examples/ListIterationBenchmark.java
index a8cfb05..07ae8eb 100644
--- a/examples/src/main/java/examples/ListIterationBenchmark.java
+++ b/examples/src/main/java/examples/ListIterationBenchmark.java
@@ -16,16 +16,17 @@
package examples;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
+
import java.util.AbstractList;
import java.util.List;
/**
* Measures iterating through list elements.
*/
-public class ListIterationBenchmark extends SimpleBenchmark {
+public class ListIterationBenchmark {
@Param({"0", "10", "100", "1000"})
private int length;
@@ -33,7 +34,7 @@ public class ListIterationBenchmark extends SimpleBenchmark {
private List<Object> list;
private Object[] array;
- @Override protected void setUp() {
+ @BeforeExperiment void setUp() {
array = new Object[length];
for (int i = 0; i < length; i++) {
array[i] = new Object();
@@ -50,24 +51,23 @@ public class ListIterationBenchmark extends SimpleBenchmark {
};
}
- @SuppressWarnings({"UnusedDeclaration"}) // TODO: fix
- public void timeListIteration(int reps) {
+ @Benchmark int listIteration(int reps) {
+ int dummy = 0;
for (int i = 0; i < reps; i++) {
for (Object value : list) {
+ dummy |= value.hashCode();
}
}
+ return dummy;
}
- @SuppressWarnings({"UnusedDeclaration"}) // TODO: fix
- public void timeArrayIteration(int reps) {
+ @Benchmark int arrayIteration(int reps) {
+ int dummy = 0;
for (int i = 0; i < reps; i++) {
for (Object value : array) {
+ dummy |= value.hashCode();
}
}
- }
-
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- Runner.main(ListIterationBenchmark.class, args);
+ return dummy;
}
} \ No newline at end of file
diff --git a/examples/src/main/java/examples/ListModificationBenchmark.java b/examples/src/main/java/examples/ListModificationBenchmark.java
new file mode 100644
index 0000000..81d14f9
--- /dev/null
+++ b/examples/src/main/java/examples/ListModificationBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package examples;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Measures performance of list operations.
+ */
+public class ListModificationBenchmark {
+
+ private enum Element {
+ INSTANCE,
+ }
+ private enum ListImpl {
+ Array {
+ @Override List<Element> create() {
+ return new ArrayList<Element>();
+ }
+ },
+ Linked {
+ @Override List<Element> create() {
+ return new LinkedList<Element>();
+ }
+ };
+
+ abstract List<Element> create();
+ }
+
+ @Param({"10", "100", "1000", "10000"})
+ private int size;
+
+ @Param({"Array", "Linked"})
+ private ListImpl implementation;
+
+ private List<Element> list;
+
+ @BeforeExperiment void setUp() throws Exception {
+ list = implementation.create();
+ for (int i = 0; i < size; i++) {
+ list.add(Element.INSTANCE);
+ }
+ }
+
+ @Benchmark void populate(int reps) throws Exception {
+ for (int rep = 0; rep < reps; rep++) {
+ List<Element> list = implementation.create();
+ for (int i = 0; i < size; i++) {
+ list.add(Element.INSTANCE);
+ }
+ }
+ }
+
+ @Benchmark void iteration(int reps) {
+ for (int rep = 0; rep < reps; rep++) {
+ Iterator<Element> iterator = list.iterator();
+ while (iterator.hasNext()) {
+ iterator.next();
+ }
+ }
+ }
+
+ @Benchmark void headAddRemove(int reps) {
+ for (int rep = 0; rep < reps; rep++) {
+ list.add(0, Element.INSTANCE);
+ list.remove(0);
+ }
+ }
+
+ @Benchmark void middleAddRemove(int reps) {
+ int index = size / 2;
+ for (int rep = 0; rep < reps; rep++) {
+ list.add(index, Element.INSTANCE);
+ list.remove(index);
+ }
+ }
+
+ @Benchmark void tailAddRemove(int reps) {
+ int index = size - 1;
+ for (int rep = 0; rep < reps; rep++) {
+ list.add(Element.INSTANCE);
+ list.remove(index);
+ }
+ }
+}
diff --git a/examples/src/main/java/examples/LoopingBackwardsBenchmark.java b/examples/src/main/java/examples/LoopingBackwardsBenchmark.java
index 1e3d1ad..d23a28d 100644
--- a/examples/src/main/java/examples/LoopingBackwardsBenchmark.java
+++ b/examples/src/main/java/examples/LoopingBackwardsBenchmark.java
@@ -16,17 +16,16 @@
package examples;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
/**
* Testing the old canard that looping backwards is faster.
*/
-public class LoopingBackwardsBenchmark extends SimpleBenchmark {
+public class LoopingBackwardsBenchmark {
@Param({"2", "20", "2000", "20000000"}) int max;
- public int timeForwards(int reps) {
+ @Benchmark int forwards(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < max; j++) {
@@ -36,7 +35,7 @@ public class LoopingBackwardsBenchmark extends SimpleBenchmark {
return dummy;
}
- public int timeBackwards(int reps) {
+ @Benchmark int backwards(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = max - 1; j >= 0; j--) {
@@ -45,8 +44,4 @@ public class LoopingBackwardsBenchmark extends SimpleBenchmark {
}
return dummy;
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(LoopingBackwardsBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/MessageDigestCreationBenchmark.java b/examples/src/main/java/examples/MessageDigestCreationBenchmark.java
index c9437bd..681d10e 100644
--- a/examples/src/main/java/examples/MessageDigestCreationBenchmark.java
+++ b/examples/src/main/java/examples/MessageDigestCreationBenchmark.java
@@ -16,29 +16,24 @@
package examples;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
import java.security.MessageDigest;
/**
* Times creating new MessageDigest instances.
*/
-public class MessageDigestCreationBenchmark extends SimpleBenchmark {
+public class MessageDigestCreationBenchmark {
// By default, just the "interesting ones". Also consider Adler32 and CRC32,
// but these are not guaranteed to be supported in all runtime environments.
@Param({"MD5", "SHA-1", "SHA-256", "SHA-512"})
String algorithm;
- public void time(int reps) throws Exception {
+ @Benchmark void time(int reps) throws Exception {
// Change this to use a dummy if the results look suspicious.
for (int i = 0; i < reps; i++) {
MessageDigest.getInstance(algorithm);
}
}
-
- public static void main(String[] args) throws Exception {
- Runner.main(MessageDigestCreationBenchmark.class, args);
- }
}
diff --git a/examples/src/main/java/examples/NoOpBenchmark.java b/examples/src/main/java/examples/NoOpBenchmark.java
new file mode 100644
index 0000000..fa24805
--- /dev/null
+++ b/examples/src/main/java/examples/NoOpBenchmark.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package examples;
+
+import com.google.caliper.Benchmark;
+
+/**
+ * This is the absolute minimal benchmark. It does nothing but time the rep loop.
+ */
+public class NoOpBenchmark {
+ @Benchmark long increment(long reps) {
+ long result = 0;
+ for (; result < reps; result++) {}
+ return result;
+ }
+}
diff --git a/examples/src/main/java/examples/StringBuilderBenchmark.java b/examples/src/main/java/examples/StringBuilderBenchmark.java
index 3c839d3..c39b3be 100644
--- a/examples/src/main/java/examples/StringBuilderBenchmark.java
+++ b/examples/src/main/java/examples/StringBuilderBenchmark.java
@@ -16,117 +16,176 @@
package examples;
+import static java.lang.Character.MIN_SURROGATE;
+
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.Runner;
-import com.google.caliper.SimpleBenchmark;
/**
* Tests the performance of various StringBuilder methods.
*/
-public class StringBuilderBenchmark extends SimpleBenchmark {
+public class StringBuilderBenchmark {
- @Param({"1", "10", "100"}) private int length;
+ @Param({"1", "10", "100"}) private int length;
- public void timeAppendBoolean(int reps) {
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(true);
- }
- }
+ @Benchmark void appendBoolean(int reps) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < reps; ++i) {
+ sb.setLength(0);
+ for (int j = 0; j < length; ++j) {
+ sb.append(true);
+ sb.append(false);
+ }
}
+ }
- public void timeAppendChar(int reps) {
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append('c');
- }
- }
+ @Benchmark void appendChar(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append('c');
+ }
}
+ }
- public void timeAppendCharArray(int reps) {
- char[] chars = "chars".toCharArray();
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(chars);
- }
- }
+ @Benchmark void appendCharArray(int reps) {
+ char[] chars = "chars".toCharArray();
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(chars);
+ }
}
+ }
- public void timeAppendCharSequence(int reps) {
- CharSequence cs = "chars";
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(cs);
- }
- }
+ @Benchmark void appendCharSequence(int reps) {
+ CharSequence cs = "chars";
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(cs);
+ }
}
+ }
- public void timeAppendDouble(int reps) {
- double d = 1.2;
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(d);
- }
- }
+ @Benchmark void appendDouble(int reps) {
+ double d = 1.2;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(d);
+ }
}
+ }
- public void timeAppendFloat(int reps) {
- float f = 1.2f;
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(f);
- }
- }
+ @Benchmark void appendFloat(int reps) {
+ float f = 1.2f;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(f);
+ }
}
+ }
- public void timeAppendInt(int reps) {
- int n = 123;
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(n);
- }
- }
+ @Benchmark void appendInt(int reps) {
+ int n = 123;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(n);
+ }
}
+ }
- public void timeAppendLong(int reps) {
- long l = 123;
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(l);
- }
- }
+ @Benchmark void appendLong(int reps) {
+ long l = 123;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(l);
+ }
}
+ }
- public void timeAppendObject(int reps) {
- Object o = new Object();
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(o);
- }
- }
+ @Benchmark void appendObject(int reps) {
+ Object o = new Object();
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(o);
+ }
}
+ }
- public void timeAppendString(int reps) {
- String s = "chars";
- for (int i = 0; i < reps; ++i) {
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < length; ++j) {
- sb.append(s);
- }
+ @Benchmark void appendString(int reps) {
+ String s = "chars";
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(s);
+ }
+ }
+ }
+
+ @Benchmark void appendNull(int reps) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < reps; ++i) {
+ sb.setLength(0);
+ for (int j = 0; j < length; ++j) {
+ sb.append((String)null);
+ sb.append((StringBuilder)null);
+ }
+ }
+ }
+
+ /** Times .reverse() when no surrogates are present. */
+ @Benchmark void reverseNoSurrogates(int reps) {
+ final int length = Math.min(this.length, MIN_SURROGATE);
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; j++) {
+ sb.appendCodePoint(j);
+ }
+ for (int i = 0; i < reps; i++) {
+ for (int j = 0; j < 4; j++) {
+ sb.reverse();
+ }
+ if (sb.codePointAt(0) > MIN_SURROGATE)
+ throw new Error();
+ }
+ }
+
+ /** Times .codePointAt(int) when no surrogates are present. */
+ @Benchmark void codePointAtNoSurrogates(int reps) {
+ final int length = Math.min(this.length, MIN_SURROGATE);
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; j++) {
+ sb.appendCodePoint(j);
+ }
+ for (int i = 0; i < reps; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < length - 1; k++) {
+ if (sb.codePointAt(k) > MIN_SURROGATE)
+ throw new Error();
}
+ }
}
+ }
- // TODO: remove this from all examples when IDE plugins are ready
- public static void main(String[] args) throws Exception {
- Runner.main(StringBuilderBenchmark.class, args);
+ /** Times .codePointBefore(int) when no surrogates are present. */
+ @Benchmark void codePointBeforeNoSurrogates(int reps) {
+ final int length = Math.min(this.length, MIN_SURROGATE);
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; j++) {
+ sb.appendCodePoint(j);
+ }
+ for (int i = 0; i < reps; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 1; k < length; k++) {
+ if (sb.codePointBefore(k) > MIN_SURROGATE)
+ throw new Error();
+ }
+ }
}
+ }
}
diff --git a/examples/src/main/java/examples/Utf8Benchmark.java b/examples/src/main/java/examples/Utf8Benchmark.java
new file mode 100644
index 0000000..97b4e00
--- /dev/null
+++ b/examples/src/main/java/examples/Utf8Benchmark.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package examples;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+
+import java.nio.charset.Charset;
+import java.util.Random;
+
+/**
+ * Benchmark for operations with the UTF-8 charset.
+ */
+public class Utf8Benchmark {
+
+ static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ /**
+ * The maximum code point used in generated text. Different values
+ * provide reasonable models of different real-world human text.
+ */
+ static class MaxCodePoint {
+ final int value;
+
+ /**
+ * Convert the input string to a code point. Accepts regular
+ * decimal numerals, hex strings, and some symbolic names
+ * meaningful to humans.
+ */
+ private static int decode(String userFriendly) {
+ try {
+ return Integer.decode(userFriendly);
+ } catch (NumberFormatException ignored) {
+ if (userFriendly.matches("(?i)(?:American|English|ASCII)")) {
+ // 1-byte UTF-8 sequences - "American" ASCII text
+ return 0x80;
+ } else if (userFriendly.matches("(?i)(?:French|Latin|Western.*European)")) {
+ // Mostly 1-byte UTF-8 sequences, mixed with occasional 2-byte
+ // sequences - "Western European" text
+ return 0x90;
+ } else if (userFriendly.matches("(?i)(?:Branch.*Prediction.*Hostile)")) {
+ // Defeat branch predictor for: c < 0x80 ; branch taken 50% of the time.
+ return 0x100;
+ } else if (userFriendly.matches("(?i)(?:Greek|Cyrillic|European|ISO.?8859)")) {
+ // Mostly 2-byte UTF-8 sequences - "European" text
+ return 0x800;
+ } else if (userFriendly.matches("(?i)(?:Chinese|Han|Asian|BMP)")) {
+ // Mostly 3-byte UTF-8 sequences - "Asian" text
+ return Character.MIN_SUPPLEMENTARY_CODE_POINT;
+ } else if (userFriendly.matches("(?i)(?:Cuneiform|rare|exotic|supplementary.*)")) {
+ // Mostly 4-byte UTF-8 sequences - "rare exotic" text
+ return Character.MAX_CODE_POINT;
+ } else {
+ throw new IllegalArgumentException("Can't decode codepoint " + userFriendly);
+ }
+ }
+ }
+
+ public static MaxCodePoint valueOf(String userFriendly) {
+ return new MaxCodePoint(userFriendly);
+ }
+
+ private MaxCodePoint(String userFriendly) {
+ value = decode(userFriendly);
+ }
+ }
+
+ /**
+ * The default values of maxCodePoint below provide pretty good
+ * performance models of different kinds of common human text.
+ * @see MaxCodePoint#decode
+ */
+ @Param({"0x80", "0x100", "0x800", "0x10000", "0x10ffff"}) MaxCodePoint maxCodePoint;
+
+ static final int STRING_COUNT = 1 << 7;
+
+ @Param({"65536"}) int charCount;
+ private String[] strings;
+
+ /**
+ * Computes arrays of valid unicode Strings.
+ */
+ @BeforeExperiment void setUp() {
+ final long seed = 99;
+ final Random rnd = new Random(seed);
+ strings = new String[STRING_COUNT];
+ for (int i = 0; i < STRING_COUNT; i++) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < charCount; j++) {
+ int codePoint;
+ // discard illegal surrogate "codepoints"
+ do {
+ codePoint = rnd.nextInt(maxCodePoint.value);
+ } while (isSurrogate(codePoint));
+ sb.appendCodePoint(codePoint);
+ }
+ strings[i] = sb.toString();
+ }
+ // The reps will continue until the non-determinism detector is pacified!
+ getBytes(100);
+ }
+
+ /**
+ * Benchmarks {@link String#getBytes} on valid strings containing
+ * pseudo-randomly-generated codePoints less than {@code
+ * maxCodePoint}. A constant seed is used, so separate runs perform
+ * identical computations.
+ */
+ @Benchmark void getBytes(int reps) {
+ final String[] strings = this.strings;
+ final int mask = STRING_COUNT - 1;
+ for (int i = 0; i < reps; i++) {
+ String string = strings[i & mask];
+ byte[] bytes = string.getBytes(UTF_8);
+ if (bytes[0] == 86 && bytes[bytes.length - 1] == 99) {
+ throw new Error("Unlikely! We're just defeating the optimizer!");
+ }
+ }
+ }
+
+ /** Character.isSurrogate was added in Java SE 7. */
+ private boolean isSurrogate(int c) {
+ return (Character.MIN_HIGH_SURROGATE <= c &&
+ c <= Character.MAX_LOW_SURROGATE);
+ }
+}
diff --git a/examples/src/main/java/examples/VarargsBenchmark.java b/examples/src/main/java/examples/VarargsBenchmark.java
new file mode 100644
index 0000000..eceb72d
--- /dev/null
+++ b/examples/src/main/java/examples/VarargsBenchmark.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package examples;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.google.caliper.api.SkipThisScenarioException;
+
+import java.util.Random;
+
+/**
+ * Benchmarks the overhead created by using varargs instead of parameter expansion.
+ *
+ * @author gak@google.com (Gregory Kick)
+ */
+public final class VarargsBenchmark {
+ enum Strategy {
+ VARARGS {
+ @Override long one(long a) {
+ return varargs(a);
+ }
+
+ @Override long two(long a, long b) {
+ return varargs(a, b);
+ }
+
+ @Override long three(long a, long b, long c) {
+ return varargs(a, b, c);
+ }
+
+ @Override long four(long a, long b, long c, long d) {
+ return varargs(a, b, c, d);
+ }
+
+ @Override long five(long a, long b, long c, long d, long e) {
+ return varargs(a, b, c, d);
+ }
+
+ @Override long six(long a, long b, long c, long d, long e, long f) {
+ return varargs(a, b, c, d, e, f);
+ }},
+ EXPANSION {
+ @Override long one(long a) {
+ return VarargsBenchmark.one(a);
+ }
+
+ @Override long two(long a, long b) {
+ return VarargsBenchmark.two(a, b);
+ }
+
+ @Override long three(long a, long b, long c) {
+ return VarargsBenchmark.three(a, b, c);
+ }
+
+ @Override long four(long a, long b, long c, long d) {
+ return VarargsBenchmark.four(a, b, c, d);
+ }
+
+ @Override long five(long a, long b, long c, long d, long e) {
+ return VarargsBenchmark.five(a, b, c, d, e);
+ }
+
+ @Override long six(long a, long b, long c, long d, long e, long f) {
+ return VarargsBenchmark.six(a, b, c, d, e, f);
+ }
+ };
+
+ abstract long one(long a);
+
+ abstract long two(long a, long b);
+
+ abstract long three(long a, long b, long c);
+
+ abstract long four(long a, long b, long c, long d);
+
+ abstract long five(long a, long b, long c, long d, long e);
+
+ abstract long six(long a, long b, long c, long d, long e, long f);
+ }
+
+ private static long varargs(long... longs) {
+ long result = 0;
+ for (long i : longs) {
+ result ^= i;
+ }
+ return result;
+ }
+
+ private static long one(long a) {
+ return a;
+ }
+
+ private static long two(long a, long b) {
+ return a ^ b;
+ }
+
+ private static long three(long a, long b, long c) {
+ return a ^ b ^ c;
+ }
+
+ private static long four(long a, long b, long c, long d) {
+ return a ^ b ^ c ^ d;
+ }
+
+ private static long five(long a, long b, long c, long d, long e) {
+ return a ^ b ^ c ^ d ^ e;
+ }
+
+ private static long six(long a, long b, long c, long d, long e, long f) {
+ return a ^ b ^ c ^ d ^ e ^ f;
+ }
+
+ @Param private Strategy strategy;
+ @Param({"1", "2", "3", "4", "5", "6"}) private int arguments;
+
+ private long[] data = new long[2048];
+
+ @BeforeExperiment void setUp() {
+ Random random = new Random();
+ for (int i = 0; i < data.length; i++) {
+ data[i] = random.nextLong();
+ }
+ }
+
+ @Benchmark long invocation(int reps) {
+ switch (arguments) {
+ case 1:
+ return oneArgument(reps);
+ case 2:
+ return twoArguments(reps);
+ case 3:
+ return threeArguments(reps);
+ case 4:
+ return fourArguments(reps);
+ case 5:
+ return fiveArguments(reps);
+ case 6:
+ return sixArguments(reps);
+ default:
+ throw new SkipThisScenarioException();
+ }
+ }
+
+ private long oneArgument(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.one(data[i % dataLength]);
+ }
+ return dummy;
+ }
+
+ private long twoArguments(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.two(data[i % dataLength], data[(i + 1) % dataLength]);
+ }
+ return dummy;
+ }
+
+ private long threeArguments(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.three(
+ data[i % dataLength],
+ data[(i + 1) % dataLength],
+ data[(i + 2) % dataLength]);
+ }
+ return dummy;
+ }
+
+ private long fourArguments(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.four(
+ data[i % dataLength],
+ data[(i + 1) % dataLength],
+ data[(i + 2) % dataLength],
+ data[(i + 3) % dataLength]);
+ }
+ return dummy;
+ }
+
+ private long fiveArguments(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.five(
+ data[i % dataLength],
+ data[(i + 1) % dataLength],
+ data[(i + 2) % dataLength],
+ data[(i + 3) % dataLength],
+ data[(i + 4) % dataLength]);
+ }
+ return dummy;
+ }
+
+ private long sixArguments(int reps) {
+ long dummy = 0;
+ long[] data = this.data;
+ int dataLength = data.length;
+ for (int i = 0; i < reps; i++) {
+ dummy += strategy.six(
+ data[i % dataLength],
+ data[(i + 1) % dataLength],
+ data[(i + 2) % dataLength],
+ data[(i + 3) % dataLength],
+ data[(i + 4) % dataLength],
+ data[(i + 5) % dataLength]);
+ }
+ return dummy;
+ }
+}
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
new file mode 100644
index 0000000..8caadbe
--- /dev/null
+++ b/expectations/knownfailures.txt
@@ -0,0 +1,76 @@
+[
+{
+ description: "ObjectGraphMeasurer doesn't work on Android",
+ names: [
+ "com.google.caliper.memory.ObjectGraphMeasurerTest"
+ ]
+},
+{
+ description: "Ljava/lang/management/ManagementFactory; doesn't exist on Android",
+ names: [
+ "com.google.caliper.config.CaliperConfigTest#getDefaultVmConfig",
+ "com.google.caliper.runner.StreamServiceTest",
+ "com.google.caliper.runner.WorkerProcessTest"
+ ]
+},
+{
+ description: "@RunWith(MockitoJUnitRunner) doesn't work in Vogar",
+ names: [
+ "com.google.caliper.config.CaliperConfigLoaderTest",
+ "com.google.caliper.config.LoggingConfigLoaderTest",
+ "com.google.caliper.runner.ExperimentingRunnerModuleTest"
+ ]
+},
+{
+ description: "Assumes it is running on a standard Java platform with java binary",
+ names: [
+ "com.google.caliper.config.VmConfigTest#testExecutable"
+ ]
+},
+{
+ description: "AllocationInstrument doesn't work on Android",
+ names: [
+ "com.google.caliper.runner.AllocationInstrumentTest",
+ "com.google.caliper.runner.BadUserCodeTest#testComplexNonDeterministicAllocation_noTrackAllocations",
+ "com.google.caliper.runner.BadUserCodeTest#testComplexNonDeterministicAllocation_trackAllocations",
+ "com.google.caliper.runner.BadUserCodeTest#testNonDeterministicAllocation_noTrackAllocations",
+ "com.google.caliper.runner.BadUserCodeTest#testNonDeterministicAllocation_trackAllocations",
+ "com.google.caliper.runner.MalformedBenchmarksTest#noBenchmarkMethods"
+ ]
+},
+{
+ description: "ArbitraryMeasurementInstrument doesn't work on Android",
+ names: [
+ "com.google.caliper.runner.ArbitraryMeasurmentInstrumentTest"
+ ]
+},
+{
+ description: "Android only has 256M heap",
+ names: [
+ "com.google.caliper.runner.RuntimeInstrumentTest#gcBeforeEachOptionIsHonored",
+ "com.google.caliper.runner.RuntimeInstrumentTest#gcBeforeEachOptionIsReallyNecessary"
+ ]
+},
+{
+ description: "Checks for a specific exception message that is an implementation detail of JVM",
+ names: [
+ "com.google.caliper.runner.MalformedBenchmarksTest#unparsableParamDefault"
+ ]
+},
+{
+ description: "Unknown cause",
+ names: [
+ "com.google.caliper.runner.ServerSocketServiceTest#getConnectionStoppedService",
+ "com.google.caliper.runner.BadUserCodeTest#testExceptionInMethod_notInDryRun",#
+ "com.google.caliper.runner.RuntimeInstrumentTest#maxWarmupWallTimeOptionIsHonored"
+ ]
+},
+{
+ description: "Possible race in parse code that causes dryRun to be true when it is false, goes away under debug",
+ names: [
+ "com.google.caliper.options.ParsedOptionsTest#testDefaults_DoNotRequireBenchmarkClassName",
+ "com.google.caliper.options.ParsedOptionsTest#testDefaults_RequireBenchmarkClassName",
+ "com.google.caliper.options.ParsedOptionsTest#testKitchenSink"
+ ]
+}
+]
diff --git a/lib/gson-1.7.1.jar b/lib/gson-1.7.1.jar
deleted file mode 100644
index eb54274..0000000
--- a/lib/gson-1.7.1.jar
+++ /dev/null
Binary files differ
diff --git a/lib/gson-2.2.2-sources.jar b/lib/gson-2.2.2-sources.jar
new file mode 100644
index 0000000..26c43d2
--- /dev/null
+++ b/lib/gson-2.2.2-sources.jar
Binary files differ
diff --git a/lib/gson-2.2.2.jar b/lib/gson-2.2.2.jar
new file mode 100644
index 0000000..9adc66f
--- /dev/null
+++ b/lib/gson-2.2.2.jar
Binary files differ
diff --git a/lib/gson-1.7.1.jar.txt b/lib/gson-2.2.2.jar.txt
index 892eaed..892eaed 100644
--- a/lib/gson-1.7.1.jar.txt
+++ b/lib/gson-2.2.2.jar.txt
diff --git a/lib/java-allocation-instrumenter-2.0-sources.jar b/lib/java-allocation-instrumenter-2.0-sources.jar
new file mode 100644
index 0000000..d8cc489
--- /dev/null
+++ b/lib/java-allocation-instrumenter-2.0-sources.jar
Binary files differ
diff --git a/lib/jersey-client-1.11-sources.jar b/lib/jersey-client-1.11-sources.jar
new file mode 100644
index 0000000..58164ca
--- /dev/null
+++ b/lib/jersey-client-1.11-sources.jar
Binary files differ
diff --git a/lib/jersey-client-1.11.jar b/lib/jersey-client-1.11.jar
new file mode 100644
index 0000000..42d8925
--- /dev/null
+++ b/lib/jersey-client-1.11.jar
Binary files differ
diff --git a/lib/jersey-client-1.11.jar.txt b/lib/jersey-client-1.11.jar.txt
new file mode 100644
index 0000000..8681d13
--- /dev/null
+++ b/lib/jersey-client-1.11.jar.txt
@@ -0,0 +1,712 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates or
+ contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any), and the
+ Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing Original
+ Software with files containing Modifications, in each case including
+ portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other than
+ Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first makes
+ Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum extent
+ possible, whether at the time of the initial grant or subsequently
+ acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of any of
+ the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original Software or
+ previous Modifications;
+
+ B. Any new file that contains any part of the Original Software or
+ previous Modification; or
+
+ C. Any new file that is contributed or otherwise made available under
+ the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable form of
+ computer software code that is originally released under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter
+ acquired, including without limitation, method, process, and apparatus
+ claims, in any patent Licensable by grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software code
+ in which modifications are made and (b) associated documentation
+ included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity exercising
+ rights under, and complying with all of the terms of, this License. For
+ legal entities, "You" includes any entity which controls, is controlled
+ by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, the Initial Developer hereby
+ grants You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Initial Developer, to use, reproduce, modify, display,
+ perform, sublicense and distribute the Original Software (or portions
+ thereof), with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or selling of
+ Original Software, to make, have made, use, practice, sell, and offer
+ for sale, and/or otherwise dispose of the Original Software (or portions
+ thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are effective on the
+ date Initial Developer first distributes or otherwise makes the Original
+ Software available to a third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is granted:
+ (1) for code that You delete from the Original Software, or (2) for
+ infringements caused by: (i) the modification of the Original Software,
+ or (ii) the combination of the Original Software with other software or
+ devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, each Contributor hereby grants
+ You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Contributor to use, reproduce, modify, display, perform,
+ sublicense and distribute the Modifications created by such Contributor
+ (or portions thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or selling of
+ Modifications made by that Contributor either alone and/or in
+ combination with its Contributor Version (or portions of such
+ combination), to make, use, sell, offer for sale, have made, and/or
+ otherwise dispose of: (1) Modifications made by that Contributor (or
+ portions thereof); and (2) the combination of Modifications made by that
+ Contributor with its Contributor Version (or portions of such
+ combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on
+ the date Contributor first distributes or otherwise makes the
+ Modifications available to a third party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is granted:
+ (1) for any code that Contributor has deleted from the Contributor
+ Version; (2) for infringements caused by: (i) third party modifications
+ of Contributor Version, or (ii) the combination of Modifications made by
+ that Contributor with other software (except as part of the Contributor
+ Version) or other devices; or (3) under Patent Claims infringed by
+ Covered Software in the absence of Modifications made by that
+ Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make available in
+ Executable form must also be made available in Source Code form and that
+ Source Code form must be distributed only under the terms of this
+ License. You must include a copy of this License with every copy of the
+ Source Code form of the Covered Software You distribute or otherwise
+ make available. You must inform recipients of any such Covered Software
+ in Executable form as to how they can obtain such Covered Software in
+ Source Code form in a reasonable manner on or through a medium
+ customarily used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You believe
+ Your Modifications are Your original creation(s) and/or You have
+ sufficient rights to grant the rights conveyed by this License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that identifies
+ You as the Contributor of the Modification. You may not remove or alter
+ any copyright, patent or trademark notices contained within the Covered
+ Software, or any notices of licensing or any descriptive text giving
+ attribution to any Contributor or the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in Source
+ Code form that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. You may choose to offer,
+ and to charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Software. However, you
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity or liability obligation is offered by
+ You alone, and You hereby agree to indemnify the Initial Developer and
+ every Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software under the
+ terms of this License or under the terms of a license of Your choice,
+ which may contain terms different from this License, provided that You
+ are in compliance with the terms of this License and that the license
+ for the Executable form does not attempt to limit or alter the
+ recipient's rights in the Source Code form from the rights set forth in
+ this License. If You distribute the Covered Software in Executable form
+ under a different license, You must make it absolutely clear that any
+ terms which differ from this License are offered by You alone, not by
+ the Initial Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such terms
+ You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with other
+ code not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Oracle is the initial license steward and may publish revised and/or new
+ versions of this License from time to time. Each version will be given a
+ distinguishing version number. Except as provided in Section 4.3, no one
+ other than the license steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. If the Initial
+ Developer includes a notice in the Original Software prohibiting it from
+ being distributed or otherwise made available under any subsequent
+ version of the License, You must distribute and make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. Otherwise, You may
+ also choose to use, distribute or otherwise make the Covered Software
+ available under the terms of any subsequent version of the License
+ published by the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new license
+ for Your Original Software, You may create and use a modified version of
+ this License if You: (a) rename the license and remove any references to
+ the name of the license steward (except to note that the license differs
+ from this License); and (b) otherwise make it clear that the license
+ contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+ SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY
+ RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME
+ THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS
+ DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO
+ USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. Provisions
+ which, by their nature, must remain in effect beyond the termination of
+ this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding declaratory
+ judgment actions) against Initial Developer or a Contributor (the
+ Initial Developer or Contributor against whom You assert such claim is
+ referred to as "Participant") alleging that the Participant Software
+ (meaning the Contributor Version where the Participant is a Contributor
+ or the Original Software where the Participant is the Initial Developer)
+ directly or indirectly infringes any patent, then any and all rights
+ granted directly or indirectly to You by such Participant, the Initial
+ Developer (if the Initial Developer is not the Participant) and all
+ Contributors under Sections 2.1 and/or 2.2 of this License shall, upon
+ 60 days notice from Participant terminate prospectively and
+ automatically at the expiration of such 60 day notice period, unless if
+ within such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally or
+ pursuant to a written agreement with Participant.
+
+ 6.3. If You assert a patent infringement claim against Participant
+ alleging that the Participant Software directly or indirectly infringes
+ any patent where such claim is resolved (such as by license or
+ settlement) prior to the initiation of patent infringement litigation,
+ then the reasonable value of the licenses granted by such Participant
+ under Sections 2.1 or 2.2 shall be taken into account in determining the
+ amount or value of any payment or license.
+
+ 6.4. In the event of termination under Sections 6.1 or 6.2 above, all
+ end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses granted
+ to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED
+ SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY
+ PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
+ GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL
+ OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+ OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION
+ AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1))
+ and "commercial computer software documentation" as such terms are used
+ in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and
+ 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government
+ End Users acquire Covered Software with only those rights set forth
+ herein. This U.S. Government Rights clause is in lieu of, and
+ supersedes, any other FAR, DFAR, or other clause or provision that
+ addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject matter
+ hereof. If any provision of this License is held to be unenforceable,
+ such provision shall be reformed only to the extent necessary to make it
+ enforceable. This License shall be governed by the law of the
+ jurisdiction specified in a notice contained within the Original
+ Software (except to the extent applicable law, if any, provides
+ otherwise), excluding such jurisdiction's conflict-of-law provisions.
+ Any litigation relating to this License shall be subject to the
+ jurisdiction of the courts located in the jurisdiction and venue
+ specified in a notice contained within the Original Software, with the
+ losing party responsible for costs, including, without limitation, court
+ costs and reasonable attorneys' fees and expenses. The application of
+ the United Nations Convention on Contracts for the International Sale of
+ Goods is expressly excluded. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall
+ not apply to this License. You agree that You alone are responsible for
+ compliance with the United States export administration regulations (and
+ the export control laws and regulation of any other countries) when You
+ use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly, out
+ of its utilization of rights under this License and You agree to work
+ with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION
+LICENSE (CDDL)
+
+The code released under the CDDL shall be governed by the laws of the
+State of California (excluding conflict-of-law provisions). Any
+litigation relating to this License shall be subject to the jurisdiction
+of the Federal Courts of the Northern District of California and the
+state courts of the State of California, with venue lying in Santa Clara
+County, California.
+
+
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place,
+Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public
+License applies to most of the Free Software Foundation's software and
+to any other program whose authors commit to using it. (Some other Free
+Software Foundation software is covered by the GNU Library General
+Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this
+service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have.
+You must make sure that they, too, receive or can get the source code.
+And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program" means
+either the Program or any derivative work under copyright law: that is
+to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy an appropriate copyright notice
+and disclaimer of warranty; keep intact all the notices that refer to
+this License and to the absence of any warranty; and give any other
+recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of
+it, thus forming a work based on the Program, and copy and distribute
+such modifications or work under the terms of Section 1 above, provided
+that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may
+ redistribute the program under these conditions, and telling the user
+ how to view a copy of this License. (Exception: if the Program itself is
+ interactive but does not normally print such an announcement, your work
+ based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the
+scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections
+1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2
+ above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access
+to copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will
+not have their licenses terminated so long as such parties remain in
+full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further restrictions
+on the recipients' exercise of the rights granted herein. You are not
+responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is implemented
+by public license practices. Many people have made generous
+contributions to the wide range of software distributed through that
+system in reliance on consistent application of that system; it is up to
+the author/donor to decide if he or she is willing to distribute
+software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded. In such case, this License incorporates the
+limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version
+number of this License, you may choose any version ever published by the
+Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the
+author to ask for permission. For software which is copyrighted by the
+Free Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program 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 for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision
+ comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is
+ free software, and you are welcome to redistribute it under certain
+ conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the commands
+you use may be called something other than `show w' and `show c'; they
+could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library. If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+#
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Oracle are subject to the following
+clarification and special exception to the GPL Version 2, but only where
+Oracle has expressly included in the particular source file's header the
+words "Oracle designates this particular file as subject to the
+"Classpath" exception as provided by Oracle in the License file that
+accompanied this code."
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License Version 2 cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under terms
+of your choice, provided that you also meet, for each linked independent
+module, the terms and conditions of the license of that module. An
+independent module is a module which is not derived from or based on
+this library. If you modify this library, you may extend this exception
+to your version of the library, but you are not obligated to do so. If
+you do not wish to do so, delete this exception statement from your
+version.
diff --git a/lib/jersey-core-1.11-sources.jar b/lib/jersey-core-1.11-sources.jar
new file mode 100644
index 0000000..b685f2c
--- /dev/null
+++ b/lib/jersey-core-1.11-sources.jar
Binary files differ
diff --git a/lib/jersey-core-1.11.jar b/lib/jersey-core-1.11.jar
new file mode 100644
index 0000000..d19f7ae
--- /dev/null
+++ b/lib/jersey-core-1.11.jar
Binary files differ
diff --git a/lib/jersey-core-1.11.jar.txt b/lib/jersey-core-1.11.jar.txt
new file mode 100644
index 0000000..8681d13
--- /dev/null
+++ b/lib/jersey-core-1.11.jar.txt
@@ -0,0 +1,712 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates or
+ contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any), and the
+ Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing Original
+ Software with files containing Modifications, in each case including
+ portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other than
+ Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first makes
+ Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum extent
+ possible, whether at the time of the initial grant or subsequently
+ acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of any of
+ the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original Software or
+ previous Modifications;
+
+ B. Any new file that contains any part of the Original Software or
+ previous Modification; or
+
+ C. Any new file that is contributed or otherwise made available under
+ the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable form of
+ computer software code that is originally released under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter
+ acquired, including without limitation, method, process, and apparatus
+ claims, in any patent Licensable by grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software code
+ in which modifications are made and (b) associated documentation
+ included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity exercising
+ rights under, and complying with all of the terms of, this License. For
+ legal entities, "You" includes any entity which controls, is controlled
+ by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, the Initial Developer hereby
+ grants You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Initial Developer, to use, reproduce, modify, display,
+ perform, sublicense and distribute the Original Software (or portions
+ thereof), with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or selling of
+ Original Software, to make, have made, use, practice, sell, and offer
+ for sale, and/or otherwise dispose of the Original Software (or portions
+ thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are effective on the
+ date Initial Developer first distributes or otherwise makes the Original
+ Software available to a third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is granted:
+ (1) for code that You delete from the Original Software, or (2) for
+ infringements caused by: (i) the modification of the Original Software,
+ or (ii) the combination of the Original Software with other software or
+ devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, each Contributor hereby grants
+ You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Contributor to use, reproduce, modify, display, perform,
+ sublicense and distribute the Modifications created by such Contributor
+ (or portions thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or selling of
+ Modifications made by that Contributor either alone and/or in
+ combination with its Contributor Version (or portions of such
+ combination), to make, use, sell, offer for sale, have made, and/or
+ otherwise dispose of: (1) Modifications made by that Contributor (or
+ portions thereof); and (2) the combination of Modifications made by that
+ Contributor with its Contributor Version (or portions of such
+ combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on
+ the date Contributor first distributes or otherwise makes the
+ Modifications available to a third party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is granted:
+ (1) for any code that Contributor has deleted from the Contributor
+ Version; (2) for infringements caused by: (i) third party modifications
+ of Contributor Version, or (ii) the combination of Modifications made by
+ that Contributor with other software (except as part of the Contributor
+ Version) or other devices; or (3) under Patent Claims infringed by
+ Covered Software in the absence of Modifications made by that
+ Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make available in
+ Executable form must also be made available in Source Code form and that
+ Source Code form must be distributed only under the terms of this
+ License. You must include a copy of this License with every copy of the
+ Source Code form of the Covered Software You distribute or otherwise
+ make available. You must inform recipients of any such Covered Software
+ in Executable form as to how they can obtain such Covered Software in
+ Source Code form in a reasonable manner on or through a medium
+ customarily used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You believe
+ Your Modifications are Your original creation(s) and/or You have
+ sufficient rights to grant the rights conveyed by this License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that identifies
+ You as the Contributor of the Modification. You may not remove or alter
+ any copyright, patent or trademark notices contained within the Covered
+ Software, or any notices of licensing or any descriptive text giving
+ attribution to any Contributor or the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in Source
+ Code form that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. You may choose to offer,
+ and to charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Software. However, you
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity or liability obligation is offered by
+ You alone, and You hereby agree to indemnify the Initial Developer and
+ every Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software under the
+ terms of this License or under the terms of a license of Your choice,
+ which may contain terms different from this License, provided that You
+ are in compliance with the terms of this License and that the license
+ for the Executable form does not attempt to limit or alter the
+ recipient's rights in the Source Code form from the rights set forth in
+ this License. If You distribute the Covered Software in Executable form
+ under a different license, You must make it absolutely clear that any
+ terms which differ from this License are offered by You alone, not by
+ the Initial Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such terms
+ You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with other
+ code not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Oracle is the initial license steward and may publish revised and/or new
+ versions of this License from time to time. Each version will be given a
+ distinguishing version number. Except as provided in Section 4.3, no one
+ other than the license steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. If the Initial
+ Developer includes a notice in the Original Software prohibiting it from
+ being distributed or otherwise made available under any subsequent
+ version of the License, You must distribute and make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. Otherwise, You may
+ also choose to use, distribute or otherwise make the Covered Software
+ available under the terms of any subsequent version of the License
+ published by the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new license
+ for Your Original Software, You may create and use a modified version of
+ this License if You: (a) rename the license and remove any references to
+ the name of the license steward (except to note that the license differs
+ from this License); and (b) otherwise make it clear that the license
+ contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+ SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY
+ RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME
+ THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS
+ DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO
+ USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. Provisions
+ which, by their nature, must remain in effect beyond the termination of
+ this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding declaratory
+ judgment actions) against Initial Developer or a Contributor (the
+ Initial Developer or Contributor against whom You assert such claim is
+ referred to as "Participant") alleging that the Participant Software
+ (meaning the Contributor Version where the Participant is a Contributor
+ or the Original Software where the Participant is the Initial Developer)
+ directly or indirectly infringes any patent, then any and all rights
+ granted directly or indirectly to You by such Participant, the Initial
+ Developer (if the Initial Developer is not the Participant) and all
+ Contributors under Sections 2.1 and/or 2.2 of this License shall, upon
+ 60 days notice from Participant terminate prospectively and
+ automatically at the expiration of such 60 day notice period, unless if
+ within such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally or
+ pursuant to a written agreement with Participant.
+
+ 6.3. If You assert a patent infringement claim against Participant
+ alleging that the Participant Software directly or indirectly infringes
+ any patent where such claim is resolved (such as by license or
+ settlement) prior to the initiation of patent infringement litigation,
+ then the reasonable value of the licenses granted by such Participant
+ under Sections 2.1 or 2.2 shall be taken into account in determining the
+ amount or value of any payment or license.
+
+ 6.4. In the event of termination under Sections 6.1 or 6.2 above, all
+ end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses granted
+ to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED
+ SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY
+ PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
+ GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL
+ OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+ OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION
+ AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1))
+ and "commercial computer software documentation" as such terms are used
+ in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and
+ 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government
+ End Users acquire Covered Software with only those rights set forth
+ herein. This U.S. Government Rights clause is in lieu of, and
+ supersedes, any other FAR, DFAR, or other clause or provision that
+ addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject matter
+ hereof. If any provision of this License is held to be unenforceable,
+ such provision shall be reformed only to the extent necessary to make it
+ enforceable. This License shall be governed by the law of the
+ jurisdiction specified in a notice contained within the Original
+ Software (except to the extent applicable law, if any, provides
+ otherwise), excluding such jurisdiction's conflict-of-law provisions.
+ Any litigation relating to this License shall be subject to the
+ jurisdiction of the courts located in the jurisdiction and venue
+ specified in a notice contained within the Original Software, with the
+ losing party responsible for costs, including, without limitation, court
+ costs and reasonable attorneys' fees and expenses. The application of
+ the United Nations Convention on Contracts for the International Sale of
+ Goods is expressly excluded. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall
+ not apply to this License. You agree that You alone are responsible for
+ compliance with the United States export administration regulations (and
+ the export control laws and regulation of any other countries) when You
+ use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly, out
+ of its utilization of rights under this License and You agree to work
+ with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION
+LICENSE (CDDL)
+
+The code released under the CDDL shall be governed by the laws of the
+State of California (excluding conflict-of-law provisions). Any
+litigation relating to this License shall be subject to the jurisdiction
+of the Federal Courts of the Northern District of California and the
+state courts of the State of California, with venue lying in Santa Clara
+County, California.
+
+
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place,
+Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public
+License applies to most of the Free Software Foundation's software and
+to any other program whose authors commit to using it. (Some other Free
+Software Foundation software is covered by the GNU Library General
+Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this
+service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have.
+You must make sure that they, too, receive or can get the source code.
+And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program" means
+either the Program or any derivative work under copyright law: that is
+to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy an appropriate copyright notice
+and disclaimer of warranty; keep intact all the notices that refer to
+this License and to the absence of any warranty; and give any other
+recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of
+it, thus forming a work based on the Program, and copy and distribute
+such modifications or work under the terms of Section 1 above, provided
+that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may
+ redistribute the program under these conditions, and telling the user
+ how to view a copy of this License. (Exception: if the Program itself is
+ interactive but does not normally print such an announcement, your work
+ based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the
+scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections
+1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2
+ above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access
+to copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will
+not have their licenses terminated so long as such parties remain in
+full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further restrictions
+on the recipients' exercise of the rights granted herein. You are not
+responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is implemented
+by public license practices. Many people have made generous
+contributions to the wide range of software distributed through that
+system in reliance on consistent application of that system; it is up to
+the author/donor to decide if he or she is willing to distribute
+software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded. In such case, this License incorporates the
+limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version
+number of this License, you may choose any version ever published by the
+Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the
+author to ask for permission. For software which is copyrighted by the
+Free Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program 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 for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision
+ comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is
+ free software, and you are welcome to redistribute it under certain
+ conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the commands
+you use may be called something other than `show w' and `show c'; they
+could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library. If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+#
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Oracle are subject to the following
+clarification and special exception to the GPL Version 2, but only where
+Oracle has expressly included in the particular source file's header the
+words "Oracle designates this particular file as subject to the
+"Classpath" exception as provided by Oracle in the License file that
+accompanied this code."
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License Version 2 cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under terms
+of your choice, provided that you also meet, for each linked independent
+module, the terms and conditions of the license of that module. An
+independent module is a module which is not derived from or based on
+this library. If you modify this library, you may extend this exception
+to your version of the library, but you are not obligated to do so. If
+you do not wish to do so, delete this exception statement from your
+version.
diff --git a/lib/joda-time-2.1-sources.jar b/lib/joda-time-2.1-sources.jar
new file mode 100644
index 0000000..44e4ed8
--- /dev/null
+++ b/lib/joda-time-2.1-sources.jar
Binary files differ
diff --git a/lib/joda-time-2.1.jar b/lib/joda-time-2.1.jar
new file mode 100644
index 0000000..b2aca95
--- /dev/null
+++ b/lib/joda-time-2.1.jar
Binary files differ
diff --git a/lib/joda-time-2.1.jar.txt b/lib/joda-time-2.1.jar.txt
new file mode 100644
index 0000000..230f1d4
--- /dev/null
+++ b/lib/joda-time-2.1.jar.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/lib/jsr311-api-1.1.1-sources.jar b/lib/jsr311-api-1.1.1-sources.jar
new file mode 100644
index 0000000..2cd5eb8
--- /dev/null
+++ b/lib/jsr311-api-1.1.1-sources.jar
Binary files differ
diff --git a/lib/jsr311-api-1.1.1.jar b/lib/jsr311-api-1.1.1.jar
new file mode 100644
index 0000000..ec8bc81
--- /dev/null
+++ b/lib/jsr311-api-1.1.1.jar
Binary files differ
diff --git a/lib/jsr311-api-1.1.1.jar.txt b/lib/jsr311-api-1.1.1.jar.txt
new file mode 100644
index 0000000..8681d13
--- /dev/null
+++ b/lib/jsr311-api-1.1.1.jar.txt
@@ -0,0 +1,712 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates or
+ contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any), and the
+ Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing Original
+ Software with files containing Modifications, in each case including
+ portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other than
+ Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first makes
+ Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum extent
+ possible, whether at the time of the initial grant or subsequently
+ acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of any of
+ the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original Software or
+ previous Modifications;
+
+ B. Any new file that contains any part of the Original Software or
+ previous Modification; or
+
+ C. Any new file that is contributed or otherwise made available under
+ the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable form of
+ computer software code that is originally released under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter
+ acquired, including without limitation, method, process, and apparatus
+ claims, in any patent Licensable by grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software code
+ in which modifications are made and (b) associated documentation
+ included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity exercising
+ rights under, and complying with all of the terms of, this License. For
+ legal entities, "You" includes any entity which controls, is controlled
+ by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, the Initial Developer hereby
+ grants You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Initial Developer, to use, reproduce, modify, display,
+ perform, sublicense and distribute the Original Software (or portions
+ thereof), with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or selling of
+ Original Software, to make, have made, use, practice, sell, and offer
+ for sale, and/or otherwise dispose of the Original Software (or portions
+ thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are effective on the
+ date Initial Developer first distributes or otherwise makes the Original
+ Software available to a third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is granted:
+ (1) for code that You delete from the Original Software, or (2) for
+ infringements caused by: (i) the modification of the Original Software,
+ or (ii) the combination of the Original Software with other software or
+ devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and subject to
+ third party intellectual property claims, each Contributor hereby grants
+ You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark)
+ Licensable by Contributor to use, reproduce, modify, display, perform,
+ sublicense and distribute the Modifications created by such Contributor
+ (or portions thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or selling of
+ Modifications made by that Contributor either alone and/or in
+ combination with its Contributor Version (or portions of such
+ combination), to make, use, sell, offer for sale, have made, and/or
+ otherwise dispose of: (1) Modifications made by that Contributor (or
+ portions thereof); and (2) the combination of Modifications made by that
+ Contributor with its Contributor Version (or portions of such
+ combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on
+ the date Contributor first distributes or otherwise makes the
+ Modifications available to a third party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is granted:
+ (1) for any code that Contributor has deleted from the Contributor
+ Version; (2) for infringements caused by: (i) third party modifications
+ of Contributor Version, or (ii) the combination of Modifications made by
+ that Contributor with other software (except as part of the Contributor
+ Version) or other devices; or (3) under Patent Claims infringed by
+ Covered Software in the absence of Modifications made by that
+ Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make available in
+ Executable form must also be made available in Source Code form and that
+ Source Code form must be distributed only under the terms of this
+ License. You must include a copy of this License with every copy of the
+ Source Code form of the Covered Software You distribute or otherwise
+ make available. You must inform recipients of any such Covered Software
+ in Executable form as to how they can obtain such Covered Software in
+ Source Code form in a reasonable manner on or through a medium
+ customarily used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You believe
+ Your Modifications are Your original creation(s) and/or You have
+ sufficient rights to grant the rights conveyed by this License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that identifies
+ You as the Contributor of the Modification. You may not remove or alter
+ any copyright, patent or trademark notices contained within the Covered
+ Software, or any notices of licensing or any descriptive text giving
+ attribution to any Contributor or the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in Source
+ Code form that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. You may choose to offer,
+ and to charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Software. However, you
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity or liability obligation is offered by
+ You alone, and You hereby agree to indemnify the Initial Developer and
+ every Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software under the
+ terms of this License or under the terms of a license of Your choice,
+ which may contain terms different from this License, provided that You
+ are in compliance with the terms of this License and that the license
+ for the Executable form does not attempt to limit or alter the
+ recipient's rights in the Source Code form from the rights set forth in
+ this License. If You distribute the Covered Software in Executable form
+ under a different license, You must make it absolutely clear that any
+ terms which differ from this License are offered by You alone, not by
+ the Initial Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such terms
+ You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with other
+ code not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Oracle is the initial license steward and may publish revised and/or new
+ versions of this License from time to time. Each version will be given a
+ distinguishing version number. Except as provided in Section 4.3, no one
+ other than the license steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. If the Initial
+ Developer includes a notice in the Original Software prohibiting it from
+ being distributed or otherwise made available under any subsequent
+ version of the License, You must distribute and make the Covered
+ Software available under the terms of the version of the License under
+ which You originally received the Covered Software. Otherwise, You may
+ also choose to use, distribute or otherwise make the Covered Software
+ available under the terms of any subsequent version of the License
+ published by the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new license
+ for Your Original Software, You may create and use a modified version of
+ this License if You: (a) rename the license and remove any references to
+ the name of the license steward (except to note that the license differs
+ from this License); and (b) otherwise make it clear that the license
+ contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+ SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY
+ RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME
+ THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS
+ DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO
+ USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. Provisions
+ which, by their nature, must remain in effect beyond the termination of
+ this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding declaratory
+ judgment actions) against Initial Developer or a Contributor (the
+ Initial Developer or Contributor against whom You assert such claim is
+ referred to as "Participant") alleging that the Participant Software
+ (meaning the Contributor Version where the Participant is a Contributor
+ or the Original Software where the Participant is the Initial Developer)
+ directly or indirectly infringes any patent, then any and all rights
+ granted directly or indirectly to You by such Participant, the Initial
+ Developer (if the Initial Developer is not the Participant) and all
+ Contributors under Sections 2.1 and/or 2.2 of this License shall, upon
+ 60 days notice from Participant terminate prospectively and
+ automatically at the expiration of such 60 day notice period, unless if
+ within such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally or
+ pursuant to a written agreement with Participant.
+
+ 6.3. If You assert a patent infringement claim against Participant
+ alleging that the Participant Software directly or indirectly infringes
+ any patent where such claim is resolved (such as by license or
+ settlement) prior to the initiation of patent infringement litigation,
+ then the reasonable value of the licenses granted by such Participant
+ under Sections 2.1 or 2.2 shall be taken into account in determining the
+ amount or value of any payment or license.
+
+ 6.4. In the event of termination under Sections 6.1 or 6.2 above, all
+ end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses granted
+ to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED
+ SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY
+ PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
+ GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL
+ OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+ OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION
+ AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1))
+ and "commercial computer software documentation" as such terms are used
+ in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and
+ 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government
+ End Users acquire Covered Software with only those rights set forth
+ herein. This U.S. Government Rights clause is in lieu of, and
+ supersedes, any other FAR, DFAR, or other clause or provision that
+ addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject matter
+ hereof. If any provision of this License is held to be unenforceable,
+ such provision shall be reformed only to the extent necessary to make it
+ enforceable. This License shall be governed by the law of the
+ jurisdiction specified in a notice contained within the Original
+ Software (except to the extent applicable law, if any, provides
+ otherwise), excluding such jurisdiction's conflict-of-law provisions.
+ Any litigation relating to this License shall be subject to the
+ jurisdiction of the courts located in the jurisdiction and venue
+ specified in a notice contained within the Original Software, with the
+ losing party responsible for costs, including, without limitation, court
+ costs and reasonable attorneys' fees and expenses. The application of
+ the United Nations Convention on Contracts for the International Sale of
+ Goods is expressly excluded. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall
+ not apply to this License. You agree that You alone are responsible for
+ compliance with the United States export administration regulations (and
+ the export control laws and regulation of any other countries) when You
+ use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly, out
+ of its utilization of rights under this License and You agree to work
+ with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION
+LICENSE (CDDL)
+
+The code released under the CDDL shall be governed by the laws of the
+State of California (excluding conflict-of-law provisions). Any
+litigation relating to this License shall be subject to the jurisdiction
+of the Federal Courts of the Northern District of California and the
+state courts of the State of California, with venue lying in Santa Clara
+County, California.
+
+
+
+
+The GNU General Public License (GPL) Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place,
+Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public
+License applies to most of the Free Software Foundation's software and
+to any other program whose authors commit to using it. (Some other Free
+Software Foundation software is covered by the GNU Library General
+Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this
+service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have.
+You must make sure that they, too, receive or can get the source code.
+And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program" means
+either the Program or any derivative work under copyright law: that is
+to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy an appropriate copyright notice
+and disclaimer of warranty; keep intact all the notices that refer to
+this License and to the absence of any warranty; and give any other
+recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of
+it, thus forming a work based on the Program, and copy and distribute
+such modifications or work under the terms of Section 1 above, provided
+that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may
+ redistribute the program under these conditions, and telling the user
+ how to view a copy of this License. (Exception: if the Program itself is
+ interactive but does not normally print such an announcement, your work
+ based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the
+scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections
+1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2
+ above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access
+to copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will
+not have their licenses terminated so long as such parties remain in
+full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further restrictions
+on the recipients' exercise of the rights granted herein. You are not
+responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is implemented
+by public license practices. Many people have made generous
+contributions to the wide range of software distributed through that
+system in reliance on consistent application of that system; it is up to
+the author/donor to decide if he or she is willing to distribute
+software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded. In such case, this License incorporates the
+limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version
+number of this License, you may choose any version ever published by the
+Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the
+author to ask for permission. For software which is copyrighted by the
+Free Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program 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 for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision
+ comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is
+ free software, and you are welcome to redistribute it under certain
+ conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the commands
+you use may be called something other than `show w' and `show c'; they
+could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library. If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+#
+
+"CLASSPATH" EXCEPTION TO THE GPL VERSION 2
+
+Certain source files distributed by Oracle are subject to the following
+clarification and special exception to the GPL Version 2, but only where
+Oracle has expressly included in the particular source file's header the
+words "Oracle designates this particular file as subject to the
+"Classpath" exception as provided by Oracle in the License file that
+accompanied this code."
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License Version 2 cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under terms
+of your choice, provided that you also meet, for each linked independent
+module, the terms and conditions of the license of that module. An
+independent module is a module which is not derived from or based on
+this library. If you modify this library, you may extend this exception
+to your version of the library, but you are not obligated to do so. If
+you do not wish to do so, delete this exception statement from your
+version.
diff --git a/tutorial/Tutorial.java b/tutorial/Tutorial.java
index 625d387..025a4d5 100644
--- a/tutorial/Tutorial.java
+++ b/tutorial/Tutorial.java
@@ -16,8 +16,9 @@
package tutorial;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
import com.google.caliper.Param;
-import com.google.caliper.SimpleBenchmark;
/**
* Caliper tutorial. To run the example benchmarks in this file:
@@ -31,7 +32,7 @@ public class Tutorial {
*
* Notice:
*
- * - We write a class that extends com.google.caliper.SimpleBenchmark.
+ * - We write a class that extends com.google.caliper.Benchmark.
* - It contains a public instance method whose name begins with 'time' and
* which accepts a single 'int reps' parameter.
* - The body of the method simply executes the code we wish to measure,
@@ -48,8 +49,8 @@ public class Tutorial {
* --------- ---
* NanoTime 233
*/
- public static class Benchmark1 extends SimpleBenchmark {
- public void timeNanoTime(int reps) {
+ public static class Benchmark1 {
+ @Benchmark void timeNanoTime(int reps) {
for (int i = 0; i < reps; i++) {
System.nanoTime();
}
@@ -69,13 +70,13 @@ public class Tutorial {
* NanoTime 248
* CurrentTimeMillis 118
*/
- public static class Benchmark2 extends SimpleBenchmark {
- public void timeNanoTime(int reps) {
+ public static class Benchmark2 {
+ @Benchmark void timeNanoTime(int reps) {
for (int i = 0; i < reps; i++) {
System.nanoTime();
}
}
- public void timeCurrentTimeMillis(int reps) {
+ @Benchmark void timeCurrentTimeMillis(int reps) {
for (int i = 0; i < reps; i++) {
System.currentTimeMillis();
}
@@ -86,11 +87,11 @@ public class Tutorial {
* Let's try iterating over a large array. This seems simple enough, but
* there is a problem!
*/
- public static class Benchmark3 extends SimpleBenchmark {
+ public static class Benchmark3 {
private final int[] array = new int[1000000];
@SuppressWarnings("UnusedDeclaration") // IDEA tries to warn us!
- public void timeArrayIteration_BAD(int reps) {
+ @Benchmark void timeArrayIteration_BAD(int reps) {
for (int i = 0; i < reps; i++) {
for (int ignoreMe : array) {}
}
@@ -123,10 +124,10 @@ public class Tutorial {
* With this change, Caliper should report a much more realistic value, more
* on the order of an entire millisecond.
*/
- public static class Benchmark4 extends SimpleBenchmark {
+ public static class Benchmark4 {
private final int[] array = new int[1000000];
- public int timeArrayIteration_fixed(int reps) {
+ @Benchmark int timeArrayIteration_fixed(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int doNotIgnoreMe : array) {
@@ -164,17 +165,17 @@ public class Tutorial {
* ArrayIteration 1000 477 ||||||||||||||||||||||||||||||
*
*/
- public static class Benchmark5 extends SimpleBenchmark {
+ public static class Benchmark5 {
@Param int size; // set automatically by framework
private int[] array; // set by us, in setUp()
- @Override protected void setUp() {
+ @BeforeExperiment void setUp() {
// @Param values are guaranteed to have been injected by now
array = new int[size];
}
- public int timeArrayIteration(int reps) {
+ @Benchmark int timeArrayIteration(int reps) {
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int doNotIgnoreMe : array) {