public override bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues)
        {
            // Make sure we're running a step that this applies to
            if (JobStep.Name.IndexOf("Copyright", StringComparison.OrdinalIgnoreCase) == -1)
            {
                return(false);
            }

            // Find any files in compiler output format
            HashSet <string> SourceFileNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (Match FileMatch in Regex.Matches(Diagnostic.Message, @"^\s*(?:WARNING|ERROR):\s*([^ ]+\.[a-zA-Z]+):", RegexOptions.Multiline))
            {
                if (FileMatch.Success)
                {
                    SourceFileNames.Add(FileMatch.Groups[1].Value);
                }
            }

            // If we found any source files, create a diagnostic category for them
            if (SourceFileNames.Count > 0)
            {
                BuildHealthIssue Issue = new BuildHealthIssue(Category, Job.Url, new BuildHealthDiagnostic(JobStep.Name, Diagnostic.Message, Diagnostic.Url));
                Issue.FileNames.UnionWith(SourceFileNames);
                Issues.Add(Issue);
                return(true);
            }

            // Otherwise pass
            return(false);
        }
Exemple #2
0
        public override bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues)
        {
            // Find any files in compiler output format
            HashSet <string> SourceFileNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (Match FileMatch in Regex.Matches(Diagnostic.Message, @"^\s*(?:In file included from\s*)?((?:[A-Za-z]:)?[^\s(:]+)[\(:]\d", RegexOptions.Multiline))
            {
                if (FileMatch.Success)
                {
                    string FileName = GetNormalizedFileName(FileMatch.Groups[1].Value, JobStep.BaseDirectory);
                    if (SourceFileExtensions.Any(x => FileName.EndsWith(x, StringComparison.OrdinalIgnoreCase)))
                    {
                        SourceFileNames.Add(FileName);
                    }
                }
            }

            // If we found any source files, create a diagnostic category for them
            if (SourceFileNames.Count > 0)
            {
                BuildHealthIssue Issue = new BuildHealthIssue(Category, Job.Url, new BuildHealthDiagnostic(JobStep.Name, ShortenPaths(Diagnostic.Message), Diagnostic.Url));
                Issue.FileNames.UnionWith(SourceFileNames);
                Issues.Add(Issue);
                return(true);
            }

            // Otherwise pass
            return(false);
        }
        public override bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues)
        {
            string DefaultProject = String.Format("{0} (Unmatched)", Job.Project);

            BuildHealthIssue Issue = new BuildHealthIssue(DefaultProject, Category, Job.Url, new BuildHealthDiagnostic(JobStep.Name, JobStep.Url, Diagnostic.Message, Diagnostic.Url));

            Issue.Identifiers.Add(Diagnostic.Message);
            Issues.Add(Issue);

            return(true);
        }
Exemple #4
0
 /// <summary>
 /// Creates fingerprints from any matching diagnostics
 /// </summary>
 /// <param name="Job">The job that was run</param>
 /// <param name="JobStep">The job step that was run</param>
 /// <param name="Diagnostics">List of diagnostics that were produced by the build. Items should be removed from this list if they match.</param>
 /// <param name="Issues">List which receives all the matched issues.</param>
 public virtual void Match(InputJob Job, InputJobStep JobStep, List <InputDiagnostic> Diagnostics, List <BuildHealthIssue> Issues)
 {
     for (int Idx = 0; Idx < Diagnostics.Count; Idx++)
     {
         InputDiagnostic Diagnostic = Diagnostics[Idx];
         if (TryMatch(Job, JobStep, Diagnostic, Issues))
         {
             Diagnostics.RemoveAt(Idx);
             Idx--;
         }
     }
 }
        public override bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues)
        {
            HashSet <string> FileNames = new HashSet <string>();

            foreach (Match Match in Regex.Matches(Diagnostic.Message, @"^\s*Log[a-zA-Z0-9]+:\s+(?:Error:|Warning:)\s+((?:[a-zA-Z]:)?[^:]+(?:.uasset|.umap)):\s*(.*)"))
            {
                FileNames.Add(GetNormalizedFileName(Match.Groups[1].Value, JobStep.BaseDirectory));
            }

            if (FileNames.Count > 0)
            {
                BuildHealthIssue Issue = new BuildHealthIssue(Job.Project, Category, Job.Url, new BuildHealthDiagnostic(JobStep.Name, JobStep.Url, Diagnostic.Message, Diagnostic.Url));
                Issue.FileNames.UnionWith(FileNames);
                Issues.Add(Issue);
                return(true);
            }
            return(false);
        }
