Create the SimpleStackFrameDeminfier which uses significantly less memory than the StackTraceDeminifier during runtime.

This commit is contained in:
Christian Gonzalez 2016-11-15 17:45:05 -08:00
Родитель 411d1a54ab
Коммит 64538ebc58
13 изменённых файлов: 137 добавлений и 177 удалений

Просмотреть файл

@ -12,17 +12,15 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Returns a FunctionMap describing the locations of every funciton in the source code.
/// The functions are to be sorted descending by start position.
/// </summary>
public List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, StreamReader sourceMapStreamReader)
public List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap)
{
if (sourceCodeStreamReader == null || sourceMapStreamReader == null)
if (sourceCodeStreamReader == null || sourceMap == null)
{
return null;
}
List<FunctionMapEntry> result = ParseSourceCode(sourceMapStreamReader);
List<FunctionMapEntry> result = ParseSourceCode(sourceCodeStreamReader);
SourceMapParser sourceMapParser = new SourceMapParser();
SourceMap sourceMap = sourceMapParser.ParseSourceMap(sourceMapStreamReader);
foreach (FunctionMapEntry functionMapEntry in result)
{
functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);

Просмотреть файл

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
@ -10,12 +12,12 @@ namespace SourcemapToolkit.CallstackDeminifier
private readonly IFunctionMapGenerator _functionMapGenerator;
private readonly KeyValueCache<string,List<FunctionMapEntry>> _functionMapCache;
public FunctionMapStore(ISourceCodeProvider sourceCodeProvider, ISourceMapProvider sourceMapProvider)
public FunctionMapStore(ISourceCodeProvider sourceCodeProvider, Func<string, SourceMap> sourceMapGetter)
{
_functionMapGenerator = new FunctionMapGenerator();
_functionMapCache = new KeyValueCache<string, List<FunctionMapEntry>>(sourceCodeUrl => _functionMapGenerator.GenerateFunctionMap(
sourceCodeProvider.GetSourceCode(sourceCodeUrl),
sourceMapProvider.GetSourceMapContentsForCallstackUrl(sourceCodeUrl)));
sourceMapGetter(sourceCodeUrl)));
}
/// <summary>

Просмотреть файл

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
@ -9,6 +10,6 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Returns a FunctionMap describing the locations of every funciton in the source code.
/// The functions are to be sorted in decreasing order by start position.
/// </summary>
List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, StreamReader sourceMapStreamReader);
List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap);
}
}

