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:
Vance Morrison
2016-06-15 10:11:06 -07:00
parent cd5ae131be
commit 2db0489337
6 changed files with 94 additions and 88 deletions
@@ -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)
+35 -55
View File
@@ -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>
+1 -1
View File
@@ -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")]
+7
View File
@@ -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
+49 -30
View File
@@ -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));