Exemple #6
0
        /// <summary>
        /// Adds a new build history for a stream
        /// </summary>
        /// <param name="Issue">The issue to add a build to</param>
        /// <param name="InputJob">The job containing the error</param>
        /// <param name="InputJobStep">The job step containing the error</param>
        /// <param name="InputErrorUrl">Url of the error</param>
        /// <param name="State">Current persistent state. Used to find previous build history.</param>
        void AddFailureToIssue(BuildHealthIssue Issue, InputJob InputJob, InputJobStep InputJobStep, string InputErrorUrl, BuildHealthState State)
        {
            // Find or add a step name to history mapping
            Dictionary <string, BuildHealthJobHistory> StepNameToHistory;

            if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
            {
                StepNameToHistory = new Dictionary <string, BuildHealthJobHistory>();
                Issue.Streams.Add(InputJob.Stream, StepNameToHistory);
            }

            // Find or add a history for this step
            BuildHealthJobHistory History;

            if (!StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
            {
                History = new BuildHealthJobHistory(State.FindBuildBefore(InputJob.Stream, InputJob.Change, InputJobStep.Name));
                StepNameToHistory.Add(InputJobStep.Name, History);
            }

            // Add the new build
            History.AddFailedBuild(CreateBuildForJobStep(InputJob, InputJobStep, InputErrorUrl));
        }
Exemple #7
0
 /// <summary>
 /// Creates a TrackedBuild instance for the given jobstep
 /// </summary>
 /// <param name="InputJob">The job to create a build for</param>
 /// <param name="InputJobStep">The step to create a build for</param>
 /// <param name="InputErrorUrl">The error Url</param>
 /// <returns>New build instance</returns>
 BuildHealthJobStep CreateBuildForJobStep(InputJob InputJob, InputJobStep InputJobStep, string InputErrorUrl)
 {
     return(new BuildHealthJobStep(InputJob.Change, InputJob.Name, InputJob.Url, InputJobStep.Name, InputJobStep.Url, InputErrorUrl));
 }
Exemple #8
0
        /// <summary>
        /// Finds or adds an issue for a particular issue
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        BuildHealthIssue MergeIntoExistingIssue(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep, BuildHealthIssue InputIssue, Lazy <IReadOnlyList <ChangeInfo> > LazyChanges)
        {
            // Find the pattern matcher for this fingerprint
            PatternMatcher Matcher = CategoryToMatcher[InputIssue.Category];

            // Check if it can be added to an existing open issue
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                // Check this issue already exists in the current stream
                Dictionary <string, BuildHealthJobHistory> StepNameToHistory;
                if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
                {
                    continue;
                }

                // Check that this issue has not already been closed
                BuildHealthJobHistory History;
                if (StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
                {
                    if (!History.CanAddFailedBuild(InputJob.Change))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!StepNameToHistory.Values.Any(x => x.CanAddFailedBuild(InputJob.Change)))
                    {
                        continue;
                    }
                }

                // Try to merge the fingerprint
                if (!Matcher.CanMerge(InputIssue, Issue))
                {
                    continue;
                }

                // Add the new build
                Matcher.Merge(InputIssue, Issue);
                return(Issue);
            }

            // Check if this issue can be merged with an issue built in another stream
            IReadOnlyList <ChangeInfo> Changes = LazyChanges.Value;

            if (Changes != null && Changes.Count > 0)
            {
                SortedSet <int> SourceChanges = new SortedSet <int>(Changes.SelectMany(x => x.SourceChanges));
                foreach (BuildHealthIssue Issue in State.Issues)
                {
                    // Check if this issue does not already contain this stream, but contains one of the causing changes
                    if (Issue.Streams.ContainsKey(InputJob.Stream))
                    {
                        continue;
                    }
                    if (!SourceChanges.Any(x => Issue.SourceChanges.Contains(x)))
                    {
                        continue;
                    }
                    if (!Matcher.CanMerge(InputIssue, Issue))
                    {
                        continue;
                    }

                    // Merge the issue
                    Matcher.Merge(InputIssue, Issue);
                    return(Issue);
                }
            }

            // Check if it can be merged into an issue that's been created for this job. We only do this after exhausting all other options.
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                if (Issue.InitialJobUrl == InputIssue.InitialJobUrl && Matcher.CanMergeInitialJob(InputIssue, Issue))
                {
                    Matcher.Merge(InputIssue, Issue);
                    return(Issue);
                }
            }

            return(null);
        }
