This commit is contained in:
Mukul Sabharwal 2018-09-28 22:58:16 -07:00
Родитель 39ca5dcf4d
Коммит 6ef9e375c2
13 изменённых файлов: 184 добавлений и 254 удалений

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

@ -3,90 +3,12 @@
namespace Microsoft.BPerf.StackAggregation
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.BPerf.ModuleInformation.Abstractions;
using Microsoft.BPerf.SymbolicInformation.Interfaces;
using Microsoft.Diagnostics.Symbols;
using Microsoft.Diagnostics.Tracing.Stacks;
public sealed class GenericStackSource : StackSource
public abstract class GenericStackSource : StackSource
{
private readonly List<StackInfo> stacks;
private readonly List<Frame> frames;
private readonly List<string> pseudoFrames;
private readonly int pseudoFramesStartOffset;
private readonly List<StackSourceSample> samples;
private readonly IInstructionPointerToSymbolicNameProvider eipToNameProvider;
private readonly Action<Action<StackSourceSample>> indirectCallback;
public GenericStackSource(IStackSamplesProvider samplesProvider, IInstructionPointerToSymbolicNameProvider eipToNameProvider, Action<Action<StackSourceSample>> indirectCallback)
{
this.stacks = samplesProvider.Stacks;
this.frames = samplesProvider.Frames;
this.pseudoFrames = samplesProvider.PseudoFrames;
this.pseudoFramesStartOffset = samplesProvider.PseudoFramesStartOffset;
this.samples = samplesProvider.Samples;
this.SampleTimeRelativeMSecLimit = samplesProvider.MaxTimeRelativeMSec;
this.eipToNameProvider = eipToNameProvider;
this.indirectCallback = indirectCallback;
}
public override int CallStackIndexLimit => this.stacks.Count;
public override int CallFrameIndexLimit => this.frames.Count + (int)StackSourceFrameIndex.Start;
public override int SampleIndexLimit => this.samples.Count;
public override double SampleTimeRelativeMSecLimit { get; }
public override void ForEach(Action<StackSourceSample> callback)
{
StackInformationAbstractionsEventSource.Logger.BeginForEachSample();
this.indirectCallback(callback);
StackInformationAbstractionsEventSource.Logger.EndForEachSample();
}
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex)
{
return (StackSourceCallStackIndex)this.stacks[(int)callStackIndex].CallerID;
}
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex)
{
return (StackSourceFrameIndex)this.stacks[(int)callStackIndex].FrameID;
}
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName)
{
var frame = this.frames[(int)frameIndex];
if ((int)frameIndex < this.pseudoFramesStartOffset)
{
return this.eipToNameProvider.GetSymbolicName(frame.ProcessId, frame.InstructionPointer);
}
return this.pseudoFrames[(int)frame.ProcessId];
}
public override StackSourceSample GetSampleByIndex(StackSourceSampleIndex sampleIndex)
{
return this.samples[(int)sampleIndex];
}
public ValueTask<SourceLocation> GetSourceLocation(StackSourceFrameIndex frameIndex)
{
var frame = this.frames[(int)frameIndex];
return this.eipToNameProvider.GetSourceLocation(frame.ProcessId, frame.InstructionPointer);
}
public abstract ValueTask<SourceLocation> GetSourceLocation(StackSourceFrameIndex frameIndex);
}
}

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

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@ -8,11 +8,11 @@
<ItemGroup>
<ProjectReference Include="..\Microsoft.BPerf.ModuleInformation.Abstractions\Microsoft.BPerf.ModuleInformation.Abstractions.csproj" />
<ProjectReference Include="..\Microsoft.BPerf.SymbolicInformation.Interfaces\Microsoft.BPerf.SymbolicInformation.Interfaces.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.Tracing.Stacks\Microsoft.Diagnostics.Tracing.Stacks.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.0.0" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.26" />
</ItemGroup>
<Import Project="..\SourceLink.targets" />

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

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.0.0" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.26" />
</ItemGroup>
<Import Project="..\SourceLink.targets" />

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

