mirror of
https://github.com/Kae7in/perfview
synced 2026-08-09 12:09:44 +00:00
Fix issue where opening a .trace.zip file failes with a file in use error when opening CPU stacks.
The issue was twofold. First we were opening ZIP files in exclusive mode when reading, that is bad, use ZipFile.Open which does the right thing (allows other simultaneous readers) However a related issue is that we did not close the ZIP file when the CTF open failed. This left the file open so that the next open (to open stacks) would fail.
This commit is contained in:
@@ -114,7 +114,7 @@ namespace Diagnostics.Tracing.StackSources
|
||||
/// </summary>
|
||||
public void SetSymbolFile(string path)
|
||||
{
|
||||
this.SetSymbolFile(new ZipArchive(new FileStream(path, FileMode.Open)));
|
||||
this.SetSymbolFile(ZipFile.OpenRead(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace Diagnostics.Tracing.StackSources
|
||||
archive = null;
|
||||
if (path.EndsWith(".zip"))
|
||||
{
|
||||
archive = new ZipArchive(new FileStream(path, FileMode.Open));
|
||||
archive = ZipFile.OpenRead(path);
|
||||
ZipArchiveEntry foundEntry = null;
|
||||
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
|
||||
@@ -4594,7 +4594,7 @@ namespace PerfView
|
||||
allocName = "Alloc >= 32768";
|
||||
return allocName;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
protected internal override void ConfigureStackWindow(string stackSourceName, StackWindow stackWindow)
|
||||
{
|
||||
@@ -5177,7 +5177,7 @@ namespace PerfView
|
||||
}
|
||||
public override ImageSource Icon { get { return GuiApp.MainWindow.Resources["FileBitmapImage"] as ImageSource; } }
|
||||
|
||||
#region private
|
||||
#region private
|
||||
/// <summary>
|
||||
/// See if the log has events from VS providers. If so we should register the VS providers.
|
||||
/// </summary>
|
||||
@@ -5261,7 +5261,7 @@ namespace PerfView
|
||||
bool m_notifiedAboutLostEvents;
|
||||
bool m_notifiedAboutWin8;
|
||||
string m_extraTopStats;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
class WTPerfViewFile : PerfViewFile
|
||||
@@ -5403,7 +5403,7 @@ namespace PerfView
|
||||
stackWindow.CallTreeTab.IsSelected = true;
|
||||
}
|
||||
|
||||
#region private
|
||||
#region private
|
||||
[Flags]
|
||||
enum PageProtection
|
||||
{
|
||||
@@ -5471,7 +5471,7 @@ namespace PerfView
|
||||
return string.Format("<MemoryNode Name=\"{0}\" Start=\"0x{1:x}\" Length=\"0x{2:x}\"/>", Details, Address, Size);
|
||||
}
|
||||
|
||||
#region private
|
||||
#region private
|
||||
|
||||
private void Insert(MemoryNode newNode)
|
||||
{
|
||||
@@ -5519,7 +5519,7 @@ namespace PerfView
|
||||
ulong.TryParse(attrValue, out ret);
|
||||
return ret;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
class VMMapStackSource : InternStackSource
|
||||
@@ -5623,7 +5623,7 @@ namespace PerfView
|
||||
StackSourceSample m_sample;
|
||||
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
class PdbScopePerfViewFile : PerfViewFile
|
||||
@@ -5825,7 +5825,7 @@ namespace PerfView
|
||||
}
|
||||
}
|
||||
|
||||
#region private
|
||||
#region private
|
||||
|
||||
protected internal void OpenDump(TextWriter log)
|
||||
{
|
||||
@@ -5973,7 +5973,7 @@ namespace PerfView
|
||||
|
||||
internal protected GCHeapDump m_gcDump;
|
||||
string m_extraTopStats;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
public partial class LinuxPerfViewData : PerfViewFile
|
||||
@@ -6060,10 +6060,10 @@ namespace PerfView
|
||||
advanced.AddChild(new PerfViewJitStats(this));
|
||||
}
|
||||
|
||||
if(memory.Children.Count > 0)
|
||||
if (memory.Children.Count > 0)
|
||||
m_Children.Add(memory);
|
||||
|
||||
if(advanced.Children.Count > 0)
|
||||
if (advanced.Children.Count > 0)
|
||||
m_Children.Add(advanced);
|
||||
|
||||
return null;
|
||||
@@ -6096,6 +6096,9 @@ namespace PerfView
|
||||
m_traceLog.Dispose();
|
||||
m_traceLog = null;
|
||||
}
|
||||
else if (m_noTraceLogInfo)
|
||||
return null;
|
||||
|
||||
var dataFileName = FilePath;
|
||||
var options = new TraceLogOptions();
|
||||
options.ConversionLog = log;
|
||||
@@ -6107,59 +6110,35 @@ namespace PerfView
|
||||
options.LocalSymbolsOnly = false;
|
||||
options.ShouldResolveSymbols = delegate (string moduleFilePath) { return false; }; // Don't resolve any symbols
|
||||
|
||||
// But if there is a directory called EtwManifests exists, look in there instead.
|
||||
//var etwManifestDirPath = Path.Combine(Path.GetDirectoryName(dataFileName), "EtwManifests");
|
||||
//if (Directory.Exists(etwManifestDirPath))
|
||||
//options.ExplicitManifestDir = etwManifestDirPath;
|
||||
|
||||
//UnZipIfNecessary(ref dataFileName, log);
|
||||
|
||||
// If the etlx file exists, delete it so we can regenerate it.
|
||||
|
||||
// Generate the etlx file path / name.
|
||||
string etlxFile = dataFileName + ".etlx";
|
||||
if (File.Exists(etlxFile))
|
||||
string etlxFile = CacheFiles.FindFile(dataFileName, ".etlx");
|
||||
if (!File.Exists(etlxFile) || File.GetLastWriteTimeUtc(etlxFile) < File.GetLastWriteTimeUtc(dataFileName))
|
||||
{
|
||||
FileUtilities.ForceDelete(etlxFile);
|
||||
}
|
||||
|
||||
log.WriteLine("Creating ETLX file {0} from {1}", etlxFile, dataFileName);
|
||||
|
||||
try
|
||||
{
|
||||
TraceLog.CreateFromLttngTextDataFile(dataFileName, etlxFile, options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.WriteLine("Exception encountered when building the tracelog.");
|
||||
log.WriteLine(ex.ToString());
|
||||
m_traceLog = null;
|
||||
return m_traceLog;
|
||||
log.WriteLine("Creating ETLX file {0} from {1}", etlxFile, dataFileName);
|
||||
try
|
||||
{
|
||||
TraceLog.CreateFromLttngTextDataFile(dataFileName, etlxFile, options);
|
||||
}
|
||||
catch (EndOfStreamException) // Throws this if there is no CTF Information
|
||||
{
|
||||
log.WriteLine("Trying to open CTF stream failed, no CTF information");
|
||||
Debug.Assert(m_traceLog == null);
|
||||
m_noTraceLogInfo = true;
|
||||
return m_traceLog;
|
||||
}
|
||||
}
|
||||
|
||||
var dataFileSize = "Unknown";
|
||||
if (File.Exists(dataFileName))
|
||||
dataFileSize = ((new System.IO.FileInfo(dataFileName)).Length / 1000000.0).ToString("n3") + " MB";
|
||||
|
||||
log.WriteLine("ETL Size {0} ETLX Size {1:n3} MB", dataFileSize, (new System.IO.FileInfo(etlxFile)).Length / 1000000.0);
|
||||
|
||||
try
|
||||
{
|
||||
m_traceLog = new TraceLog(etlxFile);
|
||||
|
||||
// Add some more parser that we would like.
|
||||
//new ETWClrProfilerTraceEventParser(m_traceLog);
|
||||
//new MicrosoftWindowsNDISPacketCaptureTraceEventParser(m_traceLog);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Open the ETLX file.
|
||||
m_traceLog = new TraceLog(etlxFile);
|
||||
m_utcLastWriteAtOpen = File.GetLastWriteTimeUtc(FilePath);
|
||||
if (App.CommandLineArgs.UnsafePDBMatch)
|
||||
m_traceLog.CodeAddresses.UnsafePDBMatching = true;
|
||||
|
||||
if (m_traceLog.Truncated) // Warn about truncation.
|
||||
{
|
||||
GuiApp.MainWindow.Dispatcher.BeginInvoke((Action)delegate ()
|
||||
@@ -6170,9 +6149,10 @@ namespace PerfView
|
||||
return m_traceLog;
|
||||
}
|
||||
|
||||
#region Private
|
||||
#region Private
|
||||
TraceLog m_traceLog;
|
||||
#endregion
|
||||
bool m_noTraceLogInfo;
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -6407,7 +6387,7 @@ namespace PerfView
|
||||
ConfigureAsEtwStackWindow(stackWindow, false, false);
|
||||
}
|
||||
|
||||
#region private
|
||||
#region private
|
||||
|
||||
/// <summary>
|
||||
/// Search for scenario data files matching a pattern, and add them to a dictionary.
|
||||
@@ -6549,7 +6529,7 @@ namespace PerfView
|
||||
return pathDict;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -52,5 +52,5 @@ using System.Windows;
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.17.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.18.0")]
|
||||
[assembly: InternalsVisibleTo("PerfViewTests")]
|
||||
@@ -8051,6 +8051,13 @@
|
||||
</li>
|
||||
-->
|
||||
<li>
|
||||
Version 1.9.18 6/15/16
|
||||
<ul>
|
||||
<li>
|
||||
Fixed issue where .Trace.ZIP files without Lttng information would fail when viewing the CPU stacks with a file in use error.
|
||||
</li>
|
||||
</ul>
|
||||
</li><li>
|
||||
Version 1.9.17 6/14/16
|
||||
<ul>
|
||||
<li>Fixes issue with out of memory when taking a .GCDump from a very large process dump. Improved the out of
|
||||
|
||||
@@ -45,52 +45,65 @@ namespace Microsoft.Diagnostics.Tracing
|
||||
{
|
||||
_filename = fileName;
|
||||
_zip = ZipFile.Open(fileName, ZipArchiveMode.Read);
|
||||
|
||||
_channels = new List<Tuple<ZipArchiveEntry, CtfMetadata>>();
|
||||
foreach (ZipArchiveEntry metadataArchive in _zip.Entries.Where(p => Path.GetFileName(p.FullName) == "metadata"))
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
CtfMetadataLegacyParser parser = new CtfMetadataLegacyParser(metadataArchive.Open());
|
||||
CtfMetadata metadata = new CtfMetadata(parser);
|
||||
|
||||
string path = Path.GetDirectoryName(metadataArchive.FullName);
|
||||
_channels.AddRange(from entry in _zip.Entries
|
||||
where Path.GetDirectoryName(entry.FullName) == path && Path.GetFileName(entry.FullName).StartsWith("channel")
|
||||
select new Tuple<ZipArchiveEntry, CtfMetadata>(entry, metadata));
|
||||
_channels = new List<Tuple<ZipArchiveEntry, CtfMetadata>>();
|
||||
foreach (ZipArchiveEntry metadataArchive in _zip.Entries.Where(p => Path.GetFileName(p.FullName) == "metadata"))
|
||||
{
|
||||
CtfMetadataLegacyParser parser = new CtfMetadataLegacyParser(metadataArchive.Open());
|
||||
CtfMetadata metadata = new CtfMetadata(parser);
|
||||
|
||||
pointerSize = Path.GetDirectoryName(metadataArchive.FullName).EndsWith("64-bit") ? 8 : 4;
|
||||
}
|
||||
string path = Path.GetDirectoryName(metadataArchive.FullName);
|
||||
_channels.AddRange(from entry in _zip.Entries
|
||||
where Path.GetDirectoryName(entry.FullName) == path && Path.GetFileName(entry.FullName).StartsWith("channel")
|
||||
select new Tuple<ZipArchiveEntry, CtfMetadata>(entry, metadata));
|
||||
|
||||
pointerSize = Path.GetDirectoryName(metadataArchive.FullName).EndsWith("64-bit") ? 8 : 4;
|
||||
}
|
||||
|
||||
|
||||
IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD));
|
||||
TraceEventNativeMethods.ZeroMemory(mem, sizeof(TraceEventNativeMethods.EVENT_RECORD));
|
||||
_header = (TraceEventNativeMethods.EVENT_RECORD*)mem;
|
||||
IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD));
|
||||
TraceEventNativeMethods.ZeroMemory(mem, sizeof(TraceEventNativeMethods.EVENT_RECORD));
|
||||
_header = (TraceEventNativeMethods.EVENT_RECORD*)mem;
|
||||
|
||||
int processors = (from entry in _channels
|
||||
let filename = entry.Item1.FullName
|
||||
let i = filename.LastIndexOf('_')
|
||||
let processor = filename.Substring(i + 1)
|
||||
select int.Parse(processor)
|
||||
).Max() + 1;
|
||||
int processors = (from entry in _channels
|
||||
let filename = entry.Item1.FullName
|
||||
let i = filename.LastIndexOf('_')
|
||||
let processor = filename.Substring(i + 1)
|
||||
select int.Parse(processor)
|
||||
).Max() + 1;
|
||||
|
||||
numberOfProcessors = processors;
|
||||
numberOfProcessors = processors;
|
||||
|
||||
// TODO: Need to cleanly separate clocks, but in practice there's only the one clock.
|
||||
CtfClock clock = _channels.First().Item2.Clocks.First();
|
||||
// TODO: Need to cleanly separate clocks, but in practice there's only the one clock.
|
||||
CtfClock clock = _channels.First().Item2.Clocks.First();
|
||||
|
||||
long firstEventTimestamp = (long)new ChannelList(_channels).First().Current.Timestamp;
|
||||
var channels = new ChannelList(_channels);
|
||||
if (channels.Count() == 0)
|
||||
throw new EndOfStreamException("No CTF Information found in ZIP file.");
|
||||
|
||||
_QPCFreq = (long)clock.Frequency;
|
||||
sessionStartTimeQPC = firstEventTimestamp;
|
||||
_syncTimeQPC = firstEventTimestamp;
|
||||
_syncTimeUTC = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds((clock.Offset - 1) / clock.Frequency);
|
||||
long firstEventTimestamp = (long)channels.First().Current.Timestamp;
|
||||
|
||||
_eventMapping = InitEventMap();
|
||||
_QPCFreq = (long)clock.Frequency;
|
||||
sessionStartTimeQPC = firstEventTimestamp;
|
||||
_syncTimeQPC = firstEventTimestamp;
|
||||
_syncTimeUTC = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds((clock.Offset - 1) / clock.Frequency);
|
||||
|
||||
_eventMapping = InitEventMap();
|
||||
success = true;
|
||||
#if DEBUG
|
||||
//// Uncomment for debug output.
|
||||
//_debugOut = File.CreateText("debug.txt");
|
||||
//_debugOut.AutoFlush = true;
|
||||
#endif
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
Dispose(); // This closes the ZIP file we opened. We don't want to leave it dangling.
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, ETWMapping> InitEventMap()
|
||||
@@ -577,7 +590,13 @@ namespace Microsoft.Diagnostics.Tracing
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_zip.Dispose();
|
||||
{
|
||||
if (_zip != null)
|
||||
{
|
||||
_zip.Dispose();
|
||||
_zip = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
//Marshal.FreeHGlobal(new IntPtr(_header));
|
||||
|
||||
Reference in New Issue
Block a user