[
  {
    "path": ".config/dotnet-tools.json",
    "content": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"wix\": {\n      \"version\": \"4.0.5\",\n      \"commands\": [\n        \"wix\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# Remove the line below if you want to inherit .editorconfig settings from higher directories\nroot = true\n\n# C# files\n[*.cs]\n\n#### Core EditorConfig Options ####\n\n# Indentation and spacing\nindent_size = 4\nindent_style = space\ntab_width = 4\n\n# New line preferences\nend_of_line = crlf\ninsert_final_newline = false\n\n#### .NET Coding Conventions ####\n\n# Organize usings\ndotnet_separate_import_directive_groups = true\ndotnet_sort_system_directives_first = true\nfile_header_template = unset\n\n# this. and Me. preferences\ndotnet_style_qualification_for_event = false\ndotnet_style_qualification_for_field = false\ndotnet_style_qualification_for_method = false\ndotnet_style_qualification_for_property = false\n\n# Language keywords vs BCL types preferences\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\ndotnet_style_predefined_type_for_member_access = true:suggestion\n\n# Parentheses preferences\ndotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion\ndotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion\n\n# Modifier preferences\ndotnet_style_require_accessibility_modifiers = for_non_interface_members\n\n# Expression-level preferences\ndotnet_style_coalesce_expression = true\ndotnet_style_collection_initializer = true\ndotnet_style_explicit_tuple_names = true\ndotnet_style_namespace_match_folder = true\ndotnet_style_null_propagation = true\ndotnet_style_object_initializer = true\ndotnet_style_operator_placement_when_wrapping = beginning_of_line\ndotnet_style_prefer_auto_properties = true:suggestion\ndotnet_style_prefer_compound_assignment = true\ndotnet_style_prefer_conditional_expression_over_assignment = true\ndotnet_style_prefer_conditional_expression_over_return = true\ndotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed\ndotnet_style_prefer_inferred_anonymous_type_member_names = true\ndotnet_style_prefer_inferred_tuple_names = true\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true\ndotnet_style_prefer_simplified_boolean_expressions = true\ndotnet_style_prefer_simplified_interpolation = true\n\n# Field preferences\ndotnet_style_readonly_field = true\n\n# Parameter preferences\ndotnet_code_quality_unused_parameters = all\n\n# Suppression preferences\ndotnet_remove_unnecessary_suppression_exclusions = none\n\n# New line preferences\ndotnet_style_allow_multiple_blank_lines_experimental = true\ndotnet_style_allow_statement_immediately_after_block_experimental = true\n\n#### C# Coding Conventions ####\n\n# var preferences\ncsharp_style_var_elsewhere = true:warning\ncsharp_style_var_for_built_in_types = true:warning\ncsharp_style_var_when_type_is_apparent = true:warning\n\n# Expression-bodied members\ncsharp_style_expression_bodied_accessors = when_on_single_line:warning\ncsharp_style_expression_bodied_constructors = when_on_single_line:silent\ncsharp_style_expression_bodied_indexers = when_on_single_line:warning\ncsharp_style_expression_bodied_lambdas = when_on_single_line:warning\ncsharp_style_expression_bodied_local_functions = when_on_single_line:warning\ncsharp_style_expression_bodied_methods = when_on_single_line:silent\ncsharp_style_expression_bodied_operators = when_on_single_line:warning\ncsharp_style_expression_bodied_properties = when_on_single_line:warning\n\n# Pattern matching preferences\ncsharp_style_pattern_matching_over_as_with_null_check = true:error\ncsharp_style_pattern_matching_over_is_with_cast_check = true:error\ncsharp_style_prefer_extended_property_pattern = true:suggestion\ncsharp_style_prefer_not_pattern = true:suggestion\ncsharp_style_prefer_pattern_matching = true:suggestion\ncsharp_style_prefer_switch_expression = true:warning\n\n# Null-checking preferences\ncsharp_style_conditional_delegate_call = true:warning\n\n# Modifier preferences\ncsharp_prefer_static_local_function = true:warning\ncsharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async\ncsharp_style_prefer_readonly_struct = true:warning\ncsharp_style_prefer_readonly_struct_member = true:warning\n\n# Code-block preferences\ncsharp_prefer_braces = true:warning\ncsharp_prefer_simple_using_statement = false:warning\ncsharp_style_namespace_declarations = block_scoped:warning\ncsharp_style_prefer_method_group_conversion = true:suggestion\ncsharp_style_prefer_top_level_statements = false:suggestion\n\n# Expression-level preferences\ncsharp_prefer_simple_default_expression = true:suggestion\ncsharp_style_deconstructed_variable_declaration = true:suggestion\ncsharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion\ncsharp_style_inlined_variable_declaration = true:suggestion\ncsharp_style_prefer_index_operator = true:suggestion\ncsharp_style_prefer_local_over_anonymous_function = true:suggestion\ncsharp_style_prefer_null_check_over_type_check = true:warning\ncsharp_style_prefer_range_operator = true:suggestion\ncsharp_style_prefer_tuple_swap = true:suggestion\ncsharp_style_prefer_utf8_string_literals = true:suggestion\ncsharp_style_throw_expression = false:suggestion\ncsharp_style_unused_value_assignment_preference = discard_variable:warning\ncsharp_style_unused_value_expression_statement_preference = discard_variable:warning\n\n# 'using' directive preferences\ncsharp_using_directive_placement = outside_namespace:suggestion\n\n# New line preferences\ncsharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent\ncsharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent\ncsharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent\ncsharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent\ncsharp_style_allow_embedded_statements_on_same_line_experimental = true:silent\n\n#### C# Formatting Rules ####\n\n# New line preferences\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_else = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_anonymous_types = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_between_query_expression_clauses = true\n\n# Indentation preferences\ncsharp_indent_block_contents = true\ncsharp_indent_braces = false\ncsharp_indent_case_contents = true\ncsharp_indent_case_contents_when_block = true\ncsharp_indent_labels = one_less_than_current\ncsharp_indent_switch_labels = true\n\n# Space preferences\ncsharp_space_after_cast = false\ncsharp_space_after_colon_in_inheritance_clause = true\ncsharp_space_after_comma = true\ncsharp_space_after_dot = false\ncsharp_space_after_keywords_in_control_flow_statements = true\ncsharp_space_after_semicolon_in_for_statement = true\ncsharp_space_around_binary_operators = before_and_after\ncsharp_space_around_declaration_statements = false\ncsharp_space_before_colon_in_inheritance_clause = true\ncsharp_space_before_comma = false\ncsharp_space_before_dot = false\ncsharp_space_before_open_square_brackets = false\ncsharp_space_before_semicolon_in_for_statement = false\ncsharp_space_between_empty_square_brackets = false\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\ncsharp_space_between_method_call_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_name_and_open_parenthesis = false\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\ncsharp_space_between_parentheses = false\ncsharp_space_between_square_brackets = false\n\n# Wrapping preferences\ncsharp_preserve_single_line_blocks = true\ncsharp_preserve_single_line_statements = true\n\n#### Naming styles ####\n\n# Naming rules\n\ndotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion\ndotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface\ndotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i\n\ndotnet_naming_rule.types_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.types_should_be_pascal_case.symbols = types\ndotnet_naming_rule.types_should_be_pascal_case.style = pascal_case\n\ndotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members\ndotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case\n\n# Symbol specifications\n\ndotnet_naming_symbols.interface.applicable_kinds = interface\ndotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.interface.required_modifiers = \n\ndotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum\ndotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.types.required_modifiers = \n\ndotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method\ndotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.non_field_members.required_modifiers = \n\n# Naming styles\n\ndotnet_naming_style.pascal_case.required_prefix = \ndotnet_naming_style.pascal_case.required_suffix = \ndotnet_naming_style.pascal_case.word_separator = \ndotnet_naming_style.pascal_case.capitalization = pascal_case\n\ndotnet_naming_style.begins_with_i.required_prefix = I\ndotnet_naming_style.begins_with_i.required_suffix = \ndotnet_naming_style.begins_with_i.word_separator = \ndotnet_naming_style.begins_with_i.capitalization = pascal_case\n\n[*.{cs,vb}]\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion\ndotnet_style_prefer_auto_properties = true:warning\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_prefer_simplified_boolean_expressions = true:suggestion\ndotnet_style_prefer_conditional_expression_over_assignment = true:silent\ndotnet_style_prefer_conditional_expression_over_return = true:silent\ndotnet_style_explicit_tuple_names = true:warning\ndotnet_style_prefer_inferred_tuple_names = true:suggestion\ndotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion\ndotnet_style_operator_placement_when_wrapping = beginning_of_line\ntab_width = 4\nindent_size = 4\nend_of_line = crlf\ndotnet_style_prefer_compound_assignment = true:suggestion\ndotnet_style_prefer_simplified_interpolation = true:suggestion\ndotnet_style_namespace_match_folder = true:suggestion\ndotnet_style_readonly_field = true:warning\ndotnet_style_predefined_type_for_locals_parameters_members = true:warning\ndotnet_style_predefined_type_for_member_access = true:warning\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:warning\ndotnet_style_allow_multiple_blank_lines_experimental = false:warning\ndotnet_style_allow_statement_immediately_after_block_experimental = true:silent\ndotnet_code_quality_unused_parameters = all:error\ndotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion\ndotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_event = false:suggestion"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n.nuget/nuget.exe\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\nharvest.wxs\nharvest2.wxs\nharvest3.wxs\nSetup/SignParams.ps1\nSetupBootstrapper/SignParams.ps1\n*.wixobj.nuget/nuget.exe\n*.wixobj"
  },
  {
    "path": "ConvertWorkload/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup useLegacyV2RuntimeActivationPolicy=\"true\"> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/>\n    </startup>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"Microsoft.SqlServer.XE.Core\" publicKeyToken=\"89845dcd8080cc91\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-14.100.0.0\" newVersion=\"14.100.0.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>\n"
  },
  {
    "path": "ConvertWorkload/ConvertWorkload.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{62E37C03-BA08-46CE-A583-D71FB7A8825B}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>ConvertWorkload</RootNamespace>\n    <AssemblyName>ConvertWorkload</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DefineConstants>TRACE;DEBUG</DefineConstants>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\Release\\</OutputPath>\n    <Prefer32Bit>false</Prefer32Bit>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>bin\\Release\\</OutputPath>\n    <Prefer32Bit>false</Prefer32Bit>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <OutputPath>bin\\Debug\\</OutputPath>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\CommandLineParser.1.9.71\\lib\\net45\\CommandLine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\NLog.4.4.12\\lib\\net45\\NLog.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Data.SQLite, Version=1.0.112.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Data.SQLite.Core.1.0.112.0\\lib\\net46\\System.Data.SQLite.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"..\\SharedAssemblyInfo.cs\">\n      <Link>Properties\\SharedAssemblyInfo.cs</Link>\n    </Compile>\n    <Compile Include=\"EventReader.cs\" />\n    <Compile Include=\"EventWriter.cs\" />\n    <Compile Include=\"ExtendedEventsEventReader.cs\" />\n    <Compile Include=\"LocalDBManager.cs\" />\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"SqlTraceEventReader.cs\" />\n    <Compile Include=\"WorkloadConverter.cs\" />\n    <Compile Include=\"WorkloadFileEventWriter.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\">\n      <SubType>Designer</SubType>\n    </None>\n    <None Include=\"NLog.config\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\WorkloadTools\\WorkloadTools.csproj\">\n      <Project>{ae6e4548-8c33-4728-8504-88aa9666020b}</Project>\n      <Name>WorkloadTools</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\System.Data.SQLite.Core.1.0.109.2\\build\\net46\\System.Data.SQLite.Core.targets\" Condition=\"Exists('..\\packages\\System.Data.SQLite.Core.1.0.109.2\\build\\net46\\System.Data.SQLite.Core.targets')\" />\n  <Import Project=\"..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets\" Condition=\"Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets'))\" />\n  </Target>\n</Project>"
  },
  {
    "path": "ConvertWorkload/EventReader.cs",
    "content": "﻿using System;\nusing WorkloadTools;\n\nnamespace ConvertWorkload\n{\n    public abstract class EventReader : IDisposable\n    {\n        private WorkloadEventFilter _filter;\n\n        public string[] ApplicationFilter { get; set; }\n        public string[] DatabaseFilter { get; set; }\n        public string[] HostFilter { get; set; }\n        public string[] LoginFilter { get; set; }\n\n        protected bool stopped;\n        protected IEventQueue Events;\n\n        protected WorkloadEventFilter Filter\n        {\n            get\n            {\n                if (_filter != null)\n                {\n                    _filter.ApplicationFilter.PredicateValue = ApplicationFilter;\n                    _filter.DatabaseFilter.PredicateValue = DatabaseFilter;\n                    _filter.HostFilter.PredicateValue = HostFilter;\n                    _filter.LoginFilter.PredicateValue = LoginFilter;\n                    return _filter;\n                }\n                else\n                {\n                    return null;\n                }\n            }\n            set\n            {\n                _filter = value;\n            }\n        }\n\n        public abstract bool HasFinished();\n\n        public abstract WorkloadEvent Read();\n\n        public abstract bool HasMoreElements();\n\n        public void Dispose()\n        {\n            stopped = true;\n            Events.Dispose();\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected abstract void Dispose(bool disposing);\n    }\n}"
  },
  {
    "path": "ConvertWorkload/EventWriter.cs",
    "content": "﻿using System;\nusing WorkloadTools;\n\nnamespace ConvertWorkload\n{\n    public abstract class EventWriter : IDisposable\n    {\n        protected bool stopped;\n\n        public abstract void Write(WorkloadEvent evt);\n\n        public void Dispose()\n        {\n            stopped = true;\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected abstract void Dispose(bool disposing);\n\n    }\n}"
  },
  {
    "path": "ConvertWorkload/ExtendedEventsEventReader.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools;\nusing WorkloadTools.Listener;\nusing WorkloadTools.Listener.ExtendedEvents;\nusing WorkloadTools.Util;\n\nnamespace ConvertWorkload\n{\n \n    public class ExtendedEventsEventReader : EventReader\n    {\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        private string filePath;\n        private bool started = false;\n        private bool finished = false;\n\n        private FileTargetXEventDataReader reader;\n\n        public ExtendedEventsEventReader(string path)\n        {\n            Events = new BinarySerializedBufferedEventQueue();\n            Events.BufferSize = 10000;\n            filePath = path;\n            Filter = new ExtendedEventsEventFilter();\n        }\n\n\n        private void ReadEventsFromFile()\n        {\n            try\n            {\n                var info = new SqlConnectionInfo();\n                info.ServerName = \"(localdb)\\\\MSSQLLocalDB\";\n\n                var sqlCreateTable = @\"\n                    IF OBJECT_ID('tempdb.dbo.trace_reader_queue') IS NULL\n                    BEGIN\n                        CREATE TABLE tempdb.dbo.trace_reader_queue (\n                            ts datetime DEFAULT GETDATE(),\n                            path nvarchar(4000)\n                        )\n                    END\n                    TRUNCATE TABLE tempdb.dbo.trace_reader_queue;\n                    INSERT INTO tempdb.dbo.trace_reader_queue (path) VALUES(@path);\n                \";\n\n                using (var conn = new SqlConnection())\n                {\n                    conn.ConnectionString = info.ConnectionString();\n                    conn.Open();\n                    using (var cmd = conn.CreateCommand())\n                    {\n                        cmd.CommandText = sqlCreateTable;\n                        var prm = new SqlParameter()\n                        {\n                            ParameterName = \"@path\",\n                            DbType = System.Data.DbType.String,\n                            Size = 4000,\n                            Value = filePath\n                        };\n                        cmd.Parameters.Add(prm);\n                        cmd.ExecuteNonQuery();\n                    }\n                }\n\n                reader = new FileTargetXEventDataReader(info.ConnectionString(), null, Events, ExtendedEventsWorkloadListener.ServerType.LocalDB);\n                reader.ReadEvents();\n                finished = true;\n                    \n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                Dispose();\n            }\n        }\n\n\n        public override bool HasFinished()\n        {\n            return finished && !Events.HasMoreElements();\n        }\n\n        public override bool HasMoreElements()\n        {\n            return !finished && !stopped && (started ? Events.HasMoreElements() : true);\n        }\n\n        public override WorkloadEvent Read()\n        {\n            if (!started)\n            {\n                var t = Task.Factory.StartNew(ReadEventsFromFile);\n                started = true;\n            }\n\n            WorkloadEvent result = null;\n            while (!Events.TryDequeue(out result))\n            {\n                if (stopped || finished)\n                {\n                    return null;\n                }\n\n                Thread.Sleep(5);\n            }\n            return result;\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            if (!stopped)\n            {\n                stopped = true;\n                reader.Stop();\n                reader.Dispose();\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "ConvertWorkload/LocalDBManager.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Data.SqlClient;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net;\r\nusing System.Security.Principal;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing WorkloadTools;\r\n\r\nnamespace ConvertWorkload\r\n{\r\n    internal class LocalDBManager\r\n    {\r\n\r\n        public bool IsElevated\r\n        {\r\n            get\r\n            {\r\n                return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);\r\n            }\r\n        }\r\n\r\n        public string DownloadLocalDB()\r\n        {\r\n            var localPath = Path.GetTempPath() + \"SqlLocalDB.msi\";\r\n            using (var client = new WebClient())\r\n            {\r\n                var wp = WebRequest.DefaultWebProxy;\r\n                wp.Credentials = CredentialCache.DefaultCredentials;\r\n                client.Proxy = wp;\r\n                client.DownloadFile(\"https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi\", localPath);\r\n            }\r\n            return localPath;\r\n        }\r\n\r\n\r\n        public void InstallLocalDB()\r\n        {\r\n            if (!IsElevated)\r\n            {\r\n                throw new InvalidOperationException(\"Installing LocalDB requires elevation.\");\r\n            }\r\n            var localFileName = DownloadLocalDB();\r\n            var p = new Process();\r\n            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;\r\n            p.StartInfo.FileName = \"c:\\\\windows\\\\system32\\\\msiexec.exe\";\r\n            p.StartInfo.Arguments = \" /i \"+ localFileName +\" /qn IACCEPTSQLLOCALDBLICENSETERMS=YES\";\r\n            p.StartInfo.UseShellExecute = false;\r\n            p.StartInfo.RedirectStandardOutput = true;\r\n            p.StartInfo.RedirectStandardError = true;\r\n            //Vista or higher check\r\n            if (System.Environment.OSVersion.Version.Major >= 6)\r\n            {\r\n                p.StartInfo.Verb = \"runas\";\r\n            }\r\n            p.Start();\r\n            p.WaitForExit();\r\n        }\r\n\r\n        public bool CanConnectToLocalDB()\r\n        {\r\n            try\r\n            {\r\n                var info = new SqlConnectionInfo();\r\n                info.ServerName = @\"(localdb)\\MSSQLLocalDB\";\r\n                info.UseIntegratedSecurity = true;\r\n                using (var conn = new SqlConnection(info.ConnectionString() + \";Connect Timeout=30;\"))\r\n                {\r\n                    conn.Open();\r\n                }\r\n                return true;\r\n            }\r\n            catch(Exception)\r\n            {\r\n                return false;\r\n            }\r\n        }\r\n\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "ConvertWorkload/NLog.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<nlog xmlns=\"http://www.nlog-project.org/schemas/NLog.xsd\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd\"\n      autoReload=\"true\"\n      throwExceptions=\"false\"\n      internalLogLevel=\"Off\" internalLogFile=\"c:\\temp\\nlog-internal.log\">\n\n\n  <targets>\n    <target name=\"logfile\" xsi:type=\"File\" fileName=\"ConvertWorkload.log\" layout=\"${longdate} - ${level} - ${logger} : ${message}\" />\n    <target name=\"warnfile\" xsi:type=\"File\" fileName=\"Warnings.log\" layout=\"${longdate} - ${message}\" />\n    <target name=\"console\" xsi:type=\"Console\" layout=\"${level} - ${logger} : ${message}\"/>\n  </targets>\n\n  <rules>\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"logfile\" />\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"console\" />\n    <logger name=\"*\" levels=\"Warn\" writeTo=\"warnfile\" />\n  </rules>\n</nlog>\n"
  },
  {
    "path": "ConvertWorkload/Program.cs",
    "content": "﻿using CommandLine;\nusing CommandLine.Text;\nusing NLog;\nusing NLog.Targets;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Listener.Trace;\n\nnamespace ConvertWorkload\n{\n    class Program\n    {\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n        private static CancellationTokenSource source;\n\n\n        static void Main(string[] args)\n        {\n            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(GenericErrorHandler);\n            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;\n\n            var assembly = System.Reflection.Assembly.GetExecutingAssembly();\n            var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);\n            var version = fvi.FileMajorPart.ToString() + \".\" + fvi.FileMinorPart.ToString() + \".\" + fvi.FileBuildPart.ToString();\n            var name = assembly.FullName;\n            logger.Info(name + \" \" + version);\n\n\n            try\n            {\n                var options = new Options();\n                if (!CommandLine.Parser.Default.ParseArguments(args, options))\n                {\n                    \n                    return;\n                }\n                Run(options);\n            }\n            catch (Exception e)\n            {\n                logger.Error(e);\n            }\n        }\n\n        private static void Run(Options options)\n        {\n            // reconfigure loggers to use a file in the current directory\n            // or the file specified by the \"Log\" commandline parameter\n            if(LogManager.Configuration != null)\n            {\n                var target = (FileTarget)LogManager.Configuration.FindTargetByName(\"logfile\");\n                if (target != null)\n                {\n                    var pathToLog = options.LogFile;\n                    if (pathToLog == null)\n                    {\n                        pathToLog = Path.Combine(Environment.CurrentDirectory, \"ConvertWorkload.log\");\n                    }\n                    if (!Path.IsPathRooted(pathToLog))\n                    {\n                        pathToLog = Path.Combine(Environment.CurrentDirectory, pathToLog);\n                    }\n                    target.FileName = pathToLog;\n                    LogManager.ReconfigExistingLoggers();\n                }\n            }\n\n            // check whether localdb is installed\n            logger.Info(\"Checking LocalDB...\");\n            var manager = new LocalDBManager();\n            if (!manager.CanConnectToLocalDB())\n            {\n                logger.Info(\"Installing LocalDB...\");\n                try\n                {\n                    manager.InstallLocalDB();\n                }\n                catch (InvalidOperationException)\n                {\n                    logger.Error(\"This operation requires elevation. Restart the application as an administrator.\");\n                    return;\n                }\n            }\n\n            EventReader reader = null;\n            if (options.InputFile.EndsWith(\".trc\"))\n            {\n                reader = new SqlTraceEventReader(options.InputFile);\n            }\n            else \n            {\n                reader = new ExtendedEventsEventReader(options.InputFile);\n            }\n            EventWriter writer = new WorkloadFileEventWriter(options.OutputFile);\n            var converter = new WorkloadConverter(reader, writer);\n            if(options.ApplicationFilter != null)\n            {\n                converter.ApplicationFilter = new string[1] { options.ApplicationFilter };\n            }\n\n            if (options.DatabaseFilter != null)\n            {\n                converter.DatabaseFilter = new string[1] { options.DatabaseFilter };\n            }\n\n            if (options.HostFilter != null)\n            {\n                converter.HostFilter = new string[1] { options.HostFilter };\n            }\n\n            if (options.LoginFilter != null)\n            {\n                converter.LoginFilter = new string[1] { options.LoginFilter };\n            }\n\n            Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) {\n                e.Cancel = true;\n                logger.Info(\"Received shutdown signal...\");\n                source.CancelAfter(TimeSpan.FromSeconds(10)); // give a 10 seconds cancellation grace period \n                converter.Stop();\n            };\n\n            var t = processConverter(converter);\n            t.Wait();\n            logger.Info(\"Converter stopped.\");\n        }\n\n        public static void CancelNotification()\n        {\n            logger.Info(\"Shutdown complete.\");\n        }\n\n        public static async Task processConverter(WorkloadConverter converter)\n        {\n            source = new CancellationTokenSource();\n            source.Token.Register(CancelNotification);\n            var completionSource = new TaskCompletionSource<object>();\n            source.Token.Register(() => completionSource.TrySetCanceled());\n            var task = Task.Factory.StartNew(() => converter.Convert(), source.Token);\n            await Task.WhenAny(task, completionSource.Task);\n        }\n\n        static void GenericErrorHandler(object sender, UnhandledExceptionEventArgs e)\n        {\n            try\n            {\n                logger.Error(e.ToString());\n            }\n            finally\n            {\n                Console.WriteLine(\"Caught unhandled exception...\");\n\n            }\n        }\n    }\n\n\n    class Options\n    {\n        [Option('L', \"Log\", HelpText = \"Log file\")]\n        public string LogFile { get; set; }\n\n        [Option('I', \"Input\", HelpText = \"Input file\", Required = true)]\n        public string InputFile { get; set; }\n\n        [Option('O', \"Output\", HelpText = \"Output file\", Required = true)]\n        public string OutputFile { get; set; }\n\n        [Option('A', \"ApplicationFilter\", HelpText = \"Application filter\")]\n        public string ApplicationFilter { get; set; }\n\n        [Option('D', \"DatabaseFilter\", HelpText = \"Database filter\")]\n        public string DatabaseFilter { get; set; }\n\n        [Option('H', \"HostFilter\", HelpText = \"Host Filter\")]\n        public string HostFilter { get; set; }\n\n        [Option('U', \"LoginFilter\", HelpText = \"Login Filter\")]\n        public string LoginFilter { get; set; }\n\n        [ParserState]\n        public IParserState LastParserState { get; set; }\n\n        [HelpOption]\n        public string GetUsage()\n        {\n            return HelpText.AutoBuild(this,\n              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));\n        }\n\n    }\n}\n"
  },
  {
    "path": "ConvertWorkload/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"62e37c03-ba08-46ce-a583-d71fb7a8825b\")]\n"
  },
  {
    "path": "ConvertWorkload/SqlTraceEventReader.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools;\nusing WorkloadTools.Listener;\nusing WorkloadTools.Listener.Trace;\n\nnamespace ConvertWorkload\n{\n    public class SqlTraceEventReader : EventReader\n    {\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        private string tracePath;\n        private bool started = false;\n        private bool finished = false;\n\n        private FileTraceEventDataReader reader;\n\n        public SqlTraceEventReader(string path)\n        {\n            Events = new BinarySerializedBufferedEventQueue();\n            Events.BufferSize = 10000;\n            tracePath = path;\n            Filter = new TraceEventFilter();\n        }\n\n\n        private void ReadEventsFromFile()\n        {\n            try\n            {\n                var info = new SqlConnectionInfo();\n                info.ServerName = \"(localdb)\\\\MSSQLLocalDB\";\n\n                var sqlCreateTable = @\"\n                    IF OBJECT_ID('tempdb.dbo.trace_reader_queue') IS NULL\n                    BEGIN\n                        CREATE TABLE tempdb.dbo.trace_reader_queue (\n                            ts datetime DEFAULT GETDATE(),\n                            path nvarchar(4000)\n                        )\n                    END\n                    TRUNCATE TABLE tempdb.dbo.trace_reader_queue;\n                    INSERT INTO tempdb.dbo.trace_reader_queue (path) VALUES(@path);\n                \";\n                \n                using(var conn = new SqlConnection())\n                {\n                    conn.ConnectionString = info.ConnectionString();\n                    conn.Open();\n                    using(var cmd = conn.CreateCommand())\n                    {\n                        cmd.CommandText = sqlCreateTable;\n                        var prm = new SqlParameter()\n                        {\n                            ParameterName = \"@path\",\n                            DbType = System.Data.DbType.String,\n                            Size = 4000,\n                            Value = tracePath\n                        };\n                        cmd.Parameters.Add(prm);\n                        cmd.ExecuteNonQuery();\n                    }\n                }\n\n                reader = new FileTraceEventDataReader(info.ConnectionString(), Filter, Events);\n                reader.ReadEvents();\n                finished = true;\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                Dispose();\n            }\n\n        }\n\n        public override WorkloadEvent Read()\n        {\n            if (!started)\n            {\n                var t = Task.Factory.StartNew(ReadEventsFromFile);\n                started = true;\n            }\n\n            WorkloadEvent result = null;\n            while (!Events.TryDequeue(out result))\n            {\n                if (stopped || finished)\n                {\n                    return null;\n                }\n\n                Thread.Sleep(5);\n            }\n            return result;\n        }\n\n        protected override void Dispose(bool disposing) \n        {\n            if (!stopped)\n            {\n                stopped = true;\n                reader.Stop();\n                reader.Dispose();\n            }\n        }\n\n        public override bool HasMoreElements()\n        {\n            return !finished && !stopped && (started ? Events.HasMoreElements() : true);\n        }\n\n        public override bool HasFinished()\n        {\n            return finished && !Events.HasMoreElements();\n        }\n    }\n}\n"
  },
  {
    "path": "ConvertWorkload/WorkloadConverter.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing WorkloadTools;\n\nnamespace ConvertWorkload\n{\n    public class WorkloadConverter\n    {\n\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        private EventReader reader;\n        private EventWriter writer;\n        private bool stopped = false;\n\n        public string[] ApplicationFilter { get; set; }\n        public string[] DatabaseFilter { get; set; }\n        public string[] HostFilter { get; set; }\n        public string[] LoginFilter { get; set; }\n\n        public WorkloadConverter(EventReader reader, EventWriter writer)\n        {\n            this.reader = reader;\n            this.writer = writer;\n        }\n\n        public void Convert()\n        {\n            try\n            {\n                if (ApplicationFilter != null)\n                {\n                    reader.ApplicationFilter = ApplicationFilter;\n                }\n\n                if (DatabaseFilter != null)\n                {\n                    reader.DatabaseFilter = DatabaseFilter;\n                }\n\n                if (HostFilter != null)\n                {\n                    reader.HostFilter = HostFilter;\n                }\n\n                if (LoginFilter != null)\n                {\n                    reader.LoginFilter = LoginFilter;\n                }\n\n                while ((!reader.HasFinished() || reader.HasMoreElements()) && !stopped)\n                {\n                    writer.Write(reader.Read());\n                }\n            }\n            catch(Exception ex)\n            {\n                stopped = true;\n                logger.Error(ex);\n            }\n            finally\n            {\n                Stop();\n            }\n        }\n\n        public void Stop()\n        {\n            stopped = true;\n            reader.Dispose();\n            writer.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "ConvertWorkload/WorkloadFileEventWriter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing WorkloadTools;\nusing WorkloadTools.Consumer.WorkloadFile;\n\nnamespace ConvertWorkload\n{\n    public class WorkloadFileEventWriter : EventWriter\n    {\n        private WorkloadFileWriterConsumer consumer;\n\n        public WorkloadFileEventWriter(string outputPath)\n        {\n            consumer = new WorkloadFileWriterConsumer()\n            {\n                OutputFile = outputPath\n            };\n        }\n\n        public override void Write(WorkloadEvent evt)\n        {\n            consumer.Consume(evt);\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            consumer.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "ConvertWorkload/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"CommandLineParser\" version=\"1.9.71\" targetFramework=\"net461\" />\n  <package id=\"Microsoft.SqlServer.SqlManagementObjects\" version=\"140.17283.0\" targetFramework=\"net461\" />\n  <package id=\"NLog\" version=\"4.4.12\" targetFramework=\"net461\" />\n  <package id=\"System.Collections\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.Console\" version=\"4.0.0\" targetFramework=\"net461\" />\n  <package id=\"System.Data.SQLite.Core\" version=\"1.0.112.0\" targetFramework=\"net461\" />\n  <package id=\"System.Diagnostics.Debug\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.Globalization\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.IO\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Linq\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Linq.Expressions\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection.Extensions\" version=\"4.0.1\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection.TypeExtensions\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Resources.ResourceManager\" version=\"4.0.1\" targetFramework=\"net461\" />\n  <package id=\"System.Runtime\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Runtime.Extensions\" version=\"4.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "DebuggingTools/capture.json",
    "content": "{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"SqlTraceWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"localhost\"\n            },\n            \"DatabaseFilter\": \"benchmark\",\n            \"ApplicationFilter\": \"testapp\",\n            \"TimeoutMinutes\": 10\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"WorkloadFileWriterConsumer\",\n                \"OutputFile\": \"C:\\\\temp\\\\WorkloadTools\\\\debug\\\\capture.sqlite\"\n            }\n            ,{\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"(local)\",\n                    \"DatabaseName\": \"benchmark_analysis\",\n                    \"SchemaName\": \"test\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "DebuggingTools/capture_trace.json",
    "content": "{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"SqlTraceWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"localhost\"\n            },\n            \"DatabaseFilter\": \"benchmark\",\n            \"TraceRolloverCount\": 100,\n            \"TimeoutMinutes\": 10\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"WorkloadFileWriterConsumer\",\n                \"OutputFile\": \"C:\\\\temp\\\\WorkloadTools\\\\debug\\\\capture.sqlite\"\n            }\n            ,{\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"(local)\",\n                    \"DatabaseName\": \"benchmark_analysis\",\n                    \"SchemaName\": \"test\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "DebuggingTools/capture_xel.json",
    "content": "{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"ExtendedEventsWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"localhost\"\n            },\n            \"ApplicationFilter\": \"testapp\",\n            \"FileTargetPath\": \"C:\\\\temp\\\\WorkloadTools\\\\debug\\\\capture.xel\"\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"WorkloadFileWriterConsumer\",\n                \"OutputFile\": \"C:\\\\temp\\\\WorkloadTools\\\\debug\\\\capture.sqlite\"\n            }\n            ,{\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"(local)\",\n                    \"DatabaseName\": \"benchmark_analysis\",\n                    \"SchemaName\": \"test\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "DebuggingTools/generate-allWorkload.bat",
    "content": "del \"c:\\temp\\workloadtools\\debug\\capture.sqlite\"\nsqlcmd -S(local) -dbenchmark -Q\"TRUNCATE TABLE dbo.benchmark\"\nstart \"\" powershell -File .\\generate-workload.ps1 -start 0\nstart \"\" powershell -File .\\generate-workload.ps1 -start 1000000\nstart \"\" powershell -File .\\generate-workload.ps1 -start 2000000\nstart \"\" powershell -File .\\generate-workload.ps1 -start 3000000\nstart \"\" powershell -File .\\generate-workload.ps1 -start 4000000\nstart \"\" powershell -File .\\generate-workload.ps1 -start 5000000"
  },
  {
    "path": "DebuggingTools/generate-workload.ps1",
    "content": "param (\n    [int]$start\n)\nGet-Date -Format \"yyyy-MM-dd HH:mm:ss\"\n$connectionString = \"Data Source=(local);Integrated Security=SSPI;Initial Catalog=benchmark;Application Name=testapp\"\n\n$connection = new-object system.data.SqlClient.SQLConnection($connectionString)\n$connection.Open()\n\n$command = new-object system.data.sqlclient.sqlcommand(\"INSERT INTO benchmark VALUES(@p0)\",$connection)\n$command.Parameters.AddWithValue(\"@p0\",0) | Out-Null\n\nfor($i = $start; $i -lt ($start + 100000); $i++) {\n    $command.Parameters[0].Value = $i\n    $command.ExecuteNonQuery() | Out-Null\n    if($i % 1000 -eq 0) {$i}\n}\n$i\n\n$connection.Close()\nGet-Date -Format \"yyyy-MM-dd HH:mm:ss\""
  },
  {
    "path": "DebuggingTools/replay.json",
    "content": "{\n    \"Controller\": {\n \n        \"Listener\":\n        {\n            \"__type\": \"FileWorkloadListener\",\n            \"Source\": \"C:\\\\temp\\\\workloadtools\\\\debug\\\\capture.sqlite\",\n            // in this case you want to simulate the original query rate\n            \"SynchronizationMode\": \"true\"\n        },\n \n        \"Consumers\":\n        [\n            {\n                \"__type\": \"ReplayConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"(local)\",\n                    \"DatabaseName\": \"benchmark\"\n                },\n                \"ConsumeResults\": \"false\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "DebuggingTools/report.bat",
    "content": "\"c:\\Program Files\\WorkloadTools\\WorkloadViewer.exe\" --BaselineServer (local) --BaselineSchema test --BaselineDatabase benchmark_analysis"
  },
  {
    "path": "DebuggingTools/setup.bat",
    "content": "sqlcmd -S(local) -Q\"CREATE DATABASE benchmark\"\nsqlcmd -S(local) -dbenchmark -Q\"CREATE TABLE dbo.benchmark ( i int NULL )\"\nsqlcmd -S(local) -Q\"CREATE DATABASE benchmark_analysis\"\n\nmkdir c:\\temp\nmkdir c:\\temp\\workloadtools\nmkdir c:\\temp\\workloadtools\\debug"
  },
  {
    "path": "DebuggingTools/start-capture.bat",
    "content": "del \"c:\\temp\\workloadtools\\debug\\capture.sqlite\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Applications\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Databases\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Errors\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Hosts\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Intervals\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Logins\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.NormalizedQueries\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WaitStats\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WorkloadDetails\"\n\"c:\\program files\\workloadtools\\sqlworkload.exe\" --File \"%CD%\\capture.json\""
  },
  {
    "path": "DebuggingTools/start-capture_trace.bat",
    "content": "sqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Applications\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Databases\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Errors\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Hosts\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Intervals\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Logins\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.NormalizedQueries\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WaitStats\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WorkloadDetails\"\n\"c:\\program files\\workloadtools\\sqlworkload.exe\" --File \"%CD%\\capture_trace.json\""
  },
  {
    "path": "DebuggingTools/start-capture_xel.bat",
    "content": "sqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Applications\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Databases\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Errors\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Hosts\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Intervals\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Logins\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.NormalizedQueries\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WaitStats\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WorkloadDetails\"\n\"c:\\program files\\workloadtools\\sqlworkload.exe\" --File \"%CD%\\capture_xel.json\""
  },
  {
    "path": "DebuggingTools/start-replay.bat",
    "content": "echo %time%\nsqlcmd -S(local) -dbenchmark -Q\"TRUNCATE TABLE benchmark\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Applications\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Databases\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Errors\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Hosts\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Intervals\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.Logins\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.NormalizedQueries\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WaitStats\"\nsqlcmd -S(local) -dbenchmark_analysis -Q\"TRUNCATE TABLE test.WorkloadDetails\"\n\"c:\\program files\\workloadtools\\sqlworkload.exe\" --File \"%CD%\\replay.json\"\necho %time%"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2018 Gianluca Sartori\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![](https://countrush-prod.azurewebsites.net/l/badge/?repository=spaghettidba.WorkloadTools)\n# WorkloadTools\n\n*WorkloadTools is a collection of tools to collect, analyze and replay SQL Server workloads, on premises and in the cloud .*\n\n## Download\n\nGo to the [release page](https://github.com/spaghettidba/WorkloadTools/releases/latest) and download the msi installer for your target bitness (x86 or x64) \n\n## Documentation\n\nIf you're looking for detailed documentation on the individual tools, please have a look at the [Wiki](https://github.com/spaghettidba/WorkloadTools/wiki)\n\nIf you're looking for usage scenarios and examples, see the [posts tagged WorkloadTools at my blog](https://spaghettidba.com/tag/workloadtools/)\n\n## SqlWorkload\n\nSqlWorkload is a command line tool to start workload collection, analyze the collected data and replay the workload to a target machine, all in real-time.\n\nSqlWorkload can connect to a SQL Server instance and capture execution related events via SqlTrace or Extended Events. These events are processed and passed to \"consumers\" that can replay the events to a target instance in real-time and analyze the statements. \nAll the batches are \"normalized\" (parameters and constants are stripped away) and metrics are calculated on each normalized batch, like cpu, duration, reads and writes.\n\nDuring the analysis, additional metrics are captured and saved regularly to the analysis database:\n\n- cpu usage\n- wait stats\n\n### Replaying and analyzing a production workload in test\n\nIf you want to compare the execution of the same workload on two different machines, you can point a first instance of SqlWorkload to your production server: SqlWorkload will analyze the workload and write the metrics to a database of your choice.\nIt will also replay the workload to a test server, where you can point a second instance of SqlWorkload to obtain the same metrics. This second instance of SqlWorkload will not perform the replay, but it will only perform the workload analysis and write it to the same database where you stored the metrics relative to production (possibly on a different schema).\n\nOnce you have captured and replayed the workload for a representative enough time, you can stop the two instances of SqlWorkload and analyze the data using the included Workload Analyzer or PowerBI dashboard.\n\n### Command line switches\n\nSqlWorkload accepts two command line switches:\n\n`--Log` Path to the log file\n\n`--File` Path to the `.JSON` configuration file\n\nIn fact, SqlWorkload supports a multitude of parameters and specifying them all in the command line can become really tedious. For this reason, SqlWorkload supports `.JSON` configuration files.\n\nThis is a sample configuration file. Please refer to [the documentation](https://github.com/spaghettidba/WorkloadTools/wiki) to see the full list of available configuration options.\n\n```javascript\n{\n    \"Controller\": {\n\n        // The Listener section describes how to capture the events\n        \"Listener\":\n        {\n            // The main parameter here is the class type of the Listener\n            \"__type\": \"ExtendedEventsWorkloadListener\",\n\n            // The ConnectionInfo describes how to connect the Listener\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"SQLDEMO\\\\SQL2014\",\n                // If you omit the UserName/Password, Windows authentication\n                // will be used\n                \"UserName\": \"sa\",\n                \"Password\": \"P4$$w0rd!\"\n            },\n\n            // Filters for the workload\n            \"DatabaseFilter\": \"DS3\",\n            \"ApplicationFilter\" : \"SomeAppName\",\n            \"HostFilter\" : \"MyComputer\",\n            \"LoginFilter\": \"sa\"\n        },\n\n        // This section contains the list of the consumers\n        // The list can contain 0 to N consumers of different types\n        \"Consumers\":\n        [\n            {\n                // This is the type of the consumer\n                \"__type\": \"ReplayConsumer\",\n\n                // The same considerations for ConnectionInfo\n                // valid for the Listener apply here as well\n                \"ConnectionInfo\":\n                {\n                    \"ServerName\": \"SQLDEMO\\\\SQL2016\",\n                    \"DatabaseName\": \"DS3\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                }\n            },\n            {\n                // Here is another example with the AnalysisConsumer\n                \"__type\": \"AnalysisConsumer\",\n\n                // ConnectionInfo\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"SQLDEMO\\\\SQL2016\",\n                    \"DatabaseName\": \"DS3\",\n                    // This \"SchemaName\" parameter is important, because it \n                    // decides where the analysis data is written to\n                    \"SchemaName\": \"baseline\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                },\n\n                // This decides how often the metrics are aggregated and \n                // written to the target database\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}\n```\n\n## WorkloadViewer\n\nWorkloadViewer is a GUI tool to analyze the data collected by the WorkloadAnalysisTarget in a SQL Server database. It shows metrics about the workload, relative to the beginning of the capture (in minutes).\n\nHere are some screenshots of WorkloadViewer. \n\n### Workload\n\nThe three charts in the \"Workload\" tab show an overview of the workload analysis: CPU, Duration and Batches/sec. Two workloads can be compared by displaying independent series (Baseline and Benchmark) for each workload.\n\n![SqlWorkload analysis Overview](./Images/SqlWorkloadOverview.png \"Overview\")\n\n### Queries\n\nThis tab displays information about the queries and how they relate to the workload. For a single workload analysis, it shows the most expensive queries. When comparing two workloads, it can be used to identify query regressions.\n\n![SqlWorkload regressed queries](./Images/SqlWorkloadRegresses.png \"RegressedQueries\")\n\n### Query Details\n\nDouble clicking a query in the \"Queries\" tab takes you to the \"Query Details\" tab, where you can see the text of the selected query, specific statistics by application, database, host and login and the average duration in a chart.\n\n![SqlWorkload query detail](./Images/SqlWorkloadDetail.png \"Detail\")\n\n## ConvertWorkload\n\nConvertWorkload is a command line tool to convert existing trace files to the internal SQLite format used by WorkloadTools. In the future, ConvertWorkload will also support conversion from existing Extended Events files.\n\nWhy converting trace file to a different intermediate format instead of supporting it directly? Trace files can be read using an API that works only in x86. While WorkloadTools **can** work in x86, using x64 builds is highly recommended, due to the possible high memory usage when capturing intensive workloads. Using a x86 API would have excluded the functionality from the x64 builds, hence using an external tool to convert trace files seems much more appropriate.\n\n### Command line switches\n\n```text\n-I or --Input               The input file (trace or extended events) to convert\n\n-O or --Output              The output SQLite file to write\n\n-L or --Log                 Specifies where to save the log file\n\n-A or --ApplicationFilter   Application filter to apply while converting the source file\n\n-D or --DatabaseFilter      Database filter to apply while converting the source file\n\n-H or --HostFilter          Host filter to apply while converting the source file\n\n-U or --LoginFilter         Login filter to apply while converting the source file\n```\n"
  },
  {
    "path": "Reports/README.md",
    "content": "# WorkloadTools Power BI Report\n\nTo analyze the data produced by WorkloadTools you can use the provided Power BI Template.\n\nIn this folder you will find:\n* A Sample Power Bi report \"WorkloadTools Report - Sample.pbix\", use it play with the report\n* The Power BI template \"WorkloadTools Report - Template.pbit\", which defines a report structure and will ask for some input parameters before it loading data.\n\n## Overview of the report pages\n![Overview](/Images/PowerBI_Overview.png)\n![WeightOnTotal](/Images/PowerBI_WeightOnTotal.png)\n![Queries](/Images/PowerBI_Queries.png)\n![QueryDetail](/Images/PowerBI_QueryDetail.png)\n![WaitStats](/Images/PowerBI_WaitStats.png)\n\n\n## Usage\n1. Download and install Power BI Desktop\n2. Open the provided template \"WorkloadTools Report - Template.pbit\" \n3. Provide the connection parameters\n4. Explore your data\n5. (optional) Save the file for later use, it won't ask again for the parameters\n\nThe report allows you to load one or two series of data, if you want to visualize only one series leave the optional parameters (\"Benchmark\") empty\n\nRequired parameters:\n* Baseline Server\\Instance\n* Baseline Database\n* Baseline Schema\n\nOptional parameters:\n* Benchmark Server\\Instance\n* Benchmark Database\n* Benchmark Schema\n\n![Input Parameters](/Images/PowerBI_InputParams.png)\n\n**Note:**\n\nWhen using only one serie of data some charts and metrics will be empty, the deltas won't be meaningful.\n\n\n### Using \"Drillthrough\" for \"Query Detail\"\n\nIn order to correctly filter the \"Query Detail\" sheet you need to use the \"Drillthrough\" function of Power BI.\nThis function is available on any visual that contains a specific fields (in this case \"Sql Hash\") and has a properly configured Drillthrough page.\n\nie: on the \"Queries\" page you can find several tables with the field \"Sql Hash\", right click it and choose \"Drillthrough\", you will now see the available drillthrough pages, if you click the page, it will show up, filtered by the selected \"Sql Hash\".\nTo go back to the previous page you can use the arrow in the top-left corner of the page.\n\n![Drillthrough](/Images/PowerBI_DrillThrough.png)\n\n## Authentication\n\nWhen connecting to the specified source or sources (if the data are on 2 different server/databases) power Bi will use windows authentication.\nIf the current user does not have the permission to access the data the load will fail.\nTo use different credentials you will need to:\n* Run the Power BI template\n* Save it as a report even if it fails to load the data\n* Go to: \"File -> options and settings -> data source settings\" select the datasource and click \"edit permission\" now you can set the authentication method and credentials\n\n## Use the Power BI Report on Existing Databases\n\n**What if I already have the data and just want to use the Power BI report?**\n\nPower BI needs a bunch of views in order to load the data, those views are created when some workload data are written to the db for the first time.\n\nYou can manually create the views by running the procedure in this folder:\n[WorkloadTools\\Consumer\\Analysis\\createAnalysisView.sql](/WorkloadTools/Consumer/Analysis/createAnalysisView.sql)\n\n```\n--At least one of the two parameters must be provided, the schema must be the same of your data tables\nEXEC [dbo].[createAnalysisView]\t@baselineSchema = N'<BaselineSchemaName>', @replaySchema = N'<BenchmarkSchemaName>'\n```\n\n## Additional Suggestions\n\nPower BI Desktop does not offer a \"wiewer mode\", it has been made to create and edit reports. Anyone with the file has full control over it, and can create/edit/delete any visual or measure in the frontend or tables, relationships and M script in the backend.\n\nIn order to make the report more usable on Power BI Desktop you can set the following\n* **Lock Objects** - this prevents them from moving around while using the report (in the top bar \"View\" → \"Lock Objects\" checkbox)\n* **Collapse Unused Bars** - this allows you to recover plently of space to view the report, you can collapse the side-bars and the top bar (small arrow i the top right corner)\n\n## Notes For Editors and Curious\n\nIf you are new to Power BI and want to make some changes, make your own report or simply know how the report works you may need to know the following\n\n* **Hidden Objects** - to have a more readable report only the strictly necessary is visible in the \"Field\" panel, some tables, columns and measures (formulas) are hidden. To view them expand the \"Field\" side-bar, right click and enable \"View hidden\"\n* **Time Field** - For how the Power BI model works:\n    * **always** use the field [Elapsed Time (min)] of the \"Time\" table in any visual that displayes the trend by time (or use the time in general)\n    * Any other [Elapsed Time (min)] Field (there is one in almost every table, set as hidden) will not propagate the filter correctly. If used you will obtain flat charts and static numbers\n\n"
  },
  {
    "path": "Setup/Product.wxs",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\">\n\n  <?if $(var.Platform) = x64 ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFiles64Folder\" ?>\n  <?else ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFilesFolder\" ?>\n  <?endif ?>\n\n  <?define ProductName=\"WorkloadTools\" ?>\n  <?define CompanyName=\"sqlconsulting.it\" ?>\n  <?define ProductVersion=\"$(var.BuildVersion)\" ?>\n\n  <Product Id=\"*\" Name=\"$(var.ProductName)\" Language=\"1033\" Version=\"$(var.ProductVersion)\"\n           Manufacturer=\"$(var.CompanyName)\" UpgradeCode=\"EF0F1905-E03F-4634-BB2E-3A5C3369AB23\">\n\n    <Package InstallerVersion=\"500\" Compressed=\"yes\" InstallScope=\"perMachine\" />\n\n    <MajorUpgrade AllowDowngrades=\"yes\" />\n    <MediaTemplate EmbedCab=\"yes\" />\n\n    <Property Id=\"REINSTALLMODE\" Value=\"amus\" />\n\n    <!--\n      These component groups are generated by heat.exe in buildmsi.ps1.\n      XSL transforms (transform.xsl, transform2.xsl, transform3.xsl) filter\n      out duplicate and unwanted files across the component groups.\n    -->\n    <Feature Id=\"ProductFeature\" Title=\"WorkloadTools\" Level=\"1\">\n      <ComponentGroupRef Id=\"ProductComponents\" />\n      <ComponentGroupRef Id=\"WorkloadViewerComponents\" />\n      <ComponentGroupRef Id=\"ProductReports\" />\n      <ComponentGroupRef Id=\"ConvertWorkloadComponents\" />\n    </Feature>\n\n  </Product>\n\n  <Fragment>\n    <Directory Id=\"TARGETDIR\" Name=\"SourceDir\">\n      <Directory Id=\"$(var.PlatformProgramFilesFolder)\">\n        <Directory Id=\"INSTALLFOLDER\" Name=\"WorkloadTools\" />\n      </Directory>\n    </Directory>\n  </Fragment>\n\n  <Fragment>\n    <DirectoryRef Id=\"INSTALLFOLDER\">\n      <Directory Id=\"dirCD80900D3B5A452B876D2B896E10F051\" Name=\"Reports\" />\n    </DirectoryRef>\n  </Fragment>\n  <Fragment>\n    <ComponentGroup Id=\"ProductReports\">\n      <Component Id=\"cmp20EE999FA46842A8A3E67D82FF987418\" Directory=\"dirCD80900D3B5A452B876D2B896E10F051\" Guid=\"9CDDBDE3-A115-4CEE-9F5F-41DCCABB618E\">\n        <File Id=\"filF8339E0B13434A31BB913FADC4066D7D\" Source=\"..\\Reports\\WorkloadTools Report - Sample.pbix\" />\n        <File Id=\"fil9A72942DD7E14FC98051DF6EAA302AF0\" Source=\"..\\Reports\\WorkloadTools Report - Template.pbit\" />\n        <File Id=\"filB480F52C6274410197E5CB8F865AE5BB\" Source=\"..\\Reports\\README.md\" />\n      </Component>\n    </ComponentGroup>\n  </Fragment>\n\n  <Fragment>\n    <ComponentGroup Id=\"ProductLicense\">\n      <Component Id=\"cmpE591726ED3774E44B75D7106236DD456\" Directory=\"INSTALLFOLDER\" Guid=\"2CA2DACC-347C-449E-A39E-4A68310BDAB1\">\n        <File Id=\"fil63DB101A0DC947AC94811D8A5C151E54\" Source=\"..\\LICENSE.md\" />\n      </Component>\n    </ComponentGroup>\n  </Fragment>\n\n</Wix>\n"
  },
  {
    "path": "Setup/Setup.wixproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  WiX v3 MSBuild project. Requires WiX Toolset v3.x to be installed.\n  See https://wixtoolset.org/ to download WiX v3.\n  For a standalone build (including harvesting), use buildmsi.ps1.\n-->\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" ToolsVersion=\"4.0\">\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">x86</Platform>\n    <ProductVersion>3.10</ProductVersion>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}</ProjectGuid>\n    <OutputType>Package</OutputType>\n    <OutputName>WorkloadTools</OutputName>\n    <WixTargetsPath Condition=\" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' \">$(MSBuildExtensionsPath32)\\Microsoft\\WiX\\v3.x\\Wix.targets</WixTargetsPath>\n    <WixTargetsPath Condition=\" '$(WixTargetsPath)' == '' \">$(MSBuildExtensionsPath)\\Microsoft\\WiX\\v3.x\\Wix.targets</WixTargetsPath>\n    <!-- Default version for local developer builds; buildmsi.ps1 always passes BuildVersion -->\n    <BuildVersion Condition=\" '$(BuildVersion)' == '' \">0.0.0.0</BuildVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x86' \">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <IntermediateOutputPath>obj\\x86\\Release\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x64' \">\n    <OutputPath>bin\\x64\\Release\\</OutputPath>\n    <IntermediateOutputPath>obj\\x64\\Release\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x86' \">\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <IntermediateOutputPath>obj\\x86\\Debug\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x64' \">\n    <OutputPath>bin\\x64\\Debug\\</OutputPath>\n    <IntermediateOutputPath>obj\\x64\\Debug\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Product.wxs\" />\n    <!--\n      harvest.wxs, harvest2.wxs and harvest3.wxs are generated by heat.exe\n      in buildmsi.ps1. Run buildmsi.ps1 before building from Visual Studio.\n    -->\n    <Compile Include=\"harvest.wxs\" Condition=\"Exists('harvest.wxs')\" />\n    <Compile Include=\"harvest2.wxs\" Condition=\"Exists('harvest2.wxs')\" />\n    <Compile Include=\"harvest3.wxs\" Condition=\"Exists('harvest3.wxs')\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"postbuild.bat\" />\n    <Content Include=\"SignMsi.ps1\" />\n    <Content Include=\"transform.xsl\" />\n    <Content Include=\"transform2.xsl\" />\n    <Content Include=\"transform3.xsl\" />\n  </ItemGroup>\n  <PropertyGroup>\n    <PostBuildEvent>call $(ProjectDir)postbuild.bat \"$(TargetPath)\" \"$(TargetDir)WorkloadTools_$(Platform)$(TargetExt)\"</PostBuildEvent>\n  </PropertyGroup>\n  <Import Project=\"$(WixTargetsPath)\" Condition=\"Exists('$(WixTargetsPath)')\" />\n  <Target Name=\"EnsureWixTargetsImported\" BeforeTargets=\"PrepareForBuild\" Condition=\" '$(WixTargetsImported)' == '' \">\n    <Warning Text=\"WiX Toolset v3 build targets were not found at '$(WixTargetsPath)'. Build skipped. Use buildmsi.ps1 to build the installer.\" />\n  </Target>\n  <Target Name=\"Build\" Condition=\" '$(WixTargetsImported)' == '' \">\n    <Message Text=\"Setup project skipped: WiX Toolset v3 is not installed. Use buildmsi.ps1 to build the installer.\" Importance=\"high\" />\n  </Target>\n</Project>"
  },
  {
    "path": "Setup/SignMsi.ps1",
    "content": "﻿[CmdletBinding()]\nParam(\n    [Parameter(Mandatory=$True,Position=1)]\n    [string]$InputFile,\n    [Parameter(Mandatory=$True,Position=2)]\n    [string]$OutputFile\n)\n\n\nif(-not (Test-Path $PSScriptRoot\\SignParams.ps1)) \n{\n    Write-Warning \"No code signing is applied to the .msi file.\"\n    Write-Warning \"You need to create a file called SignParams.ps1 and provide signing info.\"\n\n    Write-Output \"Moving $InputFile --> $OutputFile\"\n    Move-Item $InputFile $OutputFile -Force\n\n    exit\n}\n\n# read paramters\n$signParams = get-content $PSScriptRoot\\SignParams.ps1 -Raw\nInvoke-Expression $signParams\n\n$params = $(\n     'sign'\n    ,'/fd'\n    ,'SHA1'\n    ,'/f'\n    ,('\"' + $certPath + '\"')\n    ,'/p'\n    ,('\"' + $certPass + '\"')\n    ,'/sha1'\n    ,$certSha\n    ,'/t'\n    ,('\"' + $certTime + '\"')\n    ,'/d'\n    ,'\"WorkloadTools\"'\n)\n\n& $signTool ($params + $InputFile)\n\nWrite-Output \"Moving $InputFile --> $OutputFile\"\nMove-Item $InputFile $OutputFile -Force\n"
  },
  {
    "path": "Setup/buildmsi.ps1",
    "content": "param (\n    [Parameter(Mandatory=$false)]\n    [string]$BuildVersion = \"1.0.0.0\",\n    [Parameter(Mandatory=$false)]\n    [string]$Platform = \"x64\"\n)\n\nSet-Location $PSScriptRoot\n\n# ---------------------------------------------------------------------------\n# Locate MSBuild via vswhere (ships with Visual Studio 2017+)\n# ---------------------------------------------------------------------------\n$vswhere = \"${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe\"\nif (-not (Test-Path $vswhere)) {\n    throw \"vswhere.exe not found at '$vswhere'. Visual Studio 2017 or newer is required.\"\n}\n\n$msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild `\n    -find MSBuild\\**\\Bin\\MSBuild.exe | Select-Object -First 1\nif (-not $msbuild) {\n    throw \"MSBuild.exe not found. Please install Visual Studio with the MSBuild component.\"\n}\n\n# ---------------------------------------------------------------------------\n# Locate WiX v3 tools (heat.exe, candle.exe, light.exe)\n# The WIX environment variable is set automatically by the WiX v3 installer.\n# ---------------------------------------------------------------------------\n$wixDir = $null\nif ($env:WIX -and (Test-Path $env:WIX)) {\n    $wixDir = $env:WIX\n}\nif (-not $wixDir) {\n    $wixDir = @(\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.14\",\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.11\",\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.10\",\n        \"${env:ProgramFiles}\\WiX Toolset v3.14\",\n        \"${env:ProgramFiles}\\WiX Toolset v3.11\"\n    ) | Where-Object { Test-Path $_ } | Select-Object -First 1\n}\nif (-not $wixDir) {\n    throw \"WiX Toolset v3 not found. Install from https://wixtoolset.org/ or set the WIX environment variable.\"\n}\n\n$heat   = Join-Path $wixDir \"bin\\heat.exe\"\n$candle = Join-Path $wixDir \"bin\\candle.exe\"\n$light  = Join-Path $wixDir \"bin\\light.exe\"\n\n# ---------------------------------------------------------------------------\n# Build the .NET projects whose output directories will be harvested into\n# the MSI by heat.exe.\n#\n# SqlWorkload and WorkloadViewer use the \"AnyCPU\" MSBuild platform to produce\n# x64 output (bin\\x64\\Release) or \"x86\" for x86 output (bin\\x86\\Release).\n# ConvertWorkload always outputs to bin\\Release regardless of platform.\n# ---------------------------------------------------------------------------\n$netPlatform = if ($Platform -eq 'x86') { 'x86' } else { 'AnyCPU' }\n\n& $msbuild \"$PSScriptRoot\\..\\SqlWorkload\\SqlWorkload.csproj\" `\n    -t:Rebuild -p:Configuration=Release \"-p:Platform=$netPlatform\" `\n    -nologo -verbosity:minimal\nif ($LASTEXITCODE -ne 0) { throw \"SqlWorkload build failed.\" }\n\n& $msbuild \"$PSScriptRoot\\..\\WorkloadViewer\\WorkloadViewer.csproj\" `\n    -t:Rebuild -p:Configuration=Release \"-p:Platform=$netPlatform\" `\n    -nologo -verbosity:minimal\nif ($LASTEXITCODE -ne 0) { throw \"WorkloadViewer build failed.\" }\n\n& $msbuild \"$PSScriptRoot\\..\\ConvertWorkload\\ConvertWorkload.csproj\" `\n    -t:Rebuild -p:Configuration=Release `\n    -nologo -verbosity:minimal\nif ($LASTEXITCODE -ne 0) { throw \"ConvertWorkload build failed.\" }\n\n# ---------------------------------------------------------------------------\n# Prepare output and intermediate directories\n# ---------------------------------------------------------------------------\n$outDir = \"$PSScriptRoot\\bin\\$Platform\\Release\"\n$objDir = \"$PSScriptRoot\\obj\\$Platform\\Release\"\n\nforeach ($dir in $outDir, $objDir) {\n    if (-not (Test-Path $dir)) {\n        New-Item -ItemType Directory -Path $dir -Force | Out-Null\n    } elseif (Test-Path \"$dir\\*\") {\n        Remove-Item \"$dir\\*\" -Recurse -Force\n    }\n}\n\n# ---------------------------------------------------------------------------\n# Harvest .NET output directories with heat.exe (WiX v3)\n# The generated harvest*.wxs files are placed in the Setup project directory\n# so they are picked up as Compile items in Setup.wixproj.\n# ---------------------------------------------------------------------------\n$sqlWorkloadDir    = [System.IO.Path]::GetFullPath(\"$PSScriptRoot\\..\\SqlWorkload\\bin\\$Platform\\Release\")\n$workloadViewerDir = [System.IO.Path]::GetFullPath(\"$PSScriptRoot\\..\\WorkloadViewer\\bin\\$Platform\\Release\")\n$convertWorkloadDir = [System.IO.Path]::GetFullPath(\"$PSScriptRoot\\..\\ConvertWorkload\\bin\\Release\")\n\n& $heat dir \"$sqlWorkloadDir\" `\n    -cg ProductComponents -dr INSTALLFOLDER -srd -sreg -ag `\n    -var var.SqlWorkloadDir `\n    -t \"$PSScriptRoot\\transform.xsl\" `\n    -out \"$PSScriptRoot\\harvest.wxs\" -nologo\nif ($LASTEXITCODE -ne 0) { throw \"heat.exe failed for SqlWorkload.\" }\n\n& $heat dir \"$workloadViewerDir\" `\n    -cg WorkloadViewerComponents -dr INSTALLFOLDER -srd -sreg -ag `\n    -var var.WorkloadViewerDir `\n    -t \"$PSScriptRoot\\transform.xsl\" -t \"$PSScriptRoot\\transform2.xsl\" `\n    -t \"$PSScriptRoot\\transform-nodirs.xsl\" `\n    -out \"$PSScriptRoot\\harvest2.wxs\" -nologo\nif ($LASTEXITCODE -ne 0) { throw \"heat.exe failed for WorkloadViewer.\" }\n\n& $heat dir \"$convertWorkloadDir\" `\n    -cg ConvertWorkloadComponents -dr INSTALLFOLDER -srd -sreg -ag `\n    -var var.ConvertWorkloadDir `\n    -t \"$PSScriptRoot\\transform.xsl\" -t \"$PSScriptRoot\\transform2.xsl\" `\n    -t \"$PSScriptRoot\\transform3.xsl\" -t \"$PSScriptRoot\\transform-nodirs.xsl\" `\n    -out \"$PSScriptRoot\\harvest3.wxs\" -nologo\nif ($LASTEXITCODE -ne 0) { throw \"heat.exe failed for ConvertWorkload.\" }\n\n# ---------------------------------------------------------------------------\n# Compile all WXS sources with candle.exe\n# ---------------------------------------------------------------------------\n$arch = if ($Platform -eq 'x86') { 'x86' } else { 'x64' }\n\n& $candle `\n    \"$PSScriptRoot\\Product.wxs\" `\n    \"$PSScriptRoot\\harvest.wxs\" `\n    \"$PSScriptRoot\\harvest2.wxs\" `\n    \"$PSScriptRoot\\harvest3.wxs\" `\n    -arch $arch `\n    \"-dBuildVersion=$BuildVersion\" `\n    \"-dPlatform=$Platform\" `\n    \"-dSqlWorkloadDir=$sqlWorkloadDir\" `\n    \"-dWorkloadViewerDir=$workloadViewerDir\" `\n    \"-dConvertWorkloadDir=$convertWorkloadDir\" `\n    -out \"$objDir\\\" `\n    -nologo\nif ($LASTEXITCODE -ne 0) { throw \"candle.exe failed.\" }\n\n# ---------------------------------------------------------------------------\n# Link with light.exe to produce the MSI\n# ---------------------------------------------------------------------------\n$wixObjs = Get-ChildItem \"$objDir\\*.wixobj\" | Select-Object -ExpandProperty FullName\n\n& $light $wixObjs `\n    -out \"$outDir\\WorkloadTools.msi\" `\n    -nologo\nif ($LASTEXITCODE -ne 0) { throw \"light.exe failed.\" }\n\n# ---------------------------------------------------------------------------\n# Sign (or just rename if no signing cert is configured)\n# ---------------------------------------------------------------------------\n. $PSScriptRoot\\SignMsi.ps1 `\n    -InputFile \"$outDir\\WorkloadTools.msi\" `\n    -OutputFile \"$outDir\\WorkloadTools_$Platform.msi\""
  },
  {
    "path": "Setup/postbuild.bat",
    "content": "powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File %~dp0\\SignMsi.ps1 -InputFile %1 -OutputFile %2\n\n"
  },
  {
    "path": "Setup/transform-nodirs.xsl",
    "content": "<?xml version=\"1.0\" ?>\n<xsl:stylesheet version=\"1.0\"\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:wix=\"http://schemas.microsoft.com/wix/2006/wi\">\n\n  <!-- Copy all attributes and elements to the output. -->\n  <xsl:template match=\"@*|*\">\n    <xsl:copy>\n      <xsl:apply-templates select=\"@*\" />\n      <xsl:apply-templates select=\"*\" />\n    </xsl:copy>\n  </xsl:template>\n\n  <xsl:output method=\"xml\" indent=\"yes\" />\n\n  <!--\n    Remove Fragment elements that only contain DirectoryRef children (i.e. the\n    directory-structure fragments that heat.exe generates for each subdirectory).\n    Those directories are already declared in harvest.wxs (SqlWorkload) and\n    re-declaring them in secondary harvest files causes LGHT0091 \"Duplicate symbol\"\n    errors during linking. The ComponentGroup fragment is unaffected because it\n    does not contain a DirectoryRef as a direct child.\n    -->\n  <xsl:template match=\"wix:Fragment[wix:DirectoryRef and not(wix:ComponentGroup)]\" />\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "Setup/transform.xsl",
    "content": "﻿<?xml version=\"1.0\" ?>\n<xsl:stylesheet version=\"1.0\"\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:wix=\"http://schemas.microsoft.com/wix/2006/wi\">\n\n  <!-- Copy all attributes and elements to the output. -->\n  <xsl:template match=\"@*|*\">\n    <xsl:copy>\n      <xsl:apply-templates select=\"@*\" />\n      <xsl:apply-templates select=\"*\" />\n    </xsl:copy>\n  </xsl:template>\n\n  <xsl:output method=\"xml\" indent=\"yes\" />\n\n  <!--\n    This section includes all the files that need not be included\n    in the setup. For instance, all local log files.\n    -->\n\n  <xsl:key name=\"log-search\" match=\"wix:Component[contains(wix:File/@Source, '.log')]\" use=\"@Id\" />\n  <xsl:template match=\"wix:Component[key('log-search', @Id)]\" />\n  <xsl:template match=\"wix:ComponentRef[key('log-search', @Id)]\" />\n</xsl:stylesheet>"
  },
  {
    "path": "Setup/transform2.xsl",
    "content": "<?xml version=\"1.0\" ?>\n<xsl:stylesheet version=\"1.0\"\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:wix=\"http://schemas.microsoft.com/wix/2006/wi\">\n\n  <!-- Copy all attributes and elements to the output. -->\n  <xsl:template match=\"@*|*\">\n    <xsl:copy>\n      <xsl:apply-templates select=\"@*\" />\n      <xsl:apply-templates select=\"*\" />\n    </xsl:copy>\n  </xsl:template>\n\n  <xsl:output method=\"xml\" indent=\"yes\" />\n\n  <!--\n    This section includes all the files that MUST not be included\n    in the setup, because they are already included by other projects.\n    In this list we will include the libraries (nuget packages) that are\n    already included in the SqlWorkload project, in order to avoid collisions.\n\n    ##########################################################################\n    REMEMBER THAT THE MATCH IS CASE-SENSITIVE!!! \n    If your setup is not building, please double check the case of the items\n    that you want to exclude\n    ##########################################################################\n    \n    -->\n\n  <xsl:key name=\"commandline-search\" match=\"wix:Component[contains(wix:File/@Source, 'CommandLine.')]\" use=\"@Id\" />\n  <xsl:template match=\"wix:Component[key('commandline-search', @Id)]\" />\n  <xsl:template match=\"wix:ComponentRef[key('commandline-search', @Id)]\" />\n  \n  <xsl:key name=\"nlog-search\" match=\"wix:Component[contains(wix:File/@Source, 'NLog.')]\" use=\"@Id\" />\n  <xsl:template match=\"wix:Component[key('nlog-search', @Id)]\" />\n  <xsl:template match=\"wix:ComponentRef[key('nlog-search', @Id)]\" />\n\n  <xsl:key name=\"config-search\" match=\"wix:Directory[@Name='Config']\" use=\"@Id\" />\n  <xsl:template match=\"wix:Directory[key('config-search', @Id)]\" />\n  <xsl:template match=\"wix:DirectoryRef[key('config-search', @Id)]\" />\n\n  <xsl:key name=\"consumer-search\" match=\"wix:Directory[@Name='Consumer']\" use=\"@Id\" />\n  <xsl:template match=\"wix:Directory[key('consumer-search', @Id)]\" />\n  <xsl:template match=\"wix:DirectoryRef[key('consumer-search', @Id)]\" />\n\n  <xsl:key name=\"listener-search\" match=\"wix:Directory[@Name='Listener']\" use=\"@Id\" />\n  <xsl:template match=\"wix:Directory[key('listener-search', @Id)]\" />\n  <xsl:template match=\"wix:DirectoryRef[key('listener-search', @Id)]\" />\n\n  <xsl:key name=\"sql-search\" match=\"wix:Component[contains(wix:File/@Source, '.sql')]\" use=\"@Id\" />\n  <xsl:template match=\"wix:Component[key('sql-search', @Id)]\" />\n  <xsl:template match=\"wix:ComponentRef[key('sql-search', @Id)]\" />\n</xsl:stylesheet>"
  },
  {
    "path": "Setup/transform3.xsl",
    "content": "<?xml version=\"1.0\" ?>\n<xsl:stylesheet version=\"1.0\"\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:wix=\"http://schemas.microsoft.com/wix/2006/wi\">\n\n  <!-- Copy all attributes and elements to the output. -->\n  <xsl:template match=\"@*|*\">\n    <xsl:copy>\n      <xsl:apply-templates select=\"@*\" />\n      <xsl:apply-templates select=\"*\" />\n    </xsl:copy>\n  </xsl:template>\n\n  <xsl:output method=\"xml\" indent=\"yes\" />\n\n  <!--\n    This section includes all the files that MUST not be included\n    in the setup, because they are already included by other projects.\n    In this list we will include the libraries (nuget packages) that are\n    already included in the SqlWorkload project, in order to avoid collisions.\n\n    ##########################################################################\n    REMEMBER THAT THE MATCH IS CASE-SENSITIVE!!! \n    If your setup is not building, please double check the case of the items\n    that you want to exclude\n    ##########################################################################\n    \n    -->\n\n  <xsl:key name=\"unwanted-search\" match=\"wix:Component[not(contains(wix:File/@Source, '\\ConvertWorkload.'))]\" use=\"@Id\" />\n  <xsl:template match=\"wix:Component[key('unwanted-search', @Id)]\" />\n  <xsl:template match=\"wix:ComponentRef[key('unwanted-search', @Id)]\" />\n\n</xsl:stylesheet>"
  },
  {
    "path": "SetupBootstrapper/Bundle.wxs",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Wix xmlns=\"http://schemas.microsoft.com/wix/2006/wi\"\n     xmlns:bal=\"http://schemas.microsoft.com/wix/BalExtension\">\n\n  <?if $(var.Platform) = x64 ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFiles64Folder\" ?>\n  <?else ?>\n    <?define PlatformProgramFilesFolder = \"ProgramFilesFolder\" ?>\n  <?endif ?>\n\n  <?define ProductName=\"WorkloadTools\" ?>\n  <?define CompanyName=\"sqlconsulting.it\" ?>\n  <?define ProductVersion=\"$(var.BuildVersion)\" ?>\n\n  <Bundle Name=\"$(var.ProductName)\" Version=\"$(var.ProductVersion)\" Manufacturer=\"$(var.CompanyName)\" UpgradeCode=\"cad976c4-d0c6-4313-b605-ec3749a23b5f\">\n    <BootstrapperApplicationRef Id=\"WixStandardBootstrapperApplication.RtfLicense\">\n      <bal:WixStandardBootstrapperApplication\n            LicenseFile=\"License\\License.rtf\"\n            LogoFile=\"License\\Icon.png\"\n            LogoSideFile=\"License\\SideLogo.png\"\n            />\n    </BootstrapperApplicationRef>\n\n    <Variable Name=\"InstallFolder\" Value=\"[$(var.PlatformProgramFilesFolder)]WorkloadTools\" Type=\"string\" />\n\n    <Chain>\n      <MsiPackage SourceFile=\"..\\Setup\\bin\\$(var.Platform)\\Release\\WorkloadTools_$(var.Platform).msi\" Id=\"WorkloadToolsMsi\">\n        <MsiProperty Name=\"INSTALLFOLDER\" Value=\"[InstallFolder]\" />\n      </MsiPackage>\n      <PackageGroupRef Id=\"VCRedist\"/>\n    </Chain>\n  </Bundle>\n\n  <Fragment>\n    <PackageGroup Id=\"VCRedist\">\n      <ExePackage\n         Cache=\"no\"\n         Compressed=\"yes\"\n         PerMachine=\"yes\"\n         Permanent=\"yes\"\n         Vital=\"yes\"\n         Name=\"Redist\\vcredist_$(var.Platform).exe\"\n         SourceFile=\"Redist\\vcredist_$(var.Platform).exe\"\n         InstallCommand=\"/install /quiet /norestart\">\n\n        <ExitCode Value=\"3010\" Behavior=\"forceReboot\"/>\n\n        <!-- Ignore \"Newer version installed\" error -->\n        <ExitCode Value=\"1638\" Behavior=\"success\"/>\n      </ExePackage>\n    </PackageGroup>\n  </Fragment>\n</Wix>\n"
  },
  {
    "path": "SetupBootstrapper/License/License.rtf",
    "content": "{\\rtf1\\adeflang1025\\ansi\\ansicpg1252\\uc1\\adeff0\\deff0\\stshfdbch0\\stshfloch31506\\stshfhich31506\\stshfbi31506\\deflang1033\\deflangfe1033\\themelang1033\\themelangfe0\\themelangcs0{\\fonttbl{\\f0\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}{\\f2\\fbidi \\fmodern\\fcharset0\\fprq1{\\*\\panose 02070309020205020404}Courier New;}\n{\\f34\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02040503050406030204}Cambria Math;}{\\f37\\fbidi \\fswiss\\fcharset0\\fprq2{\\*\\panose 020f0502020204030204}Calibri;}{\\flomajor\\f31500\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}\n{\\fdbmajor\\f31501\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}{\\fhimajor\\f31502\\fbidi \\fswiss\\fcharset0\\fprq2{\\*\\panose 020f0302020204030204}Calibri Light;}\n{\\fbimajor\\f31503\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}{\\flominor\\f31504\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}\n{\\fdbminor\\f31505\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}{\\fhiminor\\f31506\\fbidi \\fswiss\\fcharset0\\fprq2{\\*\\panose 020f0502020204030204}Calibri;}\n{\\fbiminor\\f31507\\fbidi \\froman\\fcharset0\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}{\\f43\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}{\\f44\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}\n{\\f46\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}{\\f47\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}{\\f48\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}{\\f49\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}\n{\\f50\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}{\\f51\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}{\\f63\\fbidi \\fmodern\\fcharset238\\fprq1 Courier New CE;}{\\f64\\fbidi \\fmodern\\fcharset204\\fprq1 Courier New Cyr;}\n{\\f66\\fbidi \\fmodern\\fcharset161\\fprq1 Courier New Greek;}{\\f67\\fbidi \\fmodern\\fcharset162\\fprq1 Courier New Tur;}{\\f68\\fbidi \\fmodern\\fcharset177\\fprq1 Courier New (Hebrew);}{\\f69\\fbidi \\fmodern\\fcharset178\\fprq1 Courier New (Arabic);}\n{\\f70\\fbidi \\fmodern\\fcharset186\\fprq1 Courier New Baltic;}{\\f71\\fbidi \\fmodern\\fcharset163\\fprq1 Courier New (Vietnamese);}{\\f383\\fbidi \\froman\\fcharset238\\fprq2 Cambria Math CE;}{\\f384\\fbidi \\froman\\fcharset204\\fprq2 Cambria Math Cyr;}\n{\\f386\\fbidi \\froman\\fcharset161\\fprq2 Cambria Math Greek;}{\\f387\\fbidi \\froman\\fcharset162\\fprq2 Cambria Math Tur;}{\\f390\\fbidi \\froman\\fcharset186\\fprq2 Cambria Math Baltic;}{\\f391\\fbidi \\froman\\fcharset163\\fprq2 Cambria Math (Vietnamese);}\n{\\f413\\fbidi \\fswiss\\fcharset238\\fprq2 Calibri CE;}{\\f414\\fbidi \\fswiss\\fcharset204\\fprq2 Calibri Cyr;}{\\f416\\fbidi \\fswiss\\fcharset161\\fprq2 Calibri Greek;}{\\f417\\fbidi \\fswiss\\fcharset162\\fprq2 Calibri Tur;}\n{\\f418\\fbidi \\fswiss\\fcharset177\\fprq2 Calibri (Hebrew);}{\\f419\\fbidi \\fswiss\\fcharset178\\fprq2 Calibri (Arabic);}{\\f420\\fbidi \\fswiss\\fcharset186\\fprq2 Calibri Baltic;}{\\f421\\fbidi \\fswiss\\fcharset163\\fprq2 Calibri (Vietnamese);}\n{\\flomajor\\f31508\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}{\\flomajor\\f31509\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}{\\flomajor\\f31511\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}\n{\\flomajor\\f31512\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}{\\flomajor\\f31513\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}{\\flomajor\\f31514\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}\n{\\flomajor\\f31515\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}{\\flomajor\\f31516\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}{\\fdbmajor\\f31518\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}\n{\\fdbmajor\\f31519\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}{\\fdbmajor\\f31521\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}{\\fdbmajor\\f31522\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}\n{\\fdbmajor\\f31523\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}{\\fdbmajor\\f31524\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}{\\fdbmajor\\f31525\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}\n{\\fdbmajor\\f31526\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}{\\fhimajor\\f31528\\fbidi \\fswiss\\fcharset238\\fprq2 Calibri Light CE;}{\\fhimajor\\f31529\\fbidi \\fswiss\\fcharset204\\fprq2 Calibri Light Cyr;}\n{\\fhimajor\\f31531\\fbidi \\fswiss\\fcharset161\\fprq2 Calibri Light Greek;}{\\fhimajor\\f31532\\fbidi \\fswiss\\fcharset162\\fprq2 Calibri Light Tur;}{\\fhimajor\\f31533\\fbidi \\fswiss\\fcharset177\\fprq2 Calibri Light (Hebrew);}\n{\\fhimajor\\f31534\\fbidi \\fswiss\\fcharset178\\fprq2 Calibri Light (Arabic);}{\\fhimajor\\f31535\\fbidi \\fswiss\\fcharset186\\fprq2 Calibri Light Baltic;}{\\fhimajor\\f31536\\fbidi \\fswiss\\fcharset163\\fprq2 Calibri Light (Vietnamese);}\n{\\fbimajor\\f31538\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}{\\fbimajor\\f31539\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}{\\fbimajor\\f31541\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}\n{\\fbimajor\\f31542\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}{\\fbimajor\\f31543\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}{\\fbimajor\\f31544\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}\n{\\fbimajor\\f31545\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}{\\fbimajor\\f31546\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}{\\flominor\\f31548\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}\n{\\flominor\\f31549\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}{\\flominor\\f31551\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}{\\flominor\\f31552\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}\n{\\flominor\\f31553\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}{\\flominor\\f31554\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}{\\flominor\\f31555\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}\n{\\flominor\\f31556\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}{\\fdbminor\\f31558\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}{\\fdbminor\\f31559\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}\n{\\fdbminor\\f31561\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}{\\fdbminor\\f31562\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}{\\fdbminor\\f31563\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}\n{\\fdbminor\\f31564\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}{\\fdbminor\\f31565\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}{\\fdbminor\\f31566\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}\n{\\fhiminor\\f31568\\fbidi \\fswiss\\fcharset238\\fprq2 Calibri CE;}{\\fhiminor\\f31569\\fbidi \\fswiss\\fcharset204\\fprq2 Calibri Cyr;}{\\fhiminor\\f31571\\fbidi \\fswiss\\fcharset161\\fprq2 Calibri Greek;}{\\fhiminor\\f31572\\fbidi \\fswiss\\fcharset162\\fprq2 Calibri Tur;}\n{\\fhiminor\\f31573\\fbidi \\fswiss\\fcharset177\\fprq2 Calibri (Hebrew);}{\\fhiminor\\f31574\\fbidi \\fswiss\\fcharset178\\fprq2 Calibri (Arabic);}{\\fhiminor\\f31575\\fbidi \\fswiss\\fcharset186\\fprq2 Calibri Baltic;}\n{\\fhiminor\\f31576\\fbidi \\fswiss\\fcharset163\\fprq2 Calibri (Vietnamese);}{\\fbiminor\\f31578\\fbidi \\froman\\fcharset238\\fprq2 Times New Roman CE;}{\\fbiminor\\f31579\\fbidi \\froman\\fcharset204\\fprq2 Times New Roman Cyr;}\n{\\fbiminor\\f31581\\fbidi \\froman\\fcharset161\\fprq2 Times New Roman Greek;}{\\fbiminor\\f31582\\fbidi \\froman\\fcharset162\\fprq2 Times New Roman Tur;}{\\fbiminor\\f31583\\fbidi \\froman\\fcharset177\\fprq2 Times New Roman (Hebrew);}\n{\\fbiminor\\f31584\\fbidi \\froman\\fcharset178\\fprq2 Times New Roman (Arabic);}{\\fbiminor\\f31585\\fbidi \\froman\\fcharset186\\fprq2 Times New Roman Baltic;}{\\fbiminor\\f31586\\fbidi \\froman\\fcharset163\\fprq2 Times New Roman (Vietnamese);}}\n{\\colortbl;\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;\\red0\\green0\\blue128;\\red0\\green128\\blue128;\\red0\\green128\\blue0;\n\\red128\\green0\\blue128;\\red128\\green0\\blue0;\\red128\\green128\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;\\red0\\green0\\blue0;\\red0\\green0\\blue0;}{\\*\\defchp \\f31506\\fs22 }{\\*\\defpap \\ql \\li0\\ri0\\sa160\\sl259\\slmult1\n\\widctlpar\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0 }\\noqfpromote {\\stylesheet{\\ql \\li0\\ri0\\sa160\\sl259\\slmult1\\widctlpar\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af0\\afs22\\alang1025 \\ltrch\\fcs0 \n\\f31506\\fs22\\lang1033\\langfe1033\\cgrid\\langnp1033\\langfenp1033 \\snext0 \\sqformat \\spriority0 Normal;}{\\*\\cs10 \\additive \\ssemihidden \\sunhideused \\spriority1 Default Paragraph Font;}{\\*\n\\ts11\\tsrowd\\trftsWidthB3\\trpaddl108\\trpaddr108\\trpaddfl3\\trpaddft3\\trpaddfb3\\trpaddfr3\\trcbpat1\\trcfpat1\\tblind0\\tblindtype3\\tsvertalt\\tsbrdrt\\tsbrdrl\\tsbrdrb\\tsbrdrr\\tsbrdrdgl\\tsbrdrdgr\\tsbrdrh\\tsbrdrv \\ql \\li0\\ri0\\sa160\\sl259\\slmult1\n\\widctlpar\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af31506\\afs22\\alang1025 \\ltrch\\fcs0 \\f31506\\fs22\\lang1033\\langfe1033\\cgrid\\langnp1033\\langfenp1033 \\snext11 \\ssemihidden \\sunhideused Normal Table;}{\\*\\cs15 \\additive \n\\rtlch\\fcs1 \\af0 \\ltrch\\fcs0 \\ul\\cf2 \\sbasedon10 \\ssemihidden \\sunhideused \\styrsid6231089 Hyperlink;}{\\s16\\ql \\li0\\ri0\\widctlpar\n\\tx916\\tx1832\\tx2748\\tx3664\\tx4580\\tx5496\\tx6412\\tx7328\\tx8244\\tx9160\\tx10076\\tx10992\\tx11908\\tx12824\\tx13740\\tx14656\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af2\\afs20\\alang1025 \\ltrch\\fcs0 \n\\f2\\fs20\\lang1033\\langfe1033\\cgrid\\langnp1033\\langfenp1033 \\sbasedon0 \\snext16 \\slink17 \\ssemihidden \\sunhideused \\styrsid6231089 HTML Preformatted;}{\\*\\cs17 \\additive \\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20 \n\\sbasedon10 \\slink16 \\slocked \\ssemihidden \\styrsid6231089 HTML Preformatted Char;}}{\\*\\pgptbl {\\pgp\\ipgp0\\itap0\\li0\\ri0\\sb0\\sa0}}{\\*\\rsidtbl \\rsid1144825\\rsid6231089\\rsid7602890\\rsid8207159\\rsid10379327\\rsid11422003\\rsid12734223\\rsid13719530\\rsid14571638\n\\rsid15563878}{\\mmathPr\\mmathFont34\\mbrkBin0\\mbrkBinSub0\\msmallFrac0\\mdispDef1\\mlMargin0\\mrMargin0\\mdefJc1\\mwrapIndent1440\\mintLim0\\mnaryLim1}{\\info{\\author Gianluca Sartori}{\\operator Gianluca Sartori}{\\creatim\\yr2020\\mo3\\dy30\\hr16\\min21}\n{\\revtim\\yr2020\\mo3\\dy30\\hr17\\min5}{\\version5}{\\edmins3}{\\nofpages1}{\\nofwords160}{\\nofchars914}{\\nofcharsws1072}{\\vern123}}{\\*\\xmlnstbl {\\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\n\\paperw12240\\paperh15840\\margl1440\\margr1440\\margt1440\\margb1440\\gutter0\\ltrsect \n\\widowctrl\\ftnbj\\aenddoc\\trackmoves0\\trackformatting1\\donotembedsysfont1\\relyonvml0\\donotembedlingdata0\\grfdocevents0\\validatexml1\\showplaceholdtext0\\ignoremixedcontent0\\saveinvalidxml0\\showxmlerrors1\\noxlattoyen\n\\expshrtn\\noultrlspc\\dntblnsbdb\\nospaceforul\\formshade\\horzdoc\\dgmargin\\dghspace180\\dgvspace180\\dghorigin1440\\dgvorigin1440\\dghshow1\\dgvshow1\n\\jexpand\\viewkind1\\viewscale120\\pgbrdrhead\\pgbrdrfoot\\splytwnine\\ftnlytwnine\\htmautsp\\nolnhtadjtbl\\useltbaln\\alntblind\\lytcalctblwd\\lyttblrtgr\\lnbrkrule\\nobrkwrptbl\\snaptogridincell\\allowfieldendsel\\wrppunct\n\\asianbrkrule\\rsidroot6231089\\newtblstyruls\\nogrowautofit\\usenormstyforlist\\noindnmbrts\\felnbrelev\\nocxsptable\\indrlsweleven\\noafcnsttbl\\afelev\\utinl\\hwelev\\spltpgpar\\notcvasp\\notbrkcnstfrctbl\\notvatxbx\\krnprsnet\\cachedcolbal \\nouicompat \\fet0\n{\\*\\wgrffmtfilter 2450}\\nofeaturethrottle1\\ilfomacatclnup0\\ltrpar \\sectd \\ltrsect\\linex0\\endnhere\\sectlinegrid360\\sectdefaultcl\\sftnbj {\\*\\pnseclvl1\\pnucrm\\pnstart1\\pnindent720\\pnhang {\\pntxta .}}{\\*\\pnseclvl2\\pnucltr\\pnstart1\\pnindent720\\pnhang \n{\\pntxta .}}{\\*\\pnseclvl3\\pndec\\pnstart1\\pnindent720\\pnhang {\\pntxta .}}{\\*\\pnseclvl4\\pnlcltr\\pnstart1\\pnindent720\\pnhang {\\pntxta )}}{\\*\\pnseclvl5\\pndec\\pnstart1\\pnindent720\\pnhang {\\pntxtb (}{\\pntxta )}}{\\*\\pnseclvl6\\pnlcltr\\pnstart1\\pnindent720\\pnhang \n{\\pntxtb (}{\\pntxta )}}{\\*\\pnseclvl7\\pnlcrm\\pnstart1\\pnindent720\\pnhang {\\pntxtb (}{\\pntxta )}}{\\*\\pnseclvl8\\pnlcltr\\pnstart1\\pnindent720\\pnhang {\\pntxtb (}{\\pntxta )}}{\\*\\pnseclvl9\\pnlcrm\\pnstart1\\pnindent720\\pnhang {\\pntxtb (}{\\pntxta )}}\n\\pard\\plain \\ltrpar\\ql \\li0\\ri0\\widctlpar\\tx916\\tx1832\\tx2748\\tx3664\\tx4580\\tx5496\\tx6412\\tx7328\\tx8244\\tx9160\\tx10076\\tx10992\\tx11908\\tx12824\\tx13740\\tx14656\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0\\pararsid6231089 \\rtlch\\fcs1 \n\\af0\\afs22\\alang1025 \\ltrch\\fcs0 \\f31506\\fs22\\lang1033\\langfe1033\\cgrid\\langnp1033\\langfenp1033 {\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid12734223 MIT License}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 \n\n\\par \n\\par Copyright (c) 20}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089 20}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089  Gianluca Sartori\n\\par \n\\par Permission is hereby granted, free of charge, to any person obtaining a copy}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 \nof this software and associated documentation files (the \"Software\"), to deal}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 \nin the Software without restriction, including without limitation the rights}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 to use, copy, modify, merge, pu\nblish, distribute, sublicense, and/or sell}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 \ncopies of the Software, and to permit persons to whom the Software is}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 \nfurnished to do so, subject to the following conditions:}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089 \n\\par }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825\\charrsid6231089 \n\\par }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 The above copyright notice and this permission notice shall be included in }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825 a}{\\rtlch\\fcs1 \\af2\\afs20 \n\\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 ll}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 copies or substantial portions of the Software.\n\n\\par \n\\par }\\pard \\ltrpar\\ql \\li0\\ri0\\widctlpar\\tx916\\tx1832\\tx2748\\tx3664\\tx4580\\tx5496\\tx6412\\tx7328\\tx8244\\tx9160\\tx10076\\tx10992\\tx11908\\tx12824\\tx13740\\tx14656\\wrapdefault\\aspalpha\\aspnum\\faauto\\adjustright\\rin0\\lin0\\itap0\\pararsid1144825 {\\rtlch\\fcs1 \n\\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \n\\f2\\fs20\\cf1\\insrsid6231089\\charrsid6231089 SOFTWARE.}{\\rtlch\\fcs1 \\af2\\afs20 \\ltrch\\fcs0 \\f2\\fs20\\cf1\\insrsid1144825  }{\\rtlch\\fcs1 \\af0 \\ltrch\\fcs0 \\insrsid13719530\\charrsid6231089 \n\\par }{\\*\\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a\n9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad\n5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6\nb01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0\n0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6\na7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f\nc7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512\n0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462\na1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865\n6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b\n4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b\n4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f\n7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd\ned21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d\n7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b\nd09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52\nfa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71\nb175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b\nfa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567\n9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd\n79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf\n5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2\nd398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1\n738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68\n2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac\n5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a\nb17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9\n493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2\nbe04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f\nf9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64\n7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e\nb48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4\n6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd\nf942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d\n7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39\n4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf\n1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a\nfaadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2\n67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9\n416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27\n1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b\n8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4\n8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65\n2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36\n3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e\n3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985\n0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000\n0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000\n000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000\n7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000\n000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000\n000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000}\n{\\*\\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d\n617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169\n6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363\n656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}\n{\\*\\latentstyles\\lsdstimax376\\lsdlockeddef0\\lsdsemihiddendef0\\lsdunhideuseddef0\\lsdqformatdef0\\lsdprioritydef99{\\lsdlockedexcept \\lsdqformat1 \\lsdpriority0 \\lsdlocked0 Normal;\\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 1;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 3;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 4;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 5;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 6;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 7;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 8;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority9 \\lsdlocked0 heading 9;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 1;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 3;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 4;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 5;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 6;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 7;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 8;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index 9;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 1;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 3;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 4;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 5;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 6;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 7;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 8;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority39 \\lsdlocked0 toc 9;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Normal Indent;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 footnote text;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 annotation text;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 header;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 footer;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 index heading;\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority35 \\lsdlocked0 caption;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 table of figures;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 envelope address;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 envelope return;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 footnote reference;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 annotation reference;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 line number;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 page number;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 endnote reference;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 endnote text;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 table of authorities;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 macro;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 toa heading;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Bullet;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Number;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List 3;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List 4;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List 5;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Bullet 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Bullet 3;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Bullet 4;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Bullet 5;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Number 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Number 3;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Number 4;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Number 5;\\lsdqformat1 \\lsdpriority10 \\lsdlocked0 Title;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Closing;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Signature;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority1 \\lsdlocked0 Default Paragraph Font;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text Indent;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Continue;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Continue 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Continue 3;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Continue 4;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 List Continue 5;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Message Header;\\lsdqformat1 \\lsdpriority11 \\lsdlocked0 Subtitle;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Salutation;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Date;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text First Indent;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text First Indent 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Note Heading;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text 3;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text Indent 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Body Text Indent 3;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Block Text;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Hyperlink;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 FollowedHyperlink;\\lsdqformat1 \\lsdpriority22 \\lsdlocked0 Strong;\n\\lsdqformat1 \\lsdpriority20 \\lsdlocked0 Emphasis;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Document Map;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Plain Text;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 E-mail Signature;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Top of Form;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Bottom of Form;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Normal (Web);\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Acronym;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Address;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Cite;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Code;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Definition;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Keyboard;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Preformatted;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Sample;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Typewriter;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 HTML Variable;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 annotation subject;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 No List;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Outline List 1;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Outline List 2;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Outline List 3;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Balloon Text;\\lsdpriority39 \\lsdlocked0 Table Grid;\n\\lsdsemihidden1 \\lsdlocked0 Placeholder Text;\\lsdqformat1 \\lsdpriority1 \\lsdlocked0 No Spacing;\\lsdpriority60 \\lsdlocked0 Light Shading;\\lsdpriority61 \\lsdlocked0 Light List;\\lsdpriority62 \\lsdlocked0 Light Grid;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1;\\lsdpriority64 \\lsdlocked0 Medium Shading 2;\\lsdpriority65 \\lsdlocked0 Medium List 1;\\lsdpriority66 \\lsdlocked0 Medium List 2;\\lsdpriority67 \\lsdlocked0 Medium Grid 1;\\lsdpriority68 \\lsdlocked0 Medium Grid 2;\n\\lsdpriority69 \\lsdlocked0 Medium Grid 3;\\lsdpriority70 \\lsdlocked0 Dark List;\\lsdpriority71 \\lsdlocked0 Colorful Shading;\\lsdpriority72 \\lsdlocked0 Colorful List;\\lsdpriority73 \\lsdlocked0 Colorful Grid;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 1;\n\\lsdpriority61 \\lsdlocked0 Light List Accent 1;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 1;\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 1;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 1;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 1;\n\\lsdsemihidden1 \\lsdlocked0 Revision;\\lsdqformat1 \\lsdpriority34 \\lsdlocked0 List Paragraph;\\lsdqformat1 \\lsdpriority29 \\lsdlocked0 Quote;\\lsdqformat1 \\lsdpriority30 \\lsdlocked0 Intense Quote;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 1;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 1;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 1;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 1;\\lsdpriority70 \\lsdlocked0 Dark List Accent 1;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 1;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 1;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 1;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 2;\\lsdpriority61 \\lsdlocked0 Light List Accent 2;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 2;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 2;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 2;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 2;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 2;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 2;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 2;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 2;\\lsdpriority70 \\lsdlocked0 Dark List Accent 2;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 2;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 2;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 2;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 3;\\lsdpriority61 \\lsdlocked0 Light List Accent 3;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 3;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 3;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 3;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 3;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 3;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 3;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 3;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 3;\\lsdpriority70 \\lsdlocked0 Dark List Accent 3;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 3;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 3;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 3;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 4;\\lsdpriority61 \\lsdlocked0 Light List Accent 4;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 4;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 4;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 4;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 4;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 4;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 4;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 4;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 4;\\lsdpriority70 \\lsdlocked0 Dark List Accent 4;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 4;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 4;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 4;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 5;\\lsdpriority61 \\lsdlocked0 Light List Accent 5;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 5;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 5;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 5;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 5;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 5;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 5;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 5;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 5;\\lsdpriority70 \\lsdlocked0 Dark List Accent 5;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 5;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 5;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 5;\\lsdpriority60 \\lsdlocked0 Light Shading Accent 6;\\lsdpriority61 \\lsdlocked0 Light List Accent 6;\\lsdpriority62 \\lsdlocked0 Light Grid Accent 6;\n\\lsdpriority63 \\lsdlocked0 Medium Shading 1 Accent 6;\\lsdpriority64 \\lsdlocked0 Medium Shading 2 Accent 6;\\lsdpriority65 \\lsdlocked0 Medium List 1 Accent 6;\\lsdpriority66 \\lsdlocked0 Medium List 2 Accent 6;\n\\lsdpriority67 \\lsdlocked0 Medium Grid 1 Accent 6;\\lsdpriority68 \\lsdlocked0 Medium Grid 2 Accent 6;\\lsdpriority69 \\lsdlocked0 Medium Grid 3 Accent 6;\\lsdpriority70 \\lsdlocked0 Dark List Accent 6;\\lsdpriority71 \\lsdlocked0 Colorful Shading Accent 6;\n\\lsdpriority72 \\lsdlocked0 Colorful List Accent 6;\\lsdpriority73 \\lsdlocked0 Colorful Grid Accent 6;\\lsdqformat1 \\lsdpriority19 \\lsdlocked0 Subtle Emphasis;\\lsdqformat1 \\lsdpriority21 \\lsdlocked0 Intense Emphasis;\n\\lsdqformat1 \\lsdpriority31 \\lsdlocked0 Subtle Reference;\\lsdqformat1 \\lsdpriority32 \\lsdlocked0 Intense Reference;\\lsdqformat1 \\lsdpriority33 \\lsdlocked0 Book Title;\\lsdsemihidden1 \\lsdunhideused1 \\lsdpriority37 \\lsdlocked0 Bibliography;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdqformat1 \\lsdpriority39 \\lsdlocked0 TOC Heading;\\lsdpriority41 \\lsdlocked0 Plain Table 1;\\lsdpriority42 \\lsdlocked0 Plain Table 2;\\lsdpriority43 \\lsdlocked0 Plain Table 3;\\lsdpriority44 \\lsdlocked0 Plain Table 4;\n\\lsdpriority45 \\lsdlocked0 Plain Table 5;\\lsdpriority40 \\lsdlocked0 Grid Table Light;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light;\\lsdpriority47 \\lsdlocked0 Grid Table 2;\\lsdpriority48 \\lsdlocked0 Grid Table 3;\\lsdpriority49 \\lsdlocked0 Grid Table 4;\n\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful;\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 1;\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 1;\n\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 1;\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 1;\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 1;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 1;\n\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 1;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 2;\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 2;\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 2;\n\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 2;\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 2;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 2;\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 2;\n\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 3;\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 3;\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 3;\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 3;\n\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 3;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 3;\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 3;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 4;\n\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 4;\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 4;\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 4;\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 4;\n\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 4;\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 4;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 5;\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 5;\n\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 5;\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 5;\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 5;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 5;\n\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 5;\\lsdpriority46 \\lsdlocked0 Grid Table 1 Light Accent 6;\\lsdpriority47 \\lsdlocked0 Grid Table 2 Accent 6;\\lsdpriority48 \\lsdlocked0 Grid Table 3 Accent 6;\n\\lsdpriority49 \\lsdlocked0 Grid Table 4 Accent 6;\\lsdpriority50 \\lsdlocked0 Grid Table 5 Dark Accent 6;\\lsdpriority51 \\lsdlocked0 Grid Table 6 Colorful Accent 6;\\lsdpriority52 \\lsdlocked0 Grid Table 7 Colorful Accent 6;\n\\lsdpriority46 \\lsdlocked0 List Table 1 Light;\\lsdpriority47 \\lsdlocked0 List Table 2;\\lsdpriority48 \\lsdlocked0 List Table 3;\\lsdpriority49 \\lsdlocked0 List Table 4;\\lsdpriority50 \\lsdlocked0 List Table 5 Dark;\n\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful;\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 1;\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 1;\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 1;\n\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 1;\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 1;\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 1;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 1;\n\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 2;\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 2;\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 2;\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 2;\n\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 2;\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 2;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 2;\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 3;\n\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 3;\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 3;\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 3;\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 3;\n\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 3;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 3;\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 4;\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 4;\n\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 4;\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 4;\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 4;\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 4;\n\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 4;\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 5;\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 5;\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 5;\n\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 5;\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 5;\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 5;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 5;\n\\lsdpriority46 \\lsdlocked0 List Table 1 Light Accent 6;\\lsdpriority47 \\lsdlocked0 List Table 2 Accent 6;\\lsdpriority48 \\lsdlocked0 List Table 3 Accent 6;\\lsdpriority49 \\lsdlocked0 List Table 4 Accent 6;\n\\lsdpriority50 \\lsdlocked0 List Table 5 Dark Accent 6;\\lsdpriority51 \\lsdlocked0 List Table 6 Colorful Accent 6;\\lsdpriority52 \\lsdlocked0 List Table 7 Colorful Accent 6;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Mention;\n\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Smart Hyperlink;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Hashtag;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Unresolved Mention;\\lsdsemihidden1 \\lsdunhideused1 \\lsdlocked0 Smart Link;}}{\\*\\datastore 01050000\n02000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000\nd0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nfffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000d0bc\na2b6a406d601feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000\n00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000105000000000000}}"
  },
  {
    "path": "SetupBootstrapper/SetupBootstrapper.wixproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  WiX v3 MSBuild project. Requires WiX Toolset v3.x to be installed.\n  See https://wixtoolset.org/ to download WiX v3.\n  For a standalone build, use buildexe.ps1.\n  The MSI (Setup project) must be built before building this bundle.\n-->\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" ToolsVersion=\"4.0\">\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">x86</Platform>\n    <ProductVersion>3.10</ProductVersion>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>{CAD976C4-D0C6-4313-B605-EC3749A23B5F}</ProjectGuid>\n    <OutputType>Bundle</OutputType>\n    <OutputName>WorkloadTools</OutputName>\n    <WixTargetsPath Condition=\" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' \">$(MSBuildExtensionsPath32)\\Microsoft\\WiX\\v3.x\\Wix.targets</WixTargetsPath>\n    <WixTargetsPath Condition=\" '$(WixTargetsPath)' == '' \">$(MSBuildExtensionsPath)\\Microsoft\\WiX\\v3.x\\Wix.targets</WixTargetsPath>\n    <!-- Default version for local developer builds; buildexe.ps1 always passes BuildVersion -->\n    <BuildVersion Condition=\" '$(BuildVersion)' == '' \">0.0.0.0</BuildVersion>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x86' \">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <IntermediateOutputPath>obj\\x86\\Release\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x64' \">\n    <OutputPath>bin\\x64\\Release\\</OutputPath>\n    <IntermediateOutputPath>obj\\x64\\Release\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x86' \">\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <IntermediateOutputPath>obj\\x86\\Debug\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x64' \">\n    <OutputPath>bin\\x64\\Debug\\</OutputPath>\n    <IntermediateOutputPath>obj\\x64\\Debug\\</IntermediateOutputPath>\n    <DefineConstants>BuildVersion=$(BuildVersion);Platform=$(Platform)</DefineConstants>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Bundle.wxs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <WixExtension Include=\"WixBalExtension\" />\n    <WixExtension Include=\"WixUIExtension\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Folder Include=\"License\" />\n    <Folder Include=\"Redist\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"License\\Icon.png\" />\n    <Content Include=\"License\\License.rtf\" />\n    <Content Include=\"postbuild.bat\" />\n    <Content Include=\"Redist\\vcredist_x64.exe\" />\n    <Content Include=\"Redist\\vcredist_x86.exe\" />\n    <Content Include=\"SignMsi.ps1\" />\n  </ItemGroup>\n  <PropertyGroup>\n    <PostBuildEvent>call $(ProjectDir)postbuild.bat \"$(TargetPath)\" \"$(TargetDir)WorkloadTools_$(Platform)$(TargetExt)\"</PostBuildEvent>\n  </PropertyGroup>\n  <Import Project=\"$(WixTargetsPath)\" Condition=\"Exists('$(WixTargetsPath)')\" />\n  <Target Name=\"EnsureWixTargetsImported\" BeforeTargets=\"PrepareForBuild\" Condition=\" '$(WixTargetsImported)' == '' \">\n    <Warning Text=\"WiX Toolset v3 build targets were not found at '$(WixTargetsPath)'. Build skipped. Use buildexe.ps1 to build the bootstrapper.\" />\n  </Target>\n  <Target Name=\"Build\" Condition=\" '$(WixTargetsImported)' == '' \">\n    <Message Text=\"SetupBootstrapper project skipped: WiX Toolset v3 is not installed. Use buildexe.ps1 to build the bootstrapper.\" Importance=\"high\" />\n  </Target>\n</Project>"
  },
  {
    "path": "SetupBootstrapper/SignMsi.ps1",
    "content": "﻿[CmdletBinding()]\nParam(\n    [Parameter(Mandatory=$True,Position=1)]\n    [string]$InputFile,\n    [Parameter(Mandatory=$True,Position=2)]\n    [string]$OutputFile\n)\n\n\nif(-not (Test-Path $PSScriptRoot\\SignParams.ps1)) \n{\n    Write-Warning \"No code signing is applied to the .msi file.\"\n    Write-Warning \"You need to create a file called SignParams.ps1 and provide signing info.\"\n    \n    Write-Output \"Moving $InputFile --> $OutputFile\"\n    Move-Item $InputFile $OutputFile -Force\n\n    exit\n}\n\n# read paramters\n$signParams = get-content $PSScriptRoot\\SignParams.ps1 -Raw\nInvoke-Expression $signParams\n\n$params = $(\n     'sign'\n    ,'/fd'\n    ,'SHA1'\n    ,'/f'\n    ,('\"' + $certPath + '\"')\n    ,'/p'\n    ,('\"' + $certPass + '\"')\n    ,'/sha1'\n    ,$certSha\n    ,'/t'\n    ,('\"' + $certTime + '\"')\n    ,'/d'\n    ,'\"WorkloadTools\"'\n)\n\n$ParentPath = Split-Path -Path $InputFile\n\n& $insigniaPath $(\"-ib\",\"$InputFile\",\"-o\",\"$ParentPath\\engine.exe\")\n& $signTool ($params + \"$ParentPath\\engine.exe\")\n\n& $insigniaPath $(\"-ab\",\"$ParentPath\\engine.exe\",$InputFile,\"-o\",$InputFile)\n& $signTool ($params + $InputFile)\n\n\nWrite-Output \"Moving $InputFile --> $OutputFile\"\nMove-Item $InputFile $OutputFile -Force\n\nRemove-Item \"$ParentPath\\engine.exe\""
  },
  {
    "path": "SetupBootstrapper/buildexe.ps1",
    "content": "param (\n    [Parameter(Mandatory=$false)]\n    [string]$BuildVersion = \"1.0.0.0\",\n    [Parameter(Mandatory=$false)]\n    [string]$Platform = \"x64\"\n)\n\n# ---------------------------------------------------------------------------\n# Build the MSI first\n# ---------------------------------------------------------------------------\n. $PSScriptRoot\\..\\Setup\\buildmsi.ps1 -BuildVersion $BuildVersion -Platform $Platform\n\nSet-Location $PSScriptRoot\n\n# ---------------------------------------------------------------------------\n# Resolve the version from SharedAssemblyInfo.cs if the caller left the default\n# ---------------------------------------------------------------------------\nif ($BuildVersion -eq \"1.0.0.0\") {\n    $BuildVersion = (Get-Content ..\\SharedAssemblyInfo.cs |\n        Where-Object { $_.StartsWith(\"[assembly: AssemblyVersion(\") }).\n        Replace('[assembly: AssemblyVersion(\"','').Replace('\")]','')\n}\n\n# ---------------------------------------------------------------------------\n# Locate WiX v3 tools (candle.exe, light.exe)\n# The WIX environment variable is set automatically by the WiX v3 installer.\n# ---------------------------------------------------------------------------\n$wixDir = $null\nif ($env:WIX -and (Test-Path $env:WIX)) {\n    $wixDir = $env:WIX\n}\nif (-not $wixDir) {\n    $wixDir = @(\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.14\",\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.11\",\n        \"${env:ProgramFiles(x86)}\\WiX Toolset v3.10\",\n        \"${env:ProgramFiles}\\WiX Toolset v3.14\",\n        \"${env:ProgramFiles}\\WiX Toolset v3.11\"\n    ) | Where-Object { Test-Path $_ } | Select-Object -First 1\n}\nif (-not $wixDir) {\n    throw \"WiX Toolset v3 not found. Install from https://wixtoolset.org/ or set the WIX environment variable.\"\n}\n\n$candle = Join-Path $wixDir \"bin\\candle.exe\"\n$light  = Join-Path $wixDir \"bin\\light.exe\"\n\n# ---------------------------------------------------------------------------\n# Prepare output and intermediate directories\n# ---------------------------------------------------------------------------\n$outDir = \"$PSScriptRoot\\bin\\$Platform\\Release\"\n$objDir = \"$PSScriptRoot\\obj\\$Platform\\Release\"\n\nforeach ($dir in $outDir, $objDir) {\n    if (-not (Test-Path $dir)) {\n        New-Item -ItemType Directory -Path $dir -Force | Out-Null\n    } elseif (Test-Path \"$dir\\*\") {\n        Remove-Item \"$dir\\*\" -Recurse -Force\n    }\n}\n\n# ---------------------------------------------------------------------------\n# Compile Bundle.wxs with candle.exe\n# ---------------------------------------------------------------------------\n$arch = if ($Platform -eq 'x86') { 'x86' } else { 'x64' }\n\n& $candle `\n    \"$PSScriptRoot\\Bundle.wxs\" `\n    -arch $arch `\n    \"-dBuildVersion=$BuildVersion\" `\n    \"-dPlatform=$Platform\" `\n    -out \"$objDir\\\" `\n    -nologo -ext WixBalExtension\nif ($LASTEXITCODE -ne 0) { throw \"candle.exe failed for Bundle.wxs.\" }\n\n# ---------------------------------------------------------------------------\n# Link with light.exe to produce the bootstrapper EXE\n# ---------------------------------------------------------------------------\n$wixObjs = Get-ChildItem \"$objDir\\*.wixobj\" | Select-Object -ExpandProperty FullName\n\n& $light $wixObjs `\n    -out \"$outDir\\WorkloadTools.exe\" `\n    -nologo -ext WixBalExtension\nif ($LASTEXITCODE -ne 0) { throw \"light.exe failed for Bundle.\" }\n\n# ---------------------------------------------------------------------------\n# Sign (or just rename if no signing cert is configured)\n# ---------------------------------------------------------------------------\n. $PSScriptRoot\\SignMsi.ps1 `\n    -InputFile \"$outDir\\WorkloadTools.exe\" `\n    -OutputFile \"$outDir\\WorkloadTools_$Platform.exe\""
  },
  {
    "path": "SetupBootstrapper/postbuild.bat",
    "content": "powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File %~dp0\\SignMsi.ps1 -InputFile %1 -OutputFile %2\n\n"
  },
  {
    "path": "SharedAssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"WorkloadTools\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"spaghettidba\")]\n[assembly: AssemblyProduct(\"WorkloadTools\")]\n[assembly: AssemblyCopyright(\"Copyright © 2021 spaghettidba\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.7.4\")]\n[assembly: AssemblyFileVersion(\"1.7.4\")]\n"
  },
  {
    "path": "SqlWorkload/NLog.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<nlog xmlns=\"http://www.nlog-project.org/schemas/NLog.xsd\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd\"\n      autoReload=\"true\"\n      throwExceptions=\"false\"\n      internalLogLevel=\"Off\" \n      internalLogFile=\"c:\\temp\\nlog-internal.log\">\n\n  <targets>\n    <target name=\"logfile\" xsi:type=\"File\" fileName=\"SqlWorkload.log\" layout=\"${longdate} - ${level} - ${logger}${when:when='${event-properties:item=Worker}'=='':else=(${event-properties:item=Worker})} : ${message:withexception=true}\" />\n    <target name=\"warnfile\" xsi:type=\"File\" fileName=\"Warnings.log\" layout=\"${longdate} - ${logger}${when:when='${event-properties:item=Worker}'=='':else=(${event-properties:item=Worker})} - ${message:withexception=true}\" />\n    <target name=\"console\" xsi:type=\"ColoredConsole\" layout=\"${level} - ${logger}${when:when='${event-properties:item=Worker}'=='':else=(${event-properties:item=Worker})} : ${message:withexception=true}\"/>\n  </targets>\n\n  <rules>\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"logfile\" />\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"console\" />\n    <logger name=\"*\" levels=\"Warn\" writeTo=\"warnfile\" />\n  </rules>\n</nlog>\n"
  },
  {
    "path": "SqlWorkload/Program.cs",
    "content": "﻿using CommandLine;\nusing CommandLine.Text;\nusing NLog;\nusing NLog.Targets;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools;\nusing WorkloadTools.Config;\nusing WorkloadTools.Consumer;\nusing WorkloadTools.Listener;\nusing WorkloadTools.Listener.ExtendedEvents;\nusing WorkloadTools.Listener.Trace;\n\nnamespace SqlWorkload\n{\n    class Program\n    {\n\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n        private static CancellationTokenSource source;\n\n        static void Main(string[] args)\n        {\n            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(GenericErrorHandler);\n            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;\n\n            var assembly = System.Reflection.Assembly.GetExecutingAssembly();\n            var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);\n            var version = fvi.FileMajorPart.ToString() + \".\" + fvi.FileMinorPart.ToString() + \".\" + fvi.FileBuildPart.ToString();\n            var name = assembly.FullName;\n            logger.Info(name + \" \" + version);\n\n            try\n            {\n                var options = new Options();\n                if (!CommandLine.Parser.Default.ParseArguments(args, options))\n                {\n                    return;\n                }\n                Run(options);\n            }\n            catch(Exception e)\n            {\n                logger.Error(e);\n            }\n\n        }\n\n        static void Run(Options options)\n        {\n            // reconfigure loggers to use a file in the current directory\n            // or the file specified by the \"Log\" commandline parameter\n            if (LogManager.Configuration != null)\n            {\n                var target = (FileTarget)LogManager.Configuration.FindTargetByName(\"logfile\");\n                if (target != null)\n                {\n                    var pathToLog = options.LogFile;\n                    if (pathToLog == null)\n                    {\n                        pathToLog = Path.Combine(Environment.CurrentDirectory, \"SqlWorkload.log\");\n                    }\n                    if (!Path.IsPathRooted(pathToLog))\n                    {\n                        pathToLog = Path.Combine(Environment.CurrentDirectory, pathToLog);\n                    }\n                    target.FileName = pathToLog;\n\n                    if(options.LogLevel != null)\n                    {\n                        foreach(var rule in LogManager.Configuration.LoggingRules)\n                        {\n                            foreach (var level in LogLevel.AllLoggingLevels)\n                            {\n                                rule.DisableLoggingForLevel(level);\n                            }\n                            rule.EnableLoggingForLevels(LogLevel.FromString(options.LogLevel),LogLevel.Fatal);\n                        }\n                    }\n\n                    LogManager.ReconfigExistingLoggers();\n                }\n            }\n\n            options.ConfigurationFile = System.IO.Path.GetFullPath(options.ConfigurationFile);\n            logger.Info(String.Format(\"Reading configuration from '{0}'\", options.ConfigurationFile));\n\n            if (!File.Exists(options.ConfigurationFile))\n            {\n                logger.Error(\"File not found!\");\n                Console.WriteLine(options.GetUsage());\n                return;\n            }\n\n            var config = SqlWorkloadConfig.LoadFromFile(options.ConfigurationFile);\n            config.Controller.Listener.Source = System.IO.Path.GetFullPath(config.Controller.Listener.Source);\n\n            Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) {\n                e.Cancel = true;\n                logger.Info(\"Received shutdown signal...\");\n                source.CancelAfter(TimeSpan.FromSeconds(100)); // give a 100 seconds cancellation grace period \n                config.Controller.Stop();\n            };\n\n            var t = processController(config.Controller);\n            t.Wait();\n            logger.Info(\"Controller stopped.\");\n            config.Controller.Dispose();\n            logger.Info(\"Controller disposed.\");\n        }\n\n\n\n        static void GenericErrorHandler(object sender, UnhandledExceptionEventArgs e)\n        {\n            try\n            {\n                logger.Error(e.ToString());\n            }\n            finally\n            {\n                Console.WriteLine(\"Caught unhandled exception...\");\n            }\n        }\n\n\n        public static async Task processController(WorkloadController controller)\n        {\n            source = new CancellationTokenSource();\n            source.Token.Register(CancelNotification);\n            var completionSource = new TaskCompletionSource<object>();\n            source.Token.Register(() => completionSource.TrySetCanceled());\n            var task = Task.Factory.StartNew(() => controller.Run(), source.Token);\n            await Task.WhenAny(task, completionSource.Task);\n        }\n\n        public static void CancelNotification()\n        {\n            logger.Info(\"Shutdown complete.\");\n        }\n\n\n    }\n\n\n\n\n\n    class Options\n    {\n        [Option('F', \"File\", DefaultValue = \"SqlWorkload.json\", HelpText = \"Configuration file\")]\n        public string ConfigurationFile { get; set; }\n\n        [Option('L', \"Log\", HelpText = \"Log file\")]\n        public string LogFile { get; set; }\n\n        [Option('E', \"LogLevel\", HelpText = \"Log level\")]\n        public string LogLevel { get; set; }\n\n        [ParserState]\n        public IParserState LastParserState { get; set; }\n\n        [HelpOption]\n        public string GetUsage()\n        {\n            return HelpText.AutoBuild(this,\n              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));\n        }\n    \n    }\n\n}\n"
  },
  {
    "path": "SqlWorkload/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: Guid(\"fb46ad2c-df81-4d35-b419-d93e5ef9d98a\")]\n\n"
  },
  {
    "path": "SqlWorkload/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SqlWorkload.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    public class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"SqlWorkload.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to true.\n        /// </summary>\n        public static string TRUNCATE_TO_4000 {\n            get {\n                return ResourceManager.GetString(\"TRUNCATE_TO_4000\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SqlWorkload/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"TRUNCATE_TO_4000\" xml:space=\"preserve\">\n    <value>true</value>\n  </data>\n</root>"
  },
  {
    "path": "SqlWorkload/SqlWorkload.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>SqlWorkload</RootNamespace>\n    <AssemblyName>SqlWorkload</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\x64\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\x64\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\CommandLineParser.1.9.71\\lib\\net45\\CommandLine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"DouglasCrockford.JsMin, Version=1.1.3.0, Culture=neutral, PublicKeyToken=99147aa1108448b7, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\DouglasCrockford.JsMin.1.1.3\\lib\\net40-client\\DouglasCrockford.JsMin.dll</HintPath>\n    </Reference>\n    <Reference Include=\"NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\NLog.4.7.15\\lib\\net45\\NLog.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Configuration\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Data.SQLite, Version=1.0.112.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Data.SQLite.Core.1.0.112.0\\lib\\net46\\System.Data.SQLite.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.ServiceModel\" />\n    <Reference Include=\"System.Transactions\" />\n    <Reference Include=\"System.Web.Extensions\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"..\\SharedAssemblyInfo.cs\">\n      <Link>Properties\\SharedAssemblyInfo.cs</Link>\n    </Compile>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"app.config\" />\n    <None Include=\"NLog.config\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\WorkloadTools\\WorkloadTools.csproj\">\n      <Project>{ae6e4548-8c33-4728-8504-88aa9666020b}</Project>\n      <Name>WorkloadTools</Name>\n      <Private>True</Private>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets\" Condition=\"Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets'))\" />\n  </Target>\n</Project>"
  },
  {
    "path": "SqlWorkload/app.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <startup>\n    <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/>\n  </startup>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"Microsoft.SqlServer.XE.Core\" publicKeyToken=\"89845dcd8080cc91\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-14.100.0.0\" newVersion=\"14.100.0.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n    <gcConcurrent enabled=\"false\"/>\n  </runtime>\n</configuration>\n"
  },
  {
    "path": "SqlWorkload/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"CommandLineParser\" version=\"1.9.71\" targetFramework=\"net451\" />\n  <package id=\"DouglasCrockford.JsMin\" version=\"1.1.3\" targetFramework=\"net451\" />\n  <package id=\"NLog\" version=\"4.7.15\" targetFramework=\"net48\" />\n  <package id=\"System.Data.SQLite.Core\" version=\"1.0.112.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "WorkloadTools/BinarySerializedBufferedEventQueue.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Runtime.Serialization.Formatters.Binary;\nusing WorkloadTools.Util;\nusing System.Diagnostics;\n\nnamespace WorkloadTools\n{\n    public class BinarySerializedBufferedEventQueue : BufferedEventQueue\n    {\n        private readonly string baseFolder;\n\n        private int _minFile, _maxFile;\n\n        private readonly string file_name_uniquifier = \"\";\n\n        private readonly BinaryFormatter _formatter = new BinaryFormatter();\n\n        public BinarySerializedBufferedEventQueue() : base()\n        {\n            file_name_uniquifier = DateTime.Now.ToString(\"yyyyMMddHHmm\") + \"_\" + (\"000000000\" + (Environment.TickCount & int.MaxValue)).Right(9) + \"_\";\n            baseFolder = Path.Combine(Path.Combine(System.IO.Path.GetTempPath(), \"WorkloadTools\"), \"SerializedEventQueue\");\n            _ = System.IO.Directory.CreateDirectory(baseFolder);\n            _minFile = 0;\n            _maxFile = 0;\n\n        }\n\n        protected override WorkloadEvent[] ReadEvents(int count)\n        {\n            WorkloadEvent[] result = null;\n            var destFile = Path.Combine(baseFolder, file_name_uniquifier + (\"000000000\" + _minFile).Right(9) + \".cache\");\n            \n            using (var fileStream = new System.IO.FileStream(destFile, System.IO.FileMode.Open))\n            using (var bufferedStream = new BufferedStream(fileStream))\n            {\n                result = (WorkloadEvent[])_formatter.Deserialize(bufferedStream);\n                if(result.Length != count)\n                {\n                    throw new ArgumentOutOfRangeException($\"The deserialized array is of the wrong size (expected: {count}, found: {result.Length})\");\n                }\n            }\n\n            File.Delete(destFile);\n            _minFile++;\n\n            return result;\n        }\n\n        protected override void WriteEvents(WorkloadEvent[] events)\n        {\n            var destFile = Path.Combine(baseFolder, file_name_uniquifier);\n            // c# does not have a String.Right method, so I created\n            // an extension for it. Crazy, right?\n            destFile += (\"000000000\" + _maxFile).Right(9) + \".cache\";\n\n            if (File.Exists(destFile))\n            {\n                File.Delete(destFile);\n            }\n\n            using (var fileStream = new FileStream(destFile, FileMode.CreateNew))\n            using (var bufferedStream = new BufferedStream(fileStream))\n            {\n                _formatter.Serialize(bufferedStream, events);\n                fileStream.Close();\n            }\n            _maxFile++;\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            // delete all pending files\n            for (var i=_minFile; i<=_maxFile; i++)\n            {\n                var destFile = Path.Combine(baseFolder, file_name_uniquifier);\n                destFile += (\"000000000\" + i).Right(9) + \".cache\";\n                if (File.Exists(destFile))\n                {\n                    File.Delete(destFile);\n                }\n\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/BufferedEventQueue.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools\n{\n    public abstract class BufferedEventQueue : IEventQueue \n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private int _bufferSize;\n\n        public int BufferSize\n        {\n            get => _bufferSize;\n            set { _bufferSize = value; initialize(); }\n        }\n\n        protected object syncRoot = new object();\n\n        private WorkloadEvent[] _array;\n        private WorkloadEvent[] _overflowArray;\n        private int _head;               // First valid element in the queue\n        private int _tail;               // Last valid element in the queue\n        private int _size;               // Number of elements in the array\n        private int _totOverflowSize;    // Total number of elements in the overflow array AND disk\n        private int _overflowSize;       // Current number of elements in the overflow array\n        private int _overflowBufferSize;\n\n        public virtual int Count\n        {\n            get\n            {\n                lock (syncRoot)\n                {\n                    return _size + _totOverflowSize;\n                }\n            }\n        }\n\n        public BufferedEventQueue()\n        {\n            initialize();\n        }\n\n        private void initialize()\n        {\n            _array = new WorkloadEvent[BufferSize];\n            _overflowArray = null;\n            _overflowBufferSize = BufferSize / 2;\n        }\n\n        protected abstract void WriteEvents(WorkloadEvent[] events);\n\n        protected abstract WorkloadEvent[] ReadEvents(int count);\n\n        public virtual void Enqueue(WorkloadEvent evt)\n        {\n            lock (syncRoot)\n            {\n                if (_overflowArray != null)\n                {\n                    // write to the overflow array\n                    _overflowArray[_overflowSize] = evt;\n                    _overflowSize++;\n                    _totOverflowSize++;\n                    if(_overflowSize == _overflowBufferSize)\n                    {\n                        // decide what to do with the overflow:\n                        // if we have enough room in the base array\n                        // AND no events persisted to disk, then\n                        // write events back to the queue\n                        // if the base array does not have enough room, \n                        // write the overflow array to the database\n                        // and allocate a new overflow array\n\n                        if((_size <= BufferSize - _overflowBufferSize) && (_totOverflowSize - _overflowSize <= 0))\n                        {\n                            EnqueueAll(_overflowArray);\n                            _overflowSize = 0;\n                            _overflowArray = null;\n                        }\n                        else\n                        {\n                            WriteEvents(_overflowArray);\n                            _overflowSize = 0;\n                            _overflowArray = new WorkloadEvent[_overflowBufferSize];\n                        }\n                    }\n                }\n                else\n                {\n                    _array[_tail] = evt;\n                    _tail = (_tail + 1) % _array.Length;\n                    _size++;\n\n                    // the internal array is at capacity: allocate an overflow array\n                    // with size = 50% of BufferSize\n                    if (_size == _array.Length)\n                    {\n                        _overflowArray = new WorkloadEvent[_overflowBufferSize];\n                    }\n                }\n            }\n        }\n\n        private void EnqueueAll(WorkloadEvent[] source)\n        {\n            EnqueueAll(source, source.Length);\n        }\n\n        private void EnqueueAll(WorkloadEvent[] source, int count)\n        {\n            if(count > source.Length)\n            {\n                throw new ArgumentOutOfRangeException($\"The 'count' argument ({count}) is greater than the length of the array to enqueue ({source.Length}).\");\n            }\n\n            if (_head < _tail)\n            {\n                var numFirst = _array.Length - _tail;\n                if (numFirst > count)\n                {\n                    numFirst = count;\n                }\n\n                Array.Copy(source, 0, _array, _tail, numFirst);\n                _tail = (_tail + numFirst) % _array.Length;\n                if (numFirst < count)\n                {\n                    var numSecond = count - numFirst;\n                    Array.Copy(source, numFirst, _array, _tail, numSecond);\n                    _tail = (_tail + numSecond) % _array.Length;\n                }\n            }\n            else\n            {\n                Array.Copy(source, 0, _array, _tail, count);\n                _tail = (_tail + count) % _array.Length;\n            }\n            _size += count;\n            _totOverflowSize -= count;\n        }\n\n        public virtual bool TryDequeue(out WorkloadEvent result)\n        {\n            result = null;\n            try\n            {\n                lock (syncRoot)\n                {\n                    if (Count == 0)\n                    {\n                        return false;\n                    }\n\n                    result = _array[_head];\n                    _array[_head] = null;\n                    _head = (_head + 1) % _array.Length;\n                    _size--;\n\n                    if(_totOverflowSize > 0)\n                    {\n\n                        // if we have space available and overflowed \n                        // events on disk, then we read them back \n                        if (_totOverflowSize - _overflowSize > 0) \n                        {\n                            if (_size == _array.Length - _overflowBufferSize)\n                            {\n                                EnqueueAll(ReadEvents(_overflowBufferSize));\n                            }\n                        }\n                        else\n                        {\n                            // if we have events in the overflow array (but not on disk)\n                            // and enough space in the queue (at least 75% free), put them back\n                            if (_overflowSize > 0 && _size <= _array.Length - (_overflowBufferSize + (_overflowBufferSize / 2)))\n                            {\n                                EnqueueAll(_overflowArray, _overflowSize);\n                                _overflowSize = 0;\n                                _overflowArray = null;\n                                _totOverflowSize = 0;\n                            }\n                        }\n\n                    }\n\n                }\n                return true;\n            }\n            catch (Exception e)\n            {\n                logger.Warn(e, \"Unable to dequeue\");\n                result = null;\n                return false;\n            }\n        }\n\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected abstract void Dispose(bool disposing);\n\n        public bool HasMoreElements()\n        {\n            return Count > 0;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Config/AnalysisSample.json",
    "content": "﻿{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"ExtendedEventsWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"testServer\",\n                \"UserName\": \"sa\",\n                \"Password\": \"P4$$w0rd!\"\n            },\n            \"DatabaseFilter\": \"MyDB\"\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"testServer\",\n                    \"DatabaseName\": \"SqlWorkload\",\n                    \"SchemaName\": \"replay\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "WorkloadTools/Config/ReplaySample.json",
    "content": "﻿{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"ExtendedEventsWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"ProductionServer\",\n                \"UserName\": \"sa\",\n                \"Password\": \"P4$$w0rd!\"\n            },\n            \"DatabaseFilter\": \"MyDB\"\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"ReplayConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"testServer\",\n                    \"DatabaseName\": \"MyDB\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                }\n            },\n            {\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"testServer\",\n                    \"DatabaseName\": \"SqlWorkload\",\n                    \"SchemaName\": \"baseline\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "WorkloadTools/Config/Sample.json",
    "content": "﻿{\n    \"Controller\": {\n\n        \"Listener\":\n        {\n            \"__type\": \"ExtendedEventsWorkloadListener\",\n            \"ConnectionInfo\":\n            {\n                \"ServerName\": \"SQLDEMO\\\\SQL2014\",\n                \"UserName\": \"sa\",\n                \"Password\": \"P4$$w0rd!\"\n            },\n            \"DatabaseFilter\": \"DS3\"\n        },\n\n        \"Consumers\":\n        [\n            {\n                \"__type\": \"ReplayConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"SQLDEMO\\\\SQL2016\",\n                    \"DatabaseName\": \"DS3\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                }\n            },\n            {\n                \"__type\": \"AnalysisConsumer\",\n                \"ConnectionInfo\": \n                {\n                    \"ServerName\": \"SQLDEMO\\\\SQL2016\",\n                    \"DatabaseName\": \"DS3\",\n                    \"SchemaName\": \"baseline\",\n                    \"UserName\": \"sa\",\n                    \"Password\": \"P4$$w0rd!\"\n                },\n                \"UploadIntervalSeconds\": 60\n            }\n        ]\n    }\n}"
  },
  {
    "path": "WorkloadTools/Config/SqlWorkloadConfig.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing WorkloadTools;\nusing System.Web.Script.Serialization;\nusing System.IO;\nusing DouglasCrockford.JsMin;\nusing WorkloadTools.Listener.ExtendedEvents;\nusing WorkloadTools.Consumer.Replay;\nusing WorkloadTools.Consumer.Analysis;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Config\n{\n    public class SqlWorkloadConfig\n    {\n        public SqlWorkloadConfig()\n        {\n        }\n\n        public WorkloadController Controller { get; set; }\n\n        public static SqlWorkloadConfig LoadFromFile(string path)\n        {\n            var ser = new JavaScriptSerializer(new SqlWorkloadConfigTypeResolver());\n            ser.RegisterConverters(new JavaScriptConverter[] { new ModelConverter() });\n            using (var r = new StreamReader(path))\n            {\n                var json = r.ReadToEnd();\n                var minifier = new JsMinifier();\n                // minify JSON to strip away comments\n                // Comments in config files are very useful but JSON parsers\n                // do not allow comments. Minification solves the issue.\n                SqlWorkloadConfig result = null;\n                string jsonMin = null;\n                try\n                {\n                    jsonMin = minifier.Minify(json);\n                }\n                catch (Exception e)\n                {\n                    throw new FormatException($\"Unable to load configuration from '{path}'. The file contains syntax errors.\", e);\n                }\n\n                try\n                {\n                    result = ser.Deserialize<SqlWorkloadConfig>(jsonMin);\n                }\n                catch (Exception e)\n                {\n                    throw new FormatException($\"Unable to load configuration from '{path}'. The file contains semantic errors.\", e);\n                }\n                return result;\n            }\n        }\n\n        public static void Test()\n        {\n            var ser = new JavaScriptSerializer(new SqlWorkloadConfigTypeResolver());\n            var x = new SqlWorkloadConfig()\n            {\n                Controller = new WorkloadController()\n            };\n            x.Controller.Listener = new ExtendedEventsWorkloadListener()\n            {\n                Source = \"Listener\\\\ExtendedEvents\\\\sqlworkload.sql\",\n                ConnectionInfo = new SqlConnectionInfo()\n                {\n                    ServerName = \"SQLDEMO\\\\SQL2014\",\n                    UserName = \"sa\",\n                    Password = \"P4$$w0rd!\"\n                }\n            };\n            //x.Controller.Listener.Filter.DatabaseFilter.PredicateValue = \"DS3\";\n\n            x.Controller.Consumers.Add(new ReplayConsumer()\n            {\n                ConnectionInfo = new SqlConnectionInfo()\n                {\n                    ServerName = \"SQLDEMO\\\\SQL2016\",\n                    UserName = \"sa\",\n                    Password = \"P4$$w0rd!\"\n                }\n            });\n\n            x.Controller.Consumers.Add(new ReplayConsumer()\n            {\n                ConnectionInfo = new SqlConnectionInfo()\n                {\n                    ServerName = \"SQLDEMO\\\\SQL2016\",\n                    UserName = \"sa\",\n                    Password = \"P4$$w0rd!\",\n                    DatabaseName = \"RTR\",\n                    SchemaName = \"baseline\"\n                },\n                DatabaseMap = new Dictionary<string, string>()\n                {\n                    { \"DatabaseA\", \"DatabaseB\" },\n                    { \"DatabaseC\", \"DatabaseD\" }\n                }\n            });\n\n            var s = ser.Serialize(x);\n\n            Console.WriteLine(s);\n\n            //SqlWorkloadConfig tc = ser.Deserialize<SqlWorkloadConfig>(Samples.Sample.ToString());\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Config/SqlWorkloadConfigTypeResolver.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Web.Script.Serialization;\n\nnamespace WorkloadTools.Config\n{\n    internal class SqlWorkloadConfigTypeResolver : SimpleTypeResolver\n    {\n\n        private static readonly Dictionary<string, Type> mappedTypes = new Dictionary<string, Type>();\n\n        static SqlWorkloadConfigTypeResolver()\n        {\n            var currentAssembly = Assembly.GetExecutingAssembly();\n            var nameSpace = \"WorkloadTools\";\n            var types = currentAssembly.GetTypes().Where(t => t != null && t.FullName.StartsWith(nameSpace) & !t.FullName.Contains(\"+\")).ToArray();\n            foreach (var t in types)\n            {\n                try\n                {\n                    mappedTypes.Add(t.AssemblyQualifiedName, t);\n                    mappedTypes.Add(t.Name, t);\n                }\n                catch(Exception)\n                {\n                    throw;\n                }\n            }\n        }\n\n        public override Type ResolveType(string id)\n        {\n            if (mappedTypes.ContainsKey(id))\n            {\n                return mappedTypes[id];\n            }\n            else\n            {\n                return base.ResolveType(id);\n            }\n        }\n\n        public override string ResolveTypeId(Type type)\n        {\n            return base.ResolveTypeId(type);\n        }\n    }\n}"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/AnalysisConsumer.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing WorkloadTools.Consumer.Analysis;\n\nnamespace WorkloadTools.Consumer.Analysis\n{\n    public class AnalysisConsumer : BufferedWorkloadConsumer\n    {\n        private WorkloadAnalyzer analyzer;\n\n        private int _uploadIntervalSeconds;\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n        public int UploadIntervalSeconds\n        {\n            get => _uploadIntervalSeconds;\n            set\n            {\n                if (value % 60 != 0)\n                {\n                    throw new ArgumentOutOfRangeException(\"UploadIntervalSeconds must be an exact multiple of 60\");\n                }\n                _uploadIntervalSeconds = value;\n            }\n        }\n\n        public int UploadIntervalMinutes\n        {\n            get => _uploadIntervalSeconds / 60;\n            set => _uploadIntervalSeconds = value * 60;\n        }\n\n        public int MaximumWriteRetries { get; set; } = 5;\n\n\t\tpublic bool SqlNormalizerTruncateTo4000 { get; set; }\n\t\tpublic bool SqlNormalizerTruncateTo1024 { get; set; }\n\n        public bool WriteDetail { get; set; } = true;\n        public bool WriteSummary { get; set; } = true;\n\n        public override void ConsumeBuffered(WorkloadEvent evt)\n        {\n            if(analyzer == null)\n            {\n                analyzer = new WorkloadAnalyzer()\n                {\n                    Interval = UploadIntervalSeconds / 60,\n                    ConnectionInfo = ConnectionInfo,\n\t\t\t\t\tMaximumWriteRetries = MaximumWriteRetries,\n\t\t\t\t\tTruncateTo1024 = SqlNormalizerTruncateTo1024,\n\t\t\t\t\tTruncateTo4000 = SqlNormalizerTruncateTo4000,\n                    WriteDetail = WriteDetail\n\t\t\t\t};\n            }\n\n            analyzer.Add(evt);\n        }\n\n        public override bool HasMoreEvents()\n        {\n            return analyzer.HasEventsQueued || !Buffer.IsEmpty;\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            if (analyzer != null)\n            {\n                analyzer.Stop();\n                analyzer.Dispose();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/DatabaseSchema.sql",
    "content": "﻿IF SCHEMA_ID('{SchemaName}') IS NULL\n    EXEC('CREATE SCHEMA [{SchemaName}]');\n\nIF OBJECT_ID('{SchemaName}.WorkloadDetails') IS NULL\n\nCREATE TABLE [{SchemaName}].[WorkloadDetails](\n\t[interval_id] [int] NOT NULL,\n\n\t[sql_hash] [bigint] NOT NULL,\n\t[application_id] [int] NOT NULL,\n\t[database_id] [int] NOT NULL,\n\t[host_id] [int] NOT NULL,\n\t[login_id] [int] NOT NULL,\n\n\t[avg_cpu_us] [bigint] NULL,\n    [min_cpu_us] [bigint] NULL,\n    [max_cpu_us] [bigint] NULL,\n    [sum_cpu_us] [bigint] NULL,\n\n\t[avg_reads] [bigint] NULL,\n    [min_reads] [bigint] NULL,\n    [max_reads] [bigint] NULL,\n    [sum_reads] [bigint] NULL,\n\n\t[avg_writes] [bigint] NULL,\n    [min_writes] [bigint] NULL,\n    [max_writes] [bigint] NULL,\n    [sum_writes] [bigint] NULL,\n\n\t[avg_duration_us] [bigint] NULL,\n    [min_duration_us] [bigint] NULL,\n    [max_duration_us] [bigint] NULL,\n    [sum_duration_us] [bigint] NULL,\n\n    [execution_count] [bigint] NULL,\n\n    CONSTRAINT PK_WorkloadDetails PRIMARY KEY CLUSTERED (\n        [interval_id], \n        [sql_hash], \n        [application_id], \n        [database_id], \n        [host_id], \n        [login_id]\n    )\n)\n\n\n\nIF OBJECT_ID('{SchemaName}.WorkloadSummary') IS NULL\n\nCREATE TABLE [{SchemaName}].[WorkloadSummary](\n\t[application_id] [int] NOT NULL,\n\t[database_id] [int] NOT NULL,\n\t[host_id] [int] NOT NULL,\n\t[login_id] [int] NOT NULL,\n\n    [min_cpu_us] [bigint] NULL,\n    [max_cpu_us] [bigint] NULL,\n    [sum_cpu_us] [bigint] NULL,\n\n    [min_reads] [bigint] NULL,\n    [max_reads] [bigint] NULL,\n    [sum_reads] [bigint] NULL,\n\n    [min_writes] [bigint] NULL,\n    [max_writes] [bigint] NULL,\n    [sum_writes] [bigint] NULL,\n\n    [min_duration_us] [bigint] NULL,\n    [max_duration_us] [bigint] NULL,\n    [sum_duration_us] [bigint] NULL,\n\n    [min_execution_date] datetime,\n    [max_execution_date] datetime,\n\n    [execution_count] [bigint] NULL,\n\n    CONSTRAINT PK_WorkloadSummary PRIMARY KEY CLUSTERED (\n        [application_id], \n        [database_id], \n        [host_id], \n        [login_id]\n    )\n)\n\n\n\nIF OBJECT_ID('{SchemaName}.Applications') IS NULL\n\nCREATE TABLE [{SchemaName}].[Applications](\n\t[application_id] [int] NOT NULL PRIMARY KEY,\n\t[application_name] [nvarchar](128) NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.Databases') IS NULL\n\nCREATE TABLE [{SchemaName}].[Databases](\n\t[database_id] [int] NOT NULL PRIMARY KEY,\n\t[database_name] [nvarchar](128) NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.Hosts') IS NULL\n\nCREATE TABLE [{SchemaName}].[Hosts](\n\t[host_id] [int] NOT NULL PRIMARY KEY,\n\t[host_name] [nvarchar](128) NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.Logins') IS NULL\n\nCREATE TABLE [{SchemaName}].[Logins](\n\t[login_id] [int] NOT NULL PRIMARY KEY,\n\t[login_name] [nvarchar](128) NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.Intervals') IS NULL\n\nCREATE TABLE [{SchemaName}].[Intervals] (\n\t[interval_id] [int] NOT NULL PRIMARY KEY,\n\t[end_time] [datetime] NOT NULL,\n\t[duration_minutes] [int] NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.NormalizedQueries') IS NULL\n\nCREATE TABLE [{SchemaName}].[NormalizedQueries](\n\t[sql_hash] [bigint] NOT NULL PRIMARY KEY,\n\t[normalized_text] [nvarchar](max) NOT NULL,\n    [example_text] [nvarchar](max) NULL\n)\n\nIF OBJECT_ID('{SchemaName}.PerformanceCounters') IS NULL\n\nCREATE TABLE [{SchemaName}].[PerformanceCounters](\n\t[interval_id] [int] NOT NULL,\n    [counter_name] [varchar](255) NOT NULL,\n    [min_counter_value] [float] NOT NULL,\n    [max_counter_value] [float] NOT NULL,\n    [avg_counter_value] [float] NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.WaitStats') IS NULL\n\nCREATE TABLE [{SchemaName}].[WaitStats](\n\t[interval_id] [int] NOT NULL,\n    [wait_type] [varchar](255) NOT NULL,\n    [wait_sec] [float] NOT NULL,\n    [resource_sec] [float] NOT NULL,\n    [signal_sec] [float] NOT NULL,\n    [wait_count] [bigint] NOT NULL\n)\n\nIF OBJECT_ID('{SchemaName}.DiskPerf') IS NULL\n\nCREATE TABLE [{SchemaName}].[DiskPerf] (\n    [interval_id] [int] NOT NULL,\n    [database_name] nvarchar(128) NULL,\n    [physical_filename] nvarchar(128) NULL,\n    [logical_filename] nvarchar(128) NULL,\n    [file_type] nvarchar(128) NULL,\n    [volume_mount_point] nvarchar(128) NULL,\n    [read_latency_ms] int NULL,\n    [reads] int NULL,\n    [read_bytes] int NULL,\n    [write_latency_ms] int NULL,\n    [writes] int NULL,\n    [write_bytes] int NULL,\n    [cum_read_latency_ms] bigint NULL,\n    [cum_reads] bigint NULL,\n    [cum_read_bytes] bigint NULL,\n    [cum_write_latency_ms] bigint NULL,\n    [cum_writes] bigint NULL,\n    [cum_write_bytes] bigint NULL\n);\n\n\nIF OBJECT_ID('{SchemaName}.Errors') IS NULL\n\nCREATE TABLE [{SchemaName}].[Errors](\n\t[interval_id] [int] NOT NULL,\n\t[error_type] [nvarchar](30) NOT NULL,\n\t[message] [nvarchar](max) NULL,\n\t[error_count] int NULL\n)\n"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/NormalizedSqlText.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Consumer.Analysis\n{\n    public class NormalizedSqlText\n    {\n        public enum CommandTypeEnum\n        {\n            SP_EXECUTE,\n            SP_PREPARE,\n            SP_UNPREPARE,\n            SP_CURSOR,\n            OTHER,\n            SP_RESET_CONNECTION,\n            SP_RESET_CONNECTION_NONPOOLED\n        }\n\n        public NormalizedSqlText()\n        {\n            CommandType = CommandTypeEnum.OTHER;\n        }\n\n        public NormalizedSqlText(string command) : this()\n        {\n            NormalizedText = command;\n            OriginalText = command;\n            Statement = command;\n            Handle = 0;\n        }\n\n        public string OriginalText { get; set; }\n        public string Statement { get; set; }\n        public string NormalizedText { get; set; }\n        public int Handle { get; set; }\n        public CommandTypeEnum CommandType { get; set; }\n        internal int ReferenceCount { get; set; }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/SqlTextNormalizer.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing System.Threading;\nusing WorkloadTools.Properties;\n\nnamespace WorkloadTools.Consumer.Analysis\n{\n    public class SqlTextNormalizer\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private static readonly Hashtable prepSql = new Hashtable();\n\n        private static readonly ConcurrentDictionary<long, NormalizedSqlText> cachedQueries = new ConcurrentDictionary<long, NormalizedSqlText>();\n\n        private static readonly Regex _doubleApostrophe = new Regex(\"('')(?<string>.*?)('')\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.CultureInvariant);\n        private static readonly Regex _delimiterStart = new Regex(\"(--)|(/\\\\*)|'\", RegexOptions.Compiled);\n        private static readonly Regex _spreadCsv = new Regex(\",(?=\\\\S)\", RegexOptions.Compiled);\n        private static readonly Regex _spaces = new Regex(\"\\\\s+\", RegexOptions.Compiled);\n        private static readonly Regex _inlineComment = new Regex(\"--.*$\", RegexOptions.Multiline | RegexOptions.Compiled);\n        private static readonly Regex _prepareSql = new Regex(\"EXEC\\\\s+(?<preptype>SP_PREP(ARE|EXEC))\\\\s+@P1\\\\s+OUTPUT,\\\\s*(NULL|(N\\\\'.+?\\\\')),\\\\s*N(?<remaining>.+)$\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _prepExecRpc = new Regex(\"SET\\\\s+@P1=(?<stmtnum>\\\\d+)\\\\s+EXEC\\\\s+SP_PREPEXECRPC\\\\s+@P1\\\\s+OUTPUT,\\\\s*N\\\\'(?<statement>.+?)'\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _preppedSqlStatement = new Regex(\"^(')(?<statement>((?!\\\\1).|\\\\1{2})*)\\\\1\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _execPrepped = new Regex(\"^EXEC\\\\s+SP_EXECUTE\\\\s+(?<stmtnum>\\\\d+)\", RegexOptions.Compiled);\n        private static readonly Regex _execUnprep = new Regex(\"EXEC\\\\s+SP_UNPREPARE\\\\s+(?<stmtnum>\\\\d+)\", RegexOptions.Compiled);\n        private static readonly Regex _cursor = new Regex(\"EXEC\\\\s+SP_CURSOROPEN\\\\s+(@CURSOR\\\\s*=\\\\s*)?\\\\@P1\\\\s+OUTPUT\\\\,\\\\s*(@STMT\\\\s*=\\\\s*)?(N)?(?<tick>')  (?<statement>      ((  (?!\\\\k<tick>)  .|\\\\k<tick>{2})*)   )    \\\\k<tick>\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);\n        private static readonly Regex _cursorPrepExec = new Regex(\"EXEC\\r\\n\\\\s+     # any spaces\\r\\nsp_cursorprepexec\\r\\n.+       # any characters up to the string\\r\\nN  \\r\\n(?<tick>')   # matches an apostraphe\\r\\n(?!@)    # but no @ following\\r\\n(?<statement>   ((  (?!\\\\k<tick>)  .|\\\\k<tick>{2})*)   )    # all the characters ...\\r\\n\\\\k<tick>   # until the next tick that isn't doubled.\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);\n        private static readonly Regex _spExecuteSql = new Regex(\"EXEC\\\\s+SP_EXECUTESQL\\\\s+N\\\\'(?<statement>.+?)\\\\'\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _spExecuteSqlWithStatement = new Regex(\"EXEC\\\\s+SP_EXECUTESQL\\\\s+@statement\\\\s*=\\\\s*N\\\\'(?<statement>.+?)\\\\'\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _objectName = new Regex(\"EXEC(UTE){0,1}\\\\s(?<schema>(\\\\w+\\\\.)*)(?<object>\\\\w+)\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _dbAndObjectName = new Regex(\"EXEC(UTE){0,1}\\\\s+(?<database>\\\\w+)\\\\.\\\\.(?<object>\\\\w+)\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _emptyString = new Regex(\"\\\\'\\\\'\", RegexOptions.Compiled);\n        private static readonly Regex _unicodeConstant = new Regex(\"N{STR}\", RegexOptions.Compiled);\n        private static readonly Regex _stringConstant = new Regex(\"(')(((?!\\\\1).|\\\\1{2})*)\\\\1\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _paramNameValueStr = new Regex(@\"@(?<paramname>\\w+)\\s*=\\s*N?'(?<paramvalue>(?:[^']|'')*)'\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _paramNameValueNum = new Regex(@\"@(?<paramname>\\w+)\\s?=\\s?(?<paramvalue>([0-9.])+)\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _binaryConstant = new Regex(\"0X([0-9ABCDEF])+\", RegexOptions.Compiled);\n        private static readonly Regex _numericConstant = new Regex(\"(?<prefix>[\\\\(\\\\s,=\\\\-><\\\\!\\\\&\\\\|\\\\+\\\\*\\\\/\\\\%\\\\~\\\\$])(?<digits>[\\\\-\\\\.\\\\d]+)\", RegexOptions.Compiled);\n        private static readonly Regex _inClause = new Regex(\"IN\\\\s*\\\\(\\\\s*\\\\{.*\\\\}\\\\s*\\\\)\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _brackets = new Regex(\"(\\\\[|\\\\])\", RegexOptions.Compiled);\n        private static readonly Regex _TVPExecute = new Regex(@\"DECLARE\\s*@(?<tablename>(\\w+))\\s*(AS)?\\s*(?<tabletype>(\\w+)).*EXEC(UTE)?\\s*(?<object>(\\S+)).*@\\k<tablename>\\sREADONLY\", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\n\n        public bool TruncateTo4000 { get; set; }\n        public bool TruncateTo1024 { get; set; }\n\n        private static readonly Thread Sweeper;\n\n        static SqlTextNormalizer()\n        {\n            Sweeper = new Thread(() =>\n            {\n                try\n                {\n                    while (true)\n                    {\n                        var toDelete = cachedQueries.Where(t => t.Value.ReferenceCount < 10).ToList();\n                        foreach (var el in toDelete)\n                        {\n                            _ = cachedQueries.TryRemove(el.Key, out var nst);\n                        }\n                        Thread.Sleep(30000);\n                    }\n                }\n                catch (Exception e)\n                {\n                    logger.Error(e.Message);\n                    logger.Error(e.StackTrace);\n                }\n            })\n            {\n                IsBackground = true,\n                Name = \"SqlTextNormalizer.CacheSweeper\"\n            };\n            Sweeper.Start();\n        }\n\n        public NormalizedSqlText NormalizeSqlText(string sql, int spid)\n        {\n            try\n            {\n                var hashCode = sql.GetHashCode();\n                if (cachedQueries.TryGetValue(hashCode, out var result))\n                {\n                    if (result != null && result.OriginalText == sql)\n                    {\n                        result.ReferenceCount++;\n                        return result;\n                    }\n                }\n                result = NormalizeSqlText(sql, spid, true);\n                logger.Trace(\"NormalizeSqlText:[{0}]: {1}\", spid, sql);\n                if (result != null)\n                { \n                    logger.Trace(\"NormalizeSqlText:[{0}]: {1}\", spid, result.NormalizedText);\n                    result.ReferenceCount = 1;\n                    _ = cachedQueries.TryAdd(hashCode, result);\n                }\n                return result;\n            }\n            catch (Exception)\n            {\n                throw;\n            }\n        }\n\n        public NormalizedSqlText NormalizeSqlText(string sql, int spid, bool spreadCsv)\n        {\n            var normalizedSqlText = new NormalizedSqlText\n            {\n                OriginalText = sql,\n                NormalizedText = sql\n            };\n            var result = normalizedSqlText;\n\n            if (sql == null)\n            {\n                result.OriginalText = \"\";\n                result.NormalizedText = \"\";\n                result.Statement = \"\";\n                return result;\n            }\n\n            sql = sql.Trim();\n\n            if (TruncateTo1024  && sql.Length > 1024000)\n            {\n                result.Statement = \"{SQL>1MB}\";\n                result.NormalizedText = \"{SQL>1MB}\";\n                return result;\n            }\n                \n            var flag1 = false;\n            var flag2 = false;\n            var num = 0;\n\n            if ((sql == \"sp_reset_connection\") || (sql == \"exec sp_reset_connection\") || (sql == \"exec sp_reset_connection /*Nonpooled*/\"))\n            {\n                return null;\n            }\n\n            sql = FixComments(sql);\n            sql = _spaces.Replace(sql, \" \").ToUpper(CultureInfo.InvariantCulture);\n\n            sql = _doubleApostrophe.Replace(sql, \"{STR}\");\n            var matchPrepExecRpc = _prepExecRpc.Match(sql);\n            if (matchPrepExecRpc.Success)\n            {\n                sql = matchPrepExecRpc.Groups[\"statement\"].ToString();\n                result.Statement = sql;\n                result.NormalizedText = sql;\n            }\n            var matchPrepareSql = _prepareSql.Match(sql);\n            if (matchPrepareSql.Success)\n            {\n                if (matchPrepareSql.Groups[\"preptype\"].ToString().ToLower() == \"sp_prepare\")\n                {\n                    flag2 = true;\n                }\n                //num = !(match3.Groups[\"stmtnum\"].ToString() == \"NULL\") ? Convert.ToInt32(match3.Groups[\"stmtnum\"].ToString()) : 0;\n                sql = matchPrepareSql.Groups[\"remaining\"].ToString();\n                var matchPreppedSqlStatement = _preppedSqlStatement.Match(sql);\n                if (matchPreppedSqlStatement.Success)\n                {\n                    sql = matchPreppedSqlStatement.Groups[\"statement\"].ToString();\n                    sql = _doubleApostrophe.Replace(sql, \"'${string}'\");\n                    result.Statement = sql;\n                    result.NormalizedText = sql;\n                }\n                flag1 = true;\n            }\n\n            var matchExecPrepped = _execPrepped.Match(sql);\n            if (matchExecPrepped.Success)\n            {\n                num = Convert.ToInt32(matchExecPrepped.Groups[\"stmtnum\"].ToString());\n                if (prepSql.ContainsKey((object)(spid.ToString() + \"_\" + num.ToString())))\n                {\n                    result.NormalizedText = TruncateSql(\"{PREPARED} \" + prepSql[(object)(spid.ToString() + \"_\" + num.ToString())].ToString());\n                    return result;\n                }\n            }\n\n            var matchExecUnprep = _execUnprep.Match(sql);\n            if (matchExecUnprep.Success)\n            {\n                num = Convert.ToInt32(matchExecUnprep.Groups[\"stmtnum\"].ToString());\n                var str = spid.ToString() + \"_\" + num.ToString();\n                if (prepSql.ContainsKey((object)str))\n                {\n                    sql = prepSql[(object)str].ToString();\n                    prepSql.Remove((object)(spid.ToString() + \"_\" + num.ToString()));\n\n                    result.NormalizedText = TruncateSql(\"{UNPREPARING} \" + sql);\n                    return result;\n                }\n            }\n\n            \n           \n            var matchCursor = _cursor.Match(sql);\n            if (matchCursor.Success)\n            {\n                sql = matchCursor.Groups[\"statement\"].ToString();\n                sql = _doubleApostrophe.Replace(sql, \"'${string}'\");\n                result.Statement = sql;\n                result.NormalizedText =  \"{CURSOR} \" + sql;\n            }\n            var matchCursorPrepexec = _cursorPrepExec.Match(sql);\n            if (matchCursorPrepexec.Success)\n            {\n                sql = matchCursorPrepexec.Groups[\"statement\"].ToString();\n                sql = _doubleApostrophe.Replace(sql, \"'${string}'\");\n                result.Statement = sql;\n                result.NormalizedText = \"{CURSOR} \" + sql;\n            }\n            var matchSpExecuteSql = _spExecuteSql.Match(sql);\n            if (matchSpExecuteSql.Success)\n            {\n                sql = matchSpExecuteSql.Groups[\"statement\"].ToString();\n                result.Statement = sql;\n                result.NormalizedText = sql;\n            }\n\n            var matchSpExecuteSqlWithStatement = _spExecuteSqlWithStatement.Match(sql);\n            if (matchSpExecuteSqlWithStatement.Success)\n            {\n                sql = matchSpExecuteSqlWithStatement.Groups[\"statement\"].ToString();\n                result.Statement = sql;\n                result.NormalizedText = sql;\n            }\n\n            if (!_brackets.Match(sql).Success)\n            {\n                var matchDbAndObjectName = _dbAndObjectName.Match(sql);\n                if (matchDbAndObjectName.Success)\n                {\n                    sql = matchDbAndObjectName.Groups[\"object\"].ToString();\n                }\n                else\n                {\n                    var matchObjectName = _objectName.Match(sql);\n                    if (matchObjectName.Success)\n                    {\n                        sql = matchObjectName.Groups[\"object\"].ToString();\n                    }\n                }\n                if (sql == \"SP_CURSOR\" || sql == \"SP_CURSORFETCH\" || sql == \"SP_CURSORCLOSE\" || sql == \"SP_RESET_CONNECTION\")\n                {\n                    return null;\n                }\n            }\n\n            if (sql.Contains(\"EXEC\") && sql.Contains(\"READONLY\"))\n            {\n                var matchTVPExecute = _TVPExecute.Match(sql);\n                if (matchTVPExecute.Success)\n                {\n                    result.Statement = sql;\n                    result.NormalizedText = \"EXECUTE \" + matchTVPExecute.Groups[\"object\"].ToString();\n                }\n            }\n\n            result.NormalizedText = _paramNameValueStr.Replace(result.NormalizedText, \"@${paramname} = {STR}\");\n            result.NormalizedText = _paramNameValueNum.Replace(result.NormalizedText, \"@${paramname} = {NUM}\");\n            result.NormalizedText = _emptyString.Replace(result.NormalizedText, \"{STR}\");\n            result.NormalizedText = _stringConstant.Replace(result.NormalizedText, \"{STR}\");\n            result.NormalizedText = _unicodeConstant.Replace(result.NormalizedText, \"{NSTR}\");\n            result.NormalizedText = _binaryConstant.Replace(result.NormalizedText, \"{BINARY}\");\n            result.NormalizedText = _numericConstant.Replace(result.NormalizedText, \"${prefix}{##}\");\n            result.NormalizedText = _inClause.Replace(result.NormalizedText, \"{IN}\");\n            if (spreadCsv)\n            {\n                result.NormalizedText = _spreadCsv.Replace(result.NormalizedText, \", \");\n            }\n\n            result.NormalizedText = _spaces.Replace(result.NormalizedText, \" \");\n            result.NormalizedText = TruncateSql(result.NormalizedText);\n            if (flag1 && num != 0)\n            {\n                var theKey = (object)(spid.ToString() + \"_\" + num.ToString());\n                if (!prepSql.ContainsKey(theKey))\n                {\n                    prepSql.Add(theKey, sql);\n                }\n                else\n                {\n                    prepSql[theKey] = sql;\n                }\n            }\n\n            if (flag2)\n            {\n                result.NormalizedText = TruncateSql(\"{PREPARING} \" + sql);\n                return result;\n            }\n            if (flag1 && !flag2)\n            {\n                result.NormalizedText = TruncateSql(\"{PREPARED} \" + sql);\n                return result;\n            }\n            result.NormalizedText = TruncateSql(result.NormalizedText);\n            return result;\n        }\n\n        private string TruncateSql(string sql)\n        {\n            sql = sql.Trim();\n            if (TruncateTo4000 && sql.Length > 4000)\n            {\n                return sql.Substring(0, 4000);\n            }\n\n            return sql;\n        }\n\n        private string FixComments(string sql)\n        {\n            var str = sql;\n            var num = 0;\n            var startat = 0;\n            var match1 = _delimiterStart.Match(sql, startat);\n            while (match1.Success)\n            {\n                switch (match1.Value)\n                {\n                    case \"'\":\n                        var match2 = _stringConstant.Match(sql, match1.Index);\n                        if (match2.Success)\n                        {\n                            startat = match1.Index + match2.Length;\n                            break;\n                        }\n                        ++startat;\n                        break;\n                    case \"--\":\n                        startat = match1.Index;\n                        if (_inlineComment.Match(sql, startat).Success)\n                        {\n                            sql = _inlineComment.Replace(sql, \"\", 1, startat);\n                            break;\n                        }\n                        ++startat;\n                        break;\n                    case \"/*\":\n                        var index = match1.Index;\n                        sql = RemoveBlockComments(sql, index);\n                        startat = index + 1;\n                        break;\n                    default:\n                        return sql;\n                }\n                if (startat < sql.Length)\n                {\n                    match1 = _delimiterStart.Match(sql, startat);\n                    ++num;\n                    if (num > 1000000)\n                    {\n                        throw new Exception(\"Infinite loop in FixComments (\" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + \")\" + Environment.NewLine + Environment.NewLine + str);\n                    }\n                }\n                else\n                {\n                    break;\n                }\n            }\n            return sql;\n        }\n\n        private string RemoveBlockComments(string sql, int position)\n        {\n            var stringBuilder = new StringBuilder(sql.Length);\n            _ = stringBuilder.Append(sql.Substring(0, position));\n            var num = 0;\n            var startIndex = position;\n            while (startIndex < sql.Length - 1)\n            {\n                switch (sql.Substring(startIndex, 2))\n                {\n                    case \"/*\":\n                        ++num;\n                        startIndex = startIndex + 1 + 1;\n                        break;\n                    case \"*/\":\n                        --num;\n                        startIndex = startIndex + 1 + 1;\n                        break;\n                    default:\n                        ++startIndex;\n                        break;\n                }\n                if (num == 0)\n                {\n                    if (startIndex < sql.Length)\n                    {\n                        _ = stringBuilder.Append(sql.Substring(startIndex, sql.Length - startIndex));\n                    }\n\n                    return stringBuilder.ToString();\n                }\n            }\n            return sql;\n        }\n\n        public long GetHashCode(string text)\n        {\n            text = text ?? \"\";\n            var num = text.Length / 2;\n            return (int.MaxValue * (long)text.Substring(0, num).GetHashCode()) + text.Substring(num, text.Length - num).GetHashCode();\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/WorkloadAnalyzer.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Util;\nusing System.Collections.Concurrent;\nusing FastMember;\nusing Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;\n\nnamespace WorkloadTools.Consumer.Analysis\n{\n    public class WorkloadAnalyzer : IDisposable\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n        public int Interval { get; set; }\n\n        private readonly Dictionary<long, NormalizedQuery> normalizedQueries = new Dictionary<long, NormalizedQuery>();\n        private readonly Dictionary<string, int> applications = new Dictionary<string, int>();\n        private readonly Dictionary<string, int> databases = new Dictionary<string, int>();\n        private readonly Dictionary<string, int> logins = new Dictionary<string, int>();\n        private readonly Dictionary<string, int> hosts = new Dictionary<string, int>();\n\n        private Queue<WorkloadEvent> _internalQueue = new Queue<WorkloadEvent>();\n        private readonly object _internalQueueLock = new object();\n        private Thread Worker;\n        private bool stopped = false;\n        public int MaxInternalQueueSize { get; set; } = 10000;\n\n        private ConcurrentDictionary<ExecutionDetailKey,List<ExecutionDetailValue>> rawData;\n\t\tprivate DataTable errorData;\n        private readonly SqlTextNormalizer normalizer;\n        private bool TargetTableCreated = false;\n        private bool FirstIntervalWritten = false;\n\n        private DataTable counterData;\n        private DataTable waitsData;\n        private DataTable diskPerfData;\n\n        public int MaximumWriteRetries { get; set; }\n\t\tpublic bool TruncateTo4000 { get; set; }\n\t\tpublic bool TruncateTo1024 { get; set; }\n        public bool WriteDetail { get; set; } = true;\n        public bool WriteSummary { get; set; } = true;\n\n        private class NormalizedQuery\n        {\n            public long Hash { get; set; }\n            public string NormalizedText { get; set; }\n            public string ExampleText { get; set; }\n        }\n\n        private DateTime lastDump = DateTime.MinValue;\n        private DateTime lastEventTime = DateTime.MinValue;\n        private volatile int lastWrittenIntervalId = -1;\n\n        public WorkloadAnalyzer()\n\t\t{\n\t\t\tnormalizer = new SqlTextNormalizer()\n\t\t\t{\n\t\t\t\tTruncateTo1024 = TruncateTo1024,\n\t\t\t\tTruncateTo4000 = TruncateTo4000\n\t\t\t};\n\t\t}\n\n        public bool HasEventsQueued\n        {\n            get\n            {\n                lock (_internalQueueLock)\n                {\n                    return _internalQueue.Count > 0;\n                }\n            }\n        }\n\n        private void CloseInterval()\n        {\n            // Write collected data to the destination database\n            var duration = lastEventTime - lastDump;\n            if (duration.TotalMinutes >= Interval)\n            {\n                // Avoid writing the same interval_id twice. This can happen when\n                // Interval=0 (the default) and multiple events share the same\n                // second-precision timestamp: after the first write sets\n                // lastDump=lastEventTime, the condition above is 0>=0 (always true),\n                // so the next loop iteration would attempt to INSERT to WorkloadDetails\n                // for an interval_id that was already committed.\n                var prospectiveIntervalId = ComputeIntervalId(lastEventTime);\n                if (prospectiveIntervalId == lastWrittenIntervalId)\n                {\n                    lastDump = lastEventTime;\n                    return;\n                }\n\n                try\n                {\n                    var numRetries = 0;\n                    while (numRetries <= MaximumWriteRetries)\n                    {\n                        try\n                        {\n                            WriteToServer(lastEventTime);\n                            numRetries = MaximumWriteRetries + 1;\n                        }\n                        catch (Exception ex)\n                        {\n                            logger.Warn(\"Unable to write workload analysis.\");\n                            logger.Warn(ex.Message);\n\n                            if (numRetries == MaximumWriteRetries)\n                            {\n                                throw;\n                            }\n                        }\n                        numRetries++;\n                    }\n                }\n                catch (Exception e)\n                {\n                    try\n                    {\n                        logger.Error(e, \"Unable to write workload analysis info to the destination database.\");\n                        logger.Error(e.StackTrace);\n                    }\n                    catch\n                    {\n                        Console.WriteLine(string.Format(\"Unable to write to the database: {0}.\", e.Message));\n                    }\n                }\n                finally\n                {\n                    lastDump = lastEventTime;\n                }\n            }\n\n        }\n\n        private void ProcessQueue()\n        {\n            while (!stopped)\n            {\n                WorkloadEvent data = null;\n                bool hasData = false;\n\n                lock (_internalQueueLock)\n                {\n                    CloseInterval();\n\n                    if (_internalQueue.Count > 0)\n                    {\n                        data = _internalQueue.Dequeue();\n                        hasData = true;\n                        // Notify Add() that a slot is now free\n                        Monitor.PulseAll(_internalQueueLock);\n                    }\n                }\n\n                if (hasData)\n                {\n                    InternalAdd(data);\n                }\n                else\n                {\n                    // Sleep outside the lock so Add() is not blocked unnecessarily\n                    Thread.Sleep(10);\n                }\n            }\n        }\n\n        public void Add(WorkloadEvent evt)\n        {\n            if (evt is ExecutionWorkloadEvent executionEvent && string.IsNullOrEmpty(executionEvent.Text))\n            {\n                return;\n            }\n\n            try\n            {\n                ProvisionWorker();\n            }\n            catch (Exception e)\n            {\n                logger.Error(e, \"Unable to start the worker thread for WorkloadAnalyzer\");\n            }\n\n            lock (_internalQueueLock)\n            {\n                // Block when the queue is full to avoid unbounded memory growth\n                while (!stopped && _internalQueue.Count >= MaxInternalQueueSize)\n                {\n                    Monitor.Wait(_internalQueueLock);\n                }\n\n                if (stopped) return;\n\n                lastEventTime = evt.StartTime;\n                if (lastDump == DateTime.MinValue)\n                {\n                    lastDump = lastEventTime;\n                }\n                _internalQueue.Enqueue(evt);\n            }\n\n        }\n\n        private void ProvisionWorker()\n        {\n            var startNewWorker = false;\n            if (Worker == null)\n            {\n                startNewWorker = true;\n            }\n            else\n            {\n                if (!Worker.IsAlive)\n                {\n                    startNewWorker = true;\n                }\n            }\n\n            if (startNewWorker)\n            {\n                // Start a new background worker if the thread is null\n                // or stopped / aborted\n                Worker = new Thread(() =>\n                {\n                    try\n                    {\n                        ProcessQueue();\n                    }\n                    catch (Exception e)\n                    {\n                        logger.Error(e.Message);\n                        logger.Error(e.StackTrace);\n                    }\n                })\n                {\n                    IsBackground = true,\n                    Name = \"RealtimeWorkloadAnalyzer.Worker\"\n                };\n                Worker.Start();\n\n                Thread.Sleep(100);\n            }\n        }\n\n        private void InternalAdd(WorkloadEvent evt)\n        {\n            if (evt is ExecutionWorkloadEvent executionEvent)\n            {\n                InternalAdd(executionEvent);\n            }\n\n            if (evt is ErrorWorkloadEvent errorEvent)\n            {\n                InternalAdd(errorEvent);\n            }\n\n            if (evt is CounterWorkloadEvent counterEvent)\n            {\n                InternalAdd(counterEvent);\n            }\n\n            if (evt is WaitStatsWorkloadEvent waitStatsEvent)\n            {\n                InternalAdd(waitStatsEvent);\n            }\n\n            if (evt is DiskPerfWorkloadEvent diskPerfEvent)\n            {\n                InternalAdd(diskPerfEvent);\n            }\n        }\n\n\t\tprivate void InternalAdd(ErrorWorkloadEvent evt)\n\t\t{\n\t\t\tvar row = errorData.NewRow();\n\t\t\trow.SetField(\"message\", evt.Text);\n            row.SetField(\"type\", evt.Type);\n            errorData.Rows.Add(row);\n\t\t}\n\n\t\tprivate void InternalAdd(WaitStatsWorkloadEvent evt)\n        {\n            if (waitsData == null)\n            {\n                waitsData = evt.Waits;\n            }\n            else\n            {\n                waitsData.Merge(evt.Waits);\n            }\n        }\n\n        private void InternalAdd(DiskPerfWorkloadEvent evt)\n        {\n            if (diskPerfData == null)\n            {\n                diskPerfData = evt.DiskPerf;\n            }\n            else\n            {\n                diskPerfData.Merge(evt.DiskPerf);\n            }\n        }\n\n        private void InternalAdd(CounterWorkloadEvent evt)\n        {\n            if (counterData == null)\n            {\n                counterData = new DataTable();\n\n                _ = counterData.Columns.Add(\"event_time\", typeof(DateTime));\n                _ = counterData.Columns.Add(\"counter_name\", typeof(string));\n                _ = counterData.Columns.Add(\"counter_value\", typeof(float));\n            }\n\n            foreach(var cntr in evt.Counters.Keys)\n            {\n                var row = counterData.NewRow();\n\n                row.SetField(\"event_time\", evt.StartTime);\n                row.SetField(\"counter_name\", cntr.ToString());\n                row.SetField(\"counter_value\", evt.Counters[cntr]);\n\n                counterData.Rows.Add(row);\n            }\n            \n        }\n\n        private void InternalAdd(ExecutionWorkloadEvent evt)\n        {\n            if (rawData == null)\n            {\n                PrepareDataTables();\n                PrepareDictionaries();\n            }\n\n            var norm = normalizer.NormalizeSqlText(evt.Text, (int)evt.SPID);\n\n            string normSql;\n            if (norm != null)\n            {\n                normSql = norm.NormalizedText;\n            }\n            else\n            {\n                return;\n            }\n\n            if (normSql == null)\n            {\n                return;\n            }\n\n            var hash = normalizer.GetHashCode(normSql);\n\n            if (!normalizedQueries.ContainsKey(hash))\n            {\n                normalizedQueries.Add(hash, new NormalizedQuery { Hash = hash, NormalizedText = normSql, ExampleText = evt.Text });\n            }\n\n            var appId = -1;\n            if (evt.ApplicationName != null && !applications.TryGetValue(evt.ApplicationName, out appId))\n            {\n                applications.Add(evt.ApplicationName, appId = applications.Count);\n            }\n\n            var dbId = -1;\n            if (evt.DatabaseName != null && !databases.TryGetValue(evt.DatabaseName, out dbId))\n            {\n                databases.Add(evt.DatabaseName, dbId = databases.Count);\n            }\n\n            var hostId = -1;\n            if (evt.HostName != null && !hosts.TryGetValue(evt.HostName, out hostId))\n            {\n                hosts.Add(evt.HostName, hostId = hosts.Count);\n            }\n\n            var loginId = -1;\n            if (evt.LoginName != null && !logins.TryGetValue(evt.LoginName, out loginId))\n            {\n                logins.Add(evt.LoginName, loginId = logins.Count);\n            }\n\n            var theKey = new ExecutionDetailKey()\n            {\n                Sql_hash = hash,\n                Application_id = appId,\n                Database_id = dbId,\n                Host_id = hostId,\n                Login_id = loginId\n            };\n            var theValue = new ExecutionDetailValue()\n            {\n                Event_time = evt.StartTime,\n                Cpu_us = evt.CPU,\n                Reads = evt.Reads,\n                Writes = evt.Writes,\n                Duration_us = evt.Duration\n            };\n\n            // Look up execution detail \n            if (rawData.TryGetValue(theKey, out var theList))\n            {\n                if (theList == null)\n                {\n                    theList = new List<ExecutionDetailValue>();\n                }\n                theList.Add(theValue);\n            }\n            else\n            {\n                theList = new List<ExecutionDetailValue>\n                {\n                    theValue\n                };\n\n                if (!rawData.TryAdd(theKey, theList))\n                {\n                    throw new InvalidOperationException(\"Unable to add an executionEvent to the queue\");\n                }\n            }\n        }\n\n        public void Stop()\n        {\n            try\n            {\n                WriteToServer(lastEventTime);\n            }\n            catch (Exception e)\n            {\n                // duplicate key errors might be thrown at this time\n                // that's expected if trying to upload to the same\n                // interval already uploaded and new queries with the \n                // same hash have been captured\n                if(!e.Message.Contains(\"Violation of PRIMARY KEY\"))\n                {\n                    throw;\n                }\n            }\n            stopped = true;\n            // Wake up any Add() calls that may be blocked waiting for queue space\n            lock (_internalQueueLock)\n            {\n                Monitor.PulseAll(_internalQueueLock);\n            }\n        }\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        private void WriteToServer(DateTime intervalTime)\n        {\n            logger.Trace(\"Writing Workload Analysis data\");\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n\n                if (!TargetTableCreated)\n                {\n                    CreateTargetTables();\n                    TargetTableCreated = true;\n                }\n\n                var tran = conn.BeginTransaction();\n\n                try\n                {\n                    var current_interval_id = 0;\n                    if(WriteDetail)\n                    {\n                        current_interval_id = CreateInterval(conn, tran, intervalTime);\n                    }\n\n                    WriteDictionary(applications, conn, tran, \"applications\");\n                    WriteDictionary(databases, conn, tran, \"databases\");\n                    WriteDictionary(hosts, conn, tran, \"hosts\");\n                    WriteDictionary(logins, conn, tran, \"logins\");\n\n                    if (rawData == null)\n                    {\n                        PrepareDataTables();\n                    }\n\n                    lock (rawData)\n                    {\n                        if (WriteSummary)\n                        {\n                            WriteExecutionSummary(conn, tran);\n                        }\n\n                        if (WriteDetail)\n                        {\n                            WriteExecutionDetails(conn, tran, current_interval_id);\n                        }\n\n                        rawData.Clear();\n                    }\n\n                    if (WriteDetail)\n                    {\n                        WriteNormalizedQueries(normalizedQueries, conn, tran);\n                        WriteExecutionErrors(conn, tran, current_interval_id);\n                        WritePerformanceCounters(conn, tran, current_interval_id);\n                        WriteWaitsData(conn, tran, current_interval_id);\n                        WriteDiskPerf(conn, tran, current_interval_id);\n                    }\n\n                    tran.Commit();\n                    if (WriteDetail)\n                    {\n                        lastWrittenIntervalId = current_interval_id;\n                    }\n                }\n                catch(Exception)\n                {\n                    tran.Rollback();\n                    throw;\n                }\n\n            }\n\n        }\n\n        private void WriteWaitsData(SqlConnection conn, SqlTransaction tran, int current_interval_id)\n        {\n            if (waitsData == null)\n            {\n                return;\n            }\n\n            lock (waitsData)\n            {\n                using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                                SqlBulkCopyOptions.KeepIdentity |\n                                                SqlBulkCopyOptions.FireTriggers |\n                                                SqlBulkCopyOptions.CheckConstraints |\n                                                SqlBulkCopyOptions.TableLock,\n                                                tran))\n                {\n\n                    bulkCopy.DestinationTableName = \"[\" + ConnectionInfo.SchemaName + \"].[WaitStats]\";\n                    bulkCopy.BatchSize = 1000;\n                    bulkCopy.BulkCopyTimeout = 300;\n\n                    var Table = from t in waitsData.AsEnumerable()\n                                group t by new\n                                {\n                                    wait_type = t.Field<string>(\"wait_type\")\n                                }\n                                into grp\n                                select new\n                                {\n                                    interval_id = current_interval_id,\n\n                                    grp.Key.wait_type,\n\n                                    wait_sec = grp.Sum(t => t.Field<double>(\"wait_sec\")),\n                                    resource_sec = grp.Sum(t => t.Field<double>(\"resource_sec\")),\n                                    signal_sec = grp.Sum(t => t.Field<double>(\"signal_sec\")),\n                                    wait_count = grp.Sum(t => t.Field<double>(\"wait_count\"))\n                                };\n\n                    using(var dt = DataUtils.ToDataTable(Table))\n                    {\n                        bulkCopy.WriteToServer(dt);\n                    }\n                    \n                    logger.Info(\"Wait stats written\");\n                }\n                waitsData.Dispose();\n                waitsData = null;\n            }\n        }\n\n        private void WriteDiskPerf(SqlConnection conn, SqlTransaction tran, int current_interval_id)\n        {\n            if (diskPerfData == null)\n            {\n                return;\n            }\n\n            lock (diskPerfData)\n            {\n                using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                                SqlBulkCopyOptions.KeepIdentity |\n                                                SqlBulkCopyOptions.FireTriggers |\n                                                SqlBulkCopyOptions.CheckConstraints |\n                                                SqlBulkCopyOptions.TableLock,\n                                                tran))\n                {\n\n                    bulkCopy.DestinationTableName = \"[\" + ConnectionInfo.SchemaName + \"].[DiskPerf]\";\n                    bulkCopy.BatchSize = 1000;\n                    bulkCopy.BulkCopyTimeout = 300;\n\n                    var Table = from t in diskPerfData.AsEnumerable()\n                                group t by new\n                                {\n                                    database_name = t.Field<string>(\"database_name\"),\n                                    physical_filename = t.Field<string>(\"physical_filename\"),\n                                    logical_filename = t.Field<string>(\"logical_filename\"),\n                                    file_type = t.Field<string>(\"file_type\"),\n                                    volume_mount_point = t.Field<string>(\"volume_mount_point\"),\n                                }\n                                into grp\n                                select new\n                                {\n                                    interval_id = current_interval_id,\n\n                                    grp.Key.database_name,\n                                    grp.Key.physical_filename,\n                                    grp.Key.logical_filename,\n                                    grp.Key.file_type,\n                                    grp.Key.volume_mount_point,\n\n                                    read_latency_ms = grp.Average(t => t.Field<double>(\"read_latency_ms\")),\n                                    reads = grp.Sum(t => t.Field<double>(\"reads\")),\n                                    read_bytes = grp.Sum(t => t.Field<double>(\"read_bytes\")),\n                                    write_latency_ms = grp.Average(t => t.Field<double>(\"write_latency_ms\")),\n                                    writes = grp.Sum(t => t.Field<double>(\"writes\")),\n                                    write_bytes = grp.Sum(t => t.Field<double>(\"write_bytes\")),\n\n                                    cum_read_latency_ms = grp.Max(t => t.Field<double?>(\"cum_read_latency_ms\")),\n                                    cum_reads = grp.Max(t => t.Field<double?>(\"cum_reads\")),\n                                    cum_read_bytes = grp.Max(t => t.Field<double?>(\"cum_read_bytes\")),\n                                    cum_write_latency_ms = grp.Max(t => t.Field<double?>(\"cum_write_latency_ms\")),\n                                    cum_writes = grp.Max(t => t.Field<double?>(\"cum_writes\")),\n                                    cum_write_bytes = grp.Max(t => t.Field<double?>(\"cum_write_bytes\"))\n                                };\n\n                    using (var dt = DataUtils.ToDataTable(Table))\n                    {\n                        bulkCopy.WriteToServer(dt);\n                    }\n\n                    logger.Info(\"Disk perf written\");\n                }\n                diskPerfData.Dispose();\n                diskPerfData = null;\n            }\n        }\n\n        private void WritePerformanceCounters(SqlConnection conn, SqlTransaction tran, int current_interval_id)\n        {\n            if (counterData == null)\n            {\n                return;\n            }\n\n            lock (counterData)\n            {\n                using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                                SqlBulkCopyOptions.KeepIdentity |\n                                                SqlBulkCopyOptions.FireTriggers |\n                                                SqlBulkCopyOptions.CheckConstraints |\n                                                SqlBulkCopyOptions.TableLock,\n                                                tran))\n                {\n\n                    bulkCopy.DestinationTableName = \"[\" + ConnectionInfo.SchemaName + \"].[PerformanceCounters]\";\n                    bulkCopy.BatchSize = 1000;\n                    bulkCopy.BulkCopyTimeout = 300;\n\n                    var Table = from t in counterData.AsEnumerable()\n                                group t by new\n                                {\n                                    counter_name = t.Field<string>(\"counter_name\")\n                                }\n                                into grp\n                                select new\n                                {\n                                    interval_id = current_interval_id,\n\n                                    grp.Key.counter_name,\n\n                                    min_counter_value = grp.Min(t => t.Field<float>(\"counter_value\")),\n                                    max_counter_value = grp.Max(t => t.Field<float>(\"counter_value\")),\n                                    avg_counter_value = grp.Average(t => t.Field<float>(\"counter_value\"))\n                                };\n\n                    using (var dt = DataUtils.ToDataTable(Table))\n                    {\n                        bulkCopy.WriteToServer(dt);\n                    }\n                    logger.Info(\"Performance counters written\");\n                }\n                counterData.Dispose();\n                counterData = null;\n            }\n        }\n\n        private void WriteExecutionSummary(SqlConnection conn, SqlTransaction tran)\n        {\n            // create temporary table for uploading data\n            var sql = $@\"\n                IF OBJECT_ID('tempdb..#WorkloadSummary') IS NOT NULL \n                    DROP TABLE #WorkloadSummary;\n                    \n                SELECT TOP(0) * INTO #WorkloadSummary FROM [{ConnectionInfo.SchemaName}].WorkloadSummary;\n            \";\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n\n            // bulk copy data to temp table\n            using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                            SqlBulkCopyOptions.KeepIdentity |\n                                            SqlBulkCopyOptions.FireTriggers |\n                                            SqlBulkCopyOptions.CheckConstraints |\n                                            SqlBulkCopyOptions.TableLock,\n                                            tran))\n            {\n\n                bulkCopy.DestinationTableName = \"#WorkloadSummary\";\n                bulkCopy.BatchSize = 1000;\n                bulkCopy.BulkCopyTimeout = 300;\n\n                var Table = from t in rawData.Keys\n                            from v in rawData[t]\n                            group new\n                            {\n                                v.Cpu_us,\n                                v.Duration_us,\n                                v.Event_time,\n                                v.Reads,\n                                v.Writes\n                            }\n                            by new\n                            {\n                                application_id = t.Application_id,\n                                database_id = t.Database_id,\n                                host_id = t.Host_id,\n                                login_id = t.Login_id\n                            }\n                            into grp\n                            select new\n                            {\n                                grp.Key.application_id,\n                                grp.Key.database_id,\n                                grp.Key.host_id,\n                                grp.Key.login_id,\n\n                                min_cpu_us = grp.Min(v => v.Cpu_us),\n                                max_cpu_us = grp.Max(v => v.Cpu_us),\n                                sum_cpu_us = grp.Sum(v => v.Cpu_us),\n\n                                min_reads = grp.Min(v => v.Reads),\n                                max_reads = grp.Max(v => v.Reads),\n                                sum_reads = grp.Sum(v => v.Reads),\n\n                                min_writes = grp.Min(v => v.Writes),\n                                max_writes = grp.Max(v => v.Writes),\n                                sum_writes = grp.Sum(v => v.Writes),\n\n                                min_duration_us = grp.Min(v => v.Duration_us),\n                                max_duration_us = grp.Max(v => v.Duration_us),\n                                sum_duration_us = grp.Sum(v => v.Duration_us),\n\n                                min_execution_date = grp.Min(v => v.Event_time),\n                                max_execution_date = grp.Max(v => v.Event_time),\n\n                                execution_count = grp.Count()\n                            };\n\n                using (var reader = ObjectReader.Create(Table, \"application_id\", \"database_id\", \"host_id\", \"login_id\", \"min_cpu_us\", \"max_cpu_us\", \"sum_cpu_us\", \"min_reads\", \"max_reads\", \"sum_reads\", \"min_writes\", \"max_writes\", \"sum_writes\", \"min_duration_us\", \"max_duration_us\", \"sum_duration_us\", \"min_execution_date\", \"max_execution_date\", \"execution_count\"))\n                {\n                    bulkCopy.WriteToServer(reader);\n                }\n\n            }\n\n            var affectedRows = 0;\n            // merge with existing data\n\n            sql = $@\"\n                UPDATE WS\n                SET min_cpu_us = CASE WHEN T.min_cpu_us < WS.min_cpu_us THEN T.min_cpu_us ELSE WS.min_cpu_us END,\n                    max_cpu_us = CASE WHEN T.max_cpu_us > WS.max_cpu_us THEN T.max_cpu_us ELSE WS.max_cpu_us END,\n                    sum_cpu_us += T.sum_cpu_us,\n                    min_reads  = CASE WHEN T.min_reads < WS.min_reads THEN T.min_reads ELSE WS.min_reads END,\n                    max_reads  = CASE WHEN T.max_reads > WS.max_reads THEN T.max_reads ELSE WS.max_reads END,\n                    sum_reads  += T.sum_reads,\n                    min_writes = CASE WHEN T.min_writes < WS.min_writes THEN T.min_writes ELSE WS.min_writes END,\n                    max_writes = CASE WHEN T.max_writes > WS.max_writes THEN T.max_writes ELSE WS.max_writes END,\n                    sum_writes += T.sum_writes,\n                    min_duration_us = CASE WHEN T.min_duration_us < WS.min_duration_us THEN T.min_duration_us ELSE WS.min_duration_us END,\n                    max_duration_us = CASE WHEN T.max_duration_us > WS.max_duration_us THEN T.max_duration_us ELSE WS.max_duration_us END,\n                    sum_duration_us += T.sum_duration_us,\n                    min_execution_date = CASE WHEN T.min_execution_date < WS.min_execution_date THEN T.min_execution_date ELSE WS.min_execution_date END,\n                    max_execution_date = CASE WHEN T.max_execution_date > WS.max_execution_date THEN T.max_execution_date ELSE WS.max_execution_date END,\n                    execution_count += T.execution_count\n                FROM [{ConnectionInfo.SchemaName}].WorkloadSummary AS WS\n                INNER JOIN #WorkloadSummary AS T\n                    ON  T.application_id = WS.application_id\n                    AND T.database_id    = WS.database_id\n                    AND T.host_id        = WS.host_id\n                    AND T.login_id       = WS.login_id;\n            \";\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                affectedRows += cmd.ExecuteNonQuery();\n            }\n\n            sql = $@\"\n                INSERT INTO [{ConnectionInfo.SchemaName}].WorkloadSummary \n                SELECT * \n                FROM #WorkloadSummary AS T\n                WHERE NOT EXISTS (\n                    SELECT *\n                    FROM [{ConnectionInfo.SchemaName}].WorkloadSummary AS WS\n                    WHERE   T.application_id = WS.application_id\n                        AND T.database_id    = WS.database_id\n                        AND T.host_id        = WS.host_id\n                        AND T.login_id       = WS.login_id\n                );\n            \";\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                affectedRows += cmd.ExecuteNonQuery();\n            }\n\n            logger.Info($\"Summary info written ({affectedRows} rows)\");\n        }\n\n        private void WriteExecutionDetails(SqlConnection conn, SqlTransaction tran, int current_interval_id)\n        {\n            int numRows;\n\n            using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                            SqlBulkCopyOptions.KeepIdentity |\n                                            SqlBulkCopyOptions.FireTriggers |\n                                            SqlBulkCopyOptions.CheckConstraints |\n                                            SqlBulkCopyOptions.TableLock,\n                                            tran))\n            {\n\n                bulkCopy.DestinationTableName = \"[\" + ConnectionInfo.SchemaName + \"].[WorkloadDetails]\";\n                bulkCopy.BatchSize = 1000;\n                bulkCopy.BulkCopyTimeout = 300;\n\n                var Table = from t in rawData.Keys\n                            from v in rawData[t]\n                            group new\n                            {\n                                v.Cpu_us,\n                                v.Duration_us,\n                                v.Event_time,\n                                v.Reads,\n                                v.Writes\n                            }\n                            by new\n                            {\n                                sql_hash = t.Sql_hash,\n                                application_id = t.Application_id,\n                                database_id = t.Database_id,\n                                host_id = t.Host_id,\n                                login_id = t.Login_id\n                            }\n                            into grp\n                            select new\n                            {\n                                interval_id = current_interval_id,\n\n                                grp.Key.sql_hash,\n                                grp.Key.application_id,\n                                grp.Key.database_id,\n                                grp.Key.host_id,\n                                grp.Key.login_id,\n\n                                avg_cpu_us = grp.Average(v => v.Cpu_us),\n                                min_cpu_us = grp.Min(v => v.Cpu_us),\n                                max_cpu_us = grp.Max(v => v.Cpu_us),\n                                sum_cpu_us = grp.Sum(v => v.Cpu_us),\n\n                                avg_reads = grp.Average(v => v.Reads),\n                                min_reads = grp.Min(v => v.Reads),\n                                max_reads = grp.Max(v => v.Reads),\n                                sum_reads = grp.Sum(v => v.Reads),\n\n                                avg_writes = grp.Average(v => v.Writes),\n                                min_writes = grp.Min(v => v.Writes),\n                                max_writes = grp.Max(v => v.Writes),\n                                sum_writes = grp.Sum(v => v.Writes),\n\n                                avg_duration_us = grp.Average(v => v.Duration_us),\n                                min_duration_us = grp.Min(v => v.Duration_us),\n                                max_duration_us = grp.Max(v => v.Duration_us),\n                                sum_duration_us = grp.Sum(v => v.Duration_us),\n\n                                execution_count = grp.Count()\n                            };\n\n                using (var reader = ObjectReader.Create(Table, \"interval_id\", \"sql_hash\", \"application_id\", \"database_id\", \"host_id\", \"login_id\", \"avg_cpu_us\", \"min_cpu_us\", \"max_cpu_us\", \"sum_cpu_us\", \"avg_reads\", \"min_reads\", \"max_reads\", \"sum_reads\", \"avg_writes\", \"min_writes\", \"max_writes\", \"sum_writes\", \"avg_duration_us\", \"min_duration_us\", \"max_duration_us\", \"sum_duration_us\", \"execution_count\"))\n                {\n                    bulkCopy.WriteToServer(reader);\n                }\n                numRows = rawData.Sum(x => x.Value.Count);\n                logger.Info($\"{numRows} rows aggregated\");\n                numRows = rawData.Count();\n                logger.Info($\"{numRows} rows written\");\n            }\n        }\n\n\t\tprivate void WriteExecutionErrors(SqlConnection conn, SqlTransaction tran, int current_interval_id)\n\t\t{\n\n\t\t\tif (errorData == null)\n            {\n                PrepareDataTables();\n            }\n\n            lock (errorData)\n\t\t\t{\n\t\t\t\tusing (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n\t\t\t\t\t\t\t\t\t\t\t\tSqlBulkCopyOptions.KeepIdentity |\n\t\t\t\t\t\t\t\t\t\t\t\tSqlBulkCopyOptions.FireTriggers |\n\t\t\t\t\t\t\t\t\t\t\t\tSqlBulkCopyOptions.CheckConstraints |\n\t\t\t\t\t\t\t\t\t\t\t\tSqlBulkCopyOptions.TableLock,\n\t\t\t\t\t\t\t\t\t\t\t\ttran))\n\t\t\t\t{\n\n\t\t\t\t\tbulkCopy.DestinationTableName = \"[\" + ConnectionInfo.SchemaName + \"].[Errors]\";\n\t\t\t\t\tbulkCopy.BatchSize = 1000;\n\t\t\t\t\tbulkCopy.BulkCopyTimeout = 300;\n\n\t\t\t\t\tvar Table = from t in errorData.AsEnumerable()\n\t\t\t\t\t\t\t\tgroup t by new\n\t\t\t\t\t\t\t\t{\n                                    type = t.Field<int>(\"type\"),\n\t\t\t\t\t\t\t\t\tmessage = t.Field<string>(\"message\")\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tinto grp\n\t\t\t\t\t\t\t\tselect new\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tinterval_id = current_interval_id,\n                                    error_type = ((WorkloadEvent.EventType)grp.Key.type).ToString(),\n\t\t\t\t\t\t\t\t\tgrp.Key.message,\n\t\t\t\t\t\t\t\t\terror_count = grp.Count()\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\tbulkCopy.WriteToServer(DataUtils.ToDataTable(Table));\n\t\t\t\t}\n\t\t\t\terrorData.Rows.Clear();\n\t\t\t}\n\t\t}\n\n\t\tprivate void WriteDictionary(Dictionary<string, int> values, SqlConnection conn, SqlTransaction tran, string name)\n        {\n\n            // create a temporary table\n\n            var sql = @\"\n                SELECT TOP(0) *\n                INTO #{0}\n                FROM [{1}].[{0}];\n            \";\n            sql = string.Format(sql, name, ConnectionInfo.SchemaName);\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n\n            // bulk insert into temporary\n            using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                                                SqlBulkCopyOptions.KeepIdentity |\n                                                                SqlBulkCopyOptions.FireTriggers |\n                                                                SqlBulkCopyOptions.CheckConstraints |\n                                                                SqlBulkCopyOptions.TableLock,\n                                                                tran))\n            {\n\n                bulkCopy.DestinationTableName = \"#\" + name;\n                bulkCopy.BatchSize = 1000;\n                bulkCopy.BulkCopyTimeout = 300;\n                bulkCopy.WriteToServer(DataUtils.ToDataTable(from t in values select new { t.Value, t.Key }));\n\n            }\n\n            // merge new data\n\n            sql = @\"\n                INSERT INTO [{1}].[{0}s]\n                SELECT *\n                FROM #{0}s AS src\n                WHERE NOT EXISTS (\n                    SELECT *\n                    FROM [{1}].[{0}s] AS dst \n                    WHERE dst.[{0}_id] = src.[{0}_id]\n                );\n            \";\n            sql = string.Format(sql, name.Substring(0, name.Length - 1), ConnectionInfo.SchemaName);\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n        }\n\n        private void WriteNormalizedQueries(Dictionary<long, NormalizedQuery> values, SqlConnection conn, SqlTransaction tran)\n        {\n            // create a temporary table\n\n            var sql = @\"\n                SELECT TOP(0) *\n                INTO #NormalizedQueries\n                FROM [{0}].[NormalizedQueries];\n            \";\n            sql = string.Format(sql, ConnectionInfo.SchemaName);\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n\n            // bulk insert into temporary\n            using (var bulkCopy = new System.Data.SqlClient.SqlBulkCopy(conn,\n                                                                SqlBulkCopyOptions.KeepIdentity |\n                                                                SqlBulkCopyOptions.FireTriggers |\n                                                                SqlBulkCopyOptions.CheckConstraints |\n                                                                SqlBulkCopyOptions.TableLock,\n                                                                tran))\n            {\n\n                bulkCopy.DestinationTableName = \"#NormalizedQueries\";\n                bulkCopy.BatchSize = 1000;\n                bulkCopy.BulkCopyTimeout = 300;\n                bulkCopy.WriteToServer(DataUtils.ToDataTable(from t in values where t.Value != null select new { t.Value.Hash, t.Value.NormalizedText, t.Value.ExampleText }));\n\n            }\n\n            // merge new data\n\n            sql = @\"\n                INSERT INTO [{0}].[NormalizedQueries]\n                SELECT *\n                FROM #NormalizedQueries AS src\n                WHERE NOT EXISTS (\n                    SELECT *\n                    FROM [{0}].[NormalizedQueries] AS dst \n                    WHERE dst.[sql_hash] = src.[sql_hash]\n                );\n            \";\n            sql = string.Format(sql, ConnectionInfo.SchemaName);\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n\n            // Erase from memory all the normalized queries \n            // already written to the database. This should reduce\n            // the memory footprint quite a lot\n            foreach(var hash in values.Keys.ToList())\n            {\n                values[hash] = null;\n            }\n            // Run the Garbage Collector in a separate task\n            _ = Task.Factory.StartNew(() => InvokeGC());\n        }\n\n        private void InvokeGC()\n        {\n            GC.Collect();\n            GC.WaitForPendingFinalizers();\n        }\n\n        private int ComputeIntervalId(DateTime intervalTime)\n        {\n            // interval id is the number of seconds since 01/01/2000\n            return (int)intervalTime.Subtract(DateTime.MinValue.AddYears(1999)).TotalSeconds;\n        }\n\n        private int CreateInterval(SqlConnection conn, SqlTransaction tran, DateTime intervalTime)\n        {\n            var sql = @\"\n                UPDATE [{0}].[Intervals]\n                SET  end_time = @end_time\n                    ,duration_minutes = @duration_minutes\n                WHERE interval_id = @interval_id;\n                \n                IF @@ROWCOUNT = 0\n                    INSERT INTO [{0}].[Intervals] (interval_id, end_time, duration_minutes) \n                    VALUES (@interval_id, @end_time, @duration_minutes); \n            \";\n            sql = string.Format(sql, ConnectionInfo.SchemaName);\n\n            var interval_id = ComputeIntervalId(intervalTime);\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.Transaction = tran;\n                cmd.CommandText = sql;\n                _ = cmd.Parameters.AddWithValue(\"@interval_id\", interval_id);\n                _ = cmd.Parameters.AddWithValue(\"@end_time\", intervalTime);\n                _ = cmd.Parameters.AddWithValue(\"@duration_minutes\", Interval);\n                _ = cmd.ExecuteNonQuery();\n            }\n\n            // If this the first interval of the analysis, write\n            // a marker interval with duration = 0 \n            if (!FirstIntervalWritten)\n            {\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.Transaction = tran;\n                    cmd.CommandText = sql;\n                    _ = cmd.Parameters.AddWithValue(\"@interval_id\", interval_id - 1);\n                    _ = cmd.Parameters.AddWithValue(\"@end_time\", intervalTime.AddSeconds(-1));\n                    _ = cmd.Parameters.AddWithValue(\"@duration_minutes\", 0);\n                    _ = cmd.ExecuteNonQuery();\n                    FirstIntervalWritten = true;\n                }\n            }\n\n            return interval_id;\n        }\n\n        private void PrepareDataTables()\n        {\n            rawData = new ConcurrentDictionary<ExecutionDetailKey, List<ExecutionDetailValue>>();\n\t\t\terrorData = new DataTable();\n            _ = errorData.Columns.Add(\"type\", typeof(int));\n            _ = errorData.Columns.Add(\"message\", typeof(string));\n\t\t}\n\n        private void PrepareDictionaries()\n        {\n\t\t\tCreateTargetDatabase();\n\n\t\t\tusing (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n\n                var sql = string.Format(@\"SELECT * FROM [{0}].[Applications]\",ConnectionInfo.SchemaName);\n                AddAllRows(conn, sql, applications);\n\n                sql = string.Format(@\"SELECT * FROM [{0}].[Databases]\", ConnectionInfo.SchemaName);\n                AddAllRows(conn, sql, databases);\n\n                sql = string.Format(@\"SELECT * FROM [{0}].[Hosts]\", ConnectionInfo.SchemaName);\n                AddAllRows(conn, sql, hosts);\n\n                sql = string.Format(@\"SELECT * FROM [{0}].[Logins]\", ConnectionInfo.SchemaName);\n                AddAllRows(conn, sql, logins);\n            }\n        }\n\n        private void AddAllRows(SqlConnection conn, string sql,  Dictionary<string, int> d)\n        {\n            try\n            {\n\n                using (var adapter = new SqlDataAdapter(sql, conn))\n                {\n                    using (var ds = new DataSet())\n                    {\n                        _ = adapter.Fill(ds);\n                        var dt = ds.Tables[0];\n                        foreach (DataRow dr in dt.Rows)\n                        {\n                            d.Add((string)dr[1], (int)dr[0]);\n                        }\n                    }\n                }\n            }\n            catch(SqlException e)\n            {\n                logger.Trace(\"Unable to read saved classifiers from the analysis database: {0}\", e.Message);\n            }\n            catch(Exception e)\n            {\n                logger.Error(e.Message);\n                throw;\n            }\n        }\n\n        protected void CreateTargetTables()\n        {\n\t\t\tCreateTargetDatabase();\n\n\t\t\tvar sql = File.ReadAllText(WorkloadController.BaseLocation + \"\\\\Consumer\\\\Analysis\\\\DatabaseSchema.sql\");\n\n            sql = sql.Replace(\"{DatabaseName}\", ConnectionInfo.DatabaseName);\n            sql = sql.Replace(\"{SchemaName}\", ConnectionInfo.SchemaName);\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                conn.ChangeDatabase(ConnectionInfo.DatabaseName);\n\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = sql;\n                    _ = cmd.ExecuteNonQuery();\n                }\n\n                sql = \"IF OBJECT_ID('dbo.createAnalysisView') IS NULL EXEC('CREATE PROCEDURE dbo.createAnalysisView AS RETURN 0')\";\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = sql;\n                    _ = cmd.ExecuteNonQuery();\n                }\n\n                sql = File.ReadAllText(WorkloadController.BaseLocation + \"\\\\Consumer\\\\Analysis\\\\createAnalysisView.sql\");\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = sql;\n                    _ = cmd.ExecuteNonQuery();\n                }\n\n                // Invoke the stored procedure to create the workload comparison view\n                sql = @\"\n                    DECLARE @name1 sysname, @name2 sysname;\n\n                    SELECT @name1 = [1], @name2 = [2]\n                    FROM (\n                        SELECT TOP(2) OBJECT_SCHEMA_NAME(object_id) AS schema_name, ROW_NUMBER() OVER (ORDER BY create_date DESC) AS RN\n                        FROM sys.tables\n                        WHERE name = 'WorkloadDetails'\n                        ORDER BY create_date DESC\n                    ) AS src\n                    PIVOT( MIN(schema_name) FOR RN IN ([1], [2])) AS p;\n\n                    SELECT @name1 ,@name2\n\n                    IF OBJECT_ID(@name1 + '.WorkloadDetails') IS NOT NULL OR OBJECT_ID(@name2 + '.WorkloadDetails') IS NOT NULL\n                    BEGIN\n                        EXEC createAnalysisView @name1, @name2;\n                    END\n                \";\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = sql;\n                    _ = cmd.ExecuteNonQuery();\n                }\n            }\n        }\n\n\t\tprotected void CreateTargetDatabase()\n\t\t{\n\n\t\t\ttry\n\t\t\t{\n                var databaseName = ConnectionInfo.DatabaseName;\n                using (var conn = new SqlConnection())\n\t\t\t\t{\n                    // create a new connection to the target server \n                    // for the analysis database, on the master db\n                    // then create the target database if not available\n                    var ci = new SqlConnectionInfo(ConnectionInfo);\n                    ci.DatabaseName = \"master\";\n\t\t\t\t\tconn.ConnectionString = ConnectionInfo.ConnectionString();\n\t\t\t\t\tconn.Open();\n\n\t\t\t\t\tusing (var cmd = conn.CreateCommand())\n\t\t\t\t\t{\n\t\t\t\t\t\tvar createDb = @\"\n\t\t\t\t\t\tIF DB_ID(@name) IS NULL\n\t\t\t\t\t\tBEGIN\n\t\t\t\t\t\t    DECLARE @sql nvarchar(max); \n\t\t\t\t\t\t\tSET @sql = N'CREATE DATABASE ' + QUOTENAME(@name);\n\t\t\t\t\t\t\tEXEC sp_executesql @sql;\n\t\t\t\t\t\tEND\n\t\t\t\t\t\";\n\t\t\t\t\t\tcmd.CommandText = createDb;\n                        _ = cmd.Parameters.AddWithValue(\"@name\", databaseName);\n                        _ = cmd.ExecuteNonQuery();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n            catch(Exception e) {\n                logger.Warn(\"Unable to create the target database for the analysis\", e.Message);\n            }\n\n\t\t}\n\n        public void Dispose()\n        {\n            rawData?.Clear();\n            errorData?.Dispose();\n            counterData?.Dispose();\n            waitsData?.Dispose();\n        }\n\n        internal class ExecutionDetailKey : IEquatable<ExecutionDetailKey>\n        {\n            public long Sql_hash { get; set; }\n            public int Application_id { get; set; }\n            public int Database_id { get; set; }\n            public int Host_id { get; set; }\n            public int Login_id { get; set; }\n\n            public override int GetHashCode()\n            {\n                var hash = 497;\n                unchecked\n                {\n                    hash = (hash * 17) + Sql_hash.GetHashCode();\n                    hash = (hash * 17) + Application_id.GetHashCode();\n                    hash = (hash * 17) + Database_id.GetHashCode();\n                    hash = (hash * 17) + Host_id.GetHashCode();\n                    hash = (hash * 17) + Login_id.GetHashCode();\n                }\n                return hash;\n            }\n\n            public override bool Equals(object other)\n            {\n                return Equals(other as ExecutionDetailKey);\n            }\n\n            public bool Equals(ExecutionDetailKey other)\n            {\n                return other != null\n                    && Sql_hash.Equals(other.Sql_hash)\n                    && Application_id.Equals(other.Application_id)\n                    && Database_id.Equals(other.Database_id)\n                    && Host_id.Equals(other.Host_id)\n                    && Login_id.Equals(other.Login_id);\n            }\n        }\n\n        internal class ExecutionDetailValue\n        {\n            public DateTime Event_time { get; set; }\n            public long? Cpu_us { get; set; }\n            public long? Reads { get; set; }\n            public long? Writes { get; set; }\n            public long? Duration_us { get; set; }\n        }\n    }\n}\n\n"
  },
  {
    "path": "WorkloadTools/Consumer/Analysis/createAnalysisView.sql",
    "content": "﻿ALTER PROCEDURE [dbo].[createAnalysisView]\n\t@baselineSchema AS nvarchar(max),\n\t@replaySchema AS nvarchar(max)\nAS\nBEGIN\n\t\nSET NOCOUNT ON;\nSET TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n\nBEGIN TRY\n\tBEGIN TRAN;\n\tDECLARE @sql nvarchar(max);\n\n\t-- DROP PowerBI_WaitStats\n\tIF OBJECT_ID( QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WaitStats') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WaitStats') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\tIF OBJECT_ID( QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WaitStats') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WaitStats') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\n\t-- DROP PowerBI_WinPerfCounters\n\tIF OBJECT_ID( QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WinPerfCounters') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WinPerfCounters') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\tIF OBJECT_ID( QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WinPerfCounters') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WinPerfCounters') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\n\t-- DROP PowerBI_WorkloadData\n\tIF OBJECT_ID( QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WorkloadData') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WorkloadData') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\tIF OBJECT_ID( QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WorkloadData') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WorkloadData') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\n\t-- DROP PowerBI_WorkloadQueries\n\tIF OBJECT_ID( QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WorkloadQueries') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_WorkloadQueries') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\tIF OBJECT_ID( QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WorkloadQueries') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_WorkloadQueries') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\n\t-- DROP PowerBI_Time\n\tIF OBJECT_ID( QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_Time') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@baselineSchema) +'.'+ QUOTENAME('PowerBI_Time') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\tIF OBJECT_ID( QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_Time') ) IS NULL\n\tBEGIN\n\t\tSET @sql = 'CREATE VIEW ' + QUOTENAME(@replaySchema) +'.'+ QUOTENAME('PowerBI_Time') + ' AS SELECT 1 AS one '\n\t\tEXEC(@sql)\n\tEND\n\n\n\t-- CREATE VIEWS\n\t--===========================================================\n\tDECLARE @PowerBI_WorkloadQueries nvarchar(max) = N'\n\tALTER VIEW {0}.[PowerBI_WorkloadQueries] \n\tAS\n\tSELECT\n\t\tbNQ.[sql_hash] AS [Sql Hash],\n\t\tbNQ.[normalized_text] AS [Sql Normalized Text], \n\t\tbNQ.[example_text] AS [Sql Sample Text]\n\tFROM {0}.[NormalizedQueries] AS bNQ\n\t'\n\n\tIF @baselineSchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WorkloadQueries, '{0}', @baselineSchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tIF @replaySchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WorkloadQueries, '{0}', @replaySchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\n\t--===========================================================\n\tDECLARE @PowerBI_WinPerfCounters nvarchar(max) = N'\n\tALTER VIEW {0}.[PowerBI_WinPerfCounters]\n\tAS\n\tSELECT\n\t\tbPC.[counter_name] AS [Win Counter]\n\t\t,bPC.[min_counter_value] AS [Counter Min Value]\n\t\t,bPC.[max_counter_value] AS [Counter Max Value]\n\t\t,bPC.[avg_counter_value] AS [Counter Average Value]\n\t\t,bIn.[Elapsed Time (min)]\n\tFROM {0}.[PerformanceCounters] AS bPC\n\tINNER JOIN (\n\t\tSELECT\n\t\t\t[interval_id],\n\t\t\t[duration_minutes],\n\t\t\t[end_time],\n\t\t\tSUM([duration_minutes]) OVER(ORDER BY [end_time] ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [Elapsed Time (min)]\n\t\tFROM {0}.[Intervals]\n\t) AS bIn\n\t\tON bPC.[interval_id] = bIn.[interval_id]\n\t'\n\n\tIF @baselineSchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WinPerfCounters, '{0}', @baselineSchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tIF @replaySchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WinPerfCounters, '{0}', @replaySchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\n\t--===========================================================\n\tDECLARE @PowerBI_WorkloadData nvarchar(max) = N'\n\tALTER VIEW {0}.[PowerBI_WorkloadData]\n\tAS\n\tSELECT\n\t\tbWD.[sql_hash] AS [Sql Hash],\n\t\tbIn.[duration_minutes] AS [Interval Duration (min)], \n\t\tbIn.[end_time] AS [Interval End Time],\n\t\tbIn.[Elapsed Time (min)],\n\t\tbAp.[application_name] AS [Application], \n\t\tbDB.[database_name] AS [Database], \n\t\tbHS.[host_name] AS [Host], \n\t\tbLI.[login_name] AS [Login],\n\t\tbWD.[avg_cpu_us] AS [Avg Cpu (µs)], \n\t\tbWD.[min_cpu_us] AS [Min Cpu (µs)], \n\t\tbWD.[max_cpu_us] AS [Max Cpu (µs)], \n\t\tbWD.[sum_cpu_us] AS [Sum Cpu (µs)], \n\t\tbWD.[avg_reads] AS [Avg Reads], \n\t\tbWD.[min_reads] AS [Min Reads], \n\t\tbWD.[max_reads] AS [Max Reads], \n\t\tbWD.[sum_reads] AS [Sum Reads], \n\t\tbWD.[avg_writes] AS [Avg Writes], \n\t\tbWD.[min_writes] AS [Min Writes], \n\t\tbWD.[max_writes] AS [Max Writes], \n\t\tbWD.[sum_writes] AS [Sum Writes], \n\t\tbWD.[avg_duration_us] AS [Avg Duration (µs)], \n\t\tbWD.[min_duration_us] AS [Min Duration (µs)], \n\t\tbWD.[max_duration_us] AS [Max Duration (µs)], \n\t\tbWD.[sum_duration_us] AS [Sum Duration (µs)], \n\t\tbWD.[execution_count] AS [Execution Count]\n\tFROM {0}.WorkloadDetails AS bWD\n\tINNER JOIN (\n\t\tSELECT\n\t\t\t[interval_id],\n\t\t\t[duration_minutes],\n\t\t\t[end_time],\n\t\t\tSUM([duration_minutes]) OVER(ORDER BY [end_time] ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [Elapsed Time (min)]\n\t\tFROM {0}.[Intervals]\n\t) AS bIn\n\t\tON bIn.[interval_id] = bWD.[interval_id]\n\tINNER JOIN {0}.Applications AS bAp\n\t\tON bAp.[application_id] = bWD.[application_id]\n\tINNER JOIN {0}.Databases AS bDB\n\t\tON bDB.[database_id] = [bWD].database_id\n\tINNER JOIN {0}.Hosts AS bHS\n\t\tON bHS.[host_id] = bWD.[host_id]\n\tINNER JOIN {0}.Logins AS bLI\n\t\tON bLI.[login_id] = bWD.[login_id]\n\t'\n\n\tIF @baselineSchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WorkloadData, '{0}', @baselineSchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tIF @replaySchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WorkloadData, '{0}', @replaySchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\n\t--===========================================================\n\tDECLARE @PowerBI_WaitStats nvarchar(max) = N'\n\tALTER VIEW {0}.[PowerBI_WaitStats]\n\tAS\n\tSELECT\n\t\t bWS.[interval_id]\n\t\t,bWS.[wait_type] AS [Wait Type]\n\t\t,bWS.[wait_sec]*1000 AS [Wait Time (µs)]\n\t\t,bWS.[resource_sec]*1000 AS [Wait Time Resource (µs)]\n\t\t,bWS.[signal_sec]*1000 AS [Wait Time Signal (µs)]\n\t\t,bWS.[wait_count] AS [Wait Count]\n\t\t,bIn.[Elapsed Time (min)]\n\t\t,COALESCE(WsCat.[Wait Category],''<n/a>'') AS [Wait Type Category]\n\tFROM {0}.[WaitStats] AS bWS\n\tINNER JOIN (\n\t\tSELECT\n\t\t\t[interval_id],\n\t\t\t[duration_minutes],\n\t\t\t[end_time],\n\t\t\tSUM([duration_minutes]) OVER(ORDER BY [end_time] ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [Elapsed Time (min)]\n\t\tFROM {0}.[Intervals]\n\t) AS bIn\n\t\tON bIn.[interval_id] = bWS.[interval_id]\n\tLEFT JOIN (\n\tVALUES \n\t\t (''HADR_AG_MUTEX'',''Replication'',0)\n\t\t,(''HADR_AR_CRITICAL_SECTION_ENTRY'',''Replication'',0)\n\t\t,(''HADR_AR_MANAGER_MUTEX'',''Replication'',0)\n\t\t,(''HADR_AR_UNLOAD_COMPLETED'',''Replication'',0)\n\t\t,(''HADR_ARCONTROLLER_NOTIFICATIONS_SUBSCRIBER_LIST'',''Replication'',0)\n\t\t,(''HADR_BACKUP_BULK_LOCK'',''Replication'',0)\n\t\t,(''HADR_BACKUP_QUEUE'',''Replication'',0)\n\t\t,(''HADR_CLUSAPI_CALL'',''Replication'',0)\n\t\t,(''HADR_COMPRESSED_CACHE_SYNC'',''Replication'',0)\n\t\t,(''HADR_CONNECTIVITY_INFO'',''Replication'',0)\n\t\t,(''HADR_DATABASE_FLOW_CONTROL'',''Replication'',0)\n\t\t,(''HADR_DATABASE_VERSIONING_STATE'',''Replication'',0)\n\t\t,(''HADR_DATABASE_WAIT_FOR_RECOVERY'',''Replication'',0)\n\t\t,(''HADR_DATABASE_WAIT_FOR_RESTART'',''Replication'',0)\n\t\t,(''HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'',''Replication'',0)\n\t\t,(''HADR_DB_COMMAND'',''Replication'',0)\n\t\t,(''HADR_DB_OP_COMPLETION_SYNC'',''Replication'',0)\n\t\t,(''HADR_DB_OP_START_SYNC'',''Replication'',0)\n\t\t,(''HADR_DBR_SUBSCRIBER'',''Replication'',0)\n\t\t,(''HADR_DBR_SUBSCRIBER_FILTER_LIST'',''Replication'',0)\n\t\t,(''HADR_DBSEEDING'',''Replication'',0)\n\t\t,(''HADR_DBSEEDING_LIST'',''Replication'',0)\n\t\t,(''HADR_DBSTATECHANGE_SYNC'',''Replication'',0)\n\t\t,(''HADR_FABRIC_CALLBACK'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_BLOCK_FLUSH'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_FILE_CLOSE'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_FILE_REQUEST'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_IOMGR'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_IOMGR_IOCOMPLETION'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_MANAGER'',''Replication'',0)\n\t\t,(''HADR_FILESTREAM_PREPROC'',''Replication'',0)\n\t\t,(''HADR_GROUP_COMMIT'',''Replication'',0)\n\t\t,(''HADR_LOGCAPTURE_SYNC'',''Replication'',0)\n\t\t,(''HADR_LOGCAPTURE_WAIT'',''Replication'',0)\n\t\t,(''HADR_LOGPROGRESS_SYNC'',''Replication'',0)\n\t\t,(''HADR_NOTIFICATION_DEQUEUE'',''Replication'',0)\n\t\t,(''HADR_NOTIFICATION_WORKER_EXCLUSIVE_ACCESS'',''Replication'',0)\n\t\t,(''HADR_NOTIFICATION_WORKER_STARTUP_SYNC'',''Replication'',0)\n\t\t,(''HADR_NOTIFICATION_WORKER_TERMINATION_SYNC'',''Replication'',0)\n\t\t,(''HADR_PARTNER_SYNC'',''Replication'',0)\n\t\t,(''HADR_READ_ALL_NETWORKS'',''Replication'',0)\n\t\t,(''HADR_RECOVERY_WAIT_FOR_CONNECTION'',''Replication'',0)\n\t\t,(''HADR_RECOVERY_WAIT_FOR_UNDO'',''Replication'',0)\n\t\t,(''HADR_REPLICAINFO_SYNC'',''Replication'',0)\n\t\t,(''HADR_SEEDING_CANCELLATION'',''Replication'',0)\n\t\t,(''HADR_SEEDING_FILE_LIST'',''Replication'',0)\n\t\t,(''HADR_SEEDING_LIMIT_BACKUPS'',''Replication'',0)\n\t\t,(''HADR_SEEDING_SYNC_COMPLETION'',''Replication'',0)\n\t\t,(''HADR_SEEDING_TIMEOUT_TASK'',''Replication'',0)\n\t\t,(''HADR_SEEDING_WAIT_FOR_COMPLETION'',''Replication'',0)\n\t\t,(''HADR_SYNC_COMMIT'',''Replication'',0)\n\t\t,(''HADR_SYNCHRONIZING_THROTTLE'',''Replication'',0)\n\t\t,(''HADR_TDS_LISTENER_SYNC'',''Replication'',0)\n\t\t,(''HADR_TDS_LISTENER_SYNC_PROCESSING'',''Replication'',0)\n\t\t,(''HADR_THROTTLE_LOG_RATE_GOVERNOR'',''Log Rate Governor'',0)\n\t\t,(''HADR_TIMER_TASK'',''Replication'',0)\n\t\t,(''HADR_TRANSPORT_DBRLIST'',''Replication'',0)\n\t\t,(''HADR_TRANSPORT_FLOW_CONTROL'',''Replication'',0)\n\t\t,(''HADR_TRANSPORT_SESSION'',''Replication'',0)\n\t\t,(''HADR_WORK_POOL'',''Replication'',0)\n\t\t,(''HADR_WORK_QUEUE'',''Replication'',0)\n\t\t,(''HADR_XRF_STACK_ACCESS'',''Replication'',0)\n\t\t,(''INSTANCE_LOG_RATE_GOVERNOR'',''Log Rate Governor'',0)\n\t\t,(''BROKER_TASK_SUBMIT'',''Service Broker'',0)\n\t\t,(''BROKER_TO_FLUSH'',''Service Broker'',0)\n\t\t,(''BROKER_TRANSMISSION_OBJECT'',''Service Broker'',0)\n\t\t,(''BROKER_TRANSMISSION_TABLE'',''Service Broker'',0)\n\t\t,(''BROKER_TRANSMISSION_WORK'',''Service Broker'',0)\n\t\t,(''BROKER_FORWARDER'',''Service Broker'',0)\n\t\t,(''CXCONSUMER'',''Parallelism'',0)\n\t\t,(''DTCNEW_ENLIST'',''Transaction'',0)\n\t\t,(''DTCNEW_PREPARE'',''Transaction'',0)\n\t\t,(''DTCNEW_RECOVERY'',''Transaction'',0)\n\t\t,(''DTCNEW_TM'',''Transaction'',0)\n\t\t,(''DTCNEW_TRANSACTION_ENLISTMENT'',''Transaction'',0)\n\t\t,(''DTCPNTSYNC'',''Transaction'',0)\n\t\t,(''BROKER_DISPATCHER'',''Service Broker'',0)\n\t\t,(''BROKER_SERVICE'',''Service Broker'',0)\n\t\t,(''BROKER_START'',''Service Broker'',0)\n\t\t,(''BROKER_TASK_SHUTDOWN'',''Service Broker'',0)\n\t\t,(''EXTERNAL_SCRIPT_NETWORK_IOF'',''Network IO'',0)\n\t\t,(''FT_COMPROWSET_RWLOCK'',''Full Text Search'',0)\n\t\t,(''FT_IFTS_RWLOCK'',''Full Text Search'',0)\n\t\t,(''FT_IFTS_SCHEDULER_IDLE_WAIT'',''Idle'',0)\n\t\t,(''FT_IFTSHC_MUTEX'',''Full Text Search'',0)\n\t\t,(''FT_IFTSISM_MUTEX'',''Full Text Search'',0)\n\t\t,(''FT_MASTER_MERGE'',''Full Text Search'',0)\n\t\t,(''FT_MASTER_MERGE_COORDINATOR'',''Full Text Search'',0)\n\t\t,(''FT_METADATA_MUTEX'',''Full Text Search'',0)\n\t\t,(''FT_PROPERTYLIST_CACHE'',''Full Text Search'',0)\n\t\t,(''IO_QUEUE_LIMIT'',''Other Disk IO'',0)\n\t\t,(''IO_RETRY'',''Other Disk IO'',0)\n\t\t,(''POOL_LOG_RATE_GOVERNOR'',''Log Rate Governor'',0)\n\t\t,(''PREEMPTIVE_ABR'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CLOSEBACKUPMEDIA'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CLOSEBACKUPTAPE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CLOSEBACKUPVDIDEVICE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CLUSAPI_CLUSTERRESOURCECONTROL'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_COCREATEINSTANCE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_COGETCLASSOBJECT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_CREATEACCESSOR'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_DELETEROWS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_GETCOMMANDTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_GETDATA'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_GETNEXTROWS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_GETRESULT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_GETROWSBYBOOKMARK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBFLUSH'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBLOCKREGION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBREADAT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBSETSIZE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBSTAT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBUNLOCKREGION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_LBWRITEAT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_QUERYINTERFACE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_RELEASE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_RELEASEACCESSOR'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_RELEASEROWS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_RELEASESESSION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_RESTARTPOSITION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_SEQSTRMREAD'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_SEQSTRMREADANDWRITE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_SETDATAFAILURE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_SETPARAMETERINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_SETPARAMETERPROPERTIES'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMLOCKREGION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMSEEKANDREAD'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMSEEKANDWRITE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMSETSIZE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMSTAT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_COM_STRMUNLOCKREGION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CONSOLEWRITE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_CREATEPARAM'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DEBUG'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSADDLINK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSLINKEXISTCHECK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSLINKHEALTHCHECK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSREMOVELINK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSREMOVEROOT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSROOTFOLDERCHECK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSROOTINIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DFSROOTSHARECHECK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_ABORT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_ABORTREQUESTDONE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_BEGINTRANSACTION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_COMMITREQUESTDONE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_ENLIST'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_DTC_PREPAREREQUESTDONE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_FILESIZEGET'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_FSAOLEDB_ABORTTRANSACTION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_FSAOLEDB_COMMITTRANSACTION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_FSAOLEDB_STARTTRANSACTION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_FSRECOVER_UNCONDITIONALUNDO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_GETRMINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_HADR_LEASE_MECHANISM'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_HTTP_EVENT_WAIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_HTTP_REQUEST'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_LOCKMONITOR'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_MSS_RELEASE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_ODBCOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLE_UNINIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_ABORTORCOMMITTRAN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_ABORTTRAN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_GETDATASOURCE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_GETLITERALINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_GETPROPERTIES'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_GETPROPERTYINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_GETSCHEMALOCK'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_JOINTRANSACTION'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_RELEASE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDB_SETPROPERTIES'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OLEDBOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_ACCEPTSECURITYCONTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_ACQUIRECREDENTIALSHANDLE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_AUTHENTICATIONOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_AUTHORIZATIONOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_AUTHZGETINFORMATIONFROMCONTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_AUTHZINITIALIZECONTEXTFROMSID'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_AUTHZINITIALIZERESOURCEMANAGER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_BACKUPREAD'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CLOSEHANDLE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CLUSTEROPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_COMOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_COMPLETEAUTHTOKEN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_COPYFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CREATEDIRECTORY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CREATEFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CRYPTACQUIRECONTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CRYPTIMPORTKEY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_CRYPTOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DECRYPTMESSAGE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DELETEFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DELETESECURITYCONTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DEVICEIOCONTROL'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DEVICEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DIRSVC_NETWORKOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DISCONNECTNAMEDPIPE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DOMAINSERVICESOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DSGETDCNAME'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_DTCOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_ENCRYPTMESSAGE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FILEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FINDFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FLUSHFILEBUFFERS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FORMATMESSAGE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FREECREDENTIALSHANDLE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_FREELIBRARY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GENERICOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETADDRINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETCOMPRESSEDFILESIZE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETDISKFREESPACE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETFILEATTRIBUTES'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETFILESIZE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETFINALFILEPATHBYHANDLE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETLONGPATHNAME'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETPROCADDRESS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETVOLUMENAMEFORVOLUMEMOUNTPOINT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_GETVOLUMEPATHNAME'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_INITIALIZESECURITYCONTEXT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_LIBRARYOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_LOADLIBRARY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_LOGONUSER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_LOOKUPACCOUNTSID'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_MESSAGEQUEUEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_MOVEFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETGROUPGETUSERS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETLOCALGROUPGETMEMBERS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETUSERGETGROUPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETUSERGETLOCALGROUPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETUSERMODALSGET'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICYFREE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_OPENDIRECTORY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_PDH_WMI_INIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_PIPEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_PROCESSOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_QUERYCONTEXTATTRIBUTES'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_QUERYREGISTRY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_QUERYSECURITYCONTEXTTOKEN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_REMOVEDIRECTORY'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_REPORTEVENT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_REVERTTOSELF'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_RSFXDEVICEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SECURITYOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SERVICEOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SETENDOFFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SETFILEPOINTER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SETFILEVALIDDATA'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SETNAMEDSECURITYINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SQLCLROPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_SQMLAUNCH'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_VERIFYSIGNATURE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_VERIFYTRUST'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_VSSOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_WAITFORSINGLEOBJECT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_WINSOCKOPS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_WRITEFILE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_WRITEFILEGATHER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_OS_WSASETLASTERROR'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_REENLIST'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_RESIZELOG'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_ROLLFORWARDREDO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_ROLLFORWARDUNDO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SB_STOPENDPOINT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SERVER_STARTUP'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SETRMINFO'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SHAREDMEM_GETDATA'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SNIOPEN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SOSHOST'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SOSTESTING'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_SP_SERVER_DIAGNOSTICS'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_STARTRM'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_STREAMFCB_CHECKPOINT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_STREAMFCB_RECOVER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_STRESSDRIVER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_TESTING'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_TRANSIMPORT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_UNMARSHALPROPAGATIONTOKEN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_VSS_CREATESNAPSHOT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_VSS_CREATEVOLUMESNAPSHOT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_CALLBACKEXECUTE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_CX_FILE_OPEN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_CX_HTTP_CALL'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_DISPATCHER'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_ENGINEINIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_GETTARGETSTATE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_SESSIONCOMMIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_TARGETFINALIZE'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_TARGETINIT'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XE_TIMERRUN'',''Preemptive'',0)\n\t\t,(''PREEMPTIVE_XETESTING'',''Preemptive'',0)\n\t\t,(''PWAIT_HADR_ACTION_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADR_CHANGE_NOTIFIER_TERMINATION_SYNC'',''Replication'',0)\n\t\t,(''PWAIT_HADR_CLUSTER_INTEGRATION'',''Replication'',0)\n\t\t,(''PWAIT_HADR_FAILOVER_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADR_JOIN'',''Replication'',0)\n\t\t,(''PWAIT_HADR_OFFLINE_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADR_ONLINE_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADR_POST_ONLINE_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADR_SERVER_READY_CONNECTIONS'',''Replication'',0)\n\t\t,(''PWAIT_HADR_WORKITEM_COMPLETED'',''Replication'',0)\n\t\t,(''PWAIT_HADRSIM'',''Replication'',0)\n\t\t,(''PWAIT_RESOURCE_SEMAPHORE_FT_PARALLEL_QUERY_SYNC'',''Full Text Search'',0)\n\t\t,(''LCK_M_BU_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_BU_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_IS_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_IS_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_IU_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_IU_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_IX_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_IX_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RIn_NL_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RIn_NL_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RIn_S_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RIn_S_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RIn_U_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RIn_U_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RIn_X_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RIn_X_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RS_S_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RS_S_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RS_U_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RS_U_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RX_S_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RX_S_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RX_U_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RX_U_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_RX_X_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_RX_X_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_S_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_S_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_SCH_M_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_SCH_M_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_SCH_S_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_SCH_S_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''MEMORY_ALLOCATION_EXT'',''Memory'',0)\n\t\t,(''MEMORY_GRANT_UPDATE'',''Memory'',0)\n\t\t,(''LCK_M_SIU_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_SIU_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_SIX_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_SIX_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_U_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_U_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_UIX_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_UIX_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LCK_M_X_ABORT_BLOCKERS'',''Lock'',0)\n\t\t,(''LCK_M_X_LOW_PRIORITY'',''Lock'',0)\n\t\t,(''LOGMGR_PMM_LOG'',''Tran Log IO'',0)\n\t\t,(''REPL_HISTORYCACHE_ACCESS'',''Replication'',0)\n\t\t,(''REPL_TRANFSINFO_ACCESS'',''Replication'',0)\n\t\t,(''REPL_TRANHASHTABLE_ACCESS'',''Replication'',0)\n\t\t,(''REPL_TRANTEXTINFO_ACCESS'',''Replication'',0)\n\t\t,(''RESERVED_MEMORY_ALLOCATION_EXT'',''Memory'',0)\n\t\t,(''SLEEP_MASTERDBREADY'',''Idle'',0)\n\t\t,(''SLEEP_MASTERMDREADY'',''Idle'',0)\n\t\t,(''SLEEP_MASTERUPGRADED'',''Idle'',0)\n\t\t,(''SLEEP_MEMORYPOOL_ALLOCATEPAGES'',''Idle'',0)\n\t\t,(''SLEEP_RETRY_VIRTUALALLOC'',''Idle'',0)\n\t\t,(''SQLTRACE_FILE_BUFFER'',''Tracing'',0)\n\t\t,(''SQLTRACE_FILE_READ_IO_COMPLETION'',''Tracing'',0)\n\t\t,(''SQLTRACE_FILE_WRITE_IO_COMPLETION'',''Tracing'',0)\n\t\t,(''SQLTRACE_INCREMENTAL_FLUSH_SLEEP'',''Idle'',0)\n\t\t,(''SQLTRACE_PENDING_BUFFER_WRITERS'',''Tracing'',0)\n\t\t,(''TRACE_EVTNOTIF'',''Tracing'',0)\n\t\t,(''WRITE_COMPLETION'',''Other Disk IO'',0)\n\t\t,(''SLEEP_WORKSPACE_ALLOCATEPAGE'',''Idle'',0)\n\t\t,(''SLEEP_BUFFERPOOL_HELPLW'',''Idle'',0)\n\t\t,(''ABR'',''Other'',0)\n\t\t,(''ASSEMBLY_LOAD'',''SQLCLR'',0)\n\t\t,(''ASYNC_DISKPOOL_LOCK'',''Buffer I/O'',0)\n\t\t,(''ASYNC_IO_COMPLETION'',''Other Disk IO'',0)\n\t\t,(''ASYNC_NETWORK_IO'',''Network IO'',0)\n\t\t,(''BACKUP'',''Backup'',0)\n\t\t,(''BACKUP_CLIENTLOCK'',''Backup'',0)\n\t\t,(''BACKUP_OPERATOR'',''Backup'',0)\n\t\t,(''BACKUPBUFFER'',''Backup'',0)\n\t\t,(''BACKUPIO'',''Other Disk IO'',0)\n\t\t,(''BACKUPTHREAD'',''Backup'',0)\n\t\t,(''BAD_PAGE_PROCESS'',''Other'',0)\n\t\t,(''BROKER_CONNECTION_RECEIVE_TASK'',''Service Broker'',0)\n\t\t,(''BROKER_ENDPOINT_STATE_MUTEX'',''Service Broker'',0)\n\t\t,(''BROKER_EVENTHANDLER'',''Service Broker'',1)\n\t\t,(''BROKER_INIT'',''Service Broker'',0)\n\t\t,(''BROKER_MASTERSTART'',''Service Broker'',0)\n\t\t,(''BROKER_RECEIVE_WAITFOR'',''User Wait'',1)\n\t\t,(''BROKER_REGISTERALLENDPOINTS'',''Service Broker'',0)\n\t\t,(''BROKER_SHUTDOWN'',''Service Broker'',0)\n\t\t,(''BROKER_TASK_STOP'',''Service Broker'',0)\n\t\t,(''BROKER_TRANSMITTER'',''Service Broker'',1)\n\t\t,(''BUILTIN_HASHKEY_MUTEX'',''Other'',0)\n\t\t,(''CHECK_PRINT_RECORD'',''Other'',0)\n\t\t,(''CHECKPOINT_QUEUE'',''Idle'',1)\n\t\t,(''CHKPT'',''Tran Log IO'',1)\n\t\t,(''CLR_AUTO_EVENT'',''SQL CLR'',1)\n\t\t,(''CLR_CRST'',''SQL CLR'',0)\n\t\t,(''CLR_JOIN'',''SQL CLR'',0)\n\t\t,(''CLR_MANUAL_EVENT'',''SQL CLR'',1)\n\t\t,(''CLR_MEMORY_SPY'',''SQL CLR'',0)\n\t\t,(''CLR_MONITOR'',''SQL CLR'',0)\n\t\t,(''CLR_RWLOCK_READER'',''SQL CLR'',0)\n\t\t,(''CLR_RWLOCK_WRITER'',''SQL CLR'',0)\n\t\t,(''CLR_SEMAPHORE'',''SQL CLR'',0)\n\t\t,(''CLR_TASK_START'',''SQL CLR'',0)\n\t\t,(''CLRHOST_STATE_ACCESS'',''SQL CLR'',0)\n\t\t,(''CMEMPARTITIONED'',''Memory'',0)\n\t\t,(''CMEMTHREAD'',''Memory'',0)\n\t\t,(''CPU'',''CPU'',0)\n\t\t,(''CURSOR'',''Other'',0)\n\t\t,(''CURSOR_ASYNC'',''Other'',0)\n\t\t,(''CXPACKET'',''Parallelism'',1)\n\t\t,(''DAC_INIT'',''Other'',0)\n\t\t,(''DBCC_COLUMN_TRANSLATION_CACHE'',''Other'',0)\n\t\t,(''DBMIRROR_DBM_EVENT'',''Mirroring'',0)\n\t\t,(''DBMIRROR_DBM_MUTEX'',''Mirroring'',0)\n\t\t,(''DBMIRROR_EVENTS_QUEUE'',''Mirroring'',0)\n\t\t,(''DBMIRROR_SEND'',''Mirroring'',0)\n\t\t,(''DBMIRROR_WORKER_QUEUE'',''Mirroring'',0)\n\t\t,(''DBMIRRORING_CMD'',''Mirroring'',0)\n\t\t,(''DBTABLE'',''Other'',0)\n\t\t,(''DEADLOCK_ENUM_MUTEX'',''Latch'',0)\n\t\t,(''DEADLOCK_TASK_SEARCH'',''Other'',0)\n\t\t,(''DEBUG'',''Other'',0)\n\t\t,(''DISABLE_VERSIONING'',''Other'',0)\n\t\t,(''DISKIO_SUSPEND'',''Backup'',0)\n\t\t,(''DLL_LOADING_MUTEX'',''Other'',0)\n\t\t,(''DROPTEMP'',''Other'',0)\n\t\t,(''DTC'',''Transaction'',0)\n\t\t,(''DTC_ABORT_REQUEST'',''Transaction'',0)\n\t\t,(''DTC_RESOLVE'',''Transaction'',0)\n\t\t,(''DTC_STATE'',''Transaction'',0)\n\t\t,(''DTC_TMDOWN_REQUEST'',''Transaction'',0)\n\t\t,(''DTC_WAITFOR_OUTCOME'',''Transaction'',0)\n\t\t,(''DUMP_LOG_COORDINATOR'',''Other'',0)\n\t\t,(''DUMP_LOG_COORDINATOR_QUEUE'',''Other'',0)\n\t\t,(''DUMPTRIGGER'',''Other'',0)\n\t\t,(''EC'',''Other'',0)\n\t\t,(''EE_PMOLOCK'',''Memory'',0)\n\t\t,(''EE_SPECPROC_MAP_INIT'',''Other'',0)\n\t\t,(''ENABLE_VERSIONING'',''Other'',0)\n\t\t,(''ERROR_REPORTING_MANAGER'',''Other'',0)\n\t\t,(''EXCHANGE'',''Parallelism'',1)\n\t\t,(''EXECSYNC'',''Parallelism'',1)\n\t\t,(''EXECUTION_PIPE_EVENT_INTERNAL'',''Other'',0)\n\t\t,(''FAILPOINT'',''Other'',0)\n\t\t,(''FCB_REPLICA_READ'',''Replication'',0)\n\t\t,(''FCB_REPLICA_WRITE'',''Replication'',0)\n\t\t,(''FS_GARBAGE_COLLECTOR_SHUTDOWN'',''SQLCLR'',0)\n\t\t,(''FSAGENT'',''Idle'',1)\n\t\t,(''FT_RESTART_CRAWL'',''Full Text Search'',0)\n\t\t,(''FT_RESUME_CRAWL'',''Other'',0)\n\t\t,(''FULLTEXT GATHERER'',''Full Text Search'',0)\n\t\t,(''GUARDIAN'',''Other'',0)\n\t\t,(''HTTP_ENDPOINT_COLLCREATE'',''Other'',0)\n\t\t,(''HTTP_ENUMERATION'',''Other'',0)\n\t\t,(''HTTP_START'',''Other'',0)\n\t\t,(''IMP_IMPORT_MUTEX'',''Other'',0)\n\t\t,(''IMPPROV_IOWAIT'',''Other'',0)\n\t\t,(''INDEX_USAGE_STATS_MUTEX'',''Latch'',0)\n\t\t,(''INTERNAL_TESTING'',''Other'',0)\n\t\t,(''IO_AUDIT_MUTEX'',''Other'',0)\n\t\t,(''IO_COMPLETION'',''Other Disk IO'',0)\n\t\t,(''KSOURCE_WAKEUP'',''Idle'',1)\n\t\t,(''KTM_ENLISTMENT'',''Other'',0)\n\t\t,(''KTM_RECOVERY_MANAGER'',''Other'',0)\n\t\t,(''KTM_RECOVERY_RESOLUTION'',''Other'',0)\n\t\t,(''LATCH_DT'',''Latch'',0)\n\t\t,(''LATCH_EX'',''Latch'',0)\n\t\t,(''LATCH_KP'',''Latch'',0)\n\t\t,(''LATCH_NL'',''Latch'',0)\n\t\t,(''LATCH_SH'',''Latch'',0)\n\t\t,(''LATCH_UP'',''Latch'',0)\n\t\t,(''LAZYWRITER_SLEEP'',''Idle'',1)\n\t\t,(''LCK_M_BU'',''Lock'',0)\n\t\t,(''LCK_M_IS'',''Lock'',0)\n\t\t,(''LCK_M_IU'',''Lock'',0)\n\t\t,(''LCK_M_IX'',''Lock'',0)\n\t\t,(''LCK_M_RIn_NL'',''Lock'',0)\n\t\t,(''LCK_M_RIn_S'',''Lock'',0)\n\t\t,(''LCK_M_RIn_U'',''Lock'',0)\n\t\t,(''LCK_M_RIn_X'',''Lock'',0)\n\t\t,(''LCK_M_RS_S'',''Lock'',0)\n\t\t,(''LCK_M_RS_U'',''Lock'',0)\n\t\t,(''LCK_M_RX_S'',''Lock'',0)\n\t\t,(''LCK_M_RX_U'',''Lock'',0)\n\t\t,(''LCK_M_RX_X'',''Lock'',0)\n\t\t,(''LCK_M_S'',''Lock'',0)\n\t\t,(''LCK_M_SCH_M'',''Lock'',0)\n\t\t,(''LCK_M_SCH_S'',''Lock'',0)\n\t\t,(''LCK_M_SIU'',''Lock'',0)\n\t\t,(''LCK_M_SIX'',''Lock'',0)\n\t\t,(''LCK_M_U'',''Lock'',0)\n\t\t,(''LCK_M_UIX'',''Lock'',0)\n\t\t,(''LCK_M_X'',''Lock'',0)\n\t\t,(''LOGBUFFER'',''Tran Log IO'',0)\n\t\t,(''LOGMGR'',''Tran Log IO'',0)\n\t\t,(''LOGMGR_FLUSH'',''Tran Log IO'',0)\n\t\t,(''LOGMGR_QUEUE'',''Idle'',1)\n\t\t,(''LOGMGR_RESERVE_APPEND'',''Tran Log IO'',0)\n\t\t,(''LOWFAIL_MEMMGR_QUEUE'',''Memory'',0)\n\t\t,(''MIRROR_SEND_MESSAGE'',''Other'',0)\n\t\t,(''MISCELLANEOUS'',''Other'',0)\n\t\t,(''MSQL_DQ'',''Network I/O'',0)\n\t\t,(''MSQL_SYNC_PIPE'',''Other'',0)\n\t\t,(''MSQL_XACT_MGR_MUTEX'',''Transaction'',0)\n\t\t,(''MSQL_XACT_MUTEX'',''Transaction'',0)\n\t\t,(''MSQL_XP'',''Other'',0)\n\t\t,(''MSSEARCH'',''Full Text Search'',0)\n\t\t,(''NET_WAITFOR_PACKET'',''Network IO'',0)\n\t\t,(''OLEDB'',''Network I/O'',0)\n\t\t,(''ONDEMAND_TASK_QUEUE'',''Idle'',1)\n\t\t,(''PAGEIOLATCH_DT'',''Buffer IO'',0)\n\t\t,(''PAGEIOLATCH_EX'',''Buffer IO'',0)\n\t\t,(''PAGEIOLATCH_KP'',''Buffer IO'',0)\n\t\t,(''PAGEIOLATCH_NL'',''Buffer IO'',0)\n\t\t,(''PAGEIOLATCH_SH'',''Buffer IO'',0)\n\t\t,(''PAGEIOLATCH_UP'',''Buffer IO'',0)\n\t\t,(''PAGELATCH_DT'',''Buffer Latch'',0)\n\t\t,(''PAGELATCH_EX'',''Buffer Latch'',0)\n\t\t,(''PAGELATCH_KP'',''Buffer Latch'',0)\n\t\t,(''PAGELATCH_NL'',''Buffer Latch'',0)\n\t\t,(''PAGELATCH_SH'',''Buffer Latch'',0)\n\t\t,(''PAGELATCH_UP'',''Buffer Latch'',0)\n\t\t,(''PARALLEL_BACKUP_QUEUE'',''Other'',0)\n\t\t,(''PRINT_ROLLBACK_PROGRESS'',''Other'',0)\n\t\t,(''QNMANAGER_ACQUIRE'',''Other'',0)\n\t\t,(''QPJOB_KILL'',''Other'',0)\n\t\t,(''QPJOB_WAITFOR_ABORT'',''Other'',0)\n\t\t,(''QRY_MEM_GRANT_INFO_MUTEX'',''Other'',0)\n\t\t,(''QUERY_ERRHDL_SERVICE_DONE'',''Other'',0)\n\t\t,(''QUERY_EXECUTION_INDEX_SORT_EVENT_OPEN'',''Other'',0)\n\t\t,(''QUERY_NOTIFICATION_MGR_MUTEX'',''Other'',0)\n\t\t,(''QUERY_NOTIFICATION_SUBSCRIPTION_MUTEX'',''Other'',0)\n\t\t,(''QUERY_NOTIFICATION_TABLE_MGR_MUTEX'',''Other'',0)\n\t\t,(''QUERY_NOTIFICATION_UNITTEST_MUTEX'',''Other'',0)\n\t\t,(''QUERY_OPTIMIZER_PRINT_MUTEX'',''Other'',0)\n\t\t,(''QUERY_REMOTE_BRICKS_DONE'',''Other'',0)\n\t\t,(''QUERY_TRACEOUT'',''Tracing'',0)\n\t\t,(''RECOVER_CHANGEDB'',''Other'',0)\n\t\t,(''REPL_CACHE_ACCESS'',''Replication'',0)\n\t\t,(''REPL_SCHEMA_ACCESS'',''Replication'',0)\n\t\t,(''REPLICA_WRITES'',''Replication'',0)\n\t\t,(''REQUEST_DISPENSER_PAUSE'',''Other'',0)\n\t\t,(''REQUEST_FOR_DEADLOCK_SEARCH'',''Idle'',1)\n\t\t,(''RESOURCE_QUEUE'',''Idle'',1)\n\t\t,(''RESOURCE_SEMAPHORE'',''Memory'',0)\n\t\t,(''RESOURCE_SEMAPHORE_MUTEX'',''Compilation'',0)\n\t\t,(''RESOURCE_SEMAPHORE_QUERY_COMPILE'',''Compilation'',0)\n\t\t,(''RESOURCE_SEMAPHORE_SMALL_QUERY'',''Compilation'',0)\n\t\t,(''SEC_DROP_TEMP_KEY'',''Other'',0)\n\t\t,(''SEQUENTIAL_GUID'',''Other'',0)\n\t\t,(''SERVER_IDLE_CHECK'',''Idle'',1)\n\t\t,(''SHUTDOWN'',''Other'',0)\n\t\t,(''SLEEP_BPOOL_FLUSH'',''Idle'',1)\n\t\t,(''SLEEP_DBSTARTUP'',''Idle'',1)\n\t\t,(''SLEEP_DCOMSTARTUP'',''Idle'',1)\n\t\t,(''SLEEP_MSDBSTARTUP'',''Idle'',1)\n\t\t,(''SLEEP_SYSTEMTASK'',''Idle'',1)\n\t\t,(''SLEEP_TASK'',''Idle'',1)\n\t\t,(''SLEEP_TEMPDBSTARTUP'',''Idle'',1)\n\t\t,(''SNI_CRITICAL_SECTION'',''Other'',0)\n\t\t,(''SNI_HTTP_ACCEPT'',''Idle'',1)\n\t\t,(''SNI_HTTP_WAITFOR_0_DISCON'',''Other'',0)\n\t\t,(''SNI_LISTENER_ACCESS'',''Other'',0)\n\t\t,(''SNI_TASK_COMPLETION'',''Other'',0)\n\t\t,(''SOAP_READ'',''Full Text Search'',0)\n\t\t,(''SOAP_WRITE'',''Full Text Search'',0)\n\t\t,(''SOS_CALLBACK_REMOVAL'',''Other'',0)\n\t\t,(''SOS_DISPATCHER_MUTEX'',''Other'',0)\n\t\t,(''SOS_LOCALALLOCATORLIST'',''Other'',0)\n\t\t,(''SOS_OBJECT_STORE_DESTROY_MUTEX'',''Other'',0)\n\t\t,(''SOS_PROCESS_AFFINITY_MUTEX'',''Other'',0)\n\t\t,(''SOS_RESERVEDMEMBLOCKLIST'',''Memory'',0)\n\t\t,(''SOS_SCHEDULER_YIELD'',''CPU'',0)\n\t\t,(''SOS_STACKSTORE_INIT_MUTEX'',''Other'',0)\n\t\t,(''SOS_SYNC_TASK_ENQUEUE_EVENT'',''Other'',0)\n\t\t,(''SOS_VIRTUALMEMORY_LOW'',''Memory'',0)\n\t\t,(''SOSHOST_EVENT'',''Other'',0)\n\t\t,(''SOSHOST_INTERNAL'',''Other'',0)\n\t\t,(''SOSHOST_MUTEX'',''Other'',0)\n\t\t,(''SOSHOST_RWLOCK'',''Other'',0)\n\t\t,(''SOSHOST_SEMAPHORE'',''Other'',0)\n\t\t,(''SOSHOST_SLEEP'',''Other'',0)\n\t\t,(''SOSHOST_TRACELOCK'',''Other'',0)\n\t\t,(''SOSHOST_WAITFORDONE'',''Other'',0)\n\t\t,(''SQLCLR_APPDOMAIN'',''SQL CLR'',0)\n\t\t,(''SQLCLR_ASSEMBLY'',''SQL CLR'',0)\n\t\t,(''SQLCLR_DEADLOCK_DETECTION'',''SQL CLR'',0)\n\t\t,(''SQLCLR_QUANTUM_PUNISHMENT'',''SQL CLR'',0)\n\t\t,(''SQLSORT_NORMMUTEX'',''Other'',0)\n\t\t,(''SQLSORT_SORTMUTEX'',''Other'',0)\n\t\t,(''SQLTRACE_BUFFER_FLUSH'',''Idle'',1)\n\t\t,(''SQLTRACE_LOCK'',''Other'',0)\n\t\t,(''SQLTRACE_SHUTDOWN'',''Tracing'',0)\n\t\t,(''SQLTRACE_WAIT_ENTRIES'',''Idle'',0)\n\t\t,(''SRVPROC_SHUTDOWN'',''Other'',0)\n\t\t,(''TEMPOBJ'',''Other'',0)\n\t\t,(''THREADPOOL'',''Worker Thread'',0)\n\t\t,(''TIMEPRIV_TIMEPERIOD'',''Other'',0)\n\t\t,(''TRACEWRITE'',''Tracing'',1)\n\t\t,(''TRAN_MARKLATCH_DT'',''Transaction'',0)\n\t\t,(''TRAN_MARKLATCH_EX'',''Transaction'',0)\n\t\t,(''TRAN_MARKLATCH_KP'',''Transaction'',0)\n\t\t,(''TRAN_MARKLATCH_NL'',''Transaction'',0)\n\t\t,(''TRAN_MARKLATCH_SH'',''Transaction'',0)\n\t\t,(''TRAN_MARKLATCH_UP'',''Transaction'',0)\n\t\t,(''TRANSACTION_MUTEX'',''Transaction'',0)\n\t\t,(''UTIL_PAGE_ALLOC'',''Memory'',0)\n\t\t,(''VIA_ACCEPT'',''Other'',0)\n\t\t,(''VIEW_DEFINITION_MUTEX'',''Latch'',0)\n\t\t,(''WAIT_FOR_RESULTS'',''User Wait'',1)\n\t\t,(''WAITFOR'',''User Wait'',1)\n\t\t,(''WAITFOR_TASKSHUTDOWN'',''Idle'',1)\n\t\t,(''WAITSTAT_MUTEX'',''Other'',0)\n\t\t,(''WCC'',''Other'',0)\n\t\t,(''WORKTBL_DROP'',''Other'',0)\n\t\t,(''WRITELOG'',''Tran Log IO'',0)\n\t\t,(''XACT_OWN_TRANSACTION'',''Transaction'',0)\n\t\t,(''XACT_RECLAIM_SESSION'',''Transaction'',0)\n\t\t,(''XACTLOCKINFO'',''Transaction'',0)\n\t\t,(''XACTWORKSPACE_MUTEX'',''Transaction'',0)\n\t\t,(''XE_BUFFERMGR_ALLPROCECESSED_EVENT'',''Other'',0)\n\t\t,(''XE_BUFFERMGR_FREEBUF_EVENT'',''Other'',0)\n\t\t,(''XE_DISPATCHER_JOIN'',''Other'',0)\n\t\t,(''XE_DISPATCHER_WAIT'',''Idle'',1)\n\t\t,(''XE_MODULEMGR_SYNC'',''Other'',0)\n\t\t,(''XE_OLS_LOCK'',''Other'',0)\n\t\t,(''XE_SERVICES_MUTEX'',''Other'',0)\n\t\t,(''XE_SESSION_CREATE_SYNC'',''Other'',0)\n\t\t,(''XE_SESSION_SYNC'',''Other'',0)\n\t\t,(''XE_STM_CREATE'',''Other'',0)\n\t\t,(''XE_TIMER_EVENT'',''Idle'',1)\n\t\t,(''XE_TIMER_MUTEX'',''Other'',0)\n\t) AS WsCat ([Wait Type],[Wait Category],[Ignore])\n\t\tON bWS.[wait_type] = WsCat.[Wait Type]\n\t'\n\n\tIF @baselineSchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WaitStats, '{0}', @baselineSchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tIF @replaySchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_WaitStats, '{0}', @replaySchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\n\t--===========================================================\n\tDECLARE @PowerBI_Time nvarchar(max) = N'\n\tALTER VIEW {0}.[PowerBI_Time] AS\n\tSELECT\n\t\tSUM([duration_minutes]) OVER(ORDER BY [end_time] ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [Elapsed Time (min)]\n\tFROM {0}.[Intervals]\n\t'\n\n\tIF @baselineSchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_Time, '{0}', @baselineSchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tIF @replaySchema IS NOT NULL\n\t\tBEGIN\n\t\t\tSET @sql = REPLACE(@PowerBI_Time, '{0}', @replaySchema);\n\t\t\tEXEC(@sql);\n\t\tEND\n\tCOMMIT;\nEND TRY\nBEGIN CATCH \n\tDECLARE @ErrorMessage NVARCHAR(4000)\n    DECLARE @ErrorSeverity INT\n    DECLARE @ErrorState INT\n \n    SELECT  @ErrorMessage = ERROR_MESSAGE(),\n            @ErrorSeverity = ERROR_SEVERITY(),\n            @ErrorState = ERROR_STATE()\n \n    IF XACT_STATE() <> 0\n        ROLLBACK TRAN\n \n    RAISERROR ( @ErrorMessage, @ErrorSeverity, @ErrorState)\nEND CATCH\n\nEND"
  },
  {
    "path": "WorkloadTools/Consumer/BufferedWorkloadConsumer.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing NLog;\n\nnamespace WorkloadTools.Consumer\n{\n    public abstract class BufferedWorkloadConsumer : WorkloadConsumer\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        protected bool stopped = false;\n        protected ConcurrentQueue<WorkloadEvent> Buffer { get; set; } = new ConcurrentQueue<WorkloadEvent>();\n        protected Task BufferReader { get; set; }\n\n        private SpinWait spin = new SpinWait();\n        \n        public int BufferSize { get; set; } = 100000;\n\n        public override sealed void Consume(WorkloadEvent evt)\n        {\n            if (evt == null)\n            {\n                return;\n            }\n\n            // Ensure that the buffer does not get too big\n            while (Buffer.Count >= BufferSize)\n            {\n                logger.Trace(\"Buffer is full so spinning\");\n                spin.SpinOnce();\n            }\n\n            // If the buffer has room, enqueue the event\n            logger.Trace(\"Adding event {eventType} with start time {startTime:yyyy-MM-ddTHH\\\\:mm\\\\:ss.fffffff} to buffer\", evt.Type, evt.StartTime);\n            Buffer.Enqueue(evt);\n\n            if(BufferReader == null)\n            {\n                BufferReader = Task.Factory.StartNew(() => ProcessBuffer());\n            }\n        }\n\n        protected void ProcessBuffer()\n        {\n            while (!stopped)\n            {\n                try\n                {\n                    WorkloadEvent evt;\n                    while (!Buffer.TryDequeue(out evt))\n                    {\n                        if (stopped)\n                        {\n                            return;\n                        }\n\n                        spin.SpinOnce();\n                    }\n\n                    if (evt == null)\n                    {\n                        continue;\n                    }\n\n                    ConsumeBuffered(evt);\n                }\n                catch (Exception ex)\n                {\n                    logger.Error($\"Error occurred consuming buffered events: {ex.Message}.\");\n                    throw;\n                }\n            }\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            stopped = true;\n        }\n\n        public abstract void ConsumeBuffered(WorkloadEvent evt);\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Replay/ReplayCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Consumer.Replay\n{\n    public class ReplayCommand\n    {\n        public string CommandText { get; set; }\n        public string Database { get; set; }\n        public string ApplicationName { get; set; }\n        public double ReplayOffset { get; set; } = 0; // milliseconds\n        public DateTime StartTime { get; set; }\n        public long? EventSequence { get; set; }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Replay/ReplayConsumer.cs",
    "content": "using System;\nusing System.CodeDom.Compiler;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing NLog;\n\nnamespace WorkloadTools.Consumer.Replay\n{\n    public class ReplayConsumer : BufferedWorkloadConsumer\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private SpinWait spin = new SpinWait();\n        public int ThreadLimit = 256;//32\n        public int InactiveWorkerTerminationTimeoutSeconds = 300;\n        private readonly Semaphore WorkLimiter;\n\n        public bool DisplayWorkerStats { get; set; } = true;\n        public bool ConsumeResults { get; set; } = true;\n        public int QueryTimeoutSeconds { get; set; } = 30;\n        public int WorkerStatsCommandCount { get; set; } = 1000;\n        public bool MimicApplicationName { get; set; } = false;\n        public int FailRetryCount { get; set; } = 0;\n        public int TimeoutRetryCount { get; set; } = 0;\n        public bool RaiseErrorsToSqlEventTracing { get; private set; } = true;\n        public bool RelativeDelays { get; set; } = false;\n\n        private LogLevel _CommandErrorLogLevel = LogLevel.Error;\n        public string CommandErrorLogLevel\n        {\n            get => _CommandErrorLogLevel.Name;\n            set => _CommandErrorLogLevel = LogLevel.FromString(value);\n        }\n\n        public Dictionary<string, string> DatabaseMap { get; set; } = new Dictionary<string, string>();\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n        public ThreadingModeEnum ThreadingMode { get; set; } = ThreadingModeEnum.WorkerTask;\n\n        private readonly ConcurrentDictionary<string, ReplayWorker> ReplayWorkers = new ConcurrentDictionary<string, ReplayWorker>();\n\n        private Thread sweeper;\n\n        private long eventCount;\n        private long dispatchedEventCount;\n        private DateTime startTime = DateTime.MinValue;\n\n        // holds the total number of events to replay\n        // only available when reading from a file\n        // for realtime replays this is not available\n        private long totalEventCount = 0;\n\n        // holds the number of events that have been executed by the workers\n        private long executedEventCount;\n\n        // watchdog: fires if no command has been executed for WatchdogIntervalSeconds\n        public int WatchdogIntervalSeconds { get; set; } = 30;\n        private Timer watchdogTimer;\n\n        public enum ThreadingModeEnum : int\n        {\n            ThreadPools = 1,\n            Tasks = 2,\n            WorkerTask = 3,\n            Serial = 4\n        }\n\n        public ReplayConsumer()\n        {\n            WorkLimiter = new Semaphore(ThreadLimit, ThreadLimit);\n        }\n\n        private void EnsureWatchdogRunning()\n        {\n            if (watchdogTimer != null)\n            {\n                return;\n            }\n\n            var intervalMs = WatchdogIntervalSeconds * 1000;\n            watchdogTimer = new Timer(\n                delegate\n                {\n                    var current = Interlocked.Read(ref executedEventCount);\n                    var dispatched = Interlocked.Read(ref dispatchedEventCount);\n                    var bufferedEventCount = ReplayWorkers.Values.Sum(x => x.QueueLength);\n\n                    // Always log on every watchdog tick so the user can see progress\n                    // (or lack thereof) at wall-clock intervals, regardless of whether\n                    // the event count has crossed a modulus boundary.\n                    LogReplayProgress(current, forceLog: true);\n\n                    // Check for completion: all dispatched events have been executed\n                    // and nothing is left in any buffer.\n                    if (dispatched > 0\n                        && current >= dispatched\n                        && Buffer.Count == 0\n                        && bufferedEventCount == 0)\n                    {\n                        // Stop the watchdog - nothing left to watch.\n                        watchdogTimer?.Dispose();\n                        watchdogTimer = null;\n                    }\n                },\n                null,\n                intervalMs,\n                intervalMs);\n        }\n\n        private string WorkerKey(ExecutionWorkloadEvent evnt)\n        {\n            // In SQL the SPID is only unqiue while the session is in use.\n            // When the SPID is reused it may be for a different database.\n\n            var result = $\"{evnt.SPID}_{evnt.DatabaseName}\";\n\n            if (MimicApplicationName)\n            {\n                // When the SPID is reused it may be for a different Host, User or Application\n                // but this application can only mimic the Application Name so if we're doing\n                // that include that in the key.\n                result += $\"_{evnt.ApplicationName}\";\n            }\n\n            return result;\n        }\n\n        public override void ConsumeBuffered(WorkloadEvent evnt)\n        {\n            if (evnt is MessageWorkloadEvent messageEvent)\n            {\n                if (messageEvent.MsgType == MessageWorkloadEvent.MessageType.TotalEvents)\n                {\n                    try\n                    {\n                        totalEventCount = (long)messageEvent.Value;\n                    }\n                    catch (Exception e)\n                    {\n                        logger.Error(e, $\"Unable to set the total number of events\");\n                    }\n                }\n            }\n\n            // totalEventCount is EVERY Event except the initial MessageWorkloadEvent for the TotalEvents,\n            // so always increment the counter.\n            eventCount++;\n\n            if (!(evnt is ExecutionWorkloadEvent))\n            {\n                return;\n            }\n\n            if (evnt.Type != WorkloadEvent.EventType.RPCStarting && evnt.Type != WorkloadEvent.EventType.BatchStarting)\n            {\n                return;\n            }\n\n            // dispatchedEventCount tracks only the ExecutionWorkloadEvents that have been\n            // dispatched to a worker, giving a stable monotonic counter to drive log intervals.\n            dispatchedEventCount++;\n\n            if (startTime == DateTime.MinValue)\n            {\n                // Pad the start time so that the first event isn't behind by the time the worker has started up on a thread.\n                startTime = DateTime.Now.AddTicks(TimeSpan.TicksPerSecond);\n                logger.Info(\"All future delays will be calculated from this point + 1s, triggered by event {@event}\", evnt);\n            }\n\n            var evt = (ExecutionWorkloadEvent)evnt;\n\n            if (stopped) { return; }\n\n            var command = new ReplayCommand()\n            {\n                CommandText = evt.Text,\n                Database = evt.DatabaseName,\n                ApplicationName = evt.ApplicationName,\n                ReplayOffset = evt.ReplayOffset,\n                StartTime = evt.StartTime,\n                EventSequence = evt.EventSequence\n            };\n\n            var workerKey = WorkerKey(evt);\n\n            if (ReplayWorkers.TryGetValue(workerKey, out var rw))\n            {\n                // Ensure that the buffer does not get too big\n                while (rw.QueueLength >= (BufferSize * .9))\n                {\n                    spin.SpinOnce();\n                }\n\n                if (stopped) { return; }\n\n                rw.AppendCommand(command);\n            }\n            else\n            {\n                logger.Debug(\"Creating Worker {Worker}\", workerKey);\n\n                rw = new ReplayWorker(workerKey)\n                {\n                    ConnectionInfo = ConnectionInfo,\n                    ReplayIntervalSeconds = 0,\n                    StopOnError = false,\n                    DisplayWorkerStats = DisplayWorkerStats,\n                    ConsumeResults = ConsumeResults,\n                    QueryTimeoutSeconds = QueryTimeoutSeconds,\n                    WorkerStatsCommandCount = WorkerStatsCommandCount,\n                    MimicApplicationName = MimicApplicationName,\n                    DatabaseMap = DatabaseMap,\n                    StartTime = startTime,\n                    FailRetryCount = FailRetryCount,\n                    TimeoutRetryCount = TimeoutRetryCount,\n                    CommandErrorLogLevel = _CommandErrorLogLevel,\n                    RaiseErrorsToSqlEventTracing = RaiseErrorsToSqlEventTracing,\n                    RelativeDelays = RelativeDelays\n                };\n\n                rw.CommandExecuted += OnWorkerCommandExecuted;\n\n                rw.AppendCommand(command);\n\n                if (stopped) { return; }\n                _ = ReplayWorkers.TryAdd(workerKey, rw);\n            }\n\n            // Ensure the worker is running.\n            // If new it needs starting for the first time.\n            // If existing it may have stopped if the command queue became empty.\n            RunWorker(rw);\n\n            if (sweeper == null)\n            {\n                sweeper = new Thread(new ThreadStart(\n                                delegate\n                                {\n                                    try\n                                    {\n                                        RunSweeper();\n                                    }\n                                    catch (Exception e)\n                                    {\n                                        try { logger.Error(e, \"Unhandled exception in TraceManager.RunSweeper\"); }\n                                        catch { Console.WriteLine(e.Message); }\n                                    }\n                                }\n                                ))\n                {\n                    IsBackground = true\n                };\n                sweeper.Start();\n            }\n\n            EnsureWatchdogRunning();\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            logger.Info(\"Disposing ReplayConsumer\");\n            stopped = true;\n\n            watchdogTimer?.Dispose();\n            watchdogTimer = null;\n\n            foreach (var r in ReplayWorkers.Values)\n            {\n                r.Dispose();\n            }\n            WorkLimiter.Dispose();\n        }\n\n        // Sweeper thread: removes from the workers list all the workers\n        // that have not executed a command in the last 5 minutes\n        private void RunSweeper()\n        {\n            while (!stopped)\n            {\n                logger.Debug(\"Looking for workers that have been idle for {InactiveWorkerTerminationTimeoutSeconds}s\", InactiveWorkerTerminationTimeoutSeconds);\n\n                try\n                {\n                    // Use .ToList() to materialise the list so that ReplayWorkers.TryRemove does not cause an exception that the list has changed during the iteration\n                    foreach (var wrk in ReplayWorkers.Values.Where(x => x.LastCommandTime > DateTime.MinValue && x.LastCommandTime < DateTime.Now.AddSeconds(-InactiveWorkerTerminationTimeoutSeconds) && !x.HasCommands).ToList())\n                    {\n                        if(stopped) { return; }\n\n                        logger.Debug(\"Removing worker {Worker} which has not executed a command since {lastCommand}\", wrk.Name, wrk.LastCommandTime);\n\n                        RemoveWorker(wrk.Name);\n                    }\n                }\n                catch (Exception e)\n                {\n                    logger.Warn(e, \"Error when removing idle workers\");\n                }\n\n                Thread.Sleep(InactiveWorkerTerminationTimeoutSeconds * 1000); // sleep some seconds\n            }\n        }\n\n        private void RemoveWorker(string name)\n        {\n            _ = ReplayWorkers.TryRemove(name, out var outWrk);\n\n            if (outWrk != null)\n            {\n                outWrk.CommandExecuted -= OnWorkerCommandExecuted;\n                outWrk.Stop();\n                outWrk.Dispose();\n            }\n        }\n\n        private void RunWorker(ReplayWorker wrk)\n        {\n            try\n            {\n                if (stopped)\n                {\n                    return;\n                }\n\n                if (wrk.HasCommands)\n                {\n                    if (ThreadingMode == ThreadingModeEnum.ThreadPools)\n                    {\n                        // Using a semaphore to avoid overwhelming the threadpool\n                        // Without this precaution, the memory consumption goes to the roof\n                        _ = WorkLimiter.WaitOne();\n\n                        var queued = false;\n                        try\n                        {\n                            // Queue the execution of a statement in the threadpool.\n                            // The statement will get executed in a separate thread eventually.\n                            _ = ThreadPool.QueueUserWorkItem(\n                                delegate\n                                {\n                                    try\n                                    {\n                                        wrk.ExecuteNextCommand();\n                                    }\n                                    catch (Exception e)\n                                    {\n                                        try\n                                        {\n                                            logger.Error(e, \"Unhandled exception in ReplayWorker.ExecuteCommand\");\n                                        }\n                                        catch\n                                        {\n                                            Console.WriteLine(e.Message);\n                                        }\n                                    }\n                                    finally\n                                    {\n                                        // Release only after execution completes to actually\n                                        // bound the number of concurrently executing commands.\n                                        _ = WorkLimiter.Release();\n                                    }\n                                }\n                                );\n                            queued = true;\n                        }\n                        finally\n                        {\n                            // If queuing itself failed, release the slot we acquired\n                            // so the semaphore is not permanently leaked.\n                            if (!queued) { _ = WorkLimiter.Release(); }\n                        }\n                    }\n                    else if (ThreadingMode == ThreadingModeEnum.Tasks)\n                    {\n                        // TODO: Is this not the same as WorkerTask?\n                        // Here the task is created by ReplayConsumer.\n                        // With WorkerTask the task is created by ReplayWorker.Start.\n\n                        // Using a semaphore to avoid overwhelming the threadpool\n                        // Without this precaution, the memory consumption goes to the roof\n                        _ = WorkLimiter.WaitOne();\n\n                        // Start a new Task to run the statement\n                        var t = Task.Factory.StartNew(\n                            delegate\n                            {\n                                try\n                                {\n                                    wrk.ExecuteNextCommand();\n                                }\n                                catch (Exception e)\n                                {\n                                    try\n                                    {\n                                        logger.Error(e, \"Unhandled exception in ReplayWorker.ExecuteCommand\");\n                                    }\n                                    catch\n                                    {\n                                        Console.WriteLine(e.Message);\n                                    }\n                                }\n                                finally\n                                {\n                                    // Release only after execution completes to actually\n                                    // bound the number of concurrently executing commands.\n                                    _ = WorkLimiter.Release();\n                                }\n                            }\n                            );\n                    }\n                    else if (ThreadingMode == ThreadingModeEnum.WorkerTask)\n                    {\n                        if (!wrk.IsRunning && !stopped)\n                        {\n                            wrk.Start();\n                        }\n                    }\n                    else if (ThreadingMode == ThreadingModeEnum.Serial)\n                    {\n                        wrk.ExecuteNextCommand();\n                    }\n                }\n            }\n            catch (InvalidOperationException)\n            {\n                //ignore ...\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex, \"Error starting worker\");\n            }\n        }\n\n        public override bool HasMoreEvents()\n        {\n            return ReplayWorkers.Count(t => t.Value.HasCommands) > 0 || Buffer.Count > 0;\n        }\n\n        private long completionLogged = 0;\n\n        private void OnWorkerCommandExecuted(object sender, EventArgs e)\n        {\n            var executed = Interlocked.Increment(ref executedEventCount);\n            EnsureWatchdogRunning();\n            LogReplayProgress(executed);\n\n            // Log completion eagerly as soon as the last command is executed,\n            // without waiting for the next watchdog tick which may never fire\n            // if the controller disposes first.\n            // Only applies when totalEventCount is known (i.e. file-based replay).\n            // For realtime replay, totalEventCount is 0 and there is no defined end.\n            if (totalEventCount > 0)\n            {\n                var dispatched = Interlocked.Read(ref dispatchedEventCount);\n                if (executed >= dispatched\n                    && Buffer.Count == 0\n                    && ReplayWorkers.Values.Sum(x => x.QueueLength) == 0\n                    && Interlocked.CompareExchange(ref completionLogged, 1, 0) == 0)\n                {\n                    LogReplayProgress(executed, forceLog: true);\n                    logger.Info(\"Replay completed: {executed} commands executed out of {dispatched} dispatched\", executed, dispatched);\n                }\n            }\n        }\n\n        private void LogReplayProgress(long executed, bool forceLog = false)\n        {\n            // Determine the log interval:\n            // - If dispatchedEventCount is known and > 0: aim for ~1000 log lines for large workloads,\n            //   fall back to ~10 for small ones (< 1000 events).\n            // - If dispatchedEventCount is unknown: fall back to WorkerStatsCommandCount.\n            var dispatched = Interlocked.Read(ref dispatchedEventCount);\n\n            long logInterval;\n            if (dispatched > 0)\n            {\n                var fineInterval = dispatched / 1000;\n                var coarseInterval = Math.Max(1, dispatched / 10);\n                logInterval = fineInterval >= 1 ? fineInterval : coarseInterval;\n            }\n            else\n            {\n                logInterval = Math.Max(1, WorkerStatsCommandCount);\n            }\n\n            if (forceLog || executed == 1 || executed % logInterval == 0)\n            {\n                var bufferedEventCount = ReplayWorkers.Values.Sum(x => x.QueueLength);\n                if (dispatched > 0)\n                {\n                    var percentInfo = (double)executed / (double)dispatched;\n                    logger.Info($\"{executed} ({percentInfo:P}) events replayed - {Buffer.Count + bufferedEventCount} events buffered\");\n                }\n                else\n                {\n                    logger.Info($\"{executed} events replayed - {Buffer.Count + bufferedEventCount} events buffered\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Replay/ReplayWorker.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SqlClient;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing NLog;\n\nusing WorkloadTools.Consumer.Analysis;\nusing WorkloadTools.Listener;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Consumer.Replay\n{\n    internal class ReplayWorker : IDisposable\n    {\n        private const int SkippedDelayCountThreshold = 100;\n\n        // Unlike the other loggers this one is not static because we\n        // need unique properties for each instance of ReplayWorker.\n        private readonly Logger logger;\n        public bool DisplayWorkerStats { get; set; }\n        public bool ConsumeResults { get; set; }\n        public int QueryTimeoutSeconds { get; set; }\n        public int WorkerStatsCommandCount { get; set; }\n        public bool MimicApplicationName { get; set; }\n        public LogLevel CommandErrorLogLevel { get; set; }\n        public bool RelativeDelays { get; set; }\n\n        public int FailRetryCount { get; set; }\n        public int TimeoutRetryCount { get; set; }\n\n        private SqlConnection Conn { get; set; }\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n\n        public int ReplayIntervalSeconds { get; set; } = 0;\n        public bool StopOnError { get; set; } = false;\n        public string Name { get; private set; }\n        public int SPID { get; set; }\n        public bool IsRunning { get; private set; } = false;\n        public bool RaiseErrorsToSqlEventTracing { get; set; } = true;\n\n        public DateTime StartTime { get; set; }\n\n        public Dictionary<string, string> DatabaseMap { get; set; } = new Dictionary<string, string>();\n\n        private Task runner = null;\n        private CancellationTokenSource tokenSource;\n        private double lastCommandOffet = 0;\n        private bool skipNextDelay = false;\n\n        public ReplayWorker(string name)\n        {\n            Name = name;\n            logger = LogManager.GetCurrentClassLogger().WithProperty(\"Worker\", name);\n        }\n\n        public bool HasCommands => !Commands.IsEmpty;\n\n        public int QueueLength => Commands.Count;\n\n        public DateTime LastCommandTime { get; private set; }\n\n        private long commandCount = 0;\n        private long previousCommandCount = 0;\n        private DateTime previousCPSComputeTime = DateTime.Now;\n        private readonly List<int> commandsPerSecond = new List<int>();\n\n        private readonly ConcurrentQueue<ReplayCommand> Commands = new ConcurrentQueue<ReplayCommand>();\n\n        public bool IsStopped { get; private set; } = false;\n\n        private readonly SqlTransformer transformer = new SqlTransformer();\n\n        private readonly Dictionary<int, int> preparedStatements = new Dictionary<int, int>();\n        private SpinWait _spinWait = new SpinWait();\n\n        private int continiousSkippedDelays = 0;\n\n        private enum UserErrorType\n        {\n            Timeout = 82,\n            Error = 83\n        }\n\n        private void InitializeConnection(string applicationName)\n        {\n            logger.Debug(\"Connecting to server {serverName}\", ConnectionInfo.ServerName);\n\n            ConnectionInfo.DatabaseMap = DatabaseMap;\n            var connString = ConnectionInfo.ConnectionString(applicationName);\n\n            Conn = new SqlConnection(connString);\n            Conn.Open();\n\n            logger.Debug(\"Connected\");\n        }\n\n        public void Start()\n        {\n            tokenSource = new CancellationTokenSource();\n            var token = tokenSource.Token;\n\n            if (runner != null && runner.IsCompleted)\n            {\n                runner.Dispose();\n                runner = null;\n            }\n\n            if (runner == null && !IsStopped)\n            {\n                // Given the potential for lots of Workers we need to allow over-subscription of threads using the LongRunning option.\n                // \"\n                //     Specifies that a task will be a long-running, coarse-grained operation involving\n                //     fewer, larger components than fine-grained systems. It provides a hint to the\n                //     System.Threading.Tasks.TaskScheduler that oversubscription may be warranted.\n                //     Oversubscription lets you create more threads than the available number of hardware\n                //     threads. It also provides a hint to the task scheduler that an additional thread\n                //     might be required for the task so that it does not block the forward progress\n                //     of other threads or work items on the local thread-pool queue.\n                // \"\n                runner = Task.Factory.StartNew(() => Run(), token, TaskCreationOptions.LongRunning, TaskScheduler.Current);\n            }\n        }\n\n        public void Run()\n        {\n            IsRunning = true;\n            while (!IsStopped && IsRunning)\n            {\n                try\n                {\n                    ExecuteNextCommand();\n                }\n                catch (Exception e)\n                {\n                    logger.Error(e, \"Error starting Worker\");\n                }\n            }\n        }\n\n        public void Stop()\n        {\n            logger.Debug(\"Stopping\");\n\n            IsStopped = true;\n            IsRunning = false;\n            tokenSource?.Cancel();\n\n            logger.Debug(\"Stopped\");\n        }\n\n        public event EventHandler CommandExecuted;\n\n        public void ExecuteNextCommand()\n        {\n            var cmd = GetNextCommand();\n            if (cmd != null)\n            {\n                try\n                {\n                    ExecuteCommand(cmd);\n                }\n                finally\n                {\n                    commandCount++;\n                    CommandExecuted?.Invoke(this, EventArgs.Empty);\n                }\n            }\n            else\n            {\n                // Release the thread when out of work\n                IsRunning = false;\n            }\n        }\n\n        public ReplayCommand GetNextCommand()\n        {\n            _ = Commands.TryDequeue(out var result);\n\n            // Previously this method would loop and use a spinWait.\n            // Memory dumps taken of a large workload showed a very large number of tasks in a Scheduled state on this spin.\n            // Better concurrency has been achieved by letting the task complete.\n            // The ReplayConsumer will then start a task up again if more work comes to this worker in future.\n\n            return result;\n        }\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public void ExecuteCommand(ReplayCommand command, int failRetryCount = 0, int timeoutRetryCount = 0)\n        {\n            LastCommandTime = DateTime.Now;\n\n            var applicationName = \"WorkloadTools-ReplayWorker\";\n            if (MimicApplicationName)\n            {\n                applicationName = command.ApplicationName;\n                if (string.IsNullOrEmpty(applicationName))\n                {\n                    ConnectionInfo.ApplicationName = \"WorkloadTools-ReplayWorker\";\n                }\n            }\n\n            if (Conn == null)\n            {\n                try\n                {\n                    InitializeConnection(applicationName);\n                }\n                catch (SqlException se)\n                {\n                    logger.Error(se, \"Unable to acquire the connection. Quitting the ReplayWorker\");\n\n                    return;\n                }\n            }\n\n            if (Conn != null)\n            {\n                while (Conn.State == ConnectionState.Connecting)\n                {\n                    if (IsStopped)\n                    {\n                        return;\n                    }\n\n                    logger.Debug(\"Connection is in connecting state. Sleeping for 5ms\");\n\n                    Thread.Sleep(5);\n                }\n            }\n\n            if (Conn == null || (Conn.State == ConnectionState.Closed) || (Conn.State == ConnectionState.Broken))\n            {\n                InitializeConnection(applicationName);\n            }\n\n            // Extract the handle from the prepared statement\n            var nst = transformer.Normalize(command.CommandText);\n\n            // If the command comes with a replay offset, evaluate it now.\n            // The offset in milliseconds is set in FileWorkloadListener.\n            // The other listeners do not set this value, as they\n            // already come with the original timing\n            if (command.ReplayOffset > 0 && !skipNextDelay)\n            {\n                if (RelativeDelays && lastCommandOffet > 0)\n                {\n                    var relativeDelay = command.ReplayOffset - lastCommandOffet;\n\n                    logger.Debug(\"Command start time is {startTime:yyyy-MM-ddTHH\\\\:mm\\\\:ss.fffffff} which is an offset of {relativeDelay} from the last command for this session\", command.StartTime, relativeDelay);\n\n                    PreciseDelay(relativeDelay);\n                }\n                else\n                {\n                    var delayMs = command.ReplayOffset - (DateTime.Now - StartTime).TotalMilliseconds;\n\n                    // Delay execution only if necessary\n                    if (delayMs > 0)\n                    {\n                        // We're not skipping this delay, so reset the counter.\n                        continiousSkippedDelays = 0;\n\n                        // Each command has a requested offset from the beginning\n                        // of the workload and this class does its best to respect it.\n                        // If the previous commands take longer in the target environment\n                        // the offset cannot be respected and the command will execute\n                        // without further waits, but there is no way to recover \n                        // the delay that has built up to that point.\n                        logger.Debug(\"Command start time is {startTime:yyyy-MM-ddTHH\\\\:mm\\\\:ss.fffffff} which is an offset of {ReplayOffset}ms from the start so waiting\", command.StartTime, command.ReplayOffset);\n\n                        PreciseDelay(delayMs);\n                    }\n                    else if (delayMs < -10000)\n                    {\n                        // If we're more than 10s behind then \n                        logger.Debug(\"Command start time is {startTime:yyyy-MM-ddTHH\\\\:mm\\\\:ss.fffffff} which is an offset of {ReplayOffset}ms from the start but replay is behind so it should have executed {delayMs}ms ago\", command.StartTime, command.ReplayOffset, delayMs);\n                        continiousSkippedDelays++;\n\n                        if (continiousSkippedDelays % SkippedDelayCountThreshold == 0)\n                        {\n                            // If we are consistently behind and the configuration has\n                            // requested SynchronizationMode we're actually doing a stress test.\n                            logger.Warn(\"The last {skippedDelays} Commands requested a delay but replay is > 10s behind so were processed immediately which may indicate that either this tool or the target system cannot keep up with the workload. You could try switching to relative delays with the RelativeDelays parameter\", continiousSkippedDelays);\n                        }\n                    }\n                }\n            }\n\n            if (IsStopped)\n            {\n                return;\n            }\n\n            // Record the requested and actual command start time in case we're doing relative sleeps\n            lastCommandOffet = command.ReplayOffset;\n\n            if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_RESET_CONNECTION)\n            {\n                // If event is a sp_reset_connection, call a connection close and open to\n                // force connection to get back to connection pool and reset it so that\n                // it's clean for the next event\n                Conn.Close();\n                Conn.Open();\n\n                // Generally appliations that (correctly) close their connection\n                // regularly and rely on the SQL Client Connection Pool, they will do\n                // so after a command.\n                // This results in the opening of the next connection getting a connection\n                // from the pool and triggering SP_Reset_Connection.\n                // As such captured traces show that the gap between SP_Reset_Connection\n                // and the next command is a few ms only.\n                // Given we struggle to do super-accurate delays, and given the original\n                // application likely had 0 delay in this scenario, skip the delay for the\n                // next command.\n                skipNextDelay = true;\n\n                return;\n            }\n            else if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_RESET_CONNECTION_NONPOOLED)\n            {\n                // If event is a nonpooled sp_reset_connection, call a ClearPool(conn)\n                // to force a new connection.\n                ClearPool(Conn);\n\n                // Generally appliations that (correctly) close their connection\n                // regularly and rely on the SQL Client Connection Pool, they will do\n                // so after a command.\n                // This results in the opening of the next connection getting a connection\n                // from the pool and triggering SP_Reset_Connection.\n                // As such captured traces show that the gap between SP_Reset_Connection\n                // and the next command is a few ms only.\n                // Given we struggle to do super-accurate delays, and given the original\n                // application likely had 0 delay in this scenario, skip the delay for the\n                // next command.\n                skipNextDelay = true;\n\n                return;\n            }\n            else if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_PREPARE)\n            {\n                command.CommandText = nst.NormalizedText;\n            }\n            else if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_UNPREPARE || nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_EXECUTE)\n            {\n                // look up the statement to unprepare in the dictionary\n                if (preparedStatements.ContainsKey(nst.Handle))\n                {\n                    // the sp_execute statement has already been \"normalized\"\n                    // by replacing the original statement number with the § placeholder\n                    command.CommandText = nst.NormalizedText.ReplaceFirst(\"§\", preparedStatements[nst.Handle].ToString());\n\n                    if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_UNPREPARE)\n                    {\n                        _ = preparedStatements.Remove(nst.Handle);\n                    }\n                }\n                else\n                {\n                    return; // statement not found: better return\n                }\n            }\n\n            // If we get here it isn't a connection reset event, so ensure the next command delays correctly.\n            skipNextDelay = false;\n\n            try\n            {\n                // Try to remap the database according to the database map\n                if (DatabaseMap.ContainsKey(command.Database))\n                {\n                    command.Database = DatabaseMap[command.Database];\n                }\n\n                if (Conn.Database != command.Database)\n                {\n                    logger.Debug(\"Changing database to {databaseName}\", command.Database);\n\n                    Conn.ChangeDatabase(command.Database);\n                }\n\n                using (var cmd = new SqlCommand(command.CommandText))\n                {\n                    cmd.Connection = Conn;\n                    cmd.CommandTimeout = QueryTimeoutSeconds;\n\n                    if (nst.CommandType == NormalizedSqlText.CommandTypeEnum.SP_PREPARE)\n                    {\n                        if (cmd.CommandText == null)\n                        {\n                            return;\n                        }\n\n                        var handle = -1;\n                        try\n                        {\n                            var res = cmd.ExecuteScalar();\n                            if (res != null)\n                            {\n                                handle = (int)res;\n                                if (!preparedStatements.ContainsKey(nst.Handle))\n                                {\n                                    preparedStatements.Add(nst.Handle, handle);\n                                }\n                            }\n                        }\n                        catch (NullReferenceException)\n                        {\n                            throw;\n                        }\n                    }\n                    else if (ConsumeResults)\n                    {\n                        using (var reader = cmd.ExecuteReader())\n                        using (var consumer = new ResultSetConsumer(reader))\n                        {\n                            consumer.Consume();\n                        }\n                    }\n                    else\n                    {\n                        _ = cmd.ExecuteNonQuery();\n                    }\n                }\n\n                logger.Trace(\"SUCCESS - \\n{commandText}\", command.CommandText);\n                if (commandCount > 0 && commandCount % WorkerStatsCommandCount == 0)\n                {\n                    var seconds = (DateTime.Now - previousCPSComputeTime).TotalSeconds;\n                    var cps = (commandCount - previousCommandCount) / ((seconds == 0) ? 1 : seconds);\n                    previousCPSComputeTime = DateTime.Now;\n                    previousCommandCount = commandCount;\n\n                    if (DisplayWorkerStats)\n                    {\n                        commandsPerSecond.Add((int)cps);\n                        cps = commandsPerSecond.Average();\n\n                        logger.Info(\"{commandCount} commands executed - {pendingCommands} commands pending - Last Event Sequence: {lastEventSequence} - {cps} commands per second\", commandCount, Commands.Count, command.EventSequence, (int)cps);\n                    }\n                }\n\n                // Update the LastCommandTime again in case the duration of a Consume() call exceeded LastCommandTime + InactiveWorkerTerminationTimeoutSeconds\n                LastCommandTime = DateTime.Now;\n            }\n            catch (SqlException e)\n            {\n                // handle timeouts\n                if (e.Number == -2)\n                {\n                    RaiseTimeoutEvent(command.CommandText);\n                }\n                else\n                {\n                    RaiseErrorEvent(command, e.Message);\n                }\n\n                // If the workload is exepected to include lots of errors then logging at the default Error level can become really noisy!\n                logger.Log(CommandErrorLogLevel, e, \"Sequence[{eventSequence}] - Error: \\n{commandText}\", command.EventSequence, command.CommandText);\n\n                if (StopOnError)\n                {\n                    ClearPool(Conn);\n\n                    throw;\n                }\n                else\n                {\n                    if (e.Number != -2 && failRetryCount < FailRetryCount)\n                    {\n                        logger.Warn(\"Retrying Sequence[{eventSequence}] - Retrying command (current fail retry: {failRetryCount})\", command.EventSequence, failRetryCount);\n                        ExecuteCommand(command, ++failRetryCount, timeoutRetryCount);\n                    }\n                    if (e.Number == -2 && timeoutRetryCount < TimeoutRetryCount)\n                    {\n                        logger.Warn(\"Retrying Sequence[{eventSequence}] - Retrying command (current timeout retry: {timeoutRetryCount})\", command.EventSequence, timeoutRetryCount);\n                        ExecuteCommand(command, failRetryCount, ++timeoutRetryCount);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                // If the workload is exepected to include lots of errors then logging at the default Error level can become really noisy!\n                logger.Log(CommandErrorLogLevel, e, \"Sequence[{eventSequence}] - Error: \\n{commandText}\", command.EventSequence, command.CommandText);\n\n                ClearPool(Conn);\n\n                if (StopOnError)\n                {\n                    throw;\n                }\n            }\n        }\n\n        private void ClearPool(SqlConnection conn)\n        {\n            if (conn == null)\n            {\n                return;\n            }\n\n            try { SqlConnection.ClearPool(conn); } catch (Exception) { /*swallow */}\n\n            if (conn.State == ConnectionState.Open)\n            {\n                try { conn.Close(); } catch (Exception) { /* swallow */ }\n                try { conn.Dispose(); conn = null; } catch (Exception) { /* swallow */ }\n            }\n        }\n\n        private void RaiseTimeoutEvent(string commandText)\n        {\n            if (!RaiseErrorsToSqlEventTracing) { return; }\n\n            RaiseErrorEvent($\"WorkloadTools.Timeout[{QueryTimeoutSeconds}]\", commandText, UserErrorType.Timeout);\n        }\n\n        private void RaiseErrorEvent(ReplayCommand Command, string ErrorMessage)\n        {\n            if (!RaiseErrorsToSqlEventTracing) { return; }\n\n            var msg = $@\"DATABASE:\n{Command.Database}\nSEQUENCE:\n{Command.EventSequence}\nMESSAGE:\n{ErrorMessage}\n--------------------\n{Command.CommandText}\n\";\n\n            RaiseErrorEvent(\"WorkloadTools.Replay\", msg, UserErrorType.Error);\n        }\n\n        private void RaiseErrorEvent(string info, string message, UserErrorType type)\n        {\n            if (!RaiseErrorsToSqlEventTracing) { return; }\n\n            // Raise a custom event. Both SqlTrace and Extended Events can capture this event.\n            var sql = \"EXEC sp_trace_generateevent @eventid = @eventid, @userinfo = @userinfo, @userdata = @userdata;\";\n\n            try\n            {\n                using (var cmd = new SqlCommand(sql))\n                {\n                    // Creating a new connection to raise the custom event to don't mess up\n                    // with existing connection as a reset(close the connection) now would cause a \n                    // next event call to fail in case it has dependencies of objects or \n                    // user settings used in the connection\n                    var connString = ConnectionInfo.ConnectionString();\n                    var connErrorEvent = new SqlConnection(connString);\n                    connErrorEvent.Open();\n\n                    cmd.Connection = connErrorEvent;\n                    _ = cmd.Parameters.Add(new SqlParameter(\"@eventid\", SqlDbType.Int) { Value = type });\n                    _ = cmd.Parameters.Add(new SqlParameter(\"@userinfo\", SqlDbType.NVarChar, 128) { Value = info });\n                    _ = cmd.Parameters.Add(new SqlParameter(\"@userdata\", SqlDbType.VarBinary, 8000) { Value = Encoding.Unicode.GetBytes(message.Substring(0, message.Length > 8000 ? 8000 : message.Length)) });\n                    _ = cmd.ExecuteNonQuery();\n\n                    ClearPool(connErrorEvent);\n                }\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex, \"Unable to raise error event\");\n            }\n        }\n\n        public void AppendCommand(ReplayCommand cmd)\n        {\n            Commands.Enqueue(cmd);\n        }\n\n        private void PreciseDelay(double delayMs)\n        {\n            var startingTimestamp = Stopwatch.GetTimestamp();\n            var targetTicks = TimeSpan.TicksPerMillisecond * delayMs;\n            long elapsedMs;\n\n            while (Stopwatch.GetTimestamp() - startingTimestamp < targetTicks)\n            {\n                if (IsStopped)\n                {\n                    return;\n                }\n\n                elapsedMs = (Stopwatch.GetTimestamp() - startingTimestamp) / TimeSpan.TicksPerMillisecond;\n\n                // Thread.Sleep is less accurate than Thread.SpinWait, but consumes less CPU while idle.\n                // So to balance accuracy vs CPU impact use a hybrid approach.\n                if (delayMs - elapsedMs > 10000)\n                {\n                    logger.Debug(\"Sleeping for {threadSleepMs}ms - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 8000, delayMs, elapsedMs);\n                    Thread.Sleep(8000);\n                }\n                else if (delayMs - elapsedMs > 1000)\n                {\n                    logger.Debug(\"Sleeping for {threadSleepMs}ms - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 800, delayMs, elapsedMs);\n                    Thread.Sleep(800);\n                }\n                else if (delayMs - elapsedMs > 500)\n                {\n                    logger.Debug(\"Sleeping for {threadSleepMs}ms - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 300, delayMs, elapsedMs);\n                    Thread.Sleep(300);\n                }\n                else if (delayMs - elapsedMs > 20)\n                {\n                    logger.Debug(\"Sleeping for {threadSleepMs}ms - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 5, delayMs, elapsedMs);\n                    Thread.Sleep(5);\n                }\n                else if (delayMs - elapsedMs > 5)\n                {\n                    logger.Debug(\"Spinning for {spinIterations} iterations - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 100, delayMs, elapsedMs);\n                    Thread.SpinWait(100);\n                }\n                else if (delayMs - elapsedMs > 1)\n                {\n                    logger.Debug(\"Spinning for {spinIterations} iterations - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 50, delayMs, elapsedMs);\n                    Thread.SpinWait(50);\n                }\n                else\n                {\n                    logger.Debug(\"Spinning for {spinIterations} iterations - delay is {delayMs}ms - elapsed is {elapsedMs}ms\", 10, delayMs, elapsedMs);\n                    Thread.SpinWait(10);\n                }\n            }\n\n            // Highlight if the delays are inaccurate (with 100ms error margin)\n            // If there are a lot of these warnings then it may suggest either\n            // the above ReplayOffsetSleepThresholdMs/ThreadSpinIterations are\n            // too high or the replay host does not have enough CPU capacity to\n            // replay the source workload.\n            elapsedMs = (Stopwatch.GetTimestamp() - startingTimestamp) / TimeSpan.TicksPerMillisecond;\n            if (elapsedMs > delayMs + 100)\n            {\n                logger.Warn(\"Requested delay was {requestedDelay}ms but actual delay was {actualDelay}ms which may indicate that this tool cannot keep up with the source workload, possibly due to insufficient CPU Cores for parallel processing\", delayMs, elapsedMs);\n            }\n            else\n            {\n                logger.Debug(\"Requested delay was {requestedDelay}ms and actual delay was {actualDelay}ms\", delayMs, elapsedMs);\n            }\n        }\n\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected void Dispose(bool disposing)\n        {\n            if (disposing)\n            {\n                logger.Debug(\"Disposing ReplayWorker\");\n\n                Stop();\n                try\n                {\n                    if (Conn != null)\n                    {\n                        if (Conn.State == ConnectionState.Open)\n                        {\n                            try { Conn.Close(); } catch (Exception) { /* swallow */ }\n                            try { Conn.Dispose(); } catch (Exception) { /* swallow */ }\n                        }\n                        Conn = null;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    logger.Warn(ex);\n                }\n                try\n                {\n                    if (runner != null)\n                    {\n                        while (!(runner.IsCompleted || runner.IsFaulted || runner.IsCanceled))\n                        {\n                            _spinWait.SpinOnce();\n                        }\n                        runner.Dispose();\n                        runner = null;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    logger.Warn(ex);\n                }\n                try\n                {\n                    if (tokenSource != null)\n                    {\n                        tokenSource.Dispose();\n                        tokenSource = null;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    logger.Warn(ex);\n                }\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "WorkloadTools/Consumer/Replay/ReplayWorker.cs.bak",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading;\n\nnamespace WorkloadTools.Consumer.Replay\n{\n    class ReplayWorker\n    {\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        private SqlConnection conn { get; set; }\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n\n        public int ReplayIntervalSeconds { get; set; } = 0;\n        public bool StopOnError { get; set; } = false;\n        public string Name { get; set; }\n        public int SPID { get; set; }\n\n        private long commandCount = 0;\n\n        public int CommandCount\n        {\n            [MethodImpl(MethodImplOptions.Synchronized)]\n            get\n            {\n                return Commands.Count;\n            }\n        }\n\n        private Queue<ReplayCommand> Commands = new Queue<ReplayCommand>();\n\n        private void InitializeConnection()\n        {\n            logger.Info(String.Format(\"Worker [{0}] - Connecting to server {1} for replay...\", Name, ConnectionInfo.ServerName));\n            string connString = BuildConnectionString();\n            conn = new SqlConnection(connString);\n            conn.Open();\n            logger.Info(String.Format(\"Worker [{0}] - Connected\", Name));\n        }\n\n        private string BuildConnectionString()\n        {\n            string connectionString = \"Data Source=\" + ConnectionInfo.ServerName + \";\";\n            if (String.IsNullOrEmpty(ConnectionInfo.DatabaseName))\n            {\n                connectionString += \"Initial Catalog = master; \";\n            }\n            else\n            {\n                connectionString += \"Initial Catalog = \" + ConnectionInfo.DatabaseName + \"; \";\n            }\n            if (String.IsNullOrEmpty(ConnectionInfo.UserName))\n            {\n                connectionString += \"Integrated Security = SSPI; \";\n            }\n            else\n            {\n                connectionString += \"User Id = \" + ConnectionInfo.UserName + \"; \";\n                connectionString += \"Password = \" + ConnectionInfo.Password + \"; \";\n            }\n            connectionString += \"Application Name=WorkloadTools;\";\n            return connectionString;\n        }\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public void ExecuteNextCommand()\n        {\n            ReplayCommand cmd = GetNextCommand();\n            if (cmd != null)\n            {\n                ExecuteCommand(cmd);\n                commandCount++;\n            }\n        }\n\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public ReplayCommand GetNextCommand()\n        {\n            if (Commands.Count == 0)\n            {\n                return null;\n            }\n            return Commands.Dequeue();\n        }\n\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public void ExecuteCommand(ReplayCommand command)\n        {\n            if (conn == null)\n            {\n                try\n                {\n                    InitializeConnection();\n                }\n                catch (SqlException se)\n                {\n                    logger.Error(se.Message);\n                    logger.Error(String.Format(\"Worker [{0}] - Unable to acquire the connection. Quitting the ReplayWorker\", Name));\n                    return;\n                }\n            }\n\n\n            while (conn.State == System.Data.ConnectionState.Connecting)\n            {\n                Thread.Sleep(5);\n            }\n\n            if ((conn.State == System.Data.ConnectionState.Closed) || (conn.State == System.Data.ConnectionState.Broken))\n            {\n                conn.Open();\n            }\n\n            try\n            {\n                if (conn.Database != command.Database)\n                {\n                    logger.Trace(String.Format(\"Worker [{0}] - Changing database to {1} \", Name, command.Database));\n                    conn.ChangeDatabase(command.Database);\n                }\n\n                SqlCommand cmd = new SqlCommand(command.CommandText);\n                cmd.Connection = conn;\n                cmd.ExecuteNonQuery();\n\n                logger.Trace(String.Format(\"Worker [{0}] - SUCCES - \\n{1}\", Name, command.CommandText));\n                if (commandCount % 100 == 0)\n                    logger.Info(String.Format(\"Worker [{0}] - {1} commands executed.\", Name, commandCount.ToString()));\n            }\n            catch (Exception e)\n            {\n                if (StopOnError)\n                {\n                    logger.Error(String.Format(\"Worker[{0}] - Error: \\n{1}\", Name, command.CommandText));\n                    throw;\n                }\n                else\n                {\n                    logger.Trace(String.Format(\"Worker [{0}] - Error: {1}\", Name, command.CommandText));\n                    logger.Warn(String.Format(\"Worker [{0}] - Error: {1}\", Name, e.Message));\n                    logger.Trace(e.StackTrace);\n                }\n            }\n        }\n\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public void AppendCommand(ReplayCommand cmd)\n        {\n            Commands.Enqueue(cmd);\n        }\n\n        [MethodImpl(MethodImplOptions.Synchronized)]\n        public void AppendCommand(string commandText, string databaseName)\n        {\n            Commands.Enqueue(new ReplayCommand() { CommandText = commandText, Database = databaseName });\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/Replay/ResultSetConsumer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Consumer.Replay\n{\n    public class ResultSetConsumer : IDisposable\n    {\n        private readonly SqlDataReader reader;\n\n        public ResultSetConsumer(SqlDataReader sqlDataReader)\n        {\n            reader = sqlDataReader;\n        }\n\n        public void Consume()\n        {\n            while (reader.Read())\n            {\n                // do nothing: I just need to pull all the rows\n            }\n        }\n\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected virtual void Dispose(bool disposing) { }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/WorkloadConsumer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Consumer\n{\n    public abstract class WorkloadConsumer : IDisposable\n    {\n        public abstract void Consume(WorkloadEvent evt);\n\n        public abstract bool HasMoreEvents();\n\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected virtual void Dispose(bool disposing) { }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Consumer/WorkloadFile/WorkloadFileWriterConsumer.cs",
    "content": "﻿using NLog;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SQLite;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace WorkloadTools.Consumer.WorkloadFile\n{\n    public class WorkloadFileWriterConsumer : BufferedWorkloadConsumer\n    {\n\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public string OutputFile { get; set; }\n        public static int CACHE_SIZE = 1000;\n        // controls how often the data is written to the database\n        // if not enough events are generated to flush the cache\n        // a flush is forced every CACHE_FLUSH_HEARTBEAT_MINUTES \n        public static int CACHE_FLUSH_HEARTBEAT_MINUTES = 1;\n        public DateTime lastFlush = DateTime.Now;\n\n        private bool databaseInitialized = false;\n        private int row_id = 1;\n        private string connectionString;\n\n        private readonly object syncRoot = new object();\n\n        private SQLiteConnection conn;\n        private SQLiteCommand events_cmd;\n        private SQLiteCommand events_update_cmd;\n        private SQLiteCommand waits_cmd;\n        private SQLiteCommand diskperf_cmd;\n        private SQLiteCommand counters_cmd;\n\n        private long _rowsInserted = 0;\n\n        private readonly string insert_events = @\"\n                INSERT INTO Events (\n                    row_id,\n                    event_sequence,\n                    event_type,\n                    start_time,\n                    client_app_name,\n                    client_host_name,\n                    database_name,\n                    server_principal_name,\n                    session_id,\n                    sql_text,\n                    cpu,\n                    duration,\n                    reads,\n                    writes\n                )\n                VALUES (\n                    $row_id,\n                    $event_sequence,\n                    $event_type,\n                    $start_time,\n                    $client_app_name,\n                    $client_host_name,\n                    $database_name,\n                    $server_principal_name,\n                    $session_id,\n                    $sql_text,\n                    $cpu,\n                    $duration,\n                    $reads,\n                    $writes\n                );\";\n\n        private readonly string update_events = @\"\n                UPDATE Events SET cpu = $cpu,\n                                  duration = $duration,\n                                  reads = $reads,\n                                  writes = $writes,\n                                  sql_text = $sql_text\n                WHERE row_id = (SELECT row_id \n                                FROM Events\n                                WHERE session_id = $session_id\n                                AND event_sequence < $event_sequence\n                                AND IFNULL(duration, 0) = 0\n                                AND event_type IN (2, -3) /* 2 = RPCStarting, -3 = BatchStarting */\n                                ORDER BY EVENT_SEQUENCE DESC\n                                LIMIT 1);\";\n\n        private readonly string insert_waits = @\"\n                INSERT INTO Waits (\n                    row_id,\n                    wait_type,\n                    wait_sec,\n                    resource_sec,\n                    signal_sec,\n                    wait_count\n                )\n                VALUES (\n                    $row_id,\n                    $wait_type,\n                    $wait_sec,\n                    $resource_sec,\n                    $signal_sec,\n                    $wait_count\n                );\";\n\n        private readonly string insert_counters = @\"\n                INSERT INTO Counters (\n                    row_id,\n                    name,\n                    value\n                )\n                VALUES (\n                    $row_id,\n                    $name,\n                    $value\n                );\";\n\n\n        private readonly string insert_diskperf = @\"\n                INSERT INTO DiskPerf (\n                    row_id,\n                    database_name,\n                    physical_filename,\n                    logical_filename,\n                    file_type,\n                    volume_mount_point,\n                    read_latency_ms,\n                    reads,\n                    write_bytes,\n                    write_latency_ms,\n                    writes,\n                    write_bytes,\n                    cum_read_latency_ms,\n                    cum_reads,\n                    cum_read_bytes,\n                    cum_write_latency_ms,\n                    cum_writes,\n                    cum_write_bytes\n                )\n                VALUES (\n                    $row_id,\n                    $database_name,\n                    $physical_filename,\n                    $logical_filename,\n                    $file_type,\n                    $volume_mount_point,\n                    $read_latency_ms,\n                    $reads,\n                    $write_bytes,\n                    $write_latency_ms,\n                    $writes,\n                    $write_bytes,\n                    $cum_read_latency_ms,\n                    $cum_reads,\n                    $cum_write_bytes,\n                    $cum_write_latency_ms,\n                    $cum_writes,\n                    $cum_write_bytes\n                );\";\n\n        private readonly Queue<WorkloadEvent> cache = new Queue<WorkloadEvent>(CACHE_SIZE);\n\n        private bool forceFlush = false;\n\n        public override void ConsumeBuffered(WorkloadEvent evt)\n        {\n            if (!databaseInitialized)\n            {\n                InitializeDatabase();\n            }\n\n            lock (syncRoot)\n            {\n                cache.Enqueue(evt);\n\n                Flush();\n            }\n        }\n\n        private void Flush()\n        {\n            if (DateTime.Now.Subtract(lastFlush).TotalMinutes >= CACHE_FLUSH_HEARTBEAT_MINUTES)\n            {\n                forceFlush = true;\n            }\n\n            if (cache.Count == CACHE_SIZE || forceFlush)\n            {\n                InitializeConnection();\n                var tran = conn.BeginTransaction();\n                try\n                {\n                    lock (syncRoot)\n                    {\n                        while (cache.Count > 0)\n                        {\n                            InsertEvent(cache.Dequeue());\n                        }\n                    }\n                    tran.Commit();\n                }\n                catch\n                {\n                    try\n                    {\n                        tran.Rollback();\n                    }\n                    catch (Exception)\n                    {\n                        //swallow\n                    }\n                    throw;\n                }\n                finally\n                {\n                    lastFlush = DateTime.Now;\n                    forceFlush = false;\n                }\n            }\n        }\n\n        /*\n         * Initializes the database connection.\n         * Connection string settings that affect performance:\n         * - synchronous = off | full | normal\n         * - journal mode = memory | delete | persist | off\n         * - cache size = <number>\n         * - temp store = memory\n         * - locking mode = exclusive\n         */\n        private void InitializeConnection()\n        {\n            if (conn == null && connectionString != null)\n            {\n                conn = new SQLiteConnection(connectionString);\n                conn.Open();\n            }\n\n            if (events_cmd == null)\n            {\n                events_cmd = new SQLiteCommand(insert_events, conn);\n            }\n\n            if (events_update_cmd == null)\n            {\n                events_update_cmd = new SQLiteCommand(update_events, conn);\n            }\n\n            if (waits_cmd == null)\n            {\n                waits_cmd = new SQLiteCommand(insert_waits, conn);\n            }\n\n            if (diskperf_cmd == null)\n            {\n                diskperf_cmd = new SQLiteCommand(insert_diskperf, conn);\n            }\n\n            if (counters_cmd == null)\n            {\n                counters_cmd = new SQLiteCommand(insert_counters, conn);\n            }\n        }\n\n        private void UpdateExecutionEvent(WorkloadEvent evnt)\n        {\n            var evt = (ExecutionWorkloadEvent)evnt;\n\n            _ = events_update_cmd.Parameters.AddWithValue(\"$event_sequence\", evt.EventSequence);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$session_id\", evt.SPID);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$cpu\", evt.CPU);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$duration\", evt.Duration);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$reads\", evt.Reads);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$writes\", evt.Writes);\n            _ = events_update_cmd.Parameters.AddWithValue(\"$sql_text\", evt.Text);\n\n            int rowcount;\n            rowcount = events_update_cmd.ExecuteNonQuery();\n            if (rowcount == 0)\n            {\n                logger.Debug(\"Starting event not found - \" + $\"EventSequence: {evt.EventSequence}\");\n            }\n        }\n\n        private void InsertExecutionEvent(WorkloadEvent evnt)\n        {\n            var evt = (ExecutionWorkloadEvent)evnt;\n\n            _ = events_cmd.Parameters.AddWithValue(\"$row_id\", row_id++);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_sequence\", evt.EventSequence);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_type\", evt.Type);\n            _ = events_cmd.Parameters.AddWithValue(\"$start_time\", evt.StartTime);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_app_name\", evt.ApplicationName);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_host_name\", evt.HostName);\n            _ = events_cmd.Parameters.AddWithValue(\"$database_name\", evt.DatabaseName);\n            _ = events_cmd.Parameters.AddWithValue(\"$server_principal_name\", evt.LoginName);\n            _ = events_cmd.Parameters.AddWithValue(\"$session_id\", evt.SPID);\n            _ = events_cmd.Parameters.AddWithValue(\"$sql_text\", evt.Text);\n            _ = events_cmd.Parameters.AddWithValue(\"$cpu\", evt.CPU);\n            _ = events_cmd.Parameters.AddWithValue(\"$duration\", evt.Duration);\n            _ = events_cmd.Parameters.AddWithValue(\"$reads\", evt.Reads);\n            _ = events_cmd.Parameters.AddWithValue(\"$writes\", evt.Writes);\n\n            _ = events_cmd.ExecuteNonQuery();\n\n        }\n\n        private void InsertEvent(WorkloadEvent evnt)\n        {\n            try\n            {\n                if (evnt is ExecutionWorkloadEvent)\n                {\n                    if ((evnt.Type == WorkloadEvent.EventType.BatchCompleted) || (evnt.Type == WorkloadEvent.EventType.RPCCompleted))\n                    {\n                        UpdateExecutionEvent(evnt);\n                    }\n                    else\n                    {\n                        InsertExecutionEvent(evnt);\n                    }\n                }\n\n                if (evnt is CounterWorkloadEvent)\n                {\n                    InsertCounterEvent(evnt);\n                }\n\n                if (evnt is WaitStatsWorkloadEvent)\n                {\n                    InsertWaitEvent(evnt);\n                }\n\n                if (evnt is DiskPerfWorkloadEvent)\n                {\n                    InsertDiskPerfEvent(evnt);\n                }\n\n                _rowsInserted++;\n                if ((_rowsInserted % CACHE_SIZE == 0) || forceFlush)\n                {\n                    if (forceFlush)\n                    {\n                        forceFlush = false;\n                    }\n\n                    logger.Info(\"{_rowsInserted} events saved\", _rowsInserted);\n                }\n            }\n            catch (Exception e)\n            {\n                if (stopped)\n                {\n                    return;\n                }\n\n                logger.Error(e, \"Unable to write to the destination file\");\n                throw;\n            }\n\n        }\n\n        private void InsertWaitEvent(WorkloadEvent evnt)\n        {\n            var evt = (WaitStatsWorkloadEvent)evnt;\n\n            var eventRowId = row_id++;\n\n            _ = events_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_sequence\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_type\", evt.Type);\n            _ = events_cmd.Parameters.AddWithValue(\"$start_time\", evt.StartTime);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_app_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_host_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$database_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$server_principal_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$session_id\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$sql_text\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$cpu\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$duration\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$reads\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$writes\", null);\n\n            _ = events_cmd.ExecuteNonQuery();\n\n            foreach (DataRow dr in evt.Waits.Rows)\n            {\n                _ = waits_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n                _ = waits_cmd.Parameters.AddWithValue(\"$wait_type\", dr[\"wait_type\"]);\n                _ = waits_cmd.Parameters.AddWithValue(\"$wait_sec\", dr[\"wait_sec\"]);\n                _ = waits_cmd.Parameters.AddWithValue(\"$resource_sec\", dr[\"resource_sec\"]);\n                _ = waits_cmd.Parameters.AddWithValue(\"$signal_sec\", dr[\"signal_sec\"]);\n                _ = waits_cmd.Parameters.AddWithValue(\"$wait_count\", dr[\"wait_count\"]);\n\n                _ = waits_cmd.ExecuteNonQuery();\n            }\n        }\n\n\n        private void InsertDiskPerfEvent(WorkloadEvent evnt)\n        {\n            var evt = (DiskPerfWorkloadEvent)evnt;\n\n            var eventRowId = row_id++;\n\n            _ = events_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_sequence\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_type\", evt.Type);\n            _ = events_cmd.Parameters.AddWithValue(\"$start_time\", evt.StartTime);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_app_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_host_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$database_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$server_principal_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$session_id\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$sql_text\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$cpu\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$duration\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$reads\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$writes\", null);\n\n            _ = events_cmd.ExecuteNonQuery();\n\n            foreach (DataRow dr in evt.DiskPerf.Rows)\n            {\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$database_name\", dr[\"database_name\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$physical_filename\", dr[\"physical_filename\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$logical_filename\", dr[\"logical_filename\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$file_type\", dr[\"file_type\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$volume_mount_point\", dr[\"volume_mount_point\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$read_latency_ms\", dr[\"read_latency_ms\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$reads\", dr[\"reads\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$read_bytes\", dr[\"read_bytes\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$write_latency_ms\", dr[\"write_latency_ms\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$writes\", dr[\"writes\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$write_bytes\", dr[\"write_bytes\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_read_latency_ms\", dr[\"cum_read_latency_ms\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_reads\", dr[\"cum_reads\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_read_bytes\", dr[\"cum_read_bytes\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_write_latency_ms\", dr[\"cum_write_latency_ms\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_writes\", dr[\"cum_writes\"]);\n                _ = diskperf_cmd.Parameters.AddWithValue(\"$cum_write_bytes\", dr[\"cum_write_bytes\"]);\n\n                _ = diskperf_cmd.ExecuteNonQuery();\n            }\n        }\n\n        private void InsertCounterEvent(WorkloadEvent evnt)\n        {\n            var evt = (CounterWorkloadEvent)evnt;\n\n            var eventRowId = row_id++;\n\n            _ = events_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_sequence\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$event_type\", evt.Type);\n            _ = events_cmd.Parameters.AddWithValue(\"$start_time\", evt.StartTime);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_app_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$client_host_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$database_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$server_principal_name\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$session_id\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$sql_text\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$cpu\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$duration\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$reads\", null);\n            _ = events_cmd.Parameters.AddWithValue(\"$writes\", null);\n\n            _ = events_cmd.ExecuteNonQuery();\n\n            foreach (var dr in evt.Counters)\n            {\n                _ = counters_cmd.Parameters.AddWithValue(\"$row_id\", eventRowId);\n                _ = counters_cmd.Parameters.AddWithValue(\"$name\", dr.Key.ToString());\n                _ = counters_cmd.Parameters.AddWithValue(\"$value\", dr.Value);\n\n                _ = counters_cmd.ExecuteNonQuery();\n            }\n        }\n\n        public void InitializeDatabase()\n        {\n            logger.Info(\"Writing event data to {OutputFile}\", OutputFile);\n\n            if (!File.Exists(OutputFile))\n            {\n                _ = Directory.CreateDirectory(Directory.GetParent(OutputFile).FullName);\n                SQLiteConnection.CreateFile(OutputFile);\n            }\n\n            var sqlCreateTable = $@\"\nCREATE TABLE IF NOT EXISTS FileProperties (\n    name TEXT NOT NULL PRIMARY KEY,\n    value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS Events (\n    row_id INTEGER PRIMARY KEY,\n    event_sequence INTEGER,\n    event_type INTEGER,\n    start_time date NOT NULL,\n    client_app_name TEXT NULL, \n    client_host_name TEXT NULL, \n    database_name TEXT NULL, \n    server_principal_name TEXT NULL, \n    session_id INTEGER NULL, \n    sql_text  TEXT NULL,\n    cpu INTEGER NULL,\n    duration INTEGER NULL,\n    reads INTEGER NULL,\n    writes INTEGER NULL\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS Index_Session_ID_Event_Sequence ON Events(\n    session_id ASC,\n    event_sequence DESC\n);\n\nCREATE INDEX IF NOT EXISTS Index_Start_Time_Row_ID ON Events(\n    Start_Time ASC,\n    Row_ID ASC\n);\n\nCREATE TABLE IF NOT EXISTS Counters (\n    row_id INTEGER,\n    name TEXT NULL,\n    value FLOAT NULL\n);\n\nCREATE TABLE IF NOT EXISTS Waits (\n    row_id INTEGER,\n    wait_type TEXT NULL,\n    wait_sec INTEGER NULL,\n    resource_sec INTEGER NULL,\n    signal_sec INTEGER NULL,\n    wait_count INTEGER NULL \n);\n\n\nCREATE TABLE IF NOT EXISTS DiskPerf (\n    row_id INTEGER,\n    database_name TEXT NULL,\n    physical_filename TEXT NULL,\n    logical_filename TEXT NULL,\n    file_type TEXT NULL,\n    volume_mount_point TEXT NULL,\n    read_latency_ms INTEGER NULL,\n    reads INTEGER NULL,\n    read_bytes INTEGER NULL,\n    write_latency_ms INTEGER NULL,\n    writes INTEGER NULL,\n    write_bytes INTEGER NULL,\n    cum_read_latency_ms INTEGER NULL,\n    cum_reads INTEGER NULL,\n    cum_read_bytes INTEGER NULL,\n    cum_write_latency_ms INTEGER NULL,\n    cum_writes INTEGER NULL,\n    cum_write_bytes INTEGER NULL\n);\n\n\nINSERT INTO FileProperties (name, value)\nSELECT 'FormatVersion','{Assembly.GetEntryAssembly().GetName().Version}'\n WHERE NOT EXISTS (SELECT *\n                     FROM FileProperties\n                    WHERE name = 'FormatVersion'\n                  );\";\n\n            var sqlMaxSeq = @\"SELECT COALESCE(MAX(row_id), 0) + 1 FROM Events;\";\n\n            connectionString = \"Data Source=\" + OutputFile + \";Version=3;Cache Size=10000;Locking Mode=Exclusive;Journal Mode=Memory;\";\n\n            using (var m_dbConnection = new SQLiteConnection(connectionString))\n            {\n                m_dbConnection.Open();\n                try\n                {\n                    var command = new SQLiteCommand(sqlCreateTable, m_dbConnection);\n                    _ = command.ExecuteNonQuery();\n\n                    command = new SQLiteCommand(sqlMaxSeq, m_dbConnection);\n                    row_id = Convert.ToInt32(command.ExecuteScalar());\n                }\n                catch (Exception)\n                {\n                    throw;\n                }\n            }\n\n            databaseInitialized = true;\n\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            logger.Info(\"Closing the connection to the output file\");\n\n            // Signal ProcessBuffer to stop so it no longer competes for the connection\n            stopped = true;\n\n            // Wait for the background ProcessBuffer task to finish its current operation.\n            // Use a timeout to avoid blocking indefinitely if the task is unresponsive.\n            try\n            {\n                BufferReader?.Wait(TimeSpan.FromSeconds(60));\n            }\n            catch (Exception)\n            {\n                // Task may have already faulted; continue so we can flush remaining data\n            }\n\n            // At this point ProcessBuffer has stopped (stopped=true exits its loop) or\n            // we timed out. Drain any events still sitting in the base-class Buffer into\n            // the local cache so they are persisted before we close the connection.\n            // ConcurrentQueue.TryDequeue is thread-safe even in the unlikely case that\n            // the task is still winding down.\n            if (databaseInitialized)\n            {\n                WorkloadEvent evt;\n                while (Buffer.TryDequeue(out evt))\n                {\n                    if (evt != null)\n                    {\n                        cache.Enqueue(evt);\n                    }\n                }\n            }\n\n            forceFlush = true;\n            if (conn != null)\n            {\n                Flush();\n            }\n\n            try\n            {\n                conn?.Close();\n                conn?.Dispose();\n\n                events_cmd?.Dispose();\n                waits_cmd?.Dispose();\n                counters_cmd?.Dispose();\n            }\n            catch (Exception)\n            {\n                //ignore\n            }\n        }\n\n        public override bool HasMoreEvents()\n        {\n            return cache.Count > 0 || Buffer.Count > 0;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/CounterWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public class CounterWorkloadEvent : WorkloadEvent\n    {\n        public enum CounterNameEnum\n        {\n            AVG_CPU_USAGE = 1\n        }\n\n        public Dictionary<CounterNameEnum, float> Counters { get; internal set; } = new Dictionary<CounterNameEnum, float>();\n\n        public CounterWorkloadEvent()\n        {\n            Type = EventType.PerformanceCounter;\n        }\n        \n    }\n}\n"
  },
  {
    "path": "WorkloadTools/DiskPerfWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public class DiskPerfWorkloadEvent : WorkloadEvent\n    {\n        public DataTable DiskPerf;\n\n        public DiskPerfWorkloadEvent()\n        {\n            Type = EventType.DiskPerf;\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/ErrorWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools\n{\n\t[Serializable]\n\tpublic class ErrorWorkloadEvent : ExecutionWorkloadEvent\n\t{\n\t\tpublic ErrorWorkloadEvent()\n        {\n            Type = EventType.Error;\n        }\n\t}\n}\n"
  },
  {
    "path": "WorkloadTools/ExecutionWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public class ExecutionWorkloadEvent : WorkloadEvent\n    {\n        public string Text { get; set; }\n        public int? SPID { get; set; }\n        public string ApplicationName { get; set; }\n        public string DatabaseName { get; set; }\n        public string LoginName { get; set; }\n        public string HostName { get; set; }\n        public long? Reads { get; set; }\n        public long? Writes { get; set; }\n        public long? CPU { get; set; }      // MICROSECONDS\n        public long? Duration { get; set; } // MICROSECONDS\n        public long? EventSequence { get; set; }\n        // This is the requested offset in milliseconds\n        // from the the beginning of the workload\n        public double ReplayOffset { get; set; } = 0; // MILLISECONDS \n    }\n}\n"
  },
  {
    "path": "WorkloadTools/FilterPredicate.cs",
    "content": "﻿using System;\n\nnamespace WorkloadTools\n{\n    public abstract class FilterPredicate\n    {\n        private string[] _predicateValue;\n\n        public enum FilterColumnName : byte\n        {\n            DatabaseName = 35,\n            HostName = 8,\n            ApplicationName = 10,\n            LoginName = 11\n        }\n\n        public enum FilterComparisonOperator : byte\n        {\n            Equal = 0,\n            Not_Equal = 1,\n            Greater_Than = 2,\n            Less_Than = 3,\n            Greater_Than_Or_Equal = 4,\n            Less_Than_Or_Equal = 5,\n            LIKE = 6,\n            NOT_LIKE = 7\n        }\n\n        public FilterPredicate()\n        {\n        }\n\n        public FilterColumnName ColumnName { get; set; }\n        public string[] PredicateValue {\n            get => _predicateValue;\n            set {\n                _predicateValue = value;\n                if (value != null)\n                {\n                    ComparisonOperator = new FilterComparisonOperator[_predicateValue.Length];\n                    for (var i = 0; i < value.Length; i++)\n                    {\n                        var thisValue = value[i];\n                        if (!string.IsNullOrEmpty(thisValue) && thisValue.StartsWith(\"^\"))\n                        {\n                            _predicateValue[i] = thisValue.Substring(1);\n                            ComparisonOperator[i] = FilterComparisonOperator.Not_Equal;\n                        }\n                        else\n                        {\n                            ComparisonOperator[i] = FilterComparisonOperator.Equal;\n                        }\n                    }\n                }\n            }\n        }\n        public FilterComparisonOperator[] ComparisonOperator { get; set; }\n        public bool IsPredicateSet { get { return PredicateValue != null; } }\n        public bool IsPushedDown { get; set; } = false;\n\n        public FilterPredicate(FilterColumnName name)\n        {\n            ColumnName = name;\n        }\n\n        public abstract string PushDown();\n\n        protected string EscapeFilter(string value)\n        {\n            return value.Replace(\"'\", \"''\");\n        }\n\n        public static string ComparisonOperatorAsString(FilterComparisonOperator op)\n        {\n            var result = string.Empty;\n            switch (op)\n            {\n                case FilterComparisonOperator.Equal:\n                    result = \"=\";\n                    break;\n                case FilterComparisonOperator.Not_Equal:\n                    result = \"<>\";\n                    break;\n                case FilterComparisonOperator.Greater_Than:\n                    result = \">\";\n                    break;\n                case FilterComparisonOperator.Less_Than:\n                    result = \"<\";\n                    break;\n                case FilterComparisonOperator.Greater_Than_Or_Equal:\n                    result = \">=\";\n                    break;\n                case FilterComparisonOperator.Less_Than_Or_Equal:\n                    result = \"<=\";\n                    break;\n                case FilterComparisonOperator.LIKE:\n                    result = \"LIKE\";\n                    break;\n                case FilterComparisonOperator.NOT_LIKE:\n                    result = \"NOT LIKE\";\n                    break;\n            }\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/IEventQueue.cs",
    "content": "﻿using System;\n\nnamespace WorkloadTools\n{\n\n    public enum EventQueueType\n    {\n        MMF,\n        Sqlite,\n        LiteDB,\n        BinarySerialized\n    }\n\n    public interface IEventQueue : IDisposable\n    {\n        int BufferSize { get; set; }\n\n        bool TryDequeue(out WorkloadEvent result);\n        bool HasMoreElements();\n        void Enqueue(WorkloadEvent evt);\n    }\n}"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/ExtendedEventsEventFilter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public class ExtendedEventsEventFilter : WorkloadEventFilter\n    {\n        public bool IsSqlAzure { get; set; }\n\n        public ExtendedEventsEventFilter()\n        {\n            ApplicationFilter = new ExtendedEventsFilterPredicate(FilterPredicate.FilterColumnName.ApplicationName);\n            DatabaseFilter = new ExtendedEventsFilterPredicate(FilterPredicate.FilterColumnName.DatabaseName);\n            HostFilter = new ExtendedEventsFilterPredicate(FilterPredicate.FilterColumnName.HostName);\n            LoginFilter = new ExtendedEventsFilterPredicate(FilterPredicate.FilterColumnName.LoginName);\n            ((ExtendedEventsFilterPredicate)ApplicationFilter).IsSqlAzure = IsSqlAzure;\n            ((ExtendedEventsFilterPredicate)DatabaseFilter).IsSqlAzure = IsSqlAzure;\n            ((ExtendedEventsFilterPredicate)HostFilter).IsSqlAzure = IsSqlAzure;\n            ((ExtendedEventsFilterPredicate)LoginFilter).IsSqlAzure = IsSqlAzure;\n\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/ExtendedEventsFilterPredicate.cs",
    "content": "﻿using System;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public class ExtendedEventsFilterPredicate : FilterPredicate\n    {\n        public ExtendedEventsFilterPredicate(FilterColumnName name) : base(name)\n        {\n        }\n\n        public bool IsSqlAzure { get; set; }\n\n        public override string PushDown()\n        {\n            if (!IsPredicateSet)\n            {\n                return string.Empty;\n            }\n\n            IsPushedDown = true;\n            var result = \"(\";\n\n            // Implementing multivalued filters with negative values\n            // requires analyzing the syntax of the filters\n            //\n            // Let's say I have a filter like this:\n            // \"DatabaseFilter\" = [\"master\",\"model\",\"^tempdb\",\"msdb\"]\n            //\n            // It literally says I want master, model and msdb, but I don't want tempdb\n            // In this case, it means that I want master, model and tempdb\n            // But if I only had negative filters, it would mean anything but those databases.\n\n            var hasPositives = false;\n            var hasNegatives = false;\n\n            for (var i = 0; i < ComparisonOperator.Length; i++)\n            {\n                if (ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    hasNegatives = true;\n                }\n                else\n                {\n                    hasPositives = true;\n                }\n            }\n\n            for (var i = 0; i < PredicateValue.Length; i++)\n            {\n                if (hasNegatives && hasPositives && ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    // In this case I only care for the positives\n                    continue;\n                }\n\n                if (i > 0)\n                {\n                    if (hasNegatives && !hasPositives)\n                    {\n                        result += \" AND \";\n                    }\n                    else\n                    {\n                        result += \" OR \";\n                    }\n                }\n\n                switch (ColumnName)\n                {\n                    case FilterColumnName.ApplicationName:\n                        result += \"sqlserver.client_app_name\";\n                        break;\n                    case FilterColumnName.HostName:\n                        result += \"sqlserver.client_hostname\";\n                        break;\n                    case FilterColumnName.LoginName:\n                        if (IsSqlAzure)\n                        {\n                            result += \"sqlserver.username\";\n                        }\n                        else\n                        {\n                            result += \"sqlserver.server_principal_name\";\n                        }\n                        break;\n                    case FilterColumnName.DatabaseName:\n                        result += \"sqlserver.database_name\";\n                        break;\n                }\n                result += \" \" + FilterPredicate.ComparisonOperatorAsString(ComparisonOperator[i]) + \" N'\" + EscapeFilter(PredicateValue[i]) + \"'\";\n            }\n            result += \")\";\n            return result;\n        }\n    }\n}"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/ExtendedEventsWorkloadListener.cs",
    "content": "﻿using Microsoft.SqlServer.XEvent.Linq;\nusing NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public class ExtendedEventsWorkloadListener : WorkloadListener\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private SpinWait spin = new SpinWait();\n\n        protected XEventDataReader reader;\n\n        public string SessionName { get; set; } = \"sqlworkload\";\n\n        public bool ReuseExistingSession { get; set; } = false;\n\n        public enum ServerType\n        {\n            FullInstance,\n            AzureSqlDatabase,\n            AzureSqlManagedInstance,\n            LocalDB\n        }\n\n        private ServerType serverType { get; set; }\n\n        // Path to the file target\n        // Mandatory on SqlAzure\n        // If not specified, On Premises SQLServer will use the streaming API\n        public string FileTargetPath { get; set; }\n\n        private long eventCount;\n\n        public ExtendedEventsWorkloadListener() : base()\n        {\n            Filter = new ExtendedEventsEventFilter();\n            Source = WorkloadController.BaseLocation + \"\\\\Listener\\\\ExtendedEvents\\\\sqlworkload.sql\";\n        }\n\n        public override void Initialize()\n        {\n            using (var conn = new SqlConnection())\n            {\n                if (ConnectionInfo == null)\n                {\n                    throw new ArgumentNullException(\"You need to provide ConnectionInfo to inizialize an ExtendedEventsWorkloadListener\");\n                }\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n\n                LoadServerType(conn);\n\n                if (serverType == ServerType.AzureSqlDatabase)\n                {\n                    if (ConnectionInfo.DatabaseName == null)\n                    {\n                        throw new ArgumentException(\"Azure SqlDatabase does not support starting Extended Events sessions on the master database. Please specify a database name.\");\n                    }\n\n                    ((ExtendedEventsEventFilter)Filter).IsSqlAzure = true;\n                }\n                else\n                {\n                    ConnectionInfo.DatabaseName = \"master\";\n                }\n\n                logger.Info($\"Reading Extended Events session definition from {Source}\");\n\n                string sessionSql = null;\n                try\n                {\n                    sessionSql = System.IO.File.ReadAllText(Source);\n\n                    // Push Down EventFilters\n                    var filters = string.Empty;\n\n                    var appFilter = Filter.ApplicationFilter.PushDown();\n                    var dbFilter = Filter.DatabaseFilter.PushDown();\n                    var hostFilter = Filter.HostFilter.PushDown();\n                    var loginFilter = Filter.LoginFilter.PushDown();\n\n                    if (appFilter != string.Empty)\n                    {\n                        filters += ((filters == string.Empty) ? string.Empty : \" AND \") + appFilter;\n                    }\n                    if (dbFilter != string.Empty)\n                    {\n                        filters += ((filters == string.Empty) ? string.Empty : \" AND \") + dbFilter;\n                    }\n                    if (hostFilter != string.Empty)\n                    {\n                        filters += ((filters == string.Empty) ? string.Empty : \" AND \") + hostFilter;\n                    }\n                    if (loginFilter != string.Empty)\n                    {\n                        filters += ((filters == string.Empty) ? string.Empty : \" AND \") + loginFilter;\n                    }\n\n                    if (filters != string.Empty)\n                    {\n                        filters = \"WHERE \" + filters;\n                    }\n\n                    var sessionType = serverType == ServerType.AzureSqlDatabase ? \"DATABASE\" : \"SERVER\";\n                    var principalName = serverType == ServerType.AzureSqlDatabase ? \"username\" : \"server_principal_name\";\n\n                    sessionSql = string.Format(sessionSql, filters, sessionType, principalName);\n\n                }\n                catch (Exception e)\n                {\n                    throw new ArgumentException(\"Cannot open the source script to start the extended events session\", e);\n                }\n\n                if (!ReuseExistingSession)\n                {\n                    StopSession(conn);\n\n                    using (var cmd = conn.CreateCommand())\n                    {\n                        cmd.CommandText = sessionSql;\n                        _ = cmd.ExecuteNonQuery();\n                    }\n                    if (FileTargetPath != null)\n                    {\n                        var sql = @\"\n                        ALTER EVENT SESSION [{2}] ON {0}\n                        ADD TARGET package0.event_file(SET filename=N'{1}',max_file_size=(100))\n                    \";\n\n                        sql = string.Format(sql, serverType == ServerType.FullInstance ? \"SERVER\" : \"DATABASE\", FileTargetPath, SessionName);\n\n                        using (var cmd = conn.CreateCommand())\n                        {\n                            cmd.CommandText = sql;\n                            _ = cmd.ExecuteNonQuery();\n                        }\n                    }\n                }\n\n                // Mark the transaction\n                SetTransactionMark(serverType != ServerType.AzureSqlDatabase);\n\n                _ = Task.Factory.StartNew(() => ReadEvents());\n\n                //Initialize the source of performance counters events\n                _ = Task.Factory.StartNew(() => ReadPerfCountersEvents());\n\n                // Initialize the source of wait stats events\n                _ = Task.Factory.StartNew(() => ReadWaitStatsEvents());\n\n                // Initialize the source of disk performance stats\n                _ = Task.Factory.StartNew(() => ReadDiskPerformanceEvents());\n            }\n        }\n\n        public override WorkloadEvent Read()\n        {\n            try\n            {\n                WorkloadEvent result = null;\n                while (!Events.TryDequeue(out result))\n                {\n                    if (stopped)\n                    {\n                        return null;\n                    }\n\n                    spin.SpinOnce();\n                }\n                eventCount++;\n                return result;\n            }\n            catch (Exception)\n            {\n                if (stopped)\n                {\n                    return null;\n                }\n                else\n                {\n                    throw;\n                }\n            }\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            stopped = true;\n            try\n            {\n                logger.Info($\"Disposing ExtendedEventsWorkloadListener.\");\n                logger.Debug($\"[{eventCount}] events read.\");\n                logger.Debug($\"Events in the queue? [{Events.HasMoreElements()}]\");\n                if(reader != null)\n                {\n                    reader.Stop();\n                }\n                if (!ReuseExistingSession)\n                {\n                    using (var conn = new SqlConnection())\n                    {\n                        conn.ConnectionString = ConnectionInfo.ConnectionString();\n                        conn.Open();\n                        StopSession(conn);\n                    }\n                }\n            }\n            catch (Exception x)\n            {\n                // swallow\n                logger.Warn($\"Error disposing ExtendedEventWorkloadListener: {x.Message}\");\n            }\n            logger.Info($\"Extended Events session [{SessionName}] stopped successfully.\");\n        }\n\n        private void StopSession(SqlConnection conn)\n        {\n            var sql = @\"\n                DECLARE @condition bit = 0;\n\n                IF SERVERPROPERTY('Edition') = 'SQL Azure'\n                    AND SERVERPROPERTY('EngineEdition') = 5\n                BEGIN\n\t                SELECT @condition = 1\n\t                WHERE EXISTS (\n\t\t                SELECT *\n\t\t                FROM sys.database_event_sessions\n\t\t                WHERE name = '{1}'\n\t                )\n                END\n                ELSE\n                BEGIN \n\t                SELECT @condition = 1\n\t                WHERE EXISTS (\n\t\t                SELECT *\n\t\t                FROM sys.server_event_sessions\n\t\t                WHERE name = '{1}'\n\t                )\n                END\n\n                IF @condition = 1\n                BEGIN\n\t                BEGIN TRY\n\t\t                ALTER EVENT SESSION [{1}] ON {0} STATE = STOP;\n\t                END TRY\n\t                BEGIN CATCH\n\t\t                -- whoops...\n\t\t                PRINT ERROR_MESSAGE()\n\t                END CATCH\n\t                BEGIN TRY\n\t\t                DROP EVENT SESSION [{1}] ON {0};\n\t                END TRY\n\t                BEGIN CATCH\n\t\t                -- whoops...\n\t\t                PRINT ERROR_MESSAGE()\n\t                END CATCH\n                END\n            \";\n            sql = string.Format(sql, serverType == ServerType.AzureSqlDatabase ? \"DATABASE\" : \"SERVER\", SessionName);\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = sql;\n                _ = cmd.ExecuteNonQuery();\n            }\n        }\n\n        private void ReadEvents()\n        {\n            try\n            {\n\n                if (FileTargetPath == null)\n                {\n                    reader = new StreamXEventDataReader(ConnectionInfo.ConnectionString(), SessionName, Events);\n                }\n                else\n                {\n                    reader = new FileTargetXEventDataReader(ConnectionInfo.ConnectionString(), SessionName, Events, serverType);\n                }\n\n                reader.ReadEvents();\n\n            }\n            catch (Exception ex)\n            {\n                if (!stopped)\n                {\n                    logger.Error(ex.Message);\n                    logger.Error(ex.StackTrace);\n\n                    if (ex.InnerException != null)\n                    {\n                        logger.Error(ex.InnerException.Message);\n                    }\n\n                    Dispose();\n                }\n                else\n                {\n                    logger.Warn(ex, \"The shutdown workflow generated a warning:\");\n                }\n            }\n        }\n\n        private void LoadServerType(SqlConnection conn)\n        {\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = \"SELECT SERVERPROPERTY('Edition')\";\n                var edition = (string)cmd.ExecuteScalar();\n                cmd.CommandText = \"SELECT SERVERPROPERTY('EngineEdition')\";\n                var engineEdition = (int)cmd.ExecuteScalar();\n                if (edition == \"SQL Azure\")\n                {\n                    serverType = ServerType.AzureSqlDatabase;\n                    if (engineEdition == 8)\n                    {\n                        serverType = ServerType.AzureSqlManagedInstance;\n                    }\n                }\n                else\n                {\n                    serverType = ServerType.FullInstance;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/FileTargetXEventDataReader.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Xml;\nusing System.Xml.Linq;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public class FileTargetXEventDataReader : XEventDataReader, IDisposable\n    {\n        private readonly RingBuffer<ReadIteration> ReadIterations = new RingBuffer<ReadIteration>(10);\n\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private bool stopped = false;\n\n        public FileTargetXEventDataReader(string connectionString, string sessionName, IEventQueue events, ExtendedEventsWorkloadListener.ServerType serverType) : base(connectionString, sessionName, events, serverType)\n        {\n        }\n\n        public override void ReadEvents()\n        {\n            try\n            {\n                while (!stopped)\n                {\n                    using (var conn = new SqlConnection())\n                    {\n                        conn.ConnectionString = ConnectionString;\n                        conn.Open();\n\n                        ReadIteration previousIteration = null;\n                        if(ReadIterations.Count > 0)\n                        {\n                            previousIteration = ReadIterations.Last();\n                        }\n                        var currentIteration = InitializeReadIteration(conn, previousIteration);\n                        if (currentIteration != null)\n                        {\n                            ReadIterations.Add(currentIteration);\n                        }\n                        else\n                        {\n                            Stop();\n                            break;\n                        }\n\n                        ReadXEData(conn, currentIteration);\n\n                        // if reading from localdb one iteration is enough\n                        if (ServerType == ExtendedEventsWorkloadListener.ServerType.LocalDB)\n                        {\n                            break;\n                        }\n\n                    }\n                }\n                logger.Info($\"{EventCount} events captured.\");\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n                logger.Error(ex.StackTrace);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n            }\n        }\n\n        private void ReadXEData(SqlConnection conn, ReadIteration currentIteration)\n        {\n\n            var sqlXE = @\"\n                SELECT event_data, file_name, file_offset\n                FROM sys.fn_xe_file_target_read_file(\n                    @filename, \n                    NULL, \n                    @initial_file_name, \n                    @initial_offset\n                )\n            \";\n\n            logger.Debug(\"Reading XE data...\");\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = sqlXE;\n                cmd.CommandTimeout = 0;\n\n                var paramPath = cmd.Parameters.Add(\"@filename\", System.Data.SqlDbType.NVarChar, 260);\n                if (ServerType != ExtendedEventsWorkloadListener.ServerType.AzureSqlDatabase)\n                {\n                    paramPath.Value = currentIteration.GetXEFilePattern();\n                }\n                else\n                {\n                    // Azure SqlDatabase does not support wildcards in file names\n                    // Specify an exact file name\n                    paramPath.Value = currentIteration.StartFileName;\n                } \n\n                var paramInitialFile = cmd.Parameters.Add(\"@initial_file_name\", System.Data.SqlDbType.NVarChar, 260);\n                paramInitialFile.Value = currentIteration.StartFileName;\n\n                var paramInitialOffset = cmd.Parameters.Add(\"@initial_offset\", System.Data.SqlDbType.BigInt);\n                paramInitialOffset.Value = currentIteration.GetInitialOffset();\n\n                // don't pass initial file name and offset\n                // read directly from the initial file\n                // until we have some rows read already\n                if (\n                       EventCount == 0 \n                    || currentIteration.StartOffset <=0 \n                    || currentIteration.StartOffset == currentIteration.MinOffset\n                )\n                {\n                    if (ServerType != ExtendedEventsWorkloadListener.ServerType.LocalDB)\n                    {\n                        paramPath.Value = currentIteration.StartFileName;\n                    }\n                    paramInitialFile.Value = DBNull.Value;\n                    paramInitialOffset.Value = DBNull.Value;\n                }\n\n                retryWithNULLS:\n\n                logger.Debug($\"paramPath         : {paramPath.Value}\");\n                logger.Debug($\"paramInitialFile  : {paramInitialFile.Value}\");\n                logger.Debug($\"paramInitialOffset: {paramInitialOffset.Value}\");\n\n                // in case we don't have any data in the xe file\n                // GetInitialOffset returns -1 and we need to wait a bit \n                // to let events flow to the file target\n                if (currentIteration.GetInitialOffset() > 0)\n                {\n\n                    var transformer = new SqlTransformer();\n\n                    using (var reader = cmd.ExecuteReader())\n                    {\n                        try\n                        {\n                            var skippedRows = 0;\n                            while (reader.Read())\n                            {\n                                if (reader[\"file_name\"] != DBNull.Value)\n                                {\n                                    currentIteration.EndFileName = (string)reader[\"file_name\"];\n                                }\n\n                                if (reader[\"file_offset\"] != DBNull.Value)\n                                {\n                                    currentIteration.EndOffset = (long)reader[\"file_offset\"];\n                                }\n\n                                var xmldata = (string)reader[\"event_data\"];\n                                var doc = new XmlDocument();\n                                doc.LoadXml(xmldata);\n                                var evt = parseEvent(doc);\n\n                                // skip to the correct event in case we're reading again\n                                // from the same file and we have a reference sequence\n                                if ((currentIteration.RowsRead == 0) && (currentIteration.StartSequence > 0))\n                                {\n                                    // skip rows until we encounter the reference event_sequence\n                                    if (evt.EventSequence != currentIteration.StartSequence)\n                                    {\n                                        skippedRows++;\n                                        continue;\n                                    }\n                                    else\n                                    {\n                                        // skip one more row...\n                                        skippedRows++;\n                                        currentIteration.RowsRead++;\n                                        continue;\n                                    }\n                                }\n\n                                // this is only to print out a message, so consider\n                                // getting rid of it\n                                if (skippedRows > 0)\n                                {\n                                    logger.Debug($\"Skipped rows: {skippedRows}\");\n                                    skippedRows = 0;\n                                }\n\n                                // now we have an event, no matter if good or bad => increment rows read\n                                currentIteration.RowsRead++;\n                                if (evt.EventSequence != null)\n                                {\n                                    currentIteration.EndSequence = (long)evt.EventSequence;\n                                }\n\n                                if (evt.Type == WorkloadEvent.EventType.Unknown)\n                                {\n                                    continue;\n                                }\n\n                                if (evt.Type == WorkloadEvent.EventType.BatchStarting\n                                    ||\n                                    evt.Type == WorkloadEvent.EventType.BatchCompleted\n                                    ||\n                                    evt.Type == WorkloadEvent.EventType.RPCStarting\n                                    ||\n                                    evt.Type == WorkloadEvent.EventType.RPCCompleted\n                                    ||\n                                    evt.Type == WorkloadEvent.EventType.Message)\n                                {\n                                    if (transformer.Skip(evt.Text))\n                                    {\n                                        continue;\n                                    }\n\n                                    evt.Text = transformer.Transform(evt.Text);\n                                }\n\n                                // it's a \"good\" event: add it to the queue                       \n                                Events.Enqueue(evt);\n\n                                EventCount++;\n\n                            }\n                            logger.Debug($\"currentIteration.EndSequence : {currentIteration.EndSequence}\");\n\n                        }\n                        catch (Exception xx)\n                        {\n                            if (xx.Message.Contains(\"Specify an offset that exists in the log file\"))\n                            {\n                                // retry the query without specifying the offset / file pair\n                                paramInitialFile.Value = DBNull.Value;\n                                paramInitialOffset.Value = DBNull.Value;\n                                goto retryWithNULLS;\n                            }\n                            else\n                            {\n                                throw;\n                            }\n                        }\n\n                    }\n                }\n\n                // Wait before querying the events file again\n                if (currentIteration.RowsRead < ReadIteration.DEFAULT_TRACE_ROWS_SLEEP_THRESHOLD)\n                {\n                    Thread.Sleep(ReadIteration.DEFAULT_TRACE_INTERVAL_SECONDS * 1000);\n                }\n            }\n\n        }\n\n        private ReadIteration InitializeReadIteration(SqlConnection conn, ReadIteration previous)\n        {\n\n            var sqlPath = @\"\n                SELECT file_name, ISNULL(file_offset,-1) AS file_offset\n                FROM (\n                    SELECT CAST(target_data AS xml).value('(/EventFileTarget/File/@name)[1]','nvarchar(1000)') AS file_name\n                    FROM sys.dm_xe_{0}session_targets AS t\n                    INNER JOIN sys.dm_xe_{0}sessions AS s\n                        ON t.event_session_address = s.address\n                    WHERE s.name = @sessionName\n                        AND target_name = 'event_file'\n                ) AS fileName\n                OUTER APPLY (\n                    SELECT TOP(1) file_offset\n                    FROM fn_xe_file_target_read_file(file_name,NULL,NULL,NULL)\n                ) AS fileOffset;\n            \";\n\n            var sqlPathLocaldb = @\"\n                IF OBJECT_ID('tempdb.dbo.trace_reader_queue') IS NOT NULL\n                BEGIN\n                    SELECT TOP(1) path, CAST(1 AS bigint) AS file_offset\n                    FROM tempdb.dbo.trace_reader_queue\n                    ORDER BY ts DESC\n                END\n                ELSE\n                BEGIN\n                    SELECT '' AS path, CAST(-1 AS bigint) AS file_offset\n                END\n            \";\n\n            var databaseSuffix = ServerType == ExtendedEventsWorkloadListener.ServerType.AzureSqlDatabase ? \"database_\" : \"\";\n            ReadIteration currentIteration = null;\n            using (var cmdPath = conn.CreateCommand())\n            {\n                if (ServerType == ExtendedEventsWorkloadListener.ServerType.LocalDB)\n                {\n                    cmdPath.CommandText = sqlPathLocaldb;\n                }\n                else\n                {\n                    cmdPath.CommandText = string.Format(sqlPath, databaseSuffix);\n                    var paramSessionName = cmdPath.Parameters.Add(\"@sessionName\", System.Data.SqlDbType.NVarChar, 260);\n                    paramSessionName.Value = SessionName;\n                }\n\n                try\n                {\n                    logger.Debug(\"Initializing read iteration\");\n\n                    using (var reader = cmdPath.ExecuteReader())\n                    {\n                        // should return only one row\n                        if (reader.Read())\n                        {\n                            currentIteration = new ReadIteration()\n                            {\n                                StartFileName = reader.GetString(0),\n                                MinOffset = reader.GetInt64(1)\n                            };\n                            currentIteration.EndFileName = currentIteration.StartFileName;\n                            if (previous != null)\n                            {\n                                //if we have a previous iteration, keep reading from that file first\n                                currentIteration.StartFileName = previous.EndFileName;\n\n                                // we need to read the file from the previous distinct offset\n                                // to avoid skipping events. The function fn_xe_file_target_read_file\n                                // will skip all events up to the @initial_offset INCLUDED,\n                                // so we need to start from the previous offset and skip some rows\n                                currentIteration.StartOffset = ReadIteration.GetSecondLastOffset(currentIteration.StartFileName);\n\n                                // we will use the previous event sequence as the boundary to where \n                                // we need to start reading events again\n                                currentIteration.StartSequence = previous.EndSequence;\n                                currentIteration.EndSequence = previous.EndSequence;\n\n                                // if reading from localdb we don't need to wait for more data\n                                if (ServerType == ExtendedEventsWorkloadListener.ServerType.LocalDB)\n                                {\n                                    if (\n                                        (currentIteration.StartFileName == previous.StartFileName) &&\n                                        (currentIteration.StartSequence == previous.StartSequence)\n                                    )\n                                    {\n                                        return null;\n                                    }\n                                }\n                            }\n\n                            logger.Debug($\"currentIteration.StartFileName: {currentIteration.StartFileName}\");\n                            logger.Debug($\"currentIteration.MinOffset    : {currentIteration.MinOffset}\");\n                            logger.Debug($\"currentIteration.EndFileName  : {currentIteration.EndFileName}\");\n                            logger.Debug($\"currentIteration.StartOffset  : {currentIteration.StartOffset}\");\n                            logger.Debug($\"currentIteration.StartSequence: {currentIteration.StartSequence}\");\n                        }\n                    }\n                }\n                catch (Exception e)\n                {\n                    logger.Error(e.StackTrace);\n                    throw;\n                }\n            }\n            return currentIteration;\n        }\n\n        // Parses all event data from the the data reader\n        private ExecutionWorkloadEvent parseEvent(XmlDocument doc)\n        {\n            var evt = new ExecutionWorkloadEvent();\n\n            var eventNode = doc.DocumentElement.SelectSingleNode(\"/event\");\n            var name = eventNode.Attributes[\"name\"].InnerText;\n\n            if (name == \"sql_batch_completed\")\n            {\n                evt.Type = WorkloadEvent.EventType.BatchCompleted;\n            }\n            else if (name == \"rpc_completed\")\n            {\n                evt.Type = WorkloadEvent.EventType.RPCCompleted;\n            }\n            else if (name == \"sql_batch_starting\")\n            {\n                evt.Type = WorkloadEvent.EventType.BatchStarting;\n            }\n            else if (name == \"rpc_starting\")\n            {\n                evt.Type = WorkloadEvent.EventType.RPCStarting;\n            }\n            else if (name == \"login\")\n            {\n                evt.Type = WorkloadEvent.EventType.RPCStarting;\n            }\n            else if (name == \"attention\")\n            {\n                evt.Type = WorkloadEvent.EventType.Timeout;\n            }\n            else if (name == \"user_event\")\n            {\n                evt.Type = WorkloadEvent.EventType.Error;\n            }\n            else\n            {\n                evt.Type = WorkloadEvent.EventType.Unknown;\n                return evt;\n            }\n\n            var timestamp = DateTimeOffset.Parse(eventNode.Attributes[\"timestamp\"].Value);\n            evt.StartTime = timestamp.LocalDateTime;\n\n            foreach (XmlNode node in eventNode.ChildNodes)\n            {\n                switch ((string)node.Attributes[\"name\"].Value)\n                {\n                    case \"statement\":\n                        if (evt.Type == WorkloadEvent.EventType.RPCCompleted || evt.Type == WorkloadEvent.EventType.RPCStarting)\n                        {\n                            evt.Text = (string)node.FirstChild.FirstChild.Value;\n                        }\n                        break;\n                    case \"batch_text\":\n                        if (evt.Type == WorkloadEvent.EventType.BatchCompleted || evt.Type == WorkloadEvent.EventType.BatchStarting)\n                        {\n                            evt.Text = (string)node.FirstChild.FirstChild.Value;\n                        }\n                        break;\n                    case \"sql_text\":\n                        if (evt.Type == WorkloadEvent.EventType.Timeout)\n                        {\n                            evt.Text = (string)node.FirstChild.FirstChild.Value;\n                        }\n                        break;\n                    case \"client_app_name\":\n                        evt.ApplicationName = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"database_name\":\n                        evt.DatabaseName = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"client_hostname\":\n                        evt.HostName = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"server_principal_name\":\n                        evt.LoginName = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"username\":\n                        evt.LoginName = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"session_id\":\n                        evt.SPID = Convert.ToInt32(node.FirstChild.FirstChild.Value);\n                        break;\n                    case \"cpu_time\":\n                        evt.CPU = Convert.ToInt64(node.FirstChild.FirstChild.Value);\n                        break;\n                    case \"duration\":\n                        evt.Duration = Convert.ToInt64(node.FirstChild.FirstChild.Value);\n                        if (evt.Type == WorkloadEvent.EventType.Timeout)\n                        {\n                            evt.CPU = Convert.ToInt64(evt.Duration);\n                        }\n                        break;\n                    case \"logical_reads\":\n                        evt.Reads = Convert.ToInt64(node.FirstChild.FirstChild.Value);\n                        break;\n                    case \"writes\":\n                        evt.Writes = Convert.ToInt64(node.FirstChild.FirstChild.Value);\n                        break;\n                    case \"user_data\":\n                        evt.Text = (string)node.FirstChild.FirstChild.Value;\n                        break;\n                    case \"event_sequence\":\n                        evt.EventSequence = Convert.ToInt64(node.FirstChild.FirstChild.Value);\n                        break;\n                    case \"is_cached\":\n                        var vIsCached = Convert.ToBoolean(node.FirstChild.FirstChild.Value);\n                        if (!vIsCached) /* If is not cached then consider it a new login */\n                        {\n                            // A nonpooled login will trigger Login event with EventSubClass = 1\n                            // Setting text to sp_reset_connection and including comment on to \n                            // be able to understand this is a nonpooled login on replay\n                            evt.Text = \"exec sp_reset_connection /*Nonpooled*/\";\n                        }\n                        else\n                        {\n                            evt.Type = WorkloadEvent.EventType.Unknown;\n                            evt.Text = \"\";\n                            return evt;\n                        }\n                        break;\n                    default:\n                        break;\n                }\n            }\n            return evt;\n        }\n\n        public override void Stop()\n        {\n            stopped = true;\n        }\n\n        public void Dispose()\n        {\n            Events.Dispose();\n        }\n    }\n\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/StreamXEventDataReader.cs",
    "content": "﻿using Microsoft.SqlServer.XEvent.Linq;\nusing NLog;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing static WorkloadTools.Listener.Trace.TraceEventParser;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public class StreamXEventDataReader : XEventDataReader\n    {\n\n        private enum FieldType\n        {\n            Action,\n            Field\n        }\n\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private bool stopped;\n\n        public StreamXEventDataReader(string connectionString, string sessionName, IEventQueue events) : base(connectionString, sessionName, events, ExtendedEventsWorkloadListener.ServerType.AzureSqlDatabase)\n        {\n        }\n\n        public override void ReadEvents()\n        {\n\n            EventCount = 0;\n            var transformer = new SqlTransformer();\n\n            using (var eventstream = new QueryableXEventData(\n                                            ConnectionString,\n                                            SessionName,\n                                            EventStreamSourceOptions.EventStream,\n                                            EventStreamCacheOptions.CacheToDisk))\n            {\n\n                var eventsEnumerator = eventstream.GetEnumerator();\n\n                while (!stopped)\n                {\n                    if (eventsEnumerator.MoveNext())\n                    {\n                        var evt = eventsEnumerator.Current;\n                        var workloadEvent = new ExecutionWorkloadEvent();\n                        try\n                        {\n                            workloadEvent.EventSequence = Convert.ToInt64(TryGetValue(evt, FieldType.Action, \"event_sequence\"));\n                            var commandText = string.Empty;\n                            if (evt.Name == \"rpc_starting\")\n                            {\n                                commandText = (string)TryGetValue(evt, FieldType.Field, \"statement\");\n                                workloadEvent.Type = WorkloadEvent.EventType.RPCStarting;\n                            }\n                            else if (evt.Name == \"sql_batch_starting\")\n                            {\n                                commandText = (string)TryGetValue(evt, FieldType.Field, \"batch_text\");\n                                workloadEvent.Type = WorkloadEvent.EventType.BatchStarting;\n                            }\n                            else if (evt.Name == \"rpc_completed\")\n                            {\n                                commandText = (string)TryGetValue(evt, FieldType.Field, \"statement\");\n                                workloadEvent.Type = WorkloadEvent.EventType.RPCCompleted;\n                            }\n                            else if (evt.Name == \"sql_batch_completed\")\n                            {\n                                commandText = (string)TryGetValue(evt, FieldType.Field, \"batch_text\");\n                                workloadEvent.Type = WorkloadEvent.EventType.BatchCompleted;\n                            }\n                            else if (evt.Name == \"login\")\n                            {\n                                var vIsCached = Convert.ToBoolean(TryGetValue(evt, FieldType.Field, \"is_cached\"));\n                                if (!vIsCached) /* If is not cached then consider it a new login */\n                                {\n                                    workloadEvent.Type = WorkloadEvent.EventType.RPCStarting;\n                                    // A nonpooled login will trigger Login event with EventSubClass = 1\n                                    // Setting text to sp_reset_connection and including comment on to \n                                    // be able to understand this is a nonpooled login on replay\n                                    commandText = \"exec sp_reset_connection /*Nonpooled*/\";\n                                }\n                                else\n                                {\n                                    workloadEvent.Type = WorkloadEvent.EventType.Unknown;\n                                    continue;\n                                }\n                            }\n                            else if (evt.Name == \"attention\")\n                            {\n                                workloadEvent = new ErrorWorkloadEvent();\n                                var value = TryGetValue(evt, FieldType.Action, \"sql_text\");\n\n                                if (value == null)\n                                {\n                                    continue;\n                                }\n\n                                try\n                                {\n                                    if (value is string stringValue)\n                                    {\n                                        commandText = stringValue;\n                                    }\n                                    else if (value is byte[] byteValue)\n                                    {\n                                        commandText = Encoding.Unicode.GetString(byteValue);\n                                    }\n                                    else\n                                    {\n                                        throw new ArgumentException(\"Argument is of the wrong type\");\n                                    }\n                                }\n                                catch (Exception e)\n                                {\n                                    logger.Error(e, $\"Unable to extract sql_text from attention event. Value is of type ${value.GetType().FullName}\");\n\n                                }\n                                workloadEvent.Text = commandText;\n                                workloadEvent.Type = WorkloadEvent.EventType.Timeout;\n                            }\n                            else if (evt.Name == \"user_event\")\n                            {\n                                workloadEvent = new ErrorWorkloadEvent();\n                                var num = (int)TryGetValue(evt, FieldType.Field, \"event_id\");\n                                if (num == 83 || num == 82)\n                                {\n                                    if (TryGetString(evt, FieldType.Field, \"user_info\").StartsWith(\"WorkloadTools.\"))\n                                    {\n                                        commandText = TryGetString(evt, FieldType.Field, \"user_data\");\n                                        workloadEvent.Text = commandText;\n\n                                        if (num == 83)\n                                        {\n                                            workloadEvent.Type = WorkloadEvent.EventType.Error;\n                                        }\n                                        else\n                                        {\n                                            workloadEvent.Type = WorkloadEvent.EventType.Timeout;\n                                        }\n                                    }\n                                    else\n                                    {\n                                        workloadEvent.Type = WorkloadEvent.EventType.Unknown;\n                                        continue;\n                                    }\n                                }\n                            }\n                            else\n                            {\n                                workloadEvent.Type = WorkloadEvent.EventType.Unknown;\n                                continue;\n                            }\n\n                            try\n                            {\n                                workloadEvent.ApplicationName = TryGetString(evt, FieldType.Action, \"client_app_name\");\n                                workloadEvent.DatabaseName = TryGetString(evt, FieldType.Action, \"database_name\");\n                                workloadEvent.HostName = TryGetString(evt, FieldType.Action, \"client_hostname\");\n                                workloadEvent.LoginName = TryGetString(evt, FieldType.Action, \"server_principal_name\");\n                                workloadEvent.SPID = TryGetInt32(evt, FieldType.Action, \"session_id\");\n                                if (commandText != null)\n                                {\n                                    workloadEvent.Text = commandText;\n                                }\n\n                                workloadEvent.StartTime = evt.Timestamp.LocalDateTime;\n\n                                if (workloadEvent.Type == WorkloadEvent.EventType.Error)\n                                {\n                                    workloadEvent.Duration = 0;\n                                    workloadEvent.CPU = 0;\n                                }\n                                else if (workloadEvent.Type == WorkloadEvent.EventType.Timeout)\n                                {\n                                    workloadEvent.Duration = TryGetInt64(evt, FieldType.Field, \"duration\");\n                                    workloadEvent.CPU = Convert.ToInt64(workloadEvent.Duration);\n                                }\n                                else\n                                {\n                                    if (evt.Name == \"rpc_completed\" || evt.Name == \"sql_batch_completed\")\n                                    {\n                                        workloadEvent.Reads = TryGetInt64(evt, FieldType.Field, \"logical_reads\");\n                                        workloadEvent.Writes = TryGetInt64(evt, FieldType.Field, \"writes\");\n                                        workloadEvent.CPU = TryGetInt64(evt, FieldType.Field, \"cpu_time\");\n                                        workloadEvent.Duration = TryGetInt64(evt, FieldType.Field, \"duration\");\n                                    }\n                                }\n                            }\n                            catch (Exception e)\n                            {\n                                logger.Error(e, \"Error converting XE data from the stream.\");\n                                throw;\n                            }\n\n                            // preprocess and filter events\n                            if (workloadEvent.Type == WorkloadEvent.EventType.BatchStarting\n                                ||\n                                workloadEvent.Type == WorkloadEvent.EventType.BatchCompleted\n                                ||\n                                workloadEvent.Type == WorkloadEvent.EventType.RPCStarting\n                                ||\n                                workloadEvent.Type == WorkloadEvent.EventType.RPCCompleted\n                                ||\n                                workloadEvent.Type == WorkloadEvent.EventType.Message)\n                            {\n                                if (transformer.Skip(workloadEvent.Text))\n                                {\n                                    continue;\n                                }\n\n                                workloadEvent.Text = transformer.Transform(workloadEvent.Text);\n                            }\n\n                            Events.Enqueue(workloadEvent);\n\n                            EventCount++;\n                        }\n                        catch (Exception ex)\n                        {\n                            logger.Error($\"Error converting XE data from the stream: {ex.Message}\");\n                            try\n                            {\n                            \n                                logger.Error($\"    event type            : {workloadEvent.Type}\");\n                                logger.Error($\"    client_app_name       : {TryGetString(evt, FieldType.Action, \"client_app_name\")}\");\n                                logger.Error($\"    database_name         : {TryGetString(evt, FieldType.Action, \"database_name\")}\");\n                                logger.Error($\"    client_hostname       : {TryGetString(evt, FieldType.Action, \"client_hostname\")}\");\n                                logger.Error($\"    server_principal_name : {TryGetString(evt, FieldType.Action, \"server_principal_name\")}\");\n                                logger.Error($\"    session_id            : {TryGetString(evt, FieldType.Action, \"session_id\")}\");\n                                logger.Error($\"    duration              : {TryGetString(evt, FieldType.Field, \"duration\")}\");\n                                logger.Error($\"    logical_reads         : {TryGetString(evt, FieldType.Field, \"logical_reads\")}\");\n                                logger.Error($\"    writes                : {TryGetString(evt, FieldType.Field, \"writes\")}\");\n                                logger.Error($\"    cpu_time              : {TryGetString(evt, FieldType.Field, \"cpu_time\")}\");\n                            }\n                            catch (Exception)\n                            {\n                                //ignore, it is only logging\n                            }\n                            throw;\n                        }\n                    }\n                }\n            }\n        }\n\n        private object TryGetValue(PublishedEvent evt, FieldType t, string name)\n        {\n            object result = null;\n            if (t == FieldType.Action)\n            {\n                if (evt.Actions.TryGetValue(name, out var act))\n                {\n                    result = act.Value;\n                }\n            }\n            else\n            {\n                if (evt.Fields.TryGetValue(name, out var fld))\n                {\n                    result = fld.Value;\n                }\n            }\n\n            // check whether last char is a null char (\\0)\n            // because this breaks writing this string to the sqlite database\n            // which considers it as a BLOB\n            if (result is string stringValue)\n            {\n                while (stringValue.EndsWith(\"\\0\"))\n                {\n                    stringValue = stringValue.Remove(stringValue.Length - 1);\n                }\n                result = stringValue;\n            }\n            return result;\n        }\n\n        private string TryGetString(PublishedEvent evt, FieldType t, string name)\n        {\n            var tmp = TryGetValue(evt, t, name);\n            if(tmp != null && tmp.GetType() != typeof(DBNull))\n            {\n                if (tmp is string strinValue)\n                {\n                    return strinValue;\n                }\n                else if (tmp is byte[] byteValue)\n                {\n                    return Encoding.Unicode.GetString(byteValue);\n                }\n                else\n                {\n                    throw new ArgumentException(\"Argument is of the wrong type\");\n                }\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        private int? TryGetInt32(PublishedEvent evt, FieldType t, string name)\n        {\n            var tmp = TryGetValue(evt, t, name);\n            if (tmp != null && tmp.GetType() != typeof(DBNull))\n            {\n                return Convert.ToInt32(tmp);\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        private long? TryGetInt64(PublishedEvent evt, FieldType t, string name)\n        {\n            var tmp = TryGetValue(evt, t, name);\n            if (tmp != null && tmp.GetType() != typeof(DBNull))\n            {\n                return Convert.ToInt64(tmp);\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public override void Stop()\n        {\n            stopped = true;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/XEventDataReader.cs",
    "content": "﻿using Microsoft.SqlServer.XEvent.Linq;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.ExtendedEvents\n{\n    public abstract class XEventDataReader \n    {\n\n        public string ConnectionString { get; set; }\n        public string SessionName { get; set; }\n        public IEventQueue Events { get; set; }\n        public long EventCount { get; protected set; }\n        public ExtendedEventsWorkloadListener.ServerType ServerType { get; set; }\n\n        public XEventDataReader(\n                string connectionString, \n                string sessionName,\n                IEventQueue events,\n                ExtendedEventsWorkloadListener.ServerType serverType\n            )  \n        {\n            ConnectionString = connectionString;\n            SessionName = sessionName;\n            Events = events;\n            ServerType = serverType;\n        }\n\n        public abstract void ReadEvents();\n\n        public abstract void Stop();\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ExtendedEvents/sqlworkload.sql",
    "content": "﻿\nCREATE EVENT SESSION [sqlworkload] ON {1}\nADD EVENT sqlserver.attention (\n\tACTION(\t\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id, \n\t\tsqlserver.sql_text\n\t)\n\t{0}\n),\nADD EVENT sqlserver.rpc_starting ( \n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n\t{0}\n),\nADD EVENT sqlserver.rpc_completed (\n\tSET collect_data_stream = (0),\n\tcollect_output_parameters = (1),\n\tcollect_statement = (1) \n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n\t{0}\n),\nADD EVENT sqlserver.sql_batch_starting (\n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n\t{0}\n),\nADD EVENT sqlserver.sql_batch_completed (\n\tSET collect_batch_text = (1) \n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n\t{0}\n),\nADD EVENT sqlserver.login ( \n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n\t{0}\n),\nADD EVENT sqlserver.user_event(\n\tACTION(\n\t\tpackage0.event_sequence, \n\t\tsqlserver.client_app_name, \n\t\tsqlserver.client_hostname, \n\t\tsqlserver.database_id, \n\t\tsqlserver.database_name, \n\t\tsqlserver.{2}, \n\t\tsqlserver.session_id\n\t) \n    WHERE [sqlserver].[like_i_sql_unicode_string]([user_info],N'WorkloadTools%')\n)\nWITH (\n\tMAX_MEMORY = 40960 KB,\n\tEVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,\n\tMAX_DISPATCH_LATENCY = 30 SECONDS,\n\tMAX_EVENT_SIZE = 0 KB,\n\tMEMORY_PARTITION_MODE = PER_CPU,\n\tTRACK_CAUSALITY = OFF,\n\tSTARTUP_STATE = OFF\n);\n\n\nALTER EVENT SESSION [sqlworkload] ON {1} STATE = START;"
  },
  {
    "path": "WorkloadTools/Listener/File/FileEventFilter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.File\n{\n    public class FileEventFilter : WorkloadEventFilter\n    {\n        public bool IsSqlAzure { get; set; }\n\n        public FileEventFilter()\n        {\n            ApplicationFilter = new FileFilterPredicate(FilterPredicate.FilterColumnName.ApplicationName);\n            DatabaseFilter = new FileFilterPredicate(FilterPredicate.FilterColumnName.DatabaseName);\n            HostFilter = new FileFilterPredicate(FilterPredicate.FilterColumnName.HostName);\n            LoginFilter = new FileFilterPredicate(FilterPredicate.FilterColumnName.LoginName);\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/File/FileFilterPredicate.cs",
    "content": "﻿using System;\n\nnamespace WorkloadTools.Listener.File\n{\n    public class FileFilterPredicate : FilterPredicate\n    {\n        public FileFilterPredicate(FilterColumnName name) : base(name)\n        {\n        }\n\n        public bool IsSqlAzure { get; set; }\n\n        public override string PushDown()\n        {\n            if (!IsPredicateSet)\n            {\n                return string.Empty;\n            }\n\n            IsPushedDown = true;\n            var result = \"(\";\n\n            var hasPositives = false;\n            var hasNegatives = false;\n\n            for (var i = 0; i < ComparisonOperator.Length; i++)\n            {\n                if (ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    hasNegatives = true;\n                }\n                else\n                {\n                    hasPositives = true;\n                }\n            }\n\n            for (var i = 0; i < PredicateValue.Length; i++)\n            {\n                if (hasNegatives && hasPositives && ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    // In this case I only care for the positives\n                    continue;\n                }\n\n                if (i > 0)\n                {\n                    if (hasNegatives && !hasPositives)\n                    {\n                        result += \" AND \";\n                    }\n                    else\n                    {\n                        result += \" OR \";\n                    }\n                }\n\n                result += ColumnName.ToString();\n                result += \" \" + FilterPredicate.ComparisonOperatorAsString(ComparisonOperator[i]) + \" '\" + EscapeFilter(PredicateValue[i]) + \"'\";\n            }\n            result += \")\";\n            return result;\n        }\n    }\n}"
  },
  {
    "path": "WorkloadTools/Listener/File/FileWorkloadListener.cs",
    "content": "﻿using System;\nusing System.Data;\nusing System.Data.Common;\nusing System.Data.SQLite;\nusing System.Linq;\nusing System.Text;\n\nusing NLog;\n\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Listener.File\n{\n    public class FileWorkloadListener : WorkloadListener\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        // Default behaviour is replay events in synchronization mode\n        // (keeping the same event rate found in the source workload).\n        // The other option is stress mode: events are replayed one\n        // after another without waiting\n        public bool SynchronizationMode { get; set; } = true;\n\n        private DateTime startTime = DateTime.MinValue;\n        private long totalEvents;\n        private SQLiteConnection conn;\n        private SQLiteDataReader reader;\n        private string connectionString;\n\n        private MessageWorkloadEvent totalEventsMessage = null;\n        private bool totalEventsMessageSent = false;\n\n        public FileWorkloadListener() : base()\n        {\n            Filter = new FileEventFilter();\n        }\n\n        public override void Initialize()\n        {\n            connectionString = \"Data Source=\" + Source + \";Version=3;Read Only=True;Journal Mode=Off;Synchronous=Off;\";\n\n            totalEvents = ValidateFile();\n            if (totalEvents < 0)\n            {\n                throw new FormatException($\"The input file \\\"{Source}\\\" is not a valid workload file\");\n            }\n\n            totalEventsMessage = new MessageWorkloadEvent()\n            {\n                MsgType = MessageWorkloadEvent.MessageType.TotalEvents,\n                Value = totalEvents\n            };\n\n            // Push Down EventFilters\n            var filters = GetFilterClause();\n\n            try\n            {\n                var sql = string.Empty;\n\n                // Events are executed on event_sequence order\n                logger.Info(\"Reading the full data for every event that matches filters. This may take awhile on large trace files please be patient\");\n                sql = \"SELECT * FROM Events \" + filters + \" ORDER BY start_time ASC, row_id ASC\";\n\n                conn = new SQLiteConnection(connectionString);\n                conn.Open();\n                var command = new SQLiteCommand(sql, conn);\n                reader = command.ExecuteReader();\n            }\n            catch (Exception e)\n            {\n                logger.Error(e);\n                throw;\n            }\n        }\n\n        // returns the number of events to replay, if any\n        // returns -1 in case the file format is invalid\n        private long ValidateFile()\n        {\n            // Push Down EventFilters\n            var filters = GetFilterClause();\n\n            string sql;\n            if (string.IsNullOrEmpty(filters))\n            {\n                // Only works if you didn't delete rows from the table\n                // WorkloadTools doesn't delete anything. If you deleted \n                // rows manually, blame yourself.\n                sql = \"SELECT MAX(ROWID) FROM Events LIMIT 1\";\n            }\n            else\n            {\n                // SELECT COUNT(*) can be slow on large traces unless VACUUM is used in\n                // Sqlite, but is the only solution when filters are applied.\n                // When filters are applied extra indexes may be useful in future,\n                // especially on large traces.\n                sql = \"SELECT COUNT(*) FROM Events\";\n            }\n\n            long result = -1;\n\n            try\n            {\n                using (var m_dbConnection = new SQLiteConnection(connectionString))\n                {\n                    m_dbConnection.Open();\n                    try\n                    {\n                        var command = new SQLiteCommand(sql, m_dbConnection);\n                        result = (long)command.ExecuteScalar();\n                    }\n                    catch (Exception e)\n                    {\n                        result = -1;\n                        logger.Error(e, \"Unable to query the Events table in source file\");\n                    }\n                }\n\n                logger.Info(\"The source file contains {result} events\", result);\n            }\n            catch (Exception e)\n            {\n                logger.Error(e, \"Unable to open the source file\");\n                result = -1;\n            }\n            return result;\n        }\n\n        public override WorkloadEvent Read()\n        {\n            WorkloadEvent result = null;\n\n            // first I need to return the event that\n            // contains the total number of events in the file\n            // once this is done I can start sending the actual events\n            if (!totalEventsMessageSent)\n            {\n                totalEventsMessageSent = true;\n                return totalEventsMessage;\n            }\n\n            // process actual events from the file\n            try\n            {\n                if (reader == null)\n                {\n                    return null;\n                }\n\n                var validEventFound = false;\n                var transformer = new SqlTransformer();\n\n                do\n                {\n                    if (!reader.Read())\n                    {\n                        stopped = true;\n                        return null;\n                    }\n\n                    result = ReadEvent(reader);\n\n                    // Handle replay sleep for synchronization mode\n                    // The sleep cannot happen here, but it has to \n                    // happen later in the replay workflow, because\n                    // it would only delay the insertion in the queue\n                    // and it would not separate the events during the replay\n                    if (result is ExecutionWorkloadEvent execEvent)\n                    {\n                        if (SynchronizationMode)\n                        {\n                            if (startTime != DateTime.MinValue)\n                            {\n                                var commandOffset = (result.StartTime - startTime).TotalMilliseconds;\n                                if (commandOffset > 0)\n                                {\n                                    execEvent.ReplayOffset = commandOffset;\n                                }\n                            }\n                            else\n                            {\n                                startTime = execEvent.StartTime;\n                            }\n                        }\n                        else\n                        {\n                            // Leave it at 0. The replay consumer will interpret this\n                            // as \"do not wait for the requested offset\" and will replay\n                            // the event without waiting\n                            execEvent.ReplayOffset = 0;\n                        }\n\n                        // preprocess and filter events\n                        if (execEvent.Type == WorkloadEvent.EventType.BatchStarting\n                            ||\n                            execEvent.Type == WorkloadEvent.EventType.BatchCompleted\n                            ||\n                            execEvent.Type == WorkloadEvent.EventType.RPCStarting\n                            ||\n                            execEvent.Type == WorkloadEvent.EventType.RPCCompleted\n                            ||\n                            execEvent.Type == WorkloadEvent.EventType.Message)\n                        {\n                            if (transformer.Skip(execEvent.Text))\n                            {\n                                continue;\n                            }\n\n                            execEvent.Text = transformer.Transform(execEvent.Text);\n                        }\n\n                    }\n                    // Filter events\n                    if (result is ExecutionWorkloadEvent)\n                    {\n                        validEventFound = Filter.Evaluate(result);\n                    }\n                    else\n                    {\n                        validEventFound = true;\n                    }\n\n                }\n                while (!validEventFound);\n            }\n            catch (Exception e)\n            {\n                if (stopped)\n                {\n                    return null;\n                }\n\n                DateTime? eventDate = null;\n                if (result != null)\n                {\n                    eventDate = result.StartTime;\n                }\n\n                logger.Error(e);\n                logger.Error($\"Unable to read next event. Current event date: {eventDate}\");\n                throw;\n            }\n\n            return result;\n        }\n\n        private WorkloadEvent ReadEvent(SQLiteDataReader reader)\n        {\n            var type = (WorkloadEvent.EventType)reader.GetInt32(reader.GetOrdinal(\"event_type\"));\n            var row_id = reader.GetInt64(reader.GetOrdinal(\"row_id\"));\n\n            try\n            {\n                switch (type)\n                {\n                    case WorkloadEvent.EventType.PerformanceCounter:\n                        var cr = new CounterWorkloadEvent\n                        {\n                            StartTime = reader.GetDateTime(reader.GetOrdinal(\"start_time\"))\n                        };\n                        ReadCounters(row_id, cr);\n\n                        return cr;\n\n                    case WorkloadEvent.EventType.WAIT_stats:\n                        return new WaitStatsWorkloadEvent\n                        {\n                            StartTime = reader.GetDateTime(reader.GetOrdinal(\"start_time\")),\n                            Type = type\n                        };\n\n                    case WorkloadEvent.EventType.DiskPerf:\n                        return new DiskPerfWorkloadEvent\n                        {\n                            StartTime = reader.GetDateTime(reader.GetOrdinal(\"start_time\")),\n                            Type = type\n                        };\n\n                    case WorkloadEvent.EventType.Error:\n                        return new ErrorWorkloadEvent\n                        {\n                            StartTime = reader.GetDateTime(reader.GetOrdinal(\"start_time\")),\n                            Type = type,\n                            Text = GetString(reader, \"sql_text\")\n                        };\n\n                    default:\n                        return new ExecutionWorkloadEvent\n                        {\n                            EventSequence = GetInt64(reader, \"event_sequence\"),\n                            ApplicationName = GetString(reader, \"client_app_name\"),\n                            StartTime = reader.GetDateTime(reader.GetOrdinal(\"start_time\")),\n                            HostName = GetString(reader, \"client_host_name\"),\n                            DatabaseName = GetString(reader, \"database_name\"),\n                            LoginName = GetString(reader, \"server_principal_name\"),\n                            SPID = reader.GetInt32(reader.GetOrdinal(\"session_id\")),\n                            Text = GetString(reader, \"sql_text\"),\n                            CPU = GetInt64(reader, \"cpu\"),\n                            Duration = GetInt64(reader, \"duration\"),\n                            Reads = GetInt64(reader, \"reads\"),\n                            Writes = GetInt64(reader, \"writes\"),\n                            Type = type\n                        };\n\n                }\n            }\n            catch (Exception e)\n            {\n                throw new InvalidOperationException($\"Invalid data at row_id {row_id}\", e);\n            }\n        }\n\n        private string GetString(SQLiteDataReader reader, string columnName)\n        {\n            var result = reader[columnName];\n            if (result != null)\n            {\n                if (result.GetType() == typeof(DBNull))\n                {\n                    result = null;\n                }\n                else if (result is byte[] v)\n                {\n                    result = Encoding.Unicode.GetString(v);\n                }\n            }\n            return (string)result;\n        }\n\n        private long? GetInt64(SQLiteDataReader reader, string columnName)\n        {\n            var result = reader[columnName];\n            if (result != null)\n            {\n                if (result.GetType() == typeof(DBNull))\n                {\n                    result = null;\n                }\n            }\n            return (long?)result;\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            if ((reader != null) && (!reader.IsClosed))\n            {\n                reader.Close();\n            }\n            conn.Dispose();\n        }\n\n        protected override void ReadPerfCountersEvents()\n        {\n        }\n\n        protected override void ReadWaitStatsEvents()\n        {\n            // Why nothing read here?\n        }\n\n        protected override void ReadDiskPerformanceEvents()\n        {\n            // Why nothing read here?\n        }\n\n        private void ReadCounters(long row_id, CounterWorkloadEvent cev)\n        {\n            var sql = \"SELECT * FROM Counters WHERE row_id = $row_id\";\n\n            try\n            {\n                using (var m_dbConnection = new SQLiteConnection(connectionString))\n                {\n                    m_dbConnection.Open();\n                    try\n                    {\n                        using (var command = new SQLiteCommand(sql, m_dbConnection))\n                        {\n                            _ = command.Parameters.AddWithValue(\"$row_id\", row_id);\n                            using (var rdr = command.ExecuteReader())\n                            {\n                                while (rdr.Read())\n                                {\n                                    var name = (CounterWorkloadEvent.CounterNameEnum)Enum.Parse(typeof(CounterWorkloadEvent.CounterNameEnum), (string)rdr[\"name\"]);\n                                    cev.Counters.Add(name, rdr.GetFloat(rdr.GetOrdinal(\"value\")));\n                                }\n                                rdr.Close();\n                            }\n                        }\n                    }\n                    catch (Exception e)\n                    {\n                        logger.Error(e, $\"Unable to query Counters for row_id {row_id}\");\n                        throw;\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                logger.Error(e, \"Unable to query Counters from the source file\");\n            }\n        }\n\n        private string GetFilterClause()\n        {\n            // Push Down EventFilters\n            var filters = string.Empty;\n\n            var appFilter = Filter.ApplicationFilter.PushDown();\n            var dbFilter = Filter.DatabaseFilter.PushDown();\n            var hostFilter = Filter.HostFilter.PushDown();\n            var loginFilter = Filter.LoginFilter.PushDown();\n\n            if (appFilter != string.Empty)\n            {\n                filters += ((filters == string.Empty) ? string.Empty : \" AND \") + appFilter;\n            }\n            if (dbFilter != string.Empty)\n            {\n                filters += ((filters == string.Empty) ? string.Empty : \" AND \") + dbFilter;\n            }\n            if (hostFilter != string.Empty)\n            {\n                filters += ((filters == string.Empty) ? string.Empty : \" AND \") + hostFilter;\n            }\n            if (loginFilter != string.Empty)\n            {\n                filters += ((filters == string.Empty) ? string.Empty : \" AND \") + loginFilter;\n            }\n\n            if (filters != string.Empty)\n            {\n                filters = \"WHERE (\" + filters + \") \";\n\n                // these events should not be filtered out\n                // 4 - PerformanceCounter\n                // 5 - Timeout\n                // 6 - WaitStats\n                // 7 - Error\n                filters += \"OR event_type IN (4,5,6,7,8)\";\n            }\n\n            return filters;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/ReadIteration.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Listener\n{\n    // This class is used internally to keep track\n    // of the files, offsets and event_sequences\n    // when reading events from extended events or trace files\n    internal class ReadIteration\n    {\n        public const int DEFAULT_TRACE_INTERVAL_SECONDS = 5;\n        public const int DEFAULT_TRACE_ROWS_SLEEP_THRESHOLD = 500;\n        public const long TRACE_DEFAULT_OFFSET = 500;\n\n        #region staticStuff\n        private static int _lastFileHash;\n        private static long _lastOffset;\n\n        private static readonly Dictionary<string, SortedSet<long>> recordedOffsets = new Dictionary<string, SortedSet<long>>();\n\n        private static void AddOffset(string filename, long offset)\n        {\n            // perf optimization: most of the time the last \n            // file/offset pair is passed over and over again\n            if (filename.GetHashCode() == _lastFileHash && offset == _lastOffset)\n            {\n                return;\n            }\n\n            // new values? ok, let's add them\n            // one more check doesn't hurt though\n            if (recordedOffsets.TryGetValue(filename, out var offsets))\n            {\n                if (!offsets.Contains(offset))\n                {\n                    _ = offsets.Add(offset);\n                }\n            }\n            else\n            {\n                offsets = new SortedSet<long>();\n                _ = offsets.Add(offset);\n                recordedOffsets.Add(filename, offsets);\n            }\n\n            // let's keep track of the last inserted values\n            _lastFileHash = filename.GetHashCode();\n            _lastOffset = offset;\n        }\n\n        public static long GetLastOffset(string filename)\n        {\n            long result = -1;\n            if (recordedOffsets.TryGetValue(filename, out var offsets))\n            {\n                result = offsets.Max();\n            }\n            return result;\n        }\n\n        public static long GetSecondLastOffset(string filename)\n        {\n            long result = -1;\n            if (recordedOffsets.TryGetValue(filename, out var offsets))\n            {\n                if (offsets.Count >= 2)\n                {\n                    result = offsets.ElementAt(offsets.Count - 2);\n                }\n            }\n            return result;\n        }\n        #endregion staticStuff\n\n        public string StartFileName { get; set; }\n        public string EndFileName { get; set; }\n        public long MinOffset { get; set; }\n        public long StartOffset { get; set; }\n        private long _endOffset = -1;\n        public long EndOffset\n        {\n            get => _endOffset;\n            set\n            {\n                // add current offset\n                AddOffset(EndFileName, value);\n\n                // set new value\n                _endOffset = value;\n            }\n        }\n        public long StartSequence { get; set; }\n        public long EndSequence { get; set; }\n        public long RowsRead { get; set; }\n        public int Files { get; set; }\n\n        // try to identify the root part of the rollover file name\n        // the root is the part of the name before the numeric suffix\n        // EG: mySessionName1234.xel => root = mySessionName\n        public string GetXEFilePattern()\n        {\n            var filePattern = \"\";\n            for (var j = StartFileName.Length - 4; j > 1 && StartFileName.Substring(j - 1, 1).All(char.IsDigit); j--)\n            {\n                filePattern = StartFileName.Substring(0, j - 1);\n            }\n            filePattern += \"*.xel\";\n            return filePattern;\n        }\n\n        // Initial offset to be used as a parameter to the fn_xe_file_target_read_file function\n        public long GetInitialOffset()\n        {\n            long result = -1;\n            if (MinOffset > result)\n            {\n                result = MinOffset;\n            }\n\n            if (StartOffset > result)\n            {\n                result = StartOffset;\n            }\n\n            if (EndOffset > result)\n            {\n                result = EndOffset;\n            }\n\n            return result;\n        }\n\n        public long GetInitialSequence()\n        {\n            long result = -1;\n            if (StartSequence > result)\n            {\n                result = StartSequence;\n            }\n\n            if (EndSequence > result)\n            {\n                result = EndSequence;\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/SqlTransformer.cs",
    "content": "﻿using Microsoft.SqlServer.Management.SqlParser.Metadata;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing WorkloadTools.Consumer.Analysis;\n\nnamespace WorkloadTools.Listener\n{\n    public class SqlTransformer\n    {\n\n        private static readonly Regex _execPrepped = new Regex(\"^EXEC\\\\s+SP_EXECUTE\\\\s+(?<stmtnum>\\\\d+)\", RegexOptions.IgnoreCase | RegexOptions.Compiled);\n        private static readonly Regex _execUnprep = new Regex(\"EXEC\\\\s+SP_UNPREPARE\\\\s+(?<stmtnum>\\\\d+)\", RegexOptions.IgnoreCase | RegexOptions.Compiled);\n        private static readonly Regex _prepareSql = new Regex(\"EXEC\\\\s+(?<preptype>SP_PREP(ARE|EXEC))\\\\s+@P1\\\\s+OUTPUT,\\\\s*(NULL|(N\\\\'.*?\\\\')),\\\\s*N(?<remaining>.+)$\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _preppedSqlStatement = new Regex(\"^(')(?<statement>((?!\\\\1).|\\\\1{2})*)\\\\1\", RegexOptions.Compiled | RegexOptions.Singleline);\n        private static readonly Regex _doubleApostrophe = new Regex(\"('')(?<string>.*?)('')\", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.CultureInvariant);\n\n        private static readonly MatchEvaluator decimal38Evaluator = new MatchEvaluator(MakeFloat);\n\n        private static string MakeFloat(Match match)\n        {\n            if (match.Value.EndsWith(\"E0\"))\n            {\n                return match.Value;\n            }\n            else\n            {\n                return match.Value + \"E0\";\n            }\n        }\n\n        public string Transform(string command)\n        {\n            // remove the handle from the sp_prepexec call\n            if (command.Contains(\"sp_prepexec \"))\n            {\n                command = RemoveFirstP1(command, out _);\n                if (!command.EndsWith(\"EXEC sp_unprepare @p1;\"))\n                {\n                    command += \" ; EXEC sp_unprepare @p1;\";\n                }\n            }\n\n            //  remove the handle from the sp_cursoropen call\n            else if (command.Contains(\"sp_cursoropen \"))\n            {\n                command = RemoveFirstP1(command, out _);\n                if (!command.EndsWith(\"EXEC sp_cursorclose @p1;\"))\n                {\n                    command += \" ; EXEC sp_cursorclose @p1;\";\n                }\n            }\n\n            //  remove the handle from the sp_cursorprepexec call\n            else if (command.Contains(\"sp_cursorprepexec \"))\n            {\n                command = RemoveFirstP1(command, out _);\n                if (!command.EndsWith(\"EXEC sp_cursorunprepare @p1;\"))\n                {\n                    command += \" ; EXEC sp_cursorunprepare @p1;\";\n                }\n            }\n\n            // trim numbers with precision > 38\n            // rpc_completed events may return float parameters\n            // as long numeric strings that exceed the maximum decimal\n            // precision of 38. \n            // Any decimal numeric string in T-SQL is interpreted as decimal,\n            // unless it ends with \"E0\", which designates a float literal. \n            // Any decimal numeric string longer than 38 characters needs to\n            // be appended \"E0\" to be treated as float.\n            //\n            // Unfortunately RegExs are evil and also match numbers\n            // that already have their \"E0\" appended, so I need to append\n            // only when not found\n            //\n            // RegEx: \\b([0-9\\.]{38,})+([E]+[0]+)?\\b\n            // \\b                 means \"word boundary\", including whitespace, punctuation or begin/end input\n            // ([0-9\\.]{38,})+    means \"numbers or . repeated at least 38 times\"\n            // ([E]+[0]+)?        means \"E0\" zero or one time\n            // \\b                 means word boundary again\n            command = Regex.Replace(command, @\"\\b([0-9\\.]{38,})+([E]+[0]+)?\\b\", decimal38Evaluator);\n\n            return command;\n        }\n\n        public bool Skip(string command)\n        {\n            if (string.IsNullOrEmpty(command))\n            {\n                return true;\n            }\n            // skip reset connection commands\n            //if (command.Contains(\"sp_reset_connection\"))\n            //    return true;\n\n            // skip unprepare commands\n            //if (command.Contains(\"sp_unprepare \"))\n            //    return true;\n\n            // skip cursor fetch\n            if (command.Contains(\"sp_cursor \"))\n            {\n                return true;\n            }\n\n            // skip cursor fetch\n            if (command.Contains(\"sp_cursorfetch \"))\n            {\n                return true;\n            }\n\n            // skip cursor close\n            if (command.Contains(\"sp_cursorclose \"))\n            {\n                return true;\n            }\n\n            // skip cursor option\n            if (command.Contains(\"sp_cursoroption \"))\n            {\n                return true;\n            }\n\n            // skip cursor unprepare\n            if (command.Contains(\"sp_cursorunprepare \"))\n            {\n                return true;\n            }\n\n            // skip internal commands\n            if (command.Contains(\"fn_xe_file_target_read_file\")\n                ||\n                command.Contains(\"ALTER EVENT SESSION\")\n                ||\n                command.Contains(\"fn_trace_getinfo\"))\n            {\n                return true;\n            }\n\n            // skip KILL commands\n            if (command.StartsWith(\"KILL\"))\n            {\n                return true;\n            }\n\n            // skip BULK INSERT commands\n            if (command.StartsWith(\"insert bulk\"))\n            {\n                return true;\n            }\n\n            // skip sp_execute\n            //if (command.Contains(\"sp_execute \"))\n            //    return true;\n\n            return false;\n        }\n\n        private string RemoveFirstP1(string command, out string originalP1)\n        {\n            var idx = command.IndexOf(\"set @p1=\");\n            originalP1 = null;\n            if (idx > 0)\n            {\n                originalP1 = \"\";\n                var sb = new StringBuilder(command);\n                idx += 8; // move past \"set @p1=\"\n\n                // replace numeric chars with 0s\n                while (char.IsNumber(sb[idx]))\n                {\n                    originalP1 += sb[idx];\n                    sb[idx] = '0';\n                    idx++;\n                }\n                command = sb.ToString();\n            }\n            return command;\n        }\n\n        private string RemoveFirstPrepStatementNum(string command, out string originalStmtNum)\n        {\n            var idx = command.IndexOf(\" sp_execute \");\n            originalStmtNum = null;\n            if (idx > 0)\n            {\n                originalStmtNum = \"\";\n                var sb = new StringBuilder(command);\n                idx += 12; // move past \" sp_execute \"\n\n                // replace numeric chars with §\n                var iter = 0;\n                var initialIdx = idx;\n                while (idx < sb.Length && char.IsNumber(sb[idx]))\n                {\n                    originalStmtNum += sb[idx];\n                    if(iter == 0)\n                    {\n                        sb[idx] = '§';\n                    }\n                    else\n                    {\n                        sb[idx] = ' ';\n                    }\n                    idx++;\n                    iter++;\n                }\n                // remove extra characters after the newly added § symbol\n                if(initialIdx + 1 < sb.Length && iter > 1)\n                {\n                    _ = sb.Remove(initialIdx + 1, iter - 1);\n                }\n                command = sb.ToString();\n            }\n            return command;\n        }\n\n        public NormalizedSqlText Normalize(string command)\n        {\n            var result = new NormalizedSqlText(command);\n\n            var num = 0;\n\n            if (command.Contains(\"sp_reset_connection\"))\n            {\n                if (command.Contains(\"Nonpooled\"))\n                {\n                    result.CommandType = NormalizedSqlText.CommandTypeEnum.SP_RESET_CONNECTION_NONPOOLED;\n                }\n                else\n                {\n                    result.CommandType = NormalizedSqlText.CommandTypeEnum.SP_RESET_CONNECTION;\n                }\n                return result;\n            }                \n\n            var match3 = _prepareSql.Match(command);\n            if (match3.Success)\n            {\n                if (match3.Groups[\"preptype\"].ToString().ToLower() == \"sp_prepare\")\n                {\n                    if(match3.Groups[\"stmtnum\"].Success)\n                    {\n                        num = !(match3.Groups[\"stmtnum\"].ToString() == \"NULL\") ? Convert.ToInt32(match3.Groups[\"stmtnum\"].ToString()) : 0;\n                    }\n\n                    var sql = match3.Groups[\"remaining\"].ToString();\n                    var match4 = _preppedSqlStatement.Match(sql);\n                    if (match4.Success)\n                    {\n                        sql = match4.Groups[\"statement\"].ToString();\n                        sql = _doubleApostrophe.Replace(sql, \"'${string}'\");\n                        result.Statement = sql;\n                        result.NormalizedText = RemoveFirstP1(result.OriginalText, out var originalHandle);\n                        if (int.TryParse(originalHandle, out var n))\n                        {\n                            result.Handle = n;\n                        }\n                        else\n                        {\n                            result.Handle = num;\n                        }\n                        result.CommandType = NormalizedSqlText.CommandTypeEnum.SP_PREPARE;\n                    }\n                }\n                return result;\n            }\n\n            var match5 = _execPrepped.Match(command);\n            if (match5.Success)\n            {\n                num = Convert.ToInt32(match5.Groups[\"stmtnum\"].ToString());\n                result.Handle = num;\n                var textWithPlaceHolder = RemoveFirstPrepStatementNum(result.Statement, out var originalHandle);\n                if (int.TryParse(originalHandle, out var n))\n                {\n                    result.Handle = n;\n                }\n                else\n                {\n                    result.Handle = num;\n                }\n                result.Statement = textWithPlaceHolder;\n                result.NormalizedText = textWithPlaceHolder;\n                result.CommandType = NormalizedSqlText.CommandTypeEnum.SP_EXECUTE;\n                return result;\n            }\n\n            var match6 = _execUnprep.Match(command);\n            if (match6.Success)\n            {\n                num = Convert.ToInt32(match6.Groups[\"stmtnum\"].ToString());\n                result.Handle = num;\n                result.Statement = \"EXEC sp_unprepare §\";\n                result.NormalizedText = \"EXEC sp_unprepare §\";\n                result.CommandType = NormalizedSqlText.CommandTypeEnum.SP_UNPREPARE;\n                return result;\n            }\n\n            return result;\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/FileTraceEventDataReader.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class FileTraceEventDataReader : TraceEventDataReader\n    {\n\n        private readonly RingBuffer<ReadIteration> ReadIterations = new RingBuffer<ReadIteration>(10);\n\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private int TraceRowsSleepThreshold { get; set; } = 5000;\n        private int TraceIntervalSeconds { get; set; } = 10;\n\n        private bool stopped = false;\n        private int traceId = -1;\n\n        private readonly TraceUtils utils;\n        private bool _checkedFormat;\n\n        public FileTraceEventDataReader(string connectionString, WorkloadEventFilter filter, IEventQueue events) : base(connectionString, filter, events)\n        {\n            utils = new TraceUtils();\n        }\n\n        public override void ReadEvents()\n        {\n\n            try\n            {\n                var retryCount = 0;\n\n                while (!stopped)\n                {\n                    using (var conn = new SqlConnection())\n                    {\n                        conn.ConnectionString = ConnectionString;\n                        conn.Open();\n\n                        ReadIteration previousIteration = null;\n                        if (ReadIterations.Count > 0)\n                        {\n                            previousIteration = ReadIterations.Last();\n                        }\n                        var currentIteration = InitializeReadIteration(conn, previousIteration);\n                        if (currentIteration != null)\n                        {\n                            ReadIterations.Add(currentIteration);\n                        }\n                        else\n                        {\n                            Stop();\n                            break;\n                        }\n\n                        try\n                        {\n                            ReadTraceData(conn, currentIteration);\n                            retryCount = 0;\n                        }\n                        catch (SqlException) {\n                            retryCount++;\n                            if(retryCount > 2)\n                            {\n                                throw;\n                            }\n                        }\n\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n                logger.Error(ex.StackTrace);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                Dispose();\n            }\n        }\n\n        private ReadIteration InitializeReadIteration(SqlConnection conn, ReadIteration previous)\n        {\n\n            var sqlPath = @\"\n                SELECT value AS path\n                FROM ::fn_trace_getinfo(default)\n                WHERE traceid = @traceId\n                    AND property = 2;\n            \";\n\n            var sqlPathLocaldb = @\"\n                IF OBJECT_ID('tempdb.dbo.trace_reader_queue') IS NOT NULL\n                BEGIN\n                    SELECT TOP(1) path\n                    FROM tempdb.dbo.trace_reader_queue\n                    ORDER BY ts DESC\n                END\n                ELSE\n                BEGIN\n                    SELECT '' AS path\n                END\n            \";\n            \n            ReadIteration currentIteration = null;\n            using (var cmdPath = conn.CreateCommand())\n            {\n                if (conn.DataSource.StartsWith(\"(localdb)\", StringComparison.InvariantCultureIgnoreCase))\n                {\n                    _checkedFormat = false;\n                    cmdPath.CommandText = sqlPathLocaldb;\n                }\n                else\n                {\n                    _checkedFormat = true;\n                    cmdPath.CommandText = sqlPath;\n\n                    // Get trace id\n                    if (traceId == -1)\n                    {\n                        var tracePath = utils.GetSqlDefaultLogPath(conn);\n                        traceId = utils.GetTraceId(conn, Path.Combine(tracePath, \"sqlworkload\"));\n                        if (traceId == -1)\n                        {\n                            throw new InvalidOperationException(\"The SqlWorkload capture trace is not running.\");\n                        }\n                    }\n                    var paramTraceId = cmdPath.Parameters.Add(\"@traceId\", System.Data.SqlDbType.Int);\n                    paramTraceId.Value = traceId;\n                }   \n\n                try\n                {\n                    logger.Debug(\"Initializing read iteration\");\n\n                    using (var reader = cmdPath.ExecuteReader())\n                    {\n                        // should return only one row\n                        if (reader.Read())\n                        {\n                            currentIteration = new ReadIteration()\n                            {\n                                StartFileName = reader.GetString(0),\n                                Files = 1\n                            };\n                            currentIteration.EndFileName = currentIteration.StartFileName;\n                            if (previous != null)\n                            {\n                                //if we have a previous iteration, keep reading from that file first\n                                currentIteration.StartFileName = previous.EndFileName;\n                                // if the file has changed from the previous iteration\n                                // read the default number of files ( = 0 )\n                                if(currentIteration.StartFileName != currentIteration.EndFileName)\n                                {\n                                    currentIteration.Files = 0;\n                                }\n\n                                // we will use the previous event sequence as the boundary to where \n                                // we need to start reading events again\n                                currentIteration.StartSequence = previous.EndSequence;\n                                currentIteration.EndSequence = previous.EndSequence;\n\n                                // trace files do not have an offset like xe files but\n                                // the offset can be used to go back and read events\n                                // from the previous sequence minus a safety offset\n                                currentIteration.StartOffset = previous.EndSequence - ReadIteration.TRACE_DEFAULT_OFFSET;\n\n                                // if reading from localdb we don't need to wait for more data\n                                if (conn.DataSource.StartsWith(\"(localdb)\", StringComparison.InvariantCultureIgnoreCase))\n                                {\n                                    if (\n                                        (currentIteration.StartFileName == previous.StartFileName) && \n                                        (currentIteration.StartSequence == previous.StartSequence)\n                                    )\n                                    {\n                                        return null;\n                                    }\n                                }\n\n                            }\n\n                            logger.Debug($\"currentIteration.StartFileName: {currentIteration.StartFileName}\");\n                            logger.Debug($\"currentIteration.MinOffset    : {currentIteration.MinOffset}\");\n                            logger.Debug($\"currentIteration.EndFileName  : {currentIteration.EndFileName}\");\n                            logger.Debug($\"currentIteration.StartOffset  : {currentIteration.StartOffset}\");\n                            logger.Debug($\"currentIteration.StartSequence: {currentIteration.StartSequence}\");\n                        }\n                    }\n                }\n                catch (Exception e)\n                {\n                    logger.Error(e.StackTrace);\n                    throw;\n                }\n            }\n\n            // check columns in the source file (if localdb)\n            // before returning the read iteration\n            if (!_checkedFormat)\n            {\n                if (!utils.CheckTraceFormat(conn, currentIteration.StartFileName))\n                {\n                    throw new InvalidDataException($\"The trace file {currentIteration.StartFileName} lacks critical column information. See the documentation for required trace columns.\");\n                }\n            }\n            \n            return currentIteration;\n        }\n\n        private void ReadTraceData(SqlConnection conn, ReadIteration currentIteration)\n        {\n            var sqlReadTrace = @\"\n                SELECT EventSequence\n\t                ,Error\n\t                ,TextData\n\t                ,BinaryData\n\t                ,DatabaseID\n\t                ,HostName\n\t                ,ApplicationName\n\t                ,LoginName\n\t                ,SPID\n\t                ,Duration\n\t                ,StartTime\n\t                ,EndTime\n\t                ,Reads\n\t                ,Writes\n\t                ,CPU\n\t                ,EventClass\n\t                ,DatabaseName\n\t                ,EventSubClass\n                FROM fn_trace_gettable(@path, @number_files)\n            \";\n\n            if (currentIteration.StartSequence > 0)\n            {\n                sqlReadTrace += \"WHERE EventSequence > @event_offset\";\n            }\n\n            logger.Debug(\"Reading Trace data...\");\n\n            var parser = new TraceEventParser();\n\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = sqlReadTrace;\n\n                var paramPath = cmd.Parameters.Add(\"@path\", System.Data.SqlDbType.NVarChar, 260);\n                paramPath.Value = currentIteration.StartFileName;\n\n                var paramNumberFiles = cmd.Parameters.Add(\"@number_files\", System.Data.SqlDbType.Int);\n                paramNumberFiles.Value = currentIteration.Files;\n\n                var paramInitialSequence = cmd.Parameters.Add(\"@event_offset\", System.Data.SqlDbType.BigInt);\n                paramInitialSequence.Value = currentIteration.StartOffset;\n\n                // don't pass initial file name and offset\n                // read directly from the initial file\n                // until we have some rows read already\n                if (\n                       EventCount == 0\n                    || currentIteration.StartOffset <= 0\n                    || currentIteration.StartOffset == currentIteration.MinOffset\n                )\n                {\n                    paramPath.Value = currentIteration.StartFileName;\n                    paramNumberFiles.Value = 0;\n                    paramInitialSequence.Value = 0;\n                }\n\n                logger.Debug($\"paramPath           : {paramPath.Value}\");\n                logger.Debug($\"paramNumberFiles    : {paramNumberFiles.Value}\");\n                logger.Debug($\"paramInitialSequence: {paramInitialSequence.Value}\");\n\n            \n                var transformer = new SqlTransformer();\n\n                try\n                {\n                    using (var reader = cmd.ExecuteReader())\n                    {\n                        var skippedRows = 0;\n                        while (reader.Read())\n                        {\n                            if (reader[\"EventSequence\"] != DBNull.Value)\n                            {\n                                currentIteration.EndSequence = (long)reader[\"EventSequence\"];\n                            }\n\n                            // read the event from the sqldatareader\n                            var evt = parser.ParseEvent(reader);\n\n                            // skip invalid events\n                            if (evt.Type == WorkloadEvent.EventType.Unknown)\n                            {\n                                continue;\n                            }\n\n                            // skip to the correct event in case we're reading again\n                            // from the same file and we have a reference sequence\n                            if ((currentIteration.RowsRead == 0) && (currentIteration.StartSequence > 0))\n                            {\n                                // skip rows until we encounter the reference event_sequence\n                                if (evt.EventSequence != currentIteration.StartSequence)\n                                {\n                                    skippedRows++;\n                                    continue;\n                                }\n                                else\n                                {\n                                    // skip one more row...\n                                    skippedRows++;\n                                    currentIteration.RowsRead++;\n                                    continue;\n                                }\n                            }\n\n                            // this is only to print out a message, so consider\n                            // getting rid of it\n                            if (skippedRows > 0)\n                            {\n                                logger.Debug($\"Skipped rows: {skippedRows}\");\n                                skippedRows = 0;\n                            }\n\n                            // now we have an event, no matter if good or bad => increment rows read\n                            currentIteration.RowsRead++;\n                            if (evt.EventSequence != null)\n                            {\n                                currentIteration.EndSequence = (long)evt.EventSequence;\n                            }\n\n                            if (evt.Type == WorkloadEvent.EventType.BatchStarting\n                                ||\n                                evt.Type == WorkloadEvent.EventType.BatchCompleted\n                                ||\n                                evt.Type == WorkloadEvent.EventType.RPCStarting\n                                ||\n                                evt.Type == WorkloadEvent.EventType.RPCCompleted\n                                ||\n                                evt.Type == WorkloadEvent.EventType.Message)\n                            {\n                                if (transformer.Skip(evt.Text))\n                                {\n                                    continue;\n                                }\n\n                                if (!Filter.Evaluate(evt))\n                                {\n                                    continue;\n                                }\n\n                                evt.Text = transformer.Transform(evt.Text);\n                            }\n\n                            // it's a \"good\" event: add it to the queue                       \n                            Events.Enqueue(evt);\n\n                            EventCount++;\n\n                        }\n                        logger.Debug($\"currentIteration.EndSequence : {currentIteration.EndSequence}\");\n                    }\n\n                }\n                catch (Exception)\n                {\n                    throw;\n                }\n\n                // Wait before querying the events file again\n                if (currentIteration.RowsRead < ReadIteration.DEFAULT_TRACE_ROWS_SLEEP_THRESHOLD \n                    && currentIteration.StartFileName == currentIteration.EndFileName)\n                {\n                    Thread.Sleep(ReadIteration.DEFAULT_TRACE_INTERVAL_SECONDS * 1000);\n                }\n            }\n\n        }\n\n        public override void Stop()\n        {\n            stopped = true;\n        }\n\n        public bool IsStopped\n        {\n            get { return stopped; }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/ProfilerEventFilter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class ProfilerEventFilter : WorkloadEventFilter\n    {\n\n        public ProfilerEventFilter()\n        {\n            ApplicationFilter = new ProfilerFilterPredicate(FilterPredicate.FilterColumnName.ApplicationName);\n            DatabaseFilter = new ProfilerFilterPredicate(FilterPredicate.FilterColumnName.DatabaseName);\n            HostFilter = new ProfilerFilterPredicate(FilterPredicate.FilterColumnName.HostName);\n            LoginFilter = new ProfilerFilterPredicate(FilterPredicate.FilterColumnName.LoginName);\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/ProfilerFilterPredicate.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class ProfilerFilterPredicate : FilterPredicate\n    {\n        public ProfilerFilterPredicate(FilterColumnName name) : base(name)\n        {\n        }\n\n        public override string PushDown()\n        {\n            IsPushedDown = false;\n            return string.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/ProfilerWorkloadListener.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Listener.Trace;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class ProfilerWorkloadListener : WorkloadListener\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private readonly ConcurrentQueue<WorkloadEvent> events = new ConcurrentQueue<WorkloadEvent>();\n        private TraceServerWrapper trace;\n\n        public ProfilerWorkloadListener() : base()\n        {\n            Filter = new ProfilerEventFilter();\n            Source = WorkloadController.BaseLocation + \"\\\\Listener\\\\Trace\\\\sqlworkload.tdf\";\n        }\n        \n\n        public override void Initialize()\n        {\n            var conn = new SqlConnectionInfoWrapper\n            {\n                ServerName = ConnectionInfo.ServerName,\n                DatabaseName = \"master\"\n            };\n\n            if (string.IsNullOrEmpty(ConnectionInfo.UserName))\n            {\n                conn.UseIntegratedSecurity = true;\n            }\n            else\n            {\n                conn.UserName = ConnectionInfo.UserName;\n                conn.Password = ConnectionInfo.Password;\n            }\n\n            trace = new TraceServerWrapper();\n\n            try\n            {\n                trace.InitializeAsReader(conn, Source);\n\n                _ = Task.Factory.StartNew(() => ReadEvents());\n\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                throw;\n            }\n        }\n\n        public override WorkloadEvent Read()\n        {\n            WorkloadEvent result = null;\n            while(!stopped && !events.TryDequeue(out result)) {\n                Thread.Sleep(10);\n            }\n            return result;\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            if (stopped)\n            {\n                return;\n            }\n            // close the trace, if open\n            // shut down the reader thread\n            stopped = true;\n            try\n            {\n                trace.Close();\n                trace.Stop();\n            }\n            catch (Exception)\n            {\n                // naughty dev swallows exceptions...\n            }\n        }\n\n        private void ReadEvents()\n        {\n            try\n            {\n                while (trace.Read() && !stopped)\n                {\n                    try\n                    {\n                        var evt = new ExecutionWorkloadEvent();\n\n                        if (trace.GetValue(\"EventClass\").ToString() == \"RPC:Completed\")\n                        {\n                            evt.Type = WorkloadEvent.EventType.RPCCompleted;\n                        }\n                        else if (trace.GetValue(\"EventClass\").ToString() == \"SQL:BatchCompleted\")\n                        {\n                            evt.Type = WorkloadEvent.EventType.BatchCompleted;\n                        }\n                        else\n                        {\n                            evt.Type = WorkloadEvent.EventType.Unknown;\n                        }\n\n                        evt.ApplicationName = (string)trace.GetValue(\"ApplicationName\");\n                        evt.DatabaseName = (string)trace.GetValue(\"DatabaseName\");\n                        evt.HostName = (string)trace.GetValue(\"HostName\");\n                        evt.LoginName = (string)trace.GetValue(\"LoginName\");\n                        evt.SPID = (int?)trace.GetValue(\"SPID\");\n                        evt.Text = (string)trace.GetValue(\"TextData\");\n                        evt.Reads = (long?)trace.GetValue(\"Reads\");\n                        evt.Writes = (long?)trace.GetValue(\"Writes\");\n                        evt.CPU = (long?)trace.GetValue(\"CPU\") * 1000; // Profiler captures CPU as milliseconds => convert to microseconds\n                        evt.Duration = (long?)trace.GetValue(\"Duration\");\n                        evt.StartTime = DateTime.Now;\n\n                        if (!Filter.Evaluate(evt))\n                        {\n                            continue;\n                        }\n\n                        events.Enqueue(evt);\n                    }\n                    catch (Exception ex)\n                    {\n                        logger.Error(ex.Message);\n\n                        if (ex.InnerException != null)\n                        {\n                            logger.Error(ex.InnerException.Message);\n                        }\n                    }\n\n                } // while (Read)\n\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                Dispose();\n            }\n        }\n\n}\n}\n\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/SqlConnectionInfoWrapper.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class SqlConnectionInfoWrapper\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public object SqlConnectionInfo { get; set; }\n        public string ServerName\n        {\n            get => (string)SqlConnectionInfo.GetType().GetProperty(\"ServerName\")?.GetGetMethod()?.Invoke(SqlConnectionInfo, (object[])null);\n            set => _ = (SqlConnectionInfo.GetType().GetProperty(\"ServerName\")?.GetSetMethod()?.Invoke(SqlConnectionInfo, new object[] { value }));\n        }\n        public string DatabaseName\n        {\n            get => (string)SqlConnectionInfo.GetType().GetProperty(\"DatabaseName\")?.GetGetMethod()?.Invoke(SqlConnectionInfo, (object[])null);\n            set => _ = (SqlConnectionInfo.GetType().GetProperty(\"DatabaseName\")?.GetSetMethod()?.Invoke(SqlConnectionInfo, new object[] { value }));\n        }\n        public bool UseIntegratedSecurity\n        {\n            get => (bool)SqlConnectionInfo.GetType().GetProperty(\"UseIntegratedSecurity\")?.GetGetMethod()?.Invoke(SqlConnectionInfo, (object[])null);\n            set => _ = (SqlConnectionInfo.GetType().GetProperty(\"UseIntegratedSecurity\")?.GetSetMethod()?.Invoke(SqlConnectionInfo, new object[] { value }));\n        }\n        public string UserName\n        {\n            get => (string)SqlConnectionInfo.GetType().GetProperty(\"UserName\")?.GetGetMethod()?.Invoke(SqlConnectionInfo, (object[])null);\n            set => _ = (SqlConnectionInfo.GetType().GetProperty(\"UserName\")?.GetSetMethod()?.Invoke(SqlConnectionInfo, new object[] { value }));\n        }\n        public string Password\n        {\n            get => (string)SqlConnectionInfo.GetType().GetProperty(\"Password\")?.GetGetMethod()?.Invoke(SqlConnectionInfo, (object[])null);\n            set => _ = (SqlConnectionInfo.GetType().GetProperty(\"Password\")?.GetSetMethod()?.Invoke(SqlConnectionInfo, new object[] { value }));\n        }\n\n        public SqlConnectionInfoWrapper()\n        {\n            Type type;\n            try\n            {\n#pragma warning disable 618\n                var assembly = Assembly.LoadWithPartialName(\"Microsoft.SqlServer.ConnectionInfo\");\n#pragma warning restore 618\n                type = assembly.GetType(\"Microsoft.SqlServer.Management.Common.SqlConnectionInfo\");\n                SqlConnectionInfo = type.InvokeMember((string)null, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, (Binder)null, (object)null, (object[])null);\n            }\n            catch (Exception ex)\n            {\n                logger.Error(\"Unable to load SMO library\");\n                logger.Error(ex.Message);\n                throw;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/SqlTraceWorkloadListener.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Listener.Trace;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class SqlTraceWorkloadListener : WorkloadListener\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public enum StreamSourceEnum\n        {\n            StreamFromFile,\n            StreamFromTDS\n        }\n\n        \n\n        private int traceId = -1;\n        private string tracePath;\n\n        // By default, stream from TDS\n        public StreamSourceEnum StreamSource { get; set; } = StreamSourceEnum.StreamFromTDS;\n\n\t\tpublic int TraceSizeMB { get; set; } = 10;\n\t\tpublic int TraceRolloverCount { get; set; } = 30;\n\t\t\n\t\t\n\n        private readonly TraceUtils utils;\n\n\t\tpublic SqlTraceWorkloadListener() : base()\n        {\n            Filter = new TraceEventFilter();\n            Source = WorkloadController.BaseLocation + \"\\\\Listener\\\\Trace\\\\sqlworkload.sql\";\n            utils = new TraceUtils();\n        }\n\n        public override void Initialize()\n        {\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n\n                string traceSql = null;\n                try\n                {\n                    traceSql = System.IO.File.ReadAllText(Source);\n\n                    // Push Down EventFilters\n                    var filters = \"\";\n                    filters += Environment.NewLine + Filter.ApplicationFilter.PushDown();\n                    filters += Environment.NewLine + Filter.DatabaseFilter.PushDown();\n                    filters += Environment.NewLine + Filter.HostFilter.PushDown();\n                    filters += Environment.NewLine + Filter.LoginFilter.PushDown();\n\n                    tracePath = utils.GetSqlDefaultLogPath(conn);\n                    traceSql = string.Format(traceSql, TraceSizeMB, TraceRolloverCount, Path.Combine(tracePath  ,\"sqlworkload\"), filters);\n                }\n                catch (Exception e)\n                {\n                    throw new ArgumentException(\"Cannot open the source script to start the sql trace\", e);\n                }\n\n                var id = utils.GetTraceId(conn, Path.Combine(tracePath, \"sqlworkload\"));\n                if(id > 0)\n                {\n                    StopTrace(conn, id);\n                }\n\n                var cmd = conn.CreateCommand();\n                cmd.CommandText = traceSql;\n                traceId = (int)cmd.ExecuteScalar();\n\n                // Mark the transaction\n                SetTransactionMark(true);\n\n                // Initialize the source of execution related events\n                if (StreamSource == StreamSourceEnum.StreamFromFile)\n                {\n                    _ = Task.Factory.StartNew(() => ReadEventsFromFile());\n                }\n                else if (StreamSource == StreamSourceEnum.StreamFromTDS)\n                {\n                    _ = Task.Factory.StartNew(() => ReadEventsFromTDS());\n                }\n\n                // Initialize the source of performance counters events\n                _ = Task.Factory.StartNew(() => ReadPerfCountersEvents());\n\n                // Initialize the source of wait stats events\n                _ = Task.Factory.StartNew(() => ReadWaitStatsEvents());\n\n                // Initialize the source of disk performance stats\n                _ = Task.Factory.StartNew(() => ReadDiskPerformanceEvents());\n            }\n        }\n\n        public override WorkloadEvent Read()\n        {\n            try\n            { \n                WorkloadEvent result = null;\n                while (!Events.TryDequeue(out result))\n                {\n                    if (stopped)\n                    {\n                        return null;\n                    }\n\n                    Thread.Sleep(5);\n                }\n                return result;\n            }\n            catch (Exception)\n            {\n                if (stopped)\n                {\n                    return null;\n                }\n                else\n                {\n                    throw;\n                }\n            }\n        }\n\n        private void ReadEventsFromTDS()\n        {\n            using (var reader = new FileTraceEventDataReader(ConnectionInfo.ConnectionString(), Filter, Events))\n            {\n                reader.ReadEvents();\n            }\n        }\n\n        // Read Workload events directly from the trace files\n        // on the server, via local path\n        // This method only works when the process is running\n        // on the same machine as the SQL Server\n        private void ReadEventsFromFile()\n        {\n            try\n            {\n                while(!stopped)\n                {\n                    // get first trace rollover file\n                    var files = Directory.GetFiles(tracePath, \"sqlworkload*.trc\").ToList();\n                    files.Sort();\n                    var traceFile = files.ElementAt(0);\n\n                    using (var reader = new TraceFileWrapper())\n                    {\n                        reader.InitializeAsReader(traceFile);\n\n                        while (reader.Read() && !stopped)\n                        {\n                            try\n                            {\n                                var evt = new ExecutionWorkloadEvent();\n\n                                if (reader.GetValue(\"EventClass\").ToString() == \"RPC:Starting\")\n                                {\n                                    evt.Type = WorkloadEvent.EventType.RPCStarting;\n                                }\n                                else if (reader.GetValue(\"EventClass\").ToString() == \"SQL:BatchStarting\")\n                                {\n                                    evt.Type = WorkloadEvent.EventType.BatchStarting;\n                                }\n                                else if (reader.GetValue(\"EventClass\").ToString() == \"RPC:Completed\")\n                                {\n                                    evt.Type = WorkloadEvent.EventType.RPCCompleted;\n                                }\n                                else if (reader.GetValue(\"EventClass\").ToString() == \"SQL:BatchCompleted\")\n                                {\n                                    evt.Type = WorkloadEvent.EventType.BatchCompleted;\n                                }\n                                else\n                                {\n                                    evt.Type = WorkloadEvent.EventType.Unknown;\n                                }\n\n                                evt.ApplicationName = (string)reader.GetValue(\"ApplicationName\");\n                                evt.DatabaseName = (string)reader.GetValue(\"DatabaseName\");\n                                evt.HostName = (string)reader.GetValue(\"HostName\");\n                                evt.LoginName = (string)reader.GetValue(\"LoginName\");\n                                evt.SPID = (int?)reader.GetValue(\"SPID\");\n                                evt.Text = (string)reader.GetValue(\"TextData\");\n                                evt.Reads = (long?)reader.GetValue(\"Reads\");\n                                evt.Writes = (long?)reader.GetValue(\"Writes\");\n                                evt.CPU = (long?)Convert.ToInt64(reader.GetValue(\"CPU\")) * 1000; // SqlTrace captures CPU as milliseconds => convert to microseconds\n                                evt.Duration = (long?)reader.GetValue(\"Duration\");\n                                evt.StartTime = DateTime.Now;\n                                if (evt.Type == WorkloadEvent.EventType.RPCStarting || evt.Type == WorkloadEvent.EventType.BatchStarting)\n                                {\n                                    evt.StartTime = Convert.ToDateTime(reader.GetValue(\"StartTime\"));\n                                }\n\n                                if (!Filter.Evaluate(evt))\n                                {\n                                    continue;\n                                }\n\n                                Events.Enqueue(evt);\n                            }\n                            catch (Exception ex)\n                            {\n                                logger.Error(ex.Message);\n\n                                if (ex.InnerException != null)\n                                {\n                                    logger.Error(ex.InnerException.Message);\n                                }\n                            }\n\n                        } // while (Read)\n\n                    } // using reader\n                    System.IO.File.Delete(traceFile);\n                } // while not stopped\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n\n                Dispose();\n            }\n\n        }\n\n        \n\n        protected override void Dispose(bool disposing)\n        {\n            stopped = true;\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                StopTrace(conn, traceId);\n            }\n            logger.Info(\"Trace with id={0} stopped successfully.\", traceId);\n        }\n\n        private void StopTrace(SqlConnection conn, int id)\n        {\n                var cmd = conn.CreateCommand();\n                cmd.CommandText = string.Format(@\"\n                    IF EXISTS (\n                        SELECT *\n                        FROM sys.traces\n                        WHERE id = {0}\n                    )\n                    BEGIN\n                        EXEC sp_trace_setstatus {0}, 0;\n                        EXEC sp_trace_setstatus {0}, 2;\n                    END\n                \", id);\n            _ = cmd.ExecuteNonQuery();\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceEventDataReader.cs",
    "content": "﻿using Microsoft.SqlServer.XEvent.Linq;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public abstract class TraceEventDataReader : IDisposable\n    {\n\n        public string ConnectionString { get; set; }\n        public IEventQueue Events { get; set; }\n        public long EventCount { get; protected set; }\n        public WorkloadEventFilter Filter { get; set; }\n\n        public TraceEventDataReader(\n                string connectionString, \n                WorkloadEventFilter filter,\n                IEventQueue events\n            )  \n        {\n            ConnectionString = connectionString;\n            Events = events;\n            Filter = filter;\n        }\n\n        public abstract void ReadEvents();\n\n        public abstract void Stop();\n\n        public void Dispose()\n        {\n            Events.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceEventFilter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class TraceEventFilter : WorkloadEventFilter\n    {\n\n        public TraceEventFilter()\n        {\n            ApplicationFilter = new TraceFilterPredicate(FilterPredicate.FilterColumnName.ApplicationName);\n            DatabaseFilter = new TraceFilterPredicate(FilterPredicate.FilterColumnName.DatabaseName);\n            HostFilter = new TraceFilterPredicate(FilterPredicate.FilterColumnName.HostName);\n            LoginFilter = new TraceFilterPredicate(FilterPredicate.FilterColumnName.LoginName);\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceEventParser.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class TraceEventParser\n    {\n        public enum EventClassEnum : int\n        {\n            RPC_Completed = 10,\n            SQL_BatchCompleted = 12,\n            RPC_Starting = 11,\n            SQL_BatchStarting = 13,\n            Audit_Login = 14,\n            Timeout = 82\n        }\n\n        private readonly Dictionary<string, string> columns = new Dictionary<string, string>();\n\n        public ExecutionWorkloadEvent ParseEvent(SqlDataReader reader)\n        {\n            if(columns.Count == 0)\n            {\n                for (var i = 0; i < reader.FieldCount; i++)\n                {\n                    var colName = reader.GetName(i);\n                    columns.Add(colName.ToLower(), colName);\n                }\n            }\n\n            var evt = new ExecutionWorkloadEvent();\n\n            var eventClass = (int)reader[\"EventClass\"];\n\n            if (eventClass == (int)EventClassEnum.RPC_Starting)\n            {\n                evt.Type = WorkloadEvent.EventType.RPCStarting;\n            }\n            else if (eventClass == (int)EventClassEnum.SQL_BatchStarting)\n            {\n                evt.Type = WorkloadEvent.EventType.BatchStarting;\n            }\n            else if (eventClass == (int)EventClassEnum.RPC_Completed)\n            {\n                evt.Type = WorkloadEvent.EventType.RPCCompleted;\n            }\n            else if (eventClass == (int)EventClassEnum.SQL_BatchCompleted)\n            {\n                evt.Type = WorkloadEvent.EventType.BatchCompleted;\n            }\n            else if (eventClass == (int)EventClassEnum.Audit_Login)\n            {\n                if (IsValidColumn(\"EventSubClass\") && reader[\"EventSubClass\"] != DBNull.Value)\n                {\n                    var vEventSubClass = (int)reader[\"EventSubClass\"];\n                    if (vEventSubClass == 1) /* 1 - Nonpooled */\n                    { \n                        evt.Type = WorkloadEvent.EventType.RPCStarting;\n                        // A nonpooled login will trigger Login event with EventSubClass = 1\n                        // Setting text to sp_reset_connection and including comment on to \n                        // be able to understand this is a nonpooled login on replay\n                        evt.Text = \"exec sp_reset_connection /*Nonpooled*/\";\n                    }\n                    else\n                    {\n                        evt.Type = WorkloadEvent.EventType.Unknown;\n                        return evt;\n                    }\n                }\n            }\n            else if (eventClass == (int)EventClassEnum.Timeout)\n            {\n                if (reader[\"TextData\"].ToString().StartsWith(\"WorkloadTools.Timeout[\"))\n                {\n                    evt.Type = WorkloadEvent.EventType.Timeout;\n                }\n            }\n            else\n            {\n                evt.Type = WorkloadEvent.EventType.Unknown;\n                return evt;\n            }\n            if (IsValidColumn(\"ApplicationName\") && reader[\"ApplicationName\"] != DBNull.Value)\n            {\n                evt.ApplicationName = (string)reader[\"ApplicationName\"];\n            }\n\n            if (IsValidColumn(\"DatabaseName\") && reader[\"DatabaseName\"] != DBNull.Value)\n            {\n                evt.DatabaseName = (string)reader[\"DatabaseName\"];\n            }\n\n            if (IsValidColumn(\"Hostname\") && reader[\"HostName\"] != DBNull.Value)\n            {\n                evt.HostName = (string)reader[\"HostName\"];\n            }\n\n            if (IsValidColumn(\"LoginName\") && reader[\"LoginName\"] != DBNull.Value)\n            {\n                evt.LoginName = (string)reader[\"LoginName\"];\n            }\n\n            if (IsValidColumn(\"SPID\") && reader[\"SPID\"] != DBNull.Value)\n            {\n                evt.SPID = (int?)reader[\"SPID\"];\n            }\n\n            if (IsValidColumn(\"TextData\") && reader[\"TextData\"] != DBNull.Value && eventClass != (int)EventClassEnum.Audit_Login)\n            {\n                evt.Text = (string)reader[\"TextData\"];\n            }\n\n            if (IsValidColumn(\"StartTime\") && reader[\"StartTime\"] != DBNull.Value)\n            {\n                evt.StartTime = (DateTime)reader[\"StartTime\"];\n            }\n\n            if (evt.Type == WorkloadEvent.EventType.Timeout)\n            {\n                if (IsValidColumn(\"BinaryData\") && reader[\"BinaryData\"] != DBNull.Value)\n                {\n                    var bytes = (byte[])reader[\"BinaryData\"];\n                    evt.Text = Encoding.Unicode.GetString(bytes);\n                }\n                if(IsValidColumn(\"TextData\") && reader[\"TextData\"] != DBNull.Value)\n                {\n                    evt.Duration = ExtractTimeoutDuration(reader[\"TextData\"]);\n                }\n\n                evt.CPU = Convert.ToInt64(evt.Duration);\n            }\n            else\n            {\n                if (IsValidColumn(\"Reads\") && reader[\"Reads\"] != DBNull.Value)\n                {\n                    evt.Reads = (long?)reader[\"Reads\"];\n                }\n\n                if (IsValidColumn(\"Writes\") && reader[\"Writes\"] != DBNull.Value)\n                {\n                    evt.Writes = (long?)reader[\"Writes\"];\n                }\n\n                if (IsValidColumn(\"CPU\") && reader[\"CPU\"] != DBNull.Value)\n                {\n                    evt.CPU = (long?)Convert.ToInt64(reader[\"CPU\"]) * 1000; // SqlTrace captures CPU as milliseconds => convert to microseconds\n                }\n\n                if (IsValidColumn(\"Duration\") && reader[\"Duration\"] != DBNull.Value)\n                {\n                    evt.Duration = (long?)reader[\"Duration\"];\n                }\n\n                if (IsValidColumn(\"EventSequence\") && reader[\"EventSequence\"] != DBNull.Value)\n                {\n                    evt.EventSequence = (long?)reader[\"EventSequence\"];\n                }\n            }\n\n            return evt;\n        }\n\n        private long? ExtractTimeoutDuration(object textData)\n        {\n            long result = 30;\n            if (textData != DBNull.Value)\n            {\n                var description = (string)textData;\n                var durationAsString = new string(description.Where(char.IsDigit).ToArray());\n                result = Convert.ToInt64(durationAsString);\n            }\n            return result * 1000 * 1000;\n        }\n\n        private bool IsValidColumn(string colName)\n        {\n            return columns.ContainsKey(colName.ToLower());\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceFileWrapper.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class TraceFileWrapper : IDisposable\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private static readonly Assembly _baseAssembly;\n        private static readonly Type _baseType;\n\n        static TraceFileWrapper()\n        {\n            try\n            {\n#pragma warning disable 618\n                _baseAssembly = Assembly.LoadWithPartialName(\"Microsoft.SqlServer.ConnectionInfoExtended\");\n#pragma warning restore 618\n                logger.Info(string.Format(\"SMO Version: {0}\", (object)_baseAssembly.FullName.ToString()));\n                _baseType = _baseAssembly.GetType(\"Microsoft.SqlServer.Management.Trace.TraceFile\");\n            }\n            catch (Exception ex)\n            {\n                logger.Error(\"Unable to load SMO library\");\n                logger.Error(ex.Message);\n                throw;\n            }\n        }\n\n        public object TraceFile { get; set; }\n\n        public TraceFileWrapper()\n        {\n            TraceFile = _baseType.InvokeMember((string)null, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, (Binder)null, (object)null, (object[])null);\n        }\n\n        public object this[string name]\n        {\n            get\n            {\n                var indexer = _baseType\n                    .GetProperties()\n                    .Single(p => p.GetIndexParameters().Length == 1 && p.GetIndexParameters()[0].ParameterType == typeof(string));\n                return indexer.GetValue(TraceFile, new object[] { name });\n            }\n        }\n\n        public object this[int index]\n        {\n            get\n            {\n                var indexer = _baseType\n                    .GetProperties()\n                    .Single(p => p.GetIndexParameters().Length == 1 && p.GetIndexParameters()[0].ParameterType == typeof(int));\n                return indexer.GetValue(TraceFile, new object[] { index });\n            }\n        }\n\n        public object GetValue(string Name)\n        {\n            return this[Name];\n        }\n\n        public bool HasAttribute(string Name)\n        {\n            try\n            {\n                _ = GetValue(Name);\n                return true;\n            }\n            catch (Exception)\n            {\n                return false;\n            }\n        }\n\n        public void InitializeAsReader(string fileName)\n        {\n            try\n            {\n                var args = new object[1] { fileName };\n                _ = _baseType.InvokeMember(\"InitializeAsReader\", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceFile, args);\n            }\n            catch (Exception)\n            {\n                throw;\n            }\n        }\n\n        public bool Read()\n        {\n            return (bool)_baseType.InvokeMember(\"Read\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceFile, (object[])null);\n        }\n\n        public void Dispose()\n        {\n            // naughty, naughty...\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceFilterPredicate.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    class TraceFilterPredicate : FilterPredicate\n    {\n        public TraceFilterPredicate(FilterColumnName name) : base(name)\n        {\n        }\n\n        public override string PushDown()\n        {\n            if (!IsPredicateSet)\n            {\n                return string.Empty;\n            }\n\n            IsPushedDown = true;\n            var result = \"\";\n\n            var hasPositives = false;\n            var hasNegatives = false;\n\n            for (var i = 0; i < ComparisonOperator.Length; i++)\n            {\n                if (ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    hasNegatives = true;\n                }\n                else\n                {\n                    hasPositives = true;\n                }\n            }\n\n            for (var i = 0; i < PredicateValue.Length; i++)\n            {\n                if (hasNegatives && hasPositives && ComparisonOperator[i] == FilterComparisonOperator.Not_Equal)\n                {\n                    // In this case I only care for the positives\n                    continue;\n                }\n\n                var logicalOperator = \"0\"; // AND\n                if (i > 0)\n                {\n                    if (hasNegatives && !hasPositives)\n                    {\n                        logicalOperator = \" 0 \"; // AND\n                    }\n                    else\n                    {\n                        result += \" 1 \"; // OR\n                    }\n                }\n\n                result += \"exec sp_trace_setfilter @TraceID, \" + (byte)ColumnName + \" ,  \" + logicalOperator +\", \" + (byte)ComparisonOperator[i] + \", N'\" + EscapeFilter(PredicateValue[i]) + \"'\";\n            }\n            return result;\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceServerWrapper.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    public class TraceServerWrapper\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        private static readonly Assembly _baseAssembly;\n        private static readonly Type _baseType;\n\n        private bool isRunning = false;\n        public bool IsRunning { get { return isRunning; } }\n\n        static TraceServerWrapper()\n        {\n            try\n            {\n#pragma warning disable 618\n                _baseAssembly = Assembly.LoadWithPartialName(\"Microsoft.SqlServer.ConnectionInfoExtended\");\n#pragma warning restore 618\n                logger.Info(string.Format(\"SMO Version: {0}\", (object)_baseAssembly.FullName.ToString()));\n                _baseType = _baseAssembly.GetType(\"Microsoft.SqlServer.Management.Trace.TraceServer\");\n            }\n            catch (Exception ex)\n            {\n                logger.Error(\"Unable to load SMO library\");\n                logger.Error(ex.Message);\n                throw;\n            }\n        }\n\n        public TraceServerWrapper()\n        {\n            TraceServer = _baseType.InvokeMember((string)null, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, (Binder)null, (object)null, (object[])null);\n        }\n\n        public object TraceServer { get; set; }\n\n        public object this[\n            string name\n        ]\n        {\n            get\n            {\n                var indexer = _baseType\n                    .GetProperties()\n                    .Single(p => p.GetIndexParameters().Length == 1 && p.GetIndexParameters()[0].ParameterType == typeof(string));\n                //PropertyInfo indexer = _baseType.GetProperty(\"Item\");\n                return indexer.GetValue(TraceServer, new object[] { name });\n                //return _baseType.InvokeMember(\"get_Item\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, new object[] { name });\n            }\n        }\n\n        public object GetValue(string Name)\n        {\n            return this[Name];\n        }\n\n        public bool Read()\n        {\n            return (bool)_baseType.InvokeMember(\"Read\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, (object[])null);\n        }\n\n        public void Stop()\n        {\n            //_baseType.InvokeMember(\"Pause\", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, (object[])null);\n            isRunning = false;\n            _ = _baseType.InvokeMember(\"Stop\", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, (object[])null);\n        }\n\n        public void Close()\n        {\n            _ = _baseType.InvokeMember(\"Close\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, (object[])null);\n        }\n\n        public void InitializeAsReader(SqlConnectionInfoWrapper connectionInfo, string TraceDefinition)\n        {\n            try\n            {\n                var args = new object[2] { connectionInfo.SqlConnectionInfo, TraceDefinition };\n                _ = _baseType.InvokeMember(\"InitializeAsReader\", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, (Binder)null, TraceServer, args);\n                isRunning = true;\n            }\n            catch (Exception)\n            {\n                throw;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/TraceUtils.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Listener.Trace\n{\n    internal class TraceUtils\n    {\n        public int GetTraceId(SqlConnection conn, string path)\n        {\n            var sql = @\"\n                SELECT TOP(1) id\n                FROM (\n\t                SELECT id FROM sys.traces WHERE path LIKE '{0}%'\n\t                UNION ALL\n\t                SELECT -1\n                ) AS i\n                ORDER BY id DESC\n            \";\n\n            var cmd = conn.CreateCommand();\n            cmd.CommandText = string.Format(sql, path);\n            return (int)cmd.ExecuteScalar();\n        }\n\n        public string GetSqlDefaultLogPath(SqlConnection conn)\n        {\n            var sql = @\"\n            DECLARE @defaultLog nvarchar(4000);\n\n            EXEC master.dbo.xp_instance_regread\n\t            N'HKEY_LOCAL_MACHINE',\n\t            N'Software\\Microsoft\\MSSQLServer\\MSSQLServer',\n\t            N'DefaultLog',\n\t            @defaultLog OUTPUT;\n\n            IF @defaultLog IS NULL\n            BEGIN\n\t            SELECT @defaultLog = REPLACE(physical_name,'mastlog.ldf','') \n\t            FROM sys.master_files\n                WHERE file_id = 2\n\t\t\t\t\tAND database_id = 1;\n            END\n\n            SELECT @defaultLog AS DefaultLog;\n        \";\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = sql;\n                return (string)cmd.ExecuteScalar();\n            }\n        }\n\n        \n        public bool CheckTraceFormat(SqlConnection conn, string path)\n        {\n            var sql = @\"\n                SELECT COUNT(*) AS cnt\n                FROM(\n                    SELECT TOP(100) *\n                    FROM fn_trace_gettable(@path, default)\n                ) AS data\n                WHERE EventSequence IS NOT NULL\n                    AND SPID IS NOT NULL\n            \";\n            using (var cmd = conn.CreateCommand())\n            {\n                cmd.CommandText = sql;\n                var p = cmd.CreateParameter();\n                p.ParameterName = \"@path\";\n                p.DbType = System.Data.DbType.AnsiString;\n                p.Value = path;\n                _ = cmd.Parameters.Add(p);\n                return ((int)cmd.ExecuteScalar()) > 0;\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Listener/Trace/sqlworkload.sql",
    "content": "﻿\n-- Create a Queue\ndeclare @rc int\ndeclare @TraceID int\ndeclare @maxfilesize bigint\nset @maxfilesize = '{0}';\n\ndeclare @maxnumfiles int\nset @maxnumfiles = '{1}';\n\n\nexec @rc = sp_trace_create @TraceID output, 2, N'{2}', @maxfilesize, NULL, @maxnumfiles\nif (@rc != 0) goto error\n\n\n-- Set the events\ndeclare @on bit\nset @on = 1\n\n/* RPC:Starting */\nEXEC sp_trace_setevent @TraceID, 11,  1, @on \nexec sp_trace_setevent @TraceID, 11, 10, @on\nexec sp_trace_setevent @TraceID, 11,  8, @on\nexec sp_trace_setevent @TraceID, 11, 11, @on\nexec sp_trace_setevent @TraceID, 11, 12, @on\nexec sp_trace_setevent @TraceID, 11, 13, @on\nexec sp_trace_setevent @TraceID, 11, 14, @on\nexec sp_trace_setevent @TraceID, 11, 15, @on\nexec sp_trace_setevent @TraceID, 11, 16, @on\nexec sp_trace_setevent @TraceID, 11, 17, @on\nexec sp_trace_setevent @TraceID, 11, 18, @on\nexec sp_trace_setevent @TraceID, 11, 35, @on\nexec sp_trace_setevent @TraceID, 11,  3, @on\nexec sp_trace_setevent @TraceID, 11, 31, @on\nexec sp_trace_setevent @TraceID, 11, 51, @on\n\n/* RPC:Completed */\nEXEC sp_trace_setevent @TraceID, 10,  1, @on \nexec sp_trace_setevent @TraceID, 10, 10, @on\nexec sp_trace_setevent @TraceID, 10,  8, @on\nexec sp_trace_setevent @TraceID, 10, 11, @on\nexec sp_trace_setevent @TraceID, 10, 12, @on\nexec sp_trace_setevent @TraceID, 10, 13, @on\nexec sp_trace_setevent @TraceID, 10, 14, @on\nexec sp_trace_setevent @TraceID, 10, 15, @on\nexec sp_trace_setevent @TraceID, 10, 16, @on\nexec sp_trace_setevent @TraceID, 10, 17, @on\nexec sp_trace_setevent @TraceID, 10, 18, @on\nexec sp_trace_setevent @TraceID, 10, 35, @on\nexec sp_trace_setevent @TraceID, 10,  3, @on\nexec sp_trace_setevent @TraceID, 10, 31, @on\nexec sp_trace_setevent @TraceID, 10, 51, @on\n\n/* SQL:BatchCompleted */\nexec sp_trace_setevent @TraceID, 12,  1, @on\nexec sp_trace_setevent @TraceID, 12, 11, @on\nexec sp_trace_setevent @TraceID, 12,  8, @on\nexec sp_trace_setevent @TraceID, 12, 10, @on\nexec sp_trace_setevent @TraceID, 12, 12, @on\nexec sp_trace_setevent @TraceID, 12, 13, @on\nexec sp_trace_setevent @TraceID, 12, 14, @on\nexec sp_trace_setevent @TraceID, 12, 15, @on\nexec sp_trace_setevent @TraceID, 12, 16, @on\nexec sp_trace_setevent @TraceID, 12, 17, @on\nexec sp_trace_setevent @TraceID, 12, 18, @on\nexec sp_trace_setevent @TraceID, 12, 35, @on\nexec sp_trace_setevent @TraceID, 12,  3, @on\nexec sp_trace_setevent @TraceID, 12, 31, @on\nexec sp_trace_setevent @TraceID, 12, 51, @on\n\n/* SQL:BatchStarting */\nexec sp_trace_setevent @TraceID, 13,  1, @on\nexec sp_trace_setevent @TraceID, 13, 11, @on\nexec sp_trace_setevent @TraceID, 13,  8, @on\nexec sp_trace_setevent @TraceID, 13, 10, @on\nexec sp_trace_setevent @TraceID, 13, 12, @on\nexec sp_trace_setevent @TraceID, 13, 13, @on\nexec sp_trace_setevent @TraceID, 13, 14, @on\nexec sp_trace_setevent @TraceID, 13, 15, @on\nexec sp_trace_setevent @TraceID, 13, 16, @on\nexec sp_trace_setevent @TraceID, 13, 17, @on\nexec sp_trace_setevent @TraceID, 13, 18, @on\nexec sp_trace_setevent @TraceID, 13, 35, @on\nexec sp_trace_setevent @TraceID, 13,  3, @on\nexec sp_trace_setevent @TraceID, 13, 31, @on\nexec sp_trace_setevent @TraceID, 13, 51, @on\n\n/* Audit Login */\nexec sp_trace_setevent @TraceID, 14,  1, @on\nexec sp_trace_setevent @TraceID, 14, 11, @on\nexec sp_trace_setevent @TraceID, 14,  8, @on\nexec sp_trace_setevent @TraceID, 14, 10, @on\nexec sp_trace_setevent @TraceID, 14, 12, @on\nexec sp_trace_setevent @TraceID, 14, 14, @on\nexec sp_trace_setevent @TraceID, 14, 35, @on\nexec sp_trace_setevent @TraceID, 14,  3, @on\nexec sp_trace_setevent @TraceID, 14, 51, @on\nexec sp_trace_setevent @TraceID, 14, 21, @on\n\n/* UserConfigurable:0 */\nexec sp_trace_setevent @TraceID, 82,  1, @on\nexec sp_trace_setevent @TraceID, 82,  2, @on\nexec sp_trace_setevent @TraceID, 82, 11, @on\nexec sp_trace_setevent @TraceID, 82,  8, @on\nexec sp_trace_setevent @TraceID, 82, 10, @on\nexec sp_trace_setevent @TraceID, 82, 12, @on\nexec sp_trace_setevent @TraceID, 82, 14, @on\nexec sp_trace_setevent @TraceID, 82, 35, @on \nexec sp_trace_setevent @TraceID, 82,  3, @on\nexec sp_trace_setevent @TraceID, 82, 51, @on\n\n/* UserConfigurable:0 */\nexec sp_trace_setevent @TraceID, 83,  1, @on\nexec sp_trace_setevent @TraceID, 83,  2, @on\nexec sp_trace_setevent @TraceID, 83, 11, @on\nexec sp_trace_setevent @TraceID, 83,  8, @on\nexec sp_trace_setevent @TraceID, 83, 10, @on\nexec sp_trace_setevent @TraceID, 83, 12, @on\nexec sp_trace_setevent @TraceID, 83, 14, @on\nexec sp_trace_setevent @TraceID, 83, 35, @on \nexec sp_trace_setevent @TraceID, 83,  3, @on\nexec sp_trace_setevent @TraceID, 83, 51, @on\n\n-- Set the Filters\nexec sp_trace_setfilter @TraceID, 10 ,  0, 1, N'WorkloadTools'; \n{3}\n\n-- Set the trace status to start\nexec sp_trace_setstatus @TraceID, 1\n\n-- display trace id for future references\nselect TraceID=@TraceID\ngoto finish\n\nerror: \nselect ErrorCode=@rc\n\nfinish: \n\n"
  },
  {
    "path": "WorkloadTools/MMFEventQueue.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing NFX.ApplicationModel.Pile;\n\nnamespace WorkloadTools\n{\n    public class MMFEventQueue : IDisposable , IEventQueue\n    {\n        private readonly ConcurrentQueue<PilePointer> pointers;\n        private readonly MMFPile pile;\n\n        // this has no effect on a memory mapped file...\n        public int BufferSize { get; set; }\n\n        public MMFEventQueue()\n        {\n            pile = new MMFPile(\"workloadevents\");\n            pile.DataDirectoryRoot = System.IO.Path.GetTempPath();\n            pointers = new ConcurrentQueue<PilePointer>();\n            pile.Start();\n        }\n\n        public bool TryDequeue(out WorkloadEvent result)\n        {\n            try\n            {\n                if (!pointers.TryDequeue(out var pp))\n                {\n                    result = null;\n                    return false;\n                }\n                result = (WorkloadEvent)pile.Get(pp);\n                _ = pile.Delete(pp);\n            }\n            catch(Exception)\n            {\n                result = null;\n                return false;\n            }\n            return true;\n        }\n\n        public void Enqueue(WorkloadEvent evt)\n        {\n            pointers.Enqueue(pile.Put(evt));\n        }\n\n        public void Dispose()\n        {\n            pile.WaitForCompleteStop();\n            pile.Dispose();\n        }\n\n        public bool HasMoreElements()\n        {\n            return pointers.Count > 0;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/MessagWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public class MessageWorkloadEvent : WorkloadEvent\n    {\n        public enum MessageType\n        {\n            TotalEvents\n        }\n\n        public MessageType MsgType { get; set; }\n        public object Value { get; set; }\n\n        public MessageWorkloadEvent()\n        {\n            Type = EventType.Message;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"ae6e4548-8c33-4728-8504-88aa9666020b\")]\n\n"
  },
  {
    "path": "WorkloadTools/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace WorkloadTools.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"17.3.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n        \n        [global::System.Configuration.ApplicationScopedSettingAttribute()]\n        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n        [global::System.Configuration.DefaultSettingValueAttribute(\"32\")]\n        public int ReplayConsumer_SEMAPHORE_LIMIT {\n            get {\n                return ((int)(this[\"ReplayConsumer_SEMAPHORE_LIMIT\"]));\n            }\n        }\n        \n        [global::System.Configuration.ApplicationScopedSettingAttribute()]\n        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n        [global::System.Configuration.DefaultSettingValueAttribute(\"15\")]\n        public int ReplayConsumer_WORKER_EXPIRY_TIMEOUT_SECONDS {\n            get {\n                return ((int)(this[\"ReplayConsumer_WORKER_EXPIRY_TIMEOUT_SECONDS\"]));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"http://schemas.microsoft.com/VisualStudio/2004/01/settings\" CurrentProfile=\"(Default)\" GeneratedClassNamespace=\"WorkloadTools.Properties\" GeneratedClassName=\"Settings\">\n  <Profiles />\n  <Settings>\n    <Setting Name=\"ReplayConsumer_SEMAPHORE_LIMIT\" Type=\"System.Int32\" Scope=\"Application\">\n      <Value Profile=\"(Default)\">32</Value>\n    </Setting>\n    <Setting Name=\"ReplayConsumer_WORKER_EXPIRY_TIMEOUT_SECONDS\" Type=\"System.Int32\" Scope=\"Application\">\n      <Value Profile=\"(Default)\">15</Value>\n    </Setting>\n  </Settings>\n</SettingsFile>"
  },
  {
    "path": "WorkloadTools/Properties/SharedAssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"SqlWorkload\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"spaghettidba\")]\n[assembly: AssemblyProduct(\"SqlWorkload\")]\n[assembly: AssemblyCopyright(\"Copyright © 2018 spaghettidba\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.4\")]\n[assembly: AssemblyFileVersion(\"1.0.4\")]\n"
  },
  {
    "path": "WorkloadTools/SqlConnectionInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    public class SqlConnectionInfo\n    {\n        public string ServerName { get; set; }\n        public string DatabaseName { get; set; } = \"master\";\n        public string SchemaName { get; set; } = \"dbo\";\n        public bool UseIntegratedSecurity { get; set; }\n        public string UserName { get; set; }\n        public string Password { get; set; }\n        public bool Encrypt { get; set; } = false;\n        public bool TrustServerCertificate { get; set; } = false;\n        public string ApplicationName { get; set; } = \"WorkloadTools\";\n        public int MaxPoolSize { get; set; } = 500;\n        public Dictionary<string, string> DatabaseMap { get; set; } = new Dictionary<string, string>();\n\n\n        public SqlConnectionInfo() { }\n\n        public SqlConnectionInfo(SqlConnectionInfo info)\n        {\n            this.ServerName = info.ServerName;\n            this.DatabaseName = info.DatabaseName;\n            this.SchemaName = info.SchemaName;\n            this.UseIntegratedSecurity = info.UseIntegratedSecurity;\n            this.UserName = info.UserName;\n            this.Password = info.Password;\n            this.Encrypt = info.Encrypt;\n            this.TrustServerCertificate = info.TrustServerCertificate;\n            this.ApplicationName = info.ApplicationName;\n            this.MaxPoolSize = info.MaxPoolSize;\n            this.DatabaseMap = info.DatabaseMap;\n        }\n\n        public string ConnectionString()\n        {\n            return ConnectionString(ApplicationName);\n        }\n\n        public string ConnectionString(string applicationName)\n        {\n            var connectionString = \"Data Source=\" + ServerName + \"; \";\n            connectionString += \"Max Pool Size = \" + MaxPoolSize + \"; \";\n            if (string.IsNullOrEmpty(DatabaseName))\n            {\n                connectionString += \"Initial Catalog = master; \";\n            }\n            else\n            {\n                // try to replace database name with the name\n                // in the database map, if any\n                var effectiveDatabaseName = DatabaseName;\n                if (DatabaseMap.ContainsKey(DatabaseName))\n                {\n                    effectiveDatabaseName = DatabaseMap[DatabaseName];\n                }\n                connectionString += \"Initial Catalog = \" + effectiveDatabaseName + \"; \";\n            }\n            if (string.IsNullOrEmpty(UserName))\n            {\n                connectionString += \"Integrated Security = SSPI; \";\n            }\n            else\n            {\n                connectionString += \"User Id = \" + UserName + \"; \";\n                connectionString += \"Password = \" + Password + \"; \";\n            }\n            if (!string.IsNullOrEmpty(applicationName))\n            {\n                connectionString += \"Application Name = \" + applicationName + \"; \";\n            }\n            if (Encrypt)\n            {\n                connectionString += \"Encrypt = true; \";\n            }\n            if (TrustServerCertificate)\n            {\n                connectionString += \"TrustServerCertificate = true; \";\n            }\n            return connectionString;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/SqliteEventQueue.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools\n{\n    public class SqliteEventQueue : BufferedEventQueue\n    {\n\n        public SqliteEventQueue() : base()\n        {\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            throw new NotImplementedException();\n        }\n\n        protected override WorkloadEvent[] ReadEvents(int count)\n        {\n            // STRATEGY:\n            // do not attempt deleting rows returned\n            // read all rows from the table and drop it\n            throw new NotImplementedException();\n        }\n\n        protected override void WriteEvents(WorkloadEvent[] events)\n        {\n            // STRATEGY:\n            // write to a table\n            // the table name has an index postfix like cache01, cache02...\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Util/DataUtils.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace WorkloadTools.Util\n{\n    public class DataUtils\n    {\n\n        /// <summary>\n        /// Convert a List{T} to a DataTable.\n        /// </summary>\n        public static DataTable ToDataTable<T>(IEnumerable<T> items)\n        {\n            var tb = new DataTable(typeof(T).Name);\n\n            var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);\n\n            foreach (var prop in props)\n            {\n                var t = GetCoreType(prop.PropertyType);\n                _ = tb.Columns.Add(prop.Name, t);\n            }\n\n            foreach (var item in items)\n            {\n                var values = new object[props.Length];\n\n                for (var i = 0; i < props.Length; i++)\n                {\n                    values[i] = props[i].GetValue(item, null);\n                }\n\n                _ = tb.Rows.Add(values);\n            }\n\n            return tb;\n        }\n\n        /// <summary>\n        /// Determine of specified type is nullable\n        /// </summary>\n        public static bool IsNullable(Type t)\n        {\n            return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));\n        }\n\n        /// <summary>\n        /// Return underlying type if type is Nullable otherwise return the type\n        /// </summary>\n        public static Type GetCoreType(Type t)\n        {\n            if (t != null && IsNullable(t))\n            {\n                if (!t.IsValueType)\n                {\n                    return t;\n                }\n                else\n                {\n                    return Nullable.GetUnderlyingType(t);\n                }\n            }\n            else\n            {\n                return t;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Util/ModelConverter.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Web.Script.Serialization;\nusing WorkloadTools;\n\nnamespace WorkloadTools.Util\n{\n    public class ModelConverter : JavaScriptConverter\n    {\n        public override IEnumerable<Type> SupportedTypes\n        {\n            get\n            {\n                var result = new List<Type>();\n                var currentAssembly = Assembly.GetExecutingAssembly();\n                var nameSpace = \"WorkloadTools\";\n                var types = currentAssembly.GetTypes().Where(t => t != null && t.FullName.StartsWith(nameSpace) & !t.FullName.Contains(\"+\")).ToArray();\n                foreach (var t in types)\n                {\n                    try\n                    {\n                        result.Add(t);\n                    }\n                    catch (Exception)\n                    {\n                        throw;\n                    }\n                }\n                return result;\n            }\n        }\n\n        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)\n        {\n            object p;\n            try\n            {\n                // try to create the object using its parameterless constructor\n                p = Activator.CreateInstance(type);\n            }\n            catch {\n                // try to create the object using this scary initializer that \n                // doesn't need the parameterless constructor\n                p = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);\n            }\n\n            var props = type.GetProperties();\n\n            foreach (var key in dictionary.Keys)\n            {\n                var prop = props.Where(t => t.Name == key).FirstOrDefault();\n                if (prop != null)\n                {\n                    if (prop.Name.EndsWith(\"Filter\"))\n                    {\n                        if (dictionary[key] is string stringValue)\n                        {\n                            prop.SetValue(p, new string[] { stringValue }, null);\n                        }\n                        else\n                        {\n                            prop.SetValue(p, (string[])((ArrayList)dictionary[key]).ToArray(typeof(string)), null);\n                        }\n                    }\n                    else\n                    {\n                        if (dictionary[key] is Dictionary<string, object> dictionaryValue)\n                        {\n                            if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>))\n                            {\n                                var rawDic = dictionaryValue;\n\n                                var obj = Activator.CreateInstance(prop.PropertyType);\n                                foreach (var itm in rawDic.Keys)\n                                {\n                                    ((Dictionary<string, string>)obj).Add(itm, rawDic[itm].ToString());\n                                }\n                                prop.SetValue(p, obj, null);\n                            }\n                            else\n                            {\n                                prop.SetValue(p, Deserialize(dictionaryValue, prop.PropertyType, serializer), null);\n                            }\n                        }\n                        else\n                        {\n                            if (dictionary[key] is IList && prop.PropertyType.IsGenericType)\n                            {\n                                var obj = Activator.CreateInstance(prop.PropertyType);\n                                foreach (var itm in (IEnumerable)dictionary[key])\n                                {\n                                    _ = ((IList)obj).Add(itm);\n                                }\n                                prop.SetValue(p, obj, null);\n                            }\n                            else \n                            { \n                                prop.SetValue(p, GetValueOfType(dictionary[key], prop.PropertyType), null);\n                            }\n                        }\n                           \n                    }\n                }\n            }\n\n            return p;\n        }\n\n        private object GetValueOfType(object v, Type propertyType)\n        {\n            if (propertyType == typeof(string))\n            {\n                return (string)v;\n            }\n            else if (propertyType == typeof(bool))\n            {\n                return Convert.ToBoolean(v);\n            }\n            else if (propertyType == typeof(int))\n            {\n                return Convert.ToInt32(v);\n            }\n            else if (propertyType == typeof(long))\n            {\n                return Convert.ToInt64(v);\n            }\n            else if (propertyType == typeof(DateTime))\n            {\n                return Convert.ToDateTime(v);\n            }\n            else\n            {\n                return v;\n            }\n        }\n\n        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)\n        {\n            throw new NotImplementedException();\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/Util/RingBuffer.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace WorkloadTools.Util\n{\n    /// <summary>\n    /// Represents a fixted length ring buffer to store a specified maximal count of items within.\n    /// </summary>\n    /// <typeparam name=\"T\">The generic type of the items stored within the ring buffer.</typeparam>\n    [DebuggerDisplay(\"Count = {Count}\")]\n    public class RingBuffer<T> : IList<T>, ICollection<T>,\n                                 IEnumerable<T>\n    {\n        /// <summary>\n        /// Creates a new instance of a <see cref=\"RingBuffer&lt;T&gt;\"/> with a \n        /// specified cache size.\n        /// </summary>\n        /// <param name=\"capacity\">The maximal count of items to be stored within \n        /// the ring buffer.</param>\n        public RingBuffer(int capacity)\n        {\n            // validate capacity\n            if (capacity <= 0)\n            {\n                throw new ArgumentException(\"Must be greater than zero\", \"capacity\");\n            }\n            // set capacity and init the cache\n            Capacity = capacity;\n            _buffer = new T[capacity];\n        }\n\n        /// <summary>\n        /// the internal buffer\n        /// </summary>\n        readonly T[] _buffer;\n        /// <summary>\n        /// The all-over position within the ring buffer. The position \n        /// increases continously by adding new items to the buffer. This \n        /// value is needed to calculate the current relative position within the \n        /// buffer.\n        /// </summary>\n        int _position;\n        /// <summary>\n        /// The current version of the buffer, this is required for a correct \n        /// exception handling while enumerating over the items of the buffer.\n        /// </summary>\n        long _version;\n\n        /// <summary>\n        /// Gets or sets an item for a specified position within the ring buffer.\n        /// </summary>\n        /// <param name=\"index\">The position to get or set an item.</param>\n        /// <returns>The fond item at the specified position within the ring buffer.\n        /// </returns>\n        /// <exception cref=\"IndexOutOfRangeException\"></exception>\n        public T this[int index]\n        {\n            get\n            {\n                // validate the index\n                if (index < 0 || index >= Count)\n                {\n                    throw new IndexOutOfRangeException();\n                }\n                // calculate the relative position within the rolling base array\n                var index2 = (_position - Count + index) % Capacity;\n                return _buffer[index2];\n            }\n            set => Insert(index, value);\n        }\n\n        public T Last()\n        {\n            return this[Count-1];\n        }\n\n        /// <summary>\n        /// Gets the maximal count of items within the ring buffer.\n        /// </summary>\n        public int Capacity { get; private set; }\n        /// <summary>\n        /// Get the current count of items within the ring buffer.\n        /// </summary>\n        public int Count { get; private set; }\n\n        /// <summary>\n        /// Adds a new item to the buffer.\n        /// </summary>\n        /// <param name=\"item\">The item to be added to the buffer.</param>\n        public void Add(T item)\n        {\n            // avoid an arithmetic overflow\n            if (_position == int.MaxValue)\n            {\n                _position = _position % Capacity;\n            }\n            // add a new item to the current relative position within the\n            // buffer and increase the position\n            _buffer[_position++ % Capacity] = item;\n            // increase the count if capacity is not yet reached\n            if (Count < Capacity)\n            {\n                Count++;\n            }\n            // buffer changed; next version\n            _version++;\n        }\n\n        /// <summary>\n        /// Clears the whole buffer and releases all referenced objects \n        /// currently stored within the buffer.\n        /// </summary>\n        public void Clear()\n        {\n            for (var i = 0; i < Count; i++)\n            {\n                _buffer[i] = default(T);\n            }\n\n            _position = 0;\n            Count = 0;\n            _version++;\n        }\n\n        /// <summary>\n        /// Determines if a specified item is currently present within\n        /// the buffer.\n        /// </summary>\n        /// <param name=\"item\">The item to search for within the current\n        /// buffer.</param>\n        /// <returns>True if the specified item is currently present within \n        /// the buffer; otherwise false.</returns>\n        public bool Contains(T item)\n        {\n            var index = IndexOf(item);\n            return index != -1;\n        }\n\n        /// <summary>\n        /// Copies the current items within the buffer to a specified array.\n        /// </summary>\n        /// <param name=\"array\">The target array to copy the items of \n        /// the buffer to.</param>\n        /// <param name=\"arrayIndex\">The start position witihn the target\n        /// array to start copying.</param>\n        public void CopyTo(T[] array, int arrayIndex)\n        {\n            for (var i = 0; i < Count; i++)\n            {\n                array[i + arrayIndex] = _buffer[(_position - Count + i) % Capacity];\n            }\n        }\n\n        /// <summary>\n        /// Gets an enumerator over the current items within the buffer.\n        /// </summary>\n        /// <returns>An enumerator over the current items within the buffer.\n        /// </returns>\n        public IEnumerator<T> GetEnumerator()\n        {\n            var version = _version;\n            for (var i = 0; i < Count; i++)\n            {\n                if (version != _version)\n                {\n                    throw new InvalidOperationException(\"Collection changed\");\n                }\n\n                yield return this[i];\n            }\n        }\n\n        /// <summary>\n        /// Gets the position of a specied item within the ring buffer.\n        /// </summary>\n        /// <param name=\"item\">The item to get the current position for.</param>\n        /// <returns>The zero based index of the found item within the \n        /// buffer. If the item was not present within the buffer, this\n        /// method returns -1.</returns>\n        public int IndexOf(T item)\n        {\n            // loop over the current count of items\n            for (var i = 0; i < Count; i++)\n            {\n                // get the item at the relative position within the internal array\n                var item2 = _buffer[(_position - Count + i) % Capacity];\n                // if both items are null, return true\n                if (null == item && null == item2)\n                {\n                    return i;\n                }\n                // if equal return the position\n                if (item != null && item.Equals(item2))\n                {\n                    return i;\n                }\n            }\n            // nothing found\n            return -1;\n        }\n\n        /// <summary>\n        /// Inserts an item at a specified position into the buffer.\n        /// </summary>\n        /// <param name=\"index\">The position within the buffer to add \n        /// the new item.</param>\n        /// <param name=\"item\">The new item to be added to the buffer.</param>\n        /// <exception cref=\"IndexOutOfRangeException\"></exception>\n        /// <remarks>\n        /// If the specified index is equal to the current count of items\n        /// within the buffer, the specified item will be added.\n        /// \n        /// <b>Warning</b>\n        /// Frequent usage of this method might become a bad idea if you are \n        /// working with a large buffer capacity. The insertion of an item\n        /// at a specified position within the buffer causes causes all present \n        /// items below the specified position to be moved one position.\n        /// </remarks>\n        public void Insert(int index, T item)\n        {\n            // validate index\n            if (index < 0 || index > Count)\n            {\n                throw new IndexOutOfRangeException();\n            }\n            // add if index equals to count\n            if (index == Count)\n            {\n                Add(item);\n                return;\n            }\n\n            // get the maximal count of items to be moved\n            var count = Math.Min(Count, Capacity - 1) - index;\n            // get the relative position of the new item within the buffer\n            var index2 = (_position - Count + index) % Capacity;\n\n            // move all items below the specified position\n            for (var i = index2 + count; i > index2; i--)\n            {\n                var to = i % Capacity;\n                var from = (i - 1) % Capacity;\n                _buffer[to] = _buffer[from];\n            }\n\n            // set the new item\n            _buffer[index2] = item;\n\n            // adjust storage information\n            if (Count < Capacity)\n            {\n                Count++;\n                _position++;\n            }\n            // buffer changed; next version\n            _version++;\n        }\n\n        /// <summary>\n        /// Removes a specified item from the current buffer.\n        /// </summary>\n        /// <param name=\"item\">The item to be removed.</param>\n        /// <returns>True if the specified item was successfully removed\n        /// from the buffer; otherwise false.</returns>\n        /// <remarks>\n        /// <b>Warning</b>\n        /// Frequent usage of this method might become a bad idea if you are \n        /// working with a large buffer capacity. The removing of an item \n        /// requires a scan of the buffer to get the position of the specified\n        /// item. If the item was found, the deletion requires a move of all \n        /// items stored abouve the found position.\n        /// </remarks>\n        public bool Remove(T item)\n        {\n            // find the position of the specified item\n            var index = IndexOf(item);\n            // item was not found; return false\n            if (index == -1)\n            {\n                return false;\n            }\n            // remove the item at the specified position\n            RemoveAt(index);\n            return true;\n        }\n\n        /// <summary>\n        /// Removes an item at a specified position within the buffer.\n        /// </summary>\n        /// <param name=\"index\">The position of the item to be removed.</param>\n        /// <exception cref=\"IndexOutOfRangeException\"></exception>\n        /// <remarks>\n        /// <b>Warning</b>\n        /// Frequent usage of this method might become a bad idea if you are \n        /// working with a large buffer capacity. The deletion requires a move \n        /// of all items stored abouve the found position.\n        /// </remarks>\n        public void RemoveAt(int index)\n        {\n            // validate the index\n            if (index < 0 || index >= Count)\n            {\n                throw new IndexOutOfRangeException();\n            }\n            // move all items above the specified position one step\n            // closer to zeri\n            for (var i = index; i < Count - 1; i++)\n            {\n                // get the next relative target position of the item\n                var to = (_position - Count + i) % Capacity;\n                // get the next relative source position of the item\n                var from = (_position - Count + i + 1) % Capacity;\n                // move the item\n                _buffer[to] = _buffer[from];\n            }\n            // get the relative position of the last item, which becomes empty\n            // after deletion and set the item as empty\n            var last = (_position - 1) % Capacity;\n            _buffer[last] = default(T);\n            // adjust storage information\n            _position--;\n            Count--;\n            // buffer changed; next version\n            _version++;\n        }\n\n        /// <summary>\n        /// Gets if the buffer is read-only. This method always returns false.\n        /// </summary>\n        bool ICollection<T>.IsReadOnly { get { return false; } }\n\n        /// <summary>\n        /// See generic implementation of <see cref=\"GetEnumerator\"/>.\n        /// </summary>\n        /// <returns>See generic implementation of <see cref=\"GetEnumerator\"/>.\n        /// </returns>\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n\n}\n"
  },
  {
    "path": "WorkloadTools/Util/StringExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Util\n{\n    public static class StringExtensions\n    {\n        public static string Right(this string value, int count)\n        {\n            //Check if the value is valid\n            if (string.IsNullOrEmpty(value))\n            {\n                //Set valid empty string as string could be null\n                value = string.Empty;\n            }\n            else if (value.Length > count)\n            {\n                //Make the string no longer than the max length\n                value = value.Substring(value.Length - count, count);\n            }\n\n            //Return the string\n            return value;\n        }\n\n        public static string ReplaceFirst(this string text, string search, string replace)\n        {\n            var pos = text.IndexOf(search);\n            if (pos < 0)\n            {\n                return text;\n            }\n            return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WaitStatsWorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public class WaitStatsWorkloadEvent : WorkloadEvent\n    {\n        public DataTable Waits;\n\n        public WaitStatsWorkloadEvent()\n        {\n            Type = EventType.WAIT_stats;\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WorkloadController.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing WorkloadTools.Consumer;\n\nnamespace WorkloadTools\n{\n    public class WorkloadController : IDisposable\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public static string BaseLocation = new Uri(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().CodeBase)).LocalPath;\n\n        public WorkloadListener Listener { get; set; }\n        public List<WorkloadConsumer> Consumers { get; set; } = new List<WorkloadConsumer>();\n\n        private bool forceStopped = false;\n        private bool stopped = false;\n        private bool disposed = false;\n        private const int MAX_DISPOSE_TIMEOUT_SECONDS = 5;\n\n        public WorkloadController()\n        {\n        }\n\n        public void Run()\n        {\n            try\n            {\n\t\t\t\tvar startTime = Listener.StartAt;\n\t\t\t\tvar endTime = DateTime.MaxValue;\n\n                Listener.Initialize();\n\n                logger.Info(\"Listener of type {ListenerTypeName} initialized correctly\", Listener.GetType().Name);\n                logger.Info(\"Event collection starts at {startTime}\", startTime);\n                // wait until Listener.StartAt has been reached\n                while (DateTime.Now.CompareTo(startTime) < 0)\n                {\n                    Thread.Sleep(100);\n                }\n\n                logger.Info(\"Waiting for events\");\n\n                do\n                {\n                    try\n                    {\n                        if ((!Listener.IsRunning && Consumers.All(c => !c.HasMoreEvents())) || (endTime < DateTime.Now))\n                        {\n                            stopped = true;\n                        }\n\n                        if (endTime == DateTime.MaxValue && Listener.TimeoutMinutes != 0)\n                        {\n                            endTime = startTime.AddMinutes(Listener.TimeoutMinutes);\n                        }\n\n                        var evt = Listener.Read();\n                        if (evt == null)\n                        {\n                            continue;\n                        }\n\n                        logger.Debug($\"Event of type {evt.Type} read. Start Time: {evt.StartTime}\");\n                        _ = Parallel.ForEach(Consumers, (cons) =>\n                        {\n                            cons.Consume(evt);\n                        });\n                    }\n                    catch (Exception e)\n                    {\n                        logger.Error(\"Exception reading event\");\n                        logger.Error(e.Message);\n                        logger.Error(e.StackTrace);\n                    }\n                } while (!stopped);\n\n                // even when the listener has finished, wait until all buffered consumers are finished\n                // unless the controller has been explicitly stopped by invoking Stop()\n                // give max 1 minute grace time\n                if (!forceStopped)\n                {\n                    var beginWait = DateTime.Now;\n                    while (Consumers.Where(c => c is BufferedWorkloadConsumer).Any(c => c.HasMoreEvents()) && DateTime.Now < beginWait.AddMinutes(1))\n                    {\n                        Thread.Sleep(10);\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                logger.Error(\"Uncaught Exception\");\n                logger.Error(e.Message);\n                logger.Error(e.StackTrace);\n\n                var ex = e;\n                while ((ex = ex.InnerException) != null){\n                    logger.Error(ex.Message);\n                    logger.Error(ex.StackTrace);\n                }\n            }\n        }\n\n        public void Stop()\n        {\n            forceStopped = true;\n            stopped = true;\n        }\n\n        public void Dispose()\n        {\n            if (!disposed)\n            {\n                disposed = true;\n                foreach (var cons in Consumers)\n                {\n                    cons?.Dispose();\n                }\n\n                Listener?.Dispose();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WorkloadEvent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    [Serializable]\n    public abstract class WorkloadEvent\n    {\n        public enum EventType \n        {\n            Message = 0,\n            RPCCompleted = 1,\n            RPCStarting = 2,\n            BatchStarting = -3,\n            BatchCompleted = 3,\n            PerformanceCounter = 4,\n            Timeout = 5,\n            WAIT_stats = 6,\n            Error = 7,\n            DiskPerf = 8,\n            Unknown = -1\n        }\n\n        public DateTime StartTime{ get; set; }\n        public EventType Type { get; set; } = EventType.Unknown;\n        \n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WorkloadEventFilter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace WorkloadTools\n{\n    public abstract class WorkloadEventFilter\n    {\n        public FilterPredicate ApplicationFilter { get; set; }\n        public FilterPredicate DatabaseFilter { get; set; }\n        public FilterPredicate HostFilter { get; set; }\n        public FilterPredicate LoginFilter { get; set; }\n\n        public WorkloadEventFilter()\n        {\n        }\n\n        public bool Evaluate(WorkloadEvent evnt)\n        {\n            // don't filter events that are not supposed to be filtered\n            if (!(evnt is ExecutionWorkloadEvent))\n            {\n                return true;\n            }\n\n            var evt = (ExecutionWorkloadEvent)evnt;\n\n            if (evt.Type != WorkloadEvent.EventType.BatchStarting \n                && \n                evt.Type != WorkloadEvent.EventType.RPCStarting\n                &&\n                evt.Type != WorkloadEvent.EventType.BatchCompleted\n                &&\n                evt.Type != WorkloadEvent.EventType.RPCCompleted)\n            {\n                return false;\n            }\n\n            if (!(DatabaseFilter.IsPredicateSet || LoginFilter.IsPredicateSet || HostFilter.IsPredicateSet || ApplicationFilter.IsPredicateSet))\n            {\n                return true;\n            }\n\n            var applicationFilterResults = !ApplicationFilter.IsPredicateSet || ApplicationFilter.IsPushedDown;\n            var databaseFilterResults = !DatabaseFilter.IsPredicateSet || DatabaseFilter.IsPushedDown;\n            var loginFilterResults = !LoginFilter.IsPredicateSet || LoginFilter.IsPushedDown;\n            var hostFilterResults = !HostFilter.IsPredicateSet || HostFilter.IsPushedDown;\n\n            if (ApplicationFilter.IsPredicateSet && !ApplicationFilter.IsPushedDown)\n            {\n                applicationFilterResults = ApplicationFilter.PredicateValue.Contains(evt.ApplicationName, StringComparer.CurrentCultureIgnoreCase);\n            }\n\n            if (DatabaseFilter.IsPredicateSet && !DatabaseFilter.IsPushedDown)\n            {\n                databaseFilterResults = DatabaseFilter.PredicateValue.Contains(evt.DatabaseName, StringComparer.CurrentCultureIgnoreCase);\n            }\n\n            if (LoginFilter.IsPredicateSet && !LoginFilter.IsPushedDown)\n            {\n                loginFilterResults = LoginFilter.PredicateValue.Contains(evt.LoginName, StringComparer.CurrentCultureIgnoreCase);\n            }\n\n            if (HostFilter.IsPredicateSet && !HostFilter.IsPushedDown)\n            {\n                hostFilterResults = HostFilter.PredicateValue.Contains(evt.HostName, StringComparer.CurrentCultureIgnoreCase);\n            }\n\n            return applicationFilterResults && databaseFilterResults && loginFilterResults && hostFilterResults;\n        }\n\n        public void PushDown(FilterPredicate predicate)\n        {\n            _ = predicate.PushDown();\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WorkloadListener.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SqlClient;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing WorkloadTools.Util;\n\nnamespace WorkloadTools\n{\n    public abstract class WorkloadListener : IDisposable\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n        public string Source { get; set; }\n\n        private string[] _applicationFilter;\n        private string[] _databaseFilter;\n        private string[] _hostFilter;\n        private string[] _loginFilter;\n\n        public string[] ApplicationFilter\n        {\n            get => _applicationFilter;\n            set\n            {\n                _applicationFilter = value;\n                if (_filter != null)\n                {\n                    _filter.ApplicationFilter.PredicateValue = _applicationFilter;\n                }\n            }\n        }\n        public string[] DatabaseFilter\n        {\n            get => _databaseFilter;\n            set\n            {\n                _databaseFilter = value;\n                if (_filter != null)\n                {\n                    _filter.DatabaseFilter.PredicateValue = _databaseFilter;\n                }\n            }\n        }\n        public string[] HostFilter\n        {\n            get => _hostFilter;\n            set\n            {\n                _hostFilter = value;\n                if (_filter != null)\n                {\n                    _filter.HostFilter.PredicateValue = _hostFilter;\n                }\n            }\n        }\n        public string[] LoginFilter\n        {\n            get => _loginFilter;\n            set\n            {\n                _loginFilter = value;\n                if (_filter != null)\n                {\n                    _filter.LoginFilter.PredicateValue = _loginFilter;\n                }\n            }\n        }\n\n        public int StatsCollectionIntervalSeconds { get; set; } = 60;\n        public int TimeoutMinutes { get; set; } = 0;\n        public DateTime StartAt { get; set; } = DateTime.Now;\n\n        private WorkloadEventFilter _filter;\n\n        protected WorkloadEventFilter Filter\n        {\n            get\n            {\n                if (_filter != null)\n                {\n                    return _filter;\n                }\n                else\n                {\n                    return null;\n                }\n            }\n            set => _filter = value;\n        }\n\n        protected IEventQueue Events;\n\n        public EventQueueType QueueType = EventQueueType.BinarySerialized;\n\n        protected bool stopped = false;\n\n        public WorkloadListener()\n        {\n            switch (QueueType)\n            {\n                case EventQueueType.MMF:\n                    Events = new MMFEventQueue();\n                    break;\n                case EventQueueType.LiteDB:\n                    throw new NotImplementedException();\n                case EventQueueType.Sqlite:\n                    throw new NotImplementedException();\n                case EventQueueType.BinarySerialized:\n                    Events = new BinarySerializedBufferedEventQueue();\n                    Events.BufferSize = 10000;\n                    break;\n            }\n            \n        }\n\n        public void Dispose() {\n            stopped = true;\n            Events.Dispose();\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        protected abstract void Dispose(bool disposing);\n\n        public abstract WorkloadEvent Read();\n\n        public abstract void Initialize();\n\n        public bool IsRunning { get { return !stopped; } }\n\n        // Collects some performance counters\n        protected virtual void ReadPerfCountersEvents()\n        {\n            try\n            {\n                while (!stopped)\n                {\n                    var evt = new CounterWorkloadEvent();\n                    evt.Type = WorkloadEvent.EventType.PerformanceCounter;\n                    evt.StartTime = DateTime.Now;\n\n                    evt.Counters.Add(\n                        CounterWorkloadEvent.CounterNameEnum.AVG_CPU_USAGE,\n                        GetLastCPUUsage()\n                    );\n\n                    Events.Enqueue(evt);\n\n                    Thread.Sleep(StatsCollectionIntervalSeconds * 1000); // 1 minute\n                }\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n                logger.Error(ex.StackTrace);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n            }\n        }\n\n        private int GetLastCPUUsage()\n        {\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                // Calculate CPU usage during the last minute interval\n                var sql = @\"\n                    IF SERVERPROPERTY('Edition') = 'SQL Azure'\n                        AND SERVERPROPERTY('EngineEdition') = 5\n                    BEGIN\n                        WITH CPU_Usage AS (\n                            SELECT avg_cpu_percent, end_time AS Event_Time\n                            FROM sys.dm_db_resource_stats WITH (NOLOCK) \n                        )\n                        SELECT \n                            CAST(ISNULL(AVG(avg_cpu_percent),0) AS int) AS avg_CPU_percent\n                        FROM CPU_Usage\n                        WHERE [Event_Time] >= DATEADD(minute, -{0}, GETDATE())\n                        OPTION (RECOMPILE);\n                    END\n\n                    IF SERVERPROPERTY('Edition') = 'SQL Azure'\n                        AND SERVERPROPERTY('EngineEdition') = 8 -- Managed Instance\n                    BEGIN\n                        WITH PerfCounters AS (\n\t                        SELECT DISTINCT\n\t                             RTrim(spi.[object_name]) AS [object_name]\n\t                            ,RTrim(spi.[counter_name]) AS [counter_name]\n\t                            ,RTRIM(spi.instance_name) AS [instance_name]\n\t                            ,CAST(spi.[cntr_value] AS BIGINT) AS [cntr_value]\n\t                            ,spi.[cntr_type]\n\t                        FROM sys.dm_os_performance_counters AS spi \n\t                        LEFT JOIN sys.databases AS d\n\t\t                        ON LEFT(spi.[instance_name], 36) -- some instance_name values have an additional identifier appended after the GUID\n\t\t                        = d.[name]\n\t                        WHERE\n\t\t                        counter_name IN (\n\t\t\t                         'CPU usage %'\n\t\t\t                        ,'CPU usage % base'\n\t\t                        ) \n                        )\n                        SELECT CAST(SUM(value) AS int) AS avg_CPU_percent\n                        FROM (\n                            SELECT \n\t                            CAST(CASE WHEN pc.[cntr_type] = 537003264 AND pc1.[cntr_value] > 0 THEN (pc.[cntr_value] * 1.0) / (pc1.[cntr_value] * 1.0) * 100 ELSE pc.[cntr_value] END AS float(10)) AS [value]\n                            from PerfCounters pc\n                            LEFT OUTER JOIN PerfCounters AS pc1\n\t                            ON (\n\t\t                            pc.[counter_name] = REPLACE(pc1.[counter_name],' base','')\n\t\t                            OR pc.[counter_name] = REPLACE(pc1.[counter_name],' base',' (ms)')\n\t                            )\n\t                            AND pc.[object_name] = pc1.[object_name]\n\t                            AND pc.[instance_name] = pc1.[instance_name]\n\t                            AND pc1.[counter_name] LIKE '%base'\n                            WHERE\n\t                            pc.[counter_name] NOT LIKE '% base'\n                                AND pc.object_name LIKE '%:Resource Pool Stats'\n                        ) AS p\n                        OPTION (RECOMPILE);\n                    END\n\n\n                    ELSE -- On Premises\n\n                    BEGIN\n                        WITH ts_now(ts_now) AS (\n                            SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)\n                        ),\n                        CPU_Usage AS (\n                            SELECT TOP(256) SQLProcessUtilization, \n                                            DATEADD(ms, -1 * (ts_now.ts_now - [timestamp]), GETDATE()) AS [Event_Time] \n                            FROM (\n                                SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, \n                                    record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') \n                                    AS [SystemIdle], \n                                    record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') \n                                    AS [SQLProcessUtilization], [timestamp] \n                                FROM (\n                                    SELECT [timestamp], CONVERT(xml, record) AS [record] \n                                    FROM sys.dm_os_ring_buffers WITH (NOLOCK)\n                                    WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' \n                                        AND record LIKE N'%<SystemHealth>%'\n                                ) AS x\n                            ) AS y \n                            CROSS JOIN ts_now\n                        )\n                        SELECT \n                            ISNULL(AVG(SQLProcessUtilization),0) AS avg_CPU_percent\n                        FROM CPU_Usage\n                        WHERE [Event_Time] >= DATEADD(minute, -{0}, GETDATE())\n                        OPTION (RECOMPILE);\n                    END\n                \";\n\n                sql = string.Format(sql,StatsCollectionIntervalSeconds / 60);\n\n                var avg_CPU_percent = -1;\n\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = sql;\n                    avg_CPU_percent = (int)cmd.ExecuteScalar();\n                }\n\n                return avg_CPU_percent;\n            }\n        }\n\n        protected virtual void ReadWaitStatsEvents()\n        {\n            try\n            {\n                DataTable lastWaits = null;\n                while (!stopped)\n                {\n                    var evt = new WaitStatsWorkloadEvent();\n                    evt.Type = WorkloadEvent.EventType.WAIT_stats;\n                    evt.StartTime = DateTime.Now;\n\n                    var newWaits = GetWaits();\n                    evt.Waits = GetDiffWaits(newWaits, lastWaits);\n                    lastWaits = newWaits;\n\n                    Events.Enqueue(evt);\n\n                    Thread.Sleep(StatsCollectionIntervalSeconds * 1000); // 1 minute\n                }\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n                logger.Error(ex.StackTrace);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n            }\n        }\n\n\n        private DataTable GetDiffWaits(DataTable newWaits, DataTable lastWaits)\n        {\n            // no baseline established already\n            // return all zeros\n            if (lastWaits == null)\n            {\n                var result = newWaits.Clone();\n                foreach (DataRow dr in newWaits.Rows)\n                {\n                    var nr = result.Rows.Add();\n                    nr[\"wait_type\"] = dr[\"wait_type\"];\n                    nr[\"wait_sec\"] = 0;\n                    nr[\"resource_sec\"] = 0;\n                    nr[\"signal_sec\"] = 0;\n                    nr[\"wait_count\"] = 0;\n                }\n                return result;\n            }\n\n            // catch the case when stats are reset\n            long newWaitCount = 0;\n            var newWaitCountObj = newWaits.Compute(\"SUM(wait_count)\", null);\n            if (newWaitCountObj != DBNull.Value)\n            {\n                newWaitCount = Convert.ToInt64(newWaitCountObj);\n            }\n            long lastWaitCount = 0;\n            var lastWaitCountObj = lastWaits.Compute(\"SUM(wait_count)\", null);\n            if (lastWaitCountObj != DBNull.Value)\n            {\n                lastWaitCount = Convert.ToInt64(lastWaitCountObj);\n            }\n\n            // if newWaits < lastWaits --> reset\n            // I can return newWaits without having to compute the diff\n            if (newWaitCount < lastWaitCount)\n            {\n                return newWaits;\n            }\n\n            var results = from table1 in newWaits.AsEnumerable()\n                          join table2 in lastWaits.AsEnumerable()\n                                on table1[\"wait_type\"] equals table2[\"wait_type\"]\n                          select new\n                          {\n                              wait_type = Convert.ToString(table1[\"wait_type\"]),\n                              wait_sec = Convert.ToDouble(table1[\"wait_sec\"]) - Convert.ToDouble(table2[\"wait_sec\"]),\n                              resource_sec = Convert.ToDouble(table1[\"resource_sec\"]) - Convert.ToDouble(table2[\"resource_sec\"]),\n                              signal_sec = Convert.ToDouble(table1[\"signal_sec\"]) - Convert.ToDouble(table2[\"signal_sec\"]),\n                              wait_count = Convert.ToDouble(table1[\"wait_count\"]) - Convert.ToDouble(table2[\"wait_count\"])\n                          };\n\n            return DataUtils.ToDataTable(results.Where(w => w.wait_sec > 0));\n        }\n\n        private DataTable GetWaits()\n        {\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                // Calculate waits since instance restart\n                var sql = @\"\n                    WITH [Waits] \n                    AS (\n\t                    SELECT wait_type, wait_time_ms/ 1000.0 AS [WaitS],\n                              (wait_time_ms - signal_wait_time_ms) / 1000.0 AS [ResourceS],\n                               signal_wait_time_ms / 1000.0 AS [SignalS],\n                               waiting_tasks_count AS [WaitCount]\n                        FROM sys.dm_os_wait_stats WITH (NOLOCK)\n                        WHERE [wait_type] NOT IN (\n                            N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP',\n\t\t                    N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',\n                            N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',\n                            N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE',\n\t\t                    N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',\n                            N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',\n                            N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', \n\t\t                    N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',\n                            N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', \n\t\t                    N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE',\n\t\t                    N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST',\n\t\t                    N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK',\n\t\t                    N'PREEMPTIVE_HADR_LEASE_MECHANISM', N'PREEMPTIVE_SP_SERVER_DIAGNOSTICS',\n\t\t                    N'PREEMPTIVE_OS_LIBRARYOPS', N'PREEMPTIVE_OS_COMOPS', N'PREEMPTIVE_OS_CRYPTOPS',\n\t\t                    N'PREEMPTIVE_OS_PIPEOPS', N'PREEMPTIVE_OS_AUTHENTICATIONOPS',\n\t\t                    N'PREEMPTIVE_OS_GENERICOPS', N'PREEMPTIVE_OS_VERIFYTRUST',\n\t\t                    N'PREEMPTIVE_OS_FILEOPS', N'PREEMPTIVE_OS_DEVICEOPS', N'PREEMPTIVE_OS_QUERYREGISTRY',\n\t\t                    N'PREEMPTIVE_OS_WRITEFILE',\n\t\t                    N'PREEMPTIVE_XE_CALLBACKEXECUTE', N'PREEMPTIVE_XE_DISPATCHER',\n\t\t                    N'PREEMPTIVE_XE_GETTARGETSTATE', N'PREEMPTIVE_XE_SESSIONCOMMIT',\n\t\t                    N'PREEMPTIVE_XE_TARGETINIT', N'PREEMPTIVE_XE_TARGETFINALIZE',\n                            N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',\n\t\t                    N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',\n\t\t                    N'QDS_ASYNC_QUEUE',\n                            N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH',\n\t\t                    N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP',\n\t\t                    N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',\n                            N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',\n                            N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP',\n\t\t                    N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES',\n\t\t                    N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT',\n\t\t                    N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_RECOVERY',\n\t\t                    N'XE_BUFFERMGR_ALLPROCESSED_EVENT', N'XE_DISPATCHER_JOIN',\n                            N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'XE_TIMER_EVENT')\n                        AND waiting_tasks_count > 0\n                    )\n                    SELECT\n\t                    W1.wait_type,\n                        CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) AS [wait_sec],\n                        CAST (MAX (W1.ResourceS) AS DECIMAL (16,2)) AS [resource_sec],\n                        CAST (MAX (W1.SignalS) AS DECIMAL (16,2)) AS [signal_sec],\n                        MAX (W1.WaitCount) AS [wait_count]\n                    FROM Waits AS W1\n                    GROUP BY W1.wait_type\n                    HAVING CAST (MAX (W1.WaitS) AS DECIMAL (16,2)) > 0\n                    ORDER BY wait_sec DESC\n                    OPTION (RECOMPILE);\n                \";\n\n                DataTable waits = null;\n\n                using (var adapter = new SqlDataAdapter(sql, conn))\n                {\n                    using (var ds = new DataSet())\n                    {\n                        _ = adapter.Fill(ds);\n                        waits = ds.Tables[0];\n                    }\n                }\n\n                var results = from table1 in waits.AsEnumerable()\n                              select new\n                              {\n                                  wait_type = Convert.ToString(table1[\"wait_type\"]),\n                                  wait_sec = Convert.ToDouble(table1[\"wait_sec\"]),\n                                  resource_sec = Convert.ToDouble(table1[\"resource_sec\"]),\n                                  signal_sec = Convert.ToDouble(table1[\"signal_sec\"]),\n                                  wait_count = Convert.ToDouble(table1[\"wait_count\"])\n                              };\n\n                return DataUtils.ToDataTable(results);\n            }\n        }\n\n\n        protected virtual void ReadDiskPerformanceEvents()\n        {\n            try\n            {\n                DataTable lastDiskPerf = null;\n                while (!stopped)\n                {\n                    var evt = new DiskPerfWorkloadEvent();\n                    evt.Type = WorkloadEvent.EventType.DiskPerf;\n                    evt.StartTime = DateTime.Now;\n\n                    var newDiskPerf = GetDiskPerf();\n                    evt.DiskPerf = GetDiffDiskPerf(newDiskPerf, lastDiskPerf);\n                    lastDiskPerf = newDiskPerf;\n\n                    Events.Enqueue(evt);\n\n                    Thread.Sleep(StatsCollectionIntervalSeconds * 1000); // 1 minute\n                }\n            }\n            catch (Exception ex)\n            {\n                logger.Error(ex.Message);\n                logger.Error(ex.StackTrace);\n\n                if (ex.InnerException != null)\n                {\n                    logger.Error(ex.InnerException.Message);\n                }\n            }\n        }\n\n        private DataTable GetDiffDiskPerf(DataTable newDiskPerf, DataTable lastDiskPerf)\n        {\n            // no baseline established already\n            // return all zeros\n            if (lastDiskPerf == null)\n            {\n                var result = newDiskPerf.Clone();\n\n                if (!result.Columns.Contains(\"cum_read_latency_ms\")) _ = result.Columns.Add(\"cum_read_latency_ms\", typeof(double));\n                if (!result.Columns.Contains(\"cum_reads\")) _ = result.Columns.Add(\"cum_reads\", typeof(double));\n                if (!result.Columns.Contains(\"cum_read_bytes\")) _ = result.Columns.Add(\"cum_read_bytes\", typeof(double));\n                if (!result.Columns.Contains(\"cum_write_latency_ms\")) _ = result.Columns.Add(\"cum_write_latency_ms\", typeof(double));\n                if (!result.Columns.Contains(\"cum_writes\")) _ = result.Columns.Add(\"cum_writes\", typeof(double));\n                if (!result.Columns.Contains(\"cum_write_bytes\")) _ = result.Columns.Add(\"cum_write_bytes\", typeof(double));\n\n                foreach (DataRow dr in newDiskPerf.Rows)\n                {\n                    var nr = result.Rows.Add();\n                    nr[\"database_name\"] = dr[\"database_name\"];\n                    nr[\"physical_filename\"] = dr[\"physical_filename\"];\n                    nr[\"logical_filename\"] = dr[\"logical_filename\"];\n                    nr[\"file_type\"] = dr[\"file_type\"];\n                    nr[\"read_latency_ms\"] = 0;\n                    nr[\"reads\"] = 0;\n                    nr[\"read_bytes\"] = 0;\n                    nr[\"write_latency_ms\"] = 0;\n                    nr[\"writes\"] = 0;\n                    nr[\"write_bytes\"] = 0;\n                    nr[\"cum_read_latency_ms\"] = 0;\n                    nr[\"cum_reads\"] = 0;\n                    nr[\"cum_read_bytes\"] = 0;\n                    nr[\"cum_write_latency_ms\"] = 0;\n                    nr[\"cum_writes\"] = 0;\n                    nr[\"cum_write_bytes\"] = 0;\n\n                    if (newDiskPerf.Columns.Contains(\"volume_mount_point\"))\n                    {\n                        nr[\"volume_mount_point\"] = dr[\"volume_mount_point\"];\n                    }\n                }\n                return result;\n            }\n\n\n            var results = from table1 in newDiskPerf.AsEnumerable()\n                          join table2 in lastDiskPerf.AsEnumerable()\n                                on new \n                                {\n                                    database_name = table1[\"database_name\"], \n                                    physical_filename = table1[\"physical_filename\"], \n                                    logical_filename = table1[\"logical_filename\"], \n                                    file_type = table1[\"file_type\"], \n                                    volume_mount_point = table1[\"volume_mount_point\"]\n                                } \n                                equals new \n                                { \n                                    database_name = table2[\"database_name\"], \n                                    physical_filename = table2[\"physical_filename\"], \n                                    logical_filename = table2[\"logical_filename\"], \n                                    file_type = table2[\"file_type\"], \n                                    volume_mount_point = table2[\"volume_mount_point\"] \n                                }\n                          select new\n                          {\n                              database_name      = Convert.ToString(table1[\"database_name\"]),\n                              physical_filename  = Convert.ToString(table1[\"physical_filename\"]),\n                              logical_filename   = Convert.ToString(table1[\"logical_filename\"]),\n                              file_type          = Convert.ToString(table1[\"file_type\"]),\n                              volume_mount_point = Convert.ToString(table1[\"volume_mount_point\"]),\n                              read_latency_ms    = Convert.ToDouble(table1[\"read_latency_ms\"]) - Convert.ToDouble(table2[\"read_latency_ms\"]),\n                              reads              = Convert.ToDouble(table1[\"reads\"]) - Convert.ToDouble(table2[\"reads\"]),\n                              read_bytes         = Convert.ToDouble(table1[\"read_bytes\"]) - Convert.ToDouble(table2[\"read_bytes\"]),\n                              write_latency_ms   = Convert.ToDouble(table1[\"write_latency_ms\"]) - Convert.ToDouble(table2[\"write_latency_ms\"]),\n                              writes             = Convert.ToDouble(table1[\"writes\"]) - Convert.ToDouble(table2[\"writes\"]),\n                              write_bytes        = Convert.ToDouble(table1[\"write_bytes\"]) - Convert.ToDouble(table2[\"write_bytes\"]),\n\n                              cum_read_latency_ms  = Convert.ToDouble(table1[\"read_latency_ms\"]),\n                              cum_reads            = Convert.ToDouble(table1[\"reads\"]),\n                              cum_read_bytes       = Convert.ToDouble(table1[\"read_bytes\"]),\n                              cum_write_latency_ms = Convert.ToDouble(table1[\"write_latency_ms\"]),\n                              cum_writes           = Convert.ToDouble(table1[\"writes\"]),\n                              cum_write_bytes      = Convert.ToDouble(table1[\"write_bytes\"]),\n                          };\n\n            return DataUtils.ToDataTable(results);\n        }\n\n        private DataTable GetDiskPerf()\n        {\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                // Calculate disk performance\n                var sql = @\"\n                    DECLARE\n\t                     @SqlStatement AS nvarchar(max)\n\t                    ,@MajorMinorVersion AS int = CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),4) AS int) * 100 + CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),3) AS int)\n\t                    ,@Columns AS nvarchar(max) = ''\n\t                    ,@Tables AS nvarchar(max) = ''\n\n                    IF CAST(SERVERPROPERTY('ProductVersion') AS varchar(50)) >= '10.50.2500.0' BEGIN\n\t                    SET @Columns += N'\n\t                    ,vs.[volume_mount_point]'\n\t                    SET @Tables += N'\n\t                    CROSS APPLY sys.dm_os_volume_stats(mf.[database_id], mf.[file_id]) AS vs'\n                    END\n\n                    SET @SqlStatement = N'\n                    SELECT\n\t                     DB_NAME(vfs.[database_id]) AS [database_name]\n\t                    ,COALESCE(mf.[physical_name],''RBPEX'') AS [physical_filename]\t--RPBEX = Resilient Buffer Pool Extension\n\t                    ,COALESCE(mf.[name],''RBPEX'') AS [logical_filename]\t--RPBEX = Resilient Buffer Pool Extension\n\t                    ,mf.[type_desc] AS [file_type]\n\t                    ,vfs.[io_stall_read_ms] AS [read_latency_ms]\n\t                    ,vfs.[num_of_reads] AS [reads]\n\t                    ,vfs.[num_of_bytes_read] AS [read_bytes]\n\t                    ,vfs.[io_stall_write_ms] AS [write_latency_ms]\n\t                    ,vfs.[num_of_writes] AS [writes]\n\t                    ,vfs.[num_of_bytes_written] AS [write_bytes]'\n\t                    + @Columns + N'\n                    FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs\n                    INNER JOIN sys.master_files AS mf WITH (NOLOCK)\n\t                    ON vfs.[database_id] = mf.[database_id] AND vfs.[file_id] = mf.[file_id]'\n                    + @Tables + ' OPTION (RECOMPILE)';\n\n                    EXEC sp_executesql @SqlStatement\n                \";\n\n                DataTable diskPerf = null;\n\n                using (var adapter = new SqlDataAdapter(sql, conn))\n                {\n                    using (var ds = new DataSet())\n                    {\n                        _ = adapter.Fill(ds);\n                        diskPerf = ds.Tables[0];\n                    }\n                }\n\n                var results = from table1 in diskPerf.AsEnumerable()\n                              select new\n                              {\n                                  database_name      = Convert.ToString(table1[\"database_name\"]),\n                                  physical_filename  = Convert.ToString(table1[\"physical_filename\"]),\n                                  logical_filename   = Convert.ToString(table1[\"logical_filename\"]),\n                                  file_type          = Convert.ToString(table1[\"file_type\"]),\n                                  volume_mount_point = Convert.ToString(table1[\"volume_mount_point\"]),\n                                  read_latency_ms    = Convert.ToDouble(table1[\"read_latency_ms\"]),\n                                  reads              = Convert.ToDouble(table1[\"reads\"]),\n                                  read_bytes         = Convert.ToDouble(table1[\"read_bytes\"]),\n                                  write_latency_ms   = Convert.ToDouble(table1[\"write_latency_ms\"]),\n                                  writes             = Convert.ToDouble(table1[\"writes\"]),\n                                  write_bytes        = Convert.ToDouble(table1[\"write_bytes\"]),\n                              };\n\n                return DataUtils.ToDataTable(results);\n            }\n        }\n\n        protected virtual void SetTransactionMark(bool allDatabases)\n        {\n\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString();\n                conn.Open();\n                // Create Marked Transaction\n                var sql = @\"\nDECLARE @dbname sysname\nDECLARE @sql nvarchar(max), @qry nvarchar(max)\n\nSET @qry = '\nPRINT DB_NAME()\nBEGIN TRAN WorkloadTools WITH MARK ''WorkloadTools'';\nBEGIN TRY\n\tCREATE TYPE WorkloadToolsType FROM int;\n\tDROP TYPE WorkloadToolsType;\n\tIF XACT_STATE() = 1 \n\t\tCOMMIT TRAN WorkloadTools;\nEND TRY\nBEGIN CATCH\n\tIF XACT_STATE() <> 0 \n\t\tROLLBACK TRAN WorkloadTools;\nEND CATCH\n'\n\n\nDECLARE c CURSOR STATIC LOCAL FORWARD_ONLY READ_ONLY FOR\nSELECT name\nFROM sys.databases \nWHERE database_id > 4\n\" + (allDatabases ? \"\" : \"AND database_id = DB_ID()\") + @\"\nORDER BY name\n\nOPEN c \nFETCH NEXT FROM c INTO @dbname\n\nWHILE @@FETCH_STATUS = 0\nBEGIN\n\n\tSET @sql = 'EXEC ' + QUOTENAME(@dbname) + '.sys.sp_executesql @qry'\n\n\tBEGIN TRY\n\t\tEXEC sp_executesql @sql, N'@qry nvarchar(max)', @qry\n\tEND TRY\n\tBEGIN CATCH\n\t\tPRINT 'Unable to mark the transaction on database ' + @dbname\n\tEND CATCH\n\n\tFETCH NEXT FROM c INTO @dbname\nEND\n\nCLOSE c\nDEALLOCATE c\n                \";\n\n                using (var cmd = new SqlCommand(sql, conn))\n                {\n                    _ = cmd.ExecuteNonQuery();\n                }\n\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadTools/WorkloadTools.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{AE6E4548-8C33-4728-8504-88AA9666020B}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>WorkloadTools</RootNamespace>\n    <AssemblyName>WorkloadTools</AssemblyName>\n    <FileAlignment>512</FileAlignment>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup>\n    <TargetFrameworkIdentifier>.NETFramework</TargetFrameworkIdentifier>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup>\n    <CurrentPlatform>x86</CurrentPlatform>\n    <CurrentPlatform Condition=\"'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'\">x64</CurrentPlatform>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"DouglasCrockford.JsMin, Version=1.1.3.0, Culture=neutral, PublicKeyToken=99147aa1108448b7, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\DouglasCrockford.JsMin.1.1.3\\lib\\net40-client\\DouglasCrockford.JsMin.dll</HintPath>\n    </Reference>\n    <Reference Include=\"FastMember, Version=1.5.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\FastMember.1.5.0\\lib\\net461\\FastMember.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.AzureStorageEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.AzureStorageEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.BatchParserClient, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.BatchParserClient.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.ConnectionInfo, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.ConnectionInfo.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.ConnectionInfoExtended, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.ConnectionInfoExtended.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Diagnostics.Strace, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Diagnostics.Strace.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Dmf, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Dmf.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Dmf.Common, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Dmf.Common.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.Collector, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.Collector.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.CollectorEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.CollectorEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.RegisteredServers, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.RegisteredServers.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.Sdk.Sfc, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.Sdk.Sfc.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.SqlParser, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.SqlParser.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.Utility, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.Utility.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.UtilityEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.UtilityEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.XEvent, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.XEvent.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.XEventDbScoped, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.XEventDbScoped.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.XEventDbScopedEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.XEventDbScopedEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Management.XEventEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Management.XEventEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.PolicyEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.PolicyEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.RegSvrEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.RegSvrEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.ServiceBrokerEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.ServiceBrokerEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Smo, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Smo.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SmoExtended, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SmoExtended.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SqlClrProvider, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SqlClrProvider.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SqlEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SqlEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SqlTDiagm, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SqlTDiagm.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SqlWmiManagement, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SqlWmiManagement.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.SString, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.SString.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Types, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.Types.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.WmiEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\lib\\net40\\Microsoft.SqlServer.WmiEnum.dll</HintPath>\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.Xe.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=AMD64\">\n      <SpecificVersion>False</SpecificVersion>\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\runtimes\\win-$(CurrentPlatform)\\native\\Microsoft.SqlServer.Xe.Core.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Microsoft.SqlServer.XEvent.Linq, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=AMD64\">\n      <SpecificVersion>False</SpecificVersion>\n      <HintPath>..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\runtimes\\win-$(CurrentPlatform)\\native\\Microsoft.SqlServer.XEvent.Linq.dll</HintPath>\n    </Reference>\n    <Reference Include=\"NFX, Version=3.0.0.1, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\NFX.3.5.0.5\\lib\\NFX.dll</HintPath>\n    </Reference>\n    <Reference Include=\"NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\NLog.4.7.15\\lib\\net45\\NLog.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Configuration\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Data.SQLite, Version=1.0.112.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Data.SQLite.Core.1.0.112.0\\lib\\net46\\System.Data.SQLite.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.ServiceModel\" />\n    <Reference Include=\"System.Transactions\" />\n    <Reference Include=\"System.Web.Extensions\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Config\\SqlWorkloadConfig.cs\" />\n    <Compile Include=\"Config\\SqlWorkloadConfigTypeResolver.cs\" />\n    <Compile Include=\"Consumer\\Analysis\\AnalysisConsumer.cs\" />\n    <Compile Include=\"Consumer\\BufferedWorkloadConsumer.cs\" />\n    <Compile Include=\"Consumer\\Analysis\\NormalizedSqlText.cs\" />\n    <Compile Include=\"Consumer\\Analysis\\SqlTextNormalizer.cs\" />\n    <Compile Include=\"Consumer\\Analysis\\WorkloadAnalyzer.cs\" />\n    <Compile Include=\"Consumer\\Replay\\ReplayCommand.cs\" />\n    <Compile Include=\"Consumer\\Replay\\ReplayWorker.cs\" />\n    <Compile Include=\"Consumer\\Replay\\ResultSetConsumer.cs\" />\n    <Compile Include=\"Consumer\\WorkloadFile\\WorkloadFileWriterConsumer.cs\" />\n    <Compile Include=\"CounterWorkloadEvent.cs\" />\n    <Compile Include=\"BufferedEventQueue.cs\" />\n    <Compile Include=\"ErrorWorkloadEvent.cs\" />\n    <Compile Include=\"GlobalSuppressions.cs\" />\n    <Compile Include=\"Listener\\ReadIteration.cs\" />\n    <Compile Include=\"Listener\\Trace\\FileTraceEventDataReader.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceEventDataReader.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceEventParser.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceUtils.cs\" />\n    <Compile Include=\"MessagWorkloadEvent.cs\" />\n    <Compile Include=\"MMFEventQueue.cs\" />\n    <Compile Include=\"ExecutionWorkloadEvent.cs\" />\n    <Compile Include=\"FilterPredicate.cs\" />\n    <Compile Include=\"IEventQueue.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\ExtendedEventsWorkloadListener.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\ExtendedEventsEventFilter.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\ExtendedEventsFilterPredicate.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\FileTargetXEventDataReader.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\StreamXEventDataReader.cs\" />\n    <Compile Include=\"Listener\\ExtendedEvents\\XEventDataReader.cs\" />\n    <Compile Include=\"Listener\\File\\FileEventFilter.cs\" />\n    <Compile Include=\"Listener\\File\\FileFilterPredicate.cs\" />\n    <Compile Include=\"Listener\\File\\FileWorkloadListener.cs\" />\n    <Compile Include=\"Listener\\Trace\\ProfilerWorkloadListener.cs\" />\n    <Compile Include=\"Consumer\\Replay\\ReplayConsumer.cs\" />\n    <Compile Include=\"Listener\\Trace\\SqlTraceWorkloadListener.cs\" />\n    <Compile Include=\"Listener\\SqlTransformer.cs\" />\n    <Compile Include=\"Listener\\Trace\\ProfilerEventFilter.cs\" />\n    <Compile Include=\"Listener\\Trace\\ProfilerFilterPredicate.cs\" />\n    <Compile Include=\"Listener\\Trace\\SqlConnectionInfoWrapper.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceEventFilter.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceFileWrapper.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceFilterPredicate.cs\" />\n    <Compile Include=\"Listener\\Trace\\TraceServerWrapper.cs\" />\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n      <DependentUpon>Settings.settings</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\SharedAssemblyInfo.cs\" />\n    <Compile Include=\"BinarySerializedBufferedEventQueue.cs\" />\n    <Compile Include=\"SqlConnectionInfo.cs\" />\n    <Compile Include=\"SqliteEventQueue.cs\" />\n    <Compile Include=\"Util\\DataUtils.cs\" />\n    <Compile Include=\"Util\\ModelConverter.cs\" />\n    <Compile Include=\"Util\\RingBuffer.cs\" />\n    <Compile Include=\"Util\\StringExtensions.cs\" />\n    <Compile Include=\"DiskPerfWorkloadEvent.cs\" />\n    <Compile Include=\"WaitStatsWorkloadEvent.cs\" />\n    <Compile Include=\"Consumer\\WorkloadConsumer.cs\" />\n    <Compile Include=\"WorkloadController.cs\" />\n    <Compile Include=\"WorkloadEvent.cs\" />\n    <Compile Include=\"WorkloadEventFilter.cs\" />\n    <Compile Include=\"WorkloadListener.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"app.config\" />\n    <None Include=\"Config\\ReplaySample.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"Config\\AnalysisSample.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"Config\\Sample.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"Listener\\Trace\\sqlworkload.tdf\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Listener\\ExtendedEvents\\sqlworkload.sql\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"Listener\\Trace\\sqlworkload.sql\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Consumer\\Analysis\\createAnalysisView.sql\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Consumer\\Analysis\\DatabaseSchema.sql\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\build\\net40\\Microsoft.SqlServer.SqlManagementObjects.targets\" Condition=\"Exists('..\\packages\\Microsoft.SqlServer.SqlManagementObjects.140.17279.0\\build\\net40\\Microsoft.SqlServer.SqlManagementObjects.targets')\" />\n  <Import Project=\"..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets\" Condition=\"Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\System.Data.SQLite.Core.1.0.112.0\\build\\net46\\System.Data.SQLite.Core.targets'))\" />\n  </Target>\n</Project>"
  },
  {
    "path": "WorkloadTools/app.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <configSections>\n        <sectionGroup name=\"applicationSettings\" type=\"System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\">\n            <section name=\"WorkloadTools.Properties.Settings\" type=\"System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" requirePermission=\"false\"/>\n        </sectionGroup>\n    </configSections>\n    <applicationSettings>\n        <WorkloadTools.Properties.Settings>\n            <setting name=\"ReplayConsumer_SEMAPHORE_LIMIT\" serializeAs=\"String\">\n                <value>64</value>\n            </setting>\n            <setting name=\"ReplayConsumer_WORKER_EXPIRY_TIMEOUT_SECONDS\" serializeAs=\"String\">\n                <value>15</value>\n            </setting>\n        </WorkloadTools.Properties.Settings>\n    </applicationSettings>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"Microsoft.SqlServer.XE.Core\" publicKeyToken=\"89845dcd8080cc91\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-14.100.0.0\" newVersion=\"14.100.0.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n<startup><supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/></startup></configuration>\n"
  },
  {
    "path": "WorkloadTools/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"DouglasCrockford.JsMin\" version=\"1.1.3\" targetFramework=\"net40\" />\n  <package id=\"FastMember\" version=\"1.5.0\" targetFramework=\"net461\" />\n  <package id=\"Microsoft.SqlServer.SqlManagementObjects\" version=\"140.17279.0\" targetFramework=\"net40\" />\n  <package id=\"NFX\" version=\"3.5.0.5\" targetFramework=\"net40\" />\n  <package id=\"NLog\" version=\"4.7.15\" targetFramework=\"net48\" />\n  <package id=\"System.Data.SQLite.Core\" version=\"1.0.112.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "WorkloadTools.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.6.33829.357\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"SqlWorkload\", \"SqlWorkload\\SqlWorkload.csproj\", \"{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B} = {AE6E4548-8C33-4728-8504-88AA9666020B}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"WorkloadTools\", \"WorkloadTools\\WorkloadTools.csproj\", \"{AE6E4548-8C33-4728-8504-88AA9666020B}\"\nEndProject\nProject(\"{930C7802-8A8C-48F9-8165-68863BCCD9DD}\") = \"Setup\", \"Setup\\Setup.wixproj\", \"{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A} = {FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B} = {62E37C03-BA08-46CE-A583-D71FB7A8825B}\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973} = {6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B} = {AE6E4548-8C33-4728-8504-88AA9666020B}\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E} = {898DF47E-429A-441C-B879-AC0D9EC7FA0E}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{CB5A2D19-E789-4555-B04D-FEA2908A5C92}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.editorconfig = .editorconfig\n\t\tbuild.ps1 = build.ps1\n\t\tLICENSE.md = LICENSE.md\n\t\tREADME.md = README.md\n\t\tSharedAssemblyInfo.cs = SharedAssemblyInfo.cs\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ConvertWorkload\", \"ConvertWorkload\\ConvertWorkload.csproj\", \"{62E37C03-BA08-46CE-A583-D71FB7A8825B}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"WorkloadToolsTests\", \"WorkloadToolsTests\\WorkloadToolsTests.csproj\", \"{898DF47E-429A-441C-B879-AC0D9EC7FA0E}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"WorkloadViewer\", \"WorkloadViewer\\WorkloadViewer.csproj\", \"{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B} = {AE6E4548-8C33-4728-8504-88AA9666020B}\n\tEndProjectSection\nEndProject\nProject(\"{930C7802-8A8C-48F9-8165-68863BCCD9DD}\") = \"SetupBootstrapper\", \"SetupBootstrapper\\SetupBootstrapper.wixproj\", \"{CAD976C4-D0C6-4313-B605-EC3749A23B5F}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Debug|x86.Build.0 = Debug|x86\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|x64.Build.0 = Release|Any CPU\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|x86.ActiveCfg = Release|x86\n\t\t{FB46AD2C-DF81-4D35-B419-D93E5EF9D98A}.Release|x86.Build.0 = Release|x86\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Debug|x86.Build.0 = Debug|x86\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|x86.ActiveCfg = Release|x86\n\t\t{AE6E4548-8C33-4728-8504-88AA9666020B}.Release|x86.Build.0 = Release|x86\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Debug|Any CPU.ActiveCfg = Debug|x64\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Release|Any CPU.ActiveCfg = Release|x86\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Release|x64.ActiveCfg = Release|x64\n\t\t{BBF5FDA0-C08F-48C9-9B98-E017DD8ABB5D}.Release|x86.ActiveCfg = Release|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|Any CPU.ActiveCfg = Debug|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|Any CPU.Build.0 = Debug|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Debug|x86.Build.0 = Debug|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|x86.ActiveCfg = Release|x86\n\t\t{62E37C03-BA08-46CE-A583-D71FB7A8825B}.Release|x86.Build.0 = Release|x86\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Debug|x86.Build.0 = Debug|x86\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|x64.Build.0 = Release|Any CPU\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|x86.ActiveCfg = Release|x86\n\t\t{898DF47E-429A-441C-B879-AC0D9EC7FA0E}.Release|x86.Build.0 = Release|x86\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Debug|x86.Build.0 = Debug|x86\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|x64.Build.0 = Release|Any CPU\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|x86.ActiveCfg = Release|x86\n\t\t{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}.Release|x86.Build.0 = Release|x86\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Debug|Any CPU.ActiveCfg = Debug|x86\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Release|Any CPU.ActiveCfg = Release|x86\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Release|x64.ActiveCfg = Release|x64\n\t\t{CAD976C4-D0C6-4313-B605-EC3749A23B5F}.Release|x86.ActiveCfg = Release|x86\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {3DC1D965-A979-4B9F-A16F-7A938A0DDD8C}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "WorkloadToolsTests/Properties/AssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n\n[assembly: Guid(\"898df47e-429a-441c-b879-ac0d9ec7fa0e\")]\n\n"
  },
  {
    "path": "WorkloadToolsTests/WorkloadTools/BinarySerializedBufferedEventQueueTest.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing WorkloadTools;\n\nnamespace WorkloadToolsTests.WorkloadTools\n{\n    [TestClass]\n    public class BinarySerializedBufferedEventQueueTest\n    {\n        [TestMethod]\n        public void TestEnqueueDequeueFixedList()\n        {\n            int[] numbers = {\n                  +9772\n                , -9479\n                , +70255\n                , -49216\n                , +18796\n                , -39641\n                , +60528\n                , -60690\n                , +78808\n                , -49406\n                , +42422\n                , -72132\n                , +65861\n                , -34935\n                , +55297\n                , -10699\n                , +96237\n                , -72432\n                , +55697\n                , -85962\n                , +18370\n                , -72056\n                , +97085\n                , -50146\n                , +43353\n                , -53808\n                , +28408\n                , -76107\n                , +51235\n                , -50290\n                , +67421\n                , -9696\n                , +65303\n                , -45014\n                , +53121\n                , -50691\n                , +68663\n                , -54973\n                , +34989\n                , -66099\n                , +15014\n                , -53872\n                , +97248\n                , -38096\n                , +705\n                , -23998\n                , +13872\n                , -42048\n                , +77390\n                , -71767\n                , +86413\n                , -6260\n                , +61030\n                , -51330\n                , +14412\n                , -37716\n                , +16394\n                , -20109\n                , +5862\n                , -64988\n                , +67733\n                , -84421\n                , +23954\n                , -3518\n                , +81985\n                , -32726\n                , +14828\n                , -20847\n                , +81813\n                , -4605\n                , +42036\n                , -41263\n                , +37442\n                , -89598\n                , +70947\n                , -64497\n                , +74808\n                , -58988\n                , +49441\n                , -19355\n                , -166474 };\n\n            using (var queue = new BinarySerializedBufferedEventQueue())\n            {\n                queue.BufferSize = 10000;\n                var total = 0;\n                for (var i = 0; i < numbers.Length; i++)\n                {\n                    if (i == 28)\n                    {\n                        Debug.WriteLine(\"Uh oh\");\n                    }\n                    var num = numbers[i];\n                    if (num > 0)\n                    {\n                        var initialCount = queue.Count;\n                        for (var j = 0; j < num; j++)\n                        {\n                            queue.Enqueue(new ExecutionWorkloadEvent() { Text = $\"SELECT {j} FROM sometable WHERE somecolumn = someValue ORDER BY someOtherColumn\" });\n                            if (i == 28)\n                            {\n                                Console.WriteLine($\" {j}: should be {initialCount + j + 1}  | is {queue.Count}\");\n                                if (initialCount + j + 1 == 8854)\n                                {\n                                    Console.WriteLine($\"Aaaaah!\");\n                                }\n                            }\n                        }\n                    }\n                    else\n                    {\n                        var initialCount = queue.Count;\n                        num = num * -1;\n                        WorkloadEvent evnt = null;\n                        for (var k = 0; k < num; k++)\n                        {\n                            queue.TryDequeue(out evnt);\n                            //Console.WriteLine($\" {k}: should be {initialCount - k}  | is {queue.Count}\");\n                        }\n                        num = num * -1;\n                    }\n                    total += num;\n                    Assert.AreEqual(queue.Count, total);\n                }\n            }\n\n        }\n\n\n\n\n        [TestMethod]\n        public void TestEnqueueRandomDequeueAll()\n        {\n\n            using (var queue = new BinarySerializedBufferedEventQueue())\n            {\n                queue.BufferSize = 10000;\n                var r = new Random();\n                var watch = new Stopwatch();\n\n                for (var j = 0; j < 10; j++)\n                {\n                    watch.Reset();\n                    watch.Start();\n\n\n                    var numElements = (int)(r.NextDouble() * 100000);\n\n                    for (var i = 0; i < numElements; i++)\n                    {\n                        queue.Enqueue(new ExecutionWorkloadEvent()\n                        {\n                            Text = $\"SELECT {i} FROM sometable WHERE somecolumn = someValue ORDER BY someOtherColumn\"\n                        });\n                    }\n\n                    watch.Stop();\n                    Console.WriteLine($\"Enqueue {numElements} elements elapsed: {watch.Elapsed}\");\n\n\n                    var queueLen = queue.Count;\n\n                    numElements = (int)(r.NextDouble() * 100000);\n\n                    while (numElements > queueLen)\n                    {\n                        numElements -= 500;\n                    }\n\n                    watch.Reset();\n                    watch.Start();\n\n                    for (var i = 0; i < numElements; i++)\n                    {\n                        WorkloadEvent evt = null;\n                        queue.TryDequeue(out evt);\n                        //Console.WriteLine(((ExecutionWorkloadEvent)evt).Text);\n                    }\n\n                    watch.Stop();\n                    Console.WriteLine($\"Dequeue {numElements} elements elapsed: {watch.Elapsed}\");\n                }\n\n                watch.Reset();\n                watch.Start();\n\n                var len = queue.Count;\n                WorkloadEvent evnt = null;\n                while (queue.TryDequeue(out evnt))\n                {\n                    ;\n                }\n\n                watch.Stop();\n                Console.WriteLine($\"Dequeue all {len} remaining elements elapsed: {watch.Elapsed}\");\n\n                Assert.AreEqual(queue.Count, 0);\n            }\n\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadToolsTests/WorkloadTools/SqlTextNormalizerTest.cs",
    "content": "using Microsoft.VisualStudio.TestTools.UnitTesting;\nusing WorkloadTools.Consumer.Analysis;\n\nnamespace WorkloadToolsTests.WorkloadTools\n{\n    [TestClass]\n    public class SqlTextNormalizerTest\n    {\n        private SqlTextNormalizer _normalizer;\n\n        [TestInitialize]\n        public void Initialize()\n        {\n            _normalizer = new SqlTextNormalizer();\n        }\n\n        [TestMethod]\n        public void NormalizeSqlText_SimpleStringParam_Normalized()\n        {\n            var sql = \"exec SampleStoredProcedure @Param1=N'Name1',@Key=N'123456'\";\n            var result = _normalizer.NormalizeSqlText(sql, 1, false);\n            Assert.IsNotNull(result);\n            StringAssert.Contains(result.NormalizedText, \"@PARAM1 = {STR}\");\n            StringAssert.Contains(result.NormalizedText, \"@KEY = {STR}\");\n        }\n\n        [TestMethod]\n        public void NormalizeSqlText_StringParamWithDoubleQuotes_Normalized()\n        {\n            var sql = \"exec SampleStoredProcedure @Param1=N'Name1',@Key=N'123456',@Content=N'<ROOT Attribute1=\\\"value\\\" xmlns=\\\"http://namespace.com\\\">value</ROOT>'\";\n            var result = _normalizer.NormalizeSqlText(sql, 1, false);\n            Assert.IsNotNull(result);\n            StringAssert.Contains(result.NormalizedText, \"@PARAM1 = {STR}\");\n            StringAssert.Contains(result.NormalizedText, \"@KEY = {STR}\");\n            StringAssert.Contains(result.NormalizedText, \"@CONTENT = {STR}\");\n            Assert.IsFalse(result.NormalizedText.Contains(\"ATTRIBUTE1\"), \"The XML attribute content should have been replaced by {STR}\");\n        }\n\n        [TestMethod]\n        public void NormalizeSqlText_StringParamWithEscapedSingleQuotes_Normalized()\n        {\n            var sql = \"exec SampleStoredProcedure @Param1=N'It''s a test'\";\n            var result = _normalizer.NormalizeSqlText(sql, 1, false);\n            Assert.IsNotNull(result);\n            StringAssert.Contains(result.NormalizedText, \"@PARAM1 = {STR}\");\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadToolsTests/WorkloadToolsTests.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\MSTest.TestAdapter.1.3.2\\build\\net45\\MSTest.TestAdapter.props\" Condition=\"Exists('..\\packages\\MSTest.TestAdapter.1.3.2\\build\\net45\\MSTest.TestAdapter.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{898DF47E-429A-441C-B879-AC0D9EC7FA0E}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>WorkloadToolsTests</RootNamespace>\n    <AssemblyName>WorkloadToolsTests</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <VisualStudioVersion Condition=\"'$(VisualStudioVersion)' == ''\">15.0</VisualStudioVersion>\n    <VSToolsPath Condition=\"'$(VSToolsPath)' == ''\">$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)</VSToolsPath>\n    <ReferencePath>$(ProgramFiles)\\Common Files\\microsoft shared\\VSTT\\$(VisualStudioVersion)\\UITestExtensionPackages</ReferencePath>\n    <IsCodedUITest>False</IsCodedUITest>\n    <TestProjectType>UnitTest</TestProjectType>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MSTest.TestFramework.1.3.2\\lib\\net45\\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MSTest.TestFramework.1.3.2\\lib\\net45\\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"..\\SharedAssemblyInfo.cs\">\n      <Link>Properties\\SharedAssemblyInfo.cs</Link>\n    </Compile>\n    <Compile Include=\"WorkloadTools\\BinarySerializedBufferedEventQueueTest.cs\" />\n    <Compile Include=\"WorkloadTools\\SqlTextNormalizerTest.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"app.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ConvertWorkload\\ConvertWorkload.csproj\">\n      <Project>{62e37c03-ba08-46ce-a583-d71fb7a8825b}</Project>\n      <Name>ConvertWorkload</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\SqlWorkload\\SqlWorkload.csproj\">\n      <Project>{fb46ad2c-df81-4d35-b419-d93e5ef9d98a}</Project>\n      <Name>SqlWorkload</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\WorkloadTools\\WorkloadTools.csproj\">\n      <Project>{ae6e4548-8c33-4728-8504-88aa9666020b}</Project>\n      <Name>WorkloadTools</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(VSToolsPath)\\TeamTest\\Microsoft.TestTools.targets\" Condition=\"Exists('$(VSToolsPath)\\TeamTest\\Microsoft.TestTools.targets')\" />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\MSTest.TestAdapter.1.3.2\\build\\net45\\MSTest.TestAdapter.targets\" Condition=\"Exists('..\\packages\\MSTest.TestAdapter.1.3.2\\build\\net45\\MSTest.TestAdapter.targets')\" />\n</Project>"
  },
  {
    "path": "WorkloadToolsTests/app.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"Microsoft.SqlServer.XE.Core\" publicKeyToken=\"89845dcd8080cc91\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-14.100.0.0\" newVersion=\"14.100.0.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n<startup><supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/></startup></configuration>\n"
  },
  {
    "path": "WorkloadToolsTests/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"MSTest.TestAdapter\" version=\"1.3.2\" targetFramework=\"net461\" />\n  <package id=\"MSTest.TestFramework\" version=\"1.3.2\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "WorkloadViewer/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/>\n    </startup>\n</configuration>\n"
  },
  {
    "path": "WorkloadViewer/App.xaml",
    "content": "<Application x:Class=\"WorkloadViewer.App\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:local=\"clr-namespace:WorkloadViewer\" StartupUri=\"View/MainWindow.xaml\" xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" d1p1:Ignorable=\"d\" xmlns:d1p1=\"http://schemas.openxmlformats.org/markup-compatibility/2006\">\n  <Application.Resources>\n    <ResourceDictionary>\n      <vm:ViewModelLocator x:Key=\"Locator\" d:IsDataSource=\"True\" xmlns:vm=\"clr-namespace:WorkloadViewer.ViewModel\" />\n      <ResourceDictionary.MergedDictionaries>\n        <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->\n        <ResourceDictionary Source=\"pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml\" />\n        <ResourceDictionary Source=\"pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml\" />\n        <ResourceDictionary Source=\"pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml\" />\n        <!-- Accent and AppTheme setting -->\n        <ResourceDictionary Source=\"pack://application:,,,/MahApps.Metro;component/Styles/Accents/Steel.xaml\" />\n        <ResourceDictionary Source=\"pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml\" />\n        <ResourceDictionary Source=\".\\View\\ConnectionInfoDialogStyle.xaml\" />\n      </ResourceDictionary.MergedDictionaries>\n    </ResourceDictionary>\n  </Application.Resources>\n</Application>"
  },
  {
    "path": "WorkloadViewer/App.xaml.cs",
    "content": "﻿using CommandLine;\nusing CommandLine.Text;\nusing NLog;\nusing NLog.Targets;\nusing System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace WorkloadViewer\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        private static readonly Logger logger = LogManager.GetCurrentClassLogger();\n\n        public Options Options { get; private set; }\n\n        protected override void OnStartup(StartupEventArgs e)\n        {\n            base.OnStartup(e);\n\n            Options = new Options();\n            var optionsAreGood = CommandLine.Parser.Default.ParseArguments(e.Args, Options);\n\n            if (!optionsAreGood)\n            {\n                _ = MessageBox.Show(Options.GetUsage());\n                Shutdown();\n            }\n\n            // reconfigure loggers to use a file in the current directory\n            // or the file specified by the \"Log\" commandline parameter\n            if (LogManager.Configuration != null)\n            {\n                var target = (FileTarget)LogManager.Configuration.FindTargetByName(\"logfile\");\n                if (target != null)\n                {\n                    var pathToLog = Options.LogFile ?? Path.Combine(Environment.CurrentDirectory, \"WorkloadViewer.log\");\n                    if (!Path.IsPathRooted(pathToLog))\n                    {\n                        pathToLog = Path.Combine(Environment.CurrentDirectory, pathToLog);\n                    }\n                    Console.WriteLine($\"Writing logs to {pathToLog}\");\n                    target.FileName = pathToLog;\n                    LogManager.ReconfigExistingLoggers();\n                }\n                else\n                {\n                    Console.WriteLine($\"No file targets configured\");\n                }\n            }\n            else\n            {\n                Console.WriteLine($\"NLog not configured\");\n            }\n            logger.Info(\"Starting application\");\n        }\n\n    }\n\n    public class Options\n    {\n        [Option('F', \"File\", HelpText = \"Configuration file\")]\n        public string ConfigurationFile { get; set; }\n\n        [Option('L', \"Log\", HelpText = \"Log File\")]\n        public string LogFile { get; set; }\n\n        [Option('S', \"BaselineServer\", HelpText = \"Baseline Server\")]\n        public string BaselineServer { get; set; }\n\n        [Option('D', \"BaselineDatabase\", HelpText = \"Baseline Database\")]\n        public string BaselineDatabase { get; set; }\n\n        [Option('M', \"BaselineSchema\", HelpText = \"Baseline Schema\")]\n        public string BaselineSchema { get; set; }\n\n        [Option('U', \"BaselineUsername\", HelpText = \"Baseline Username\")]\n        public string BaselineUsername { get; set; }\n\n        [Option('P', \"BaselinePassword\", HelpText = \"Baseline Password\")]\n        public string BaselinePassword { get; set; }\n\n        [Option('T', \"BenchmarkServer\", HelpText = \"Benchmark Server\")]\n        public string BenchmarkServer { get; set; }\n\n        [Option('E', \"BenchmarkDatabase\", HelpText = \"Benchmark Database\")]\n        public string BenchmarkDatabase { get; set; }\n\n        [Option('N', \"BenchmarkSchema\", HelpText = \"Benchmark Schema\")]\n        public string BenchmarkSchema { get; set; }\n\n        [Option('V', \"BenchmarkUsername\", HelpText = \"Benchmark Username\")]\n        public string BenchmarkUsername { get; set; }\n\n        [Option('Q', \"BenchmarkPassword\", HelpText = \"Benchmark Password\")]\n        public string BenchmarkPassword { get; set; }\n\n        [ParserState]\n        public IParserState LastParserState { get; set; }\n\n        [HelpOption]\n        public string GetUsage()\n        {\n            return HelpText.AutoBuild(this,\n              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Comparer/QueryResultEqualityComparer.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing WorkloadViewer.ViewModel;\n\npublic class QueryResultEqualityComparer : IEqualityComparer<QueryResult>\n{\n    public bool Equals(QueryResult x, QueryResult y)\n    {\n        return x?.query_hash == y?.query_hash;\n    }\n\n    public int GetHashCode(QueryResult obj)\n    {\n        return obj?.query_hash.GetHashCode() ?? 0;\n    }\n}"
  },
  {
    "path": "WorkloadViewer/Model/NormalizedQuery.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.Model\n{\n    public class NormalizedQuery\n    {\n        public long Hash { get; set; }\n        public string NormalizedText { get; set; }\n        public string ExampleText { get; set; }\n    }\n}\n\n"
  },
  {
    "path": "WorkloadViewer/Model/QueryDetails.cs",
    "content": "﻿using OxyPlot;\nusing OxyPlot.Axes;\nusing OxyPlot.Series;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\n\nnamespace WorkloadViewer.Model\n{\n    public class QueryDetails\n    {\n        public NormalizedQuery Query { get; private set; }\n        private WorkloadAnalysis Benchmark { get; set; }\n        private WorkloadAnalysis Baseline { get; set; }\n\n        public QueryDetails(NormalizedQuery query, WorkloadAnalysis baseline, WorkloadAnalysis benchmark)\n        {\n            Query = query;\n            Baseline = baseline;\n            Benchmark = benchmark;\n        }\n\n        public DataTable QueryStats\n        {\n            get\n            {\n                return LoadQueryStats();\n            }\n        }\n\n        public PlotModel DetailPlotModel\n        {\n            get\n            {\n                return LoadPlotModel();\n            }\n        }\n\n\n\n        private DataTable LoadQueryStats()\n        {\n            var result = new DataTable();\n            result.Columns.Add(new DataColumn(\"Application\", typeof(String)));\n            result.Columns.Add(new DataColumn(\"Database\", typeof(String)));\n            result.Columns.Add(new DataColumn(\"Host\", typeof(String)));\n            result.Columns.Add(new DataColumn(\"Login\", typeof(String)));\n            result.Columns.Add(new DataColumn(\"avg_duration_us\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_duration_us2\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_cpu_us\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_cpu_us2\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_reads\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_reads2\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_writes\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"avg_writes2\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"execution_count\", typeof(Int64)));\n            result.Columns.Add(new DataColumn(\"execution_count2\", typeof(Int64)));\n\n            var baseline = from t in Baseline.Points\n                           where t.NormalizedQuery.Hash == Query.Hash\n                           group t by new\n                           {\n                               t.ApplicationName,\n                               t.DatabaseName,\n                               t.HostName,\n                               t.LoginName\n                           }\n                           into grp\n                           select new\n                           {\n                               grp.Key.ApplicationName,\n                               grp.Key.DatabaseName,\n                               grp.Key.HostName,\n                               grp.Key.LoginName,\n                               avg_duration_us = grp.Average(t => t.AvgDurationUs),\n                               avg_cpu_us = grp.Average(t => t.AvgCpuUs),\n                               avg_reads = grp.Average(t => t.AvgReads),\n                               avg_writes = grp.Average(t => t.AvgWrites),\n                               execution_count = grp.Sum(t => t.ExecutionCount)\n                           };\n\n            var benchmark = from t in baseline where false select new { t.ApplicationName, t.DatabaseName, t.HostName, t.LoginName, t.avg_duration_us, t.avg_cpu_us, t.avg_reads, t.avg_writes, t.execution_count };\n\n            if (Benchmark != null)\n            {\n                benchmark = from t in Benchmark.Points\n                            where t.NormalizedQuery.Hash == Query.Hash\n                            group t by new\n                            {\n                                t.ApplicationName,\n                                t.DatabaseName,\n                                t.HostName,\n                                t.LoginName\n                            }\n                            into grp\n                            select new\n                            {\n                                grp.Key.ApplicationName,\n                                grp.Key.DatabaseName,\n                                grp.Key.HostName,\n                                grp.Key.LoginName,\n                                avg_duration_us = grp.Average(t => t.AvgDurationUs),\n                                avg_cpu_us = grp.Average(t => t.AvgCpuUs),\n                                avg_reads = grp.Average(t => t.AvgReads),\n                                avg_writes = grp.Average(t => t.AvgWrites),\n                                execution_count = grp.Sum(t => t.ExecutionCount)\n                            };\n            }\n\n            foreach (var itm in baseline)\n            {\n                var newRow = result.Rows.Add();\n                newRow[\"Application\"] = itm.ApplicationName;\n                newRow[\"Database\"] = itm.DatabaseName;\n                newRow[\"Host\"] = itm.HostName;\n                newRow[\"Login\"] = itm.LoginName;\n                newRow[\"avg_duration_us\"] = itm.avg_duration_us;\n                newRow[\"avg_cpu_us\"] = itm.avg_cpu_us;\n                newRow[\"avg_reads\"] = itm.avg_reads;\n                newRow[\"avg_writes\"] = itm.avg_reads;\n                newRow[\"execution_count\"] = itm.execution_count;\n\n                if (Benchmark != null)\n                {\n                    var _itm = from t in benchmark\n                               where t.ApplicationName == itm.ApplicationName\n                                  && t.DatabaseName == itm.DatabaseName\n                                  && t.HostName == itm.HostName\n                                  && t.LoginName == itm.LoginName\n                               select new { t.avg_cpu_us, t.avg_duration_us, t.avg_reads, t.avg_writes, t.execution_count };\n\n                    var itm2 = _itm.ToList();\n\n                    if(itm2.Count > 0)\n                    {\n                        newRow[\"avg_duration_us2\"] = itm2[0].avg_duration_us;\n                        newRow[\"avg_cpu_us2\"] = itm2[0].avg_cpu_us;\n                        newRow[\"avg_reads2\"] = itm2[0].avg_reads;\n                        newRow[\"avg_writes2\"] = itm2[0].avg_reads;\n                        newRow[\"execution_count2\"] = itm2[0].execution_count;\n                    }\n                    else\n                    {\n                        newRow[\"avg_duration_us2\"] = 0;\n                        newRow[\"avg_cpu_us2\"] = 0;\n                        newRow[\"avg_reads2\"] = 0;\n                        newRow[\"avg_writes2\"] = 0;\n                        newRow[\"execution_count2\"] = 0;\n                    }\n                }\n\n            }\n\n            foreach (var itm in benchmark)\n            {\n                var res = from row in result.AsEnumerable()\n                          where row.Field<string>(\"Application\") == itm.ApplicationName\n                             && row.Field<string>(\"Database\") == itm.DatabaseName\n                             && row.Field<string>(\"Host\") == itm.HostName\n                             && row.Field<string>(\"Login\") == itm.LoginName\n                          select row;\n\n                if (res.Count() == 0)\n                {\n                    var newRow = result.Rows.Add();\n                    newRow[\"Application\"] = itm.ApplicationName;\n                    newRow[\"Database\"] = itm.DatabaseName;\n                    newRow[\"Host\"] = itm.HostName;\n                    newRow[\"Login\"] = itm.LoginName;\n                    newRow[\"avg_duration_us2\"] = itm.avg_duration_us;\n                    newRow[\"avg_cpu_us2\"] = itm.avg_cpu_us;\n                    newRow[\"avg_reads2\"] = itm.avg_reads;\n                    newRow[\"avg_writes2\"] = itm.avg_reads;\n                    newRow[\"execution_count2\"] = itm.execution_count;\n\n                    var _itm = from t in baseline\n                                where t.ApplicationName == itm.ApplicationName\n                                    && t.DatabaseName == itm.DatabaseName\n                                    && t.HostName == itm.HostName\n                                    && t.LoginName == itm.LoginName\n                                select new { t.avg_cpu_us, t.avg_duration_us, t.avg_reads, t.avg_writes, t.execution_count };\n\n                    var itm2 = _itm.ToList();\n\n                    if (itm2.Count > 0)\n                    {\n                        newRow[\"avg_duration_us\"] = itm2[0].avg_duration_us;\n                        newRow[\"avg_cpu_us\"] = itm2[0].avg_cpu_us;\n                        newRow[\"avg_reads\"] = itm2[0].avg_reads;\n                        newRow[\"avg_writes\"] = itm2[0].avg_reads;\n                        newRow[\"execution_count\"] = itm2[0].execution_count;\n                    }\n                    else\n                    {\n                        newRow[\"avg_duration_us\"] = 0;\n                        newRow[\"avg_cpu_us\"] = 0;\n                        newRow[\"avg_reads\"] = 0;\n                        newRow[\"avg_writes\"] = 0;\n                        newRow[\"execution_count\"] = 0;\n                    }\n                    \n                }\n            }\n\n            return result;\n            \n        }\n\n        private PlotModel LoadPlotModel()\n        {\n            var plotModel = new PlotModel();\n            plotModel.LegendOrientation = LegendOrientation.Horizontal;\n            plotModel.LegendPlacement = LegendPlacement.Inside;\n            plotModel.LegendPosition = LegendPosition.TopLeft;\n            plotModel.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);\n            plotModel.Title = \"Average Duration\";\n\n            var offsetAxis = new LinearAxis()\n            {\n                MajorGridlineStyle = LineStyle.Dot,\n                MinorGridlineStyle = LineStyle.None,\n                Position = AxisPosition.Bottom,\n                Title = \"Offset minutes\",\n                AbsoluteMinimum = 0,\n                MinorTickSize = 0\n            };\n            plotModel.Axes.Add(offsetAxis);\n            var valueAxis1 = new LinearAxis()\n            {\n                MajorGridlineStyle = LineStyle.Dot,\n                MinorGridlineStyle = LineStyle.None,\n                Position = AxisPosition.Left,\n                StringFormat = \"N0\",\n                IsZoomEnabled = false,\n                AbsoluteMinimum = 0,\n                MaximumPadding = 0.2,\n                MinorTickSize = 0,\n                Title = \"Duration (us)\"\n            };\n            plotModel.Axes.Add(valueAxis1);\n\n            plotModel.PlotMargins = new OxyThickness(70, 0, 0, 30);\n            plotModel.Series.Clear();\n\n            plotModel.Series.Add(LoadDurationSeries(Baseline, OxyColor.Parse(\"#01B8AA\")));\n\n            if(Benchmark != null)\n            {\n                plotModel.Series.Add(LoadDurationSeries(Benchmark, OxyColor.Parse(\"#000000\")));\n            }\n            return plotModel;\n        }\n\n\n        private Series LoadDurationSeries(WorkloadAnalysis analysis, OxyColor color)\n        {\n            var durationSeries = new LineSeries()\n            {\n                StrokeThickness = 2,\n                MarkerSize = 3,\n                MarkerStroke = OxyColor.Parse(\"#FF0000\"), //Red\n                MarkerType = MarkerType.None,\n                CanTrackerInterpolatePoints = false,\n                TrackerFormatString = \"Offset: {2:0}\\n{0}: {4:0}\",\n                Title = analysis.Name,\n                Color = color,\n                Smooth = false\n            };\n\n            var Table = from t in analysis.Points\n                        where t.NormalizedQuery.Hash == Query.Hash\n                        group t by new\n                        {\n                            offset = t.OffsetMinutes\n                        }\n                        into grp\n                        orderby grp.Key.offset\n                        select new\n                        {\n                            offset_minutes = grp.Key.offset,\n                            duration = grp.Average(t => t.AvgDurationUs)\n                        };\n\n            foreach (var p in Table)\n            {\n                durationSeries.Points.Add(new DataPoint(p.offset_minutes, p.duration));\n            }\n\n            return durationSeries;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Model/SqlConnectionInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.Model\n{\n    public class SqlConnectionInfo\n    {\n        public string ServerName { get; set; }\n        public string DatabaseName { get; set; } = \"master\";\n        public string SchemaName { get; set; } = \"dbo\";\n        public bool UseIntegratedSecurity { get; set; }\n        public string UserName { get; set; }\n        public string Password { get; set; }\n        public bool Encrypt { get; set; } = false;\n        public bool TrustServerCertificate { get; set; } = false;\n        public string ApplicationName { get; set; } = \"WorkloadAnalyzer\";\n\n        public string ConnectionString\n        {\n            get\n            {\n                var connectionString = \"Data Source=\" + ServerName + \";\";\n                if (String.IsNullOrEmpty(DatabaseName))\n                {\n                    connectionString += \"Initial Catalog = master; \";\n                }\n                else\n                {\n                    connectionString += \"Initial Catalog = \" + DatabaseName + \"; \";\n                }\n                if (String.IsNullOrEmpty(UserName))\n                {\n                    connectionString += \"Integrated Security = SSPI; \";\n                }\n                else\n                {\n                    connectionString += \"User Id = \" + UserName + \"; \";\n                    connectionString += \"Password = \" + Password + \"; \";\n                }\n                if (!String.IsNullOrEmpty(ApplicationName))\n                {\n                    connectionString += \"Application Name = \" + ApplicationName + \"; \";\n                }\n                if (Encrypt)\n                {\n                    connectionString += \"Encrypt = true; \";\n                }\n                if (TrustServerCertificate)\n                {\n                    connectionString += \"TrustServerCertificate = true; \";\n                }\n                return connectionString;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Model/WorkloadAnalysis.cs",
    "content": "﻿using NLog;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Data.SqlClient;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.Model\n{\n    public class WorkloadAnalysis\n    {\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        public ObservableCollection<WorkloadAnalysisPoint> Points { get; set; }\n\n        public string Name { get; set; }\n\n        public DateTime StartDate { get; set; }\n\n        public SqlConnectionInfo ConnectionInfo { get; set; }\n\n        public void Load()\n        {\n            using (var conn = new SqlConnection())\n            {\n                conn.ConnectionString = ConnectionInfo.ConnectionString;\n                conn.Open();\n\n                var NormalizedQueries = new Dictionary<long, NormalizedQuery>();\n\n                var numIntervals = 0;\n                var preaggregation = 1;\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = \"SELECT COUNT(*) FROM \" + ConnectionInfo.SchemaName + \".Intervals WHERE duration_minutes > 0;\";\n                    cmd.CommandTimeout = 0;\n                    numIntervals = (int)cmd.ExecuteScalar();\n                }\n                if (numIntervals > 500) // around 8 hours\n                {\n                    preaggregation = 15;\n                }\n\n                if (numIntervals > 1000) // around 16 hours\n                {\n                    preaggregation = 30;\n                }\n\n                if (numIntervals > 2000) // around 32 hours\n                {\n                    preaggregation = 60;\n                }\n\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = \"SELECT TOP(1) end_time FROM \" + ConnectionInfo.SchemaName + \".Intervals ORDER BY interval_id ASC \";\n                    cmd.CommandTimeout = 0;\n                    try\n                    {\n                        StartDate = (DateTime)cmd.ExecuteScalar();\n                    }\n                    catch (Exception)\n                    {\n                        StartDate = DateTime.Today;\n                    }\n                }\n\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandText = \"SELECT * FROM \" + ConnectionInfo.SchemaName + \".NormalizedQueries\";\n                    cmd.CommandTimeout = 0;\n                    using (var rdr = cmd.ExecuteReader())\n                    {\n                        while (rdr.Read())\n                        {\n                            NormalizedQueries.Add(rdr.GetInt64(rdr.GetOrdinal(\"sql_hash\")), new NormalizedQuery()\n                            {\n                                Hash = rdr.GetInt64(rdr.GetOrdinal(\"sql_hash\")),\n                                NormalizedText = rdr.GetString(rdr.GetOrdinal(\"normalized_text\")),\n                                ExampleText = rdr.GetString(rdr.GetOrdinal(\"example_text\"))\n                            });\n                        }\n                    }\n                }\n\n                using (var cmd = conn.CreateCommand())\n                {\n                    cmd.CommandTimeout = 0;\n                    var sqlText = WorkloadViewer.Properties.Resources.WorkloadAnalysis;\n                    cmd.CommandText = sqlText.Replace(\"capture\", ConnectionInfo.SchemaName);\n                    cmd.CommandText = cmd.CommandText.Replace(\"preaggregation\", preaggregation.ToString());\n                    cmd.CommandTimeout = 0;\n                    using (var rdr = cmd.ExecuteReader())\n                    {\n                        Points = new ObservableCollection<WorkloadAnalysisPoint>();\n                        while (rdr.Read())\n                        {\n                            try\n                            {\n                                var point = new WorkloadAnalysisPoint()\n                                {\n                                    OffsetMinutes = rdr.GetInt32(rdr.GetOrdinal(\"offset_minutes\")),\n                                    DurationMinutes = rdr.GetInt32(rdr.GetOrdinal(\"duration_minutes\")),\n                                    NormalizedQuery = NormalizedQueries[rdr.GetInt64(rdr.GetOrdinal(\"sql_hash\"))],\n                                    ApplicationName = rdr.GetString(rdr.GetOrdinal(\"application_name\")),\n                                    DatabaseName = rdr.GetString(rdr.GetOrdinal(\"database_name\")),\n                                    LoginName = rdr.GetString(rdr.GetOrdinal(\"login_name\")),\n                                    HostName = rdr.GetString(rdr.GetOrdinal(\"host_name\")),\n                                    AvgCpuUs = rdr.GetInt64(rdr.GetOrdinal(\"avg_cpu_us\")),\n                                    MinCpuUs = rdr.GetInt64(rdr.GetOrdinal(\"min_cpu_us\")),\n                                    MaxCpuUs = rdr.GetInt64(rdr.GetOrdinal(\"max_cpu_us\")),\n                                    SumCpuUs = rdr.GetInt64(rdr.GetOrdinal(\"sum_cpu_us\")),\n                                    AvgReads = rdr.GetInt64(rdr.GetOrdinal(\"avg_reads\")),\n                                    MinReads = rdr.GetInt64(rdr.GetOrdinal(\"min_reads\")),\n                                    MaxReads = rdr.GetInt64(rdr.GetOrdinal(\"max_reads\")),\n                                    SumReads = rdr.GetInt64(rdr.GetOrdinal(\"sum_reads\")),\n                                    AvgWrites = rdr.GetInt64(rdr.GetOrdinal(\"avg_writes\")),\n                                    MinWrites = rdr.GetInt64(rdr.GetOrdinal(\"min_writes\")),\n                                    MaxWrites = rdr.GetInt64(rdr.GetOrdinal(\"max_writes\")),\n                                    SumWrites = rdr.GetInt64(rdr.GetOrdinal(\"sum_writes\")),\n                                    AvgDurationUs = rdr.GetInt64(rdr.GetOrdinal(\"avg_duration_us\")),\n                                    MinDurationUs = rdr.GetInt64(rdr.GetOrdinal(\"min_duration_us\")),\n                                    MaxDurationUs = rdr.GetInt64(rdr.GetOrdinal(\"max_duration_us\")),\n                                    SumDurationUs = rdr.GetInt64(rdr.GetOrdinal(\"sum_duration_us\")),\n                                    ExecutionCount = rdr.GetInt64(rdr.GetOrdinal(\"execution_count\"))\n                                };\n                                Points.Add(point);\n                            }\n                            catch(Exception e)\n                            {\n                                logger.Warn($\"Skipping invalid datapoint at {rdr.GetInt32(rdr.GetOrdinal(\"offset_minutes\"))} because of Exception: {e.StackTrace}\");\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Model/WorkloadAnalysisPoint.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.Model\n{\n    public class WorkloadAnalysisPoint\n    {\n        public int OffsetMinutes { get; set; }\n        public int DurationMinutes { get; set; }\n        public NormalizedQuery NormalizedQuery { get; set; }\n        public string ApplicationName { get; set; }\n        public string DatabaseName { get; set; }\n        public string LoginName { get; set; }\n        public string HostName { get; set; }\n        public long AvgCpuUs { get; set; }\n\t    public long MinCpuUs { get; set; } \n\t    public long MaxCpuUs { get; set; } \n\t    public long SumCpuUs { get; set; } \n\t    public long AvgReads { get; set; } \n\t    public long MinReads { get; set; } \n\t    public long MaxReads { get; set; } \n\t    public long SumReads { get; set; } \n\t    public long AvgWrites { get; set; } \n\t    public long MinWrites { get; set; } \n\t    public long MaxWrites { get; set; } \n\t    public long SumWrites { get; set; } \n\t    public long AvgDurationUs { get; set; } \n\t    public long MinDurationUs { get; set; } \n\t    public long MaxDurationUs { get; set; } \n\t    public long SumDurationUs { get; set; } \n\t    public long ExecutionCount { get; set; }\n    }\n\n}\n"
  },
  {
    "path": "WorkloadViewer/NLog.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<nlog xmlns=\"http://www.nlog-project.org/schemas/NLog.xsd\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd\"\n      autoReload=\"true\"\n      throwExceptions=\"false\"\n      internalLogLevel=\"Off\" internalLogFile=\"c:\\temp\\nlog-internal.log\">\n\n\n  <targets>\n    <target name=\"logfile\" xsi:type=\"File\" fileName=\"WorkloadViewer.log\" layout=\"${longdate} - ${level} - ${logger} : ${message}\" />\n    <target name=\"warnfile\" xsi:type=\"File\" fileName=\"Warnings.log\" layout=\"${longdate} - ${message}\" />\n    <target name=\"console\" xsi:type=\"Console\" layout=\"${level} - ${logger} : ${message}\"/>\n  </targets>\n\n  <rules>\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"logfile\" />\n    <logger name=\"*\" minlevel=\"Info\" writeTo=\"console\" />\n    <logger name=\"*\" levels=\"Warn\" writeTo=\"warnfile\" />\n  </rules>\n</nlog>\n"
  },
  {
    "path": "WorkloadViewer/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n\n\n//In order to begin building localizable applications, set\n//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file\n//inside a <PropertyGroup>.  For example, if you are using US english\n//in your source files, set the <UICulture> to en-US.  Then uncomment\n//the NeutralResourceLanguage attribute below.  Update the \"en-US\" in\n//the line below to match the UICulture setting in the project file.\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n"
  },
  {
    "path": "WorkloadViewer/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace WorkloadViewer.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"17.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    public class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"WorkloadViewer.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Byte[].\n        /// </summary>\n        public static byte[] TSQL {\n            get {\n                object obj = ResourceManager.GetObject(\"TSQL\", resourceCulture);\n                return ((byte[])(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to WITH baseData AS (\n        ///\tSELECT \n        ///\t\tDATEDIFF(minute, Base.end_time, bIn.end_time) AS offset_minutes,\n        ///\t\tbWD.sql_hash, \n        ///\t\tbWD.avg_cpu_us, \n        ///\t\tbWD.min_cpu_us, \n        ///\t\tbWD.max_cpu_us, \n        ///\t\tbWD.sum_cpu_us, \n        ///\t\tbWD.avg_reads, \n        ///\t\tbWD.min_reads, \n        ///\t\tbWD.max_reads, \n        ///\t\tbWD.sum_reads, \n        ///\t\tbWD.avg_writes, \n        ///\t\tbWD.min_writes, \n        ///\t\tbWD.max_writes, \n        ///\t\tbWD.sum_writes, \n        ///\t\tbWD.avg_duration_us, \n        ///\t\tbWD.min_duration_us, \n        ///\t\tbWD.max_duration_us, \n        ///\t\tbWD.sum_duration_us, \n        ///\t\tbWD.execution_count,\n        ///\t\tbIn.duration_minutes, \n        ///\t\tbNQ.norm [rest of string was truncated]&quot;;.\n        /// </summary>\n        public static string WorkloadAnalysis {\n            get {\n                return ResourceManager.GetString(\"WorkloadAnalysis\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"TSQL\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\TSQL.xshd;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </data>\n  <data name=\"WorkloadAnalysis\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\WorkloadAnalysis.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>\n  </data>\n</root>"
  },
  {
    "path": "WorkloadViewer/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace WorkloadViewer.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"17.3.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "WorkloadViewer/Resources/TSQL.xshd",
    "content": "﻿<?xml version=\"1.0\"?>\n<!-- Shades of Red-Brown: #a31515, #cf4315, #ffb96e, #ffdc95 -->\n<!-- Shades of Bright-Blue: #0077dc #008fe4 #8dbbdc #8de8ff -->\n<SyntaxDefinition name=\"SQL\" extensions=\".sql\"\n                  xmlns=\"http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008\">\n\n  <!-- T-SQL Reference: http://msdn.microsoft.com/de-de/library/ms189826%28v=sql.90%29.aspx -->\n\n  <Color name=\"Digits\" foreground=\"Black\" exampleText=\"3.1415f\"/>\n  <Color name=\"Comment\" foreground=\"#008000\" exampleText=\"string text = &quot;Hello, World!&quot;\"/>\n  <Color name=\"Punctuation\" foreground=\"#808080\" exampleText=\"string text = &quot;Hello, World!&quot;\"/>\n\n  <Color name=\"String\" foreground=\"Red\" exampleText=\"string text = &quot;Hello, World!&quot;\"/>\n  <Color name=\"String2\" foreground=\"#993\" exampleText=\"string text = &quot;Hello, World!&quot;\"/>\n\n  <Color name=\"Keyword\" fontWeight=\"normal\" foreground=\"Blue\" exampleText=\"SELECT\"/>\n  <Color name=\"Keyword1\" fontWeight=\"normal\" foreground=\"#FF00FF\" exampleText=\"MAX\"/>\n  <Color name=\"GoKeyword\" fontWeight=\"normal\" foreground=\"#808080\" exampleText=\"GO\"/>\n  <Color name=\"KeywordSystem\" fontWeight=\"normal\" foreground=\"#800034\" exampleText=\"sp_executesql\"/>\n\n  <Color name=\"MethodCall\" foreground=\"MidnightBlue\" fontWeight=\"bold\" />\n\n  <Color name=\"Variable\" foreground=\"Black\"  exampleText=\"@Variable\" />\n  <Color name=\"Variable1\" foreground=\"Black\" exampleText=\"@@Variable\" />\n\n  <Color name=\"ObjectReference\" foreground=\"Black\" exampleText=\"Customer.Name\" />\n  <Color name=\"ObjectReference1\" foreground=\"Black\" exampleText=\"dbo.Customer.Name\" />\n\n  <Color name=\"ObjectReferenceInBrackets\" foreground=\"Black\" exampleText=\"[Customer].[Name]\" />\n  <Color name=\"ObjectReferenceInBrackets1\" foreground=\"Black\" exampleText=\"[dbo].[Customer].[Name]\" />\n\n  <Color name=\"CommentMarkerSetTodo\"       foreground=\"Red\"     fontWeight=\"bold\" />\n  <Color name=\"CommentMarkerSetHackUndone\" foreground=\"#E0E000\" fontWeight=\"bold\" />\n\n  <RuleSet name=\"CommentMarkerSet\">\n    <Keywords color=\"CommentMarkerSetTodo\">\n      <Word>TODO</Word>\n      <Word>FIXME</Word>\n    </Keywords>\n    <Keywords color=\"CommentMarkerSetHackUndone\">\n      <Word>HACK</Word>\n      <Word>UNDONE</Word>\n    </Keywords>\n  </RuleSet>\n\n  <RuleSet  ignoreCase=\"true\">\n    <Span color=\"String\" multiline=\"true\" >\n      <Begin>'</Begin>\n      <End>'</End>\n    </Span>\n\n    <Span color=\"String2\" multiline=\"true\"  >\n      <Begin>\"</Begin>\n      <End>\"</End>\n    </Span>\n\n    <!-- span for escape sequences -->\n    <Span  color=\"Comment\" begin=\"--\" end=\"\\n\" ruleSet=\"CommentMarkerSet\"/>\n    <Span color=\"Comment\"  multiline=\"true\" ruleSet=\"CommentMarkerSet\">\n      <Begin>/\\*</Begin>\n      <End>\\*/</End>\n    </Span>\n\n    <Keywords color=\"Keyword\" >\n      <Word>ABSOLUTE</Word>\n      <Word>ACTION</Word>\n      <Word>ADD</Word>\n      <Word>ALTER</Word>\n      <Word>AS</Word>\n      <Word>ASC</Word>\n      <Word>AT</Word>\n      <Word>AUTHORIZATION</Word>\n      <Word>BACKUP</Word>\n      <Word>BEGIN</Word>\n      <Word>BIT</Word>\n      <Word>BREAK</Word>\n      <Word>BROWSE</Word>\n      <Word>BULK</Word>\n      <Word>BY</Word>\n      <Word>CASCADE</Word>\n      <Word>CASE</Word>\n      <Word>CATALOG</Word>\n      <Word>CHAR</Word>\n      <Word>CHARACTER</Word>\n      <Word>CHECK</Word>\n      <Word>CHECKPOINT</Word>\n      <Word>CLOSE</Word>\n      <Word>CLUSTERED</Word>\n      <Word>COLUMN</Word>\n      <Word>COMMIT</Word>\n      <Word>COMPUTE</Word>\n      <Word>CONNECT</Word>\n      <Word>CONSTRAINT</Word>\n      <Word>CONTAINSTABLE</Word>\n      <Word>CONTINUE</Word>\n      <Word>CREATE</Word>\n      <Word>CURRENT</Word>\n      <Word>CURRENT_DATE</Word>\n      <Word>CURSOR</Word>\n      <Word>DATABASE</Word>\n      <Word>DATE</Word>\n      <Word>DBCC</Word>\n      <Word>DEALLOCATE</Word>\n      <Word>DEC</Word>\n      <Word>DECIMAL</Word>\n      <Word>DECLARE</Word>\n      <Word>DEFAULT</Word>\n      <Word>DELETE</Word>\n      <Word>DENY</Word>\n      <Word>DESC</Word>\n      <Word>DISK</Word>\n      <Word>DISTINCT</Word>\n      <Word>DISTRIBUTED</Word>\n      <Word>DOUBLE</Word>\n      <Word>DROP</Word>\n      <Word>DUMP</Word>\n      <Word>ELSE</Word>\n      <Word>END</Word>\n      <Word>ERRLVL</Word>\n      <Word>ESCAPE</Word>\n      <Word>EXCEPT</Word>\n      <Word>EXEC</Word>\n      <Word>EXECUTE</Word>\n      <Word>EXIT</Word>\n      <Word>EXTERNAL</Word>\n      <Word>FETCH</Word>\n      <Word>FILE</Word>\n      <Word>FILLFACTOR</Word>\n      <Word>FIRST</Word>\n      <Word>FLOAT</Word>\n      <Word>FOR</Word>\n      <Word>FOREIGN</Word>\n      <Word>FREETEXT</Word>\n      <Word>FREETEXTTABLE</Word>\n      <Word>FROM</Word>\n      <Word>FULL</Word>\n      <Word>FUNCTION</Word>\n      <Word>GET</Word>\n      <Word>GLOBAL</Word>\n      <Word>GO</Word>\n      <Word>GOTO</Word>\n      <Word>GRANT</Word>\n      <Word>GROUP</Word>\n      <Word>HAVING</Word>\n      <Word>HOLDLOCK</Word>\n      <Word>IDENTITY</Word>\n      <Word>IDENTITY_INSERT</Word>\n      <Word>IDENTITYCOL</Word>\n      <Word>IF</Word>\n      <Word>IMMEDIATE</Word>\n      <Word>INCLUDE</Word>\n      <Word>INDEX</Word>\n      <Word>INSENSITIVE</Word>\n      <Word>INSERT</Word>\n      <Word>INT</Word>\n      <Word>INTEGER</Word>\n      <Word>INTERSECT</Word>\n      <Word>INTO</Word>\n      <Word>ISOLATION</Word>\n      <Word>KEY</Word>\n      <Word>KILL</Word>\n      <Word>LANGUAGE</Word>\n      <Word>LAST</Word>\n      <Word>LEVEL</Word>\n      <Word>LINENO</Word>\n      <Word>LOAD</Word>\n      <Word>LOCAL</Word>\n      <Word>MATCH</Word>\n      <Word>MERGE</Word>\n      <Word>NATIONAL</Word>\n      <Word>NCHAR</Word>\n      <Word>NEXT</Word>\n      <Word>NO</Word>\n      <Word>NOCHECK</Word>\n      <Word>NONCLUSTERED</Word>\n      <Word>NONE</Word>\n      <Word>NUMERIC</Word>\n      <Word>OF</Word>\n      <Word>OFF</Word>\n      <Word>OFFSETS</Word>\n      <Word>ON</Word>\n      <Word>OPEN</Word>\n      <Word>OPENDATASOURCE</Word>\n      <Word>OPENQUERY</Word>\n      <Word>OPENROWSET</Word>\n      <Word>OPENXML</Word>\n      <Word>OPTION</Word>\n      <Word>ORDER</Word>\n      <Word>OUTPUT</Word>\n      <Word>OVER</Word>\n      <Word>PARTIAL</Word>\n      <Word>PERCENT</Word>\n      <Word>PLAN</Word>\n      <Word>PRECISION</Word>\n      <Word>PRIMARY</Word>\n      <Word>PRINT</Word>\n      <Word>PRIOR</Word>\n      <Word>PROC</Word>\n      <Word>PROCEDURE</Word>\n      <Word>PUBLIC</Word>\n      <Word>RAISERROR</Word>\n      <Word>READ</Word>\n      <Word>READTEXT</Word>\n      <Word>REAL</Word>\n      <Word>RECONFIGURE</Word>\n      <Word>REFERENCES</Word>\n      <Word>RELATIVE</Word>\n      <Word>REPLICATION</Word>\n      <Word>RESTORE</Word>\n      <Word>RESTRICT</Word>\n      <Word>RESTRICT</Word>\n      <Word>RETURN</Word>\n      <Word>REVERT</Word>\n      <Word>REVOKE</Word>\n      <Word>ROLLBACK</Word>\n      <Word>ROLLBACK</Word>\n      <Word>ROWCOUNT</Word>\n      <Word>ROWGUIDCOL</Word>\n      <Word>ROWS</Word>\n      <Word>RULE</Word>\n      <Word>SAVE</Word>\n      <Word>SCHEMA</Word>\n      <Word>SCROLL</Word>\n      <Word>SECURITYAUDIT</Word>\n      <Word>SELECT</Word>\n      <Word>SEMANTICKEYPHRASETABLE</Word>\n      <Word>SEMANTICSIMILARITYDETAILSTABLE</Word>\n      <Word>SEMANTICSIMILARITYTABLE</Word>\n      <Word>SESSION</Word>\n      <Word>SET</Word>\n      <Word>SETUSER</Word>\n      <Word>SHUTDOWN</Word>\n      <Word>SMALLINT</Word>\n      <Word>SQL</Word>\n      <Word>STATISTICS</Word>\n      <Word>TABLE</Word>\n      <Word>TABLESAMPLE</Word>\n      <Word>TEXTSIZE</Word>\n      <Word>THEN</Word>\n      <Word>TIME</Word>\n      <Word>TIMESTAMP</Word>\n      <Word>TO</Word>\n      <Word>TOP</Word>\n      <Word>TRAN</Word>\n      <Word>TRANSACTION</Word>\n      <Word>TRIGGER</Word>\n      <Word>TRUNCATE</Word>\n      <Word>UNION</Word>\n      <Word>UNIQUE</Word>\n      <Word>UPDATETEXT</Word>\n      <Word>USE</Word>\n      <Word>USER</Word>\n      <Word>USING</Word>\n      <Word>VALUE</Word>\n      <Word>VALUES</Word>\n      <Word>VARCHAR</Word>\n      <Word>VARYING</Word>\n      <Word>VARYING</Word>\n      <Word>VIEW</Word>\n      <Word>WAITFOR</Word>\n      <Word>WHEN</Word>\n      <Word>WHERE</Word>\n      <Word>WHILE</Word>\n      <Word>WITH</Word>\n      <Word>WITHIN GROUP</Word>\n      <Word>WRITETEXT</Word>\n      <Word>ZONE</Word>\n    </Keywords>\n\n    <Keywords color=\"Keyword1\">\n      <Word>COALESCE</Word>\n      <Word>COLLATE</Word>\n      <Word>SESSION_USER</Word>\n      <Word>CONTAINS</Word>\n      <Word>CONVERT</Word>\n      <Word>SYSTEM_USER</Word>\n      <Word>CURRENT_TIME</Word>\n      <Word>CURRENT_TIMESTAMP</Word>\n      <Word>CURRENT_USER</Word>\n      <Word>NULLIF</Word>\n      <Word>TRY_CONVERT</Word>\n      <Word>TSEQUAL</Word>\n      <Word>UPDATE</Word>\n      <Word>EXTRACT</Word>\n      <Word>AVG</Word>\n      <Word>BIT_LENGTH</Word>\n      <Word>HOUR</Word>\n      <Word>SECOND</Word>\n      <Word>CAST</Word>\n      <Word>SESSION_USER</Word>\n      <Word>COALESCE</Word>\n      <Word>SPACE</Word>\n      <Word>COLLATE</Word>\n      <Word>SUBSTRING</Word>\n      <Word>SUM</Word>\n      <Word>SYSTEM_USER</Word>\n      <Word>CONVERT</Word>\n      <Word>COUNT</Word>\n      <Word>CURRENT_TIME</Word>\n      <Word>LOWER</Word>\n      <Word>CURRENT_TIMESTAMP</Word>\n      <Word>CURRENT_USER</Word>\n      <Word>MAX</Word>\n      <Word>MIN</Word>\n      <Word>MINUTE</Word>\n      <Word>DAY</Word>\n      <Word>TRIM</Word>\n      <Word>MONTH</Word>\n      <Word>UPDATE</Word>\n      <Word>UPPER</Word>\n      <Word>NULLIF</Word>\n      <Word>OCTET_LENGTH</Word>\n      <Word>YEAR</Word>\n    </Keywords>\n\n    <Keywords color=\"GoKeyword\" >\n      <Word>ALL</Word>\n      <Word>AND</Word>\n      <Word>ANY</Word>\n      <Word>BETWEEN</Word>\n      <Word>CROSS</Word>\n      <Word>EXISTS</Word>\n      <Word>GO</Word>\n      <Word>IN</Word>\n      <Word>INNER</Word>\n      <Word>IS</Word>\n      <Word>JOIN</Word>\n      <Word>LEFT</Word>\n      <Word>LIKE</Word>\n      <Word>NOT</Word>\n      <Word>NULL</Word>\n      <Word>OR</Word>\n      <Word>OUTER</Word>\n      <Word>PIVOT</Word>\n      <Word>RIGHT</Word>\n      <Word>SOME</Word>\n      <Word>UNPIVOT</Word>\n    </Keywords>\n\n    <Keywords color=\"KeywordSystem\">\n      <Word>all_columns</Word>\n      <Word>all_objects</Word>\n      <Word>all_parameters</Word>\n      <Word>all_sql_modules</Word>\n      <Word>all_views</Word>\n      <Word>allocation_units</Word>\n      <Word>assemblies</Word>\n      <Word>assembly_files</Word>\n      <Word>assembly_modules</Word>\n      <Word>assembly_references</Word>\n      <Word>assembly_types</Word>\n      <Word>asymmetric_keys</Word>\n      <Word>availability_databases_cluster</Word>\n      <Word>availability_group_listener_ip_addresses</Word>\n      <Word>availability_group_listeners</Word>\n      <Word>availability_groups_cluster</Word>\n      <Word>availability_groups</Word>\n      <Word>availability_read_only_routing_lists</Word>\n      <Word>availability_replicas</Word>\n      <Word>backup_devices</Word>\n      <Word>certificates</Word>\n      <Word>change_tracking_databases</Word>\n      <Word>change_tracking_tables</Word>\n      <Word>check_constraints</Word>\n      <Word>CHECK_CONSTRAINTS</Word>\n      <Word>COLUMN_DOMAIN_USAGE</Word>\n      <Word>column_encryption_key_values</Word>\n      <Word>column_encryption_keys</Word>\n      <Word>column_master_keys</Word>\n      <Word>COLUMN_PRIVILEGES</Word>\n      <Word>column_store_dictionaries</Word>\n      <Word>column_store_row_groups</Word>\n      <Word>column_store_segments</Word>\n      <Word>column_type_usages</Word>\n      <Word>column_xml_schema_collection_usages</Word>\n      <Word>columns</Word>\n      <Word>COLUMNS</Word>\n      <Word>computed_columns</Word>\n      <Word>configurations</Word>\n      <Word>CONSTRAINT_COLUMN_USAGE</Word>\n      <Word>CONSTRAINT_TABLE_USAGE</Word>\n      <Word>conversation_endpoints</Word>\n      <Word>conversation_groups</Word>\n      <Word>conversation_priorities</Word>\n      <Word>credentials</Word>\n      <Word>crypt_properties</Word>\n      <Word>cryptographic_providers</Word>\n      <Word>data_spaces</Word>\n      <Word>database_audit_specification_details</Word>\n      <Word>database_audit_specifications</Word>\n      <Word>database_credentials</Word>\n      <Word>database_files</Word>\n      <Word>database_filestream_options</Word>\n      <Word>database_mirroring_endpoints</Word>\n      <Word>database_mirroring_witnesses</Word>\n      <Word>database_mirroring</Word>\n      <Word>database_permissions</Word>\n      <Word>database_principals</Word>\n      <Word>database_query_store_options</Word>\n      <Word>database_recovery_status</Word>\n      <Word>database_role_members</Word>\n      <Word>database_scoped_configurations</Word>\n      <Word>database_scoped_credentials</Word>\n      <Word>databases</Word>\n      <Word>default_constraints</Word>\n      <Word>destination_data_spaces</Word>\n      <Word>dm_audit_actions</Word>\n      <Word>dm_audit_class_type_map</Word>\n      <Word>dm_broker_activated_tasks</Word>\n      <Word>dm_broker_connections</Word>\n      <Word>dm_broker_forwarded_messages</Word>\n      <Word>dm_broker_queue_monitors</Word>\n      <Word>dm_cdc_errors</Word>\n      <Word>dm_cdc_log_scan_sessions</Word>\n      <Word>dm_clr_appdomains</Word>\n      <Word>dm_clr_loaded_assemblies</Word>\n      <Word>dm_clr_properties</Word>\n      <Word>dm_clr_tasks</Word>\n      <Word>dm_column_store_object_pool</Word>\n      <Word>dm_cryptographic_provider_algorithms</Word>\n      <Word>dm_cryptographic_provider_keys</Word>\n      <Word>dm_cryptographic_provider_properties</Word>\n      <Word>dm_cryptographic_provider_sessions</Word>\n      <Word>dm_database_encryption_keys</Word>\n      <Word>dm_db_column_store_row_group_operational_stats</Word>\n      <Word>dm_db_column_store_row_group_physical_stats</Word>\n      <Word>dm_db_database_page_allocations</Word>\n      <Word>dm_db_file_space_usage</Word>\n      <Word>dm_db_fts_index_physical_stats</Word>\n      <Word>dm_db_incremental_stats_properties</Word>\n      <Word>dm_db_index_operational_stats</Word>\n      <Word>dm_db_index_physical_stats</Word>\n      <Word>dm_db_index_usage_stats</Word>\n      <Word>dm_db_log_space_usage</Word>\n      <Word>dm_db_mirroring_auto_page_repair</Word>\n      <Word>dm_db_mirroring_connections</Word>\n      <Word>dm_db_mirroring_past_actions</Word>\n      <Word>dm_db_missing_index_columns</Word>\n      <Word>dm_db_missing_index_details</Word>\n      <Word>dm_db_missing_index_group_stats</Word>\n      <Word>dm_db_missing_index_groups</Word>\n      <Word>dm_db_objects_disabled_on_compatibility_level_change</Word>\n      <Word>dm_db_partition_stats</Word>\n      <Word>dm_db_persisted_sku_features</Word>\n      <Word>dm_db_rda_migration_status</Word>\n      <Word>dm_db_rda_schema_update_status</Word>\n      <Word>dm_db_script_level</Word>\n      <Word>dm_db_session_space_usage</Word>\n      <Word>dm_db_stats_histogram</Word>\n      <Word>dm_db_stats_properties_internal</Word>\n      <Word>dm_db_stats_properties</Word>\n      <Word>dm_db_task_space_usage</Word>\n      <Word>dm_db_uncontained_entities</Word>\n      <Word>dm_db_xtp_checkpoint_files</Word>\n      <Word>dm_db_xtp_checkpoint_stats</Word>\n      <Word>dm_db_xtp_gc_cycle_stats</Word>\n      <Word>dm_db_xtp_hash_index_stats</Word>\n      <Word>dm_db_xtp_index_stats</Word>\n      <Word>dm_db_xtp_memory_consumers</Word>\n      <Word>dm_db_xtp_nonclustered_index_stats</Word>\n      <Word>dm_db_xtp_object_stats</Word>\n      <Word>dm_db_xtp_table_memory_stats</Word>\n      <Word>dm_db_xtp_transactions</Word>\n      <Word>dm_exec_background_job_queue_stats</Word>\n      <Word>dm_exec_background_job_queue</Word>\n      <Word>dm_exec_cached_plan_dependent_objects</Word>\n      <Word>dm_exec_cached_plans</Word>\n      <Word>dm_exec_compute_node_errors</Word>\n      <Word>dm_exec_compute_node_status</Word>\n      <Word>dm_exec_compute_nodes</Word>\n      <Word>dm_exec_connections</Word>\n      <Word>dm_exec_cursors</Word>\n      <Word>dm_exec_describe_first_result_set_for_object</Word>\n      <Word>dm_exec_describe_first_result_set</Word>\n      <Word>dm_exec_distributed_request_steps</Word>\n      <Word>dm_exec_distributed_requests</Word>\n      <Word>dm_exec_distributed_sql_requests</Word>\n      <Word>dm_exec_dms_services</Word>\n      <Word>dm_exec_dms_workers</Word>\n      <Word>dm_exec_external_operations</Word>\n      <Word>dm_exec_external_work</Word>\n      <Word>dm_exec_function_stats</Word>\n      <Word>dm_exec_input_buffer</Word>\n      <Word>dm_exec_plan_attributes</Word>\n      <Word>dm_exec_procedure_stats</Word>\n      <Word>dm_exec_query_memory_grants</Word>\n      <Word>dm_exec_query_optimizer_info</Word>\n      <Word>dm_exec_query_optimizer_memory_gateways</Word>\n      <Word>dm_exec_query_parallel_workers</Word>\n      <Word>dm_exec_query_plan</Word>\n      <Word>dm_exec_query_profiles</Word>\n      <Word>dm_exec_query_resource_semaphores</Word>\n      <Word>dm_exec_query_statistics_xml</Word>\n      <Word>dm_exec_query_stats</Word>\n      <Word>dm_exec_query_transformation_stats</Word>\n      <Word>dm_exec_requests</Word>\n      <Word>dm_exec_session_wait_stats</Word>\n      <Word>dm_exec_sessions</Word>\n      <Word>dm_exec_sql_text</Word>\n      <Word>dm_exec_text_query_plan</Word>\n      <Word>dm_exec_trigger_stats</Word>\n      <Word>dm_exec_valid_use_hints</Word>\n      <Word>dm_exec_xml_handles</Word>\n      <Word>dm_external_script_execution_stats</Word>\n      <Word>dm_external_script_requests</Word>\n      <Word>dm_filestream_file_io_handles</Word>\n      <Word>dm_filestream_file_io_requests</Word>\n      <Word>dm_filestream_non_transacted_handles</Word>\n      <Word>dm_fts_active_catalogs</Word>\n      <Word>dm_fts_fdhosts</Word>\n      <Word>dm_fts_index_keywords_by_document</Word>\n      <Word>dm_fts_index_keywords_by_property</Word>\n      <Word>dm_fts_index_keywords_position_by_document</Word>\n      <Word>dm_fts_index_keywords</Word>\n      <Word>dm_fts_index_population</Word>\n      <Word>dm_fts_memory_buffers</Word>\n      <Word>dm_fts_memory_pools</Word>\n      <Word>dm_fts_outstanding_batches</Word>\n      <Word>dm_fts_parser</Word>\n      <Word>dm_fts_population_ranges</Word>\n      <Word>dm_fts_semantic_similarity_population</Word>\n      <Word>dm_hadr_auto_page_repair</Word>\n      <Word>dm_hadr_automatic_seeding</Word>\n      <Word>dm_hadr_availability_group_states</Word>\n      <Word>dm_hadr_availability_replica_cluster_nodes</Word>\n      <Word>dm_hadr_availability_replica_cluster_states</Word>\n      <Word>dm_hadr_availability_replica_states</Word>\n      <Word>dm_hadr_cluster_members</Word>\n      <Word>dm_hadr_cluster_networks</Word>\n      <Word>dm_hadr_cluster</Word>\n      <Word>dm_hadr_database_replica_cluster_states</Word>\n      <Word>dm_hadr_database_replica_states</Word>\n      <Word>dm_hadr_instance_node_map</Word>\n      <Word>dm_hadr_name_id_map</Word>\n      <Word>dm_hadr_physical_seeding_stats</Word>\n      <Word>dm_io_backup_tapes</Word>\n      <Word>dm_io_cluster_shared_drives</Word>\n      <Word>dm_io_cluster_valid_path_names</Word>\n      <Word>dm_io_pending_io_requests</Word>\n      <Word>dm_io_virtual_file_stats</Word>\n      <Word>dm_logconsumer_cachebufferrefs</Word>\n      <Word>dm_logconsumer_privatecachebuffers</Word>\n      <Word>dm_logpool_consumers</Word>\n      <Word>dm_logpool_hashentries</Word>\n      <Word>dm_logpool_sharedcachebuffers</Word>\n      <Word>dm_logpool_stats</Word>\n      <Word>dm_logpoolmgr_freepools</Word>\n      <Word>dm_logpoolmgr_respoolsize</Word>\n      <Word>dm_logpoolmgr_stats</Word>\n      <Word>dm_os_buffer_descriptors</Word>\n      <Word>dm_os_buffer_pool_extension_configuration</Word>\n      <Word>dm_os_child_instances</Word>\n      <Word>dm_os_cluster_nodes</Word>\n      <Word>dm_os_cluster_properties</Word>\n      <Word>dm_os_dispatcher_pools</Word>\n      <Word>dm_os_dispatchers</Word>\n      <Word>dm_os_hosts</Word>\n      <Word>dm_os_latch_stats</Word>\n      <Word>dm_os_loaded_modules</Word>\n      <Word>dm_os_memory_allocations</Word>\n      <Word>dm_os_memory_broker_clerks</Word>\n      <Word>dm_os_memory_brokers</Word>\n      <Word>dm_os_memory_cache_clock_hands</Word>\n      <Word>dm_os_memory_cache_counters</Word>\n      <Word>dm_os_memory_cache_entries</Word>\n      <Word>dm_os_memory_cache_hash_tables</Word>\n      <Word>dm_os_memory_clerks</Word>\n      <Word>dm_os_memory_node_access_stats</Word>\n      <Word>dm_os_memory_nodes</Word>\n      <Word>dm_os_memory_objects</Word>\n      <Word>dm_os_memory_pools</Word>\n      <Word>dm_os_nodes</Word>\n      <Word>dm_os_performance_counters</Word>\n      <Word>dm_os_process_memory</Word>\n      <Word>dm_os_ring_buffers</Word>\n      <Word>dm_os_schedulers</Word>\n      <Word>dm_os_server_diagnostics_log_configurations</Word>\n      <Word>dm_os_spinlock_stats</Word>\n      <Word>dm_os_stacks</Word>\n      <Word>dm_os_sublatches</Word>\n      <Word>dm_os_sys_info</Word>\n      <Word>dm_os_sys_memory</Word>\n      <Word>dm_os_tasks</Word>\n      <Word>dm_os_threads</Word>\n      <Word>dm_os_virtual_address_dump</Word>\n      <Word>dm_os_volume_stats</Word>\n      <Word>dm_os_wait_stats</Word>\n      <Word>dm_os_waiting_tasks</Word>\n      <Word>dm_os_windows_info</Word>\n      <Word>dm_os_worker_local_storage</Word>\n      <Word>dm_os_workers</Word>\n      <Word>dm_qn_subscriptions</Word>\n      <Word>dm_repl_articles</Word>\n      <Word>dm_repl_schemas</Word>\n      <Word>dm_repl_tranhash</Word>\n      <Word>dm_repl_traninfo</Word>\n      <Word>dm_resource_governor_configuration</Word>\n      <Word>dm_resource_governor_external_resource_pool_affinity</Word>\n      <Word>dm_resource_governor_external_resource_pools</Word>\n      <Word>dm_resource_governor_resource_pool_affinity</Word>\n      <Word>dm_resource_governor_resource_pool_volumes</Word>\n      <Word>dm_resource_governor_resource_pools</Word>\n      <Word>dm_resource_governor_workload_groups</Word>\n      <Word>dm_server_audit_status</Word>\n      <Word>dm_server_memory_dumps</Word>\n      <Word>dm_server_registry</Word>\n      <Word>dm_server_services</Word>\n      <Word>dm_sql_referenced_entities</Word>\n      <Word>dm_sql_referencing_entities</Word>\n      <Word>dm_tcp_listener_states</Word>\n      <Word>dm_tran_active_snapshot_database_transactions</Word>\n      <Word>dm_tran_active_transactions</Word>\n      <Word>dm_tran_commit_table</Word>\n      <Word>dm_tran_current_snapshot</Word>\n      <Word>dm_tran_current_transaction</Word>\n      <Word>dm_tran_database_transactions</Word>\n      <Word>dm_tran_global_recovery_transactions</Word>\n      <Word>dm_tran_global_transactions_enlistments</Word>\n      <Word>dm_tran_global_transactions_log</Word>\n      <Word>dm_tran_global_transactions</Word>\n      <Word>dm_tran_locks</Word>\n      <Word>dm_tran_session_transactions</Word>\n      <Word>dm_tran_top_version_generators</Word>\n      <Word>dm_tran_transactions_snapshot</Word>\n      <Word>dm_tran_version_store</Word>\n      <Word>dm_xe_map_values</Word>\n      <Word>dm_xe_object_columns</Word>\n      <Word>dm_xe_objects</Word>\n      <Word>dm_xe_packages</Word>\n      <Word>dm_xe_session_event_actions</Word>\n      <Word>dm_xe_session_events</Word>\n      <Word>dm_xe_session_object_columns</Word>\n      <Word>dm_xe_session_targets</Word>\n      <Word>dm_xe_sessions</Word>\n      <Word>dm_xtp_gc_queue_stats</Word>\n      <Word>dm_xtp_gc_stats</Word>\n      <Word>dm_xtp_system_memory_consumers</Word>\n      <Word>dm_xtp_threads</Word>\n      <Word>dm_xtp_transaction_recent_rows</Word>\n      <Word>dm_xtp_transaction_stats</Word>\n      <Word>DOMAIN_CONSTRAINTS</Word>\n      <Word>DOMAINS</Word>\n      <Word>endpoint_webmethods</Word>\n      <Word>endpoints</Word>\n      <Word>event_notification_event_types</Word>\n      <Word>event_notifications</Word>\n      <Word>events</Word>\n      <Word>extended_procedures</Word>\n      <Word>extended_properties</Word>\n      <Word>external_data_sources</Word>\n      <Word>external_file_formats</Word>\n      <Word>external_tables</Word>\n      <Word>filegroups</Word>\n      <Word>filetable_system_defined_objects</Word>\n      <Word>filetables</Word>\n      <Word>fn_builtin_permissions</Word>\n      <Word>fn_cColvEntries_80</Word>\n      <Word>fn_cdc_check_parameters</Word>\n      <Word>fn_cdc_get_column_ordinal</Word>\n      <Word>fn_cdc_get_max_lsn</Word>\n      <Word>fn_cdc_get_min_lsn</Word>\n      <Word>fn_cdc_has_column_changed</Word>\n      <Word>fn_cdc_hexstrtobin</Word>\n      <Word>fn_cdc_map_lsn_to_time</Word>\n      <Word>fn_cdc_map_time_to_lsn</Word>\n      <Word>fn_check_object_signatures</Word>\n      <Word>fn_column_store_row_groups</Word>\n      <Word>fn_db_backup_file_snapshots</Word>\n      <Word>fn_dblog_xtp</Word>\n      <Word>fn_dblog</Word>\n      <Word>fn_dump_dblog_xtp</Word>\n      <Word>fn_dump_dblog</Word>\n      <Word>fn_EnumCurrentPrincipals</Word>\n      <Word>fn_fIsColTracked</Word>\n      <Word>fn_get_audit_file</Word>\n      <Word>fn_get_sql</Word>\n      <Word>fn_GetCurrentPrincipal</Word>\n      <Word>fn_GetRowsetIdFromRowDump</Word>\n      <Word>fn_hadr_backup_is_preferred_replica</Word>\n      <Word>fn_hadr_distributed_ag_database_replica</Word>\n      <Word>fn_hadr_distributed_ag_replica</Word>\n      <Word>fn_hadr_is_primary_replica</Word>\n      <Word>fn_hadr_is_same_replica</Word>\n      <Word>fn_helpcollations</Word>\n      <Word>fn_helpdatatypemap</Word>\n      <Word>fn_IsBitSetInBitmask</Word>\n      <Word>fn_isrolemember</Word>\n      <Word>fn_listextendedproperty</Word>\n      <Word>fn_MapSchemaType</Word>\n      <Word>fn_MSdayasnumber</Word>\n      <Word>fn_MSgeneration_downloadonly</Word>\n      <Word>fn_MSget_dynamic_filter_login</Word>\n      <Word>fn_MSorbitmaps</Word>\n      <Word>fn_MSrepl_map_resolver_clsid</Word>\n      <Word>fn_MStestbit</Word>\n      <Word>fn_MSvector_downloadonly</Word>\n      <Word>fn_MSxe_read_event_stream</Word>\n      <Word>fn_my_permissions</Word>\n      <Word>fn_numberOf1InBinaryAfterLoc</Word>\n      <Word>fn_numberOf1InVarBinary</Word>\n      <Word>fn_PhysLocCracker</Word>\n      <Word>fn_PhysLocFormatter</Word>\n      <Word>fn_repladjustcolumnmap</Word>\n      <Word>fn_repldecryptver4</Word>\n      <Word>fn_replformatdatetime</Word>\n      <Word>fn_replgetcolidfrombitmap</Word>\n      <Word>fn_replgetparsedddlcmd</Word>\n      <Word>fn_replp2pversiontotranid</Word>\n      <Word>fn_replreplacesinglequote</Word>\n      <Word>fn_replreplacesinglequoteplusprotectstring</Word>\n      <Word>fn_repluniquename</Word>\n      <Word>fn_replvarbintoint</Word>\n      <Word>fn_RowDumpCracker</Word>\n      <Word>fn_servershareddrives</Word>\n      <Word>fn_sqlagent_job_history</Word>\n      <Word>fn_sqlagent_jobs</Word>\n      <Word>fn_sqlagent_jobsteps_logs</Word>\n      <Word>fn_sqlagent_jobsteps</Word>\n      <Word>fn_sqlagent_subsystems</Word>\n      <Word>fn_sqlvarbasetostr</Word>\n      <Word>fn_stmt_sql_handle_from_sql_stmt</Word>\n      <Word>fn_trace_geteventinfo</Word>\n      <Word>fn_trace_getfilterinfo</Word>\n      <Word>fn_trace_getinfo</Word>\n      <Word>fn_trace_gettable</Word>\n      <Word>fn_translate_permissions</Word>\n      <Word>fn_validate_plan_guide</Word>\n      <Word>fn_varbintohexstr</Word>\n      <Word>fn_varbintohexsubstring</Word>\n      <Word>fn_virtualfilestats</Word>\n      <Word>fn_virtualservernodes</Word>\n      <Word>fn_xe_file_target_read_file</Word>\n      <Word>fn_yukonsecuritymodelrequired</Word>\n      <Word>foreign_key_columns</Word>\n      <Word>foreign_keys</Word>\n      <Word>fulltext_catalogs</Word>\n      <Word>fulltext_document_types</Word>\n      <Word>fulltext_index_catalog_usages</Word>\n      <Word>fulltext_index_columns</Word>\n      <Word>fulltext_index_fragments</Word>\n      <Word>fulltext_indexes</Word>\n      <Word>fulltext_languages</Word>\n      <Word>fulltext_semantic_language_statistics_database</Word>\n      <Word>fulltext_semantic_languages</Word>\n      <Word>fulltext_stoplists</Word>\n      <Word>fulltext_stopwords</Word>\n      <Word>fulltext_system_stopwords</Word>\n      <Word>function_order_columns</Word>\n      <Word>hash_indexes</Word>\n      <Word>http_endpoints</Word>\n      <Word>identity_columns</Word>\n      <Word>index_columns</Word>\n      <Word>indexes</Word>\n      <Word>internal_partitions</Word>\n      <Word>internal_tables</Word>\n      <Word>KEY_COLUMN_USAGE</Word>\n      <Word>key_constraints</Word>\n      <Word>key_encryptions</Word>\n      <Word>linked_logins</Word>\n      <Word>login_token</Word>\n      <Word>masked_columns</Word>\n      <Word>master_files</Word>\n      <Word>master_key_passwords</Word>\n      <Word>memory_optimized_tables_internal_attributes</Word>\n      <Word>message_type_xml_schema_collection_usages</Word>\n      <Word>messages</Word>\n      <Word>module_assembly_usages</Word>\n      <Word>numbered_procedure_parameters</Word>\n      <Word>numbered_procedures</Word>\n      <Word>objects</Word>\n      <Word>openkeys</Word>\n      <Word>parameter_type_usages</Word>\n      <Word>parameter_xml_schema_collection_usages</Word>\n      <Word>parameters</Word>\n      <Word>PARAMETERS</Word>\n      <Word>partition_functions</Word>\n      <Word>partition_parameters</Word>\n      <Word>partition_range_values</Word>\n      <Word>partition_schemes</Word>\n      <Word>partitions</Word>\n      <Word>periods</Word>\n      <Word>plan_guides</Word>\n      <Word>plan_persist_context_settings</Word>\n      <Word>plan_persist_plan</Word>\n      <Word>plan_persist_query_text</Word>\n      <Word>plan_persist_query</Word>\n      <Word>plan_persist_runtime_stats_interval</Word>\n      <Word>plan_persist_runtime_stats</Word>\n      <Word>procedures</Word>\n      <Word>query_context_settings</Word>\n      <Word>query_store_plan</Word>\n      <Word>query_store_query_text</Word>\n      <Word>query_store_query</Word>\n      <Word>query_store_runtime_stats_interval</Word>\n      <Word>query_store_runtime_stats</Word>\n      <Word>queue_messages_1003150619</Word>\n      <Word>queue_messages_1035150733</Word>\n      <Word>queue_messages_1067150847</Word>\n      <Word>REFERENTIAL_CONSTRAINTS</Word>\n      <Word>registered_search_properties</Word>\n      <Word>registered_search_property_lists</Word>\n      <Word>remote_data_archive_databases</Word>\n      <Word>remote_data_archive_tables</Word>\n      <Word>remote_logins</Word>\n      <Word>remote_service_bindings</Word>\n      <Word>resource_governor_configuration</Word>\n      <Word>resource_governor_external_resource_pool_affinity</Word>\n      <Word>resource_governor_external_resource_pools</Word>\n      <Word>resource_governor_resource_pool_affinity</Word>\n      <Word>resource_governor_resource_pools</Word>\n      <Word>resource_governor_workload_groups</Word>\n      <Word>routes</Word>\n      <Word>ROUTINE_COLUMNS</Word>\n      <Word>ROUTINES</Word>\n      <Word>schemas</Word>\n      <Word>SCHEMATA</Word>\n      <Word>securable_classes</Word>\n      <Word>security_policies</Word>\n      <Word>security_predicates</Word>\n      <Word>selective_xml_index_namespaces</Word>\n      <Word>selective_xml_index_paths</Word>\n      <Word>sequences</Word>\n      <Word>SEQUENCES</Word>\n      <Word>server_assembly_modules</Word>\n      <Word>server_audit_specification_details</Word>\n      <Word>server_audit_specifications</Word>\n      <Word>server_audits</Word>\n      <Word>server_event_notifications</Word>\n      <Word>server_event_session_actions</Word>\n      <Word>server_event_session_events</Word>\n      <Word>server_event_session_fields</Word>\n      <Word>server_event_session_targets</Word>\n      <Word>server_event_sessions</Word>\n      <Word>server_events</Word>\n      <Word>server_file_audits</Word>\n      <Word>server_permissions</Word>\n      <Word>server_principal_credentials</Word>\n      <Word>server_principals</Word>\n      <Word>server_role_members</Word>\n      <Word>server_sql_modules</Word>\n      <Word>server_trigger_events</Word>\n      <Word>server_triggers</Word>\n      <Word>servers</Word>\n      <Word>service_broker_endpoints</Word>\n      <Word>service_contract_message_usages</Word>\n      <Word>service_contract_usages</Word>\n      <Word>service_contracts</Word>\n      <Word>service_message_types</Word>\n      <Word>service_queue_usages</Word>\n      <Word>service_queues</Word>\n      <Word>services</Word>\n      <Word>soap_endpoints</Word>\n      <Word>sp_add_agent_parameter</Word>\n      <Word>sp_add_agent_profile</Word>\n      <Word>sp_add_data_file_recover_suspect_db</Word>\n      <Word>sp_add_log_file_recover_suspect_db</Word>\n      <Word>sp_add_log_shipping_alert_job</Word>\n      <Word>sp_add_log_shipping_primary_database</Word>\n      <Word>sp_add_log_shipping_primary_secondary</Word>\n      <Word>sp_add_log_shipping_secondary_database</Word>\n      <Word>sp_add_log_shipping_secondary_primary</Word>\n      <Word>sp_addapprole</Word>\n      <Word>sp_addarticle</Word>\n      <Word>sp_adddatatype</Word>\n      <Word>sp_adddatatypemapping</Word>\n      <Word>sp_adddistpublisher</Word>\n      <Word>sp_adddistributiondb</Word>\n      <Word>sp_adddistributor</Word>\n      <Word>sp_adddynamicsnapshot_job</Word>\n      <Word>sp_addextendedproc</Word>\n      <Word>sp_addextendedproperty</Word>\n      <Word>sp_AddFunctionalUnitToComponent</Word>\n      <Word>sp_addlinkedserver</Word>\n      <Word>sp_addlinkedsrvlogin</Word>\n      <Word>sp_addlogin</Word>\n      <Word>sp_addlogreader_agent</Word>\n      <Word>sp_addmergealternatepublisher</Word>\n      <Word>sp_addmergearticle</Word>\n      <Word>sp_addmergefilter</Word>\n      <Word>sp_addmergelogsettings</Word>\n      <Word>sp_addmergepartition</Word>\n      <Word>sp_addmergepublication</Word>\n      <Word>sp_addmergepullsubscription_agent</Word>\n      <Word>sp_addmergepullsubscription</Word>\n      <Word>sp_addmergepushsubscription_agent</Word>\n      <Word>sp_addmergesubscription</Word>\n      <Word>sp_addmessage</Word>\n      <Word>sp_addpublication_snapshot</Word>\n      <Word>sp_addpublication</Word>\n      <Word>sp_addpullsubscription_agent</Word>\n      <Word>sp_addpullsubscription</Word>\n      <Word>sp_addpushsubscription_agent</Word>\n      <Word>sp_addqreader_agent</Word>\n      <Word>sp_addqueued_artinfo</Word>\n      <Word>sp_addremotelogin</Word>\n      <Word>sp_addrole</Word>\n      <Word>sp_addrolemember</Word>\n      <Word>sp_addscriptexec</Word>\n      <Word>sp_addserver</Word>\n      <Word>sp_addsrvrolemember</Word>\n      <Word>sp_addsubscriber_schedule</Word>\n      <Word>sp_addsubscriber</Word>\n      <Word>sp_addsubscription</Word>\n      <Word>sp_addsynctriggers</Word>\n      <Word>sp_addsynctriggerscore</Word>\n      <Word>sp_addtabletocontents</Word>\n      <Word>sp_addtype</Word>\n      <Word>sp_addumpdevice</Word>\n      <Word>sp_adduser</Word>\n      <Word>sp_adjustpublisheridentityrange</Word>\n      <Word>sp_altermessage</Word>\n      <Word>sp_approlepassword</Word>\n      <Word>sp_article_validation</Word>\n      <Word>sp_articlecolumn</Word>\n      <Word>sp_articlefilter</Word>\n      <Word>sp_articleview</Word>\n      <Word>sp_assemblies_rowset_rmt</Word>\n      <Word>sp_assemblies_rowset</Word>\n      <Word>sp_assemblies_rowset2</Word>\n      <Word>sp_assembly_dependencies_rowset_rmt</Word>\n      <Word>sp_assembly_dependencies_rowset</Word>\n      <Word>sp_assembly_dependencies_rowset2</Word>\n      <Word>sp_attach_db</Word>\n      <Word>sp_attach_single_file_db</Word>\n      <Word>sp_attachsubscription</Word>\n      <Word>sp_audit_write</Word>\n      <Word>sp_autostats</Word>\n      <Word>sp_availability_group_command_internal</Word>\n      <Word>sp_bcp_dbcmptlevel</Word>\n      <Word>sp_begin_parallel_nested_tran</Word>\n      <Word>sp_bindefault</Word>\n      <Word>sp_bindrule</Word>\n      <Word>sp_bindsession</Word>\n      <Word>sp_browsemergesnapshotfolder</Word>\n      <Word>sp_browsereplcmds</Word>\n      <Word>sp_browsesnapshotfolder</Word>\n      <Word>sp_can_tlog_be_applied</Word>\n      <Word>sp_catalogs_rowset_rmt</Word>\n      <Word>sp_catalogs_rowset</Word>\n      <Word>sp_catalogs_rowset2</Word>\n      <Word>sp_catalogs</Word>\n      <Word>sp_cdc_add_job</Word>\n      <Word>sp_cdc_change_job</Word>\n      <Word>sp_cdc_cleanup_change_table</Word>\n      <Word>sp_cdc_dbsnapshotLSN</Word>\n      <Word>sp_cdc_disable_db</Word>\n      <Word>sp_cdc_disable_table</Word>\n      <Word>sp_cdc_drop_job</Word>\n      <Word>sp_cdc_enable_db</Word>\n      <Word>sp_cdc_enable_table</Word>\n      <Word>sp_cdc_generate_wrapper_function</Word>\n      <Word>sp_cdc_get_captured_columns</Word>\n      <Word>sp_cdc_get_ddl_history</Word>\n      <Word>sp_cdc_help_change_data_capture</Word>\n      <Word>sp_cdc_help_jobs</Word>\n      <Word>sp_cdc_restoredb</Word>\n      <Word>sp_cdc_scan</Word>\n      <Word>sp_cdc_start_job</Word>\n      <Word>sp_cdc_stop_job</Word>\n      <Word>sp_cdc_vupgrade_databases</Word>\n      <Word>sp_cdc_vupgrade</Word>\n      <Word>sp_certify_removable</Word>\n      <Word>sp_change_agent_parameter</Word>\n      <Word>sp_change_agent_profile</Word>\n      <Word>sp_change_log_shipping_primary_database</Word>\n      <Word>sp_change_log_shipping_secondary_database</Word>\n      <Word>sp_change_log_shipping_secondary_primary</Word>\n      <Word>sp_change_subscription_properties</Word>\n      <Word>sp_change_tracking_waitforchanges</Word>\n      <Word>sp_change_users_login</Word>\n      <Word>sp_changearticle</Word>\n      <Word>sp_changearticlecolumndatatype</Word>\n      <Word>sp_changedbowner</Word>\n      <Word>sp_changedistpublisher</Word>\n      <Word>sp_changedistributiondb</Word>\n      <Word>sp_changedistributor_password</Word>\n      <Word>sp_changedistributor_property</Word>\n      <Word>sp_changedynamicsnapshot_job</Word>\n      <Word>sp_changelogreader_agent</Word>\n      <Word>sp_changemergearticle</Word>\n      <Word>sp_changemergefilter</Word>\n      <Word>sp_changemergelogsettings</Word>\n      <Word>sp_changemergepublication</Word>\n      <Word>sp_changemergepullsubscription</Word>\n      <Word>sp_changemergesubscription</Word>\n      <Word>sp_changeobjectowner</Word>\n      <Word>sp_changepublication_snapshot</Word>\n      <Word>sp_changepublication</Word>\n      <Word>sp_changeqreader_agent</Word>\n      <Word>sp_changereplicationserverpasswords</Word>\n      <Word>sp_changesubscriber_schedule</Word>\n      <Word>sp_changesubscriber</Word>\n      <Word>sp_changesubscription</Word>\n      <Word>sp_changesubscriptiondtsinfo</Word>\n      <Word>sp_changesubstatus</Word>\n      <Word>sp_check_constbytable_rowset</Word>\n      <Word>sp_check_constbytable_rowset2</Word>\n      <Word>sp_check_constraints_rowset</Word>\n      <Word>sp_check_constraints_rowset2</Word>\n      <Word>sp_check_dynamic_filters</Word>\n      <Word>sp_check_for_sync_trigger</Word>\n      <Word>sp_check_join_filter</Word>\n      <Word>sp_check_log_shipping_monitor_alert</Word>\n      <Word>sp_check_publication_access</Word>\n      <Word>sp_check_removable</Word>\n      <Word>sp_check_subset_filter</Word>\n      <Word>sp_check_sync_trigger</Word>\n      <Word>sp_checkinvalidivarticle</Word>\n      <Word>sp_checkOraclepackageversion</Word>\n      <Word>sp_clean_db_file_free_space</Word>\n      <Word>sp_clean_db_free_space</Word>\n      <Word>sp_cleanmergelogfiles</Word>\n      <Word>sp_cleanup_log_shipping_history</Word>\n      <Word>sp_cleanup_temporal_history</Word>\n      <Word>sp_cleanupdbreplication</Word>\n      <Word>sp_column_privileges_ex</Word>\n      <Word>sp_column_privileges_rowset_rmt</Word>\n      <Word>sp_column_privileges_rowset</Word>\n      <Word>sp_column_privileges_rowset2</Word>\n      <Word>sp_column_privileges</Word>\n      <Word>sp_columns_100_rowset</Word>\n      <Word>sp_columns_100_rowset2</Word>\n      <Word>sp_columns_100</Word>\n      <Word>sp_columns_90_rowset_rmt</Word>\n      <Word>sp_columns_90_rowset</Word>\n      <Word>sp_columns_90_rowset2</Word>\n      <Word>sp_columns_90</Word>\n      <Word>sp_columns_ex_100</Word>\n      <Word>sp_columns_ex_90</Word>\n      <Word>sp_columns_ex</Word>\n      <Word>sp_columns_managed</Word>\n      <Word>sp_columns_rowset_rmt</Word>\n      <Word>sp_columns_rowset</Word>\n      <Word>sp_columns_rowset2</Word>\n      <Word>sp_columns</Word>\n      <Word>sp_commit_parallel_nested_tran</Word>\n      <Word>sp_configure_peerconflictdetection</Word>\n      <Word>sp_configure</Word>\n      <Word>sp_constr_col_usage_rowset</Word>\n      <Word>sp_constr_col_usage_rowset2</Word>\n      <Word>sp_control_dbmasterkey_password</Word>\n      <Word>sp_control_plan_guide</Word>\n      <Word>sp_copymergesnapshot</Word>\n      <Word>sp_copysnapshot</Word>\n      <Word>sp_copysubscription</Word>\n      <Word>sp_create_plan_guide_from_handle</Word>\n      <Word>sp_create_plan_guide</Word>\n      <Word>sp_create_removable</Word>\n      <Word>sp_createmergepalrole</Word>\n      <Word>sp_createorphan</Word>\n      <Word>sp_createstats</Word>\n      <Word>sp_createtranpalrole</Word>\n      <Word>sp_cursor_list</Word>\n      <Word>sp_cursor</Word>\n      <Word>sp_cursorclose</Word>\n      <Word>sp_cursorexecute</Word>\n      <Word>sp_cursorfetch</Word>\n      <Word>sp_cursoropen</Word>\n      <Word>sp_cursoroption</Word>\n      <Word>sp_cursorprepare</Word>\n      <Word>sp_cursorprepexec</Word>\n      <Word>sp_cursorunprepare</Word>\n      <Word>sp_cycle_errorlog</Word>\n      <Word>sp_databases</Word>\n      <Word>sp_datatype_info_100</Word>\n      <Word>sp_datatype_info_90</Word>\n      <Word>sp_datatype_info</Word>\n      <Word>sp_db_ebcdic277_2</Word>\n      <Word>sp_db_increased_partitions</Word>\n      <Word>sp_db_selective_xml_index</Word>\n      <Word>sp_db_vardecimal_storage_format</Word>\n      <Word>sp_dbcmptlevel</Word>\n      <Word>sp_dbfixedrolepermission</Word>\n      <Word>sp_dbmmonitoraddmonitoring</Word>\n      <Word>sp_dbmmonitorchangealert</Word>\n      <Word>sp_dbmmonitorchangemonitoring</Word>\n      <Word>sp_dbmmonitordropalert</Word>\n      <Word>sp_dbmmonitordropmonitoring</Word>\n      <Word>sp_dbmmonitorhelpalert</Word>\n      <Word>sp_dbmmonitorhelpmonitoring</Word>\n      <Word>sp_dbmmonitorresults</Word>\n      <Word>sp_dbmmonitorupdate</Word>\n      <Word>sp_dbremove</Word>\n      <Word>sp_ddopen</Word>\n      <Word>sp_defaultdb</Word>\n      <Word>sp_defaultlanguage</Word>\n      <Word>sp_delete_backup_file_snapshot</Word>\n      <Word>sp_delete_backup</Word>\n      <Word>sp_delete_http_namespace_reservation</Word>\n      <Word>sp_delete_log_shipping_alert_job</Word>\n      <Word>sp_delete_log_shipping_primary_database</Word>\n      <Word>sp_delete_log_shipping_primary_secondary</Word>\n      <Word>sp_delete_log_shipping_secondary_database</Word>\n      <Word>sp_delete_log_shipping_secondary_primary</Word>\n      <Word>sp_deletemergeconflictrow</Word>\n      <Word>sp_deletepeerrequesthistory</Word>\n      <Word>sp_deletetracertokenhistory</Word>\n      <Word>sp_denylogin</Word>\n      <Word>sp_depends</Word>\n      <Word>sp_describe_cursor_columns</Word>\n      <Word>sp_describe_cursor_tables</Word>\n      <Word>sp_describe_cursor</Word>\n      <Word>sp_describe_first_result_set</Word>\n      <Word>sp_describe_parameter_encryption</Word>\n      <Word>sp_describe_undeclared_parameters</Word>\n      <Word>sp_detach_db</Word>\n      <Word>sp_disableagentoffload</Word>\n      <Word>sp_distcounters</Word>\n      <Word>sp_drop_agent_parameter</Word>\n      <Word>sp_drop_agent_profile</Word>\n      <Word>sp_dropanonymousagent</Word>\n      <Word>sp_dropanonymoussubscription</Word>\n      <Word>sp_dropapprole</Word>\n      <Word>sp_droparticle</Word>\n      <Word>sp_dropdatatypemapping</Word>\n      <Word>sp_dropdevice</Word>\n      <Word>sp_dropdistpublisher</Word>\n      <Word>sp_dropdistributiondb</Word>\n      <Word>sp_dropdistributor</Word>\n      <Word>sp_dropdynamicsnapshot_job</Word>\n      <Word>sp_dropextendedproc</Word>\n      <Word>sp_dropextendedproperty</Word>\n      <Word>sp_droplinkedsrvlogin</Word>\n      <Word>sp_droplogin</Word>\n      <Word>sp_dropmergealternatepublisher</Word>\n      <Word>sp_dropmergearticle</Word>\n      <Word>sp_dropmergefilter</Word>\n      <Word>sp_dropmergelogsettings</Word>\n      <Word>sp_dropmergepartition</Word>\n      <Word>sp_dropmergepublication</Word>\n      <Word>sp_dropmergepullsubscription</Word>\n      <Word>sp_dropmergesubscription</Word>\n      <Word>sp_dropmessage</Word>\n      <Word>sp_droporphans</Word>\n      <Word>sp_droppublication</Word>\n      <Word>sp_droppublisher</Word>\n      <Word>sp_droppullsubscription</Word>\n      <Word>sp_dropremotelogin</Word>\n      <Word>sp_dropreplsymmetrickey</Word>\n      <Word>sp_droprole</Word>\n      <Word>sp_droprolemember</Word>\n      <Word>sp_dropserver</Word>\n      <Word>sp_dropsrvrolemember</Word>\n      <Word>sp_dropsubscriber</Word>\n      <Word>sp_dropsubscription</Word>\n      <Word>sp_droptype</Word>\n      <Word>sp_dropuser</Word>\n      <Word>sp_dsninfo</Word>\n      <Word>sp_enable_heterogeneous_subscription</Word>\n      <Word>sp_enable_sql_debug</Word>\n      <Word>sp_enableagentoffload</Word>\n      <Word>sp_enum_oledb_providers</Word>\n      <Word>sp_enumcustomresolvers</Word>\n      <Word>sp_enumdsn</Word>\n      <Word>sp_enumeratependingschemachanges</Word>\n      <Word>sp_enumerrorlogs</Word>\n      <Word>sp_enumfullsubscribers</Word>\n      <Word>sp_enumoledbdatasources</Word>\n      <Word>sp_estimate_data_compression_savings</Word>\n      <Word>sp_estimated_rowsize_reduction_for_vardecimal</Word>\n      <Word>sp_execute_external_script</Word>\n      <Word>sp_execute</Word>\n      <Word>sp_executesql</Word>\n      <Word>sp_expired_subscription_cleanup</Word>\n      <Word>sp_filestream_force_garbage_collection</Word>\n      <Word>sp_filestream_recalculate_container_size</Word>\n      <Word>sp_firstonly_bitmap</Word>\n      <Word>sp_fkeys</Word>\n      <Word>sp_flush_commit_table_on_demand</Word>\n      <Word>sp_flush_commit_table</Word>\n      <Word>sp_flush_CT_internal_table_on_demand</Word>\n      <Word>sp_flush_log</Word>\n      <Word>sp_foreign_keys_rowset_rmt</Word>\n      <Word>sp_foreign_keys_rowset</Word>\n      <Word>sp_foreign_keys_rowset2</Word>\n      <Word>sp_foreign_keys_rowset3</Word>\n      <Word>sp_foreignkeys</Word>\n      <Word>sp_fulltext_catalog</Word>\n      <Word>sp_fulltext_column</Word>\n      <Word>sp_fulltext_database</Word>\n      <Word>sp_fulltext_getdata</Word>\n      <Word>sp_fulltext_keymappings</Word>\n      <Word>sp_fulltext_load_thesaurus_file</Word>\n      <Word>sp_fulltext_pendingchanges</Word>\n      <Word>sp_fulltext_recycle_crawl_log</Word>\n      <Word>sp_fulltext_semantic_register_language_statistics_db</Word>\n      <Word>sp_fulltext_semantic_unregister_language_statistics_db</Word>\n      <Word>sp_fulltext_service</Word>\n      <Word>sp_fulltext_table</Word>\n      <Word>sp_generate_agent_parameter</Word>\n      <Word>sp_generatefilters</Word>\n      <Word>sp_get_database_scoped_credential</Word>\n      <Word>sp_get_distributor</Word>\n      <Word>sp_get_job_status_mergesubscription_agent</Word>\n      <Word>sp_get_mergepublishedarticleproperties</Word>\n      <Word>sp_get_Oracle_publisher_metadata</Word>\n      <Word>sp_get_query_template</Word>\n      <Word>sp_get_redirected_publisher</Word>\n      <Word>sp_getagentparameterlist</Word>\n      <Word>sp_getapplock</Word>\n      <Word>sp_getbindtoken</Word>\n      <Word>sp_getdefaultdatatypemapping</Word>\n      <Word>sp_getmergedeletetype</Word>\n      <Word>sp_getProcessorUsage</Word>\n      <Word>sp_getpublisherlink</Word>\n      <Word>sp_getqueuedarticlesynctraninfo</Word>\n      <Word>sp_getqueuedrows</Word>\n      <Word>sp_getschemalock</Word>\n      <Word>sp_getsqlqueueversion</Word>\n      <Word>sp_getsubscription_status_hsnapshot</Word>\n      <Word>sp_getsubscriptiondtspackagename</Word>\n      <Word>sp_gettopologyinfo</Word>\n      <Word>sp_getVolumeFreeSpace</Word>\n      <Word>sp_grant_publication_access</Word>\n      <Word>sp_grantdbaccess</Word>\n      <Word>sp_grantlogin</Word>\n      <Word>sp_help_agent_default</Word>\n      <Word>sp_help_agent_parameter</Word>\n      <Word>sp_help_agent_profile</Word>\n      <Word>sp_help_datatype_mapping</Word>\n      <Word>sp_help_fulltext_catalog_components</Word>\n      <Word>sp_help_fulltext_catalogs_cursor</Word>\n      <Word>sp_help_fulltext_catalogs</Word>\n      <Word>sp_help_fulltext_columns_cursor</Word>\n      <Word>sp_help_fulltext_columns</Word>\n      <Word>sp_help_fulltext_system_components</Word>\n      <Word>sp_help_fulltext_tables_cursor</Word>\n      <Word>sp_help_fulltext_tables</Word>\n      <Word>sp_help_log_shipping_alert_job</Word>\n      <Word>sp_help_log_shipping_monitor_primary</Word>\n      <Word>sp_help_log_shipping_monitor_secondary</Word>\n      <Word>sp_help_log_shipping_monitor</Word>\n      <Word>sp_help_log_shipping_primary_database</Word>\n      <Word>sp_help_log_shipping_primary_secondary</Word>\n      <Word>sp_help_log_shipping_secondary_database</Word>\n      <Word>sp_help_log_shipping_secondary_primary</Word>\n      <Word>sp_help_peerconflictdetection</Word>\n      <Word>sp_help_publication_access</Word>\n      <Word>sp_help_spatial_geography_histogram</Word>\n      <Word>sp_help_spatial_geography_index_xml</Word>\n      <Word>sp_help_spatial_geography_index</Word>\n      <Word>sp_help_spatial_geometry_histogram</Word>\n      <Word>sp_help_spatial_geometry_index_xml</Word>\n      <Word>sp_help_spatial_geometry_index</Word>\n      <Word>sp_help</Word>\n      <Word>sp_helpallowmerge_publication</Word>\n      <Word>sp_helparticle</Word>\n      <Word>sp_helparticlecolumns</Word>\n      <Word>sp_helparticledts</Word>\n      <Word>sp_helpconstraint</Word>\n      <Word>sp_helpdatatypemap</Word>\n      <Word>sp_helpdb</Word>\n      <Word>sp_helpdbfixedrole</Word>\n      <Word>sp_helpdevice</Word>\n      <Word>sp_helpdistpublisher</Word>\n      <Word>sp_helpdistributiondb</Word>\n      <Word>sp_helpdistributor_properties</Word>\n      <Word>sp_helpdistributor</Word>\n      <Word>sp_helpdynamicsnapshot_job</Word>\n      <Word>sp_helpextendedproc</Word>\n      <Word>sp_helpfile</Word>\n      <Word>sp_helpfilegroup</Word>\n      <Word>sp_helpindex</Word>\n      <Word>sp_helplanguage</Word>\n      <Word>sp_helplinkedsrvlogin</Word>\n      <Word>sp_helplogins</Word>\n      <Word>sp_helplogreader_agent</Word>\n      <Word>sp_helpmergealternatepublisher</Word>\n      <Word>sp_helpmergearticle</Word>\n      <Word>sp_helpmergearticlecolumn</Word>\n      <Word>sp_helpmergearticleconflicts</Word>\n      <Word>sp_helpmergeconflictrows</Word>\n      <Word>sp_helpmergedeleteconflictrows</Word>\n      <Word>sp_helpmergefilter</Word>\n      <Word>sp_helpmergelogfiles</Word>\n      <Word>sp_helpmergelogfileswithdata</Word>\n      <Word>sp_helpmergelogsettings</Word>\n      <Word>sp_helpmergepartition</Word>\n      <Word>sp_helpmergepublication</Word>\n      <Word>sp_helpmergepullsubscription</Word>\n      <Word>sp_helpmergesubscription</Word>\n      <Word>sp_helpntgroup</Word>\n      <Word>sp_helppeerrequests</Word>\n      <Word>sp_helppeerresponses</Word>\n      <Word>sp_helppublication_snapshot</Word>\n      <Word>sp_helppublication</Word>\n      <Word>sp_helppublicationsync</Word>\n      <Word>sp_helppullsubscription</Word>\n      <Word>sp_helpqreader_agent</Word>\n      <Word>sp_helpremotelogin</Word>\n      <Word>sp_helpreplfailovermode</Word>\n      <Word>sp_helpreplicationdb</Word>\n      <Word>sp_helpreplicationdboption</Word>\n      <Word>sp_helpreplicationoption</Word>\n      <Word>sp_helprole</Word>\n      <Word>sp_helprolemember</Word>\n      <Word>sp_helprotect</Word>\n      <Word>sp_helpserver</Word>\n      <Word>sp_helpsort</Word>\n      <Word>sp_helpsrvrole</Word>\n      <Word>sp_helpsrvrolemember</Word>\n      <Word>sp_helpstats</Word>\n      <Word>sp_helpsubscriberinfo</Word>\n      <Word>sp_helpsubscription_properties</Word>\n      <Word>sp_helpsubscription</Word>\n      <Word>sp_helpsubscriptionerrors</Word>\n      <Word>sp_helptext</Word>\n      <Word>sp_helptracertokenhistory</Word>\n      <Word>sp_helptracertokens</Word>\n      <Word>sp_helptrigger</Word>\n      <Word>sp_helpuser</Word>\n      <Word>sp_helpxactsetjob</Word>\n      <Word>sp_http_generate_wsdl_complex</Word>\n      <Word>sp_http_generate_wsdl_defaultcomplexorsimple</Word>\n      <Word>sp_http_generate_wsdl_defaultsimpleorcomplex</Word>\n      <Word>sp_http_generate_wsdl_simple</Word>\n      <Word>sp_identitycolumnforreplication</Word>\n      <Word>sp_IH_LR_GetCacheData</Word>\n      <Word>sp_IHadd_sync_command</Word>\n      <Word>sp_IHarticlecolumn</Word>\n      <Word>sp_IHget_loopback_detection</Word>\n      <Word>sp_IHScriptIdxFile</Word>\n      <Word>sp_IHScriptSchFile</Word>\n      <Word>sp_IHValidateRowFilter</Word>\n      <Word>sp_IHXactSetJob</Word>\n      <Word>sp_indexcolumns_managed</Word>\n      <Word>sp_indexes_100_rowset</Word>\n      <Word>sp_indexes_100_rowset2</Word>\n      <Word>sp_indexes_90_rowset_rmt</Word>\n      <Word>sp_indexes_90_rowset</Word>\n      <Word>sp_indexes_90_rowset2</Word>\n      <Word>sp_indexes_managed</Word>\n      <Word>sp_indexes_rowset_rmt</Word>\n      <Word>sp_indexes_rowset</Word>\n      <Word>sp_indexes_rowset2</Word>\n      <Word>sp_indexes</Word>\n      <Word>sp_indexoption</Word>\n      <Word>sp_invalidate_textptr</Word>\n      <Word>sp_is_makegeneration_needed</Word>\n      <Word>sp_ivindexhasnullcols</Word>\n      <Word>sp_kill_filestream_non_transacted_handles</Word>\n      <Word>sp_kill_oldest_transaction_on_secondary</Word>\n      <Word>sp_lightweightmergemetadataretentioncleanup</Word>\n      <Word>sp_link_publication</Word>\n      <Word>sp_linkedservers_rowset</Word>\n      <Word>sp_linkedservers_rowset2</Word>\n      <Word>sp_linkedservers</Word>\n      <Word>sp_lock</Word>\n      <Word>sp_logshippinginstallmetadata</Word>\n      <Word>sp_lookupcustomresolver</Word>\n      <Word>sp_mapdown_bitmap</Word>\n      <Word>sp_markpendingschemachange</Word>\n      <Word>sp_marksubscriptionvalidation</Word>\n      <Word>sp_memory_optimized_cs_migration</Word>\n      <Word>sp_mergearticlecolumn</Word>\n      <Word>sp_mergecleanupmetadata</Word>\n      <Word>sp_mergedummyupdate</Word>\n      <Word>sp_mergemetadataretentioncleanup</Word>\n      <Word>sp_mergesubscription_cleanup</Word>\n      <Word>sp_mergesubscriptionsummary</Word>\n      <Word>sp_migrate_user_to_contained</Word>\n      <Word>sp_monitor</Word>\n      <Word>sp_MS_marksystemobject</Word>\n      <Word>sp_MS_replication_installed</Word>\n      <Word>sp_MSacquireHeadofQueueLock</Word>\n      <Word>sp_MSacquireserverresourcefordynamicsnapshot</Word>\n      <Word>sp_MSacquireSlotLock</Word>\n      <Word>sp_MSacquiresnapshotdeliverysessionlock</Word>\n      <Word>sp_MSactivate_auto_sub</Word>\n      <Word>sp_MSactivatelogbasedarticleobject</Word>\n      <Word>sp_MSactivateprocedureexecutionarticleobject</Word>\n      <Word>sp_MSadd_anonymous_agent</Word>\n      <Word>sp_MSadd_article</Word>\n      <Word>sp_MSadd_compensating_cmd</Word>\n      <Word>sp_MSadd_distribution_agent</Word>\n      <Word>sp_MSadd_distribution_history</Word>\n      <Word>sp_MSadd_dynamic_snapshot_location</Word>\n      <Word>sp_MSadd_filteringcolumn</Word>\n      <Word>sp_MSadd_log_shipping_error_detail</Word>\n      <Word>sp_MSadd_log_shipping_history_detail</Word>\n      <Word>sp_MSadd_logreader_agent</Word>\n      <Word>sp_MSadd_logreader_history</Word>\n      <Word>sp_MSadd_merge_agent</Word>\n      <Word>sp_MSadd_merge_anonymous_agent</Word>\n      <Word>sp_MSadd_merge_history</Word>\n      <Word>sp_MSadd_merge_history90</Word>\n      <Word>sp_MSadd_merge_subscription</Word>\n      <Word>sp_MSadd_mergereplcommand</Word>\n      <Word>sp_MSadd_mergesubentry_indistdb</Word>\n      <Word>sp_MSadd_publication</Word>\n      <Word>sp_MSadd_qreader_agent</Word>\n      <Word>sp_MSadd_qreader_history</Word>\n      <Word>sp_MSadd_repl_alert</Word>\n      <Word>sp_MSadd_repl_command</Word>\n      <Word>sp_MSadd_repl_commands27hp</Word>\n      <Word>sp_MSadd_repl_error</Word>\n      <Word>sp_MSadd_replcmds_mcit</Word>\n      <Word>sp_MSadd_replmergealert</Word>\n      <Word>sp_MSadd_snapshot_agent</Word>\n      <Word>sp_MSadd_snapshot_history</Word>\n      <Word>sp_MSadd_subscriber_info</Word>\n      <Word>sp_MSadd_subscriber_schedule</Word>\n      <Word>sp_MSadd_subscription_3rd</Word>\n      <Word>sp_MSadd_subscription</Word>\n      <Word>sp_MSadd_tracer_history</Word>\n      <Word>sp_MSadd_tracer_token</Word>\n      <Word>sp_MSaddanonymousreplica</Word>\n      <Word>sp_MSadddynamicsnapshotjobatdistributor</Word>\n      <Word>sp_MSaddguidcolumn</Word>\n      <Word>sp_MSaddguidindex</Word>\n      <Word>sp_MSaddinitialarticle</Word>\n      <Word>sp_MSaddinitialpublication</Word>\n      <Word>sp_MSaddinitialschemaarticle</Word>\n      <Word>sp_MSaddinitialsubscription</Word>\n      <Word>sp_MSaddlightweightmergearticle</Word>\n      <Word>sp_MSaddmergedynamicsnapshotjob</Word>\n      <Word>sp_MSaddmergetriggers_from_template</Word>\n      <Word>sp_MSaddmergetriggers_internal</Word>\n      <Word>sp_MSaddmergetriggers</Word>\n      <Word>sp_MSaddpeerlsn</Word>\n      <Word>sp_MSaddsubscriptionarticles</Word>\n      <Word>sp_MSadjust_pub_identity</Word>\n      <Word>sp_MSagent_retry_stethoscope</Word>\n      <Word>sp_MSagent_stethoscope</Word>\n      <Word>sp_MSallocate_new_identity_range</Word>\n      <Word>sp_MSalreadyhavegeneration</Word>\n      <Word>sp_MSanonymous_status</Word>\n      <Word>sp_MSarticlecleanup</Word>\n      <Word>sp_MSbrowsesnapshotfolder</Word>\n      <Word>sp_MScache_agent_parameter</Word>\n      <Word>sp_MScdc_capture_job</Word>\n      <Word>sp_MScdc_cleanup_job</Word>\n      <Word>sp_MScdc_db_ddl_event</Word>\n      <Word>sp_MScdc_ddl_event</Word>\n      <Word>sp_MScdc_logddl</Word>\n      <Word>sp_MSchange_article</Word>\n      <Word>sp_MSchange_distribution_agent_properties</Word>\n      <Word>sp_MSchange_logreader_agent_properties</Word>\n      <Word>sp_MSchange_merge_agent_properties</Word>\n      <Word>sp_MSchange_mergearticle</Word>\n      <Word>sp_MSchange_mergepublication</Word>\n      <Word>sp_MSchange_originatorid</Word>\n      <Word>sp_MSchange_priority</Word>\n      <Word>sp_MSchange_publication</Word>\n      <Word>sp_MSchange_retention_period_unit</Word>\n      <Word>sp_MSchange_retention</Word>\n      <Word>sp_MSchange_snapshot_agent_properties</Word>\n      <Word>sp_MSchange_subscription_dts_info</Word>\n      <Word>sp_MSchangearticleresolver</Word>\n      <Word>sp_MSchangedynamicsnapshotjobatdistributor</Word>\n      <Word>sp_MSchangedynsnaplocationatdistributor</Word>\n      <Word>sp_MSchangeobjectowner</Word>\n      <Word>sp_MScheck_agent_instance</Word>\n      <Word>sp_MScheck_dropobject</Word>\n      <Word>sp_MScheck_Jet_Subscriber</Word>\n      <Word>sp_MScheck_logicalrecord_metadatamatch</Word>\n      <Word>sp_MScheck_merge_subscription_count</Word>\n      <Word>sp_MScheck_pub_identity</Word>\n      <Word>sp_MScheck_pull_access</Word>\n      <Word>sp_MScheck_snapshot_agent</Word>\n      <Word>sp_MScheck_subscription_expiry</Word>\n      <Word>sp_MScheck_subscription_partition</Word>\n      <Word>sp_MScheck_subscription</Word>\n      <Word>sp_MScheck_tran_retention</Word>\n      <Word>sp_MScheckexistsgeneration</Word>\n      <Word>sp_MScheckexistsrecguid</Word>\n      <Word>sp_MScheckfailedprevioussync</Word>\n      <Word>sp_MScheckidentityrange</Word>\n      <Word>sp_MScheckIsPubOfSub</Word>\n      <Word>sp_MSchecksharedagentforpublication</Word>\n      <Word>sp_MSchecksnapshotstatus</Word>\n      <Word>sp_MScleanup_agent_entry</Word>\n      <Word>sp_MScleanup_conflict</Word>\n      <Word>sp_MScleanup_publication_ADinfo</Word>\n      <Word>sp_MScleanup_subscription_distside_entry</Word>\n      <Word>sp_MScleanupdynamicsnapshotfolder</Word>\n      <Word>sp_MScleanupdynsnapshotvws</Word>\n      <Word>sp_MSCleanupForPullReinit</Word>\n      <Word>sp_MScleanupmergepublisher_internal</Word>\n      <Word>sp_MScleanupmergepublisher</Word>\n      <Word>sp_MSclear_dynamic_snapshot_location</Word>\n      <Word>sp_MSclearresetpartialsnapshotprogressbit</Word>\n      <Word>sp_MScomputelastsentgen</Word>\n      <Word>sp_MScomputemergearticlescreationorder</Word>\n      <Word>sp_MScomputemergeunresolvedrefs</Word>\n      <Word>sp_MSconflicttableexists</Word>\n      <Word>sp_MScreate_all_article_repl_views</Word>\n      <Word>sp_MScreate_article_repl_views</Word>\n      <Word>sp_MScreate_dist_tables</Word>\n      <Word>sp_MScreate_logical_record_views</Word>\n      <Word>sp_MScreate_sub_tables</Word>\n      <Word>sp_MScreate_tempgenhistorytable</Word>\n      <Word>sp_MScreatedisabledmltrigger</Word>\n      <Word>sp_MScreatedummygeneration</Word>\n      <Word>sp_MScreateglobalreplica</Word>\n      <Word>sp_MScreatelightweightinsertproc</Word>\n      <Word>sp_MScreatelightweightmultipurposeproc</Word>\n      <Word>sp_MScreatelightweightprocstriggersconstraints</Word>\n      <Word>sp_MScreatelightweightupdateproc</Word>\n      <Word>sp_MScreatemergedynamicsnapshot</Word>\n      <Word>sp_MScreateretry</Word>\n      <Word>sp_MSdbuseraccess</Word>\n      <Word>sp_MSdbuserpriv</Word>\n      <Word>sp_MSdefer_check</Word>\n      <Word>sp_MSdelete_tracer_history</Word>\n      <Word>sp_MSdeletefoldercontents</Word>\n      <Word>sp_MSdeletemetadataactionrequest</Word>\n      <Word>sp_MSdeletepeerconflictrow</Word>\n      <Word>sp_MSdeleteretry</Word>\n      <Word>sp_MSdeletetranconflictrow</Word>\n      <Word>sp_MSdelgenzero</Word>\n      <Word>sp_MSdelrow</Word>\n      <Word>sp_MSdelrowsbatch_downloadonly</Word>\n      <Word>sp_MSdelrowsbatch</Word>\n      <Word>sp_MSdelsubrows</Word>\n      <Word>sp_MSdelsubrowsbatch</Word>\n      <Word>sp_MSdependencies</Word>\n      <Word>sp_MSdetect_nonlogged_shutdown</Word>\n      <Word>sp_MSdetectinvalidpeerconfiguration</Word>\n      <Word>sp_MSdetectinvalidpeersubscription</Word>\n      <Word>sp_MSdist_activate_auto_sub</Word>\n      <Word>sp_MSdist_adjust_identity</Word>\n      <Word>sp_MSdistpublisher_cleanup</Word>\n      <Word>sp_MSdistribution_counters</Word>\n      <Word>sp_MSdistributoravailable</Word>\n      <Word>sp_MSdodatabasesnapshotinitiation</Word>\n      <Word>sp_MSdopartialdatabasesnapshotinitiation</Word>\n      <Word>sp_MSdrop_6x_publication</Word>\n      <Word>sp_MSdrop_6x_replication_agent</Word>\n      <Word>sp_MSdrop_anonymous_entry</Word>\n      <Word>sp_MSdrop_article</Word>\n      <Word>sp_MSdrop_distribution_agent</Word>\n      <Word>sp_MSdrop_distribution_agentid_dbowner_proxy</Word>\n      <Word>sp_MSdrop_dynamic_snapshot_agent</Word>\n      <Word>sp_MSdrop_logreader_agent</Word>\n      <Word>sp_MSdrop_merge_agent</Word>\n      <Word>sp_MSdrop_merge_subscription</Word>\n      <Word>sp_MSdrop_publication</Word>\n      <Word>sp_MSdrop_qreader_history</Word>\n      <Word>sp_MSdrop_snapshot_agent</Word>\n      <Word>sp_MSdrop_snapshot_dirs</Word>\n      <Word>sp_MSdrop_subscriber_info</Word>\n      <Word>sp_MSdrop_subscription_3rd</Word>\n      <Word>sp_MSdrop_subscription</Word>\n      <Word>sp_MSdrop_tempgenhistorytable</Word>\n      <Word>sp_MSdroparticleconstraints</Word>\n      <Word>sp_MSdroparticletombstones</Word>\n      <Word>sp_MSdropconstraints</Word>\n      <Word>sp_MSdropdynsnapshotvws</Word>\n      <Word>sp_MSdropfkreferencingarticle</Word>\n      <Word>sp_MSdropmergearticle</Word>\n      <Word>sp_MSdropmergedynamicsnapshotjob</Word>\n      <Word>sp_MSdropobsoletearticle</Word>\n      <Word>sp_MSdropretry</Word>\n      <Word>sp_MSdroptemptable</Word>\n      <Word>sp_MSdummyupdate_logicalrecord</Word>\n      <Word>sp_MSdummyupdate</Word>\n      <Word>sp_MSdummyupdate90</Word>\n      <Word>sp_MSdummyupdatelightweight</Word>\n      <Word>sp_MSdynamicsnapshotjobexistsatdistributor</Word>\n      <Word>sp_MSenable_publication_for_het_sub</Word>\n      <Word>sp_MSensure_single_instance</Word>\n      <Word>sp_MSenum_distribution_s</Word>\n      <Word>sp_MSenum_distribution_sd</Word>\n      <Word>sp_MSenum_distribution</Word>\n      <Word>sp_MSenum_logicalrecord_changes</Word>\n      <Word>sp_MSenum_logreader_s</Word>\n      <Word>sp_MSenum_logreader_sd</Word>\n      <Word>sp_MSenum_logreader</Word>\n      <Word>sp_MSenum_merge_agent_properties</Word>\n      <Word>sp_MSenum_merge_s</Word>\n      <Word>sp_MSenum_merge_sd</Word>\n      <Word>sp_MSenum_merge_subscriptions_90_publication</Word>\n      <Word>sp_MSenum_merge_subscriptions_90_publisher</Word>\n      <Word>sp_MSenum_merge_subscriptions</Word>\n      <Word>sp_MSenum_merge</Word>\n      <Word>sp_MSenum_metadataaction_requests</Word>\n      <Word>sp_MSenum_qreader_s</Word>\n      <Word>sp_MSenum_qreader_sd</Word>\n      <Word>sp_MSenum_qreader</Word>\n      <Word>sp_MSenum_replication_agents</Word>\n      <Word>sp_MSenum_replication_job</Word>\n      <Word>sp_MSenum_replqueues</Word>\n      <Word>sp_MSenum_replsqlqueues</Word>\n      <Word>sp_MSenum_snapshot_s</Word>\n      <Word>sp_MSenum_snapshot_sd</Word>\n      <Word>sp_MSenum_snapshot</Word>\n      <Word>sp_MSenum_subscriptions</Word>\n      <Word>sp_MSenumallpublications</Word>\n      <Word>sp_MSenumallsubscriptions</Word>\n      <Word>sp_MSenumarticleslightweight</Word>\n      <Word>sp_MSenumchanges_belongtopartition</Word>\n      <Word>sp_MSenumchanges_notbelongtopartition</Word>\n      <Word>sp_MSenumchanges</Word>\n      <Word>sp_MSenumchangesdirect</Word>\n      <Word>sp_MSenumchangeslightweight</Word>\n      <Word>sp_MSenumcolumns</Word>\n      <Word>sp_MSenumcolumnslightweight</Word>\n      <Word>sp_MSenumdeletes_forpartition</Word>\n      <Word>sp_MSenumdeleteslightweight</Word>\n      <Word>sp_MSenumdeletesmetadata</Word>\n      <Word>sp_MSenumdistributionagentproperties</Word>\n      <Word>sp_MSenumerate_PAL</Word>\n      <Word>sp_MSenumgenerations</Word>\n      <Word>sp_MSenumgenerations90</Word>\n      <Word>sp_MSenumpartialchanges</Word>\n      <Word>sp_MSenumpartialchangesdirect</Word>\n      <Word>sp_MSenumpartialdeletes</Word>\n      <Word>sp_MSenumpubreferences</Word>\n      <Word>sp_MSenumreplicas</Word>\n      <Word>sp_MSenumreplicas90</Word>\n      <Word>sp_MSenumretries</Word>\n      <Word>sp_MSenumschemachange</Word>\n      <Word>sp_MSenumsubscriptions</Word>\n      <Word>sp_MSenumthirdpartypublicationvendornames</Word>\n      <Word>sp_MSestimatemergesnapshotworkload</Word>\n      <Word>sp_MSestimatesnapshotworkload</Word>\n      <Word>sp_MSevalsubscriberinfo</Word>\n      <Word>sp_MSevaluate_change_membership_for_all_articles_in_pubid</Word>\n      <Word>sp_MSevaluate_change_membership_for_pubid</Word>\n      <Word>sp_MSevaluate_change_membership_for_row</Word>\n      <Word>sp_MSexecwithlsnoutput</Word>\n      <Word>sp_MSfast_delete_trans</Word>\n      <Word>sp_MSfetchAdjustidentityrange</Word>\n      <Word>sp_MSfetchidentityrange</Word>\n      <Word>sp_MSfillupmissingcols</Word>\n      <Word>sp_MSfilterclause</Word>\n      <Word>sp_MSfix_6x_tasks</Word>\n      <Word>sp_MSfixlineageversions</Word>\n      <Word>sp_MSFixSubColumnBitmaps</Word>\n      <Word>sp_MSfixupbeforeimagetables</Word>\n      <Word>sp_MSflush_access_cache</Word>\n      <Word>sp_MSforce_drop_distribution_jobs</Word>\n      <Word>sp_MSforcereenumeration</Word>\n      <Word>sp_MSforeach_worker</Word>\n      <Word>sp_MSforeachdb</Word>\n      <Word>sp_MSforeachtable</Word>\n      <Word>sp_MSgenerateexpandproc</Word>\n      <Word>sp_MSget_agent_names</Word>\n      <Word>sp_MSget_attach_state</Word>\n      <Word>sp_MSget_DDL_after_regular_snapshot</Word>\n      <Word>sp_MSget_dynamic_snapshot_location</Word>\n      <Word>sp_MSget_identity_range_info</Word>\n      <Word>sp_MSget_jobstate</Word>\n      <Word>sp_MSget_last_transaction</Word>\n      <Word>sp_MSget_latest_peerlsn</Word>\n      <Word>sp_MSget_load_hint</Word>\n      <Word>sp_MSget_log_shipping_new_sessionid</Word>\n      <Word>sp_MSget_logicalrecord_lineage</Word>\n      <Word>sp_MSget_max_used_identity</Word>\n      <Word>sp_MSget_min_seqno</Word>\n      <Word>sp_MSget_MSmerge_rowtrack_colinfo</Word>\n      <Word>sp_MSget_new_xact_seqno</Word>\n      <Word>sp_MSget_oledbinfo</Word>\n      <Word>sp_MSget_partitionid_eval_proc</Word>\n      <Word>sp_MSget_publication_from_taskname</Word>\n      <Word>sp_MSget_publisher_rpc</Word>\n      <Word>sp_MSget_repl_cmds_anonymous</Word>\n      <Word>sp_MSget_repl_commands</Word>\n      <Word>sp_MSget_repl_error</Word>\n      <Word>sp_MSget_session_statistics</Word>\n      <Word>sp_MSget_shared_agent</Word>\n      <Word>sp_MSget_snapshot_history</Word>\n      <Word>sp_MSget_subscriber_partition_id</Word>\n      <Word>sp_MSget_subscription_dts_info</Word>\n      <Word>sp_MSget_subscription_guid</Word>\n      <Word>sp_MSget_synctran_commands</Word>\n      <Word>sp_MSget_type_wrapper</Word>\n      <Word>sp_MSgetagentoffloadinfo</Word>\n      <Word>sp_MSgetalertinfo</Word>\n      <Word>sp_MSgetalternaterecgens</Word>\n      <Word>sp_MSgetarticlereinitvalue</Word>\n      <Word>sp_MSgetchangecount</Word>\n      <Word>sp_MSgetconflictinsertproc</Word>\n      <Word>sp_MSgetconflicttablename</Word>\n      <Word>sp_MSGetCurrentPrincipal</Word>\n      <Word>sp_MSgetdatametadatabatch</Word>\n      <Word>sp_MSgetdbversion</Word>\n      <Word>sp_MSgetdynamicsnapshotapplock</Word>\n      <Word>sp_MSgetdynsnapvalidationtoken</Word>\n      <Word>sp_MSgetgenstatus4rows</Word>\n      <Word>sp_MSgetisvalidwindowsloginfromdistributor</Word>\n      <Word>sp_MSgetlastrecgen</Word>\n      <Word>sp_MSgetlastsentgen</Word>\n      <Word>sp_MSgetlastsentrecgens</Word>\n      <Word>sp_MSgetlastupdatedtime</Word>\n      <Word>sp_MSgetlightweightmetadatabatch</Word>\n      <Word>sp_MSgetmakegenerationapplock_90</Word>\n      <Word>sp_MSgetmakegenerationapplock</Word>\n      <Word>sp_MSgetmaxbcpgen</Word>\n      <Word>sp_MSgetmaxsnapshottimestamp</Word>\n      <Word>sp_MSgetmergeadminapplock</Word>\n      <Word>sp_MSgetmetadata_changedlogicalrecordmembers</Word>\n      <Word>sp_MSgetmetadatabatch</Word>\n      <Word>sp_MSgetmetadatabatch90</Word>\n      <Word>sp_MSgetmetadatabatch90new</Word>\n      <Word>sp_MSgetonerow</Word>\n      <Word>sp_MSgetonerowlightweight</Word>\n      <Word>sp_MSgetpeerconflictrow</Word>\n      <Word>sp_MSgetpeerlsns</Word>\n      <Word>sp_MSgetpeertopeercommands</Word>\n      <Word>sp_MSgetpeerwinnerrow</Word>\n      <Word>sp_MSgetpubinfo</Word>\n      <Word>sp_MSgetreplicainfo</Word>\n      <Word>sp_MSgetreplicastate</Word>\n      <Word>sp_MSgetrowmetadata</Word>\n      <Word>sp_MSgetrowmetadatalightweight</Word>\n      <Word>sp_MSGetServerProperties</Word>\n      <Word>sp_MSgetsetupbelong_cost</Word>\n      <Word>sp_MSgetsubscriberinfo</Word>\n      <Word>sp_MSgetsupportabilitysettings</Word>\n      <Word>sp_MSgettrancftsrcrow</Word>\n      <Word>sp_MSgettranconflictrow</Word>\n      <Word>sp_MSgetversion</Word>\n      <Word>sp_MSgrantconnectreplication</Word>\n      <Word>sp_MShaschangeslightweight</Word>\n      <Word>sp_MShasdbaccess</Word>\n      <Word>sp_MShelp_article</Word>\n      <Word>sp_MShelp_distdb</Word>\n      <Word>sp_MShelp_distribution_agentid</Word>\n      <Word>sp_MShelp_identity_property</Word>\n      <Word>sp_MShelp_logreader_agentid</Word>\n      <Word>sp_MShelp_merge_agentid</Word>\n      <Word>sp_MShelp_profile</Word>\n      <Word>sp_MShelp_profilecache</Word>\n      <Word>sp_MShelp_publication</Word>\n      <Word>sp_MShelp_repl_agent</Word>\n      <Word>sp_MShelp_replication_status</Word>\n      <Word>sp_MShelp_replication_table</Word>\n      <Word>sp_MShelp_snapshot_agent</Word>\n      <Word>sp_MShelp_snapshot_agentid</Word>\n      <Word>sp_MShelp_subscriber_info</Word>\n      <Word>sp_MShelp_subscription_status</Word>\n      <Word>sp_MShelp_subscription</Word>\n      <Word>sp_MShelpcolumns</Word>\n      <Word>sp_MShelpconflictpublications</Word>\n      <Word>sp_MShelpcreatebeforetable</Word>\n      <Word>sp_MShelpdestowner</Word>\n      <Word>sp_MShelpdynamicsnapshotjobatdistributor</Word>\n      <Word>sp_MShelpfulltextindex</Word>\n      <Word>sp_MShelpfulltextscript</Word>\n      <Word>sp_MShelpindex</Word>\n      <Word>sp_MShelplogreader_agent</Word>\n      <Word>sp_MShelpmergearticles</Word>\n      <Word>sp_MShelpmergeconflictcounts</Word>\n      <Word>sp_MShelpmergedynamicsnapshotjob</Word>\n      <Word>sp_MShelpmergeidentity</Word>\n      <Word>sp_MShelpmergeschemaarticles</Word>\n      <Word>sp_MShelpobjectpublications</Word>\n      <Word>sp_MShelpreplicationtriggers</Word>\n      <Word>sp_MShelpsnapshot_agent</Word>\n      <Word>sp_MShelpsummarypublication</Word>\n      <Word>sp_MShelptracertokenhistory</Word>\n      <Word>sp_MShelptracertokens</Word>\n      <Word>sp_MShelptranconflictcounts</Word>\n      <Word>sp_MShelptype</Word>\n      <Word>sp_MShelpvalidationdate</Word>\n      <Word>sp_MSIfExistsSubscription</Word>\n      <Word>sp_MSindexspace</Word>\n      <Word>sp_MSinit_publication_access</Word>\n      <Word>sp_MSinit_subscription_agent</Word>\n      <Word>sp_MSinitdynamicsubscriber</Word>\n      <Word>sp_MSinsert_identity</Word>\n      <Word>sp_MSinsertdeleteconflict</Word>\n      <Word>sp_MSinserterrorlineage</Word>\n      <Word>sp_MSinsertgenerationschemachanges</Word>\n      <Word>sp_MSinsertgenhistory</Word>\n      <Word>sp_MSinsertlightweightschemachange</Word>\n      <Word>sp_MSinsertschemachange</Word>\n      <Word>sp_MSinvalidate_snapshot</Word>\n      <Word>sp_MSisnonpkukupdateinconflict</Word>\n      <Word>sp_MSispeertopeeragent</Word>\n      <Word>sp_MSispkupdateinconflict</Word>\n      <Word>sp_MSispublicationqueued</Word>\n      <Word>sp_MSisreplmergeagent</Word>\n      <Word>sp_MSissnapshotitemapplied</Word>\n      <Word>sp_MSkilldb</Word>\n      <Word>sp_MSlock_auto_sub</Word>\n      <Word>sp_MSlock_distribution_agent</Word>\n      <Word>sp_MSlocktable</Word>\n      <Word>sp_MSloginmappings</Word>\n      <Word>sp_MSmakearticleprocs</Word>\n      <Word>sp_MSmakebatchinsertproc</Word>\n      <Word>sp_MSmakebatchupdateproc</Word>\n      <Word>sp_MSmakeconflictinsertproc</Word>\n      <Word>sp_MSmakectsview</Word>\n      <Word>sp_MSmakedeleteproc</Word>\n      <Word>sp_MSmakedynsnapshotvws</Word>\n      <Word>sp_MSmakeexpandproc</Word>\n      <Word>sp_MSmakegeneration</Word>\n      <Word>sp_MSmakeinsertproc</Word>\n      <Word>sp_MSmakemetadataselectproc</Word>\n      <Word>sp_MSmakeselectproc</Word>\n      <Word>sp_MSmakesystableviews</Word>\n      <Word>sp_MSmakeupdateproc</Word>\n      <Word>sp_MSmap_partitionid_to_generations</Word>\n      <Word>sp_MSmarkreinit</Word>\n      <Word>sp_MSmatchkey</Word>\n      <Word>sp_MSmerge_alterschemaonly</Word>\n      <Word>sp_MSmerge_altertrigger</Word>\n      <Word>sp_MSmerge_alterview</Word>\n      <Word>sp_MSmerge_ddldispatcher</Word>\n      <Word>sp_MSmerge_getgencount</Word>\n      <Word>sp_MSmerge_getgencur_public</Word>\n      <Word>sp_MSmerge_is_snapshot_required</Word>\n      <Word>sp_MSmerge_log_identity_range_allocations</Word>\n      <Word>sp_MSmerge_parsegenlist</Word>\n      <Word>sp_MSmerge_upgrade_subscriber</Word>\n      <Word>sp_MSmergesubscribedb</Word>\n      <Word>sp_MSmergeupdatelastsyncinfo</Word>\n      <Word>sp_MSneedmergemetadataretentioncleanup</Word>\n      <Word>sp_MSNonSQLDDL</Word>\n      <Word>sp_MSNonSQLDDLForSchemaDDL</Word>\n      <Word>sp_MSobjectprivs</Word>\n      <Word>sp_MSpeerapplyresponse</Word>\n      <Word>sp_MSpeerapplytopologyinfo</Word>\n      <Word>sp_MSpeerconflictdetection_statuscollection_applyresponse</Word>\n      <Word>sp_MSpeerconflictdetection_statuscollection_sendresponse</Word>\n      <Word>sp_MSpeerconflictdetection_topology_applyresponse</Word>\n      <Word>sp_MSpeerdbinfo</Word>\n      <Word>sp_MSpeersendresponse</Word>\n      <Word>sp_MSpeersendtopologyinfo</Word>\n      <Word>sp_MSpeertopeerfwdingexec</Word>\n      <Word>sp_MSpost_auto_proc</Word>\n      <Word>sp_MSpostapplyscript_forsubscriberprocs</Word>\n      <Word>sp_MSprep_exclusive</Word>\n      <Word>sp_MSprepare_mergearticle</Word>\n      <Word>sp_MSprofile_in_use</Word>\n      <Word>sp_MSproxiedmetadata</Word>\n      <Word>sp_MSproxiedmetadatabatch</Word>\n      <Word>sp_MSproxiedmetadatalightweight</Word>\n      <Word>sp_MSpub_adjust_identity</Word>\n      <Word>sp_MSpublication_access</Word>\n      <Word>sp_MSpublicationcleanup</Word>\n      <Word>sp_MSpublicationview</Word>\n      <Word>sp_MSquery_syncstates</Word>\n      <Word>sp_MSquerysubtype</Word>\n      <Word>sp_MSrecordsnapshotdeliveryprogress</Word>\n      <Word>sp_MSreenable_check</Word>\n      <Word>sp_MSrefresh_anonymous</Word>\n      <Word>sp_MSrefresh_publisher_idrange</Word>\n      <Word>sp_MSregenerate_mergetriggersprocs</Word>\n      <Word>sp_MSregisterdynsnapseqno</Word>\n      <Word>sp_MSregistermergesnappubid</Word>\n      <Word>sp_MSregistersubscription</Word>\n      <Word>sp_MSreinit_failed_subscriptions</Word>\n      <Word>sp_MSreinit_hub</Word>\n      <Word>sp_MSreinit_subscription</Word>\n      <Word>sp_MSreinitoverlappingmergepublications</Word>\n      <Word>sp_MSreleasedynamicsnapshotapplock</Word>\n      <Word>sp_MSreleasemakegenerationapplock</Word>\n      <Word>sp_MSreleasemergeadminapplock</Word>\n      <Word>sp_MSreleaseSlotLock</Word>\n      <Word>sp_MSreleasesnapshotdeliverysessionlock</Word>\n      <Word>sp_MSremove_mergereplcommand</Word>\n      <Word>sp_MSremoveoffloadparameter</Word>\n      <Word>sp_MSrepl_agentstatussummary</Word>\n      <Word>sp_MSrepl_backup_complete</Word>\n      <Word>sp_MSrepl_backup_start</Word>\n      <Word>sp_MSrepl_check_publisher</Word>\n      <Word>sp_MSrepl_createdatatypemappings</Word>\n      <Word>sp_MSrepl_distributionagentstatussummary</Word>\n      <Word>sp_MSrepl_dropdatatypemappings</Word>\n      <Word>sp_MSrepl_enumarticlecolumninfo</Word>\n      <Word>sp_MSrepl_enumpublications</Word>\n      <Word>sp_MSrepl_enumpublishertables</Word>\n      <Word>sp_MSrepl_enumsubscriptions</Word>\n      <Word>sp_MSrepl_enumtablecolumninfo</Word>\n      <Word>sp_MSrepl_FixPALRole</Word>\n      <Word>sp_MSrepl_getdistributorinfo</Word>\n      <Word>sp_MSrepl_getpkfkrelation</Word>\n      <Word>sp_MSrepl_gettype_mappings</Word>\n      <Word>sp_MSrepl_helparticlermo</Word>\n      <Word>sp_MSrepl_init_backup_lsns</Word>\n      <Word>sp_MSrepl_isdbowner</Word>\n      <Word>sp_MSrepl_IsLastPubInSharedSubscription</Word>\n      <Word>sp_MSrepl_IsUserInAnyPAL</Word>\n      <Word>sp_MSrepl_linkedservers_rowset</Word>\n      <Word>sp_MSrepl_mergeagentstatussummary</Word>\n      <Word>sp_MSrepl_PAL_rolecheck</Word>\n      <Word>sp_MSrepl_raiserror</Word>\n      <Word>sp_MSrepl_schema</Word>\n      <Word>sp_MSrepl_setNFR</Word>\n      <Word>sp_MSrepl_snapshot_helparticlecolumns</Word>\n      <Word>sp_MSrepl_snapshot_helppublication</Word>\n      <Word>sp_MSrepl_startup_internal</Word>\n      <Word>sp_MSrepl_startup</Word>\n      <Word>sp_MSrepl_subscription_rowset</Word>\n      <Word>sp_MSrepl_testadminconnection</Word>\n      <Word>sp_MSrepl_testconnection</Word>\n      <Word>sp_MSreplagentjobexists</Word>\n      <Word>sp_MSreplcheck_permission</Word>\n      <Word>sp_MSreplcheck_pull</Word>\n      <Word>sp_MSreplcheck_subscribe_withddladmin</Word>\n      <Word>sp_MSreplcheck_subscribe</Word>\n      <Word>sp_MSreplcheckoffloadserver</Word>\n      <Word>sp_MSreplcopyscriptfile</Word>\n      <Word>sp_MSreplraiserror</Word>\n      <Word>sp_MSreplremoveuncdir</Word>\n      <Word>sp_MSreplupdateschema</Word>\n      <Word>sp_MSrequestreenumeration_lightweight</Word>\n      <Word>sp_MSrequestreenumeration</Word>\n      <Word>sp_MSreset_attach_state</Word>\n      <Word>sp_MSreset_queued_reinit</Word>\n      <Word>sp_MSreset_subscription_seqno</Word>\n      <Word>sp_MSreset_subscription</Word>\n      <Word>sp_MSreset_synctran_bit</Word>\n      <Word>sp_MSreset_transaction</Word>\n      <Word>sp_MSresetsnapshotdeliveryprogress</Word>\n      <Word>sp_MSrestoresavedforeignkeys</Word>\n      <Word>sp_MSretrieve_publication_attributes</Word>\n      <Word>sp_MSscript_article_view</Word>\n      <Word>sp_MSscript_dri</Word>\n      <Word>sp_MSscript_pub_upd_trig</Word>\n      <Word>sp_MSscript_sync_del_proc</Word>\n      <Word>sp_MSscript_sync_del_trig</Word>\n      <Word>sp_MSscript_sync_ins_proc</Word>\n      <Word>sp_MSscript_sync_ins_trig</Word>\n      <Word>sp_MSscript_sync_upd_proc</Word>\n      <Word>sp_MSscript_sync_upd_trig</Word>\n      <Word>sp_MSscriptcustomdelproc</Word>\n      <Word>sp_MSscriptcustominsproc</Word>\n      <Word>sp_MSscriptcustomupdproc</Word>\n      <Word>sp_MSscriptdatabase</Word>\n      <Word>sp_MSscriptdb_worker</Word>\n      <Word>sp_MSscriptforeignkeyrestore</Word>\n      <Word>sp_MSscriptsubscriberprocs</Word>\n      <Word>sp_MSscriptviewproc</Word>\n      <Word>sp_MSsendtosqlqueue</Word>\n      <Word>sp_MSset_dynamic_filter_options</Word>\n      <Word>sp_MSset_logicalrecord_metadata</Word>\n      <Word>sp_MSset_new_identity_range</Word>\n      <Word>sp_MSset_oledb_prop</Word>\n      <Word>sp_MSset_snapshot_xact_seqno</Word>\n      <Word>sp_MSset_sub_guid</Word>\n      <Word>sp_MSset_subscription_properties</Word>\n      <Word>sp_MSsetaccesslist</Word>\n      <Word>sp_MSsetalertinfo</Word>\n      <Word>sp_MSsetartprocs</Word>\n      <Word>sp_MSsetbit</Word>\n      <Word>sp_MSsetconflictscript</Word>\n      <Word>sp_MSsetconflicttable</Word>\n      <Word>sp_MSsetcontext_bypasswholeddleventbit</Word>\n      <Word>sp_MSsetcontext_replagent</Word>\n      <Word>sp_MSsetgentozero</Word>\n      <Word>sp_MSsetlastrecgen</Word>\n      <Word>sp_MSsetlastsentgen</Word>\n      <Word>sp_MSsetreplicainfo</Word>\n      <Word>sp_MSsetreplicaschemaversion</Word>\n      <Word>sp_MSsetreplicastatus</Word>\n      <Word>sp_MSsetrowmetadata</Word>\n      <Word>sp_MSSetServerProperties</Word>\n      <Word>sp_MSsetsubscriberinfo</Word>\n      <Word>sp_MSsettopology</Word>\n      <Word>sp_MSsetup_identity_range</Word>\n      <Word>sp_MSsetup_partition_groups</Word>\n      <Word>sp_MSsetup_use_partition_groups</Word>\n      <Word>sp_MSsetupbelongs</Word>\n      <Word>sp_MSsetupnosyncsubwithlsnatdist_cleanup</Word>\n      <Word>sp_MSsetupnosyncsubwithlsnatdist_helper</Word>\n      <Word>sp_MSsetupnosyncsubwithlsnatdist</Word>\n      <Word>sp_MSSharedFixedDisk</Word>\n      <Word>sp_MSSQLDMO70_version</Word>\n      <Word>sp_MSSQLDMO80_version</Word>\n      <Word>sp_MSSQLDMO90_version</Word>\n      <Word>sp_MSSQLOLE_version</Word>\n      <Word>sp_MSSQLOLE65_version</Word>\n      <Word>sp_MSstartdistribution_agent</Word>\n      <Word>sp_MSstartmerge_agent</Word>\n      <Word>sp_MSstartsnapshot_agent</Word>\n      <Word>sp_MSstopdistribution_agent</Word>\n      <Word>sp_MSstopmerge_agent</Word>\n      <Word>sp_MSstopsnapshot_agent</Word>\n      <Word>sp_MSsub_check_identity</Word>\n      <Word>sp_MSsub_set_identity</Word>\n      <Word>sp_MSsubscription_status</Word>\n      <Word>sp_MSsubscriptionvalidated</Word>\n      <Word>sp_MStablechecks</Word>\n      <Word>sp_MStablekeys</Word>\n      <Word>sp_MStablerefs</Word>\n      <Word>sp_MStablespace</Word>\n      <Word>sp_MStestbit</Word>\n      <Word>sp_MStran_ddlrepl</Word>\n      <Word>sp_MStran_is_snapshot_required</Word>\n      <Word>sp_MStrypurgingoldsnapshotdeliveryprogress</Word>\n      <Word>sp_MSuniquename</Word>\n      <Word>sp_MSunmarkifneeded</Word>\n      <Word>sp_MSunmarkreplinfo</Word>\n      <Word>sp_MSunmarkschemaobject</Word>\n      <Word>sp_MSunregistersubscription</Word>\n      <Word>sp_MSupdate_agenttype_default</Word>\n      <Word>sp_MSupdate_singlelogicalrecordmetadata</Word>\n      <Word>sp_MSupdate_subscriber_info</Word>\n      <Word>sp_MSupdate_subscriber_schedule</Word>\n      <Word>sp_MSupdate_subscriber_tracer_history</Word>\n      <Word>sp_MSupdate_subscription</Word>\n      <Word>sp_MSupdate_tracer_history</Word>\n      <Word>sp_MSupdatecachedpeerlsn</Word>\n      <Word>sp_MSupdategenerations_afterbcp</Word>\n      <Word>sp_MSupdategenhistory</Word>\n      <Word>sp_MSupdateinitiallightweightsubscription</Word>\n      <Word>sp_MSupdatelastsyncinfo</Word>\n      <Word>sp_MSupdatepeerlsn</Word>\n      <Word>sp_MSupdaterecgen</Word>\n      <Word>sp_MSupdatereplicastate</Word>\n      <Word>sp_MSupdatesysmergearticles</Word>\n      <Word>sp_MSuplineageversion</Word>\n      <Word>sp_MSuploadsupportabilitydata</Word>\n      <Word>sp_MSuselightweightreplication</Word>\n      <Word>sp_MSvalidate_dest_recgen</Word>\n      <Word>sp_MSvalidate_subscription</Word>\n      <Word>sp_MSvalidate_wellpartitioned_articles</Word>\n      <Word>sp_MSvalidatearticle</Word>\n      <Word>sp_MSwritemergeperfcounter</Word>\n      <Word>sp_new_parallel_nested_tran_id</Word>\n      <Word>sp_OACreate</Word>\n      <Word>sp_OADestroy</Word>\n      <Word>sp_OAGetErrorInfo</Word>\n      <Word>sp_OAGetProperty</Word>\n      <Word>sp_OAMethod</Word>\n      <Word>sp_OASetProperty</Word>\n      <Word>sp_OAStop</Word>\n      <Word>sp_objectfilegroup</Word>\n      <Word>sp_oledb_database</Word>\n      <Word>sp_oledb_defdb</Word>\n      <Word>sp_oledb_deflang</Word>\n      <Word>sp_oledb_language</Word>\n      <Word>sp_oledb_ro_usrname</Word>\n      <Word>sp_oledbinfo</Word>\n      <Word>sp_ORbitmap</Word>\n      <Word>sp_password</Word>\n      <Word>sp_peerconflictdetection_tableaug</Word>\n      <Word>sp_pkeys</Word>\n      <Word>sp_polybase_join_group</Word>\n      <Word>sp_polybase_leave_group</Word>\n      <Word>sp_PostAgentInfo</Word>\n      <Word>sp_posttracertoken</Word>\n      <Word>sp_prepare</Word>\n      <Word>sp_prepexec</Word>\n      <Word>sp_prepexecrpc</Word>\n      <Word>sp_primary_keys_rowset_rmt</Word>\n      <Word>sp_primary_keys_rowset</Word>\n      <Word>sp_primary_keys_rowset2</Word>\n      <Word>sp_primarykeys</Word>\n      <Word>sp_procedure_params_100_managed</Word>\n      <Word>sp_procedure_params_100_rowset</Word>\n      <Word>sp_procedure_params_100_rowset2</Word>\n      <Word>sp_procedure_params_90_rowset</Word>\n      <Word>sp_procedure_params_90_rowset2</Word>\n      <Word>sp_procedure_params_managed</Word>\n      <Word>sp_procedure_params_rowset</Word>\n      <Word>sp_procedure_params_rowset2</Word>\n      <Word>sp_procedures_rowset</Word>\n      <Word>sp_procedures_rowset2</Word>\n      <Word>sp_processlogshippingmonitorhistory</Word>\n      <Word>sp_processlogshippingmonitorprimary</Word>\n      <Word>sp_processlogshippingmonitorsecondary</Word>\n      <Word>sp_processlogshippingretentioncleanup</Word>\n      <Word>sp_procoption</Word>\n      <Word>sp_prop_oledb_provider</Word>\n      <Word>sp_provider_types_100_rowset</Word>\n      <Word>sp_provider_types_90_rowset</Word>\n      <Word>sp_provider_types_rowset</Word>\n      <Word>sp_publication_validation</Word>\n      <Word>sp_publicationsummary</Word>\n      <Word>sp_publishdb</Word>\n      <Word>sp_publisherproperty</Word>\n      <Word>sp_query_store_flush_db</Word>\n      <Word>sp_query_store_force_plan</Word>\n      <Word>sp_query_store_remove_plan</Word>\n      <Word>sp_query_store_remove_query</Word>\n      <Word>sp_query_store_reset_exec_stats</Word>\n      <Word>sp_query_store_unforce_plan</Word>\n      <Word>sp_rda_deauthorize_db</Word>\n      <Word>sp_rda_get_rpo_duration</Word>\n      <Word>sp_rda_reauthorize_db</Word>\n      <Word>sp_rda_reconcile_batch</Word>\n      <Word>sp_rda_reconcile_columns</Word>\n      <Word>sp_rda_reconcile_indexes</Word>\n      <Word>sp_rda_set_query_mode</Word>\n      <Word>sp_rda_set_rpo_duration</Word>\n      <Word>sp_rda_test_connection</Word>\n      <Word>sp_readerrorlog</Word>\n      <Word>sp_recompile</Word>\n      <Word>sp_redirect_publisher</Word>\n      <Word>sp_refresh_heterogeneous_publisher</Word>\n      <Word>sp_refresh_log_shipping_monitor</Word>\n      <Word>sp_refresh_parameter_encryption</Word>\n      <Word>sp_refreshsqlmodule</Word>\n      <Word>sp_refreshsubscriptions</Word>\n      <Word>sp_refreshview</Word>\n      <Word>sp_register_custom_scripting</Word>\n      <Word>sp_registercustomresolver</Word>\n      <Word>sp_reinitmergepullsubscription</Word>\n      <Word>sp_reinitmergesubscription</Word>\n      <Word>sp_reinitpullsubscription</Word>\n      <Word>sp_reinitsubscription</Word>\n      <Word>sp_releaseapplock</Word>\n      <Word>sp_releaseschemalock</Word>\n      <Word>sp_remote_data_archive_event</Word>\n      <Word>sp_remoteoption</Word>\n      <Word>sp_removedbreplication</Word>\n      <Word>sp_removedistpublisherdbreplication</Word>\n      <Word>sp_removesrvreplication</Word>\n      <Word>sp_rename</Word>\n      <Word>sp_renamedb</Word>\n      <Word>sp_repl_generate_subscriber_event</Word>\n      <Word>sp_repl_generateevent</Word>\n      <Word>sp_repladdcolumn</Word>\n      <Word>sp_replcleanupccsprocs</Word>\n      <Word>sp_replcmds</Word>\n      <Word>sp_replcounters</Word>\n      <Word>sp_replddlparser</Word>\n      <Word>sp_repldeletequeuedtran</Word>\n      <Word>sp_repldone</Word>\n      <Word>sp_repldropcolumn</Word>\n      <Word>sp_replflush</Word>\n      <Word>sp_replgetparsedddlcmd</Word>\n      <Word>sp_replhelp</Word>\n      <Word>sp_replica</Word>\n      <Word>sp_replication_agent_checkup</Word>\n      <Word>sp_replicationdboption</Word>\n      <Word>sp_replincrementlsn</Word>\n      <Word>sp_replmonitorchangepublicationthreshold</Word>\n      <Word>sp_replmonitorhelpmergesession</Word>\n      <Word>sp_replmonitorhelpmergesessiondetail</Word>\n      <Word>sp_replmonitorhelpmergesubscriptionmoreinfo</Word>\n      <Word>sp_replmonitorhelppublication</Word>\n      <Word>sp_replmonitorhelppublicationthresholds</Word>\n      <Word>sp_replmonitorhelppublisher</Word>\n      <Word>sp_replmonitorhelpsubscription</Word>\n      <Word>sp_replmonitorrefreshjob</Word>\n      <Word>sp_replmonitorsubscriptionpendingcmds</Word>\n      <Word>sp_replpostsyncstatus</Word>\n      <Word>sp_replqueuemonitor</Word>\n      <Word>sp_replrestart</Word>\n      <Word>sp_replrethrow</Word>\n      <Word>sp_replsendtoqueue</Word>\n      <Word>sp_replsetoriginator</Word>\n      <Word>sp_replsetsyncstatus</Word>\n      <Word>sp_replshowcmds</Word>\n      <Word>sp_replsqlqgetrows</Word>\n      <Word>sp_replsync</Word>\n      <Word>sp_repltrans</Word>\n      <Word>sp_replwritetovarbin</Word>\n      <Word>sp_requestpeerresponse</Word>\n      <Word>sp_requestpeertopologyinfo</Word>\n      <Word>sp_reserve_http_namespace</Word>\n      <Word>sp_reset_connection</Word>\n      <Word>sp_reset_session_context</Word>\n      <Word>sp_resetsnapshotdeliveryprogress</Word>\n      <Word>sp_resetstatus</Word>\n      <Word>sp_resign_database</Word>\n      <Word>sp_resolve_logins</Word>\n      <Word>sp_restoredbreplication</Word>\n      <Word>sp_restoremergeidentityrange</Word>\n      <Word>sp_resyncexecute</Word>\n      <Word>sp_resyncexecutesql</Word>\n      <Word>sp_resyncmergesubscription</Word>\n      <Word>sp_resyncprepare</Word>\n      <Word>sp_resyncuniquetable</Word>\n      <Word>sp_revoke_publication_access</Word>\n      <Word>sp_revokedbaccess</Word>\n      <Word>sp_revokelogin</Word>\n      <Word>sp_rollback_parallel_nested_tran</Word>\n      <Word>sp_schemafilter</Word>\n      <Word>sp_schemata_rowset</Word>\n      <Word>sp_script_reconciliation_delproc</Word>\n      <Word>sp_script_reconciliation_insproc</Word>\n      <Word>sp_script_reconciliation_sinsproc</Word>\n      <Word>sp_script_reconciliation_vdelproc</Word>\n      <Word>sp_script_reconciliation_xdelproc</Word>\n      <Word>sp_script_synctran_commands</Word>\n      <Word>sp_scriptdelproc</Word>\n      <Word>sp_scriptdynamicupdproc</Word>\n      <Word>sp_scriptinsproc</Word>\n      <Word>sp_scriptmappedupdproc</Word>\n      <Word>sp_scriptpublicationcustomprocs</Word>\n      <Word>sp_scriptsinsproc</Word>\n      <Word>sp_scriptsubconflicttable</Word>\n      <Word>sp_scriptsupdproc</Word>\n      <Word>sp_scriptupdproc</Word>\n      <Word>sp_scriptvdelproc</Word>\n      <Word>sp_scriptvupdproc</Word>\n      <Word>sp_scriptxdelproc</Word>\n      <Word>sp_scriptxupdproc</Word>\n      <Word>sp_sequence_get_range</Word>\n      <Word>sp_server_diagnostics</Word>\n      <Word>sp_server_info</Word>\n      <Word>sp_serveroption</Word>\n      <Word>sp_set_session_context</Word>\n      <Word>sp_setapprole</Word>\n      <Word>sp_SetAutoSAPasswordAndDisable</Word>\n      <Word>sp_setdefaultdatatypemapping</Word>\n      <Word>sp_setnetname</Word>\n      <Word>sp_SetOBDCertificate</Word>\n      <Word>sp_setOraclepackageversion</Word>\n      <Word>sp_setreplfailovermode</Word>\n      <Word>sp_setsubscriptionxactseqno</Word>\n      <Word>sp_settriggerorder</Word>\n      <Word>sp_setuserbylogin</Word>\n      <Word>sp_showcolv</Word>\n      <Word>sp_showlineage</Word>\n      <Word>sp_showmemo_xml</Word>\n      <Word>sp_showpendingchanges</Word>\n      <Word>sp_showrowreplicainfo</Word>\n      <Word>sp_sm_detach</Word>\n      <Word>sp_spaceused_remote_data_archive</Word>\n      <Word>sp_spaceused</Word>\n      <Word>sp_sparse_columns_100_rowset</Word>\n      <Word>sp_special_columns_100</Word>\n      <Word>sp_special_columns_90</Word>\n      <Word>sp_special_columns</Word>\n      <Word>sp_sproc_columns_100</Word>\n      <Word>sp_sproc_columns_90</Word>\n      <Word>sp_sproc_columns</Word>\n      <Word>sp_sqlagent_add_job</Word>\n      <Word>sp_sqlagent_add_jobstep</Word>\n      <Word>sp_sqlagent_delete_job</Word>\n      <Word>sp_sqlagent_help_jobstep</Word>\n      <Word>sp_sqlagent_log_job_history</Word>\n      <Word>sp_sqlagent_start_job</Word>\n      <Word>sp_sqlagent_stop_job</Word>\n      <Word>sp_sqlagent_verify_database_context</Word>\n      <Word>sp_sqlagent_write_jobstep_log</Word>\n      <Word>sp_sqlexec</Word>\n      <Word>sp_srvrolepermission</Word>\n      <Word>sp_start_user_instance</Word>\n      <Word>sp_startmergepullsubscription_agent</Word>\n      <Word>sp_startmergepushsubscription_agent</Word>\n      <Word>sp_startpublication_snapshot</Word>\n      <Word>sp_startpullsubscription_agent</Word>\n      <Word>sp_startpushsubscription_agent</Word>\n      <Word>sp_statistics_100</Word>\n      <Word>sp_statistics_rowset</Word>\n      <Word>sp_statistics_rowset2</Word>\n      <Word>sp_statistics</Word>\n      <Word>sp_stopmergepullsubscription_agent</Word>\n      <Word>sp_stopmergepushsubscription_agent</Word>\n      <Word>sp_stoppublication_snapshot</Word>\n      <Word>sp_stoppullsubscription_agent</Word>\n      <Word>sp_stoppushsubscription_agent</Word>\n      <Word>sp_stored_procedures</Word>\n      <Word>sp_subscribe</Word>\n      <Word>sp_subscription_cleanup</Word>\n      <Word>sp_subscriptionsummary</Word>\n      <Word>sp_syspolicy_execute_policy</Word>\n      <Word>sp_syspolicy_subscribe_to_policy_category</Word>\n      <Word>sp_syspolicy_unsubscribe_from_policy_category</Word>\n      <Word>sp_syspolicy_update_ddl_trigger</Word>\n      <Word>sp_syspolicy_update_event_notification</Word>\n      <Word>sp_table_constraints_rowset</Word>\n      <Word>sp_table_constraints_rowset2</Word>\n      <Word>sp_table_privileges_ex</Word>\n      <Word>sp_table_privileges_rowset_rmt</Word>\n      <Word>sp_table_privileges_rowset</Word>\n      <Word>sp_table_privileges_rowset2</Word>\n      <Word>sp_table_privileges</Word>\n      <Word>sp_table_statistics_rowset</Word>\n      <Word>sp_table_statistics2_rowset</Word>\n      <Word>sp_table_type_columns_100_rowset</Word>\n      <Word>sp_table_type_columns_100</Word>\n      <Word>sp_table_type_pkeys</Word>\n      <Word>sp_table_type_primary_keys_rowset</Word>\n      <Word>sp_table_types_rowset</Word>\n      <Word>sp_table_types</Word>\n      <Word>sp_table_validation</Word>\n      <Word>sp_tablecollations_100</Word>\n      <Word>sp_tablecollations_90</Word>\n      <Word>sp_tablecollations</Word>\n      <Word>sp_tableoption</Word>\n      <Word>sp_tables_ex</Word>\n      <Word>sp_tables_info_90_rowset_64</Word>\n      <Word>sp_tables_info_90_rowset</Word>\n      <Word>sp_tables_info_90_rowset2_64</Word>\n      <Word>sp_tables_info_90_rowset2</Word>\n      <Word>sp_tables_info_rowset_64</Word>\n      <Word>sp_tables_info_rowset</Word>\n      <Word>sp_tables_info_rowset2_64</Word>\n      <Word>sp_tables_info_rowset2</Word>\n      <Word>sp_tables_rowset_rmt</Word>\n      <Word>sp_tables_rowset</Word>\n      <Word>sp_tables_rowset2</Word>\n      <Word>sp_tables</Word>\n      <Word>sp_tableswc</Word>\n      <Word>sp_testlinkedserver</Word>\n      <Word>sp_trace_create</Word>\n      <Word>sp_trace_generateevent</Word>\n      <Word>sp_trace_getdata</Word>\n      <Word>sp_trace_setevent</Word>\n      <Word>sp_trace_setfilter</Word>\n      <Word>sp_trace_setstatus</Word>\n      <Word>sp_try_set_session_context</Word>\n      <Word>sp_unbindefault</Word>\n      <Word>sp_unbindrule</Word>\n      <Word>sp_unprepare</Word>\n      <Word>sp_unregister_custom_scripting</Word>\n      <Word>sp_unregistercustomresolver</Word>\n      <Word>sp_unsetapprole</Word>\n      <Word>sp_unsubscribe</Word>\n      <Word>sp_update_agent_profile</Word>\n      <Word>sp_update_user_instance</Word>\n      <Word>sp_updateextendedproperty</Word>\n      <Word>sp_updatestats</Word>\n      <Word>sp_upgrade_log_shipping</Word>\n      <Word>sp_user_counter1</Word>\n      <Word>sp_user_counter10</Word>\n      <Word>sp_user_counter2</Word>\n      <Word>sp_user_counter3</Word>\n      <Word>sp_user_counter4</Word>\n      <Word>sp_user_counter5</Word>\n      <Word>sp_user_counter6</Word>\n      <Word>sp_user_counter7</Word>\n      <Word>sp_user_counter8</Word>\n      <Word>sp_user_counter9</Word>\n      <Word>sp_usertypes_rowset_rmt</Word>\n      <Word>sp_usertypes_rowset</Word>\n      <Word>sp_usertypes_rowset2</Word>\n      <Word>sp_validate_redirected_publisher</Word>\n      <Word>sp_validate_replica_hosts_as_publishers</Word>\n      <Word>sp_validatecache</Word>\n      <Word>sp_validatelogins</Word>\n      <Word>sp_validatemergepublication</Word>\n      <Word>sp_validatemergepullsubscription</Word>\n      <Word>sp_validatemergesubscription</Word>\n      <Word>sp_validlang</Word>\n      <Word>sp_validname</Word>\n      <Word>sp_verifypublisher</Word>\n      <Word>sp_views_rowset</Word>\n      <Word>sp_views_rowset2</Word>\n      <Word>sp_vupgrade_mergeobjects</Word>\n      <Word>sp_vupgrade_mergetables</Word>\n      <Word>sp_vupgrade_replication</Word>\n      <Word>sp_vupgrade_replsecurity_metadata</Word>\n      <Word>sp_who</Word>\n      <Word>sp_who2</Word>\n      <Word>sp_xml_preparedocument</Word>\n      <Word>sp_xml_removedocument</Word>\n      <Word>sp_xml_schema_rowset</Word>\n      <Word>sp_xml_schema_rowset2</Word>\n      <Word>sp_xp_cmdshell_proxy_account</Word>\n      <Word>sp_xtp_bind_db_resource_pool</Word>\n      <Word>sp_xtp_checkpoint_force_garbage_collection</Word>\n      <Word>sp_xtp_control_proc_exec_stats</Word>\n      <Word>sp_xtp_control_query_exec_stats</Word>\n      <Word>sp_xtp_flush_temporal_history</Word>\n      <Word>sp_xtp_kill_active_transactions</Word>\n      <Word>sp_xtp_merge_checkpoint_files</Word>\n      <Word>sp_xtp_objects_present</Word>\n      <Word>sp_xtp_set_memory_quota</Word>\n      <Word>sp_xtp_slo_can_downgrade</Word>\n      <Word>sp_xtp_slo_downgrade_finished</Word>\n      <Word>sp_xtp_slo_prepare_to_downgrade</Word>\n      <Word>sp_xtp_unbind_db_resource_pool</Word>\n      <Word>spatial_index_tessellations</Word>\n      <Word>spatial_indexes</Word>\n      <Word>spatial_reference_systems</Word>\n      <Word>spt_values</Word>\n      <Word>sql_dependencies</Word>\n      <Word>sql_expression_dependencies</Word>\n      <Word>sql_logins</Word>\n      <Word>sql_modules</Word>\n      <Word>sqlagent_job_history</Word>\n      <Word>sqlagent_jobs</Word>\n      <Word>sqlagent_jobsteps_logs</Word>\n      <Word>sqlagent_jobsteps</Word>\n      <Word>stats_columns</Word>\n      <Word>stats</Word>\n      <Word>symmetric_keys</Word>\n      <Word>synonyms</Word>\n      <Word>sys</Word>\n      <Word>sysallocunits</Word>\n      <Word>sysaltfiles</Word>\n      <Word>sysasymkeys</Word>\n      <Word>sysaudacts</Word>\n      <Word>sysbinobjs</Word>\n      <Word>sysbinsubobjs</Word>\n      <Word>sysbrickfiles</Word>\n      <Word>syscacheobjects</Word>\n      <Word>syscerts</Word>\n      <Word>syscharsets</Word>\n      <Word>syschildinsts</Word>\n      <Word>sysclones</Word>\n      <Word>sysclsobjs</Word>\n      <Word>syscolpars</Word>\n      <Word>syscolumns</Word>\n      <Word>syscomments</Word>\n      <Word>syscommittab</Word>\n      <Word>syscompfragments</Word>\n      <Word>sysconfigures</Word>\n      <Word>sysconstraints</Word>\n      <Word>sysconvgroup</Word>\n      <Word>syscscolsegments</Word>\n      <Word>syscsdictionaries</Word>\n      <Word>syscsrowgroups</Word>\n      <Word>syscurconfigs</Word>\n      <Word>syscursorcolumns</Word>\n      <Word>syscursorrefs</Word>\n      <Word>syscursors</Word>\n      <Word>syscursortables</Word>\n      <Word>sysdatabases</Word>\n      <Word>sysdbfiles</Word>\n      <Word>sysdbfrag</Word>\n      <Word>sysdbreg</Word>\n      <Word>sysdepends</Word>\n      <Word>sysdercv</Word>\n      <Word>sysdesend</Word>\n      <Word>sysdevices</Word>\n      <Word>sysendpts</Word>\n      <Word>sysextendedrecoveryforks</Word>\n      <Word>sysextfileformats</Word>\n      <Word>sysextsources</Word>\n      <Word>sysexttables</Word>\n      <Word>sysfgfrag</Word>\n      <Word>sysfilegroups</Word>\n      <Word>sysfiles</Word>\n      <Word>sysfiles1</Word>\n      <Word>sysfoqueues</Word>\n      <Word>sysforeignkeys</Word>\n      <Word>sysfos</Word>\n      <Word>sysftinds</Word>\n      <Word>sysftproperties</Word>\n      <Word>sysftsemanticsdb</Word>\n      <Word>sysftstops</Word>\n      <Word>sysfulltextcatalogs</Word>\n      <Word>sysguidrefs</Word>\n      <Word>sysidxstats</Word>\n      <Word>sysindexes</Word>\n      <Word>sysindexkeys</Word>\n      <Word>sysiscols</Word>\n      <Word>syslanguages</Word>\n      <Word>syslnklgns</Word>\n      <Word>syslockinfo</Word>\n      <Word>syslogins</Word>\n      <Word>syslogshippers</Word>\n      <Word>sysmatrixageforget</Word>\n      <Word>sysmatrixages</Word>\n      <Word>sysmatrixbricks</Word>\n      <Word>sysmatrixconfig</Word>\n      <Word>sysmatrixmanagers</Word>\n      <Word>sysmembers</Word>\n      <Word>sysmessages</Word>\n      <Word>sysmultiobjrefs</Word>\n      <Word>sysmultiobjvalues</Word>\n      <Word>sysnsobjs</Word>\n      <Word>sysobjects</Word>\n      <Word>sysobjkeycrypts</Word>\n      <Word>sysobjvalues</Word>\n      <Word>sysoledbusers</Word>\n      <Word>sysopentapes</Word>\n      <Word>sysowners</Word>\n      <Word>sysperfinfo</Word>\n      <Word>syspermissions</Word>\n      <Word>sysphfg</Word>\n      <Word>syspriorities</Word>\n      <Word>sysprivs</Word>\n      <Word>sysprocesses</Word>\n      <Word>sysprotects</Word>\n      <Word>syspru</Word>\n      <Word>sysprufiles</Word>\n      <Word>sysqnames</Word>\n      <Word>sysreferences</Word>\n      <Word>sysremotelogins</Word>\n      <Word>sysremsvcbinds</Word>\n      <Word>sysrmtlgns</Word>\n      <Word>sysrowsetrefs</Word>\n      <Word>sysrowsets</Word>\n      <Word>sysrscols</Word>\n      <Word>sysrts</Word>\n      <Word>sysscalartypes</Word>\n      <Word>sysschobjs</Word>\n      <Word>sysseobjvalues</Word>\n      <Word>sysservers</Word>\n      <Word>syssingleobjrefs</Word>\n      <Word>syssoftobjrefs</Word>\n      <Word>syssqlguides</Word>\n      <Word>system_columns</Word>\n      <Word>system_components_surface_area_configuration</Word>\n      <Word>system_internals_allocation_units</Word>\n      <Word>system_internals_partition_columns</Word>\n      <Word>system_internals_partitions</Word>\n      <Word>system_objects</Word>\n      <Word>system_parameters</Word>\n      <Word>system_sql_modules</Word>\n      <Word>system_views</Word>\n      <Word>systypedsubobjs</Word>\n      <Word>systypes</Word>\n      <Word>sysusermsgs</Word>\n      <Word>sysusers</Word>\n      <Word>syswebmethods</Word>\n      <Word>sysxlgns</Word>\n      <Word>sysxmitbody</Word>\n      <Word>sysxmitqueue</Word>\n      <Word>sysxmlcomponent</Word>\n      <Word>sysxmlfacet</Word>\n      <Word>sysxmlplacement</Word>\n      <Word>sysxprops</Word>\n      <Word>sysxsrvs</Word>\n      <Word>TABLE_CONSTRAINTS</Word>\n      <Word>TABLE_PRIVILEGES</Word>\n      <Word>table_types</Word>\n      <Word>tables</Word>\n      <Word>TABLES</Word>\n      <Word>tcp_endpoints</Word>\n      <Word>time_zone_info</Word>\n      <Word>trace_categories</Word>\n      <Word>trace_columns</Word>\n      <Word>trace_event_bindings</Word>\n      <Word>trace_events</Word>\n      <Word>trace_subclass_values</Word>\n      <Word>traces</Word>\n      <Word>transmission_queue</Word>\n      <Word>trigger_event_types</Word>\n      <Word>trigger_events</Word>\n      <Word>triggers</Word>\n      <Word>type_assembly_usages</Word>\n      <Word>types</Word>\n      <Word>user_token</Word>\n      <Word>via_endpoints</Word>\n      <Word>VIEW_COLUMN_USAGE</Word>\n      <Word>VIEW_TABLE_USAGE</Word>\n      <Word>VIEWS</Word>\n      <Word>views</Word>\n      <Word>xml_indexes</Word>\n      <Word>xml_schema_attributes</Word>\n      <Word>xml_schema_collections</Word>\n      <Word>xml_schema_component_placements</Word>\n      <Word>xml_schema_components</Word>\n      <Word>xml_schema_elements</Word>\n      <Word>xml_schema_facets</Word>\n      <Word>xml_schema_model_groups</Word>\n      <Word>xml_schema_namespaces</Word>\n      <Word>xml_schema_types</Word>\n      <Word>xml_schema_wildcard_namespaces</Word>\n      <Word>xml_schema_wildcards</Word>\n      <Word>xp_availablemedia</Word>\n      <Word>xp_cmdshell</Word>\n      <Word>xp_create_subdir</Word>\n      <Word>xp_delete_file</Word>\n      <Word>xp_dirtree</Word>\n      <Word>xp_enum_oledb_providers</Word>\n      <Word>xp_enumerrorlogs</Word>\n      <Word>xp_enumgroups</Word>\n      <Word>xp_fileexist</Word>\n      <Word>xp_fixeddrives</Word>\n      <Word>xp_get_tape_devices</Word>\n      <Word>xp_getnetname</Word>\n      <Word>xp_grantlogin</Word>\n      <Word>xp_instance_regaddmultistring</Word>\n      <Word>xp_instance_regdeletekey</Word>\n      <Word>xp_instance_regdeletevalue</Word>\n      <Word>xp_instance_regenumkeys</Word>\n      <Word>xp_instance_regenumvalues</Word>\n      <Word>xp_instance_regread</Word>\n      <Word>xp_instance_regremovemultistring</Word>\n      <Word>xp_instance_regwrite</Word>\n      <Word>xp_logevent</Word>\n      <Word>xp_loginconfig</Word>\n      <Word>xp_logininfo</Word>\n      <Word>xp_msver</Word>\n      <Word>xp_msx_enlist</Word>\n      <Word>xp_passAgentInfo</Word>\n      <Word>xp_prop_oledb_provider</Word>\n      <Word>xp_qv</Word>\n      <Word>xp_readerrorlog</Word>\n      <Word>xp_regaddmultistring</Word>\n      <Word>xp_regdeletekey</Word>\n      <Word>xp_regdeletevalue</Word>\n      <Word>xp_regenumkeys</Word>\n      <Word>xp_regenumvalues</Word>\n      <Word>xp_regread</Word>\n      <Word>xp_regremovemultistring</Word>\n      <Word>xp_regwrite</Word>\n      <Word>xp_repl_convert_encrypt_sysadmin_wrapper</Word>\n      <Word>xp_replposteor</Word>\n      <Word>xp_revokelogin</Word>\n      <Word>xp_servicecontrol</Word>\n      <Word>xp_sprintf</Word>\n      <Word>xp_sqlagent_enum_jobs</Word>\n      <Word>xp_sqlagent_is_starting</Word>\n      <Word>xp_sqlagent_monitor</Word>\n      <Word>xp_sqlagent_notify</Word>\n      <Word>xp_sqlagent_param</Word>\n      <Word>xp_sqlmaint</Word>\n      <Word>xp_sscanf</Word>\n      <Word>xp_subdirs</Word>\n      <Word>xp_sysmail_activate</Word>\n      <Word>xp_sysmail_attachment_load</Word>\n      <Word>xp_sysmail_format_query</Word>\n    </Keywords>\n\n    <Rule color=\"ObjectReference1\">([\\d\\w]+)\\.([\\d\\w]+)\\.([\\d\\w]+)</Rule>\n    <Rule color=\"ObjectReference\">([\\d\\w]+)\\.([\\d\\w]+)</Rule>\n\n    <Rule color=\"ObjectReferenceInBrackets1\">([\\d\\w]+)\\.([\\d\\w]+)\\.([\\d\\w]+)</Rule>\n    <Rule color=\"ObjectReferenceInBrackets\">\\[([\\d\\w]+)\\]\\.\\[([\\d\\w]+)\\]\\.\\[([\\d\\w]+)\\]</Rule>\n    <Rule color=\"ObjectReferenceInBrackets\">\\[([\\d\\w]+)\\]\\.\\[([\\d\\w]+)\\]</Rule>\n\n    <Rule color=\"Punctuation\">\n      [?,.;()\\[\\]{}+\\-/%*&lt;&gt;^+~!|&amp;]+\n    </Rule>\n\n    <Rule color=\"MethodCall\">[\\d\\w_]+(?=(\\s*\\())</Rule>\n    <Rule color=\"Variable1\">@@([\\w]+)</Rule>\n    <Rule color=\"Variable\">@([\\w]+)</Rule>\n\n    <!-- Digits -->\n    <Rule color=\"Digits\">\n      \\b0[xX][0-9a-fA-F]+  # hex number\n      |\n      (    \\b\\d+(\\.[0-9]+)?   #number with optional floating point\n      |    \\.[0-9]+           #or just starting with floating point\n      )\n      ([eE][+-]?[0-9]+)? # optional exponent\n    </Rule>\n\n  </RuleSet>\n\n</SyntaxDefinition>"
  },
  {
    "path": "WorkloadViewer/Resources/WorkloadAnalysis.sql",
    "content": "﻿WITH baseData AS (\n\tSELECT \n\t\tDATEDIFF(minute, Base.end_time, bIn.end_time) AS offset_minutes,\n\t\tbWD.sql_hash, \n\t\tbWD.avg_cpu_us, \n\t\tbWD.min_cpu_us, \n\t\tbWD.max_cpu_us, \n\t\tbWD.sum_cpu_us, \n\t\tbWD.avg_reads, \n\t\tbWD.min_reads, \n\t\tbWD.max_reads, \n\t\tbWD.sum_reads, \n\t\tbWD.avg_writes, \n\t\tbWD.min_writes, \n\t\tbWD.max_writes, \n\t\tbWD.sum_writes, \n\t\tbWD.avg_duration_us, \n\t\tbWD.min_duration_us, \n\t\tbWD.max_duration_us, \n\t\tbWD.sum_duration_us, \n\t\tbWD.execution_count,\n\t\tbIn.duration_minutes, \n\t\tbNQ.normalized_text, \n\t\tbNQ.example_text, \n\t\tbAp.application_name, \n\t\tbDB.database_name, \n\t\tbHS.host_name, \n\t\tbLI.login_name\n\tFROM capture.WorkloadDetails AS bWD\n\tINNER JOIN capture.Intervals AS bIn\n\t\tON bIn.interval_id = bWD.interval_id\n\tINNER JOIN capture.NormalizedQueries AS bNQ\n\t\tON bNQ.sql_hash = bWD.sql_hash\n\tINNER JOIN capture.Applications AS bAp\n\t\tON bAp.application_id = bWD.application_id\n\tINNER JOIN capture.Databases AS bDB\n\t\tON bDB.database_id = bWD.database_id\n\tINNER JOIN capture.Hosts AS bHS\n\t\tON bHS.host_id = bWD.host_id\n\tINNER JOIN capture.Logins AS bLI\n\t\tON bLI.login_id = bWD.login_id\n\tCROSS APPLY (\n\t\tSELECT TOP(1) base.end_time\n\t\tFROM capture.Intervals AS base\n\t\tORDER BY interval_id\n\t) AS Base\n)\nSELECT \t(offset_minutes / preaggregation) * preaggregation AS offset_minutes,\n\t\tpreaggregation AS duration_minutes,\n\t\tsql_hash,\n\t\tapplication_name, \n\t\tdatabase_name, \n\t\thost_name, \n\t\tlogin_name,\n\t\tAVG(avg_cpu_us)      AS avg_cpu_us, \n\t\tMIN(min_cpu_us)      AS min_cpu_us, \n\t\tMAX(max_cpu_us)      AS max_cpu_us, \n\t\tSUM(sum_cpu_us)      AS sum_cpu_us, \n\t\tAVG(avg_reads)       AS avg_reads, \n\t\tMIN(min_reads)       AS min_reads, \n\t\tMAX(max_reads)       AS max_reads, \n\t\tSUM(sum_reads)       AS sum_reads, \n\t\tAVG(avg_writes)      AS avg_writes, \n\t\tMIN(min_writes)      AS min_writes, \n\t\tMAX(max_writes)      AS max_writes, \n\t\tSUM(sum_writes)      AS sum_writes, \n\t\tAVG(avg_duration_us) AS avg_duration_us, \n\t\tMIN(min_duration_us) AS min_duration_us, \n\t\tMAX(max_duration_us) AS max_duration_us, \n\t\tSUM(sum_duration_us) AS sum_duration_us, \n\t\tSUM(execution_count) AS execution_count\nFROM baseData\nGROUP BY \n\t\t(offset_minutes / preaggregation) * preaggregation,\n\t\tduration_minutes % preaggregation,\n\t\tsql_hash,\n\t\tapplication_name, \n\t\tdatabase_name, \n\t\thost_name, \n\t\tlogin_name\nORDER BY \n\t\toffset_minutes;"
  },
  {
    "path": "WorkloadViewer/View/ConnectionInfoDialog.xaml",
    "content": "﻿<Dialog:CustomDialog x:Class=\"WorkloadViewer.View.ConnectionInfoDialog\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:WorkloadViewer.View\"\n        mc:Ignorable=\"d\"\n        xmlns:Controls=\"clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro\"\n        xmlns:Dialog=\"clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro\"\n        d:DesignWidth=\"641\"\n        Style=\"{StaticResource NewCustomDialogStyle}\">\n\n\n\n    <StackPanel>\n        <local:ConnectionInfoEditor />\n    </StackPanel>\n\n\n</Dialog:CustomDialog>\n\n"
  },
  {
    "path": "WorkloadViewer/View/ConnectionInfoDialog.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\n\nnamespace WorkloadViewer.View\n{\n    /// <summary>\n    /// Interaction logic for ConnectionInfoDialog.xaml\n    /// </summary>\n    public partial class ConnectionInfoDialog : MahApps.Metro.Controls.Dialogs.CustomDialog\n    {\n        public ConnectionInfoDialog()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/View/ConnectionInfoDialogStyle.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:WorkloadViewer.View\"\n                    xmlns:Controls=\"clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro\"\n                    xmlns:Dialog=\"clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro\" >\n    <Style TargetType=\"{x:Type Dialog:BaseMetroDialog}\"\n        x:Key=\"NewCustomDialogStyle\"\n        BasedOn=\"{StaticResource {x:Type Dialog:BaseMetroDialog}}\">\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Dialog:BaseMetroDialog}\">\n                    <ControlTemplate.Resources>\n                        <Storyboard x:Key=\"DialogShownStoryboard\">\n                            <DoubleAnimation AccelerationRatio=\".9\"\n                                            BeginTime=\"0:0:0\"\n                                            Duration=\"0:0:0.2\"\n                                            Storyboard.TargetProperty=\"Opacity\"\n                                            To=\"1\" />\n                        </Storyboard>\n                    </ControlTemplate.Resources>\n                    <Grid Background=\"{TemplateBinding Background}\">\n                        <Border FocusVisualStyle=\"{x:Null}\"\n                            Focusable=\"False\">\n                            <Grid>\n                                <Grid.RowDefinitions>\n                                    <RowDefinition Height=\"Auto\" />\n                                    <RowDefinition Height=\"*\" />\n                                    <RowDefinition Height=\"Auto\" />\n                                </Grid.RowDefinitions>\n                                <ContentPresenter Grid.Row=\"0\"\n                                                Content=\"{TemplateBinding DialogTop}\" />\n                                <Grid Grid.Row=\"1\">\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"10*\" />\n                                        <ColumnDefinition Width=\"80*\" />\n                                        <ColumnDefinition Width=\"10*\" />\n                                    </Grid.ColumnDefinitions>\n                                    <!--  Content area  -->\n                                    <Grid Grid.Column=\"1\"\n                                        Margin=\"0 10 0 0\">\n                                        <Grid.RowDefinitions>\n                                            <RowDefinition Height=\"Auto\" />\n                                            <RowDefinition Height=\"*\" />\n                                        </Grid.RowDefinitions>\n                                        <TextBlock Grid.Row=\"0\"\n                                                FontSize=\"{DynamicResource DialogTitleFontSize}\"\n                                                Foreground=\"{TemplateBinding Foreground}\"\n                                                Text=\"{TemplateBinding Title}\"\n                                                TextWrapping=\"Wrap\" />\n                                        <ContentPresenter Grid.Row=\"1\"\n                                                        Content=\"{TemplateBinding Content}\" />\n                                    </Grid>\n                                </Grid>\n                                <ContentPresenter Grid.Row=\"2\"\n                                                Content=\"{TemplateBinding DialogBottom}\" />\n                            </Grid>\n                        </Border>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <EventTrigger RoutedEvent=\"Loaded\">\n                            <EventTrigger.Actions>\n                                <BeginStoryboard Storyboard=\"{StaticResource DialogShownStoryboard}\" />\n                            </EventTrigger.Actions>\n                        </EventTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n</ResourceDictionary>"
  },
  {
    "path": "WorkloadViewer/View/ConnectionInfoEditor.xaml",
    "content": "﻿<UserControl x:Class=\"WorkloadViewer.View.ConnectionInfoEditor\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:WorkloadViewer.ViewModel\"\n             xmlns:i=\"http://schemas.microsoft.com/expression/2010/interactivity\"\n             xmlns:command=\"http://www.galasoft.ch/mvvmlight\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"800\">\n\n    <i:Interaction.Triggers>\n        <i:EventTrigger EventName=\"KeyDown\">\n            <command:EventToCommand Command=\"{Binding KeyDownCommand}\" PassEventArgsToCommand=\"True\" />\n        </i:EventTrigger>\n    </i:Interaction.Triggers>\n\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"1*\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"1*\" />\n            <ColumnDefinition Width=\"1*\" />\n        </Grid.ColumnDefinitions>\n        <GroupBox Grid.Column=\"0\" Margin=\"0,0,10,0\">\n            <GroupBox.Header>\n                Baseline\n            </GroupBox.Header>\n            <Grid Margin=\"20\">\n                <Grid.Resources>\n                    <Style TargetType=\"{x:Type TextBox}\">\n                        <Setter Property=\"Margin\" Value=\"0,2,0,2\" />\n                    </Style>\n                </Grid.Resources>\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                </Grid.RowDefinitions>\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition Width=\"1*\" />\n                    <ColumnDefinition Width=\"1*\" />\n                </Grid.ColumnDefinitions>\n                <Label Grid.Row=\"0\" Grid.Column=\"0\">Server</Label>\n                <TextBox Grid.Row=\"0\" Grid.Column=\"1\" Text=\"{Binding BaselineServer, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\n                <Label Grid.Row=\"1\" Grid.Column=\"0\">Database</Label>\n                <TextBox Grid.Row=\"1\" Grid.Column=\"1\" Text=\"{Binding BaselineDatabase, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\n                <Label Grid.Row=\"2\" Grid.Column=\"0\">Schema</Label>\n                <TextBox Grid.Row=\"2\" Grid.Column=\"1\" Text=\"{Binding BaselineSchema, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"3\" Grid.Column=\"0\">UserName</Label>\n                <TextBox Grid.Row=\"3\" Grid.Column=\"1\" Text=\"{Binding BaselineUsername, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"4\" Grid.Column=\"0\">Password</Label>\n                <PasswordBox Grid.Row=\"4\" Grid.Column=\"1\" Margin=\"0,2,0,2\" PasswordChanged=\"Baseline_PasswordChanged\"/>\n            </Grid>\n        </GroupBox>\n        <GroupBox Grid.Column=\"1\" Margin=\"10,0,0,0\">\n            <GroupBox.Header>\n                Benchmark\n            </GroupBox.Header>\n            <Grid Margin=\"20\">\n                <Grid.Resources>\n                    <Style TargetType=\"{x:Type TextBox}\">\n                        <Setter Property=\"Margin\" Value=\"0,2,0,2\" />\n                    </Style>\n                </Grid.Resources>\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                </Grid.RowDefinitions>\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition Width=\"1*\" />\n                    <ColumnDefinition Width=\"1*\" />\n                </Grid.ColumnDefinitions>\n                <Label Grid.Row=\"0\" Grid.Column=\"0\">Server</Label>\n                <TextBox Grid.Row=\"0\" Grid.Column=\"1\" Text=\"{Binding BenchmarkServer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"1\" Grid.Column=\"0\">Database</Label>\n                <TextBox Grid.Row=\"1\" Grid.Column=\"1\" Text=\"{Binding BenchmarkDatabase, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"2\" Grid.Column=\"0\">Schema</Label>\n                <TextBox Grid.Row=\"2\" Grid.Column=\"1\" Text=\"{Binding BenchmarkSchema, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"3\" Grid.Column=\"0\">UserName</Label>\n                <TextBox Grid.Row=\"3\" Grid.Column=\"1\" Text=\"{Binding BenchmarkUsername, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"/>\n                <Label Grid.Row=\"4\" Grid.Column=\"0\">Password</Label>\n                <PasswordBox Grid.Row=\"4\" Grid.Column=\"1\" Margin=\"0,2,0,2\" PasswordChanged=\"Benchmark_PasswordChanged\"/>\n            </Grid>\n        </GroupBox>\n        <DockPanel Grid.Row=\"1\" Grid.ColumnSpan=\"2\" LastChildFill=\"False\">\n            <Button x:Name=\"CancelButton\" DockPanel.Dock=\"Right\" Margin=\"10\" Padding=\"10\" Command=\"{Binding CancelCommand}\">Cancel</Button>\n            <Button x:Name=\"OKButton\" DockPanel.Dock=\"Right\" Margin=\"10\" Padding=\"10\" Command=\"{Binding OKCommand}\">OK</Button>\n        </DockPanel>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "WorkloadViewer/View/ConnectionInfoEditor.xaml.cs",
    "content": "﻿using GalaSoft.MvvmLight.Messaging;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Automation.Peers;\nusing System.Windows.Automation.Provider;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing WorkloadViewer.ViewModel;\n\nnamespace WorkloadViewer.View\n{\n    /// <summary>\n    /// Interaction logic for ConnectionInfoEditor.xaml\n    /// </summary>\n    public partial class ConnectionInfoEditor : UserControl\n    {\n        public ConnectionInfoEditor()\n        {\n            InitializeComponent();\n\n            Messenger.Default.Register<Message>(this, (msg) => ReceiveMessage(msg));\n        }\n\n        private void ReceiveMessage(Message msg)\n        {\n            if(msg.Text == \"OK\")\n            {\n                //Fist of all, remove focus from the current text control and set it to the button\n                Keyboard.Focus(OKButton);\n                // Then fire the click event and its associated command\n                var peer = new ButtonAutomationPeer(OKButton);\n                var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;\n                invokeProv.Invoke();\n            }\n        }\n\n        private void Baseline_PasswordChanged(object sender, RoutedEventArgs e)\n        {\n            if (DataContext != null)\n            { ((dynamic)DataContext).BaselinePassword = ((PasswordBox)sender).Password; }\n        }\n\n        private void Benchmark_PasswordChanged(object sender, RoutedEventArgs e)\n        {\n            if (DataContext != null)\n            { ((dynamic)DataContext).BenchmarkPassword = ((PasswordBox)sender).Password; }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/View/MainWindow.xaml",
    "content": "﻿<Controls:MetroWindow x:Class=\"WorkloadViewer.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:Controls=\"clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro\"\n        xmlns:local=\"clr-namespace:WorkloadViewer.ViewModel\"\n        xmlns:i=\"http://schemas.microsoft.com/expression/2010/interactivity\"\n        xmlns:command=\"http://www.galasoft.ch/mvvmlight\"\n        xmlns:oxy=\"http://oxyplot.org/wpf\"\n        xmlns:Dialog=\"clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro\"\n        xmlns:avalonedit=\"http://icsharpcode.net/sharpdevelop/avalonedit\"\n        mc:Ignorable=\"d\"\n        GlowBrush=\"{DynamicResource AccentColorBrush}\"\n        SaveWindowPosition=\"True\"\n        Height=\"600\" Width=\"1490\"\n        WindowState=\"Maximized\"\n        Title=\"Workload Viewer\"\n        TitleCaps=\"False\"\n        Dialog:DialogParticipation.Register=\"{Binding}\"\n        x:Name=\"MainWin\">\n    <Window.Resources>\n        <local:MainViewModel x:Key=\"MainViewModel\" />\n\n        <Style TargetType=\"DataGridCell\">\n            <Setter Property=\"HorizontalAlignment\" Value=\"Right\" />\n        </Style>\n\n        <CollectionViewSource x:Key=\"WorkloadQueries\" Source=\"{Binding Queries, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}\" />\n\n    </Window.Resources>\n\n    <Window.DataContext>\n        <Binding Source=\"{StaticResource MainViewModel}\" />\n    </Window.DataContext>\n\n    <i:Interaction.Triggers>\n        <i:EventTrigger EventName=\"KeyDown\">\n            <command:EventToCommand Command=\"{Binding KeyDownCommand}\" PassEventArgsToCommand=\"True\" />\n        </i:EventTrigger>\n        <i:EventTrigger EventName=\"Loaded\">\n            <command:EventToCommand Command=\"{Binding LoadedCommand}\" PassEventArgsToCommand=\"True\" />\n        </i:EventTrigger>\n        <i:EventTrigger EventName=\"ContentRendered\">\n            <command:EventToCommand Command=\"{Binding RenderedCommand}\" PassEventArgsToCommand=\"True\" />\n        </i:EventTrigger>\n    </i:Interaction.Triggers>\n\n    <DockPanel>\n        <StatusBar DockPanel.Dock=\"Bottom\">\n            <StatusBar.ItemsPanel>\n                <ItemsPanelTemplate>\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition Width=\"*\" />\n                        </Grid.ColumnDefinitions>\n                    </Grid>\n                </ItemsPanelTemplate>\n            </StatusBar.ItemsPanel>\n            <StatusBarItem>\n                <TextBlock Text=\"{Binding StatusMessage}\" />\n            </StatusBarItem>\n        </StatusBar>\n\n        <TabControl TabStripPlacement=\"Bottom\" DockPanel.Dock=\"Top\" x:Name=\"MainTabControl\">\n            <TabItem Header=\"Workload\">\n                <DockPanel Name=\"ContentPanel\">\n                    <Grid x:Name=\"FiltersGrid\" DockPanel.Dock=\"Top\" Visibility=\"Visible\">\n                        <Border BorderThickness=\"0,0,0,2\" BorderBrush=\"{DynamicResource AccentColorBrush}\" >\n                        <DockPanel>\n                            <Grid DockPanel.Dock=\"Top\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"1*\" />\n                                    <ColumnDefinition Width=\"1*\" />\n                                    <ColumnDefinition Width=\"1*\" />\n                                    <ColumnDefinition Width=\"1*\" />\n                                </Grid.ColumnDefinitions>\n                                <Grid.RowDefinitions>\n                                    <RowDefinition Height=\"*\" />\n                                </Grid.RowDefinitions>\n\n                                <DataGrid ItemsSource=\"{Binding Path=ApplicationList}\" AutoGenerateColumns=\"False\" CanUserResizeColumns=\"False\" CanUserAddRows=\"False\" Grid.Column=\"0\" MaxHeight=\"150\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\" Margin=\"5\">\n                                    <DataGrid.Columns>\n                                        <DataGridCheckBoxColumn ElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            EditingElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            Header=\"\"\n                                            Binding=\"{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\n                                        />\n                                        <DataGridTextColumn \n                                            Header=\"Application Name\"\n                                            Binding=\"{Binding Name, Mode=OneWay}\"\n                                            IsReadOnly=\"True\"\n                                        />\n                                    </DataGrid.Columns>\n                                    <DataGrid.CellStyle>\n                                        <Style TargetType=\"DataGridCell\">\n                                            <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\n                                            <Style.Triggers>\n                                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                    <Setter Property=\"IsEditing\" Value=\"True\" />\n                                                </Trigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.CellStyle>\n                                    <DataGrid.RowStyle>\n                                        <Style TargetType=\"DataGridRow\">\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                                    <Setter Property=\"Background\" Value=\"Transparent\" />\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.RowStyle>\n                                    <DataGrid.Resources>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"Transparent\"/>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#000000\"/>\n                                    </DataGrid.Resources>\n                                </DataGrid>\n\n\n                                <DataGrid ItemsSource=\"{Binding Path=DatabaseList}\" AutoGenerateColumns=\"False\" CanUserResizeColumns=\"False\" CanUserAddRows=\"False\" Grid.Column=\"1\" MaxHeight=\"150\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\" Margin=\"5\">\n                                    <DataGrid.Columns>\n                                        <DataGridCheckBoxColumn ElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            EditingElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            Header=\"\"\n                                            Binding=\"{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\n                                        />\n                                        <DataGridTextColumn \n                                            Header=\"Database Name\"\n                                            Binding=\"{Binding Name, Mode=OneWay}\"\n                                            IsReadOnly=\"True\"\n                                        />\n                                    </DataGrid.Columns>\n                                    <DataGrid.CellStyle>\n                                        <Style TargetType=\"DataGridCell\">\n                                            <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\n                                            <Style.Triggers>\n                                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                    <Setter Property=\"IsEditing\" Value=\"True\" />\n                                                </Trigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.CellStyle>\n                                    <DataGrid.RowStyle>\n                                        <Style TargetType=\"DataGridRow\">\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                                    <Setter Property=\"Background\" Value=\"Transparent\" />\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.RowStyle>\n                                    <DataGrid.Resources>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"Transparent\"/>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#000000\"/>\n                                    </DataGrid.Resources>\n                                </DataGrid>\n\n                                <DataGrid ItemsSource=\"{Binding Path=HostList}\" AutoGenerateColumns=\"False\" CanUserResizeColumns=\"False\" CanUserAddRows=\"False\" Grid.Column=\"2\" MaxHeight=\"150\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\" Margin=\"5\">\n                                    <DataGrid.Columns>\n                                        <DataGridCheckBoxColumn ElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            EditingElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                            Header=\"\"\n                                            Binding=\"{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\n                                        />\n                                        <DataGridTextColumn \n                                            Header=\"Host Name\"\n                                            Binding=\"{Binding Name, Mode=OneWay}\"\n                                            IsReadOnly=\"True\"\n                                        />\n                                    </DataGrid.Columns>\n                                    <DataGrid.CellStyle>\n                                        <Style TargetType=\"DataGridCell\">\n                                            <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\n                                            <Style.Triggers>\n                                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                    <Setter Property=\"IsEditing\" Value=\"True\" />\n                                                </Trigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.CellStyle>\n                                    <DataGrid.RowStyle>\n                                        <Style TargetType=\"DataGridRow\">\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                                    <Setter Property=\"Background\" Value=\"Transparent\" />\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.RowStyle>\n                                    <DataGrid.Resources>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"Transparent\"/>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#000000\"/>\n                                    </DataGrid.Resources>\n                                </DataGrid>\n\n                                <DataGrid ItemsSource=\"{Binding Path=LoginList}\" AutoGenerateColumns=\"False\" CanUserResizeColumns=\"False\" CanUserAddRows=\"False\" Grid.Column=\"3\" MaxHeight=\"150\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\" Margin=\"5\">\n                                    <DataGrid.Columns>\n                                        <DataGridCheckBoxColumn ElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                        EditingElementStyle=\"{DynamicResource MetroDataGridCheckBox}\"\n                                        Header=\"\"\n                                        Binding=\"{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\n                                    />\n                                    <DataGridTextColumn \n                                        Header=\"Login Name\"\n                                        Binding=\"{Binding Name, Mode=OneWay}\"\n                                        IsReadOnly=\"True\"\n                                    />\n                                    </DataGrid.Columns>\n                                    <DataGrid.CellStyle>\n                                        <Style TargetType=\"DataGridCell\">\n                                            <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\n                                            <Style.Triggers>\n                                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                    <Setter Property=\"IsEditing\" Value=\"True\" />\n                                                </Trigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.CellStyle>\n                                    <DataGrid.RowStyle>\n                                        <Style TargetType=\"DataGridRow\">\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                                    <Setter Property=\"Background\" Value=\"Transparent\" />\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </DataGrid.RowStyle>\n                                    <DataGrid.Resources>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"Transparent\"/>\n                                        <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#000000\"/>\n                                    </DataGrid.Resources>\n                                </DataGrid>\n\n                            </Grid>\n                            <Button DockPanel.Dock=\"Right\" Width=\"100\" Margin=\"5\" Command=\"{Binding ApplyCommand}\">Apply</Button>\n                        </DockPanel>\n                        </Border>\n                    </Grid>\n                        \n\n                    <ScrollViewer VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\" DockPanel.Dock=\"Bottom\">\n                        <Grid x:Name=\"MainGrid\">\n                            <Grid.RowDefinitions>\n                                <RowDefinition Height=\"300\" />\n                                <RowDefinition Height=\"300\" />\n                                <RowDefinition Height=\"300\" />\n                            </Grid.RowDefinitions>\n                            <Grid.ColumnDefinitions>\n                                <ColumnDefinition Width=\"*\" />\n                            </Grid.ColumnDefinitions>\n\n                            <oxy:PlotView x:Name=\"CpuChart\" Model=\"{Binding CpuPlotModel, Mode=OneWay}\" IsMouseWheelEnabled=\"False\" Margin=\"10\" Grid.Row=\"0\"  />\n                            <oxy:PlotView x:Name=\"DurationChart\" Model=\"{Binding DurationPlotModel, Mode=OneWay}\" IsMouseWheelEnabled=\"False\" Margin=\"10\" Grid.Row=\"1\"/>\n                            <oxy:PlotView x:Name=\"BatchesChart\" Model=\"{Binding BatchesPlotModel, Mode=OneWay}\" IsMouseWheelEnabled=\"False\" Margin=\"10\" Grid.Row=\"2\"/>\n\n                        </Grid>\n                    </ScrollViewer>\n                </DockPanel>\n            </TabItem>\n\n            <TabItem Header=\"Queries\">\n                <Grid Margin=\"5\">\n                    <Grid.RowDefinitions>\n                        <RowDefinition Height=\"3*\" />\n                        <RowDefinition Height=\"1*\" />\n                    </Grid.RowDefinitions>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"*\" />\n                    </Grid.ColumnDefinitions>\n                        <DataGrid x:Name=\"Queries\" \n                                  AutoGenerateColumns=\"False\" \n                                  ItemsSource=\"{Binding Source={StaticResource WorkloadQueries}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}\" \n                                  IsReadOnly=\"True\" \n                                  SelectionUnit=\"FullRow\" \n                                  SelectionMode=\"Single\" \n                                  MouseDoubleClick=\"DataGridDoubleClick\"\n                                  VerticalScrollBarVisibility=\"Auto\" \n                                  HorizontalScrollBarVisibility=\"Auto\"\n                                  MaxHeight=\"3000\"\n                                  RowHeight=\"23\"\n                                  Grid.Row=\"0\">\n                            <DataGrid.Resources>\n                                <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"#01B8AA\"/>\n                                <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#FFFFFF\"/>\n                                <SolidColorBrush x:Key=\"{x:Static SystemColors.InactiveSelectionHighlightBrushKey}\" Color=\"Transparent\"/>\n                            </DataGrid.Resources>\n                            <DataGrid.RowStyle>\n                                <Style TargetType=\"DataGridRow\">\n                                    <Style.Triggers>\n                                        <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                            <Setter Property=\"Background\" Value=\"#01B8AA\" />\n                                        </DataTrigger>\n                                    </Style.Triggers>\n                                </Style>\n                            </DataGrid.RowStyle>\n                            <DataGrid.Columns>\n                                <DataGridTextColumn Header=\"Hash\"                 Binding=\"{Binding query_hash}\" />\n                                <DataGridTextColumn Header=\"query_text\"           Binding=\"{Binding query_text}\" Visibility=\"Collapsed\"/>\n                                <DataGridTextColumn Header=\"query_normalized\"     Binding=\"{Binding query_normalized}\" Visibility=\"Collapsed\" />\n                                <DataGridTextColumn Header=\"sum_duration_us\"      Binding=\"{Binding sum_duration_us, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"sum_duration_us2\"     Binding=\"{Binding sum_duration_us2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_sum_duration_us\" Binding=\"{Binding diff_sum_duration_us, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"avg_duration_us\"      Binding=\"{Binding avg_duration_us, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"avg_duration_us2\"     Binding=\"{Binding avg_duration_us2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_avg_duration_us\" Binding=\"{Binding diff_avg_duration_us, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"sum_cpu_us\"           Binding=\"{Binding sum_cpu_us, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"sum_cpu_us2\"          Binding=\"{Binding sum_cpu_us2, StringFormat=N0}\"  Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"diff_sum_cpu_us\"      Binding=\"{Binding diff_sum_cpu_us, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"avg_cpu_us\"           Binding=\"{Binding avg_cpu_us, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"avg_cpu_us2\"          Binding=\"{Binding avg_cpu_us2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_avg_cpu_us\"      Binding=\"{Binding diff_avg_cpu_us, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"sum_reads\"            Binding=\"{Binding sum_reads, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"sum_reads2\"           Binding=\"{Binding sum_reads2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_sum_reads\"       Binding=\"{Binding diff_sum_reads, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"avg_reads\"            Binding=\"{Binding avg_reads, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"avg_reads2\"           Binding=\"{Binding avg_reads2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_avg_reads\"       Binding=\"{Binding diff_avg_reads, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                                <DataGridTextColumn Header=\"execution_count\"      Binding=\"{Binding execution_count, StringFormat=N0}\" />\n                                <DataGridTextColumn Header=\"execution_count2\"     Binding=\"{Binding execution_count2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                                <DataGridTextColumn Header=\"diff_execution_count\" Binding=\"{Binding diff_execution_count, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                        </DataGrid.Columns>\n                        </DataGrid>\n                    <avalonedit:TextEditor \n                        SyntaxHighlighting=\"SQL\" \n                        x:Name=\"QueryText\" \n                        Grid.Row=\"1\" \n                        VerticalScrollBarVisibility=\"Visible\"\n                        HorizontalScrollBarVisibility=\"Auto\"\n                        FontFamily=\"Consolas\"\n                        FontSize=\"14\"\n                        IsReadOnly=\"True\"\n                        BorderThickness=\"0,1,0,0\"\n                        Padding=\"10,20,10,20\"\n                        ShowLineNumbers=\"True\"\n                        Document=\"{Binding ElementName=Queries, Path=SelectedItem.document}\" \n                        MouseDoubleClick=\"QueryText_MouseDoubleClick\" \n                        Tag=\"{Binding ElementName=Queries, Path=SelectedItem.query_hash}\" \n                    />\n                </Grid>\n            </TabItem>\n\n            <TabItem Header=\"Query Details\" x:Name=\"QueryDetailsTabItem\">\n                <Grid x:Name=\"DetailsGrid\" Margin=\"5\">\n                    <Grid.RowDefinitions>\n                        <RowDefinition Height=\"1*\" />\n                        <RowDefinition Height=\"1*\" />\n                        <RowDefinition Height=\"1*\" />\n                    </Grid.RowDefinitions>\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"*\" />\n                    </Grid.ColumnDefinitions>\n\n                    <avalonedit:TextEditor \n                        SyntaxHighlighting=\"SQL\" \n                        x:Name=\"QueryDetailText\" \n                        Grid.Row=\"0\" \n                        VerticalScrollBarVisibility=\"Visible\"\n                        HorizontalScrollBarVisibility=\"Auto\"\n                        FontFamily=\"Consolas\"\n                        FontSize=\"14\"\n                        IsReadOnly=\"True\"\n                        BorderThickness=\"0\"\n                        Padding=\"0,20,0,20\"\n                        ShowLineNumbers=\"True\"\n                        Document=\"{Binding ElementName=Queries, Path=SelectedItem.document}\" \n                        MouseDoubleClick=\"QueryText_MouseDoubleClick\" \n                        Tag=\"{Binding ElementName=Queries, Path=SelectedItem.query_hash}\"\n                    />\n                    \n                    <DataGrid x:Name=\"QueryStats\" AutoGenerateColumns=\"False\" ItemsSource=\"{Binding ElementName=Queries, Path=SelectedItem.querydetails.QueryStats, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}\" IsReadOnly=\"True\" SelectionUnit=\"FullRow\" SelectionMode=\"Single\" Grid.Row=\"1\">\n                        <DataGrid.Resources>\n                            <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightBrushKey}\" Color=\"#01B8AA\"/>\n                            <SolidColorBrush x:Key=\"{x:Static SystemColors.HighlightTextBrushKey}\" Color=\"#FFFFFF\"/>\n                            <SolidColorBrush x:Key=\"{x:Static SystemColors.InactiveSelectionHighlightBrushKey}\" Color=\"Transparent\"/>\n                        </DataGrid.Resources>\n                        <DataGrid.RowStyle>\n                            <Style TargetType=\"DataGridRow\">\n                                <Style.Triggers>\n                                    <DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"True\">\n                                        <Setter Property=\"Background\" Value=\"#01B8AA\" />\n                                    </DataTrigger>\n                                </Style.Triggers>\n                            </Style>\n                        </DataGrid.RowStyle>\n                        <DataGrid.Columns>\n                            <DataGridTextColumn Header=\"Application\"          Binding=\"{Binding Application}\" />\n                            <DataGridTextColumn Header=\"Database\"             Binding=\"{Binding Database}\" />\n                            <DataGridTextColumn Header=\"Host\"                 Binding=\"{Binding Host}\" />\n                            <DataGridTextColumn Header=\"Login\"                Binding=\"{Binding Login}\" /> \n                            <DataGridTextColumn Header=\"avg_duration_us\"      Binding=\"{Binding avg_duration_us, StringFormat=N0}\" />\n                            <DataGridTextColumn Header=\"avg_duration_us2\"     Binding=\"{Binding avg_duration_us2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                            <DataGridTextColumn Header=\"avg_cpu_us\"           Binding=\"{Binding avg_cpu_us, StringFormat=N0}\" />\n                            <DataGridTextColumn Header=\"avg_cpu_us2\"          Binding=\"{Binding avg_cpu_us2, StringFormat=N0}\"  Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\"/>\n                            <DataGridTextColumn Header=\"avg_reads\"            Binding=\"{Binding avg_reads, StringFormat=N0}\" />\n                            <DataGridTextColumn Header=\"avg_reads2\"           Binding=\"{Binding avg_reads2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                            <DataGridTextColumn Header=\"execution_count\"      Binding=\"{Binding execution_count, StringFormat=N0}\" />\n                            <DataGridTextColumn Header=\"execution_count2\"     Binding=\"{Binding execution_count2, StringFormat=N0}\" Visibility=\"{Binding CompareModeVisibility, Source={StaticResource MainViewModel}}\" />\n                        </DataGrid.Columns>\n                    </DataGrid>\n                    \n                    <oxy:PlotView x:Name=\"QueryDetailChart\" Model=\"{Binding ElementName=Queries, Path=SelectedItem.querydetails.DetailPlotModel, Mode=OneWay}\" Margin=\"10\" Grid.Row=\"2\"/>\n                </Grid>\n             </TabItem>\n        </TabControl>\n    </DockPanel>\n</Controls:MetroWindow>\n"
  },
  {
    "path": "WorkloadViewer/View/MainWindow.xaml.cs",
    "content": "﻿using GalaSoft.MvvmLight.Messaging;\nusing ICSharpCode.AvalonEdit;\nusing MahApps.Metro.Controls;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing WorkloadViewer.ViewModel;\nusing Path = System.IO.Path;\n\nnamespace WorkloadViewer\n{\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : MetroWindow\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            Messenger.Default.Register<SortColMessage>(this, (msg) => ReceiveSortMessage(msg));\n\n            \n            using (var stream = new MemoryStream(WorkloadViewer.Properties.Resources.TSQL))\n            {\n                using (var reader = new System.Xml.XmlTextReader(stream))\n                {\n                    var highlighting = \n                        ICSharpCode.AvalonEdit.Highlighting.Xshd.HighlightingLoader.Load(reader, ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance);\n                    QueryText.SyntaxHighlighting = highlighting;\n                    QueryDetailText.SyntaxHighlighting = highlighting;\n                }\n            }\n        }\n\n        private void ReceiveSortMessage(SortColMessage msg)\n        {\n            try\n            {\n                var dgc = Queries.Columns.First(el => el.Header.ToString().Equals(msg.ColumnName));\n                if(dgc != null)\n                {\n                    dgc.SortDirection = msg.Direction;\n                    var sd = new SortDescription(dgc.SortMemberPath, msg.Direction);\n                    var cvs = (CollectionViewSource)Resources[\"WorkloadQueries\"];\n                    cvs.SortDescriptions.Clear();\n                    cvs.SortDescriptions.Add(new SortDescription(dgc.SortMemberPath, msg.Direction));\n                }\n            }\n            catch(Exception)\n            {\n                //swallow\n            }\n        }\n\n        private void DataGridDoubleClick(object sender, MouseButtonEventArgs e)\n        {\n            if(((DataGrid)sender).SelectedItem == null)\n            {\n                return;\n            }\n            Dispatcher.BeginInvoke((Action)(() => MainTabControl.SelectedIndex = 2));\n        }\n\n        private void QueryText_MouseDoubleClick(object sender, MouseButtonEventArgs e)\n        {\n            OpenFileWithDefaultApp(sender);\n        }\n\n        private void OpenFileWithDefaultApp(object sender)\n        {\n            // save text to a temp file and open with windows\n            try\n            {\n                var editor = (TextEditor)sender;\n                var docPath = Path.Combine(Path.GetTempPath(), editor.Tag + \".sql\");\n\n                // Write the string array to a new file named \"WriteLines.txt\".\n                using (var outputFile = new StreamWriter(docPath))\n                {\n                    outputFile.WriteLine(editor.Text);\n                }\n                System.Diagnostics.Process.Start(docPath);\n            }\n            catch (Exception)\n            {\n                // swallow\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/ConnectionInfoEditorViewModel.cs",
    "content": "﻿using GalaSoft.MvvmLight;\nusing GalaSoft.MvvmLight.Command;\nusing GalaSoft.MvvmLight.Messaging;\nusing MahApps.Metro.Controls.Dialogs;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public class ConnectionInfoEditorViewModel : ViewModelBase\n    {\n        private string _baselineServer;\n        private string _baselineDatabase;\n\n        public string BaselineServer {\n            get { return _baselineServer; }\n            set {\n                _baselineServer = value;\n                if (String.IsNullOrEmpty(BenchmarkServer))\n                {\n                    BenchmarkServer = _baselineServer;\n                    RaisePropertyChanged(\"BenchmarkServer\");\n                }\n                    \n            }\n        }\n        public string BaselineDatabase\n        {\n            get { return _baselineDatabase; }\n            set\n            {\n                _baselineDatabase = value;\n                if (String.IsNullOrEmpty(BenchmarkDatabase))\n                {\n                    BenchmarkDatabase = _baselineDatabase;\n                    RaisePropertyChanged(\"BenchmarkDatabase\");\n                }\n\n            }\n        }\n        public string BaselineSchema { get; set; }\n        public string BaselineUsername { get; set; }\n        public string BaselinePassword { get; set; }\n\n        public string BenchmarkServer { get; set; }\n        public string BenchmarkDatabase { get; set; }\n        public string BenchmarkSchema { get; set; }\n        public string BenchmarkUsername { get; set; }\n        public string BenchmarkPassword { get; set; }\n\n\n        public ICommand CancelCommand { get; set; }\n        public ICommand OKCommand { get; set; }\n        public ICommand KeyDownCommand { get; set; }\n\n        public bool Cancel = false;\n        private IDialogCoordinator _dialogCoordinator;\n        public Exception Exception;\n        public MainViewModel Context;\n        public BaseMetroDialog Dialog;\n\n        public ConnectionInfoEditorViewModel()\n        {\n            CancelCommand = new RelayCommand<RoutedEventArgs>(Cancel_Pressed);\n            OKCommand = new RelayCommand<RoutedEventArgs>(OK_Pressed);\n            KeyDownCommand = new RelayCommand<KeyEventArgs>(KeyDown);\n            _dialogCoordinator = DialogCoordinator.Instance;\n            Cancel = false;\n            Exception = null;\n        }\n\n        private void KeyDown(KeyEventArgs e)\n        {\n            if (e.Key == Key.Enter)\n            {\n                var msg = new Message(\"OK\");\n                Messenger.Default.Send<Message>(msg);\n            }\n        }\n\n        private async void Cancel_Pressed(RoutedEventArgs e)\n        {\n            Cancel = true;\n            await _dialogCoordinator.HideMetroDialogAsync(Context, Dialog);\n            //App.Current.Shutdown();\n        }\n\n        private async void OK_Pressed(RoutedEventArgs e)\n        {\n            Cancel = false;\n            try\n            {\n                Context.SetConnectionInfo(this);\n            }\n            catch (Exception ex)\n            {\n                Exception = ex;\n            }\n            finally\n            {\n                if(Dialog.IsVisible)\n                {\n                    await _dialogCoordinator.HideMetroDialogAsync(Context, Dialog);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/DictionaryExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public static class DictionaryExtensions\n    {\n        public static void AddOrUpdate<TKey, TValue>(\n            this IDictionary<TKey, TValue> dict,\n            TKey key,\n            TValue addValue)\n        {\n            if (dict.ContainsKey(key))\n            {\n                dict[key] = addValue;\n            }\n            else\n            {\n                dict.Add(key, addValue);\n            }\n        }\n\n        public static TValue AddOrUpdate<TKey, TValue>(\n            this IDictionary<TKey, TValue> dict,\n            TKey key,\n            TValue addValue,\n            Func<TKey, TValue, TValue> updateValueFactory)\n        {\n            TValue existing;\n            if (dict.TryGetValue(key, out existing))\n            {\n                addValue = updateValueFactory(key, existing);\n                dict[key] = addValue;\n            }\n            else\n            {\n                dict.Add(key, addValue);\n            }\n\n            return addValue;\n        }\n\n\n        public static TValue AddOrUpdate<TKey, TValue>(\n            this IDictionary<TKey, TValue> dict,\n            TKey key,\n            Func<TKey, TValue> addValueFactory,\n            Func<TKey, TValue, TValue> updateValueFactory)\n        {\n            TValue existing;\n            if (dict.TryGetValue(key, out existing))\n            {\n                existing = updateValueFactory(key, existing);\n                dict[key] = existing;\n            }\n            else\n            {\n                existing = addValueFactory(key);\n                dict.Add(key, existing);\n            }\n\n            return existing;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/FilterDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public class FilterDefinition : IComparable, IEquatable<FilterDefinition>\n    {\n        public string Name { get; set; }\n        public bool IsChecked { get; set; }\n\n        public int CompareTo(object obj)\n        {\n            var result = -1;\n            if(obj is FilterDefinition)\n            {\n                result = Name.CompareTo(((FilterDefinition)obj).Name);\n            }\n            return result;\n        }\n\n        public bool Equals(FilterDefinition other)\n        {\n            return CompareTo(other) == 0;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/LinqExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadTools.Util\n{\n    public static class LinqExtensions\n    {\n        public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(\n        this IEnumerable<TLeft> left,\n        IEnumerable<TRight> right,\n        Func<TLeft, TKey> leftKeySelector,\n        Func<TRight, TKey> rightKeySelector,\n        Func<TLeft, TRight, TKey, TResult> resultSelector,\n        IEqualityComparer<TKey> comparator = null,\n        TLeft defaultLeft = default(TLeft),\n        TRight defaultRight = default(TRight))\n        {\n            if (left == null)\n            {\n                throw new ArgumentNullException(\"left\");\n            }\n\n            if (right == null)\n            {\n                throw new ArgumentNullException(\"right\");\n            }\n\n            if (leftKeySelector == null)\n            {\n                throw new ArgumentNullException(\"leftKeySelector\");\n            }\n\n            if (rightKeySelector == null)\n            {\n                throw new ArgumentNullException(\"rightKeySelector\");\n            }\n\n            if (resultSelector == null)\n            {\n                throw new ArgumentNullException(\"resultSelector\");\n            }\n\n            comparator = comparator ?? EqualityComparer<TKey>.Default;\n            return FullOuterJoinIterator(left, right, leftKeySelector, rightKeySelector, resultSelector, comparator, defaultLeft, defaultRight);\n        }\n\n        internal static IEnumerable<TResult> FullOuterJoinIterator<TLeft, TRight, TKey, TResult>(\n            this IEnumerable<TLeft> left,\n            IEnumerable<TRight> right,\n            Func<TLeft, TKey> leftKeySelector,\n            Func<TRight, TKey> rightKeySelector,\n            Func<TLeft, TRight, TKey, TResult> resultSelector,\n            IEqualityComparer<TKey> comparator,\n            TLeft defaultLeft,\n            TRight defaultRight)\n        {\n            var leftLookup = left.ToLookup(leftKeySelector, comparator);\n            var rightLookup = right.ToLookup(rightKeySelector, comparator);\n            var keys = leftLookup.Select(g => g.Key).Union(rightLookup.Select(g => g.Key), comparator);\n\n            foreach (var key in keys)\n            {\n                foreach (var leftValue in leftLookup[key].DefaultIfEmpty(defaultLeft))\n                {\n                    foreach (var rightValue in rightLookup[key].DefaultIfEmpty(defaultRight))\n                    {\n                        yield return resultSelector(leftValue, rightValue, key);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/MainViewModel.cs",
    "content": "using GalaSoft.MvvmLight;\nusing GalaSoft.MvvmLight.Command;\nusing MahApps.Metro.Controls.Dialogs;\nusing OxyPlot;\nusing OxyPlot.Axes;\nusing OxyPlot.Series;\nusing System;\nusing System.Collections.Generic;\nusing System.Windows.Input;\nusing System.Linq;\nusing WorkloadViewer.Model;\nusing System.Data;\nusing System.Windows;\nusing GalaSoft.MvvmLight.Messaging;\nusing NLog;\nusing System.Threading.Tasks;\nusing System.Threading;\nusing WorkloadTools.Util;\n\nnamespace WorkloadViewer.ViewModel\n{\n\n    public class MainViewModel : ViewModelBase\n    {\n\n        private static Logger logger = LogManager.GetCurrentClassLogger();\n\n        public bool CompareMode\n        {\n            get\n            {\n                return _benchmarkWorkloadAnalysis != null;\n            }\n        }\n\n        public Visibility CompareModeVisibility\n        {\n            get\n            {\n                if(CompareMode)\n                {\n                    return Visibility.Visible;\n                }\n                else\n                {\n                    return Visibility.Collapsed;\n                }\n            }\n        }\n\n        internal Options _options;\n        internal bool _invalidOptions = false;\n        private WorkloadAnalysis _baselineWorkloadAnalysis;\n        private WorkloadAnalysis _benchmarkWorkloadAnalysis;\n\n        public string StatusMessage { get; set; }\n\n        private PlotModel[] PlotModels = new PlotModel[3];\n\n        public PlotModel CpuPlotModel { get; private set; }\n        public PlotModel DurationPlotModel { get; private set; }\n        public PlotModel BatchesPlotModel { get; private set; }\n\n\n        public List<FilterDefinition> HostList { get; set; }\n        public List<FilterDefinition> ApplicationList { get; set; }\n        public List<FilterDefinition> DatabaseList { get; set; }\n        public List<FilterDefinition> LoginList { get; set; }\n\n        public ICommand LoadedCommand { get; set; }\n        public ICommand RenderedCommand { get; set; }\n        public ICommand KeyDownCommand { get; set; }\n        public ICommand ApplyCommand { get; set; }\n\n        public IEnumerable<object> Queries { get; private set; }\n\n        public bool Initialized { get; private set; } = false;\n\n\n        private IDialogCoordinator _dialogCoordinator;\n        private DateTime _lastAxisAdjust = DateTime.Now;\n\n        public MainViewModel()\n        {\n            LoadedCommand = new RelayCommand<EventArgs>(Loaded);\n            RenderedCommand = new RelayCommand<EventArgs>(Rendered);\n            KeyDownCommand = new RelayCommand<KeyEventArgs>(KeyDown);\n            ApplyCommand = new RelayCommand<EventArgs>(ApplyFilters);\n            _dialogCoordinator = DialogCoordinator.Instance;\n            PlotModels = new PlotModel[3];\n        }\n\n        private void ApplyFilters(EventArgs obj)\n        {\n            InitializeCharts();\n            InitializeQueries();\n            RefreshAllCharts();\n        }\n\n        private void Rendered(EventArgs ev)\n        {\n            if (_invalidOptions)\n            {\n                ShowStatusMessage(\"ShowConnectionInfoDialog\");\n                ShowConnectionInfoDialog();\n            }\n            else\n            {\n                ShowStatusMessage(\"Initializing\");\n                InitializeAll();\n                ShowStatusMessage(\"Initialized\");\n            }\n        }\n\n        private async void InitializeAll()\n        {\n            var controller = await _dialogCoordinator.ShowProgressAsync(this, \"Loading data\", String.Empty, false);\n            controller.SetIndeterminate();\n\n            try\n            {\n                Initialized = false;\n                await Task.Run(() =>\n                    {\n                        InitializeWorkloadAnalysis();\n                        InitializeFilters();\n                        InitializeCharts();\n                        RefreshAllCharts();\n                    });\n\n                // This cannot be run async due to threading errors\n                // in AvalonEdit.TextEditor\n                // \"TextDocument can be accessed only from the thread that owns it\"\n                InitializeQueries();\n                Initialized = true;\n            }\n            catch (Exception e)\n            {\n                ShowStatusMessage($\"Exception: {e.Message}\");\n                await _dialogCoordinator.ShowMessageAsync(this, \"WorkloadViewer\", \"Unable to load data: \" + e.Message);\n                ShowConnectionInfoDialog();\n            }\n            finally\n            {\n                await controller.CloseAsync();\n                while (controller.IsOpen)\n                {\n                    await controller.CloseAsync();\n                    Thread.Sleep(5);\n                }\n            }\n        }\n\n        private async void ShowConnectionInfoDialog()\n        {\n            var editor = new View.ConnectionInfoDialog();\n            var viewModel = new ConnectionInfoEditorViewModel() { Context = this, Dialog = editor };\n            editor.DataContext = viewModel;\n            viewModel.BaselineServer = _options.BaselineServer;\n            viewModel.BaselineDatabase = _options.BaselineDatabase;\n            viewModel.BaselineSchema = _options.BaselineSchema;\n            viewModel.BaselineUsername = _options.BaselineUsername;\n            viewModel.BaselinePassword = _options.BaselinePassword;\n            viewModel.BenchmarkServer = _options.BenchmarkServer;\n            viewModel.BenchmarkDatabase = _options.BenchmarkDatabase;\n            viewModel.BenchmarkSchema = _options.BenchmarkSchema;\n            viewModel.BenchmarkUsername = _options.BenchmarkUsername;\n            viewModel.BenchmarkPassword = _options.BenchmarkPassword;\n            await _dialogCoordinator.ShowMetroDialogAsync(this, editor);\n        }\n\n\n        public async void SetConnectionInfo(ConnectionInfoEditorViewModel viewModel)\n        {\n            _options.BaselineServer = viewModel.BaselineServer;\n            _options.BaselineDatabase = viewModel.BaselineDatabase;\n            _options.BaselineSchema = viewModel.BaselineSchema;\n            _options.BaselineUsername = viewModel.BaselineUsername;\n            _options.BaselinePassword = viewModel.BaselinePassword;\n\n            _options.BenchmarkServer = viewModel.BenchmarkServer;\n            _options.BenchmarkDatabase = viewModel.BenchmarkDatabase;\n            _options.BenchmarkSchema = viewModel.BenchmarkSchema;\n            _options.BenchmarkUsername = viewModel.BenchmarkUsername;\n            _options.BenchmarkPassword = viewModel.BenchmarkPassword;\n\n            _invalidOptions = false;\n\n            ShowStatusMessage(\"Pre InitializeAll\");\n            // now that the options are filled, I can invoke the initialization\n            InitializeAll();\n            BaseMetroDialog showingDialog = null;\n            showingDialog = await _dialogCoordinator.GetCurrentDialogAsync<BaseMetroDialog>(this);\n            if(showingDialog != null)\n            {\n                await _dialogCoordinator.HideMetroDialogAsync(this, showingDialog);\n            }\n            \n            ShowStatusMessage(\"Post InitializeAll\");\n        }\n\n\n        private void Loaded(EventArgs ev)\n        {\n            if (!ParseOptions())\n            {\n                _invalidOptions = true;\n            }\n        }\n\n        private void InitializeWorkloadAnalysis()\n        {\n            _baselineWorkloadAnalysis = new WorkloadAnalysis() { Name = \"Baseline\" };\n            _baselineWorkloadAnalysis.ConnectionInfo = new SqlConnectionInfo()\n            {\n                ServerName = _options.BaselineServer,\n                DatabaseName = _options.BaselineDatabase,\n                SchemaName = _options.BaselineSchema,\n                UserName = _options.BaselineUsername,\n                Password = _options.BaselinePassword\n            };\n            _baselineWorkloadAnalysis.Load();\n\n            if(_options.BenchmarkSchema != null)\n            {\n                _benchmarkWorkloadAnalysis = new WorkloadAnalysis() { Name = \"Benchmark\" };\n                _benchmarkWorkloadAnalysis.ConnectionInfo = new SqlConnectionInfo()\n                {\n                    ServerName = _options.BenchmarkServer,\n                    DatabaseName = _options.BenchmarkDatabase,\n                    SchemaName = _options.BenchmarkSchema,\n                    UserName = _options.BenchmarkUsername,\n                    Password = _options.BenchmarkPassword\n                };\n                _benchmarkWorkloadAnalysis.Load();\n            }\n        }\n\n\n        private void InitializeQueries()\n        {\n            // Initialize the queries\n            logger.Info(\"Entering baseline evaluation\");\n\n            var zoomIsSet = PlotModels[0].DefaultXAxis != null;\n\n            double xstart = 0;\n            double xend = 0;\n\n            if (zoomIsSet)\n            {\n                xstart = PlotModels[0].DefaultXAxis.ActualMinimum;\n                xend = PlotModels[0].DefaultXAxis.ActualMaximum;\n                if (xstart < 0)\n                {\n                    xstart = 0;\n                }\n            }\n\n            var baseline = from t in _baselineWorkloadAnalysis.Points\n                           where ApplicationList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.ApplicationName)\n                                && HostList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.HostName)\n                                && DatabaseList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.DatabaseName)\n                                && LoginList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.LoginName)\n                                && (!zoomIsSet || t.OffsetMinutes >= xstart )\n                                && (!zoomIsSet || t.OffsetMinutes <= xend)\n                           group t by new\n                           {\n                               query = t.NormalizedQuery\n                           }\n                           into grp\n                           select new\n                           {\n                               query = grp.Key.query,\n                               sum_duration_us = grp.Sum(t => t.SumDurationUs),\n                               avg_duration_us = grp.Average(t => t.AvgDurationUs),\n                               sum_cpu_us = grp.Sum(t => t.SumCpuUs),\n                               avg_cpu_us = grp.Average(t => t.AvgCpuUs),\n                               sum_reads = grp.Sum(t => t.SumReads),\n                               avg_reads = grp.Average(t => t.AvgReads),\n                               execution_count = grp.Sum(t => t.ExecutionCount)\n                           };\n\n            logger.Info(\"Baseline evaluation completed\");\n            logger.Info(\"Entering benchmark evaluation\");\n\n            var benchmark = from t in baseline where false select new { t.query, t.sum_duration_us, t.avg_duration_us, t.sum_cpu_us, t.avg_cpu_us, t.sum_reads, t.avg_reads, t.execution_count };\n\n            if (_benchmarkWorkloadAnalysis != null)\n            {\n                benchmark = from t in _benchmarkWorkloadAnalysis.Points\n                            where ApplicationList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.ApplicationName)\n                                && HostList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.HostName)\n                                && DatabaseList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.DatabaseName)\n                                && LoginList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.LoginName)\n                                && (!zoomIsSet || t.OffsetMinutes >= xstart)\n                                && (!zoomIsSet || t.OffsetMinutes <= xend)\n                            group t by new\n                            {\n                                query = t.NormalizedQuery\n                            }\n                            into grp\n                            select new\n                            {\n                                query = grp.Key.query,\n                                sum_duration_us = grp.Sum(t => t.SumDurationUs),\n                                avg_duration_us = grp.Average(t => t.AvgDurationUs),\n                                sum_cpu_us = grp.Sum(t => t.SumCpuUs),\n                                avg_cpu_us = grp.Average(t => t.AvgCpuUs),\n                                sum_reads = grp.Sum(t => t.SumReads),\n                                avg_reads = grp.Average(t => t.AvgReads),\n                                execution_count = grp.Sum(t => t.ExecutionCount)\n                            };\n            }\n\n            logger.Info(\"Benchmark evaluation completed\");\n            logger.Info(\"Merging sets\");\n\n            var leftOuterJoin =\n                from b in baseline\n                join k in benchmark\n                    on b.query.Hash equals k.query.Hash\n                    into joinedData\n                from j in joinedData.DefaultIfEmpty()\n                select new QueryResult\n                {\n                    query_hash = b.query.Hash,\n                    query_text = b.query.ExampleText,\n                    query_normalized = b.query.NormalizedText,\n                    sum_duration_us = b.sum_duration_us,\n                    avg_duration_us = b.avg_duration_us,\n                    sum_cpu_us = b.sum_cpu_us,\n                    avg_cpu_us = b.avg_cpu_us,\n                    sum_reads = b.sum_reads,\n                    avg_reads = b.avg_reads,\n                    execution_count = b.execution_count,\n                    sum_duration_us2 = j == null ? 0 : j.sum_duration_us,\n                    diff_sum_duration_us = j == null ? 0 : j.sum_duration_us - b.sum_duration_us,\n                    avg_duration_us2 = j == null ? 0 : j.avg_duration_us,\n                    diff_avg_duration_us = j == null ? 0 : j.avg_duration_us - b.avg_duration_us,\n                    sum_cpu_us2 = j == null ? 0 : j.sum_cpu_us,\n                    diff_sum_cpu_us = j == null ? 0 : j.sum_cpu_us - b.sum_cpu_us,\n                    avg_cpu_us2 = j == null ? 0 : j.avg_cpu_us,\n                    diff_avg_cpu_us = j == null ? 0 : j.avg_cpu_us - b.avg_cpu_us,\n                    sum_reads2 = j == null ? 0 : j.sum_reads,\n                    diff_sum_reads = j == null ? 0 : j.sum_reads - b.sum_reads,\n                    avg_reads2 = j == null ? 0 : j.avg_reads,\n                    diff_avg_reads = j == null ? 0 : j.avg_reads - b.avg_reads,\n                    execution_count2 = j == null ? 0 : j.execution_count,\n                    diff_execution_count = j == null ? 0 : j.execution_count - b.execution_count,\n                    querydetails = new QueryDetails(b.query, _baselineWorkloadAnalysis, _benchmarkWorkloadAnalysis),\n                    document = new ICSharpCode.AvalonEdit.Document.TextDocument() { Text = b.query.ExampleText }\n                };\n\n            var rightOuterJoin =\n                from b in benchmark\n                join k in baseline\n                    on b.query.Hash equals k.query.Hash\n                    into joinedData\n                from j in joinedData.DefaultIfEmpty()\n                select new QueryResult\n                {\n                    query_hash = b.query.Hash,\n                    query_text = b.query.ExampleText,\n                    query_normalized = b.query.NormalizedText,\n                    sum_duration_us = b.sum_duration_us,\n                    avg_duration_us = b.avg_duration_us,\n                    sum_cpu_us = b.sum_cpu_us,\n                    avg_cpu_us = b.avg_cpu_us,\n                    sum_reads = b.sum_reads,\n                    avg_reads = b.avg_reads,\n                    execution_count = b.execution_count,\n                    sum_duration_us2 = j == null ? 0 : j.sum_duration_us,\n                    diff_sum_duration_us = j == null ? 0 : j.sum_duration_us - b.sum_duration_us,\n                    avg_duration_us2 = j == null ? 0 : j.avg_duration_us,\n                    diff_avg_duration_us = j == null ? 0 : j.avg_duration_us - b.avg_duration_us,\n                    sum_cpu_us2 = j == null ? 0 : j.sum_cpu_us,\n                    diff_sum_cpu_us = j == null ? 0 : j.sum_cpu_us - b.sum_cpu_us,\n                    avg_cpu_us2 = j == null ? 0 : j.avg_cpu_us,\n                    diff_avg_cpu_us = j == null ? 0 : j.avg_cpu_us - b.avg_cpu_us,\n                    sum_reads2 = j == null ? 0 : j.sum_reads,\n                    diff_sum_reads = j == null ? 0 : j.sum_reads - b.sum_reads,\n                    avg_reads2 = j == null ? 0 : j.avg_reads,\n                    diff_avg_reads = j == null ? 0 : j.avg_reads - b.avg_reads,\n                    execution_count2 = j == null ? 0 : j.execution_count,\n                    diff_execution_count = j == null ? 0 : j.execution_count - b.execution_count,\n                    querydetails = new QueryDetails(b.query, _baselineWorkloadAnalysis, _benchmarkWorkloadAnalysis),\n                    document = new ICSharpCode.AvalonEdit.Document.TextDocument() { Text = b.query.ExampleText }\n                };\n\n            var merged = leftOuterJoin\n                .Union(rightOuterJoin, new QueryResultEqualityComparer())\n                .ToList();\n\n            Queries = merged;\n\n            logger.Info(\"Sets merged\");\n\n            RaisePropertyChanged(\"Queries\");\n            RaisePropertyChanged(\"CompareModeVisibility\");\n            RaisePropertyChanged(\"CompareMode\");\n\n            var sortCol = CompareMode ? \"diff_sum_duration_us\" : \"sum_duration_us\";\n            var msg = new SortColMessage(sortCol, System.ComponentModel.ListSortDirection.Descending);\n            Messenger.Default.Send<SortColMessage>(msg);\n        }\n\n\n        private bool ParseOptions()\n        {\n            _options = ((WorkloadViewer.App)App.Current).Options;\n\n            if(_options.ConfigurationFile != null)\n            {\n                // TODO: read configuration from file\n            }\n            else\n            {\n                if(_options.BaselineServer == null || _options.BaselineDatabase == null)\n                {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        private void KeyDown(KeyEventArgs e)\n        {\n            if(e.Key == Key.F5)\n            {\n                // TODO: refreshing should keep zoom and filters\n                InitializeAll();\n            }\n            if (e.Key == Key.F8)\n            {\n                ShowConnectionInfoDialog();\n            }\n        }\n\n\n        private void RefreshAllCharts()\n        {\n            RaisePropertyChanged(\"CpuPlotModel\");\n            RaisePropertyChanged(\"DurationPlotModel\");\n            RaisePropertyChanged(\"BatchesPlotModel\");\n        }\n\n\n        private void InitializeCharts()\n        {\n            var useDateAxis = _options.BenchmarkSchema == null;\n            var baseOffset = useDateAxis ? DateTimeAxis.ToDouble(_baselineWorkloadAnalysis.StartDate) : 0;\n\n            CpuPlotModel = InitializePlotModel(useDateAxis);\n            CpuPlotModel.Axes[1].Title = \"Cpu (us)\";\n            CpuPlotModel.Title = \"Cpu\";\n            CpuPlotModel.Series.Add(LoadCpuSeries(_baselineWorkloadAnalysis, OxyColor.Parse(\"#01B8AA\"),baseOffset));\n            if(_options.BenchmarkSchema != null)\n            {\n                CpuPlotModel.Series.Add(LoadCpuSeries(_benchmarkWorkloadAnalysis, OxyColor.Parse(\"#000000\"), baseOffset));\n            }\n            CpuPlotModel.PlotAreaBorderThickness = new OxyThickness(1,0,0,1);\n            PlotModels[0] = CpuPlotModel;\n\n\n            DurationPlotModel = InitializePlotModel(useDateAxis);\n            DurationPlotModel.Axes[1].Title = \"Duration (us)\";\n            DurationPlotModel.Title = \"Duration\";\n            DurationPlotModel.Series.Add(LoadDurationSeries(_baselineWorkloadAnalysis, OxyColor.Parse(\"#01B8AA\"), baseOffset));\n            if (_options.BenchmarkSchema != null)\n            {\n                DurationPlotModel.Series.Add(LoadDurationSeries(_benchmarkWorkloadAnalysis, OxyColor.Parse(\"#000000\"), baseOffset));\n            }\n            DurationPlotModel.PlotAreaBorderThickness = new OxyThickness(1, 0, 0, 1);\n            PlotModels[1] = DurationPlotModel;\n\n            BatchesPlotModel = InitializePlotModel(useDateAxis);\n            BatchesPlotModel.Axes[1].Title = \"Batches/second\";\n            BatchesPlotModel.Title = \"Batches/second\";\n            BatchesPlotModel.Series.Add(LoadBatchesSeries(_baselineWorkloadAnalysis, OxyColor.Parse(\"#01B8AA\"), baseOffset));\n            if (_options.BenchmarkSchema != null)\n            {\n                BatchesPlotModel.Series.Add(LoadBatchesSeries(_benchmarkWorkloadAnalysis, OxyColor.Parse(\"#000000\"), baseOffset));\n            }\n            BatchesPlotModel.PlotAreaBorderThickness = new OxyThickness(1, 0, 0, 1);\n            PlotModels[2] = BatchesPlotModel;\n        }\n\n\n        private PlotModel InitializePlotModel(bool dateXAxis)\n        {\n            var plotModel = new PlotModel();\n            plotModel.LegendOrientation = LegendOrientation.Horizontal;\n            plotModel.LegendPlacement = LegendPlacement.Inside;\n            plotModel.LegendPosition = LegendPosition.TopLeft;\n            plotModel.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);\n\n            if (!dateXAxis)\n            {\n                var offsetAxis = new LinearAxis()\n                {\n                    MajorGridlineStyle = LineStyle.Dot,\n                    MinorGridlineStyle = LineStyle.None,\n                    Position = AxisPosition.Bottom,\n                    Title = \"Offset minutes\",\n                    AbsoluteMinimum = 0,\n                    MinorTickSize = 0\n                };\n                plotModel.Axes.Add(offsetAxis);\n            }\n            else\n            {\n                LinearAxis offsetAxis = new DateTimeAxis()\n                {\n                    MajorGridlineStyle = LineStyle.Dot,\n                    MinorGridlineStyle = LineStyle.None,\n                    Position = AxisPosition.Bottom,\n                    Title = \"Time\",\n                    StringFormat = \"HH:mm\",\n                    MinorIntervalType = DateTimeIntervalType.Minutes,\n                    IntervalType = DateTimeIntervalType.Minutes,\n                };\n                plotModel.Axes.Add(offsetAxis);\n            }\n            var valueAxis1 = new LinearAxis() {\n                MajorGridlineStyle = LineStyle.Dot,\n                MinorGridlineStyle = LineStyle.None,\n                Position = AxisPosition.Left,\n                StringFormat = \"N0\",\n                IsZoomEnabled = false,\n                IsPanEnabled = false,\n                AbsoluteMinimum = 0,\n                MaximumPadding = 0.2,\n                MinorTickSize = 0\n            };\n            plotModel.Axes.Add(valueAxis1);\n\n            plotModel.PlotMargins = new OxyThickness(70, 0, 0, 30);\n            plotModel.Series.Clear();\n\n            foreach (var ax in plotModel.Axes)\n            {\n                ax.AxisChanged += (sender, e) => SynchronizeCharts(plotModel, sender, e);\n            }\n\n            return plotModel;\n        }\n\n        private void SynchronizeCharts(PlotModel plotModel, object sender, AxisChangedEventArgs e)\n        {\n            if (DateTime.Now.Subtract(_lastAxisAdjust).TotalMilliseconds < 100)\n            {\n                return;\n            }\n            _lastAxisAdjust = DateTime.Now;\n\n            try\n            {\n\n                var xstart = plotModel.DefaultXAxis.ActualMinimum;\n                var xend = plotModel.DefaultXAxis.ActualMaximum;\n\n                if (xstart < 0)\n                {\n                    xstart = 0;\n                }\n\n                foreach (var pm in PlotModels)\n                {\n                    // set x zoom only for the charts not being zoomed\n                    if (pm.Title != plotModel.Title)\n                    {\n                        pm.DefaultXAxis.Zoom(xstart, xend);\n                    }\n                    pm.InvalidatePlot(true);\n                }\n\n                InitializeQueries();\n\n            }\n            finally\n            {\n                _lastAxisAdjust = DateTime.Now;\n            }\n        }\n\n        private Series LoadCpuSeries(WorkloadAnalysis analysis, OxyColor color, double baseOffset)\n        {\n            if (analysis == null)\n            {\n                return null;\n            }\n\n            var cpuSeries = new LineSeries()\n            {\n                StrokeThickness = 2,\n                MarkerSize = 3,\n                MarkerStroke = OxyColor.Parse(\"#FF0000\"), //Red\n                MarkerType = MarkerType.None,\n                CanTrackerInterpolatePoints = false,\n                Title = analysis.Name,\n                Color = color,\n                Smooth = false\n            };\n            if(baseOffset == 0)\n            {\n                cpuSeries.TrackerFormatString = \"Offset: {2:0}\\n{0}: {4:0}\";\n            }\n\n            var Table = from t in analysis.Points\n                        where ApplicationList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.ApplicationName)\n                            && HostList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.HostName)\n                            && DatabaseList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.DatabaseName)\n                            && LoginList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.LoginName)\n                        group t by new\n                        {\n                            offset = t.OffsetMinutes\n                        }\n                        into grp\n                        orderby grp.Key.offset\n                        select new\n                        {\n                            offset_minutes = grp.Key.offset,\n                            cpu = grp.Sum(t => t.SumCpuUs)\n                        };\n\n            \n\n            foreach (var p in Table)\n            {\n                double xValue = 0;\n                if (baseOffset > 0)\n                {\n                    xValue = DateTimeAxis.ToDouble(DateTimeAxis.ToDateTime(baseOffset).AddMinutes(p.offset_minutes));\n                }\n                else\n                {\n                    xValue = p.offset_minutes;\n                }\n                cpuSeries.Points.Add(new DataPoint(xValue , p.cpu));\n            }\n\n            return cpuSeries;\n        }\n\n        private Series LoadDurationSeries(WorkloadAnalysis analysis, OxyColor color, double baseOffset)\n        {\n            if (analysis == null)\n            {\n                return null;\n            }\n\n            var durationSeries = new LineSeries()\n            {\n                StrokeThickness = 2,\n                MarkerSize = 3,\n                MarkerStroke = OxyColor.Parse(\"#FF0000\"), //Red\n                MarkerType = MarkerType.None,\n                CanTrackerInterpolatePoints = false,\n                Title = analysis.Name,\n                Color = color, \n                Smooth = false\n            };\n            if (baseOffset == 0)\n            {\n                durationSeries.TrackerFormatString = \"Offset: {2:0}\\n{0}: {4:0}\";\n            }\n\n            var Table = from t in analysis.Points\n                        where ApplicationList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.ApplicationName)\n                           && HostList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.HostName)\n                           && DatabaseList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.DatabaseName)\n                           && LoginList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.LoginName)\n                        group t by new\n                        {\n                            offset = t.OffsetMinutes\n                        }\n                        into grp\n                        orderby grp.Key.offset\n                        select new\n                        {\n                            offset_minutes = grp.Key.offset,\n                            duration = grp.Sum(t => t.SumDurationUs)\n                        };\n\n            foreach (var p in Table)\n            {\n                double xValue = 0;\n                if (baseOffset > 0)\n                {\n                    xValue = DateTimeAxis.ToDouble(DateTimeAxis.ToDateTime(baseOffset).AddMinutes(p.offset_minutes));\n                }\n                else\n                {\n                    xValue = p.offset_minutes;\n                }\n                durationSeries.Points.Add(new DataPoint(xValue, p.duration));\n            }\n\n            return durationSeries;\n        }\n\n\n        private Series LoadBatchesSeries(WorkloadAnalysis analysis, OxyColor color, double baseOffset)\n        {\n            if (analysis == null)\n            {\n                return null;\n            }\n\n            var batchesSeries = new LineSeries()\n            {\n                StrokeThickness = 2,\n                MarkerSize = 3,\n                MarkerStroke = OxyColor.Parse(\"#FF0000\"), //Red\n                MarkerType = MarkerType.None,\n                CanTrackerInterpolatePoints = false,\n                Title = analysis.Name,\n                Color = color,\n                Smooth = false\n            };\n            if (baseOffset == 0)\n            {\n                batchesSeries.TrackerFormatString = \"Offset: {2:0}\\n{0}: {4:0}\";\n            }\n\n            var Table = from t in analysis.Points\n                        where ApplicationList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.ApplicationName)\n                           && HostList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.HostName)\n                           && DatabaseList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.DatabaseName)\n                           && LoginList.Where(f => f.IsChecked).Select(f => f.Name).Contains(t.LoginName)\n                        group t by new\n                        {\n                            offset = t.OffsetMinutes\n                        }\n                        into grp\n                        orderby grp.Key.offset\n                        select new\n                        {\n                            offset_minutes = grp.Key.offset,\n                            execution_count = grp.Sum(t => t.ExecutionCount / ((t.DurationMinutes == 0 ? 1 : t.DurationMinutes) * 60))\n                        };\n\n            foreach (var p in Table)\n            {\n                double xValue = 0;\n                if (baseOffset > 0)\n                {\n                    xValue = DateTimeAxis.ToDouble(DateTimeAxis.ToDateTime(baseOffset).AddMinutes(p.offset_minutes));\n                }\n                else\n                {\n                    xValue = p.offset_minutes;\n                }\n                batchesSeries.Points.Add(new DataPoint(xValue, p.execution_count));\n            }\n\n            return batchesSeries;\n        }\n\n\n        private void InitializeFilters()\n        {\n            var baseApplications = \n                from t in _baselineWorkloadAnalysis.Points\n                group t by new { application = t.ApplicationName }\n                into grp\n                select grp.Key.application\n            ;\n            if(_benchmarkWorkloadAnalysis != null)\n            {\n                baseApplications = baseApplications.Union(\n                        from t in _benchmarkWorkloadAnalysis.Points\n                        group t by new { application = t.ApplicationName }\n                        into grp\n                        select grp.Key.application\n                    ).Distinct();\n            }\n            ApplicationList = new List<FilterDefinition>(\n                    from name in baseApplications\n                    orderby name\n                    select new FilterDefinition() { Name = name, IsChecked = true }\n                );\n\n            var baseHosts = \n                from t in _baselineWorkloadAnalysis.Points\n                group t by new { host = t.HostName }\n                into grp\n                select grp.Key.host\n            ;\n            if (_benchmarkWorkloadAnalysis != null)\n            {\n                baseHosts = baseHosts.Union(\n                        from t in _benchmarkWorkloadAnalysis.Points\n                        group t by new { host = t.HostName }\n                        into grp\n                        select grp.Key.host\n                    ).Distinct();\n            }\n            HostList = new List<FilterDefinition>(\n                    from name in baseHosts\n                    orderby name\n                    select new FilterDefinition() { Name = name, IsChecked = true }\n                );\n\n            var baseDatabases = \n                from t in _baselineWorkloadAnalysis.Points\n                group t by new { database = t.DatabaseName }\n                into grp\n                select grp.Key.database\n            ;\n            if (_benchmarkWorkloadAnalysis != null)\n            {\n                baseDatabases = baseDatabases.Union(\n                        from t in _benchmarkWorkloadAnalysis.Points\n                        group t by new { database = t.DatabaseName }\n                        into grp\n                        select grp.Key.database\n                    ).Distinct();\n            }\n            DatabaseList = new List<FilterDefinition>(\n                    from name in baseDatabases\n                    orderby name\n                    select new FilterDefinition() { Name = name, IsChecked = true }\n                );\n\n            var baseLogins = \n                from t in _baselineWorkloadAnalysis.Points\n                group t by new { login = t.LoginName }\n                into grp\n                select grp.Key.login\n            ;\n            if (_benchmarkWorkloadAnalysis != null)\n            {\n                baseLogins = baseLogins.Union(\n                        from t in _benchmarkWorkloadAnalysis.Points\n                        group t by new { login = t.LoginName }\n                        into grp\n                        select grp.Key.login\n                    ).Distinct();\n            }\n            LoginList = new List<FilterDefinition>(\n                    from name in baseLogins\n                    orderby name\n                    select new FilterDefinition() { Name = name, IsChecked = true }\n                );\n\n            RaisePropertyChanged(\"ApplicationList\");\n            RaisePropertyChanged(\"HostList\");\n            RaisePropertyChanged(\"DatabaseList\");\n            RaisePropertyChanged(\"LoginList\");\n        }\n\n\n        private void ShowStatusMessage(string message)\n        {\n            StatusMessage = message;\n            RaisePropertyChanged(\"StatusMessage\");\n        }\n            \n\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/Message.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public class Message\n    {\n        public string Text { get; set; }\n\n        public Message(string name)\n        {\n            Text = name;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/QueryResult.cs",
    "content": "﻿using WorkloadViewer.Model;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public class QueryResult\n    {\n        public long query_hash { get; set; }\n        \n        public string query_text { get; set; }\n        \n        public string query_normalized { get; set; }\n        \n        public long sum_duration_us { get; set; }\n        \n        public double avg_duration_us { get; set; }\n        \n        public long sum_cpu_us { get; set; }\n        \n        public double avg_cpu_us { get; set; }\n        \n        public long sum_reads { get; set; }\n        \n        public double avg_reads { get; set; }\n        \n        public long execution_count { get; set; }\n        \n        public long sum_duration_us2 { get; set; }\n        \n        public long diff_sum_duration_us { get; set; }\n        \n        public double avg_duration_us2 { get; set; }\n        \n        public double diff_avg_duration_us { get; set; }\n        \n        public long sum_cpu_us2 { get; set; }\n        \n        public long diff_sum_cpu_us { get; set; }\n        \n        public double avg_cpu_us2 { get; set; }\n        \n        public double diff_avg_cpu_us { get; set; }\n        \n        public long sum_reads2 { get; set; }\n        \n        public long diff_sum_reads { get; set; }\n        \n        public double avg_reads2 { get; set; }\n        \n        public double diff_avg_reads { get; set; }\n        \n        public long execution_count2 { get; set; }\n        \n        public long diff_execution_count { get; set; }\n        \n        public QueryDetails querydetails { get; set; }\n        \n        public ICSharpCode.AvalonEdit.Document.TextDocument document { get; set; }\n    }\n}"
  },
  {
    "path": "WorkloadViewer/ViewModel/SortColMessage.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace WorkloadViewer.ViewModel\n{\n    public class SortColMessage\n    {\n        public string ColumnName { get; set; }\n        public ListSortDirection Direction { get; set;  }\n\n        public SortColMessage(string columnName, ListSortDirection direction)\n        {\n            ColumnName = columnName;\n            Direction = direction;\n        }\n    }\n}\n"
  },
  {
    "path": "WorkloadViewer/ViewModel/ViewModelLocator.cs",
    "content": "/*\n  In App.xaml:\n  <Application.Resources>\n      <vm:ViewModelLocator xmlns:vm=\"clr-namespace:WorkloadViewer\"\n                           x:Key=\"Locator\" />\n  </Application.Resources>\n  \n  In the View:\n  DataContext=\"{Binding Source={StaticResource Locator}, Path=ViewModelName}\"\n\n  You can also use Blend to do all this with the tool's support.\n  See http://www.galasoft.ch/mvvm\n*/\n\nusing CommonServiceLocator;\nusing GalaSoft.MvvmLight;\nusing GalaSoft.MvvmLight.Ioc;\n\n\nnamespace WorkloadViewer.ViewModel\n{\n    /// <summary>\n    /// This class contains static references to all the view models in the\n    /// application and provides an entry point for the bindings.\n    /// </summary>\n    public class ViewModelLocator\n    {\n        /// <summary>\n        /// Initializes a new instance of the ViewModelLocator class.\n        /// </summary>\n        public ViewModelLocator()\n        {\n            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);\n\n            ////if (ViewModelBase.IsInDesignModeStatic)\n            ////{\n            ////    // Create design time view services and models\n            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();\n            ////}\n            ////else\n            ////{\n            ////    // Create run time view services and models\n            ////    SimpleIoc.Default.Register<IDataService, DataService>();\n            ////}\n\n            SimpleIoc.Default.Register<MainViewModel>();\n        }\n\n        public MainViewModel Main\n        {\n            get\n            {\n                return ServiceLocator.Current.GetInstance<MainViewModel>();\n            }\n        }\n        \n        public static void Cleanup()\n        {\n            // TODO Clear the ViewModels\n        }\n    }\n}"
  },
  {
    "path": "WorkloadViewer/WorkloadViewer.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{6E10E31F-D04D-4CB7-8BB9-71ABD4B6B973}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>WorkloadViewer</RootNamespace>\n    <AssemblyName>WorkloadViewer</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\x64\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\x64\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>Icon.ico</ApplicationIcon>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject />\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\CommandLineParser.1.9.71\\lib\\net45\\CommandLine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"CommonServiceLocator, Version=2.0.2.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\CommonServiceLocator.2.0.2\\lib\\net45\\CommonServiceLocator.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ControlzEx, Version=3.0.2.4, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\ControlzEx.3.0.2.4\\lib\\net45\\ControlzEx.dll</HintPath>\n    </Reference>\n    <Reference Include=\"GalaSoft.MvvmLight, Version=5.4.1.0, Culture=neutral, PublicKeyToken=e7570ab207bcb616, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MvvmLightLibs.5.4.1.1\\lib\\net45\\GalaSoft.MvvmLight.dll</HintPath>\n    </Reference>\n    <Reference Include=\"GalaSoft.MvvmLight.Extras, Version=5.4.1.0, Culture=neutral, PublicKeyToken=669f0b5e8f868abf, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MvvmLightLibs.5.4.1.1\\lib\\net45\\GalaSoft.MvvmLight.Extras.dll</HintPath>\n    </Reference>\n    <Reference Include=\"GalaSoft.MvvmLight.Platform, Version=5.4.1.0, Culture=neutral, PublicKeyToken=5f873c45e98af8a1, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MvvmLightLibs.5.4.1.1\\lib\\net45\\GalaSoft.MvvmLight.Platform.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ICSharpCode.AvalonEdit, Version=5.0.3.0, Culture=neutral, PublicKeyToken=9cc39be672370310, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\AvalonEdit.5.0.4\\lib\\Net40\\ICSharpCode.AvalonEdit.dll</HintPath>\n    </Reference>\n    <Reference Include=\"MahApps.Metro, Version=1.6.5.1, Culture=neutral, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MahApps.Metro.1.6.5\\lib\\net46\\MahApps.Metro.dll</HintPath>\n    </Reference>\n    <Reference Include=\"NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\NLog.4.4.12\\lib\\net45\\NLog.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OxyPlot, Version=1.0.0.0, Culture=neutral, PublicKeyToken=638079a8f0bd61e9, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OxyPlot.Core.1.0.0\\lib\\net45\\OxyPlot.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OxyPlot.Wpf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=75e952ba404cdbb0, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OxyPlot.Wpf.1.0.0\\lib\\net45\\OxyPlot.Wpf.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.ComponentModel.Composition\" />\n    <Reference Include=\"System.Console, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Console.4.0.0\\lib\\net46\\System.Console.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Reflection.TypeExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\System.Reflection.TypeExtensions.4.1.0\\lib\\net46\\System.Reflection.TypeExtensions.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\MvvmLightLibs.5.4.1.1\\lib\\net45\\System.Windows.Interactivity.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xaml\">\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\n    </Reference>\n    <Reference Include=\"UIAutomationProvider\" />\n    <Reference Include=\"WindowsBase\" />\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ApplicationDefinition Include=\"App.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </ApplicationDefinition>\n    <Compile Include=\"..\\SharedAssemblyInfo.cs\">\n      <Link>Properties\\SharedAssemblyInfo.cs</Link>\n    </Compile>\n    <Compile Include=\"Comparer\\QueryResultEqualityComparer.cs\" />\n    <Compile Include=\"Model\\NormalizedQuery.cs\" />\n    <Compile Include=\"Model\\SqlConnectionInfo.cs\" />\n    <Compile Include=\"Model\\WorkloadAnalysis.cs\" />\n    <Compile Include=\"Model\\WorkloadAnalysisPoint.cs\" />\n    <Compile Include=\"ViewModel\\ConnectionInfoEditorViewModel.cs\" />\n    <Compile Include=\"ViewModel\\DictionaryExtensions.cs\" />\n    <Compile Include=\"ViewModel\\FilterDefinition.cs\" />\n    <Compile Include=\"ViewModel\\LinqExtensions.cs\" />\n    <Compile Include=\"ViewModel\\MainViewModel.cs\" />\n    <Compile Include=\"Model\\QueryDetails.cs\" />\n    <Compile Include=\"ViewModel\\Message.cs\" />\n    <Compile Include=\"ViewModel\\QueryResult.cs\" />\n    <Compile Include=\"ViewModel\\SortColMessage.cs\" />\n    <Compile Include=\"ViewModel\\ViewModelLocator.cs\" />\n    <Page Include=\"View\\ConnectionInfoDialog.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"View\\ConnectionInfoDialogStyle.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"View\\ConnectionInfoEditor.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"View\\MainWindow.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"View\\ConnectionInfoDialog.xaml.cs\">\n      <DependentUpon>ConnectionInfoDialog.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"View\\ConnectionInfoEditor.xaml.cs\">\n      <DependentUpon>ConnectionInfoEditor.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"View\\MainWindow.xaml.cs\">\n      <DependentUpon>MainWindow.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>PublicResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <None Include=\"NLog.config\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n    <Content Include=\"Resources\\TSQL.xshd\">\n      <SubType>Designer</SubType>\n    </Content>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Resources\\WorkloadAnalysis.sql\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"Icon.ico\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "WorkloadViewer/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"AvalonEdit\" version=\"5.0.4\" targetFramework=\"net461\" />\n  <package id=\"CommandLineParser\" version=\"1.9.71\" targetFramework=\"net461\" />\n  <package id=\"CommonServiceLocator\" version=\"2.0.2\" targetFramework=\"net461\" />\n  <package id=\"ControlzEx\" version=\"3.0.2.4\" targetFramework=\"net461\" />\n  <package id=\"MahApps.Metro\" version=\"1.6.5\" targetFramework=\"net461\" />\n  <package id=\"MvvmLight\" version=\"5.4.1.1\" targetFramework=\"net461\" />\n  <package id=\"MvvmLightLibs\" version=\"5.4.1.1\" targetFramework=\"net461\" />\n  <package id=\"NLog\" version=\"4.4.12\" targetFramework=\"net461\" />\n  <package id=\"OxyPlot.Core\" version=\"1.0.0\" targetFramework=\"net461\" />\n  <package id=\"OxyPlot.Wpf\" version=\"1.0.0\" targetFramework=\"net461\" />\n  <package id=\"System.Collections\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.Console\" version=\"4.0.0\" targetFramework=\"net461\" />\n  <package id=\"System.Diagnostics.Debug\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.Globalization\" version=\"4.0.11\" targetFramework=\"net461\" />\n  <package id=\"System.IO\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Linq\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Linq.Expressions\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection.Extensions\" version=\"4.0.1\" targetFramework=\"net461\" />\n  <package id=\"System.Reflection.TypeExtensions\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Resources.ResourceManager\" version=\"4.0.1\" targetFramework=\"net461\" />\n  <package id=\"System.Runtime\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"System.Runtime.Extensions\" version=\"4.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "build.ps1",
    "content": "\n# ---------------------------------------------------------------------------\n# Locate MSBuild via vswhere (ships with Visual Studio 2017+)\n# ---------------------------------------------------------------------------\n$vswhere = \"${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe\"\nif (-not (Test-Path $vswhere)) {\n    throw \"vswhere.exe not found at '$vswhere'. Visual Studio 2017 or newer is required.\"\n}\n\n$msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild `\n    -find MSBuild\\**\\Bin\\MSBuild.exe | Select-Object -First 1\nif (-not $msbuild) {\n    throw \"MSBuild.exe not found. Please install Visual Studio with the MSBuild component.\"\n}\n\n# ---------------------------------------------------------------------------\n# Build the .NET projects (SqlWorkload, WorkloadViewer, ConvertWorkload, etc.)\n# The Setup and SetupBootstrapper WiX projects are excluded from the solution\n# build and are handled separately by buildexe.ps1 below.\n# ---------------------------------------------------------------------------\n& $msbuild \"$PSScriptRoot\\WorkloadTools.sln\" -t:Rebuild -p:Configuration=Release -p:Platform=x64\n. $PSScriptRoot\\SetupBootstrapper\\buildexe.ps1 -Platform x64\n\n& $msbuild \"$PSScriptRoot\\WorkloadTools.sln\" -t:Rebuild -p:Configuration=Release -p:Platform=x86\n. $PSScriptRoot\\SetupBootstrapper\\buildexe.ps1 -Platform x86"
  }
]