@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.BPerf.StackInformation.Etw
{
using System;
using System.Threading.Tasks;
using Microsoft.BPerf.StackAggregation;
using Microsoft.Diagnostics.Symbols;
using Microsoft.Diagnostics.Tracing.Stacks;
internal sealed class SourceAwareStackSource : GenericStackSource
{
private readonly StackSource inner;
public SourceAwareStackSource(StackSource inner)
{
this.inner = inner;
}
public override StackSource BaseStackSource => this.inner.BaseStackSource;
public override int CallStackIndexLimit => this.inner.CallStackIndexLimit;
public override int CallFrameIndexLimit => this.inner.CallFrameIndexLimit;
public override bool SamplesImmutable => this.inner.SamplesImmutable;
public override int SampleIndexLimit => this.inner.SampleIndexLimit;
public override double SampleTimeRelativeMSecLimit => this.inner.SampleTimeRelativeMSecLimit;
public override int ScenarioCount => this.inner.ScenarioCount;
public override float? SamplingRate => this.inner.SamplingRate;
public override bool IsGraphSource => this.inner.IsGraphSource;
public override bool OnlyManagedCodeStacks => this.inner.OnlyManagedCodeStacks;
public override void ParallelForEach(Action<StackSourceSample> callback, int desiredParallelism = 0)
{
this.inner.ParallelForEach(callback, desiredParallelism);
}
public override void GetReferences(StackSourceSampleIndex nodeIndex, RefDirection direction, Action<StackSourceSampleIndex> callback)
{
this.inner.GetReferences(nodeIndex, direction, callback);
}
public override int GetNumberOfFoldedFrames(StackSourceCallStackIndex callStackIndex)
{
return this.inner.GetNumberOfFoldedFrames(callStackIndex);
}
public override StackSourceSample GetSampleByIndex(StackSourceSampleIndex sampleIndex)
{
return this.inner.GetSampleByIndex(sampleIndex);
}
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex)
{
return this.inner.GetCallerIndex(callStackIndex);
}
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex)
{
return this.inner.GetFrameIndex(callStackIndex);
}
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName)
{
return this.inner.GetFrameName(frameIndex, verboseName);
}
public override void ForEach(Action<StackSourceSample> callback)
{
this.inner.ForEach(callback);
}
public override ValueTask<SourceLocation> GetSourceLocation(StackSourceFrameIndex frameIndex)
{
throw new NotImplementedException();
}
}
}

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

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.BPerf.StackInformation.Etw
{
using System.Collections.Generic;
using Microsoft.BPerf.StackAggregation;
using Microsoft.Diagnostics.Tracing.Etlx;
using Microsoft.Diagnostics.Tracing.Stacks;
public sealed class TraceLogEtwDeserializer
{
public TraceLogEtwDeserializer(string etlFileName)
{
var traceLog = new TraceLog(TraceLog.CreateFromEventTraceLogFile(etlFileName));
this.StackSource = new SourceAwareStackSource(new TraceEventStackSource(traceLog.Events));
this.EventStats = new Dictionary<int, TraceEventCounts>(traceLog.Stats.Count);
this.TraceProcesses = traceLog.Processes;
int i = 0;
foreach (var eventStat in traceLog.Stats)
{
this.EventStats.Add(i, eventStat);
i++;
}
}
public Dictionary<int, TraceEventCounts> EventStats { get; }
public TraceProcesses TraceProcesses { get; }
public GenericStackSource StackSource { get; }
}
}

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