Exemple #9
0
        /// <summary>
        /// Adds diagnostics from a job step into the issue database
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        void AddStep(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep)
        {
            // Create a lazily evaluated list of changes that are responsible for any errors
            Lazy <IReadOnlyList <ChangeInfo> > LazyChanges = new Lazy <IReadOnlyList <ChangeInfo> >(() => FindChanges(Perforce, State, InputJob));

            // Create issues for any diagnostics in this step
            List <BuildHealthIssue> InputIssues = new List <BuildHealthIssue>();

            foreach (PatternMatcher Matcher in Matchers)
            {
                Matcher.Match(InputJob, InputJobStep, InputJobStep.Diagnostics, InputIssues);
            }

            // Merge the issues together
            List <BuildHealthIssue> NewIssues = new List <BuildHealthIssue>();

            foreach (BuildHealthIssue InputIssue in InputIssues)
            {
                BuildHealthIssue OutputIssue = MergeIntoExistingIssue(Perforce, State, InputJob, InputJobStep, InputIssue, LazyChanges);
                if (OutputIssue == null)
                {
                    NewIssues.Add(InputIssue);
                    State.Issues.Add(InputIssue);
                    OutputIssue = InputIssue;
                }
                AddFailureToIssue(OutputIssue, InputJob, InputJobStep, InputIssue.Diagnostics[0].ErrorUrl, State);
            }

            // Update the watchers for any new issues
            foreach (BuildHealthIssue NewIssue in NewIssues)
            {
                IReadOnlyList <ChangeInfo> Changes = LazyChanges.Value;
                if (Changes != null)
                {
                    // Find the pattern matcher for this issue
                    PatternMatcher Matcher = CategoryToMatcher[NewIssue.Category];

                    // Update the causers
                    List <ChangeInfo> Causers = Matcher.FindCausers(Perforce, NewIssue, Changes);
                    foreach (ChangeInfo Causer in Causers)
                    {
                        NewIssue.SourceChanges.UnionWith(Causer.SourceChanges);
                        NewIssue.PendingWatchers.Add(Causer.Record.User);
                    }
                }
            }
        }
        public override bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues)
        {
            List <string> SymbolMatches = new List <string>();

            // Mac link error:
            //   Undefined symbols for architecture arm64:
            //     "Foo::Bar() const", referenced from:
            if (Regex.IsMatch(Diagnostic.Message, "^Undefined symbols"))
            {
                foreach (string Line in Diagnostic.Message.Split('\n'))
                {
                    Match SymbolMatch = Regex.Match(Line, "^  \"(.+)\"");
                    if (SymbolMatch.Success)
                    {
                        SymbolMatches.Add(SymbolMatch.Groups[1].Value);
                    }
                }
            }

            // Android link error:
            //   Foo.o:(.data.rel.ro + 0x5d88): undefined reference to `Foo::Bar()'
            Match UndefinedReference = Regex.Match(Diagnostic.Message, ": undefined reference to [`']([^`']+)");

            if (UndefinedReference.Success)
            {
                SymbolMatches.Add(UndefinedReference.Groups[1].Value);
            }

            // LLD link error:
            //   ld.lld.exe: error: undefined symbol: Foo::Bar() const
            Match LldMatch = Regex.Match(Diagnostic.Message, "error: undefined symbol:\\s*(.+)");

            if (LldMatch.Success)
            {
                SymbolMatches.Add(LldMatch.Groups[1].Value);
            }

            // Link error:
            //   Link: error: L0039: reference to undefined symbol `Foo::Bar() const' in file
            Match LinkMatch = Regex.Match(Diagnostic.Message, ": reference to undefined symbol [`']([^`']+)");

            if (LinkMatch.Success)
            {
                SymbolMatches.Add(LinkMatch.Groups[1].Value);
            }

            // Microsoft linker error:
            //   Foo.cpp.obj : error LNK2001: unresolved external symbol \"private: virtual void __cdecl UAssetManager::InitializeAssetBundlesFromMetadata_Recursive(class UStruct const *,void const *,struct FAssetBundleData &,class FName,class TSet<void const *,struct DefaultKeyFuncs<void const *,0>,class FDefaultSetAllocator> &)const \" (?InitializeAssetBundlesFromMetadata_Recursive@UAssetManager@@EEBAXPEBVUStruct@@PEBXAEAUFAssetBundleData@@VFName@@AEAV?$TSet@PEBXU?$DefaultKeyFuncs@PEBX$0A@@@VFDefaultSetAllocator@@@@@Z)",
            Match MicrosoftMatch = Regex.Match(Diagnostic.Message, ": unresolved external symbol \"([^\"]*)\"");

            if (MicrosoftMatch.Success)
            {
                SymbolMatches.Add(MicrosoftMatch.Groups[1].Value);
            }

            // Clean up all the symbol names
            SortedSet <string> SymbolNames = new SortedSet <string>(StringComparer.Ordinal);

            foreach (string SymbolMatch in SymbolMatches)
            {
                string SymbolName = SymbolMatch;

                // Remove any __declspec qualifiers
                SymbolName = Regex.Replace(SymbolName, "(?<![^a-zA-Z_])__declspec\\([^\\)]+\\)", "");

                // Remove any argument lists for functions (anything after the first paren)
                SymbolName = Regex.Replace(SymbolName, "\\(.*$", "");

                // Remove any decorators and type information (greedy match up to the last space)
                SymbolName = Regex.Replace(SymbolName, "^.* ", "");

                // Add it to the list
                SymbolNames.Add(SymbolName);
            }

            // If we found any symbol names, create a fingerprint for them
            if (SymbolNames.Count > 0)
            {
                BuildHealthIssue Issue = new BuildHealthIssue(Job.Project, Category, Job.Url, new BuildHealthDiagnostic(JobStep.Name, JobStep.Url, Diagnostic.Message, Diagnostic.Url));
                Issue.Identifiers.UnionWith(SymbolNames);
                Issues.Add(Issue);
                return(true);
            }

            // Otherwise pass
            return(false);
        }
