Updating xUnit Benchmark Scenarios to capture Etw with Pmc support. (#14734)
authorJosé Rivero <jorive@microsoft.com>
Wed, 1 Nov 2017 20:11:04 +0000 (13:11 -0700)
committerGitHub <noreply@github.com>
Wed, 1 Nov 2017 20:11:04 +0000 (13:11 -0700)
- Adding 'tiered' jitting
- Enable JitBench to capture Pmc data (Profile=On)
- Fixed some xUnit performance warnings
- Enforce DOTNET_MULTILEVEL_LOOKUP environment to be turned off
- Specify `dotnet publish` output directory.

14 files changed:
dependencies.props
perf.groovy
tests/scripts/run-xunit-perf.cmd
tests/scripts/run-xunit-perf.sh
tests/src/JIT/config/benchmark+roslyn/benchmark+roslyn.csproj
tests/src/JIT/config/benchmark+serialize/benchmark+serialize.csproj
tests/src/JIT/config/benchmark/benchmark.csproj
tests/src/performance/Scenario/JitBench/IterationData.cs [new file with mode: 0644]
tests/src/performance/Scenario/JitBench/JitBench.csproj
tests/src/performance/Scenario/JitBench/JitBenchHarness.cs
tests/src/performance/Scenario/JitBench/JitBenchHarnessOptions.cs [new file with mode: 0644]
tests/src/performance/linkbench/linkbench.cs
tests/src/performance/linkbench/linkbench.csproj
tests/src/performance/performance.csproj

index 78ee86de3e79d2633146e298f5a5b40e5aa3714f..7cb0ebadc3ae7a3bd01b804270f49ddc149b9ac9 100644 (file)
@@ -37,7 +37,7 @@
     <MicrosoftNETCoreRuntimeCoreCLRPackageVersion>2.1.0-preview1-25829-02</MicrosoftNETCoreRuntimeCoreCLRPackageVersion>
     <XunitPackageVersion>2.2.0-beta2-build3300</XunitPackageVersion>
     <XunitConsoleNetcorePackageVersion>1.0.2-prerelease-00177</XunitConsoleNetcorePackageVersion>
-    <XunitPerformanceApiPackageVersion>1.0.0-beta-build0007</XunitPerformanceApiPackageVersion>
+    <XunitPerformanceApiPackageVersion>1.0.0-beta-build0010</XunitPerformanceApiPackageVersion>
     <MicrosoftDiagnosticsTracingTraceEventPackageVersion>1.0.3-alpha-experimental</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
     <VCRuntimeVersion>1.2.0</VCRuntimeVersion>
   </PropertyGroup>
index 6bbec2cb0622928e676423c6bf2f8edf4793728f..c1628b9c334fde971f777d151cd4c46952a57f75 100644 (file)
@@ -579,19 +579,11 @@ parallel(
     ['Windows_NT'].each { os ->
         ['x64', 'x86'].each { arch ->
             ['ryujit'].each { jit ->
-
-                if (arch == 'x64' && jit == 'legacy_backend') {
-                    return
-                }
-
-                ['full_opt', 'min_opt'].each { opt_level ->
+                ['full_opt', 'min_opt', 'tiered'].each { opt_level ->
                     def architecture = arch
                     def newJob = job(Utilities.getFullJobName(project, "perf_scenarios_${os}_${arch}_${opt_level}_${jit}", isPR)) {
 
                         def testEnv = ""
-                        if (jit == 'legacy_backend') {
-                            testEnv = '-testEnv %WORKSPACE%\\tests\\legacyjit_x86_testenv.cmd'
-                        }
 
                         // Set the label.
                         label('windows_server_2016_clr_perf')
@@ -637,9 +629,15 @@ parallel(
 
                             def runXUnitPerfCommonArgs = "-arch ${arch} -configuration ${configuration} -generateBenchviewData \"%WORKSPACE%\\Microsoft.Benchview.JSONFormat\\tools\" ${uploadString} -runtype ${runType} ${testEnv} -optLevel ${opt_level} -jitName ${jit} -scenarioTest"
 
-                            // Scenario: JitBench
+                            // Profile=Off
                             batchFile("tests\\scripts\\run-xunit-perf.cmd ${runXUnitPerfCommonArgs} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\performance\\Scenario\\JitBench -group CoreCLR-Scenarios")
-                            batchFile("xcopy.exe /VYQK bin\\sandbox\\Perf-*.* bin\\toArchive\\sandbox\\Logs\\Scenario\\JitBench\\")
+                            batchFile("xcopy.exe /VYQK bin\\sandbox\\Logs\\Perf-*.* bin\\toArchive\\sandbox\\Logs\\Scenario\\JitBench\\Off\\")
+
+                            // Profile=On
+                            if (opt_level != 'min_opt') {
+                                batchFile("tests\\scripts\\run-xunit-perf.cmd ${runXUnitPerfCommonArgs} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\performance\\Scenario\\JitBench -group CoreCLR-Scenarios -collectionFlags BranchMispredictions+CacheMisses+InstructionRetired")
+                                batchFile("xcopy.exe /VYQK bin\\sandbox\\Logs\\Perf-*.* bin\\toArchive\\sandbox\\Logs\\Scenario\\JitBench\\On\\")
+                            }
                         }
                     }
 
@@ -828,7 +826,7 @@ parallel(
 
                             // Scenario: ILLink
                             batchFile("tests\\scripts\\run-xunit-perf.cmd ${runXUnitPerfCommonArgs} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\performance\\linkbench\\linkbench -group ILLink -nowarmup")
-                            batchFile("xcopy.exe /VYQK bin\\sandbox\\Perf-*.* bin\\toArchive\\sandbox\\Logs\\Scenario\\LinkBench\\")
+                            batchFile("xcopy.exe /VYQK bin\\sandbox\\Logs\\Perf-*.* bin\\toArchive\\sandbox\\Logs\\Scenario\\LinkBench\\")
                         }
                     }
 
index a024fbbe5cf0362630464545eb48f59cdec8c8a9..fa95affc89f3f4c73152b72b9604b4ec89463ce9 100644 (file)
@@ -6,6 +6,7 @@
 
 setlocal ENABLEDELAYEDEXPANSION
   set ERRORLEVEL=
+  set DOTNET_MULTILEVEL_LOOKUP=0
   set BENCHVIEW_RUN_TYPE=local
   set CORECLR_REPO=%CD%
   set LV_SANDBOX_DIR=%CORECLR_REPO%\bin\sandbox
@@ -24,10 +25,13 @@ setlocal ENABLEDELAYEDEXPANSION
   set BENCHVIEW_GROUP=CoreCLR
   set HAS_WARMUP_RUN=--drop-first-value
   set BETTER=desc
+  set OPT_LEVEL=full_opt
+  set VALID_OPTLEVELS=min_opt full_opt tiered
 
   call :parse_command_line_arguments %*
   if defined USAGE_DISPLAYED exit /b %ERRORLEVEL%
 
+  call :is_valid_optlevel       || exit /b 1
   call :set_test_architecture   || exit /b 1
   call :set_collection_config   || exit /b 1
   call :verify_benchview_tools  || exit /b 1
@@ -46,10 +50,6 @@ setlocal ENABLEDELAYEDEXPANSION
     )
   )
 
-  if not defined OPT_LEVEL (
-    set OPT_LEVEL=full_opt
-  )
-
   if not defined JIT_NAME (
     set JIT_NAME=ryujit
   )
@@ -88,12 +88,7 @@ setlocal
     )
   )
 
-  rem setup optimisation level
-  if DEFINED OPT_LEVEL (
-    if /I "%OPT_LEVEL%" == "min_opt" (
-        set COMPlus_JITMinOpts=1
-    )
-  )
+  call :setup_optimization_level
 
   rem CORE_ROOT environment variable is used by some benchmarks such as Roslyn / CscBench.
   set CORE_ROOT=%LV_SANDBOX_DIR%
@@ -108,7 +103,7 @@ setlocal
 
   set LV_CMD=
   if defined IS_SCENARIO_TEST (
-    set "LV_CMD=corerun.exe "%LV_SANDBOX_DIR%\%BENCHNAME%.%TEST_FILE_EXT%" --perf:outputdir "%LV_BENCHMARKS_OUTPUT_DIR%" --perf:runid "%LV_RUNID%" --target-architecture "%TEST_ARCHITECTURE%""
+    set "LV_CMD=%STABILITY_PREFIX% corerun.exe "%LV_SANDBOX_DIR%\%BENCHNAME%.%TEST_FILE_EXT%" --perf:outputdir "%LV_BENCHMARKS_OUTPUT_DIR%" --perf:runid "%LV_RUNID%" --target-architecture "%TEST_ARCHITECTURE%" --perf:collect %COLLECTION_FLAGS%"
   ) else (
     set "LV_CMD=%STABILITY_PREFIX% corerun.exe PerfHarness.dll "%LV_SANDBOX_DIR%\%BENCHNAME%.%TEST_FILE_EXT%" --perf:outputdir "%LV_BENCHMARKS_OUTPUT_DIR%" --perf:runid "%LV_RUNID%" --perf:collect %COLLECTION_FLAGS%"
   )
@@ -365,7 +360,7 @@ rem ****************************************************************************
   rem   files on the current working directory.
   set LV_PATTERN="%LV_BENCHMARKS_OUTPUT_DIR%\%LV_RUNID%-%BENCHNAME%.xml"
   rem The first pattern is the general case, the second is used by IlLink
-  if defined IS_SCENARIO_TEST set LV_PATTERN="%LV_RUNID%-%BENCHNAME%.xml" "%LV_RUNID%-*-%BENCHNAME%.xml"
+  if defined IS_SCENARIO_TEST set LV_PATTERN="%LV_BENCHMARKS_OUTPUT_DIR%\%LV_RUNID%-%BENCHNAME%.xml" "%LV_BENCHMARKS_OUTPUT_DIR%\%LV_RUNID%-*-%BENCHNAME%.xml"
 
   for %%f in (%LV_PATTERN%) do (
     if exist "%%~f" (
@@ -383,6 +378,11 @@ rem ****************************************************************************
 rem   Generates BenchView's submission data and upload it
 rem ****************************************************************************
 setlocal
+  if not exist measurement.json (
+    call :print_error measurement.json does not exist. There is no data to be uploaded.
+    exit /b 1
+  )
+
   set LV_SUBMISSION_ARGS=
   set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --build "%CORECLR_REPO%\build.json"
   set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --machine-data "%CORECLR_REPO%\machinedata.json"
@@ -419,7 +419,7 @@ rem ****************************************************************************
 rem   Script's usage.
 rem ****************************************************************************
   set USAGE_DISPLAYED=1
-  echo run-xunit-perf.cmd -testBinLoc ^<path_to_tests^> [-library] [-arch] ^<x86^|x64^> [-configuration] ^<Release^|Debug^> [-generateBenchviewData] ^<path_to_benchview_tools^> [-warmup] [-better] ^<asc ^| desc^> [-group] ^<group^> [-runtype] ^<rolling^|private^> [-scenarioTest] [-collectionFlags] ^<default^+CacheMisses^+InstructionRetired^+BranchMispredictions^+gcapi^> [-outputdir] ^<outputdir^>
+  echo run-xunit-perf.cmd -testBinLoc ^<path_to_tests^> [-library] [-arch] ^<x86^|x64^> [-configuration] ^<Release^|Debug^> [-generateBenchviewData] ^<path_to_benchview_tools^> [-warmup] [-better] ^<asc ^| desc^> [-group] ^<group^> [-runtype] ^<rolling^|private^> [-scenarioTest] [-collectionFlags] ^<default^+CacheMisses^+InstructionRetired^+BranchMispredictions^+gcapi^> [-outputdir] ^<outputdir^> [-optLevel] ^<%VALID_OPTLEVELS: =^|%^>
   echo/
   echo For the path to the tests you can pass a parent directory and the script will grovel for
   echo all tests in subdirectories and run them.
@@ -436,6 +436,7 @@ rem ****************************************************************************
   echo -scenarioTest should be included if you are running a scenario benchmark.
   echo -outputdir Specifies the directory where the generated performance output will be saved.
   echo -collectionFlags This is used to specify what collectoin flags get passed to the performance
+  echo -optLevel Specifies the optimization level to be used by the jit.
   echo harness that is doing the test running.  If this is not specified we only use stopwatch.
   echo Other flags are "default", which is the whatever the test being run specified, "CacheMisses",
   echo "BranchMispredictions", and "InstructionsRetired".
@@ -459,6 +460,47 @@ rem ****************************************************************************
   echo/[%DATE%][%TIME:~0,-3%] %*
   exit /b %ERRORLEVEL%
 
+:is_valid_optlevel
+rem ****************************************************************************
+rem   Validates the optlevel flag set by the user.
+rem ****************************************************************************
+setlocal
+  if not defined OPT_LEVEL (
+    call :print_error OPT_LEVEL is undefined.
+    exit /b 1
+  )
+
+  set "LV_IS_VALID_OPTLEVEL="
+  for %%i in (%VALID_OPTLEVELS%) do (
+    if /i "%%~i" == "%OPT_LEVEL%" (
+      set "LV_IS_VALID_OPTLEVEL=1"
+    )
+  )
+
+  if not defined LV_IS_VALID_OPTLEVEL (
+    call :print_error Unknown OPT_LEVEL=%OPT_LEVEL%
+    exit /b 1
+  )
+endlocal& exit /b 0
+
+:setup_optimization_level
+rem ****************************************************************************
+rem   Setup the appropriate environment variables needed for the selected
+rem   optlevel.
+rem ****************************************************************************
+  set "COMPlus_JITMinOpts="
+  set "COMPLUS_EXPERIMENTAL_TieredCompilation="
+
+  if /I "%OPT_LEVEL%" == "min_opt" (
+    set COMPlus_JITMinOpts=1
+    exit /b 0
+  )
+  if /I "%OPT_LEVEL%" == "tiered" (
+    set COMPLUS_EXPERIMENTAL_TieredCompilation=1
+    exit /b 0
+  )
+exit /b 0
+
 :run_cmd
 rem ****************************************************************************
 rem   Function wrapper used to send the command line being executed to the
index c6700f4bba56de1001b4e9ac2b0fa4c5abd62b51..677e60a39bb2cebdfe233af57d50497cd5aefe9c 100755 (executable)
@@ -43,6 +43,8 @@ function print_usage {
     echo '  --benchViewOS=<os>                : Specify the os that will be used to insert data into Benchview.'
     echo '  --runType=<local|private|rolling> : Specify the runType for Benchview. [Default: local]'
     echo '  --outputdir                       : Specifies the directory where the generated performance output will be saved.'
+    echo '  --optLevel=<min_opt|full_opt|tiered>'
+    echo '                                    : Specifies the optimization level to be used by the jit.'
 }
 
 # libExtension determines extension for dynamic library files
@@ -96,6 +98,37 @@ function handle_ctrl_c {
     exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
 }
 
+function is_valid_optlevel {
+    if [ -z "$optLevel" ]; then
+        echo "[ERROR] --optLevel is required."
+        return 1
+    fi
+
+    declare -A valid_optlevels=(
+        [min_opt]=1 [full_opt]=1 [tiered]=1
+    )
+    [[ -n "${valid_optlevels[$optLevel]}" ]] || {
+        echo "[ERROR] Specified an unknown optLevel=$optLevel";
+        return 1;
+    }
+    return 0
+}
+
+function setup_optimization_level {
+    unset COMPlus_JITMinOpts
+    unset COMPLUS_EXPERIMENTAL_TieredCompilation
+
+    if [ "$optLevel" == "min_opt" ]; then
+        export COMPlus_JITMinOpts=1
+        return 0
+    fi
+    if [ "$optLevel" == "tiered" ]; then
+        export COMPLUS_EXPERIMENTAL_TieredCompilation=1
+        return 0
+    fi
+    return 0
+}
+
 # Register the Ctrl-C handler
 trap handle_ctrl_c INT
 
@@ -204,6 +237,8 @@ function copy_test_native_bin_to_test_root {
         done
 }
 
+export DOTNET_MULTILEVEL_LOOKUP=0
+
 # Exit code constants
 readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
 readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
@@ -298,6 +333,7 @@ if [ ! -z "$BENCHVIEW_TOOLS_PATH" ] && { [ ! -d "$BENCHVIEW_TOOLS_PATH" ]; }; th
     echo BenchView path: "$BENCHVIEW_TOOLS_PATH" was specified, but it does not exist.
     exit $EXIT_CODE_EXCEPTION
 fi
+is_valid_optlevel || exit $EXIT_CODE_EXCEPTION
 if [ "$collectionflags" == "stopwatch" ]; then
     perfCollection=Off
 else
@@ -319,10 +355,7 @@ if [ ! -d "$benchmarksOutputDir" ]; then
     mkdir -p "$benchmarksOutputDir" || { echo "Failed to delete $benchmarksOutputDir"; exit 1; }
 fi
 
-# Set minopts
-if ["$optLevel" == "min_opt"]; then
-    export COMPlus_JITMinOpts=1
-fi
+setup_optimization_level || exit $EXIT_CODE_EXCEPTION
 
 cd $CORE_ROOT
 
index c46e42ad1d1eb65cc8ef00897c8b3433eb7d1e83..77cba12414c8058efed73cf6b320389ddf0f7e82 100644 (file)
@@ -26,7 +26,7 @@
       <Version>$(MicrosoftDiagnosticsTracingTraceEventPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.NETCore.Platforms">
-      <Version>2.0.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftNETCorePlatformsPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Console">
       <Version>4.4.0-beta-24913-02</Version>
index 1b85f9b91ab71b6e3b832e86f08b9cf305fe0ed0..d554c165f6b4855e662c9716c2e7c0a2c5cc6b8d 100644 (file)
@@ -23,7 +23,7 @@
       <Version>$(MicrosoftDiagnosticsTracingTraceEventPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.NETCore.Platforms">
-      <Version>2.0.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftNETCorePlatformsPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="Newtonsoft.Json">
       <Version>7.0.1</Version>
index 1c4f90faca612b7fe0a3a2240bae075ea6638667..5ade6863309eea12d6ad206624e3daae4a72e71c 100644 (file)
@@ -23,7 +23,7 @@
       <Version>$(MicrosoftDiagnosticsTracingTraceEventPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.NETCore.Platforms">
-      <Version>2.0.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftNETCorePlatformsPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Collections.NonGeneric">
       <Version>4.4.0-beta-24913-02</Version>
       <Version>4.4.0-beta-24913-02</Version>
     </PackageReference>
     <PackageReference Include="System.Memory">
-      <Version>4.5.0-preview2-25504-02</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Numerics.Vectors">
-      <Version>4.4.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Reflection">
       <Version>4.4.0-beta-24913-02</Version>
       <Version>4.4.0-beta-24913-02</Version>
     </PackageReference>
     <PackageReference Include="System.Reflection.TypeExtensions">
-      <Version>4.4.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Runtime">
       <Version>4.4.0-beta-24913-02</Version>
     </PackageReference>
     <PackageReference Include="System.Runtime.CompilerServices.Unsafe">
-      <Version>4.4.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Runtime.Extensions">
       <Version>4.4.0-beta-24913-02</Version>
diff --git a/tests/src/performance/Scenario/JitBench/IterationData.cs b/tests/src/performance/Scenario/JitBench/IterationData.cs
new file mode 100644 (file)
index 0000000..0c23f4f
--- /dev/null
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Xunit.Performance.Api;
+
+namespace JitBench
+{
+    /// <summary>
+    /// Interface used to buffer each scenario iteration/run for later post processing.
+    /// </summary>
+    internal class IterationData
+    {
+        public ScenarioExecutionResult ScenarioExecutionResult { get; set; }
+
+        public string StandardOutput { get; set; }
+
+        public double StartupTime { get; set; }
+
+        public double FirstRequestTime { get; set; }
+
+        public double SteadystateTime { get; set; }
+    }
+}
index 02ce7700f4b66fc60eb525e41134e8abdb6a3423..04077f49009af48169b1baefdcbe4106caaefa69 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
   <PropertyGroup>
@@ -12,8 +12,8 @@
     <FileAlignment>512</FileAlignment>
     <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
-    <NuGetTargetMoniker>.NETStandard,Version=v1.5</NuGetTargetMoniker>
-    <NuGetTargetMonikerShort>netstandard1.5</NuGetTargetMonikerShort>
+    <NuGetTargetMoniker>.NETStandard,Version=v1.6</NuGetTargetMoniker>
+    <NuGetTargetMonikerShort>netstandard1.6</NuGetTargetMonikerShort>
   </PropertyGroup>
   <!-- Default configurations to help VS understand the configurations -->
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
@@ -29,7 +29,9 @@
     </CodeAnalysisDependentAssemblyPaths>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="IterationData.cs" />
     <Compile Include="JitBenchHarness.cs" />
+    <Compile Include="JitBenchHarnessOptions.cs" />
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
@@ -38,4 +40,4 @@
   <PropertyGroup>
     <ProjectAssetsFile>..\..\obj\project.assets.json</ProjectAssetsFile>
   </PropertyGroup>
-</Project>
+</Project>
\ No newline at end of file
index 035b74f56f2ae6e130b2a691322c169e10a6a50c..596052437debddc3cfcec8304fd77d1e7e971154 100644 (file)
@@ -2,9 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using CommandLine;
-using CommandLine.Text;
 using Microsoft.Xunit.Performance.Api;
+using Microsoft.Xunit.Performance.Api.Profilers.Etw;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -12,12 +11,12 @@ using System.IO;
 using System.IO.Compression;
 using System.Linq;
 using System.Net.Http;
-using System.Reflection;
+using System.Text;
 using System.Text.RegularExpressions;
 
 namespace JitBench
 {
-    class Program
+    class JitBenchHarness
     {
         static void Main(string[] args)
         {
@@ -30,6 +29,10 @@ namespace JitBench
                 ProcessStartInfo startInfo = options.UseExistingSetup ? UseExistingSetup() : CreateNewSetup();
 
                 string scenarioName = "MusicStore";
+                if (!startInfo.Environment.ContainsKey("DOTNET_MULTILEVEL_LOOKUP"))
+                    throw new InvalidOperationException("DOTNET_MULTILEVEL_LOOKUP was not defined.");
+                if (startInfo.Environment["DOTNET_MULTILEVEL_LOOKUP"] != "0")
+                    throw new InvalidOperationException("DOTNET_MULTILEVEL_LOOKUP was not set to 0.");
 
                 if (options.EnableTiering)
                 {
@@ -55,30 +58,60 @@ namespace JitBench
                     scenarioName += " NoNgen";
                 }
 
-                var scenarioConfiguration = CreateScenarioConfiguration();
-
-                h.RunScenario(startInfo, () => { PrintHeader(scenarioName); }, PostIteration, PostProcessing, scenarioConfiguration);
+                var program = new JitBenchHarness("JitBench");
+                try
+                {
+                    var scenarioConfiguration = new ScenarioConfiguration(TimeSpan.FromMilliseconds(60000), startInfo) {
+                        Iterations = (int)options.Iterations,
+                        PreIterationDelegate = program.PreIteration,
+                        PostIterationDelegate = program.PostIteration,
+                    };
+                    var processesOfInterest = new string[] {
+                        "dotnet.exe",
+                    };
+                    var modulesOfInterest = new string[] {
+                        "Anonymously Hosted DynamicMethods Assembly",
+                        "clrjit.dll",
+                        "coreclr.dll",
+                        "dotnet.exe",
+                        "MusicStore.dll",
+                        "ntoskrnl.exe",
+                        "System.Private.CoreLib.dll",
+                        "Unknown",
+                    };
+
+                    if (!File.Exists(startInfo.FileName))
+                        throw new FileNotFoundException(startInfo.FileName);
+                    if (!Directory.Exists(startInfo.WorkingDirectory))
+                        throw new DirectoryNotFoundException(startInfo.WorkingDirectory);
+
+                    h.RunScenario(scenarioConfiguration, teardownDelegate: () => {
+                        return program.PostRun("MusicStore", processesOfInterest, modulesOfInterest);
+                    });
+                }
+                catch
+                {
+                    Console.WriteLine(program.StandardOutput);
+                    Console.WriteLine(program.StandardError);
+                    throw;
+                }
             }
         }
 
-        private static ScenarioConfiguration CreateScenarioConfiguration()
+        public JitBenchHarness(string scenarioBenchmarkName)
         {
-            var config = new ScenarioConfiguration(TimeSpan.FromMilliseconds(60000))
-            {
-                Iterations = (int)s_iterations
-            };
-            return config;
+            _scenarioBenchmarkName = scenarioBenchmarkName;
+            _stdout = new StringBuilder();
+            _stderr = new StringBuilder();
+            IterationsData = new List<IterationData>();
         }
 
+        public string StandardOutput => _stdout.ToString();
+
+        public string StandardError => _stderr.ToString();
+
         private static void SetupStatics(JitBenchHarnessOptions options)
         {
-            // Set variables we will need to store results.
-            s_iterations = options.Iterations;
-            s_iteration = 0;
-            s_startupTimes = new double[s_iterations];
-            s_requestTimes = new double[s_iterations];
-            s_steadystateTimes = new double[s_iterations];
-
             s_temporaryDirectory = options.IntermediateOutputDirectory;
             s_targetArchitecture = options.TargetArchitecture;
             if (string.IsNullOrWhiteSpace(s_targetArchitecture))
@@ -124,7 +157,7 @@ namespace JitBench
 
         private static void InstallSharedRuntime()
         {
-            var psi = new ProcessStartInfo() {
+            var psi = new ProcessStartInfo {
                 WorkingDirectory = s_jitBenchDevDirectory,
                 FileName = @"powershell.exe",
                 Arguments = $".\\Dotnet-Install.ps1 -SharedRuntime -InstallDir .dotnet -Channel master -Architecture {s_targetArchitecture}"
@@ -134,7 +167,7 @@ namespace JitBench
 
         private static IDictionary<string, string> InstallDotnet()
         {
-            var psi = new ProcessStartInfo() {
+            var psi = new ProcessStartInfo {
                 WorkingDirectory = s_jitBenchDevDirectory,
                 FileName = @"powershell.exe",
                 Arguments = $".\\Dotnet-Install.ps1 -InstallDir .dotnet -Channel master -Architecture {s_targetArchitecture}"
@@ -192,7 +225,7 @@ namespace JitBench
         private static IDictionary<string, string> GenerateStore(IDictionary<string, string> environment)
         {
             // This step generates some environment variables needed later.
-            var psi = new ProcessStartInfo() {
+            var psi = new ProcessStartInfo {
                 WorkingDirectory = s_jitBenchDevDirectory,
                 FileName = "powershell.exe",
                 Arguments = $"-Command \".\\AspNet-GenerateStore.ps1 -InstallDir .store -Architecture {s_targetArchitecture} -Runtime win7-{s_targetArchitecture}; gi env:JITBENCH_*, env:DOTNET_SHARED_STORE | %{{ \\\"$($_.Name)=$($_.Value)\\\" }} 1>>{EnvironmentFileName}\""
@@ -224,9 +257,20 @@ namespace JitBench
             return environment;
         }
 
+        private static void DotNetInfo(string workingDirectory, string dotnetFileName, IDictionary<string, string> environment)
+        {
+            var psi = new ProcessStartInfo {
+                WorkingDirectory = workingDirectory,
+                FileName = dotnetFileName,
+                Arguments = "--info"
+            };
+
+            LaunchProcess(psi, 60000, environment);
+        }
+
         private static void RestoreMusicStore(string workingDirectory, string dotnetFileName, IDictionary<string, string> environment)
         {
-            var psi = new ProcessStartInfo() {
+            var psi = new ProcessStartInfo {
                 WorkingDirectory = workingDirectory,
                 FileName = dotnetFileName,
                 Arguments = "restore"
@@ -237,10 +281,14 @@ namespace JitBench
 
         private static void PublishMusicStore(string workingDirectory, string dotnetFileName, IDictionary<string, string> environment)
         {
-            var psi = new ProcessStartInfo() {
+            var manifest = environment["JITBENCH_ASPNET_MANIFEST"];
+            if (!File.Exists(manifest))
+                throw new FileNotFoundException(manifest);
+
+            var psi = new ProcessStartInfo {
                 WorkingDirectory = workingDirectory,
-                FileName = "cmd.exe",
-                Arguments = $"/C \"{dotnetFileName} publish -c Release -f {JitBenchTargetFramework} --manifest %JITBENCH_ASPNET_MANIFEST% /p:MvcRazorCompileOnPublish=false\""
+                FileName = dotnetFileName,
+                Arguments = $"publish -c Release -f {JitBenchTargetFramework} --manifest \"{manifest}\" /p:MvcRazorCompileOnPublish=false -o \"{MusicStorePublishDirectory}\""
             };
 
             LaunchProcess(psi, 300000, environment);
@@ -250,24 +298,30 @@ namespace JitBench
         private static IDictionary<string, string> GetInitialEnvironment()
         {
             // TODO: This is currently hardcoded, but we could probably pull it from the powershell cmdlet call.
-            var environment = new Dictionary<string, string> { { "PATH", $"{Path.Combine(s_jitBenchDevDirectory, ".dotnet")};{Environment.GetEnvironmentVariable("PATH")}" } };
+            var dotnetPath = Path.Combine(s_jitBenchDevDirectory, ".dotnet");
+            var dotnetexe = Path.Combine(dotnetPath, "dotnet.exe");
+            if (!File.Exists(dotnetexe))
+                throw new FileNotFoundException(dotnetexe);
+
+            var environment = new Dictionary<string, string> {
+                { "DOTNET_MULTILEVEL_LOOKUP", "0" },
+                { "PATH", $"{dotnetPath};{Environment.GetEnvironmentVariable("PATH")}" }
+            };
 
             return environment;
         }
 
-        private static ProcessStartInfo UseExistingSetup()
-        {
-            PrintHeader("Using existing SETUP");
-
-            var environment = GetInitialEnvironment();
-            environment = GetEnvironment(environment, Path.Combine(s_jitBenchDevDirectory, EnvironmentFileName));
-            ValidateEnvironment(environment);
+        private static string MusicStorePublishDirectory =>
+            Path.Combine(s_musicStoreDirectory, "bin", s_targetArchitecture, "Release", JitBenchTargetFramework, "publish");
 
-            var psi = new ProcessStartInfo()
-            {
-                FileName = "cmd.exe",
-                Arguments = $"/C \"{s_dotnetProcessFileName} MusicStore.dll 1>{MusicStoreRedirectedStandardOutputFileName}\"",
-                WorkingDirectory = Path.Combine(s_musicStoreDirectory, "bin", "Release", JitBenchTargetFramework, "publish")
+        private static ProcessStartInfo CreateJitBenchStartInfo(IDictionary<string, string> environment)
+        {
+            var psi = new ProcessStartInfo {
+                Arguments = "MusicStore.dll",
+                FileName = s_dotnetProcessFileName,
+                RedirectStandardError = true,
+                RedirectStandardOutput = true,
+                WorkingDirectory = MusicStorePublishDirectory,
             };
 
             foreach (KeyValuePair<string, string> pair in environment)
@@ -275,6 +329,18 @@ namespace JitBench
 
             return psi;
         }
+
+        private static ProcessStartInfo UseExistingSetup()
+        {
+            PrintHeader("Using existing SETUP");
+
+            IDictionary<string, string> environment = GetInitialEnvironment();
+            environment = GetEnvironment(environment, Path.Combine(s_jitBenchDevDirectory, EnvironmentFileName));
+            ValidateEnvironment(environment);
+
+            return CreateJitBenchStartInfo(environment);
+        }
+
         private static ProcessStartInfo CreateNewSetup()
         {
             PrintHeader("Starting SETUP");
@@ -292,130 +358,289 @@ namespace JitBench
 
             ModifySharedFramework();
 
+            DotNetInfo(s_musicStoreDirectory, s_dotnetProcessFileName, environment);
             RestoreMusicStore(s_musicStoreDirectory, s_dotnetProcessFileName, environment);
             PublishMusicStore(s_musicStoreDirectory, s_dotnetProcessFileName, environment);
 
-            var psi = new ProcessStartInfo() {
-                FileName = "cmd.exe",
-                Arguments = $"/C \"{s_dotnetProcessFileName} MusicStore.dll 1>{MusicStoreRedirectedStandardOutputFileName}\"",
-                WorkingDirectory = Path.Combine(s_musicStoreDirectory, "bin", "Release", JitBenchTargetFramework, "publish")
-            };
-
-            foreach (KeyValuePair<string, string> pair in environment)
-                psi.Environment.Add(pair.Key, pair.Value);
-
-            return psi;
+            return CreateJitBenchStartInfo(environment);
         }
 
         private static void ValidateEnvironment(IDictionary<string, string> environment)
         {
             var expectedVariables = new string[] {
+                "DOTNET_MULTILEVEL_LOOKUP",
                 "PATH",
                 "JITBENCH_ASPNET_MANIFEST",
                 "JITBENCH_FRAMEWORK_VERSION",
                 "JITBENCH_ASPNET_VERSION",
-                "DOTNET_SHARED_STORE"
+                "DOTNET_SHARED_STORE",
             };
             if (expectedVariables.Except(environment.Keys, StringComparer.OrdinalIgnoreCase).Any())
                 throw new Exception("Missing expected environment variables.");
+
+            Console.WriteLine("**********************************************************************");
+            foreach (var env in expectedVariables)
+                Console.WriteLine($"  {env}={environment[env]}");
+            Console.WriteLine("**********************************************************************");
         }
 
-        private const string MusicStoreRedirectedStandardOutputFileName = "measures.txt";
         private const string JitBenchRepoUrl = "https://github.com/aspnet/JitBench";
         private const string JitBenchCommitSha1Id = "b7e7b786c60daa255aacaea85006afe4d4ec8306";
         private const string JitBenchTargetFramework = "netcoreapp2.1";
         private const string EnvironmentFileName = "JitBenchEnvironment.txt";
 
-        private static void PostIteration()
+        private void PreIteration(Scenario scenario)
         {
-            var path = Path.Combine(s_jitBenchDevDirectory, "src", "MusicStore", "bin", "Release", JitBenchTargetFramework, "publish");
-            path = Path.Combine(path, MusicStoreRedirectedStandardOutputFileName);
+            PrintHeader("Setting up data standard output/error process handlers.");
+
+            _stderr.Clear();
+            _stdout.Clear();
+
+            if (scenario.Process.StartInfo.RedirectStandardError)
+            {
+                scenario.Process.ErrorDataReceived += (object sender, DataReceivedEventArgs errorLine) => {
+                    if (!string.IsNullOrEmpty(errorLine.Data))
+                        _stderr.AppendLine(errorLine.Data);
+                };
+            }
+
+            if (scenario.Process.StartInfo.RedirectStandardInput)
+                throw new NotImplementedException("RedirectStandardInput has not been implemented yet.");
+
+            if (scenario.Process.StartInfo.RedirectStandardOutput)
+            {
+                scenario.Process.OutputDataReceived += (object sender, DataReceivedEventArgs outputLine) => {
+                    if (!string.IsNullOrEmpty(outputLine.Data))
+                        _stdout.AppendLine(outputLine.Data);
+                };
+            }
+        }
+
+        private void PostIteration(ScenarioExecutionResult scenarioExecutionResult)
+        {
+            PrintHeader("Processing iteration results.");
 
             double? startupTime = null;
-            double? requestTime = null;
+            double? firstRequestTime = null;
             double? steadyStateAverageTime = null;
-            foreach (string line in File.ReadLines(path))
+
+            using (var reader = new StringReader(_stdout.ToString()))
             {
-                Match match = Regex.Match(line, @"^Server started in (\d+)ms$");
-                if (match.Success && match.Groups.Count == 2)
+                string line;
+                while ((line = reader.ReadLine()) != null)
                 {
-                    startupTime = Convert.ToDouble(match.Groups[1].Value);
-                    continue;
-                }
+                    Match match = Regex.Match(line, @"^Server started in (\d+)ms$");
+                    if (match.Success && match.Groups.Count == 2)
+                    {
+                        startupTime = Convert.ToDouble(match.Groups[1].Value);
+                        continue;
+                    }
 
-                match = Regex.Match(line, @"^Request took (\d+)ms$");
-                if (match.Success && match.Groups.Count == 2)
-                {
-                    requestTime = Convert.ToDouble(match.Groups[1].Value);
-                    continue;
-                }
+                    match = Regex.Match(line, @"^Request took (\d+)ms$");
+                    if (match.Success && match.Groups.Count == 2)
+                    {
+                        firstRequestTime = Convert.ToDouble(match.Groups[1].Value);
+                        continue;
+                    }
 
-                match = Regex.Match(line, @"^Steadystate average response time: (\d+)ms$");
-                if (match.Success && match.Groups.Count == 2)
-                {
-                    steadyStateAverageTime = Convert.ToDouble(match.Groups[1].Value);
-                    break;
+                    match = Regex.Match(line, @"^Steadystate average response time: (\d+)ms$");
+                    if (match.Success && match.Groups.Count == 2)
+                    {
+                        steadyStateAverageTime = Convert.ToDouble(match.Groups[1].Value);
+                        break;
+                    }
                 }
             }
 
             if (!startupTime.HasValue)
                 throw new Exception("Startup time was not found.");
-            if (!requestTime.HasValue)
+            if (!firstRequestTime.HasValue)
                 throw new Exception("First Request time was not found.");
             if (!steadyStateAverageTime.HasValue)
                 throw new Exception("Steady state average response time not found.");
 
-            s_startupTimes[s_iteration] = startupTime.Value;
-            s_requestTimes[s_iteration] = requestTime.Value;
-            s_steadystateTimes[s_iteration] = steadyStateAverageTime.Value;
+            IterationsData.Add(new IterationData {
+                ScenarioExecutionResult = scenarioExecutionResult,
+                StandardOutput = _stdout.ToString(),
+                StartupTime = startupTime.Value,
+                FirstRequestTime = firstRequestTime.Value,
+                SteadystateTime = steadyStateAverageTime.Value,
+            });
 
-            PrintRunningStepInformation($"{s_iteration} Server started in {s_startupTimes[s_iteration]}ms");
-            PrintRunningStepInformation($"{s_iteration} Request took {s_requestTimes[s_iteration]}ms");
-            PrintRunningStepInformation($"{s_iteration} Cold start time (server start + first request time): {s_startupTimes[s_iteration] + s_requestTimes[s_iteration]}ms");
-            PrintRunningStepInformation($"{s_iteration} Average steady state response {s_steadystateTimes[s_iteration]}ms");
+            PrintRunningStepInformation($"({IterationsData.Count}) Server started in {IterationsData.Last().StartupTime}ms");
+            PrintRunningStepInformation($"({IterationsData.Count}) Request took {IterationsData.Last().FirstRequestTime}ms");
+            PrintRunningStepInformation($"({IterationsData.Count}) Cold start time (server start + first request time): {IterationsData.Last().StartupTime + IterationsData.Last().FirstRequestTime}ms");
+            PrintRunningStepInformation($"({IterationsData.Count}) Average steady state response {IterationsData.Last().SteadystateTime}ms");
 
-            ++s_iteration;
+            _stdout.Clear();
+            _stderr.Clear();
         }
 
-        private static ScenarioBenchmark PostProcessing()
+        private ScenarioBenchmark PostRun(
+            string scenarioTestModelName,
+            IReadOnlyCollection<string> processesOfInterest,
+            IReadOnlyCollection<string> modulesOfInterest)
         {
-            PrintHeader("Starting POST");
+            PrintHeader("Post-Processing scenario data.");
 
-            var scenarioBenchmark = new ScenarioBenchmark("MusicStore") {
-                Namespace = "JitBench"
-            };
+            var scenarioBenchmark = new ScenarioBenchmark(_scenarioBenchmarkName);
 
-            // Create (measured) test entries for this scenario.
-            var startup = new ScenarioTestModel("Startup");
-            scenarioBenchmark.Tests.Add(startup);
+            foreach (var iter in IterationsData)
+            {
+                var scenarioExecutionResult = iter.ScenarioExecutionResult;
+                var scenarioTestModel = scenarioBenchmark.Tests
+                    .SingleOrDefault(t => t.Name == scenarioTestModelName);
 
-            var request = new ScenarioTestModel("First Request");
-            scenarioBenchmark.Tests.Add(request);
+                if (scenarioTestModel == null)
+                {
+                    scenarioTestModel = new ScenarioTestModel(scenarioTestModelName);
+                    scenarioBenchmark.Tests.Add(scenarioTestModel);
 
-            // TODO: add response time once jit bench is updated to
-            // report more reasonable numbers.
+                    // Add measured metrics to each test.
+                    scenarioTestModel.Performance.Metrics.Add(ElapsedTimeMilliseconds);
+                }
 
-            // Add measured metrics to each test.
-            startup.Performance.Metrics.Add(new MetricModel {
-                Name = "Duration",
-                DisplayName = "Duration",
-                Unit = "ms"
-            });
-            request.Performance.Metrics.Add(new MetricModel {
-                Name = "Duration",
-                DisplayName = "Duration",
-                Unit = "ms"
-            });
+                scenarioTestModel.Performance.IterationModels.Add(new IterationModel {
+                    Iteration = new Dictionary<string, double> {
+                        { ElapsedTimeMilliseconds.Name, (scenarioExecutionResult.ProcessExitInfo.ExitTime - scenarioExecutionResult.ProcessExitInfo.StartTime).TotalMilliseconds},
+                    }
+                });
 
-            for (int i = 0; i < s_iterations; ++i)
+                // Create (measured) test entries for this scenario.
+                var startup = scenarioBenchmark.Tests
+                    .SingleOrDefault(t => t.Name == "Startup" && t.Namespace == scenarioTestModel.Name);
+                if (startup == null)
+                {
+                    startup = new ScenarioTestModel("Startup") {
+                        Namespace = scenarioTestModel.Name,
+                    };
+                    scenarioBenchmark.Tests.Add(startup);
+
+                    // Add measured metrics to each test.
+                    startup.Performance.Metrics.Add(ElapsedTimeMilliseconds);
+                }
+
+                var firstRequest = scenarioBenchmark.Tests
+                    .SingleOrDefault(t => t.Name == "First Request" && t.Namespace == scenarioTestModel.Name);
+                if (firstRequest == null)
+                {
+                    firstRequest = new ScenarioTestModel("First Request") {
+                        Namespace = scenarioTestModel.Name,
+                    };
+                    scenarioBenchmark.Tests.Add(firstRequest);
+
+                    // Add measured metrics to each test.
+                    firstRequest.Performance.Metrics.Add(ElapsedTimeMilliseconds);
+                }
+
+                startup.Performance.IterationModels.Add(new IterationModel {
+                    Iteration = new Dictionary<string, double> {
+                            { ElapsedTimeMilliseconds.Name, iter.StartupTime },
+                        },
+                });
+
+                firstRequest.Performance.IterationModels.Add(new IterationModel {
+                    Iteration = new Dictionary<string, double> {
+                            { ElapsedTimeMilliseconds.Name, iter.FirstRequestTime },
+                        },
+                });
+
+                if (!string.IsNullOrWhiteSpace(iter.ScenarioExecutionResult.EventLogFileName) &&
+                    File.Exists(iter.ScenarioExecutionResult.EventLogFileName))
+                {
+                    // Adding ETW data.
+                    scenarioBenchmark = AddEtwData(
+                        scenarioBenchmark, iter.ScenarioExecutionResult, processesOfInterest, modulesOfInterest);
+                }
+            }
+
+            for (int i = scenarioBenchmark.Tests.Count - 1; i >= 0; i--)
+                if (scenarioBenchmark.Tests[i].Performance.IterationModels.All(iter => iter.Iteration.Count == 0))
+                    scenarioBenchmark.Tests.RemoveAt(i);
+
+            return scenarioBenchmark;
+        }
+
+        private static ScenarioBenchmark AddEtwData(
+            ScenarioBenchmark scenarioBenchmark,
+            ScenarioExecutionResult scenarioExecutionResult,
+            IReadOnlyCollection<string> processesOfInterest,
+            IReadOnlyCollection<string> modulesOfInterest)
+        {
+            var metricModels = scenarioExecutionResult.PerformanceMonitorCounters
+                .Select(pmc => new MetricModel {
+                    DisplayName = pmc.DisplayName,
+                    Name = pmc.Name,
+                    Unit = pmc.Unit,
+                });
+
+            // Get the list of processes of interest.
+            Console.WriteLine($"Parsing: {scenarioExecutionResult.EventLogFileName}");
+            var processes = new SimpleTraceEventParser().GetProfileData(scenarioExecutionResult);
+
+            // Extract the Pmc data for each one of the processes.
+            foreach (var process in processes)
             {
-                var startupIteration = new IterationModel { Iteration = new Dictionary<string, double>() };
-                startupIteration.Iteration.Add("Duration", s_startupTimes[i]);
-                startup.Performance.IterationModels.Add(startupIteration);
+                if (!processesOfInterest.Any(p => p.Equals(process.Name, StringComparison.OrdinalIgnoreCase)))
+                    continue;
+
+                var processTest = scenarioBenchmark.Tests
+                    .SingleOrDefault(t => t.Name == process.Name && t.Namespace == "");
+                if (processTest == null)
+                {
+                    processTest = new ScenarioTestModel(process.Name) {
+                        Namespace = "",
+                    };
+                    scenarioBenchmark.Tests.Add(processTest);
+
+                    // Add metrics definitions.
+                    processTest.Performance.Metrics.Add(ElapsedTimeMilliseconds);
+                    processTest.Performance.Metrics.AddRange(metricModels);
+                }
 
-                var requestIteration = new IterationModel { Iteration = new Dictionary<string, double>() };
-                requestIteration.Iteration.Add("Duration", s_requestTimes[i]);
-                request.Performance.IterationModels.Add(requestIteration);
+                var processIterationModel = new IterationModel {
+                    Iteration = new Dictionary<string, double>()
+                };
+                processTest.Performance.IterationModels.Add(processIterationModel);
+
+                processIterationModel.Iteration.Add(
+                    ElapsedTimeMilliseconds.Name, process.LifeSpan.Duration.TotalMilliseconds);
+
+                // Add process metrics values.
+                foreach (var pmcData in process.PerformanceMonitorCounterData)
+                    processIterationModel.Iteration.Add(pmcData.Key.Name, pmcData.Value);
+
+                foreach (var module in process.Modules)
+                {
+                    var moduleName = Path.GetFileName(module.FullName);
+                    if (modulesOfInterest.Any(m => m.Equals(moduleName, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        var moduleTestName = $"{moduleName}";
+                        var moduleTest = scenarioBenchmark.Tests
+                            .SingleOrDefault(t => t.Name == moduleTestName && t.Namespace == process.Name);
+
+                        if (moduleTest == null)
+                        {
+                            moduleTest = new ScenarioTestModel(moduleTestName) {
+                                Namespace = process.Name,
+                                Separator = "!",
+                            };
+                            scenarioBenchmark.Tests.Add(moduleTest);
+
+                            // Add metrics definitions.
+                            moduleTest.Performance.Metrics.AddRange(metricModels);
+                        }
+
+                        var moduleIterationModel = new IterationModel {
+                            Iteration = new Dictionary<string, double>()
+                        };
+                        moduleTest.Performance.IterationModels.Add(moduleIterationModel);
+
+                        // 5. Add module metrics values.
+                        foreach (var pmcData in module.PerformanceMonitorCounterData)
+                            moduleIterationModel.Iteration.Add(pmcData.Key.Name, pmcData.Value);
+                    }
+                }
             }
 
             return scenarioBenchmark;
@@ -438,7 +663,7 @@ namespace JitBench
                 }
             }
 
-            using (var p = new Process() { StartInfo = processStartInfo })
+            using (var p = new System.Diagnostics.Process { StartInfo = processStartInfo })
             {
                 p.Start();
                 if (p.WaitForExit(timeoutMilliseconds) == false)
@@ -457,7 +682,7 @@ namespace JitBench
         {
             Console.WriteLine();
             Console.WriteLine("**********************************************************************");
-            Console.WriteLine($"** {message}");
+            Console.WriteLine($"** [{DateTime.Now}] {message}");
             Console.WriteLine("**********************************************************************");
         }
 
@@ -466,137 +691,27 @@ namespace JitBench
             Console.WriteLine($"-- {message}");
         }
 
-        private static uint s_iterations;
-        private static uint s_iteration;
-        private static double[] s_startupTimes;
-        private static double[] s_requestTimes;
-        private static double[] s_steadystateTimes;
+        private List<IterationData> IterationsData { get; }
+
+        private static MetricModel ElapsedTimeMilliseconds { get; } = new MetricModel {
+            DisplayName = "Duration",
+            Name = "Duration",
+            Unit = "ms",
+        };
+
+#if DEBUG
+        private const int NumberOfIterations = 2;
+#else
+        private const int NumberOfIterations = 11;
+#endif
+        private readonly string _scenarioBenchmarkName;
+        private readonly StringBuilder _stdout;
+        private readonly StringBuilder _stderr;
+
         private static string s_temporaryDirectory;
         private static string s_jitBenchDevDirectory;
         private static string s_dotnetProcessFileName;
         private static string s_musicStoreDirectory;
         private static string s_targetArchitecture;
-
-        /// <summary>
-        /// Provides an interface to parse the command line arguments passed to the JitBench harness.
-        /// </summary>
-        private sealed class JitBenchHarnessOptions
-        {
-            public JitBenchHarnessOptions()
-            {
-                _tempDirectory = Directory.GetCurrentDirectory();
-                _iterations = 11;
-            }
-
-            [Option("use-existing-setup", Required = false, HelpText = "Use existing setup.")]
-            public Boolean UseExistingSetup { get; set; }
-
-            [Option("tiering", Required = false, HelpText = "Enable tiered jit.")]
-            public Boolean EnableTiering { get; set; }
-
-            [Option("minopts", Required = false, HelpText = "Force jit to use minopt codegen.")]
-            public Boolean Minopts { get; set; }
-
-            [Option("disable-r2r", Required = false, HelpText = "Disable loading of R2R images.")]
-            public Boolean DisableR2R { get; set; }
-
-            [Option("disable-ngen", Required = false, HelpText = "Disable loading of ngen images.")]
-            public Boolean DisableNgen { get; set; }
-
-            [Option("iterations", Required = false, HelpText = "Number of iterations to run.")]
-            public uint Iterations { get { return _iterations; } set { _iterations = value; } }
-
-            [Option('o', Required = false, HelpText = "Specifies the intermediate output directory name.")]
-            public string IntermediateOutputDirectory
-            {
-                get { return _tempDirectory; }
-
-                set
-                {
-                    if (string.IsNullOrWhiteSpace(value))
-                        throw new InvalidOperationException("The intermediate output directory name cannot be null, empty or white space.");
-
-                    if (value.Any(c => Path.GetInvalidPathChars().Contains(c)))
-                        throw new InvalidOperationException("Specified intermediate output directory name contains invalid path characters.");
-
-                    _tempDirectory = Path.IsPathRooted(value) ? value : Path.GetFullPath(value);
-                    Directory.CreateDirectory(_tempDirectory);
-                }
-            }
-
-            [Option("target-architecture", Required = true, HelpText = "JitBench target architecture (It must match the built product that was copied into sandbox).")]
-            public string TargetArchitecture { get; set; }
-
-            public static JitBenchHarnessOptions Parse(string[] args)
-            {
-                using (var parser = new Parser((settings) => {
-                    settings.CaseInsensitiveEnumValues = true;
-                    settings.CaseSensitive = false;
-                    settings.HelpWriter = new StringWriter();
-                    settings.IgnoreUnknownArguments = true;
-                }))
-                {
-                    JitBenchHarnessOptions options = null;
-                    parser.ParseArguments<JitBenchHarnessOptions>(args)
-                        .WithParsed(parsed => options = parsed)
-                        .WithNotParsed(errors => {
-                            foreach (Error error in errors)
-                            {
-                                switch (error.Tag)
-                                {
-                                    case ErrorType.MissingValueOptionError:
-                                        throw new ArgumentException(
-                                                $"Missing value option for command line argument '{(error as MissingValueOptionError).NameInfo.NameText}'");
-                                    case ErrorType.HelpRequestedError:
-                                        Console.WriteLine(Usage());
-                                        Environment.Exit(0);
-                                        break;
-                                    case ErrorType.VersionRequestedError:
-                                        Console.WriteLine(new AssemblyName(typeof(JitBenchHarnessOptions).GetTypeInfo().Assembly.FullName).Version);
-                                        Environment.Exit(0);
-                                        break;
-                                    case ErrorType.BadFormatTokenError:
-                                    case ErrorType.UnknownOptionError:
-                                    case ErrorType.MissingRequiredOptionError:
-                                        throw new ArgumentException(
-                                                $"Missing required  command line argument '{(error as MissingRequiredOptionError).NameInfo.NameText}'");
-                                    case ErrorType.MutuallyExclusiveSetError:
-                                    case ErrorType.BadFormatConversionError:
-                                    case ErrorType.SequenceOutOfRangeError:
-                                    case ErrorType.RepeatedOptionError:
-                                    case ErrorType.NoVerbSelectedError:
-                                    case ErrorType.BadVerbSelectedError:
-                                    case ErrorType.HelpVerbRequestedError:
-                                        break;
-                                }
-                            }
-                        });
-                    return options;
-                }
-            }
-
-            public static string Usage()
-            {
-                var parser = new Parser((parserSettings) => {
-                    parserSettings.CaseInsensitiveEnumValues = true;
-                    parserSettings.CaseSensitive = false;
-                    parserSettings.EnableDashDash = true;
-                    parserSettings.HelpWriter = new StringWriter();
-                    parserSettings.IgnoreUnknownArguments = true;
-                });
-
-                var helpTextString = new HelpText {
-                    AddDashesToOption = true,
-                    AddEnumValuesToHelpText = true,
-                    AdditionalNewLineAfterOption = false,
-                    Heading = "JitBenchHarness",
-                    MaximumDisplayWidth = 80,
-                }.AddOptions(parser.ParseArguments<JitBenchHarnessOptions>(new string[] { "--help" })).ToString();
-                return helpTextString;
-            }
-
-            private string _tempDirectory;
-            private uint _iterations;
-        }
     }
 }
diff --git a/tests/src/performance/Scenario/JitBench/JitBenchHarnessOptions.cs b/tests/src/performance/Scenario/JitBench/JitBenchHarnessOptions.cs
new file mode 100644 (file)
index 0000000..65a283f
--- /dev/null
@@ -0,0 +1,136 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using CommandLine;
+using CommandLine.Text;
+using Microsoft.Xunit.Performance.Api;
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace JitBench
+{
+    /// <summary>
+    /// Provides an interface to parse the command line arguments passed to the JitBench harness.
+    /// </summary>
+    internal sealed class JitBenchHarnessOptions
+    {
+        public JitBenchHarnessOptions()
+        {
+            _tempDirectory = Directory.GetCurrentDirectory();
+            _iterations = 11;
+        }
+
+        [Option("use-existing-setup", Required = false, HelpText = "Use existing setup.")]
+        public Boolean UseExistingSetup { get; set; }
+
+        [Option("tiering", Required = false, HelpText = "Enable tiered jit.")]
+        public Boolean EnableTiering { get; set; }
+
+        [Option("minopts", Required = false, HelpText = "Force jit to use minopt codegen.")]
+        public Boolean Minopts { get; set; }
+
+        [Option("disable-r2r", Required = false, HelpText = "Disable loading of R2R images.")]
+        public Boolean DisableR2R { get; set; }
+
+        [Option("disable-ngen", Required = false, HelpText = "Disable loading of ngen images.")]
+        public Boolean DisableNgen { get; set; }
+
+        [Option("iterations", Required = false, HelpText = "Number of iterations to run.")]
+        public uint Iterations { get { return _iterations; } set { _iterations = value; } }
+
+        [Option('o', Required = false, HelpText = "Specifies the intermediate output directory name.")]
+        public string IntermediateOutputDirectory
+        {
+            get { return _tempDirectory; }
+
+            set
+            {
+                if (string.IsNullOrWhiteSpace(value))
+                    throw new InvalidOperationException("The intermediate output directory name cannot be null, empty or white space.");
+
+                if (value.Any(c => Path.GetInvalidPathChars().Contains(c)))
+                    throw new InvalidOperationException("Specified intermediate output directory name contains invalid path characters.");
+
+                _tempDirectory = Path.IsPathRooted(value) ? value : Path.GetFullPath(value);
+                Directory.CreateDirectory(_tempDirectory);
+            }
+        }
+
+        [Option("target-architecture", Required = true, HelpText = "JitBench target architecture (It must match the built product that was copied into sandbox).")]
+        public string TargetArchitecture { get; set; }
+
+        public static JitBenchHarnessOptions Parse(string[] args)
+        {
+            using (var parser = new Parser((settings) => {
+                settings.CaseInsensitiveEnumValues = true;
+                settings.CaseSensitive = false;
+                settings.HelpWriter = new StringWriter();
+                settings.IgnoreUnknownArguments = true;
+            }))
+            {
+                JitBenchHarnessOptions options = null;
+                parser.ParseArguments<JitBenchHarnessOptions>(args)
+                    .WithParsed(parsed => options = parsed)
+                    .WithNotParsed(errors => {
+                        foreach (Error error in errors)
+                        {
+                            switch (error.Tag)
+                            {
+                                case ErrorType.MissingValueOptionError:
+                                    throw new ArgumentException(
+                                            $"Missing value option for command line argument '{(error as MissingValueOptionError).NameInfo.NameText}'");
+                                case ErrorType.HelpRequestedError:
+                                    Console.WriteLine(Usage());
+                                    Environment.Exit(0);
+                                    break;
+                                case ErrorType.VersionRequestedError:
+                                    Console.WriteLine(new AssemblyName(typeof(JitBenchHarnessOptions).GetTypeInfo().Assembly.FullName).Version);
+                                    Environment.Exit(0);
+                                    break;
+                                case ErrorType.BadFormatTokenError:
+                                case ErrorType.UnknownOptionError:
+                                case ErrorType.MissingRequiredOptionError:
+                                    throw new ArgumentException(
+                                            $"Missing required  command line argument '{(error as MissingRequiredOptionError).NameInfo.NameText}'");
+                                case ErrorType.MutuallyExclusiveSetError:
+                                case ErrorType.BadFormatConversionError:
+                                case ErrorType.SequenceOutOfRangeError:
+                                case ErrorType.RepeatedOptionError:
+                                case ErrorType.NoVerbSelectedError:
+                                case ErrorType.BadVerbSelectedError:
+                                case ErrorType.HelpVerbRequestedError:
+                                    break;
+                            }
+                        }
+                    });
+                return options;
+            }
+        }
+
+        public static string Usage()
+        {
+            var parser = new Parser((parserSettings) => {
+                parserSettings.CaseInsensitiveEnumValues = true;
+                parserSettings.CaseSensitive = false;
+                parserSettings.EnableDashDash = true;
+                parserSettings.HelpWriter = new StringWriter();
+                parserSettings.IgnoreUnknownArguments = true;
+            });
+
+            var helpTextString = new HelpText {
+                AddDashesToOption = true,
+                AddEnumValuesToHelpText = true,
+                AdditionalNewLineAfterOption = false,
+                Heading = "JitBenchHarness",
+                MaximumDisplayWidth = 80,
+            }.AddOptions(parser.ParseArguments<JitBenchHarnessOptions>(new string[] { "--help" })).ToString();
+            return helpTextString;
+        }
+
+        private string _tempDirectory;
+        private uint _iterations;
+    }
+}
index 7e710a775e14be3835079faaf2e2416c32340f49..06b951c3075f3f7d41874ca725e7a6c97f8bb3ed 100644 (file)
@@ -1,17 +1,12 @@
-using Microsoft.Xunit.Performance;
-using Microsoft.Xunit.Performance.Api;
 using System;
-using System.Diagnostics;
 using System.Collections.Generic;
-using System.IO;
-using System.Reflection;
-using System.Runtime.Loader;
-using System.Text;
+using System.Diagnostics;
 using System.Globalization;
+using System.IO;
 using System.Linq;
+using System.Runtime.Loader;
 using System.Xml.Linq;
-using Xunit;
-using Xunit.Abstractions;
+using Microsoft.Xunit.Performance.Api;
 
 namespace LinkBench
 {
@@ -234,7 +229,6 @@ namespace LinkBench
 
     public class LinkBench
     {
-        private static ScenarioConfiguration scenarioConfiguration = new ScenarioConfiguration(new TimeSpan(2000000));
         private static MetricModel SizeMetric = new MetricModel { Name = "Size", DisplayName = "File Size", Unit = "MB" };
         private static MetricModel PercMetric = new MetricModel { Name = "Ratio", DisplayName = "Reduction", Unit = "Linked/Unlinked" };
         public static string Workspace;
@@ -472,7 +466,8 @@ namespace LinkBench
                 };
                 using (var h = new XunitPerformanceHarness(scriptArgs))
                 {
-                    h.RunScenario(emptyCmd, null, null, PostRun, scenarioConfiguration);
+                    var configuration = new ScenarioConfiguration(new TimeSpan(2000000), emptyCmd);
+                    h.RunScenario(configuration, PostRun);
                 }
             }
 
index f36537f19770784b56283099dc0fd182a9e06d2d..8605f1a442dc151bdd72d04065584d0d553ec786 100644 (file)
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <FileAlignment>512</FileAlignment>
     <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
-    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
-    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
-    <NuGetTargetMoniker>.NETStandard,Version=v1.4</NuGetTargetMoniker>
-    <NuGetTargetMonikerShort>netstandard1.4</NuGetTargetMonikerShort>
+    <NuGetTargetMoniker>.NETStandard,Version=v1.6</NuGetTargetMoniker>
+    <NuGetTargetMonikerShort>netstandard1.6</NuGetTargetMonikerShort>
   </PropertyGroup>
   <!-- Default configurations to help VS understand the configurations -->
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
   <PropertyGroup>
     <RestoreOutputPath>..\obj</RestoreOutputPath>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
@@ -36,4 +35,7 @@
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
   </ItemGroup>
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), performance.targets))\performance.targets" />
+  <PropertyGroup>
+    <ProjectAssetsFile>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), performance.targets))\obj\project.assets.json</ProjectAssetsFile>
+  </PropertyGroup>
 </Project>
index 4a68139f4328c8e7768b4a7ad5c54d1442c5a00f..8c2cd3c7de70fba14e6c579376b36b479aacccb4 100644 (file)
@@ -2,8 +2,8 @@
 <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
   <PropertyGroup>
-    <NugetTargetMoniker>.NETStandard,Version=v1.5</NugetTargetMoniker>
-    <NugetTargetMonikerShort>netstandard1.5</NugetTargetMonikerShort>
+    <NugetTargetMoniker>.NETStandard,Version=v1.6</NugetTargetMoniker>
+    <NugetTargetMonikerShort>netstandard1.6</NugetTargetMonikerShort>
     <IsTestProject>false</IsTestProject>
   </PropertyGroup>
   <ItemGroup>
@@ -23,7 +23,7 @@
       <Version>$(MicrosoftDiagnosticsTracingTraceEventPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.NETCore.Platforms">
-      <Version>2.0.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftNETCorePlatformsPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Collections.NonGeneric">
       <Version>4.4.0-beta-24913-02</Version>
@@ -41,7 +41,7 @@
       <Version>4.4.0-beta-24913-02</Version>
     </PackageReference>
     <PackageReference Include="System.Numerics.Vectors">
-      <Version>4.4.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Reflection">
       <Version>4.4.0-beta-24913-02</Version>
@@ -50,7 +50,7 @@
       <Version>4.4.0-beta-24913-02</Version>
     </PackageReference>
     <PackageReference Include="System.Reflection.TypeExtensions">
-      <Version>4.4.0-preview2-25302-03</Version>
+      <Version>$(MicrosoftPrivateCoreFxNETCoreAppPackageVersion)</Version>
     </PackageReference>
     <PackageReference Include="System.Runtime">
       <Version>4.4.0-beta-24913-02</Version>
     <PackageReference Include="xunit.runner.utility">
       <Version>$(XunitPackageVersion)</Version>
     </PackageReference>
+    <PackageReference Include="System.Security.Principal.Windows">
+      <Version>4.4.0</Version>
+    </PackageReference>
   </ItemGroup>
   <PropertyGroup>
-    <TargetFramework>netstandard1.5</TargetFramework>
+    <TargetFramework>netstandard1.6</TargetFramework>
     <TargetFrameworkIdentifier>.NETStandard</TargetFrameworkIdentifier>
     <PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net45+win8</PackageTargetFallback>
     <ContainsPackageReferences>true</ContainsPackageReferences>