private static bool IsTaskCL(TimelineEntry entry) { // only take TimelineBuildEntry into account TimelineBuildEntry timelineBuildEntry = entry as TimelineBuildEntry; if (timelineBuildEntry == null) { return(false); } // we're looking for Task ones TaskStartedEvent taskStartedEvent = timelineBuildEntry.BuildEntry.StartEvent as TaskStartedEvent; if (taskStartedEvent == null) { return(false); } // and only the ones called "CL" if (taskStartedEvent.TaskName != "CL") { return(false); } return(true); }
private void EnsureNoEntryOverflowsParent(TimelineEntry entry) { entry.FitChildEntries(); /* * if (entry.ChildEntries.Count > 0) * { * DateTime childrenFirstStartTimestamp = entry.ChildEntries.First().StartTimestamp; * DateTime childrenLastEndTimestamp = entry.ChildEntries.Last().EndTimestamp; * * // no child should start before its parent! overflow occurs on end timestamps * Debug.Assert(childrenFirstStartTimestamp >= entry.StartTimestamp); * * if(childrenLastEndTimestamp > entry.EndTimestamp) * { * DateTime minDate = entry.StartTimestamp; * DateTime maxDate = childrenLastEndTimestamp; * TimeSpan elapsedWithOverflow = maxDate - minDate; * TimeSpan elapsedParent = entry.ElapsedTime; * * double ratio = (double) elapsedParent.Ticks / elapsedWithOverflow.Ticks; * entry.ScaleChildrenTimestamps(ratio); * } * else * { * foreach(TimelineEntry child in entry.ChildEntries) * { * EnsureNoEntryOverflowsParent(child); * } * } * } */ }
private void PostProcess(TimelineEntry entry, TimelineEntryPostProcessor.Processor perEntryPostProcessors) { perEntryPostProcessors(entry); foreach (TimelineEntry child in entry.ChildEntries) { PostProcess(child, perEntryPostProcessors); } }
public bool IsAncestorOf(TimelineEntry other) { if (other == null) { return(false); } if (this == other.Parent) { return(true); } return(IsAncestorOf(other.Parent)); }
public static void TaskCLSingleThread(TimelineEntry entry) { // only CL tasks if (!IsTaskCL(entry)) { return; } TimelineBuildEntry timelineBuildEntry = entry as TimelineBuildEntry; // single threaded compilations contain a message that multithreaded ones don't if (!HasTaskCLSingleThreadMessage(timelineBuildEntry)) { return; } // because it's single threaded, it will first execute the compiler frontend for all // files, then the compiler backend for all files, sequentially // so, we have to parse messages and expect frontend ones first, then the special message // saying that phase is over, then the backend ones (and maybe cycle to a frontend phase) List <TimelineEntry> compilationEntries = new List <TimelineEntry>(); IEnumerable <MessageEvent> messages = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _ is MessageEvent) .Select(_ => _ as MessageEvent); bool isInFrontendPhase = true; foreach (MessageEvent message in messages) { if (isInFrontendPhase) { Debug.Assert(!s_CompileBackEndFinish.IsMatch(message.Message)); // file compilation started Match matchFileStarted = s_CompileFileStart.Match(message.Message); if (matchFileStarted.Success) { TimelineEntry compilationEntry = new TimelineEntry(matchFileStarted.Value, message.Context.NodeId, message.Timestamp, message.Timestamp); compilationEntries.Add(compilationEntry); compilationEntry.ThreadAffinity.SetParameters(compilationEntry.ThreadAffinity.ThreadId, ThreadAffinity.s_OffsetFromParentPostProcessedEntries, ThreadAffinity.s_OffsetFromParentPostProcessedEntriesIncrement); continue; } // front-end compilation finished Match matchFrontendFinished = s_CompileFrontEndFinish.Match(message.Message); if (matchFrontendFinished.Success) { TimelineEntry compilationEntry = compilationEntries.Find(_ => _.Name == matchFrontendFinished.Groups[3].Value.Split('\\').Last()); if (compilationEntry != null) { double elapsedTimeFromMessage = Double.Parse(matchFrontendFinished.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (startTimestamp < compilationEntry.StartTimestamp) { startTimestamp = compilationEntry.StartTimestamp; } // add a front-end entry TimelineEntry frontend = new TimelineEntry(matchFrontendFinished.Groups[1].Value, message.Context.NodeId, startTimestamp, message.Timestamp); compilationEntry.AddChild(frontend); compilationEntry.SetEndTimestamp(message.Timestamp); } continue; } if (message.Message == s_TaskCLSingleThreadFrontEndFinishedMessage) { isInFrontendPhase = false; } } else { Debug.Assert(!s_CompileFrontEndFinish.IsMatch(message.Message)); // back-end compilation finished Match matchBackendFinished = s_CompileBackEndFinish.Match(message.Message); if (matchBackendFinished.Success) { Debug.Assert(compilationEntries.Count > 0); // create new entry as parent of the back-end entry TimelineEntry compilationEntry = new TimelineEntry(matchBackendFinished.Groups[3].Value.Split('\\').Last(), message.Context.NodeId, compilationEntries.Last().EndTimestamp, message.Timestamp); compilationEntries.Add(compilationEntry); compilationEntry.ThreadAffinity.SetParameters(compilationEntry.ThreadAffinity.ThreadId, ThreadAffinity.s_OffsetFromParentPostProcessedEntries, ThreadAffinity.s_OffsetFromParentPostProcessedEntriesIncrement); // add a back-end entry double elapsedTimeFromMessage = Double.Parse(matchBackendFinished.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (startTimestamp < compilationEntry.StartTimestamp) { startTimestamp = compilationEntry.StartTimestamp; } TimelineEntry backend = new TimelineEntry(matchBackendFinished.Groups[1].Value, message.Context.NodeId, startTimestamp, message.Timestamp); compilationEntry.AddChild(backend); continue; } if (message.Message == s_TaskCLSingleThreadFrontEndStartedMessage) { isInFrontendPhase = true; } } } // add them all to the CL task compilationEntries.ForEach(timelineBuildEntry.AddChild); }
public static void FlagD1ReportTime(TimelineEntry entry) { if (!IsTaskCL(entry)) { return; } TimelineBuildEntry timelineBuildEntry = entry as TimelineBuildEntry; // flag /d1reportTime operates within the front-end and has three sections // - Headers: time spent including headers // - Class Definitions: time spent defining classes // - Function Definitions: time spent defining functions // each section ends with a blank line and a Total // because of how MSBuild reports messages, we require build to compile each // file in a single-threaded fashion or we'd get data mixed up IEnumerable <MessageEvent> messages = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _ is MessageEvent) .Select(_ => _ as MessageEvent); // kind of a simplified state machine TimelineEntry currentFrontendEntry = null; TimelineEntry currentSubEntry = null; D1ReportTimeSection currentSection = D1ReportTimeSection.None; int currentIndentationLevel = 0; bool processEntries = false; foreach (MessageEvent message in messages) { // file compilation started Match matchFileStarted = s_CompileFileStart.Match(message.Message); if (matchFileStarted.Success) { currentFrontendEntry = timelineBuildEntry.ChildEntries.Find(_ => _.Name == matchFileStarted.Value); Debug.Assert(currentFrontendEntry.ChildEntries.Count > 0, "Processing /d1reportTime without an existing front-end child"); // first entry is the front-end one, second one (if any) is the back-end one currentSubEntry = currentFrontendEntry.ChildEntries.First(); continue; } // processing /d1reportTime if (currentFrontendEntry != null) { switch (currentSection) { case D1ReportTimeSection.None: { // look for a new section Match matchSectionHeaders = s_D1ReportTimeSectionHeader.Match(message.Message); if (matchSectionHeaders.Success) { currentSection = D1ReportTimeSection.Headers; // first message starts with two tabs currentIndentationLevel = s_D1ReportTimeEntryBaseIndentation; TimelineEntry headersEntry = new TimelineEntry(matchSectionHeaders.Groups[1].Value, message.Context.NodeId, currentSubEntry.StartTimestamp, message.Timestamp); currentSubEntry.AddChild(headersEntry); currentSubEntry = headersEntry; processEntries = true; continue; } Match matchSectionClasses = s_D1ReportTimeSectionClassDefinition.Match(message.Message); if (matchSectionClasses.Success) { currentSection = D1ReportTimeSection.ClassDefinitions; // first message starts with two tabs currentIndentationLevel = s_D1ReportTimeEntryBaseIndentation; TimelineEntry classesEntry = new TimelineEntry(matchSectionClasses.Groups[1].Value, message.Context.NodeId, currentSubEntry.ChildEntries.Last().EndTimestamp, message.Timestamp); currentSubEntry.AddChild(classesEntry); currentSubEntry = classesEntry; processEntries = true; continue; } Match matchSectionFunctions = s_D1ReportTimeSectionFunctionDefintition.Match(message.Message); if (matchSectionFunctions.Success) { currentSection = D1ReportTimeSection.FunctionDefinitions; // first message starts with two tabs currentIndentationLevel = s_D1ReportTimeEntryBaseIndentation; TimelineEntry classesEntry = new TimelineEntry(matchSectionFunctions.Groups[1].Value, message.Context.NodeId, currentSubEntry.ChildEntries.Last().EndTimestamp, message.Timestamp); currentSubEntry.AddChild(classesEntry); currentSubEntry = classesEntry; processEntries = true; continue; } } break; case D1ReportTimeSection.Headers: case D1ReportTimeSection.ClassDefinitions: case D1ReportTimeSection.FunctionDefinitions: { // get total time spent in this section Match matchTotalTime = s_D1ReportTimeSectionTotal.Match(message.Message); if (matchTotalTime.Success) { double elapsedTimeFromMessage = Double.Parse(matchTotalTime.Groups[1].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime endTimestamp = currentSubEntry.StartTimestamp + elapsedTime; currentSubEntry.SetEndTimestamp(endTimestamp); currentSection = D1ReportTimeSection.None; currentSubEntry = currentSubEntry.Parent; continue; } // block completed? Match matchBlockCompleted = s_D1ReportTimeBlockEnd.Match(message.Message); if (matchBlockCompleted.Success) { if (processEntries) { // pop entries until we get to the top level for (int i = currentIndentationLevel; i > s_D1ReportTimeEntryBaseIndentation; --i) { currentSubEntry = currentSubEntry.Parent; } processEntries = false; } continue; } if (processEntries) { // match with an element Match matchElement = s_D1ReportTimeEntry.Match(message.Message); if (matchElement.Success) { // \t is a single character int indentationLevel = matchElement.Groups[1].Value.Length; // go to a parent, if needed if (indentationLevel < currentIndentationLevel) { for (int i = 0; i < currentIndentationLevel - indentationLevel; ++i) { currentSubEntry = currentSubEntry.Parent; } } // move to a child, if needed else if (indentationLevel > currentIndentationLevel) { currentSubEntry = currentSubEntry.ChildEntries.Last(); } // find out the start timestamp DateTime startTimestamp = currentSubEntry.StartTimestamp; if (currentSubEntry.ChildEntries.Count > 0) { startTimestamp = currentSubEntry.ChildEntries.Last().EndTimestamp; } // find out the end timestamp double elapsedTimeFromMessage = Double.Parse(matchElement.Groups[3].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime endTimestamp = startTimestamp + elapsedTime; // build element TimelineEntry elementEntry = new TimelineEntry(matchElement.Groups[2].Value, message.Context.NodeId, startTimestamp, endTimestamp); currentSubEntry.AddChild(elementEntry); currentIndentationLevel = indentationLevel; continue; } } } break; } } // front-end finished Match matchFrontendFinished = s_CompileFrontEndFinish.Match(message.Message); if (matchFrontendFinished.Success) { currentFrontendEntry = null; currentSubEntry = null; currentSection = D1ReportTimeSection.None; continue; } } }
public static void TaskLink(TimelineEntry entry) { if (!IsTaskLink(entry)) { return; } TimelineBuildEntry timelineBuildEntry = entry as TimelineBuildEntry; // because these messages aren't related to each other in any way other than parsing messages, // we'll be keeping a list of timeline entries which will open/close as the messages are processed List <TimelineEntry> linkEntries = new List <TimelineEntry>(); IEnumerable <MessageEvent> messages = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _ is MessageEvent) .Select(_ => _ as MessageEvent); // find Pass 1 and Pass 2 foreach (MessageEvent message in messages) { // Linker: Pass 1 Match matchLinkerPass1 = s_LinkPass1.Match(message.Message); if (matchLinkerPass1.Success) { double elapsedTimeFromMessage = Double.Parse(matchLinkerPass1.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (startTimestamp < timelineBuildEntry.StartTimestamp) { startTimestamp = timelineBuildEntry.StartTimestamp; } TimelineEntry pass1Entry = new TimelineEntry(s_LinkerPass1Name, message.Context.NodeId, startTimestamp, message.Timestamp); linkEntries.Add(pass1Entry); pass1Entry.ThreadAffinity.SetParameters(pass1Entry.ThreadAffinity.ThreadId, ThreadAffinity.s_OffsetFromParentPostProcessedEntries, ThreadAffinity.s_OffsetFromParentPostProcessedEntriesIncrement); continue; } // Linker: Pass 2 Match matchLinkerPass2 = s_LinkPass2.Match(message.Message); if (matchLinkerPass2.Success) { double elapsedTimeFromMessage = Double.Parse(matchLinkerPass2.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (linkEntries.Count > 0 && startTimestamp < linkEntries.Last().EndTimestamp) { startTimestamp = linkEntries.Last().EndTimestamp; } TimelineEntry pass2Entry = new TimelineEntry(s_LinkerPass2Name, message.Context.NodeId, startTimestamp, message.Timestamp); linkEntries.Add(pass2Entry); pass2Entry.ThreadAffinity.SetParameters(pass2Entry.ThreadAffinity.ThreadId, ThreadAffinity.s_OffsetFromParentPostProcessedEntries, ThreadAffinity.s_OffsetFromParentPostProcessedEntriesIncrement); continue; } } // this is the function that will be applied to all common linker messages except Pass 1 and Pass 2 messages Action <TimelineEntry, Tuple <MessageEvent, Match> > perCommonLinkerMessage = (parent, tuple) => { MessageEvent message = tuple.Item1; Match match = tuple.Item2; double elapsedTimeFromMessage = Double.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (parent.ChildEntries.Count == 0 && startTimestamp < parent.StartTimestamp) { startTimestamp = parent.StartTimestamp; } else if (parent.ChildEntries.Count > 0 && startTimestamp < parent.ChildEntries.Last().EndTimestamp) { startTimestamp = parent.ChildEntries.Last().EndTimestamp; } TimelineEntry linkerEntry = new TimelineEntry(match.Groups[2].Value.Trim(), message.Context.NodeId, startTimestamp, message.Timestamp); parent.AddChild(linkerEntry); }; // add entries within Pass 1 TimelineEntry parentEntry = linkEntries.Find(_ => _.Name == s_LinkerPass1Name); if (parentEntry != null) { IEnumerable <Tuple <MessageEvent, Match> > messagesBeforePass1 = messages.TakeWhile(_ => !s_LinkPass1.Match(_.Message).Success) // take messages until Pass 1 message .Select(_ => new Tuple <MessageEvent, Match>(_, s_LinkCommon.Match(_.Message))) // match with common regex .Where(_ => _.Item2.Success); // only take positive ones foreach (Tuple <MessageEvent, Match> tuple in messagesBeforePass1) { perCommonLinkerMessage(parentEntry, tuple); } } // add entries within Pass 2 parentEntry = linkEntries.Find(_ => _.Name == s_LinkerPass2Name); if (parentEntry != null) { IEnumerable <Tuple <MessageEvent, Match> > messagesBeforePass2 = messages.SkipWhile(_ => !s_LinkPass1.Match(_.Message).Success) // skip all messages until Pass 1 .Skip(1) // skip Pass 1 included .TakeWhile(_ => !s_LinkPass2.Match(_.Message).Success) // take messages until Pass 2 message .Select(_ => new Tuple <MessageEvent, Match>(_, s_LinkCommon.Match(_.Message))) // match with common regex .Where(_ => _.Item2.Success); // only take positive ones foreach (Tuple <MessageEvent, Match> tuple in messagesBeforePass2) { perCommonLinkerMessage(parentEntry, tuple); } } // add them all to the Link task linkEntries.ForEach(timelineBuildEntry.AddChild); }
public static void TaskCLMultiThread(TimelineEntry entry) { if (!IsTaskCL(entry)) { return; } TimelineBuildEntry timelineBuildEntry = entry as TimelineBuildEntry; // single threaded compilations contain a message that multithreaded ones don't if (HasTaskCLSingleThreadMessage(timelineBuildEntry)) { return; } // because these messages aren't related to each other in any way other than parsing messages, // we'll be keeping a list of timeline entries which will open/close as the messages are processed List <TimelineEntry> compilationEntries = new List <TimelineEntry>(); IEnumerable <MessageEvent> messages = timelineBuildEntry.BuildEntry.ChildEvents.Where(_ => _ is MessageEvent) .Select(_ => _ as MessageEvent); foreach (MessageEvent message in messages) { // file compilation started Match matchFileStarted = s_CompileFileStart.Match(message.Message); if (matchFileStarted.Success) { // with /Wall flag enabled some warnings will be mixed up with this kind of message // meaning we may not match the regex and won't add the entry TimelineEntry compilationEntry = new TimelineEntry(matchFileStarted.Value, message.Context.NodeId, message.Timestamp, message.Timestamp); compilationEntries.Add(compilationEntry); compilationEntry.ThreadAffinity.SetParameters(compilationEntry.ThreadAffinity.ThreadId, ThreadAffinity.s_OffsetFromParentPostProcessedEntries, ThreadAffinity.s_OffsetFromParentPostProcessedEntriesIncrement); continue; } // front-end compilation finished Match matchFrontendFinished = s_CompileFrontEndFinish.Match(message.Message); if (matchFrontendFinished.Success) { // with /Wall flag enabled some warnings will be mixed up with this kind of message // meaning we may not match the regex and won't add the entry // we may not even find the parent compilation entry TimelineEntry compilationEntry = compilationEntries.Find(_ => _.Name == matchFrontendFinished.Groups[3].Value.Split('\\').Last()); if (compilationEntry != null) { double elapsedTimeFromMessage = Double.Parse(matchFrontendFinished.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (startTimestamp < compilationEntry.StartTimestamp) { startTimestamp = compilationEntry.StartTimestamp; } // add a front-end entry TimelineEntry frontend = new TimelineEntry(matchFrontendFinished.Groups[1].Value, message.Context.NodeId, startTimestamp, message.Timestamp); compilationEntry.AddChild(frontend); // just in case the back-end fails, update entry's timestamp compilationEntry.SetEndTimestamp(message.Timestamp); } continue; } // back-end compilation finished Match matchBackendFinished = s_CompileBackEndFinish.Match(message.Message); if (matchBackendFinished.Success) { // with /Wall flag enabled some warnings will be mixed up with this kind of message // meaning we may not match the regex and won't add the entry // we may not even find the parent compilation entry nor the frontend sibling TimelineEntry compilationEntry = compilationEntries.Find(_ => _.Name == matchBackendFinished.Groups[3].Value.Split('\\').Last()); if (compilationEntry != null) { TimelineEntry frontendEntry = compilationEntry.ChildEntries.FirstOrDefault(); double elapsedTimeFromMessage = Double.Parse(matchBackendFinished.Groups[2].Value, CultureInfo.InvariantCulture); TimeSpan elapsedTime = TimeSpanFromSeconds(elapsedTimeFromMessage); DateTime startTimestamp = message.Timestamp - elapsedTime; if (frontendEntry != null) { if (startTimestamp < frontendEntry.EndTimestamp) { startTimestamp = frontendEntry.EndTimestamp; } } else if (startTimestamp < compilationEntry.StartTimestamp) { startTimestamp = compilationEntry.StartTimestamp; } // add a back-end entry TimelineEntry backend = new TimelineEntry(matchBackendFinished.Groups[1].Value, message.Context.NodeId, startTimestamp, message.Timestamp); compilationEntry.AddChild(backend); // complete compilation entry compilationEntry.SetEndTimestamp(message.Timestamp); } continue; } } // add them all to the CL task compilationEntries.ForEach(timelineBuildEntry.AddChild); }
private void CalculateParallelEntriesFor(TimelineEntry entry, PerNodeThreadRootEntries nodeThreadRootEntries) { if (entry is TimelineBuildEntry) { entry.ThreadAffinity.SetParameters(entry.ThreadAffinity.ThreadId, 0, ThreadAffinity.s_OffsetMSBuildEntries); } if (entry.Parent != null) { // retrieve all of the overlapping siblings List <TimelineEntry> overlappingSiblings = new List <TimelineEntry>(); foreach (TimelineEntry sibling in entry.Parent.ChildEntries) { if (entry != sibling && entry.OverlapsWith(sibling)) { overlappingSiblings.Add(sibling); } } // we may have calculated some of the siblings, take their information into account foreach (TimelineEntry overlappingSibling in overlappingSiblings) { if (overlappingSibling.ThreadAffinity.Calculated) { entry.ThreadAffinity.AddInvalid(overlappingSibling.ThreadAffinity.ThreadId); } } } // also, check the overlapping root entries from each calculated thread within the same node foreach (var pair in nodeThreadRootEntries) { if (pair.Key.Item1 == entry.NodeId) { foreach (TimelineEntry root in pair.Value) { Debug.Assert(root.ThreadAffinity.Calculated); if (!root.IsAncestorOf(entry) && entry.OverlapsWith(root)) { entry.ThreadAffinity.AddInvalid(root.ThreadAffinity.ThreadId); } } } } // now calculate where we think the entry was executed entry.ThreadAffinity.Calculate(); // are we a new root? if (entry.Parent == null || entry.ThreadAffinity.ThreadId != entry.Parent.ThreadAffinity.ThreadId) { // get or create root list for the <node id, calculated thread id> Tuple <int, int> key = new Tuple <int, int>(entry.NodeId, entry.ThreadAffinity.ThreadId); List <TimelineEntry> rootsInNodeThread = null; if (!nodeThreadRootEntries.TryGetValue(key, out rootsInNodeThread)) { rootsInNodeThread = new List <TimelineEntry>(); nodeThreadRootEntries[key] = rootsInNodeThread; } rootsInNodeThread.Add(entry); } // now that we've decided where the entry was executed, transfer this data to child entries foreach (TimelineEntry child in entry.ChildEntries) { child.ThreadAffinity.InheritDataFrom(entry.ThreadAffinity); } // continue with child entries foreach (TimelineEntry child in entry.ChildEntries) { CalculateParallelEntriesFor(child, nodeThreadRootEntries); } }
public bool OverlapsWith(TimelineEntry other) { return(NodeId == other.NodeId && StartTimestamp < other.EndTimestamp && other.StartTimestamp < EndTimestamp); }
public void AddChild(TimelineEntry entry) { ChildEntries.Add(entry); entry.Parent = this; }