@ -5,20 +5,14 @@ namespace Microsoft.BPerf.StackViewer
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using global::Diagnostics.Tracing.StackSources;
using Microsoft.BPerf.ModuleInformation.Abstractions;
using Microsoft.BPerf.StackAggregation;
using Microsoft.BPerf.StackInformation.Etw;
using Microsoft.BPerf.SymbolicInformation.ProgramDatabase;
using Microsoft.BPerf.SymbolServer.Interfaces;
using Microsoft.Diagnostics.Tracing.Stacks;
using Newtonsoft.Json.Linq;
public sealed class CallTreeData : ICallTreeData
{
@ -32,11 +26,7 @@ namespace Microsoft.BPerf.StackViewer
private readonly object lockobj = new object();
private readonly ISymbolServerArtifactRetriever symbolServerArtifactRetriever;
private readonly ISourceServerAuthorizationInformationProvider sourceServerInformationProvider;
private readonly EtwDeserializer deserializer;
private readonly GenericStackSource stackSource;
private readonly StackViewerModel model;
@ -44,11 +34,9 @@ namespace Microsoft.BPerf.StackViewer
private CallTree callTree;
public CallTreeData(ISymbolServerArtifactRetriever symbolServerArtifactRetriever, ISourceServerAuthorizationInformationProvider sourceServerInformationProvider, EtwDeserializer deserializer, StackViewerModel model)
public CallTreeData(GenericStackSource stackSource, StackViewerModel model)
{
this.symbolServerArtifactRetriever = symbolServerArtifactRetriever;
this.sourceServerInformationProvider = sourceServerInformationProvider;
this.deserializer = deserializer;
this.stackSource = stackSource;
this.model = model;
}
@ -63,21 +51,19 @@ namespace Microsoft.BPerf.StackViewer
CallTreeDataEventSource.Log.NodeCacheHit(name);
return new TreeNode(this.nodeNameCache[name]);
}
else
{
foreach (var node in this.callTree.ByID)
{
if (node.Name == name)
{
this.nodeNameCache.Add(name, node);
CallTreeDataEventSource.Log.NodeCacheMisss(name);
return new TreeNode(node);
}
}
CallTreeDataEventSource.Log.NodeCacheNotFound(name);
return null;
foreach (var node in this.callTree.ByID)
{
if (node.Name == name)
{
this.nodeNameCache.Add(name, node);
CallTreeDataEventSource.Log.NodeCacheMisss(name);
return new TreeNode(node);
}
}
CallTreeDataEventSource.Log.NodeCacheNotFound(name);
return null;
}
}
@ -213,52 +199,32 @@ namespace Microsoft.BPerf.StackViewer
if (sourceLocation != null)
{
var buildTimePath = sourceLocation.SourceFile.BuildTimeFilePath;
var srcSrvString = sourceLocation.SourceFile.SrcSrvString;
var srcSrvString = sourceLocation.SourceFile.GetSourceFile();
// TODO: src srv stream needs more support, also talk to VS folks and see how they do SourceLink
if (srcSrvString != null)
var lines = File.ReadAllLines(sourceLocation.SourceFile.GetSourceFile());
var list = new List<LineInformation>();
int i = 1;
foreach (var line in lines)
{
var doc = JObject.Parse(srcSrvString)["documents"].ToObject<Dictionary<string, string>>().First();
string urlPath = doc.Value.Replace("*", buildTimePath.Replace(doc.Key.Replace("*", string.Empty), string.Empty));
var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })
var li = new LineInformation
{
BaseAddress = new Uri(urlPath)
Line = line,
LineNumber = i++
};
var authorizationHeader = this.sourceServerInformationProvider.GetAuthorizationHeaderValue(urlPath);
if (!string.IsNullOrEmpty(authorizationHeader))
{
client.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
}
var result = await client.GetStringAsync(urlPath);
var lines = result.Split('\n');
var list = new List<LineInformation>();
int i = 1;
foreach (var line in lines)
{
var li = new LineInformation
{
Line = line,
LineNumber = i++
};
list.Add(li);
}
var si = new SourceInformation
{
BuildTimeFilePath = buildTimePath,
Lines = list,
Summary = new List<LineInformation> { new LineInformation { LineNumber = sourceLocation.LineNumber, Metric = retVal[index] } }
};
return si;
list.Add(li);
}
var si = new SourceInformation
{
BuildTimeFilePath = buildTimePath,
Lines = list,
Summary = new List<LineInformation> { new LineInformation { LineNumber = sourceLocation.LineNumber, Metric = retVal[index] } }
};
return si;
}
return null; // TODO: need to implement the local case i.e. this is the build machine
@ -283,38 +249,6 @@ namespace Microsoft.BPerf.StackViewer
return;
}
var pid = uint.Parse(this.model.Pid);
var symbolProvider = new TracePdbSymbolReaderProvider(this.symbolServerArtifactRetriever);
if (this.deserializer.ImageLoadMap.TryGetValue(pid, out var images))
{
int total = 0;
int count = 0;
foreach (var image in images)
{
count++;
total += image.InstructionPointers.Count;
}
var pdbLookupImageList = new List<ImageInfo>(count);
var ftotal = (float)total;
foreach (var image in images)
{
if ((image.InstructionPointers.Count / ftotal) * 100 >= 1.0)
{
pdbLookupImageList.Add(image);
}
}
foreach (var image in pdbLookupImageList)
{
if (this.deserializer.ImageToDebugInfoMap.TryGetValue(new ProcessImageId(pid, image.Begin), out var dbgId))
{
await symbolProvider.GetSymbolReader(image.FilePath, dbgId.Signature, dbgId.Age, dbgId.Filename);
}
}
}
var filterParams = new FilterParams
{
StartTimeRelativeMSec = this.model.Start,
@ -327,34 +261,10 @@ namespace Microsoft.BPerf.StackViewer
Name = "NoName"
};
var stackType = int.Parse(this.model.StackType);
if (!this.deserializer.EventStacks.TryGetValue(stackType, out var stackEventType))
{
throw new ArgumentException();
}
var instructionPointerDecoder = new InstructionPointerToSymbolicNameProvider(this.deserializer, symbolProvider);
var stackSource = new GenericStackSource(
this.deserializer,
instructionPointerDecoder,
callback =>
{
var sampleSource = this.deserializer;
var samples = stackEventType.SampleIndices;
foreach (var s in samples)
{
var sample = sampleSource.Samples[s];
if (sample.Scenario == pid)
{
callback(sampleSource.Samples[s]);
}
}
});
var ss = new FilterStackSource(filterParams, stackSource, ScalingPolicyKind.TimeMetric);
var ss = new FilterStackSource(filterParams, this.stackSource, ScalingPolicyKind.TimeMetric);
double startTimeRelativeMsec = double.TryParse(filterParams.StartTimeRelativeMSec, out startTimeRelativeMsec) ? Math.Max(startTimeRelativeMsec, 0.0) : 0.0;
double endTimeRelativeMsec = double.TryParse(filterParams.EndTimeRelativeMSec, out endTimeRelativeMsec) ? Math.Min(endTimeRelativeMsec, ss.SampleTimeRelativeMSecLimit) : ss.SampleTimeRelativeMSecLimit;
double endTimeRelativeMsec = double.TryParse(filterParams.EndTimeRelativeMSec, out endTimeRelativeMsec) ? Math.Min(endTimeRelativeMsec, this.stackSource.SampleTimeRelativeMSecLimit) : this.stackSource.SampleTimeRelativeMSecLimit;
this.callTree = new CallTree(ScalingPolicyKind.TimeMetric);
this.callTree.TimeHistogramController = new TimeHistogramController(this.callTree, startTimeRelativeMsec, endTimeRelativeMsec);

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

