From b0a8c70321672ec8348f37cc281c7640ec6056a9 Mon Sep 17 00:00:00 2001 From: Jan Wichelmann Date: Sat, 21 May 2022 00:53:23 +0200 Subject: [PATCH] Include call stack in SARIF report --- Tools/CiReportGenerator/CallStackData.cs | 76 +++++++++++++++++++++--- Tools/CiReportGenerator/SarifReport.cs | 17 +++++- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Tools/CiReportGenerator/CallStackData.cs b/Tools/CiReportGenerator/CallStackData.cs index 16032fd..b43e3d4 100644 --- a/Tools/CiReportGenerator/CallStackData.cs +++ b/Tools/CiReportGenerator/CallStackData.cs @@ -103,7 +103,7 @@ public SarifReport ProduceSarifReport(Dictionary<(string imageName, uint instruc }; // Generate results - report.Runs[0].Results = new List(CallStack.SelectMany(c => c.ProduceSarifReportEntries("", statements))); + report.Runs[0].Results = new List(CallStack.SelectMany(c => c.ProduceSarifReportEntries("", new Stack<(string fileName, int lineNumber, int columnNumber)?>(), statements))); return report; } @@ -180,7 +180,7 @@ public IEnumerable ProduceCodeQualityReportEntries(strin yield return codeQualityReportEntry; } - public IEnumerable ProduceSarifReportEntries(string formattedCallStack, Dictionary<(string imageName, uint instructionOffset), (string fileName, int lineNumber, int columnNumber)> statements) + public IEnumerable ProduceSarifReportEntries(string formattedCallStack, Stack<(string fileName, int lineNumber, int columnNumber)?> callStack, Dictionary<(string imageName, uint instructionOffset), (string fileName, int lineNumber, int columnNumber)> statements) { formattedCallStack += $" {SourceInstructionFormatted} -> {TargetInstructionFormatted}\n"; @@ -190,17 +190,17 @@ public IEnumerable ProduceSarifReportEntries(string formatted // Find corresponding statement // We may have to look at earlier instructions, if a statement spans more than one (string fileName, int lineNumber, int columnNumber) statementInfo = ("", 0, 0); - bool found = false; + bool statementFound = false; for(uint i = 0; i < 4096; ++i) { if(statements.TryGetValue((leakageEntry.ImageName, leakageEntry.Offset - i), out statementInfo)) { - found = true; + statementFound = true; break; } } - if(!found) + if(!statementFound) { Console.WriteLine($"Warning: Could not find instruction info for leakage entry {leakageEntry.ImageName}+{leakageEntry.Offset:x} ({leakageEntry.Type})\n{formattedCallStack}"); continue; @@ -243,22 +243,80 @@ public IEnumerable ProduceSarifReportEntries(string formatted EndColumn = statementInfo.columnNumber } }, - Message = null + Message = new SarifReportMessage { Text = "Code line associated with the leakage" } } }, PartialFingerprints = new Dictionary { ["primaryLocationLineHash"] = $"{CallStackId}-{leakageEntry.ImageName}-{leakageEntry.Offset:x}" // TODO This syntax seems to be invalid - } - // TODO codeFlows[].threadFlows[].locations[] for call stack + }, + CodeFlows = new List + { + new() + { + ThreadFlows = new List + { + new() + { + Locations = callStack + .Select((callStackElement, callStackIndex) => (callStackElement, callStackIndex)) + .Where(callStackData => callStackData.callStackElement != null) + .Select(callStackData => new SarifReportLocation + { + PhysicalLocation = new SarifReportPhysicalLocation + { + ArtifactLocation = new SarifReportPhysicalLocationArtifactLocation + { + Uri = callStackData.callStackElement!.Value.fileName + }, + Region = new SarifReportPhysicalLocationRegion + { + StartLine = callStackData.callStackElement!.Value.lineNumber, + StartColumn = callStackData.callStackElement!.Value.columnNumber, + EndLine = callStackData.callStackElement!.Value.lineNumber, + EndColumn = callStackData.callStackElement!.Value.columnNumber + } + }, + Message = new SarifReportMessage + { + Text = $"Call stack entry #{callStackData.callStackIndex}" + } + }) + .ToList() + } + } + } + } }; yield return resultEntry; } + // Get statement of call stack source instruction + (string fileName, int lineNumber, int columnNumber) sourceStatement = ("", 0, 0); + bool found = false; + for(uint i = 0; i < 4096; ++i) + { + if(statements.TryGetValue((SourceInstructionImageName, SourceInstructionOffset - i), out sourceStatement)) + { + found = true; + break; + } + } + + if(found) + callStack.Push(sourceStatement); + else + callStack.Push(null); + + // Remember source statement + // Format children - foreach(var sarifResultEntry in Children.SelectMany(c => c.ProduceSarifReportEntries(formattedCallStack, statements))) + foreach(var sarifResultEntry in Children.SelectMany(c => c.ProduceSarifReportEntries(formattedCallStack, callStack, statements))) yield return sarifResultEntry; + + // Return to parent call stack entry + callStack.Pop(); } } diff --git a/Tools/CiReportGenerator/SarifReport.cs b/Tools/CiReportGenerator/SarifReport.cs index 8e19bbc..614bfe9 100644 --- a/Tools/CiReportGenerator/SarifReport.cs +++ b/Tools/CiReportGenerator/SarifReport.cs @@ -66,7 +66,7 @@ public class SarifReportReportingDescriptor [JsonPropertyName("help")] public SarifReportReportingDescriptorHelp Help { get; set; } - + [JsonPropertyName("properties")] public SarifReportReportingDescriptorProperties Properties { get; set; } } @@ -117,6 +117,9 @@ public class SarifReportResult [JsonPropertyName("partialFingerprints")] public Dictionary PartialFingerprints { get; set; } + + [JsonPropertyName("codeFlows")] + public List CodeFlows { get; set; } } public class SarifReportLocation @@ -162,4 +165,16 @@ public class SarifReportMessage { [JsonPropertyName("text")] public string Text { get; set; } +} + +public class SarifCodeFlow +{ + [JsonPropertyName("threadFlows")] + public List ThreadFlows { get; set; } +} + +public class SarifThreadFlow +{ + [JsonPropertyName("locations")] + public List Locations { get; set; } } \ No newline at end of file