Просмотреть файл

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
namespace SourcemapToolkit.CallstackDeminifier
{
/// <summary>
/// This class only deminfies the method name in a stack frame. It does not depend on having a source map available during runtime.
/// </summary>
internal class SimpleStackFrameDeminifier : IStackFrameDeminifier
{
protected readonly IFunctionMapConsumer _functionMapConsumer;
protected readonly IFunctionMapStore _functionMapStore;
public SimpleStackFrameDeminifier(IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
{
_functionMapStore = functionMapStore;
_functionMapConsumer = functionMapConsumer;
}
public virtual StackFrame DeminifyStackFrame(StackFrame stackFrame)
{
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
FunctionMapEntry wrappingFunction = null;
// This code deminifies the stack frame by finding the wrapping function in
// the generated code and then using the source map to find the name and
// and original source location.
List<FunctionMapEntry> functionMap = _functionMapStore.GetFunctionMapForSourceCode(stackFrame.FilePath);
if (functionMap != null)
{
wrappingFunction =
_functionMapConsumer.GetWrappingFunctionForSourceLocation(stackFrame.SourcePosition, functionMap);
}
return new StackFrame {MethodName = wrappingFunction?.DeminfifiedMethodName};
}
}
}

Просмотреть файл

@ -65,10 +65,12 @@
<Compile Include="IStackFrameDeminifier.cs" />
<Compile Include="IStackTraceParser.cs" />
<Compile Include="KeyValueCache.cs" />
<Compile Include="SimpleStackFrameDeminifier.cs" />
<Compile Include="SourceMapStore.cs" />
<Compile Include="StackFrame.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StackFrameDeminifier.cs" />
<Compile Include="StackTraceDeminfierFactory.cs" />
<Compile Include="StackTraceDeminifier.cs" />
<Compile Include="StackTraceParser.cs" />
</ItemGroup>

Просмотреть файл

@ -1,70 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
/// <summary>
/// <summary>
/// Class responsible for deminifying a single stack frame in a minified stack trace.
/// </summary>
internal class StackFrameDeminifier : IStackFrameDeminifier
internal class StackFrameDeminifier : SimpleStackFrameDeminifier
{
private readonly IFunctionMapConsumer _functionMapConsumer;
private readonly IFunctionMapStore _functionMapStore;
private readonly ISourceMapStore _sourceMapStore;
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer) : base (functionMapStore, functionMapConsumer)
{
_functionMapStore = functionMapStore;
_sourceMapStore = sourceMapStore;
_functionMapConsumer = functionMapConsumer;
}
/// <summary>
/// This method will deminify a single stack from from a minified stack trace.
/// </summary>
/// <returns>Returns a stack trace that has been translated to a best guess of the original source code. Any of the fields in the stack frame may be null</returns>
public StackFrame DeminifyStackFrame(StackFrame stackFrame)
public override StackFrame DeminifyStackFrame(StackFrame stackFrame)
{
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
FunctionMapEntry wrappingFunction = null;
SourceMap sourceMap = _sourceMapStore.GetSourceMapForUrl(stackFrame.FilePath);
SourceMap sourceMap = _sourceMapStore.GetSourceMapForUrl(stackFrame.FilePath);
SourcePosition generatedSourcePosition = stackFrame.SourcePosition;
// This code deminifies the stack frame by finding the wrapping function in
// the generated code and then using the source map to find the name and
// and original source location.
List<FunctionMapEntry> functionMap = _functionMapStore.GetFunctionMapForSourceCode(stackFrame.FilePath);
if (functionMap != null)
{
wrappingFunction =
_functionMapConsumer.GetWrappingFunctionForSourceLocation(stackFrame.SourcePosition, functionMap);
}
StackFrame result = base.DeminifyStackFrame(stackFrame);
return ExtractFrameInformationFromSourceMap(wrappingFunction, sourceMap, stackFrame.SourcePosition);
}
MappingEntry generatedSourcePositionMappingEntry = sourceMap?.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition);
result.FilePath = generatedSourcePositionMappingEntry?.OriginalFileName;
result.SourcePosition = generatedSourcePositionMappingEntry?.OriginalSourcePosition;
/// <summary>
/// Gets the information necessary for a deminified stack frame from the relevant source map.
/// </summary>
/// <param name="wrappingFunction">The function that wraps the current stack frame location</param>
/// <param name="sourceMap">The relevant source map for this generated code</param>
/// <param name="generatedSourcePosition">The location that should be translated to original source code location in the deminified stack frame.</param>
/// <returns>Returns a StackFrame object with best guess values for each property. Any of the properties may be null if no match was found.</returns>
internal static StackFrame ExtractFrameInformationFromSourceMap(FunctionMapEntry wrappingFunction, SourceMap sourceMap, SourcePosition generatedSourcePosition)
{
StackFrame result = new StackFrame();
result.MethodName = wrappingFunction.DeminfifiedMethodName;
MappingEntry generatedSourcePositionMappingEntry = sourceMap?.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition);
result.FilePath = generatedSourcePositionMappingEntry?.OriginalFileName;
result.SourcePosition = generatedSourcePositionMappingEntry?.OriginalSourcePosition;
return result;
return result;
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
using System;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
public class StackTraceDeminfierFactory
{
private void ValidateArguments(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
if (sourceMapProvider == null)
{
throw new ArgumentNullException(nameof(sourceMapProvider));
}
if (generatedCodeProvider == null)
{
throw new ArgumentNullException(nameof(generatedCodeProvider));
}
}
/// <summary>
/// Creates a StackTraceDeminifier with full capabilities. StackTrace deminifiers created with this method will keep source maps cached, and thus use significantly more memory during runtime than the MethodNameStackTraceDeminifier.
/// </summary>
/// <param name="sourceMapProvider">Consumers of the API should implement this interface, which provides the source map for a given JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
/// <param name="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public StackTraceDeminifier GetStackTraceDeminfier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
ValidateArguments(sourceMapProvider, generatedCodeProvider);
ISourceMapStore sourceMapStore = new SourceMapStore(sourceMapProvider);
IStackFrameDeminifier stackFrameDeminifier = new StackFrameDeminifier(sourceMapStore,
new FunctionMapStore(generatedCodeProvider, sourceMapStore.GetSourceMapForUrl), new FunctionMapConsumer());
IStackTraceParser stackTraceParser = new StackTraceParser();
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}
/// <summary>
/// Creates a StackTraceDeminifier that only deminifies the method names. StackTrace deminifiers created with this method will use significantly less memory during runtime than the
/// </summary>
/// <param name="sourceMapProvider">Consumers of the API should implement this interface, which provides the source map for a given JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
/// <param name="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public StackTraceDeminifier GetMethodNameOnlyStackTraceDeminfier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
ValidateArguments(sourceMapProvider, generatedCodeProvider);
SourceMapParser sourceMapParser = new SourceMapParser();
IStackFrameDeminifier stackFrameDeminifier = new SimpleStackFrameDeminifier(new FunctionMapStore(generatedCodeProvider, (url) => sourceMapParser.ParseSourceMap(sourceMapProvider.GetSourceMapContentsForCallstackUrl(url))), new FunctionMapConsumer());
IStackTraceParser stackTraceParser = new StackTraceParser();
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}
}
}

Просмотреть файл

