private void ProcessLine(string logFileLine, ref int nestingLevel, Result result) { var codeFlow = result.CodeFlows[0]; const int STEP = 0; const int URI = 1; const int LINE = 2; // const int IMPORTANCE = 3; This value not persisted to SARIF const int STATE = 4; const int KIND1 = 5; // When KIND1 == "Atomic" the 6th slot is the // the remainder of the kind id, e.g., Atomic Assigment const int KIND2 = 6; // When KIND1 == "Call" the 6th and 7th slots are: const int CALLER = 6; const int CALLEE = 7; int step; string[] tokens = logFileLine.Split(' '); if (int.TryParse(tokens[STEP], out step)) { // If we find a numeric value as the first token, // this is a general step. Uri uri = null; string uriText = tokens[URI].Trim('"'); if (!uriText.Equals("?", StringComparison.Ordinal)) { if (File.Exists(uriText)) { uriText = Path.GetFullPath(uriText); } uri = new Uri(uriText, UriKind.RelativeOrAbsolute); } // We assume a valid line here. This code will throw if not. int line = int.Parse(tokens[LINE]); string sdvKind = tokens[KIND1]; if (sdvKind.Equals("Atomic", StringComparison.Ordinal)) { // For multipart SDV kinds 'Atomic XXX', we // map using the second value only, e.g, // 'Assignment' or 'Conditional' sdvKind = tokens[KIND2]; } sdvKind = sdvKind.Trim(); var threadFlowLocation = new ThreadFlowLocation { Importance = ThreadFlowLocationImportance.Unimportant, Location = new Location { Message = new Message() } }; if (uri != null) { threadFlowLocation.Location.PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = uri }, Region = new Region { StartLine = line } }; } if (sdvKind == "Call") { string extraMsg = $"{tokens[KIND1]} {tokens[CALLER]} {tokens[CALLEE]}"; string caller, callee; if (ExtractCallerAndCallee(extraMsg.Trim(), out caller, out callee)) { threadFlowLocation.Location.FullyQualifiedLogicalName = caller; threadFlowLocation.Location.Message.Text = callee; threadFlowLocation.SetProperty("target", callee); _callers.Push(caller); } else { Debug.Assert(false); } threadFlowLocation.NestingLevel = nestingLevel++; if (uri == null) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Unimportant; } else if (IsHarnessOrRulesFiles(uriText)) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Important; } else { threadFlowLocation.Importance = ThreadFlowLocationImportance.Essential; } } else if (sdvKind == "Return") { Debug.Assert(_callers.Count > 0); threadFlowLocation.NestingLevel = nestingLevel--; threadFlowLocation.Location.FullyQualifiedLogicalName = _callers.Pop(); } else { threadFlowLocation.NestingLevel = nestingLevel; threadFlowLocation.Location.Message.Text = sdvKind; } string separatorText = "^====Auto====="; string state = tokens[STATE]; string[] stateTokens = state.Split(new string[] { separatorText }, StringSplitOptions.RemoveEmptyEntries); if (stateTokens.Length > 0) { if (stateTokens.Length == 2) { threadFlowLocation.SetProperty("currentDataValues", stateTokens[0]); threadFlowLocation.SetProperty("permanentDataValues", stateTokens[1]); } else { Debug.Assert(stateTokens.Length == 1); if (stateTokens[0].StartsWith(separatorText)) { threadFlowLocation.SetProperty("permanentDataValues", stateTokens[0]); } else { threadFlowLocation.SetProperty("currentDataValues", stateTokens[0]); } } } codeFlow.ThreadFlows[0].Locations.Add(threadFlowLocation); } else { // This is the defect message. const int LEVEL = 0; string levelText = tokens[LEVEL]; result.Level = ConvertToFailureLevel(levelText); // Everything on the line following defect level comprises the message result.Message = new Message { Text = logFileLine.Substring(levelText.Length).Trim() }; // SDV currently produces 'pass' notifications when // the final line is prefixed with 'Error'. We'll examine // the message text to detect this condition if (result.Message.Text.Contains("is satisfied")) { result.Level = FailureLevel.None; result.Kind = ResultKind.Pass; } // Finally, populate this result location with the // last observed location in the code flow. IList <ThreadFlowLocation> locations = result.CodeFlows[0].ThreadFlows[0].Locations; for (int i = locations.Count - 1; i >= 0; --i) { if (locations[i].Location?.PhysicalLocation != null) { result.Locations.Add(new Location { PhysicalLocation = locations[i].Location.PhysicalLocation }); break; } } } }
private void GenerateCodeFlows(Defect defect, Result result) { List <SFA> sfas = defect?.Path?.SFAs; if (sfas == null || sfas.Count == 0) { return; } int step = 0; var locations = new List <ThreadFlowLocation>(); bool pathUsesKeyEvents = defect.Path.SFAs.Any(x => !string.IsNullOrWhiteSpace(x?.KeyEvent?.Id)); foreach (var sfa in defect.Path.SFAs) { var region = new Region() { StartColumn = sfa.Column + 1, StartLine = sfa.Line }; var uri = new Uri($"{sfa.FilePath}{sfa.FileName}", UriKind.Relative); var fileLocation = new PhysicalLocation(id: 0, fileLocation: new FileLocation(uri: uri, uriBaseId: null), region: region, contextRegion: null); var threadFlowLocation = new ThreadFlowLocation { Location = new Location { PhysicalLocation = fileLocation }, Step = ++step }; if (pathUsesKeyEvents) { if (string.IsNullOrWhiteSpace(sfa.KeyEvent?.Id)) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Unimportant; } else { threadFlowLocation.SetProperty("keyEventId", sfa.KeyEvent.Id); if (Enum.TryParse(sfa.KeyEvent.Importance, true, out ThreadFlowLocationImportance importance)) { threadFlowLocation.Importance = importance; } if (!string.IsNullOrWhiteSpace(sfa.KeyEvent.Message) && threadFlowLocation.Location?.Message != null) { threadFlowLocation.Location.Message.Text = sfa.KeyEvent.Message; } } } locations.Add(threadFlowLocation); } result.CodeFlows = new List <CodeFlow>() { SarifUtilities.CreateSingleThreadedCodeFlow(locations) }; }