private static string ExtractEventColor(TimelineBuildEntry timelineBuildEntry, TimelineEntry timelineEntry)
        {
            if (timelineBuildEntry != null)
            {
                BuildFinishedEvent endEvent = timelineBuildEntry.BuildEntry.EndEvent as BuildFinishedEvent;
                if (endEvent != null)
                {
                    return(endEvent.Succeeded ? s_BuildSucceededColor : s_BuildFailedColor);
                }
            }

            return(null);
        }
        private static void ExtractEventsIntoTrace(TimelineEntry timelineEntry, DateTime buildStartTimestamp, List <ChromeTracingEvent> events)
        {
            Debug.Assert(timelineEntry.ThreadAffinity.Calculated);

            TimelineBuildEntry timelineBuildEntry = timelineEntry as TimelineBuildEntry;

            // skip instantaneous entries
            if (timelineEntry.ElapsedTime == TimeSpan.Zero)
            {
                return;
            }

            Dictionary <string, string> args = new Dictionary <string, string>();

            // Guid
            args.Add("GUID", timelineEntry.GUID.ToString());
            if (timelineEntry.Parent != null)
            {
                args.Add("Parent GUID", timelineEntry.Parent.GUID.ToString());
            }

            // start
            if (timelineBuildEntry != null)
            {
                args.Add("Start event", timelineBuildEntry.BuildEntry.StartEvent.Message);
            }

            events.Add(new ChromeTracingEvent()
            {
                ph   = 'B',
                pid  = timelineEntry.NodeId,
                tid  = timelineEntry.ThreadAffinity.ThreadId,
                ts   = (timelineEntry.StartTimestamp - buildStartTimestamp).TotalMilliseconds * 1000.0d,
                name = timelineEntry.Name,
            });

            // child events
            foreach (TimelineEntry child in timelineEntry.ChildEntries)
            {
                ExtractEventsIntoTrace(child, buildStartTimestamp, events);
            }

            if (timelineBuildEntry != null)
            {
                // messages within this entry
                List <Event> messageEvents = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _.GetType() == typeof(MessageEvent)).ToList();
                for (int i = 0; i < messageEvents.Count(); ++i)
                {
                    double millisecondsSinceStart = (messageEvents[i].Timestamp - buildStartTimestamp).TotalMilliseconds;
                    args.Add($"Message #{i}", $"[{millisecondsSinceStart:0.###} ms] {messageEvents[i].Message}");
                }

                // warnings within this entry
                List <Event> warningEvents = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _.GetType() == typeof(WarningEvent)).ToList();
                for (int i = 0; i < warningEvents.Count(); ++i)
                {
                    double millisecondsSinceStart = (warningEvents[i].Timestamp - buildStartTimestamp).TotalMilliseconds;
                    args.Add($"Warning #{i}", $"[{millisecondsSinceStart:0.###} ms] {warningEvents[i].Message}");
                }

                // errors within this entry
                List <Event> errorEvents = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _.GetType() == typeof(ErrorEvent)).ToList();
                for (int i = 0; i < errorEvents.Count(); ++i)
                {
                    double millisecondsSinceStart = (errorEvents[i].Timestamp - buildStartTimestamp).TotalMilliseconds;
                    args.Add($"Error #{i}", $"[{millisecondsSinceStart:0.###} ms] {errorEvents[i].Message}");
                }
            }

            // end
            if (timelineBuildEntry != null)
            {
                args.Add("End event", timelineBuildEntry.BuildEntry.EndEvent.Message);
            }

            events.Add(new ChromeTracingEvent()
            {
                ph    = 'E',
                pid   = timelineEntry.NodeId,
                tid   = timelineEntry.ThreadAffinity.ThreadId,
                ts    = (timelineEntry.EndTimestamp - buildStartTimestamp).TotalMilliseconds * 1000.0d,
                name  = timelineEntry.Name,
                args  = args,
                cname = ExtractEventColor(timelineBuildEntry, timelineEntry)
            });
        }