/// <summary>
        /// <paramref name="filename"/>
        /// </summary>
        /// <param name="filename">The filename with the callstack report</param>
        public static double CSReportToDWJson(string filename, string outfname)
        {
            if (!File.Exists(filename))
            {
                throw new ArgumentException($"Specified file {filename} does not exist!");
            }
            var samples = ParseFromFile(filename);

            var times      = samples.Select(x => x.TOSFrame.CPUTime);
            var total      = times.Sum();
            var fractional = times.Select(x => (x / total) * 100).ToList();

            LongInt duration = TraceUtils.ToNanoseconds(total);

            var modFunDictionary = samples.SelectMany(sm => sm.AllSamples())
                                   .Select(p => new { Module = p.Module, Function = p.Function })
                                   .GroupBy(t => t.Module)
                                   .Select(g => new { Module = g.Key, Functions = g.Select(gg => gg.Function).Distinct() })
            ;

            // Create a two-level dictionary module -> (function -> (base, size))
            var mfdd = modFunDictionary.Select(x => new {
                Module    = x.Module,
                Functions = x.Functions.Zip((new SequenceBaseSize()).Generate(), (f, b) => new { Function = f, BaseSize = b })
                            .ToDictionary(t => t.Function, t => t.BaseSize)
            })
                       .ToDictionary(od => od.Module, od => od.Functions);

            if (mfdd.Count <= 0)
            {
                throw new Exception("Couldn't build the module/function dictionary, can't figure out why");
            }

            var mods = mfdd.Zip(Enumerable.Range(1, int.MaxValue), (x, y) => new ModuleSpec()
            {
                name   = x.Key,
                id     = y,
                begin  = new LongInt(0, 0),     // should build these according to mfdd (i.e., argument x)
                end    = new LongInt(0, 10000), // not sure why 2500 is the smallest number than seems to work
                @base  = new LongInt(0, (y - 1) * 1000),
                size   = new LongInt(0, 300),
                ranges = x.Value.Select(xx => new FunctionSpec(xx.Key, xx.Value.Base, xx.Value.Size)).ToList()
            });
            var modBase = mods.ToDictionary(x => x.name, x => x.@base);

            AddressTranslator tr = new AddressTranslator(modBase, mfdd);

            int startime = 2500; // the base is important, as it's coordinated with the modules `end`
            int stepsize = 1;

            List <FrameInfo> chains = new List <FrameInfo>();
            int idx = 0;

            foreach (var s in samples)
            {
                foreach (var y in s.Stacks)
                {
                    var fi = new FrameInfo {
                        timestamp = new LongInt(0, startime + stepsize * idx),
                        frameIPs  = y.Select(z => tr.Translate(z.Module, z.Function)).ToList()
                    };
                    fi.frameIPs.Insert(0, tr.Translate(s.TOSFrame.Module, s.TOSFrame.Function));
                    chains.Add(fi);

                    idx++;
                }
            }

            ProcessSpec proc = new ProcessSpec {
                name  = "python36.dll",
                id    = 1234,
                begin = new LongInt(0, 1000),
                //end = new LongInt(0,8000),
                end       = duration,
                isTarget  = true,
                isUser    = true,
                moduleIDs = Enumerable.Range(1, int.MaxValue).Take(mods.Count()).ToList()
            };
            List <ProcessSpec> processes = new List <ProcessSpec>(); // TODO -- is there a literal for this?

            processes.Add(proc);

            ThreadSpec thread = new ThreadSpec()
            {
                id    = 1,
                begin = new LongInt(0, 2000),
                end   = new LongInt(0, 3000),
                //stacks = chains.ToList()
                stacks = chains
            };
            List <ThreadSpec> threads = new List <ThreadSpec>();

            threads.Add(thread);
            proc.threads = threads;

            var trace = new Trace {
                totalTimeRange = new TimeSpec {
                    begin = new LongInt(0, 0),
                    //duration = new LongInt(0, (int)(total * 1000))
                    //duration = new LongInt(0, 10000)
                    duration = duration
                },
                name      = "machine-name",
                processor = new ProcessorSpec {
                    logicalCount       = 4,
                    speedInMHz         = 2670,
                    pointerSizeInBytes = 4,
                    highestUserAddress = new LongInt(0, 2147418111)
                },
                processes = processes,
                modules   = mods.ToList()
            };

            string       json   = JsonConvert.SerializeObject(trace, Formatting.Indented);
            StreamWriter writer = File.CreateText(outfname);

            writer.WriteLine(json);
            writer.Close();

            return(total);
        }
        public static void CPUReportToDWJson(string filename, string outfname, double timeTotal = 0.0)
        {
            if (!File.Exists(filename))
            {
                throw new ArgumentException($"Cannot find specified CPU utilization report {filename}");
            }

            if (timeTotal <= 0)
            {
                throw new Exception("Invalid runtime specification in CPU utilization report");
            }

            LongInt durationli = TraceUtils.ToNanoseconds(timeTotal);

            var cpuRecords = VTuneStackParser.ReadFromFile(filename)
                             .Skip(2)
                             .ParseCPURecords();

            /*
             * CPUUtilRecord first = cpuRecords.First();
             * CPUUtilRecord last = cpuRecords.Last();
             *
             * CPUUtilTrace trace = new CPUUtilTrace();
             * trace.beginTime = new LongInt(0, (long)(first.Start));
             * trace.duration = new LongInt(0, (long)(last.End - first.Start));
             * trace.counters = new List<ValueTrace> { new ValueTrace(cpuRecords.Select(r => new CPUSample(new LongInt(0, (long)r.Start), (float)r.CPUUtil)).ToList()) };
             */

            int    steps     = cpuRecords.Count() - 1;
            double totalTime = timeTotal;
            double stepSize  = totalTime / steps;

            List <ValueTrace> vts = new List <ValueTrace>();

            vts.Add(new ValueTrace(Enumerable.Range(0, int.MaxValue).Take(steps).Zip(cpuRecords, (x, y) => new CPUSample(TraceUtils.ToNanoseconds(x * stepSize), (float)(y.CPUUtil)))));

            CPUUtilTrace trace = new CPUUtilTrace {
                beginTime = new LongInt(0, 0),
                //duration = new LongInt(0, totalTime),
                duration = durationli,
                counters = vts
            };

            string json = JsonConvert.SerializeObject(trace, Formatting.Indented);

            // var fs = new FileStream(@"C:\users\perf\Sample2.counters", FileMode.Create);
            var fs = new FileStream(outfname, FileMode.Create);

            using (StreamWriter writer = new StreamWriter(fs, Encoding.Unicode)) { // encoding in Unicode here is key
                writer.WriteLine(json);
            }
        }