Exemple #11
0
        /// <summary>
        /// Finds or adds an issue for a particular fingerprint
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        BuildHealthIssue FindOrAddIssueForFingerprint(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep, BuildHealthIssue InputIssue)
        {
            // Find the pattern matcher for this fingerprint
            PatternMatcher Matcher = CategoryToMatcher[InputIssue.Category];

            // Check if it can be added to an existing open issue
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                // Check this issue already exists in the current stream
                Dictionary <string, BuildHealthJobHistory> StepNameToHistory;
                if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
                {
                    continue;
                }

                // Check that this issue has not already been closed
                BuildHealthJobHistory History;
                if (StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
                {
                    if (!History.CanAddFailedBuild(InputJob.Change))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!StepNameToHistory.Values.Any(x => x.CanAddFailedBuild(InputJob.Change)))
                    {
                        continue;
                    }
                }

                // Try to merge the fingerprint
                if (!Matcher.CanMerge(InputIssue, Issue))
                {
                    continue;
                }

                // Add the new build
                Matcher.Merge(InputIssue, Issue);
                return(Issue);
            }

            // List of changes since the last successful build in this stream
            IReadOnlyList <ChangeInfo> Changes = null;

            // Find the previous changelist that was built in this stream
            List <BuildHealthJobStep> StreamBuilds;

            if (State.Streams.TryGetValue(InputJob.Stream, out StreamBuilds))
            {
                // Find the last change submitted to this stream before it started failing
                int LastChange = -1;
                for (int Idx = 0; Idx < StreamBuilds.Count && StreamBuilds[Idx].Change < InputJob.Change; Idx++)
                {
                    LastChange = StreamBuilds[Idx].Change;
                }

                // Allow adding to any open issue that contains changes merged from other branches
                if (LastChange != -1)
                {
                    // Query for all the changes since then
                    Changes = FindChanges(Perforce, InputJob.Stream, LastChange, InputJob.Change);
                    if (Changes.Count > 0)
                    {
                        SortedSet <int> SourceChanges = new SortedSet <int>(Changes.SelectMany(x => x.SourceChanges));
                        foreach (BuildHealthIssue Issue in State.Issues)
                        {
                            // Check if this issue does not already contain this stream, but contains one of the causing changes
                            if (Issue.Streams.ContainsKey(InputJob.Stream))
                            {
                                continue;
                            }
                            if (!SourceChanges.Any(x => Issue.SourceChanges.Contains(x)))
                            {
                                continue;
                            }
                            if (!Matcher.CanMerge(InputIssue, Issue))
                            {
                                continue;
                            }

                            // Merge the issue
                            Matcher.Merge(InputIssue, Issue);
                            return(Issue);
                        }
                    }
                }
            }

            // Create new issues for everything else in this stream
            if (Changes != null)
            {
                List <ChangeInfo> Causers = Matcher.FindCausers(Perforce, InputIssue, Changes);
                foreach (ChangeInfo Causer in Causers)
                {
                    InputIssue.SourceChanges.UnionWith(Causer.SourceChanges);
                    InputIssue.PendingWatchers.Add(Causer.Record.User);
                }
            }
            State.Issues.Add(InputIssue);
            return(InputIssue);
        }