@ -1,38 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace SourcemapToolkit.CallstackDeminifier
{
/// <summary>
/// Public API for parsing and deminifying browser stack traces.
/// </summary>
public class StackTraceDeminifier
/// <summary>
/// This class is responsible for parsing a callstack string into
/// a list of StackFrame objects and providing the deminified version
/// of the stack frame.
/// </summary>
public class StackTraceDeminifier
{
private readonly IStackFrameDeminifier _stackFrameDeminifier;
private readonly IStackTraceParser _stackTraceParser;
/// <summary>
/// This class is responsible for parsing a callstack string into
/// a list of StackFrame objects and providing the deminified version
/// of the stack frame.
/// </summary>
/// <param name="sourceMapProvider">Consumers of the API should implement this interface, which provides the source map for a given JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
/// <param name="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public StackTraceDeminifier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
: this(new StackFrameDeminifier(new SourceMapStore(sourceMapProvider),
new FunctionMapStore(generatedCodeProvider, sourceMapProvider), new FunctionMapConsumer()), new StackTraceParser())
{
if (sourceMapProvider == null)
{
throw new ArgumentNullException(nameof(sourceMapProvider));
}
if (generatedCodeProvider == null)
{
throw new ArgumentNullException(nameof(generatedCodeProvider));
}
}
internal StackTraceDeminifier(IStackFrameDeminifier stackFrameDeminifier, IStackTraceParser stackTraceParser)
{
_stackFrameDeminifier = stackFrameDeminifier;

Просмотреть файл

@ -70,7 +70,6 @@
<Compile Include="StackTraceDeminifierUnitTests.cs" />
<Compile Include="StackTraceParserUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StackFrameDeminifierUnitTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SourceMapToolkit.CallstackDeminifier\SourcemapToolkit.CallstackDeminifier.csproj">

Просмотреть файл

@ -1,69 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
[TestClass]
public class StackFrameDeminifierUnitTests
{
private IStackFrameDeminifier GetStackFrameDeminifierWithMockDependencies(ISourceMapStore sourceMapStore = null, IFunctionMapStore functionMapStore = null, IFunctionMapConsumer functionMapConsumer = null)
{
if (sourceMapStore == null)
{
sourceMapStore = MockRepository.GenerateStrictMock<ISourceMapStore>();
}
if (functionMapStore == null)
{
functionMapStore = MockRepository.GenerateStrictMock<IFunctionMapStore>();
}
if (functionMapConsumer == null)
{
functionMapConsumer = MockRepository.GenerateStrictMock<IFunctionMapConsumer>();
}
return new StackFrameDeminifier(sourceMapStore, functionMapStore, functionMapConsumer);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void DeminifyStackFrame_NullInputStackFrame_ThrowsException()
{
// Arrange
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies();
StackFrame stackFrame = null;
// Act
StackFrame deminifiedStackFrame = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_HasMatchingGeneratedPositionMapping_ReturnsStackFrameWithSourcePositionAndFileName()
{
// Arrange
FunctionMapEntry functionMapEntry = null;
SourcePosition generatedSourcePosition = new SourcePosition
{
ZeroBasedColumnNumber = 25,
ZeroBasedLineNumber = 85
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(x => x.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition)).Return(new MappingEntry
{
OriginalSourcePosition = new SourcePosition { ZeroBasedColumnNumber = 10, ZeroBasedLineNumber = 20 }
});
// Act
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.AreEqual(10, deminifiedStackFrame.SourcePosition.ZeroBasedColumnNumber);
Assert.AreEqual(20, deminifiedStackFrame.SourcePosition.ZeroBasedLineNumber);
}
}
}

Просмотреть файл

@ -20,7 +20,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
sourceCodeProvider.Stub(x => x.GetSourceCode("http://localhost:11323/crashcauser.js")).Return(UnitTestUtils.StreamReaderFromString(GeneratedCodeString));
return new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
StackTraceDeminfierFactory stackTraceDeminfierFactory = new StackTraceDeminfierFactory();
return stackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider, sourceCodeProvider);
}
private void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)

Просмотреть файл

@ -18,8 +18,9 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
sourceCodeProvider.Stub(x => x.GetSourceCode("http://localhost:11323/crashcauser.min.js")).Return(UnitTestUtils.StreamReaderFromString(GeneratedCodeString));
return new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
StackTraceDeminfierFactory stackTraceDeminfierFactory = new StackTraceDeminfierFactory();
return stackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider, sourceCodeProvider);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
{

Просмотреть файл

@ -8,30 +8,6 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
[TestClass]
public class StackTraceDeminifierUnitTests
{
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void StackTraceDeminifier_NullSourceMapProvider_ThrowsException()
{
// Arrange
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
ISourceMapProvider sourceMapProvider = null;
// Act
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void StackTraceDeminifier_NullSourceCodeProvider_ThrowsException()
{
// Arrange
ISourceCodeProvider sourceCodeProvider = null;
ISourceMapProvider sourceMapProvider = MockRepository.GenerateStrictMock<ISourceMapProvider>();
// Act
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
[TestMethod]
public void DeminifyStackTrace_UnableToParseStackTraceString_ReturnsEmptyList()
{