/// <summary>
        /// there might be some hardware counter events not belonging to the benchmarked code (for example CLR or BenchmarkDotNet's Engine)
        /// to calculate the % per IP we need to know the total per benchmark, not per process
        /// </summary>
        private static Dictionary <HardwareCounter, (ulong withoutNoise, ulong total)> SumHardwareCountersStatsOfBenchmarkedCode(
            DisassemblyResult disassemblyResult, PmcStats pmcStats)
        {
            IEnumerable <ulong> Range(Asm asm)
            {
                // most probably asm.StartAddress would be enough, but I don't want to miss any edge case
                for (ulong instructionPointer = asm.StartAddress; instructionPointer < asm.EndAddress; instructionPointer++)
                {
                    yield return(instructionPointer);
                }
            }

            var instructionPointers = new HashSet <ulong>(
                disassemblyResult
                .Methods
                .SelectMany(method => method.Maps)
                .SelectMany(map => map.Instructions)
                .OfType <Asm>()
                .SelectMany(Range)
                .Distinct());

            return(pmcStats.Counters.ToDictionary(data => data.Key, data =>
            {
                ulong withoutNoise = 0, total = 0;

                foreach (var ipToCount in data.Value.PerInstructionPointer)
                {
                    total += ipToCount.Value;

                    if (instructionPointers.Contains(ipToCount.Key))
                    {
                        withoutNoise += ipToCount.Value;
                    }
                }

                return (withoutNoise, total);
            }));
        }
        private static string Export(Summary summary, BenchmarkCase benchmarkCase, DisassemblyResult disassemblyResult, PmcStats pmcStats)
        {
            string filePath = $"{Path.Combine(summary.ResultsDirectoryPath, benchmarkCase.Descriptor.WorkloadMethod.Name)}-{benchmarkCase.Job.Environment.Jit}-{benchmarkCase.Job.Environment.Platform}-instructionPointer.html";

            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            var totals    = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats);
            var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats);

            using (var stream = new StreamWriter(filePath, append: false))
            {
                using (var streamLogger = new StreamLogger(stream))
                {
                    Export(streamLogger, benchmarkCase, totals, perMethod, pmcStats.Counters.Keys.ToArray());
                }
            }

            return(filePath);
        }
        private string Export(Summary summary, Benchmark benchmark, DisassemblyResult disassemblyResult, PmcStats pmcStats)
        {
            var filePath = $"{Path.Combine(summary.ResultsDirectoryPath, benchmark.Target.Method.Name)}-{benchmark.Job.Env.Jit}-{benchmark.Job.Env.Platform}-instructionPointer.html";

            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            checked
            {
                var totals    = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats);
                var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats);

                using (var stream = Portability.StreamWriter.FromPath(filePath))
                {
                    Export(new StreamLogger(stream), benchmark, totals, perMethod, pmcStats.Counters.Keys.ToArray());
                }
            }

            return(filePath);
        }
        private static IReadOnlyList <MethodWithCounters> SumHardwareCountersPerMethod(DisassemblyResult disassemblyResult, PmcStats pmcStats)
        {
            var model = new List <MethodWithCounters>(disassemblyResult.Methods.Length);

            foreach (var method in disassemblyResult.Methods.Where(method => string.IsNullOrEmpty(method.Problem)))
            {
                var groups = new List <List <CodeWithCounters> >();

                foreach (var map in method.Maps)
                {
                    var codeWithCounters = new List <CodeWithCounters>(map.Instructions.Length);

                    foreach (var instruction in map.Instructions)
                    {
                        var totalsPerCounter = pmcStats.Counters.Keys.ToDictionary(key => key, _ => default(ulong));

                        if (instruction is Asm asm)
                        {
                            foreach (var hardwareCounter in pmcStats.Counters)
                            {
                                // most probably asm.StartAddress would be enough, but I don't want to miss any edge case
                                for (ulong instructionPointer = asm.StartAddress; instructionPointer < asm.EndAddress; instructionPointer++)
                                {
                                    if (hardwareCounter.Value.PerInstructionPointer.TryGetValue(instructionPointer, out ulong value))
                                    {
                                        totalsPerCounter[hardwareCounter.Key] = totalsPerCounter[hardwareCounter.Key] + value;
                                    }
                                }
                            }
                        }

                        codeWithCounters.Add(new CodeWithCounters
                        {
                            Code          = instruction,
                            SumPerCounter = totalsPerCounter
                        });
                    }

                    groups.Add(codeWithCounters);
                }

                model.Add(new MethodWithCounters
                {
                    Method        = method,
                    Instructions  = groups,
                    SumPerCounter = pmcStats.Counters.Keys.ToDictionary(
                        hardwareCounter => hardwareCounter,
                        hardwareCounter =>
                    {
                        ulong sum = 0;

                        foreach (var group in groups)
                        {
                            foreach (var codeWithCounter in group)
                            {
                                sum += codeWithCounter.SumPerCounter[hardwareCounter];
                            }
                        }

                        return(sum);
                    }
                        )
                });
            }

            return(model);
        }
        private string Export(Summary summary, BenchmarkCase benchmarkCase, DisassemblyResult disassemblyResult, PmcStats pmcStats)
        {
            string filePath = Path.Combine(summary.ResultsDirectoryPath,
                                           $"{FolderNameHelper.ToFolderName(benchmarkCase.Descriptor.Type)}." +
                                           $"{benchmarkCase.Descriptor.WorkloadMethod.Name}." +
                                           $"{GetShortRuntimeInfo(summary[benchmarkCase].GetRuntimeInfo())}.counters.html");

            filePath.DeleteFileIfExists();

            var totals    = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats);
            var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats);

            using (var stream = new StreamWriter(filePath, append: false))
            {
                using (var streamLogger = new StreamLogger(stream))
                {
                    Export(streamLogger, benchmarkCase, totals, perMethod, pmcStats.Counters.Keys.ToArray());
                }
            }

            return(filePath);
        }