Exemple #12
0
        /// <summary>
        /// Adds diagnostics from a job step into the issue database
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        void AddStep(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep)
        {
            // Create all the fingerprints for failures in this step
            List <BuildHealthIssue> InputIssues = new List <BuildHealthIssue>();

            if (InputJobStep.Diagnostics != null)
            {
                List <InputDiagnostic> Diagnostics = new List <InputDiagnostic>(InputJobStep.Diagnostics);
                foreach (PatternMatcher PatternMatcher in Matchers)
                {
                    PatternMatcher.Match(InputJob, InputJobStep, Diagnostics, InputIssues);
                }
            }

            // Add all the fingerprints to issues
            foreach (BuildHealthIssue InputIssue in InputIssues)
            {
                BuildHealthIssue Issue = FindOrAddIssueForFingerprint(Perforce, State, InputJob, InputJobStep, InputIssue);
                AddFailureToIssue(Issue, InputJob, InputJobStep, InputIssue.Diagnostics[0].ErrorUrl, State);
            }
        }
Exemple #13
0
 /// <summary>
 /// Tries to create a fingerprint from an individual diagnostic.
 /// </summary>
 /// <param name="Job">The job that was run</param>
 /// <param name="JobStep">The job step that was run</param>
 /// <param name="Diagnostic">A diagnostic from the given job step</param>
 /// <param name="Issues">List which receives all the matched issues.</param>
 /// <returns>True if this diagnostic should be removed (usually because a fingerprint was created)</returns>
 public abstract bool TryMatch(InputJob Job, InputJobStep JobStep, InputDiagnostic Diagnostic, List <BuildHealthIssue> Issues);