@ -12,7 +12,6 @@ namespace Microsoft.BPerf.StackViewer
using System.Threading.Tasks;
using Microsoft.BPerf.StackInformation.Abstractions;
using Microsoft.BPerf.StackInformation.Etw;
using Microsoft.BPerf.SymbolServer.Interfaces;
public sealed class DeserializedData : IDeserializedData
{
@ -20,10 +19,6 @@ namespace Microsoft.BPerf.StackViewer
private readonly FileLocationType locationType;
private readonly ISourceServerAuthorizationInformationProvider sourceServerAuthorizationInformationProvider;
private readonly ISymbolServerArtifactRetriever symbolServerArtifactRetriever;
private readonly string tempDownloadLocation;
private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
@ -36,14 +31,12 @@ namespace Microsoft.BPerf.StackViewer
private int initialized;
private EtwDeserializer deserializer;
private TraceLogEtwDeserializer deserializer;
public DeserializedData(string uri, FileLocationType locationType, ISourceServerAuthorizationInformationProvider sourceServerAuthorizationInformationProvider, ISymbolServerArtifactRetriever symbolServerArtifactRetriever, string tempDownloadLocation)
public DeserializedData(string uri, FileLocationType locationType, string tempDownloadLocation)
{
this.uri = uri;
this.locationType = locationType;
this.sourceServerAuthorizationInformationProvider = sourceServerAuthorizationInformationProvider;
this.symbolServerArtifactRetriever = symbolServerArtifactRetriever;
this.tempDownloadLocation = tempDownloadLocation;
}
@ -61,7 +54,7 @@ namespace Microsoft.BPerf.StackViewer
{
if (!this.callTreeDataCache.TryGetValue(model, out var value))
{
value = new CallTreeData(this.symbolServerArtifactRetriever, this.sourceServerAuthorizationInformationProvider, this.deserializer, model);
value = new CallTreeData(this.deserializer.StackSource, model);
this.callTreeDataCache.Add(model, value);
}
@ -116,24 +109,16 @@ namespace Microsoft.BPerf.StackViewer
var filePath = this.locationType == FileLocationType.Url ? await this.DownloadFile(this.uri) : this.uri;
this.deserializer = new EtwDeserializer(filePath);
this.deserializer = new TraceLogEtwDeserializer(filePath);
foreach (var pair in this.deserializer.EventStacks)
foreach (var pair in this.deserializer.EventStats)
{
this.stackEventTypes.Add(new StackEventTypeInfo(pair.Key, pair.Value.EventName, pair.Value.SampleIndices.Count));
this.stackEventTypes.Add(new StackEventTypeInfo(pair.Key, pair.Value.EventName, pair.Value.Count, pair.Value.StackCount));
}
foreach (var pair in this.deserializer.ImageLoadMap.OrderBy(t => t.Value.Sum(y => y.InstructionPointers.Count)).Reverse())
foreach (var pair in this.deserializer.TraceProcesses.OrderByDescending(t => t.CPUMSec))
{
long total = 0;
foreach (var image in pair.Value)
{
total += image.InstructionPointers.Count;
}
this.processList.Add(this.deserializer.ProcessIdToNameMap.TryGetValue(pair.Key, out var processName)
? new ProcessInfo(processName, pair.Key, total)
: new ProcessInfo($"Process {pair.Key}", pair.Key, total));
this.processList.Add(new ProcessInfo(pair.Name + $"({pair.ProcessID})", (int)pair.ProcessIndex, pair.CPUMSec));
}
this.initialized = 1;

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

@ -5,7 +5,6 @@ namespace Microsoft.BPerf.StackViewer
{
using System;
using System.Diagnostics.Tracing;
using Microsoft.BPerf.SymbolServer.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
@ -13,19 +12,13 @@ namespace Microsoft.BPerf.StackViewer
{
private readonly CallTreeDataCache cache;
private readonly ISourceServerAuthorizationInformationProvider sourceServerAuthorizationInformationProvider;
private readonly ISymbolServerArtifactRetriever symbolServerArtifactRetriever;
private readonly ICacheExpirationTimeProvider cacheExpirationTimeProvider;
private readonly IOptions<StackViewerSettings> stackViewerSettings;
public DeserializedDataCache(CallTreeDataCache cache, ISymbolServerArtifactRetriever symbolServerArtifactRetriever, ISourceServerAuthorizationInformationProvider sourceServerAuthorizationInformationProvider, ICacheExpirationTimeProvider cacheExpirationTimeProvider, IOptions<StackViewerSettings> stackViewerSettings)
public DeserializedDataCache(CallTreeDataCache cache, ICacheExpirationTimeProvider cacheExpirationTimeProvider, IOptions<StackViewerSettings> stackViewerSettings)
{
this.cache = cache;
this.symbolServerArtifactRetriever = symbolServerArtifactRetriever;
this.sourceServerAuthorizationInformationProvider = sourceServerAuthorizationInformationProvider;
this.cacheExpirationTimeProvider = cacheExpirationTimeProvider;
this.stackViewerSettings = stackViewerSettings;
}
@ -42,7 +35,7 @@ namespace Microsoft.BPerf.StackViewer
if (!this.cache.TryGetValue(model.Filename, out IDeserializedData data))
{
var cacheEntryOptions = new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.NeverRemove).RegisterPostEvictionCallback(callback: EvictionCallback, state: this).SetSlidingExpiration(this.cacheExpirationTimeProvider.Expiration);
data = new DeserializedData(model.Filename, model.LocationType, this.sourceServerAuthorizationInformationProvider, this.symbolServerArtifactRetriever, this.stackViewerSettings.Value.TemporaryDataFileDownloadLocation);
data = new DeserializedData(model.Filename, model.LocationType, this.stackViewerSettings.Value.TemporaryDataFileDownloadLocation);
this.cache.Set(model.Filename, data, cacheEntryOptions);
CacheMonitorEventSource.Logger.CacheEntryAdded(Environment.MachineName, model.Filename);
}

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

@ -24,7 +24,6 @@
<ProjectReference Include="..\Microsoft.BPerf.SymbolicInformation.Interfaces\Microsoft.BPerf.SymbolicInformation.Interfaces.csproj" />
<ProjectReference Include="..\Microsoft.BPerf.SymbolicInformation.ProgramDatabase\Microsoft.BPerf.SymbolicInformation.ProgramDatabase.csproj" />
<ProjectReference Include="..\Microsoft.BPerf.SymbolServer.Interfaces\Microsoft.BPerf.SymbolServer.Interfaces.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.Tracing.Stacks\Microsoft.Diagnostics.Tracing.Stacks.csproj" />
</ItemGroup>
<ItemGroup>

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

@ -1,9 +1,6 @@
namespace Microsoft.BPerf.StackViewer
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
public class ProcessListViewModel : StackViewerModel

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

@ -5,17 +5,17 @@ namespace Microsoft.BPerf.StackViewer
{
public sealed class ProcessInfo
{
public ProcessInfo(string processName, uint pid, long samplesCount)
public ProcessInfo(string processName, int pid, float cpumsec)
{
this.Name = processName;
this.Id = pid;
this.SamplesCount = samplesCount;
this.CPUMSec = cpumsec;
}
public string Name { get; }
public uint Id { get; }
public int Id { get; }
public long SamplesCount { get; }
public float CPUMSec { get; }
}
}

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

@ -5,11 +5,12 @@ namespace Microsoft.BPerf.StackViewer
{
public sealed class StackEventTypeInfo
{
public StackEventTypeInfo(int eventId, string eventName, int eventCount)
public StackEventTypeInfo(int eventId, string eventName, int eventCount, int stackEventCount)
{
this.EventId = eventId;
this.EventName = eventName;
this.EventCount = eventCount;
this.StackEventCount = stackEventCount;
}
public int EventId { get; }
@ -17,5 +18,7 @@ namespace Microsoft.BPerf.StackViewer
public string EventName { get; }
public int EventCount { get; }
public int StackEventCount { get; }
}
}

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

@ -39,8 +39,8 @@
@foreach (var process in this.Model.ProcessList)
{
<tr>
<td><a href="/ui/stackviewer/summary?filename=@this.Model.Filename&stacktype=@this.Model.StackType&pid=@process.Id">@process.Name (@process.Id)</a></td>
<td>@process.SamplesCount</td>
<td><a href="/ui/stackviewer/summary?filename=@this.Model.Filename&stacktype=@this.Model.StackType&pid=@process.Id">@process.Name</a></td>
<td>@process.CPUMSec</td>
</tr>
}
</tbody>