/// <summary> /// Accesses the instance. /// </summary> public static FRepository Get(CrashRepository Crashes, BuggRepository Buggs) { return(new FRepository() { _Crashes = Crashes, _Buggs = Buggs }); }
/// <summary> /// /// </summary> /// <returns></returns> public EntitySet <Crash> GetCrashes() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { int CrashCount = Crashes.Count; if (NumberOfCrashes != CrashCount) { NumberOfCrashes = CrashCount; if (NumberOfCrashes > 0) { BuggRepository LocalBuggRepository = new BuggRepository(); LocalBuggRepository.UpdateBuggData(this, Crashes); } } // Just fill the CallStackContainers foreach (Crash CurrentCrash in Crashes) { if (CurrentCrash.CallStackContainer == null) { CurrentCrash.CallStackContainer = CurrentCrash.GetCallStack(); } if (SourceContext == null) { SourceContext = CurrentCrash.SourceContext; } } return(Crashes); } }
/// <summary> /// Return the top lines of a callstack. /// </summary> /// <param name="Max">The number of lines to return.</param> /// <returns>A list of callstack entries.</returns> public List <string> GetFunctionCalls(int Max) { BuggRepository LocalBuggRepository = new BuggRepository(); List <string> Results = LocalBuggRepository.GetFunctionCalls(Pattern); return(Results); }
/// <summary> /// /// </summary> /// <returns></returns> public EntitySet<Crash> GetCrashes() { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { int CrashCount = Crashes.Count; if( NumberOfCrashes != CrashCount ) { NumberOfCrashes = CrashCount; if( NumberOfCrashes > 0 ) { BuggRepository LocalBuggRepository = new BuggRepository(); LocalBuggRepository.UpdateBuggData( this, Crashes ); } } // Just fill the CallStackContainers foreach( Crash CurrentCrash in Crashes ) { if( CurrentCrash.CallStackContainer == null ) { CurrentCrash.CallStackContainer = CurrentCrash.GetCallStack(); } if( SourceContext == null ) { SourceContext = CurrentCrash.SourceContext; } } return Crashes; } }
/// <summary> /// Adds the bugg as a new JIRA ticket /// </summary> public void CopyToJira() { var jc = JiraConnection.Get(); Dictionary <string, object> issueFields; if (BranchesFoundIn.Any(data => data.ToLower().Contains("//fort"))) { issueFields = CreateFortniteIssue(jc); } //else if (BranchesFoundIn.Any(data => data.ToLower().Contains("//orion"))) //{ // issueFields = CreateOrionIssue(); //} else { issueFields = CreateGeneralIssue(jc); } if (jc.CanBeUsed() && string.IsNullOrEmpty(this.Jira)) { // Additional Info URL / Link to Crash/Bugg string Key = jc.AddJiraTicket(issueFields); if (!string.IsNullOrEmpty(Key)) { Jira = Key; var buggs = new BuggRepository(); buggs.SetJIRAForBuggAndCrashes(Key, Id); } } }
/// <summary> /// Accesses the instance. /// </summary> public static FRepository Get(BuggRepository Buggs) { return(new FRepository() { _Buggs = Buggs }); }
/// <summary> /// Return the top lines of a callstack. /// </summary> /// <returns>A list of callstack entries.</returns> public List <string> GetFunctionCalls() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(Id=" + this.Id + ")")) { BuggRepository LocalBuggRepository = new BuggRepository(); List <string> Results = LocalBuggRepository.GetFunctionCalls(Pattern); return(Results); } }
/// <summary> /// Adds the bugg as a new JIRA ticket /// </summary> public void CopyToJira() { var JC = JiraConnection.Get(); if (JC.CanBeUsed() && string.IsNullOrEmpty(this.TTPID)) { Dictionary <string, object> Fields = new Dictionary <string, object>(); Fields.Add("project", new Dictionary <string, object> { { "id", 11205 } }); // UE Fields.Add("summary", "[CrashReport] " + ToJiraSummary); // Call Stack, Line 1 Fields.Add("description", "____________"); // Description Fields.Add("issuetype", new Dictionary <string, object> { { "id", "1" } }); // Bug Fields.Add("labels", new string[] { "crash" }); // <label>crash</label> Fields.Add("customfield_11500", ToJiraFirstCLAffected); // Changelist # / Found Changelist Fields.Add("environment", LatestOSAffected); // Platform // Components var SupportComponent = JC.GetNameToComponents()["Support"]; Fields.Add("components", new object[] { SupportComponent }); // ToJiraVersions Fields.Add("versions", ToJiraVersions); // ToJiraBranches Fields.Add("customfield_11201", ToJiraBranches); // ToJiraPlatforms Fields.Add("customfield_11203", ToJiraPlatforms); // Callstack customfield_11807 var Callstack = GetFunctionCalls(); string JiraCallstack = string.Join("\r\n", Callstack); Fields.Add("customfield_11807", JiraCallstack); // Callstack string Key = JC.AddJiraTicket(Fields); if (!string.IsNullOrEmpty(Key)) { TTPID = Key; BuggRepository.SetJIRAForBuggAndCrashes(Key, Id); // Update the JIRA in the bugg. Dictionary <string, object> FieldsToUpdate = new Dictionary <string, object>(); string BuggLink = "http://crashreporter/Buggs/Show/" + Id; FieldsToUpdate.Add("customfield_11205", BuggLink); // Additional Info URL / Link to Crash/Bugg JC.UpdateJiraTicket(Key, FieldsToUpdate); } } }
/// <summary> /// Adds the bugg as a new JIRA ticket /// </summary> public void CopyToJira() { var JC = JiraConnection.Get(); if (JC.CanBeUsed() && string.IsNullOrEmpty(this.Jira)) { Dictionary <string, object> Fields = new Dictionary <string, object>(); Fields.Add("project", new Dictionary <string, object> { { "id", 11205 } }); // UE Fields.Add("summary", "[CrashReport] " + ToJiraSummary); // Call Stack, Line 1 Fields.Add("description", string.Join("\r\n", ToJiraDescriptions)); // Description Fields.Add("issuetype", new Dictionary <string, object> { { "id", "1" } }); // Bug Fields.Add("labels", new string[] { "crash", "liveissue" }); // <label>crash, live issue</label> Fields.Add("customfield_11500", ToJiraFirstCLAffected); // Changelist # / Found Changelist Fields.Add("environment", LatestOSAffected); // Platform // Components var SupportComponent = JC.GetNameToComponents()["Support"]; Fields.Add("components", new object[] { SupportComponent }); // ToJiraVersions Fields.Add("versions", ToJiraVersions); // ToJiraBranches Fields.Add("customfield_12402", ToJiraBranches.ToList()); // NewBranchFoundIn // ToJiraPlatforms Fields.Add("customfield_11203", ToJiraPlatforms); // Callstack customfield_11807 string JiraCallstack = "{noformat}" + string.Join("\r\n", ToJiraFunctionCalls) + "{noformat}"; Fields.Add("customfield_11807", JiraCallstack); // Callstack string BuggLink = "http://crashreporter/Buggs/Show/" + Id; Fields.Add("customfield_11205", BuggLink); // Additional Info URL / Link to Crash/Bugg string Key = JC.AddJiraTicket(Fields); if (!string.IsNullOrEmpty(Key)) { Jira = Key; BuggRepository Buggs = new BuggRepository(); Buggs.SetJIRAForBuggAndCrashes(Key, Id); } } }
/* /// <summary> /// The empty view of the buggs page. /// </summary> /// public ActionResult Index() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { BuggsViewModel Results = new BuggsViewModel(); Results.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Results ); } }*/ /// <summary> /// The Index action. /// </summary> /// <param name="BuggsForm">The form of user data passed up from the client.</param> /// <returns>The view to display a list of Buggs on the client.</returns> public ActionResult Index( FormCollection BuggsForm ) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { BuggRepository Buggs = new BuggRepository(); FormHelper FormData = new FormHelper( Request, BuggsForm, "CrashesInTimeFrameGroup" ); BuggsViewModel Results = Buggs.GetResults( FormData ); foreach (var Bugg in Results.Results) { // Populate function calls. Bugg.GetFunctionCalls(); } Results.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Results ); } }
/// <summary> /// Return a view model containing a sorted list of crashes based on the user input. /// </summary> /// <param name="FormData">The user input from the client.</param> /// <returns>A view model to use to display a list of crashes.</returns> /// <remarks>The filtering mechanism works by generating a fluent query to describe the set of crashes we wish to view. Basically, 'Select.Where().Where().Where().Where()' etc. /// The filtering is applied in the following order: /// 1. Get all crashes. /// 2. Filter between the start and end dates. /// 3. Apply the search query. /// 4. Filter by the branch. /// 5. Filter by the game name. /// 6. Filter by the type of reports to view. /// 7. Filter by the user group. /// 8. Sort the results by the sort term. /// 9. Take one page of results.</remarks> public CrashesViewModel GetResults(FormHelper FormData) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { UsersMapping UniqueUser = null; IEnumerable <Crash> Results = null; int Skip = (FormData.Page - 1) * FormData.PageSize; int Take = FormData.PageSize; var ResultsAll = ConstructQueryForFiltering(FormData); // Filter by data and get as enumerable. Results = FilterByDate(ResultsAll, FormData.DateFrom, FormData.DateTo); // Filter by BuggId if (!string.IsNullOrEmpty(FormData.BuggId)) { int BuggId = 0; bool bValid = int.TryParse(FormData.BuggId, out BuggId); if (bValid) { BuggRepository Buggs = new BuggRepository(); Bugg NewBugg = Buggs.GetBugg(BuggId); if (NewBugg != null) { List <Crash> Crashes = NewBugg.GetCrashes(); var NewResult = Results.Intersect(Crashes, new CrashComparer()); Results = NewResult; } } } // Get UserGroup ResultCounts Dictionary <string, int> GroupCounts = GetCountsByGroupFromCrashes(Results); // Filter by user group if present int UserGroupId; if (!string.IsNullOrEmpty(FormData.UserGroup)) { UserGroupId = FRepository.Get(this).FindOrAddGroup(FormData.UserGroup); } else { UserGroupId = 1; } HashSet <int> UserIdsForGroup = FRepository.Get(this).GetUserIdsFromUserGroupId(UserGroupId); using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.Results.Users")) { List <Crash> NewResults = new List <Crash>(); foreach (Crash Crash in Results) { if (UserIdsForGroup.Contains(Crash.UserNameId.Value)) { NewResults.Add(Crash); } } Results = NewResults; } // Pass in the results and return them sorted properly Results = GetSortedResults(Results, FormData.SortTerm, (FormData.SortOrder == "Descending")); // Get the Count for pagination int ResultCount = 0; using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.Results.Users")) { ResultCount = Results.Count(); } // Grab just the results we want to display on this page Results = Results.Skip(Skip).Take(Take); using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.GetResults.GetCallstacks")) { // Process call stack for display foreach (Crash CrashInstance in Results) { // Put callstacks into an list so we can access them line by line in the view CrashInstance.CallStackContainer = GetCallStack(CrashInstance); } } return(new CrashesViewModel { Results = Results, PagingInfo = new PagingInfo { CurrentPage = FormData.Page, PageSize = FormData.PageSize, TotalResults = ResultCount }, SortOrder = FormData.SortOrder, SortTerm = FormData.SortTerm, UserGroup = FormData.UserGroup, CrashType = FormData.CrashType, SearchQuery = FormData.SearchQuery, UsernameQuery = FormData.UsernameQuery, EpicIdOrMachineQuery = FormData.EpicIdOrMachineQuery, MessageQuery = FormData.MessageQuery, BuiltFromCL = FormData.BuiltFromCL, BuggId = FormData.BuggId, JiraQuery = FormData.JiraQuery, DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, BranchName = FormData.BranchName, VersionName = FormData.VersionName, PlatformName = FormData.PlatformName, GameName = FormData.GameName, GroupCounts = GroupCounts, RealUserName = UniqueUser != null?UniqueUser.ToString() : null, }); } }
/// <summary> /// Link the Http context cache to a Bugg repository. /// </summary> /// <param name="InCache">The current Http context cache.</param> /// <param name="InBuggRepository">The repository to associate the cache with.</param> public CachedDataService(Cache InCache, BuggRepository InBuggRepository) { CacheInstance = InCache; BuggRepositoryInstance = InBuggRepository; }
/// <summary> /// Return the top lines of a callstack. /// </summary> /// <returns>A list of callstack entries.</returns> public List<string> GetFunctionCalls() { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(Id=" + this.Id + ")" ) ) { BuggRepository LocalBuggRepository = new BuggRepository(); List<string> Results = LocalBuggRepository.GetFunctionCalls( Pattern ); return Results; } }
/// <summary> /// Accesses the instance. /// </summary> public static FRepository Get( BuggRepository Buggs ) { return new FRepository() { _Buggs = Buggs }; }
/// <summary> /// Accesses the instance. /// </summary> public static FRepository Get( CrashRepository Crashes, BuggRepository Buggs ) { return new FRepository() { _Crashes = Crashes, _Buggs = Buggs }; }
/// <summary> /// Link the Http context cache to a Bugg repository. /// </summary> /// <param name="InCache">The current Http context cache.</param> /// <param name="InBuggRepository">The repository to associate the cache with.</param> public CachedDataService( Cache InCache, BuggRepository InBuggRepository ) { CacheInstance = InCache; Buggs = InBuggRepository; }
//static readonly int MinNumberOfCrashes = 2; /// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <returns>A view to display the filtered Buggs.</returns> public CSV_ViewModel GetResults( FormHelper FormData ) { BuggRepository BuggsRepo = new BuggRepository(); CrashRepository CrashRepo = new CrashRepository(); using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { string AnonymousGroup = "Anonymous"; //List<String> Users = FRepository.Get().GetUserNamesFromGroupName( AnonumousGroup ); int AnonymousGroupID = FRepository.Get( BuggsRepo ).FindOrAddGroup( AnonymousGroup ); HashSet<int> AnonumousIDs = FRepository.Get( BuggsRepo ).GetUserIdsFromUserGroup( AnonymousGroup ); int AnonymousID = AnonumousIDs.First(); HashSet<string> UserNamesForUserGroup = FRepository.Get( BuggsRepo ).GetUserNamesFromGroupName( AnonymousGroup ); // Enable to narrow results and improve debugging performance. //FormData.DateFrom = FormData.DateTo.AddDays( -1 ); FormData.DateTo = FormData.DateTo.AddDays( 1 ); var FilteringQueryJoin = CrashRepo .ListAll() .Where( c => c.EpicAccountId != "" ) // Only crashes and asserts .Where( c => c.CrashType == 1 || c.CrashType == 2 ) // Only anonymous user .Where( c => c.UserNameId == AnonymousID ) // Filter be date .Where( c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo ) .Join ( CrashRepo.Context.Buggs_Crashes, c => c, bc => bc.Crash, ( c, bc ) => new { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash.Value, BuiltFromCL = c.BuiltFromCL, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.MachineId, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType.Value, EpicId = c.EpicAccountId, BuggId = bc.BuggId, } ); var FilteringQueryCrashes = CrashRepo .ListAll() .Where( c => c.EpicAccountId != "" ) // Only crashes and asserts .Where( c => c.CrashType == 1 || c.CrashType == 2 ) // Only anonymous user .Where( c => c.UserNameId == AnonymousID ); int TotalCrashes = CrashRepo .ListAll() .Count(); int TotalCrashesYearToDate = CrashRepo .ListAll() // Year to date .Where( c => c.TimeOfCrash > new DateTime( DateTime.UtcNow.Year, 1, 1 ) ) .Count(); var CrashesFilteredWithDateQuery = FilteringQueryCrashes // Filter be date .Where( c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo ); int CrashesFilteredWithDate = CrashesFilteredWithDateQuery .Count(); int CrashesYearToDateFiltered = FilteringQueryCrashes // Year to date .Where( c => c.TimeOfCrash > new DateTime( DateTime.UtcNow.Year, 1, 1 ) ) .Count(); int AffectedUsersFiltered = FilteringQueryCrashes .Select( c => c.EpicAccountId ) .Distinct() .Count(); int UniqueCrashesFiltered = FilteringQueryCrashes .Select( c => c.Pattern ) .Distinct() .Count(); int NumCrashes = FilteringQueryJoin.Count(); // Get all users // SLOW //var Users = FRepository.Get( BuggsRepo ).GetAnalyticsUsers().ToDictionary( X => X.EpicAccountId, Y => Y ); // Export data to the file. string CSVPathname = Path.Combine( Settings.Default.CrashReporterCSV, DateTime.UtcNow.ToString( "yyyy-MM-dd.HH-mm-ss" ) ); CSVPathname += string .Format( "__[{0}---{1}]__{2}", FormData.DateFrom.ToString( "yyyy-MM-dd" ), FormData.DateTo.ToString( "yyyy-MM-dd" ), NumCrashes ) + ".csv"; string ServerPath = Server.MapPath( CSVPathname ); var CSVFile = new StreamWriter( ServerPath, true, Encoding.UTF8 ); using (FAutoScopedLogTimer ExportToCSVTimer = new FAutoScopedLogTimer( "ExportToCSV" )) { var RowType = FilteringQueryJoin.FirstOrDefault().GetType(); string AllProperties = ""; foreach( var Property in RowType.GetProperties() ) { AllProperties += Property.Name; AllProperties += "; "; } // Write header CSVFile.WriteLine( AllProperties ); foreach (var Row in FilteringQueryJoin) { var BVParts = Row.BuildVersion.Split( new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries ); if (BVParts.Length > 2 && BVParts[0] != "0") { string CleanEngineVersion = string.Format( "{0}.{1}.{2}", BVParts[0], BVParts[1], BVParts[2] ); string[] RowProperties = new string[] { Row.GameName, Row.TimeOfCrash.ToString(), Row.BuiltFromCL, Row.PlatformName, Row.EngineMode, Row.MachineId, Row.Module, CleanEngineVersion, Row.Jira, Row.Branch, Row.CrashType == 1 ? "Crash" : "Assert", Row.EpicId, Row.BuggId.ToString() }; string JoinedLine = string.Join( "; ", RowProperties ); JoinedLine += "; "; CSVFile.WriteLine( JoinedLine ); } } CSVFile.Flush(); CSVFile.Close(); CSVFile = null; } List<FCSVRow> CSVRows = FilteringQueryJoin .OrderByDescending( X => X.TimeOfCrash ) .Take( 32 ) .Select( c => new FCSVRow { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash, BuiltFromCL = c.BuiltFromCL, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.MachineId, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType, EpicId = c.EpicId, BuggId = c.BuggId, } ) .ToList(); return new CSV_ViewModel { CSVRows = CSVRows, CSVPathname = CSVPathname, DateFrom = (long)( FormData.DateFrom - CrashesViewModel.Epoch ).TotalMilliseconds, DateTo = (long)( FormData.DateTo - CrashesViewModel.Epoch ).TotalMilliseconds, DateTimeFrom = FormData.DateFrom, DateTimeTo = FormData.DateTo, AffectedUsersFiltered = AffectedUsersFiltered, UniqueCrashesFiltered = UniqueCrashesFiltered, CrashesFilteredWithDate = CrashesFilteredWithDate, TotalCrashes = TotalCrashes, TotalCrashesYearToDate = TotalCrashesYearToDate, }; } }
/// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <param name="BuggIDToBeAddedToJira">ID of the bugg that will be added to JIRA</param> /// <returns>A view to display the filtered Buggs.</returns> public ReportsViewModel GetResults( FormHelper FormData, int BuggIDToBeAddedToJira ) { BuggRepository BuggsRepo = new BuggRepository(); CrashRepository CrashRepo = new CrashRepository(); // @TODO yrx 2015-02-17 BuggIDToBeAddedToJira replace with List<int> based on check box and Submit? // It would be great to have a CSV export of this as well with buggs ID being the key I can then use to join them :) // // Enumerate JIRA projects if needed. // https://jira.ol.epicgames.net//rest/api/2/project var JC = JiraConnection.Get(); var JiraComponents = JC.GetNameToComponents(); var JiraVersions = JC.GetNameToVersions(); using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { string AnonumousGroup = "Anonymous"; //List<String> Users = FRepository.Get().GetUserNamesFromGroupName( AnonumousGroup ); int AnonymousGroupID = FRepository.Get( BuggsRepo ).FindOrAddGroup( AnonumousGroup ); HashSet<int> AnonumousIDs = FRepository.Get( BuggsRepo ).GetUserIdsFromUserGroup( AnonumousGroup ); int AnonymousID = AnonumousIDs.First(); var Crashes = CrashRepo .FilterByDate( CrashRepo.ListAll(), FormData.DateFrom, FormData.DateTo ) // Only crashes and asserts .Where( Crash => Crash.CrashType == 1 || Crash.CrashType == 2 ) // Only anonymous user .Where( Crash => Crash.UserNameId.Value == AnonymousID ) .Select( Crash => new { ID = Crash.Id, TimeOfCrash = Crash.TimeOfCrash.Value, //UserID = Crash.UserNameId.Value, BuildVersion = Crash.BuildVersion, JIRA = Crash.Jira, Platform = Crash.PlatformName, FixCL = Crash.FixedChangeList, BuiltFromCL = Crash.BuiltFromCL, Pattern = Crash.Pattern, MachineID = Crash.MachineId, Branch = Crash.Branch, Description = Crash.Description, RawCallStack = Crash.RawCallStack, } ) .ToList(); int NumCrashes = Crashes.Count; /* // Build patterns for crashes where patters is null. var CrashesWithoutPattern = FRepository.Get().Crashes .FilterByDate( FRepository.Get().Crashes.ListAll(), FormData.DateFrom, FormData.DateTo.AddDays( 1 ) ) // Only crashes and asserts .Where( Crash => Crash.Pattern == null || Crash.Pattern == "" ) .Select( Crash => Crash ) .ToList(); foreach( var Crash in CrashesWithoutPattern ) { Crash.BuildPattern(); } */ // Total # of ALL (Anonymous) crashes in timeframe int TotalAnonymousCrashes = NumCrashes; // Total # of UNIQUE (Anonymous) crashes in timeframe HashSet<string> UniquePatterns = new HashSet<string>(); HashSet<string> UniqueMachines = new HashSet<string>(); Dictionary<string, int> PatternToCount = new Dictionary<string, int>(); //List<int> CrashesWithoutPattern = new List<int>(); //List<DateTime> CrashesWithoutPatternDT = new List<DateTime>(); foreach( var Crash in Crashes ) { if( string.IsNullOrEmpty( Crash.Pattern ) ) { //CrashesWithoutPattern.Add( Crash.ID ); //CrashesWithoutPatternDT.Add( Crash.TimeOfCrash ); continue; } UniquePatterns.Add( Crash.Pattern ); UniqueMachines.Add( Crash.MachineID ); bool bAdd = !PatternToCount.ContainsKey( Crash.Pattern ); if( bAdd ) { PatternToCount.Add( Crash.Pattern, 1 ); } else { PatternToCount[Crash.Pattern]++; } } var PatternToCountOrdered = PatternToCount.OrderByDescending( X => X.Value ).ToList(); const int NumTopRecords = 200; var PatternAndCount = PatternToCountOrdered.Take( NumTopRecords ).ToDictionary( X => X.Key, Y => Y.Value ); int TotalUniqueAnonymousCrashes = UniquePatterns.Count; // Total # of AFFECTED USERS (Anonymous) in timeframe int TotalAffectedUsers = UniqueMachines.Count; var RealBuggs = BuggsRepo.Context.Buggs.Where( Bugg => PatternAndCount.Keys.Contains( Bugg.Pattern ) ).ToList(); // Build search string. HashSet<string> FoundJiras = new HashSet<string>(); Dictionary<string, List<Bugg>> JiraIDtoBugg = new Dictionary<string, List<Bugg>>(); List<Bugg> Buggs = new List<Bugg>( NumTopRecords ); foreach( var Top in PatternAndCount ) { Bugg NewBugg = RealBuggs.Where( X => X.Pattern == Top.Key ).FirstOrDefault(); if( NewBugg != null ) { using( FAutoScopedLogTimer TopTimer = new FAutoScopedLogTimer( string.Format( "{0}:{1}", Buggs.Count + 1, NewBugg.Id ) ) ) { var CrashesForBugg = Crashes.Where( Crash => Crash.Pattern == Top.Key ).ToList(); // Convert anonymous to full type; var FullCrashesForBugg = new List<Crash>( CrashesForBugg.Count ); foreach( var Anon in CrashesForBugg ) { FullCrashesForBugg.Add( new Crash() { ID = Anon.ID, TimeOfCrash = Anon.TimeOfCrash, BuildVersion = Anon.BuildVersion, Jira = Anon.JIRA, Platform = Anon.Platform, FixCL = Anon.FixCL, BuiltFromCL = Anon.BuiltFromCL, Pattern = Anon.Pattern, MachineId = Anon.MachineID, Branch = Anon.Branch, Description = Anon.Description, RawCallStack = Anon.RawCallStack, } ); } NewBugg.PrepareBuggForJira( FullCrashesForBugg ); // Verify valid JiraID, this may be still a TTP if( !string.IsNullOrEmpty( NewBugg.Jira ) ) { int TTPID = 0; int.TryParse( NewBugg.Jira, out TTPID ); if( TTPID == 0 ) { AddBuggJiraMapping(NewBugg, ref FoundJiras, ref JiraIDtoBugg); } } Buggs.Add( NewBugg ); } } else { FLogger.Global.WriteEvent( "Bugg for pattern " + Top.Key + " not found" ); } } if( BuggIDToBeAddedToJira > 0 ) { var Bugg = Buggs.Where( X => X.Id == BuggIDToBeAddedToJira ).FirstOrDefault(); if( Bugg != null ) { Bugg.CopyToJira(); AddBuggJiraMapping( Bugg, ref FoundJiras, ref JiraIDtoBugg ); } } if( JC.CanBeUsed() ) { var BuggsCopy = new List<Bugg>( Buggs ); HashSet<string> InvalidJiras = new HashSet<string>(); // Simple verification of JIRA foreach (var Value in FoundJiras) { if( Value.Length < 3 || !Value.Contains('-') ) { InvalidJiras.Add(Value); } } foreach (var InvalidJira in InvalidJiras) { FoundJiras.Remove( InvalidJira ); } // Grab the data form JIRA. string JiraSearchQuery = string.Join( " OR ", FoundJiras ); using( FAutoScopedLogTimer JiraResultsTimer = new FAutoScopedLogTimer( "JiraResults" ) ) { var JiraResults = new Dictionary<string, Dictionary<string, object>>(); try { JiraResults = JC.SearchJiraTickets( JiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string } ); } catch (System.Exception) { } // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist foreach( var Jira in JiraResults ) { string JiraID = Jira.Key; string Summary = (string)Jira.Value["summary"]; string ComponentsText = ""; System.Collections.ArrayList Components = (System.Collections.ArrayList)Jira.Value["components"]; foreach( Dictionary<string, object> Component in Components ) { ComponentsText += (string)Component["name"]; ComponentsText += " "; } Dictionary<string, object> ResolutionFields = (Dictionary<string, object>)Jira.Value["resolution"]; string Resolution = ResolutionFields != null ? (string)ResolutionFields["name"] : ""; string FixVersionsText = ""; System.Collections.ArrayList FixVersions = (System.Collections.ArrayList)Jira.Value["fixVersions"]; foreach( Dictionary<string, object> FixVersion in FixVersions ) { FixVersionsText += (string)FixVersion["name"]; FixVersionsText += " "; } int FixCL = Jira.Value["customfield_11200"] != null ? (int)(decimal)Jira.Value["customfield_11200"] : 0; List<Bugg> BuggsForJira; JiraIDtoBugg.TryGetValue( JiraID, out BuggsForJira ); //var BuggsForJira = JiraIDtoBugg[JiraID]; if( BuggsForJira != null ) { foreach( Bugg Bugg in BuggsForJira ) { Bugg.JiraSummary = Summary; Bugg.JiraComponentsText = ComponentsText; Bugg.JiraResolution = Resolution; Bugg.JiraFixVersionsText = FixVersionsText; if( FixCL != 0 ) { Bugg.JiraFixCL = FixCL.ToString(); } BuggsCopy.Remove( Bugg ); } } } } // If there are buggs, we need to update the summary to indicate an error. // Usually caused when bugg's project has changed. foreach( var Bugg in BuggsCopy.Where( b => !string.IsNullOrEmpty( b.Jira ) ) ) { Bugg.JiraSummary = "JIRA MISMATCH"; Bugg.JiraComponentsText = "JIRA MISMATCH"; Bugg.JiraResolution = "JIRA MISMATCH"; Bugg.JiraFixVersionsText = "JIRA MISMATCH"; Bugg.JiraFixCL = "JIRA MISMATCH"; } } Buggs = Buggs.OrderByDescending( b => b.CrashesInTimeFrameGroup ).ToList(); return new ReportsViewModel { Buggs = Buggs, /*Crashes = Crashes,*/ DateFrom = (long)( FormData.DateFrom - CrashesViewModel.Epoch ).TotalMilliseconds, DateTo = (long)( FormData.DateTo - CrashesViewModel.Epoch ).TotalMilliseconds, TotalAffectedUsers = TotalAffectedUsers, TotalAnonymousCrashes = TotalAnonymousCrashes, TotalUniqueAnonymousCrashes = TotalUniqueAnonymousCrashes }; } }
/// <summary> /// Link the Http context cache to a Bugg repository. /// </summary> /// <param name="inCache">The current Http context cache.</param> /// <param name="inBuggRepository">The repository to associate the cache with.</param> public CachedDataService(Cache inCache, BuggRepository inBuggRepository) { CacheInstance = inCache; Buggs = inBuggRepository; }
/// <summary> /// Adds the bugg as a new JIRA ticket /// </summary> public void CopyToJira() { var JC = JiraConnection.Get(); if( JC.CanBeUsed() && string.IsNullOrEmpty( this.Jira ) ) { Dictionary<string, object> Fields = new Dictionary<string, object>(); Fields.Add( "project", new Dictionary<string, object> { { "id", 11205 } } ); // UE Fields.Add( "summary", "[CrashReport] " + ToJiraSummary ); // Call Stack, Line 1 Fields.Add( "description", string.Join( "\r\n", ToJiraDescriptions ) ); // Description Fields.Add( "issuetype", new Dictionary<string, object> { { "id", "1" } } ); // Bug Fields.Add( "labels", new string[] { "crash", "liveissue" } ); // <label>crash, live issue</label> Fields.Add( "customfield_11500", ToJiraFirstCLAffected ); // Changelist # / Found Changelist Fields.Add( "environment", LatestOSAffected ); // Platform // Components var SupportComponent = JC.GetNameToComponents()["Support"]; Fields.Add( "components", new object[] { SupportComponent } ); // ToJiraVersions Fields.Add( "versions", ToJiraVersions ); // ToJiraBranches Fields.Add( "customfield_11201", ToJiraBranches ); // ToJiraPlatforms Fields.Add( "customfield_11203", ToJiraPlatforms ); // Callstack customfield_11807 string JiraCallstack = string.Join( "\r\n", ToJiraFunctionCalls ); Fields.Add( "customfield_11807", JiraCallstack ); // Callstack string BuggLink = "http://crashreporter/Buggs/Show/" + Id; Fields.Add( "customfield_11205", BuggLink ); // Additional Info URL / Link to Crash/Bugg string Key = JC.AddJiraTicket( Fields ); if( !string.IsNullOrEmpty( Key ) ) { Jira = Key; BuggRepository Buggs = new BuggRepository(); Buggs.SetJIRAForBuggAndCrashes( Key, Id ); } } }
/// <summary> /// Return the top lines of a callstack. /// </summary> /// <param name="Max">The number of lines to return.</param> /// <returns>A list of callstack entries.</returns> public List<string> GetFunctionCalls( int Max ) { BuggRepository LocalBuggRepository = new BuggRepository(); List<string> Results = LocalBuggRepository.GetFunctionCalls( Pattern ); return Results; }
/// <summary> /// The Show action. /// </summary> /// <param name="BuggsForm">The form of user data passed up from the client.</param> /// <param name="id">The unique id of the Bugg.</param> /// <returns>The view to display a Bugg on the client.</returns> public ActionResult Show( FormCollection BuggsForm, int id ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(BuggId=" + id + ")", bCreateNewLog: true ) ) { // Handle 'CopyToJira' button int BuggIDToBeAddedToJira = 0; foreach( var Entry in BuggsForm ) { if( Entry.ToString().Contains( Bugg.JiraSubmitName ) ) { int.TryParse( Entry.ToString().Substring( Bugg.JiraSubmitName.Length ), out BuggIDToBeAddedToJira ); break; } } BuggRepository Buggs = new BuggRepository(); // Set the display properties based on the radio buttons bool DisplayModuleNames = false; if( BuggsForm["DisplayModuleNames"] == "true" ) { DisplayModuleNames = true; } bool DisplayFunctionNames = false; if( BuggsForm["DisplayFunctionNames"] == "true" ) { DisplayFunctionNames = true; } bool DisplayFileNames = false; if( BuggsForm["DisplayFileNames"] == "true" ) { DisplayFileNames = true; } bool DisplayFilePathNames = false; if( BuggsForm["DisplayFilePathNames"] == "true" ) { DisplayFilePathNames = true; DisplayFileNames = false; } bool DisplayUnformattedCallStack = false; if( BuggsForm["DisplayUnformattedCallStack"] == "true" ) { DisplayUnformattedCallStack = true; } // Create a new view and populate with crashes List<Crash> Crashes = null; BuggViewModel Model = new BuggViewModel(); Bugg NewBugg = Buggs.GetBugg( id ); if( NewBugg == null ) { return RedirectToAction( "" ); } Crashes = NewBugg.GetCrashes(); using (FAutoScopedLogTimer GetCrashesTimer = new FAutoScopedLogTimer( "Bugg.PrepareBuggForJira" )) { if (Crashes.Count > 0) { NewBugg.PrepareBuggForJira( Crashes ); if (BuggIDToBeAddedToJira != 0) { NewBugg.CopyToJira(); } } } using( FAutoScopedLogTimer JiraResultsTimer = new FAutoScopedLogTimer( "Bugg.GrabJira" ) ) { var JC = JiraConnection.Get(); bool bValidJira = false; // Verify valid JiraID, this may be still a TTP if( !string.IsNullOrEmpty( NewBugg.Jira ) ) { int TTPID = 0; int.TryParse( NewBugg.Jira, out TTPID ); if( TTPID == 0 ) { //AddBuggJiraMapping( NewBugg, ref FoundJiras, ref JiraIDtoBugg ); bValidJira = true; } } if( JC.CanBeUsed() && bValidJira ) { // Grab the data form JIRA. string JiraSearchQuery = "key = " + NewBugg.Jira; var JiraResults = JC.SearchJiraTickets( JiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string } ); // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist foreach( var Jira in JiraResults ) { string JiraID = Jira.Key; string Summary = (string)Jira.Value["summary"]; string ComponentsText = ""; System.Collections.ArrayList Components = (System.Collections.ArrayList)Jira.Value["components"]; foreach( Dictionary<string, object> Component in Components ) { ComponentsText += (string)Component["name"]; ComponentsText += " "; } Dictionary<string, object> ResolutionFields = (Dictionary<string, object>)Jira.Value["resolution"]; string Resolution = ResolutionFields != null ? (string)ResolutionFields["name"] : ""; string FixVersionsText = ""; System.Collections.ArrayList FixVersions = (System.Collections.ArrayList)Jira.Value["fixVersions"]; foreach( Dictionary<string, object> FixVersion in FixVersions ) { FixVersionsText += (string)FixVersion["name"]; FixVersionsText += " "; } int FixCL = Jira.Value["customfield_11200"] != null ? (int)(decimal)Jira.Value["customfield_11200"] : 0; NewBugg.JiraSummary = Summary; NewBugg.JiraComponentsText = ComponentsText; NewBugg.JiraResolution = Resolution; NewBugg.JiraFixVersionsText = FixVersionsText; if( FixCL != 0 ) { NewBugg.JiraFixCL = FixCL.ToString(); } break; } } } // Apply any user settings if( BuggsForm.Count > 0 ) { if( !string.IsNullOrEmpty( BuggsForm["SetStatus"] ) ) { NewBugg.Status = BuggsForm["SetStatus"]; Buggs.SetBuggStatus( NewBugg.Status, id ); } if( !string.IsNullOrEmpty( BuggsForm["SetFixedIn"] ) ) { NewBugg.FixedChangeList = BuggsForm["SetFixedIn"]; Buggs.SetBuggFixedChangeList( NewBugg.FixedChangeList, id ); } if( !string.IsNullOrEmpty( BuggsForm["SetTTP"] ) ) { NewBugg.Jira = BuggsForm["SetTTP"]; Buggs.SetJIRAForBuggAndCrashes( NewBugg.Jira, id ); } // <STATUS> } // Set up the view model with the crash data Model.Bugg = NewBugg; Model.Crashes = Crashes; Crash NewCrash = Model.Crashes.FirstOrDefault(); if( NewCrash != null ) { using( FScopedLogTimer LogTimer2 = new FScopedLogTimer( "CallstackTrimming" ) ) { CallStackContainer CallStack = new CallStackContainer( NewCrash ); // Set callstack properties CallStack.bDisplayModuleNames = DisplayModuleNames; CallStack.bDisplayFunctionNames = DisplayFunctionNames; CallStack.bDisplayFileNames = DisplayFileNames; CallStack.bDisplayFilePathNames = DisplayFilePathNames; CallStack.bDisplayUnformattedCallStack = DisplayUnformattedCallStack; Model.CallStack = CallStack; // Shorten very long function names. foreach( CallStackEntry Entry in Model.CallStack.CallStackEntries ) { Entry.FunctionName = Entry.GetTrimmedFunctionName( 128 ); } Model.SourceContext = NewCrash.SourceContext; } Model.Bugg.LatestCrashSummary = NewCrash.Summary; } Model.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Show", Model ); } }
/// <summary> /// Adds the bugg as a new JIRA ticket /// </summary> public void CopyToJira() { var jc = JiraConnection.Get(); Dictionary<string, object> issueFields; if (BranchesFoundIn.Any(data => data.ToLower().Contains("//fort"))) { issueFields = CreateFortniteIssue(jc); } //else if (BranchesFoundIn.Any(data => data.ToLower().Contains("//orion"))) //{ // issueFields = CreateOrionIssue(); //} else { issueFields = CreateGeneralIssue(jc); } if(jc.CanBeUsed() && string.IsNullOrEmpty( this.Jira )) { // Additional Info URL / Link to Crash/Bugg string Key = jc.AddJiraTicket(issueFields); if( !string.IsNullOrEmpty( Key ) ) { Jira = Key; var buggs = new BuggRepository(); buggs.SetJIRAForBuggAndCrashes( Key, Id ); } } }
/// <summary> /// /// </summary> /// <returns></returns> public EntitySet<Crash> GetCrashes() { if( BuggCrashes == null ) { BuggCrashes = Crashes; } int CrashCount = BuggCrashes.Count; if( NumberOfCrashes != CrashCount ) { NumberOfCrashes = CrashCount; if( NumberOfCrashes > 0 ) { BuggRepository LocalBuggRepository = new BuggRepository(); LocalBuggRepository.UpdateBuggData( this, BuggCrashes ); } } // Just fill the CallStackContainers foreach( Crash CurrentCrash in BuggCrashes ) { if( CurrentCrash.CallStackContainer == null ) { CurrentCrash.CallStackContainer = CurrentCrash.GetCallStack(); } if( SourceContext == null ) { SourceContext = CurrentCrash.SourceContext; } } return BuggCrashes; }