mirror of
https://github.com/Kae7in/perfview
synced 2026-08-09 12:09:44 +00:00
A compilable state ready for some tests.
This commit is contained in:
@@ -18,7 +18,7 @@ using Microsoft.Diagnostics.Tracing.Stacks;
|
||||
//using PerfView.GuiUtilities;
|
||||
//using PerfViewModel;
|
||||
using Microsoft.Diagnostics.Symbols;
|
||||
////using Utilities;
|
||||
using Utilities;
|
||||
////using FastSerialization;
|
||||
using Microsoft.Diagnostics.Utilities;
|
||||
using Microsoft.Diagnostics.Tracing.Etlx;
|
||||
@@ -412,6 +412,7 @@ namespace PerfViewExtensibility
|
||||
/// </summary>
|
||||
public class ETLDataFile : DataFile
|
||||
{
|
||||
static string symbolPath;
|
||||
// We have the concept of a process to focus on. All STACK sampling will be filtered by this.
|
||||
// If null, then no filtering is done. Do try to limit to one process if possible as it makes
|
||||
// analysis and symbol lookup faster.
|
||||
@@ -603,114 +604,115 @@ namespace PerfViewExtensibility
|
||||
}
|
||||
#region private
|
||||
|
||||
////private static void UnZipIfNecessary(ref string inputFileName, TextWriter log, bool unpackInCache = true)
|
||||
////{
|
||||
//// if (string.Compare(Path.GetExtension(inputFileName), ".zip", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
//// {
|
||||
//// string unzipedEtlFile;
|
||||
//// if (unpackInCache)
|
||||
//// {
|
||||
//// unzipedEtlFile = CacheFiles.FindFile(inputFileName, ".etl");
|
||||
//// if (File.Exists(unzipedEtlFile) && File.GetLastWriteTimeUtc(inputFileName) <= File.GetLastWriteTimeUtc(unzipedEtlFile))
|
||||
//// {
|
||||
//// log.WriteLine("Found a existing unzipped file {0}", unzipedEtlFile);
|
||||
//// inputFileName = unzipedEtlFile;
|
||||
//// return;
|
||||
//// }
|
||||
//// }
|
||||
//// else
|
||||
//// {
|
||||
//// if (!inputFileName.EndsWith(".etl.zip", StringComparison.OrdinalIgnoreCase))
|
||||
//// throw new Exception("File does not end with the .etl.zip file extension");
|
||||
//// unzipedEtlFile = inputFileName.Substring(0, inputFileName.Length - 4);
|
||||
//// }
|
||||
private static void UnZipIfNecessary(ref string inputFileName, TextWriter log, bool unpackInCache = true)
|
||||
{
|
||||
if (string.Compare(Path.GetExtension(inputFileName), ".zip", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
string unzipedEtlFile;
|
||||
if (unpackInCache)
|
||||
{
|
||||
unzipedEtlFile = CacheFiles.FindFile(inputFileName, ".etl");
|
||||
if (File.Exists(unzipedEtlFile) && File.GetLastWriteTimeUtc(inputFileName) <= File.GetLastWriteTimeUtc(unzipedEtlFile))
|
||||
{
|
||||
log.WriteLine("Found a existing unzipped file {0}", unzipedEtlFile);
|
||||
inputFileName = unzipedEtlFile;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!inputFileName.EndsWith(".etl.zip", StringComparison.OrdinalIgnoreCase))
|
||||
throw new Exception("File does not end with the .etl.zip file extension");
|
||||
unzipedEtlFile = inputFileName.Substring(0, inputFileName.Length - 4);
|
||||
}
|
||||
|
||||
//// Stopwatch sw = Stopwatch.StartNew();
|
||||
//// log.WriteLine("[Decompressing {0}]", inputFileName);
|
||||
//// log.WriteLine("Generating output file {0}", unzipedEtlFile);
|
||||
//// var zipArchive = ZipFile.OpenRead(inputFileName);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
log.WriteLine("[Decompressing {0}]", inputFileName);
|
||||
log.WriteLine("Generating output file {0}", unzipedEtlFile);
|
||||
var zipArchive = ZipFile.OpenRead(inputFileName);
|
||||
|
||||
//// ZipArchiveEntry zippedEtlFile = null;
|
||||
//// string dirForPdbs = null;
|
||||
//// foreach (var entry in zipArchive.Entries)
|
||||
//// {
|
||||
//// if (entry.Length == 0) // Skip directories.
|
||||
//// continue;
|
||||
ZipArchiveEntry zippedEtlFile = null;
|
||||
string dirForPdbs = null;
|
||||
foreach (var entry in zipArchive.Entries)
|
||||
{
|
||||
if (entry.Length == 0) // Skip directories.
|
||||
continue;
|
||||
|
||||
//// var fullName = entry.FullName;
|
||||
//// if (fullName.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase))
|
||||
//// {
|
||||
//// fullName = fullName.Replace('/', '\\'); // normalize separator convention
|
||||
//// string pdbRelativePath = null;
|
||||
//// if (fullName.StartsWith(@"symbols\", StringComparison.OrdinalIgnoreCase))
|
||||
//// pdbRelativePath = fullName.Substring(8);
|
||||
//// else if (fullName.StartsWith(@"ngenpdbs\", StringComparison.OrdinalIgnoreCase))
|
||||
//// pdbRelativePath = fullName.Substring(9);
|
||||
//// else
|
||||
//// {
|
||||
//// var m = Regex.Match(fullName, @"^[^\\]+\.ngenpdbs?\\(.*)", RegexOptions.IgnoreCase);
|
||||
//// if (m.Success)
|
||||
//// pdbRelativePath = m.Groups[1].Value;
|
||||
//// else
|
||||
//// {
|
||||
//// log.WriteLine("WARNING: found PDB file that was not in a symbol server style directory, skipping extraction");
|
||||
//// log.WriteLine(" Unzip this ETL and PDB by hand to use this PDB.");
|
||||
//// continue;
|
||||
//// }
|
||||
//// }
|
||||
var fullName = entry.FullName;
|
||||
if (fullName.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fullName = fullName.Replace('/', '\\'); // normalize separator convention
|
||||
string pdbRelativePath = null;
|
||||
if (fullName.StartsWith(@"symbols\", StringComparison.OrdinalIgnoreCase))
|
||||
pdbRelativePath = fullName.Substring(8);
|
||||
else if (fullName.StartsWith(@"ngenpdbs\", StringComparison.OrdinalIgnoreCase))
|
||||
pdbRelativePath = fullName.Substring(9);
|
||||
else
|
||||
{
|
||||
var m = Regex.Match(fullName, @"^[^\\]+\.ngenpdbs?\\(.*)", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
pdbRelativePath = m.Groups[1].Value;
|
||||
else
|
||||
{
|
||||
log.WriteLine("WARNING: found PDB file that was not in a symbol server style directory, skipping extraction");
|
||||
log.WriteLine(" Unzip this ETL and PDB by hand to use this PDB.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//// if (dirForPdbs == null)
|
||||
//// {
|
||||
//// var inputDir = Path.GetDirectoryName(inputFileName);
|
||||
//// if (inputDir.Length == 0)
|
||||
//// inputDir = ".";
|
||||
//// var symbolsDir = Path.Combine(inputDir, "symbols");
|
||||
//// if (Directory.Exists(symbolsDir))
|
||||
//// dirForPdbs = symbolsDir;
|
||||
//// else
|
||||
//// dirForPdbs = new SymbolPath(App.SymbolPath).DefaultSymbolCache();
|
||||
//// log.WriteLine("Putting symbols in {0}", dirForPdbs);
|
||||
//// }
|
||||
if (dirForPdbs == null)
|
||||
{
|
||||
var inputDir = Path.GetDirectoryName(inputFileName);
|
||||
if (inputDir.Length == 0)
|
||||
inputDir = ".";
|
||||
var symbolsDir = Path.Combine(inputDir, "symbols");
|
||||
if (Directory.Exists(symbolsDir))
|
||||
dirForPdbs = symbolsDir;
|
||||
else
|
||||
dirForPdbs = ETLDataFile.symbolPath;
|
||||
////dirForPdbs = new SymbolPath(App.SymbolPath).DefaultSymbolCache();
|
||||
log.WriteLine("Putting symbols in {0}", dirForPdbs);
|
||||
}
|
||||
|
||||
//// var pdbTargetPath = Path.Combine(dirForPdbs, pdbRelativePath);
|
||||
//// var pdbTargetName = Path.GetFileName(pdbTargetPath);
|
||||
//// if (!File.Exists(pdbTargetPath) || (new System.IO.FileInfo(pdbTargetPath).Length != entry.Length))
|
||||
//// {
|
||||
//// var firstNameInRelativePath = pdbRelativePath;
|
||||
//// var sepIdx = firstNameInRelativePath.IndexOf('\\');
|
||||
//// if (sepIdx >= 0)
|
||||
//// firstNameInRelativePath = firstNameInRelativePath.Substring(0, sepIdx);
|
||||
//// var firstNamePath = Path.Combine(dirForPdbs, firstNameInRelativePath);
|
||||
//// if (File.Exists(firstNamePath))
|
||||
//// {
|
||||
//// log.WriteLine("Deleting pdb file that is in the way {0}", firstNamePath);
|
||||
//// FileUtilities.ForceDelete(firstNamePath);
|
||||
//// }
|
||||
//// log.WriteLine("Extracting PDB {0}", pdbRelativePath);
|
||||
//// AtomicExtract(entry, pdbTargetPath);
|
||||
//// }
|
||||
//// else
|
||||
//// log.WriteLine("PDB {0} exists, skipping", pdbRelativePath);
|
||||
//// }
|
||||
//// else if (fullName.EndsWith(".etl", StringComparison.OrdinalIgnoreCase))
|
||||
//// {
|
||||
//// if (zippedEtlFile != null)
|
||||
//// throw new Exception("The ZIP file does not have exactly 1 ETL file in it, can't auto-extract.");
|
||||
//// zippedEtlFile = entry;
|
||||
//// }
|
||||
//// }
|
||||
//// if (zippedEtlFile == null)
|
||||
//// throw new Exception("The ZIP file does not have any ETL files in it!");
|
||||
var pdbTargetPath = Path.Combine(dirForPdbs, pdbRelativePath);
|
||||
var pdbTargetName = Path.GetFileName(pdbTargetPath);
|
||||
if (!File.Exists(pdbTargetPath) || (new System.IO.FileInfo(pdbTargetPath).Length != entry.Length))
|
||||
{
|
||||
var firstNameInRelativePath = pdbRelativePath;
|
||||
var sepIdx = firstNameInRelativePath.IndexOf('\\');
|
||||
if (sepIdx >= 0)
|
||||
firstNameInRelativePath = firstNameInRelativePath.Substring(0, sepIdx);
|
||||
var firstNamePath = Path.Combine(dirForPdbs, firstNameInRelativePath);
|
||||
if (File.Exists(firstNamePath))
|
||||
{
|
||||
log.WriteLine("Deleting pdb file that is in the way {0}", firstNamePath);
|
||||
FileUtilities.ForceDelete(firstNamePath);
|
||||
}
|
||||
log.WriteLine("Extracting PDB {0}", pdbRelativePath);
|
||||
AtomicExtract(entry, pdbTargetPath);
|
||||
}
|
||||
else
|
||||
log.WriteLine("PDB {0} exists, skipping", pdbRelativePath);
|
||||
}
|
||||
else if (fullName.EndsWith(".etl", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (zippedEtlFile != null)
|
||||
throw new Exception("The ZIP file does not have exactly 1 ETL file in it, can't auto-extract.");
|
||||
zippedEtlFile = entry;
|
||||
}
|
||||
}
|
||||
if (zippedEtlFile == null)
|
||||
throw new Exception("The ZIP file does not have any ETL files in it!");
|
||||
|
||||
//// AtomicExtract(zippedEtlFile, unzipedEtlFile);
|
||||
//// log.WriteLine("Zipped size = {0:f3} MB Unzipped = {1:f3} MB",
|
||||
//// zippedEtlFile.CompressedLength / 1000000.0, zippedEtlFile.Length / 1000000.0);
|
||||
AtomicExtract(zippedEtlFile, unzipedEtlFile);
|
||||
log.WriteLine("Zipped size = {0:f3} MB Unzipped = {1:f3} MB",
|
||||
zippedEtlFile.CompressedLength / 1000000.0, zippedEtlFile.Length / 1000000.0);
|
||||
|
||||
//// File.SetLastWriteTime(unzipedEtlFile, DateTime.Now); // Touch the file
|
||||
//// inputFileName = unzipedEtlFile;
|
||||
//// log.WriteLine("Finished decompression, took {0:f0} sec", sw.Elapsed.TotalSeconds);
|
||||
//// }
|
||||
////}
|
||||
File.SetLastWriteTime(unzipedEtlFile, DateTime.Now); // Touch the file
|
||||
inputFileName = unzipedEtlFile;
|
||||
log.WriteLine("Finished decompression, took {0:f0} sec", sw.Elapsed.TotalSeconds);
|
||||
}
|
||||
}
|
||||
// Extract to a temp file and move so we get atomic update. May leave trash behind
|
||||
private static void AtomicExtract(ZipArchiveEntry zipEntry, string targetPath)
|
||||
{
|
||||
@@ -2892,7 +2894,7 @@ static class GuiModel
|
||||
source.Clr.AddCallbackForEvents<CodeSymbolsTraceData>(OnCodeSymbols);
|
||||
}
|
||||
|
||||
#region private
|
||||
#region private
|
||||
private void OnModuleLoad(ModuleLoadUnloadTraceData data)
|
||||
{
|
||||
Put(data.ProcessID, data.ModuleID, new CodeSymbolState(data, m_targetSymbolCachePath));
|
||||
@@ -2969,7 +2971,7 @@ static class GuiModel
|
||||
// Indexed by key;
|
||||
Dictionary<long, CodeSymbolState> m_symbolFiles;
|
||||
string m_targetSymbolCachePath;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3340,7 +3342,7 @@ static class GuiModel
|
||||
}
|
||||
#endif
|
||||
|
||||
#region private
|
||||
#region private
|
||||
/// <summary>
|
||||
/// Strips the file extension for files and if extension is .etl.zip removes both.
|
||||
/// </summary>
|
||||
@@ -3354,7 +3356,8 @@ static class GuiModel
|
||||
}
|
||||
return outputFileName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Gets the TraceEvents list of events from etlFile, applying a process filter if the /process argument is given.
|
||||
/// </summary>
|
||||
@@ -3362,18 +3365,19 @@ static class GuiModel
|
||||
{
|
||||
// If the user asked to focus on one process, do so.
|
||||
TraceEvents events;
|
||||
if (CommandLineArgs.Process != null)
|
||||
{
|
||||
var process = etlFile.Processes.LastProcessWithName(CommandLineArgs.Process);
|
||||
if (process == null)
|
||||
throw new Exception("Could not find process named " + CommandLineArgs.Process);
|
||||
events = process.EventsInProcess;
|
||||
}
|
||||
else
|
||||
////if (CommandLineArgs.Process != null)
|
||||
////{
|
||||
//// var process = etlFile.Processes.LastProcessWithName(CommandLineArgs.Process);
|
||||
//// if (process == null)
|
||||
//// throw new Exception("Could not find process named " + CommandLineArgs.Process);
|
||||
//// events = process.EventsInProcess;
|
||||
////}
|
||||
////else
|
||||
events = etlFile.TraceLog.Events; // All events in the process.
|
||||
return events;
|
||||
}
|
||||
|
||||
#if false
|
||||
#region private
|
||||
/// <summary>
|
||||
/// Save the CPU stacks for an ETL file into a perfView.xml.zip file.
|
||||
/// </summary>
|
||||
@@ -3636,7 +3640,7 @@ static class GuiModel
|
||||
|
||||
return (startEvent.Flags & ProcessFlags.PackageFullName) != 0;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
using System.Diagnostics;
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// This file is best viewed using outline mode (Ctrl-M Ctrl-O)
|
||||
//
|
||||
// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
|
||||
// It is available from http://www.codeplex.com/hyperAddin
|
||||
//
|
||||
using System.Text;
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
/// <summary>
|
||||
/// A cheap version of List(T). The idea is to make it as cheap as if you did it 'by hand' using an array and
|
||||
/// an int which represents the logical charCount. It is a struct to avoid an extra pointer dereference, so this
|
||||
/// is really meant to be embedded in other structures.
|
||||
/// </summary>
|
||||
#if GROWABLEARRAY_PUBLIC
|
||||
public
|
||||
#endif
|
||||
struct GrowableArray<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a growable array with the given initial size it will grow as needed. There is also the
|
||||
/// default constructor that assumes initialSize of 0 (and does not actually allocate the array.
|
||||
/// </summary>
|
||||
/// <param name="initialSize"></param>
|
||||
public GrowableArray(int initialSize)
|
||||
{
|
||||
array = new T[initialSize];
|
||||
arrayLength = 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Fetch the element at the given index. Will throw an IndexOutOfRange exception otherwise
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert((uint)index < (uint)arrayLength);
|
||||
return array[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
Debug.Assert((uint)index < (uint)arrayLength);
|
||||
array[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of elements in the array
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return arrayLength;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (arrayLength < value)
|
||||
{
|
||||
if (array != null && value <= array.Length)
|
||||
{
|
||||
// Null out the entries.
|
||||
for (int i = arrayLength; i < value; i++)
|
||||
array[i] = default(T);
|
||||
}
|
||||
else
|
||||
Realloc(value);
|
||||
}
|
||||
arrayLength = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove all elements in the array.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
arrayLength = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an item at the end of the array, growing as necessary.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Add(T item)
|
||||
{
|
||||
if (array == null || arrayLength >= array.Length)
|
||||
Realloc(0);
|
||||
array[arrayLength++] = item;
|
||||
}
|
||||
/// <summary>
|
||||
/// Add all items 'items' to the end of the array, growing as necessary.
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public void AddRange(IEnumerable<T> items)
|
||||
{
|
||||
foreach (T item in items)
|
||||
Add(item);
|
||||
}
|
||||
/// <summary>
|
||||
/// Insert 'item' directly at 'index', shifting all items >= index up. 'index' can be code:Count in
|
||||
/// which case the item is appended to the end. Larger indexes are not allowed.
|
||||
/// </summary>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
if ((uint)index > (uint)arrayLength)
|
||||
throw new IndexOutOfRangeException();
|
||||
if (array == null || arrayLength >= array.Length)
|
||||
Realloc(0);
|
||||
|
||||
// Shift everything up to make room.
|
||||
for (int idx = arrayLength; index < idx; --idx)
|
||||
array[idx] = array[idx - 1];
|
||||
|
||||
// insert the element
|
||||
array[index] = item;
|
||||
arrayLength++;
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove 'count' elements starting at 'index'
|
||||
/// </summary>
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
if (count < 0)
|
||||
throw new ArgumentException("count can't be negative");
|
||||
|
||||
if ((uint)index >= (uint)arrayLength)
|
||||
throw new IndexOutOfRangeException();
|
||||
Debug.Assert(index + count <= arrayLength); // If you violate this it does not hurt
|
||||
|
||||
// Shift everything down.
|
||||
for (int endIndex = index + count; endIndex < arrayLength; endIndex++)
|
||||
array[index++] = array[endIndex];
|
||||
|
||||
arrayLength = index;
|
||||
}
|
||||
|
||||
// Support for using an array as a map from int to T.
|
||||
/// <summary>
|
||||
/// Sets the 'index' element to 'value' growing the array if necessary (filling in default values if necessary).
|
||||
/// </summary>
|
||||
public void Set(int index, T value)
|
||||
{
|
||||
if (index >= Count)
|
||||
Count = index + 1;
|
||||
|
||||
this[index] = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the value at 'index'. Never fails, will return 'default' if out of range.
|
||||
/// </summary>
|
||||
public T Get(int index)
|
||||
{
|
||||
if ((uint)index < (uint)arrayLength)
|
||||
return this[index];
|
||||
else
|
||||
return default(T);
|
||||
}
|
||||
|
||||
// Support for stack-like operations
|
||||
/// <summary>
|
||||
/// Returns true if there are no elements in the array.
|
||||
/// </summary>
|
||||
public bool Empty { get { return arrayLength == 0; } }
|
||||
/// <summary>
|
||||
/// Remove the last element added and return it. Will throw if there are no elements.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public T Pop()
|
||||
{
|
||||
T ret = array[arrayLength - 1]; // Will cause index out of range exception
|
||||
--arrayLength;
|
||||
return ret;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the last element added Will throw if there are no elements.
|
||||
/// </summary>
|
||||
public T Top { get { return array[arrayLength - 1]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Trims the size of the array so that no more than 'maxWaste' slots are wasted. Useful when
|
||||
/// you know that the array has stopped growing.
|
||||
/// </summary>
|
||||
public void Trim(int maxWaste)
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
if (array.Length > arrayLength + maxWaste)
|
||||
{
|
||||
if (arrayLength == 0)
|
||||
array = null;
|
||||
else
|
||||
{
|
||||
T[] newArray = new T[arrayLength];
|
||||
Array.Copy(array, newArray, arrayLength);
|
||||
array = newArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Growable array was initialized by the default constructor
|
||||
/// which has no capacity (and thus will cause growth on the first addition).
|
||||
/// This method allows you to lazily set the compacity of your GrowableArray by
|
||||
/// testing if it is of EmtpyCapacity, and if so set it to some useful capacity.
|
||||
/// This avoids unecessary reallocs to get to a reasonable capacity.
|
||||
/// </summary>
|
||||
public bool EmptyCapacity { get { return array == null; } }
|
||||
|
||||
/// <summary>
|
||||
/// A string representing the array. Only intended for debugging.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("GrowableArray(Count=").Append(Count).Append(", [").AppendLine();
|
||||
for (int i = 0; i < Count; i++)
|
||||
sb.Append(" ").Append(this[i].ToString()).AppendLine();
|
||||
sb.Append(" ])");
|
||||
return sb.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// Should return -1 if x is less than elem.
|
||||
/// TODO FIX NOW use Func instead.
|
||||
/// </summary>
|
||||
public delegate int Comparison<Key>(Key x, T elem);
|
||||
/// <summary>
|
||||
/// Sets 'index' to the the smallest index such that all elements with index > 'idx' are > key. If
|
||||
/// index does not match any elements a new element should always be placed AFTER index. Note that this
|
||||
/// means that index may be -1 if the new element belongs in the first position.
|
||||
///
|
||||
/// Returns true if the return index matched exactly (success)
|
||||
///
|
||||
/// TODO FIX NOW harmonize with List.BinarySearch
|
||||
/// </summary>
|
||||
public bool BinarySearch<Key>(Key key, out int index, Comparison<Key> comparison)
|
||||
{
|
||||
// binary search
|
||||
int low = 0;
|
||||
int high = arrayLength;
|
||||
int lastLowCompare = -1; // If this number == 0 we had a match.
|
||||
|
||||
if (high > 0)
|
||||
{
|
||||
// The invariant in this loop is that
|
||||
// [0..low) <= key < [high..Count)
|
||||
for (; ; )
|
||||
{
|
||||
int mid = (low + high) / 2;
|
||||
int compareResult = comparison(key, array[mid]);
|
||||
if (compareResult >= 0) // key >= array[mid], move low up
|
||||
{
|
||||
lastLowCompare = compareResult; // remember this result, as it indicates a successful match.
|
||||
if (mid == low)
|
||||
break;
|
||||
low = mid;
|
||||
}
|
||||
else // key < array[mid], move high down
|
||||
{
|
||||
high = mid;
|
||||
if (mid == low)
|
||||
break;
|
||||
}
|
||||
|
||||
// Note that if compareResults == 0, we don't return the match eagerly because there could be
|
||||
// multiple elements that match. We want the match with the largest possible index, so we need
|
||||
// to continue the search until the valid range drops to 0
|
||||
}
|
||||
}
|
||||
|
||||
if (lastLowCompare < 0) // key < array[low], subtract 1 to indicate that new element goes BEFORE low.
|
||||
{
|
||||
Debug.Assert(low == 0); // can only happen if it is the first element
|
||||
--low;
|
||||
}
|
||||
index = low;
|
||||
|
||||
Debug.Assert(index == -1 || comparison(key, array[index]) >= 0); // element smaller or equal to key
|
||||
Debug.Assert(index + 1 >= Count || comparison(key, array[index + 1]) < 0); // The next element is strictly bigger.
|
||||
Debug.Assert((lastLowCompare != 0) || (comparison(key, array[index]) == 0)); // If we say there is a match, there is.
|
||||
return (lastLowCompare == 0);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sort the range starting at 'index' of length 'count' using 'comparision' in assending order
|
||||
/// </summary>
|
||||
public void Sort(int index, int count, System.Comparison<T> comparison)
|
||||
{
|
||||
Debug.Assert(index + count <= arrayLength);
|
||||
if (count > 0)
|
||||
Array.Sort<T>(array, index, count, new FunctorComparer<T>(comparison));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sort the whole array using 'comparison' in ascending order
|
||||
/// </summary>
|
||||
public void Sort(System.Comparison<T> comparison)
|
||||
{
|
||||
if (array != null)
|
||||
Array.Sort<T>(array, 0, arrayLength, new FunctorComparer<T>(comparison));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes 'func' for each element in the GrowableArray and returns a GrowableArray
|
||||
/// for the result.
|
||||
/// </summary>
|
||||
public GrowableArray<T1> Foreach<T1>(Func<T, T1> func)
|
||||
{
|
||||
var ret = new GrowableArray<T1>();
|
||||
ret.Count = Count;
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
ret[i] = func(array[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a linear search starting at 'startIndex'. If found return true and the index in 'index'.
|
||||
/// It is legal that 'startIndex' is greater than the charCount, in which case, the search returns false
|
||||
/// immediately. This allows a nice loop to find all items matching a pattern.
|
||||
/// </summary>
|
||||
public bool Search<Key>(Key key, int startIndex, Comparison<Key> compare, ref int index)
|
||||
{
|
||||
for (int i = startIndex; i < arrayLength; i++)
|
||||
{
|
||||
if (compare(key, array[i]) == 0)
|
||||
{
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the underlying array. Should not be used most of the time!
|
||||
/// </summary>
|
||||
public T[] UnderlyingArray { get { return array; } }
|
||||
#region private
|
||||
private void Realloc(int minSize)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
if (minSize < 16)
|
||||
minSize = 16;
|
||||
array = new T[minSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
int expandSize = array.Length * 3 / 2 + 8;
|
||||
if (minSize < expandSize)
|
||||
minSize = expandSize;
|
||||
T[] newArray = new T[minSize];
|
||||
Array.Copy(array, newArray, arrayLength);
|
||||
array = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
T[] array;
|
||||
int arrayLength;
|
||||
#endregion
|
||||
|
||||
#region TESTING
|
||||
// Unit testing. It is reasonable coverage, but concentrates on BinarySearch as that is the one that is
|
||||
// easy to get wrong.
|
||||
#if TESTING
|
||||
public static void TestGrowableArray()
|
||||
{
|
||||
GrowableArray<float> testArray = new GrowableArray<float>();
|
||||
for (float i = 1.1F; i < 10; i += 2)
|
||||
{
|
||||
int successes = TestBinarySearch(testArray);
|
||||
Debug.Assert(successes == ((int)i) / 2);
|
||||
testArray.Add(i);
|
||||
}
|
||||
|
||||
for (float i = 0.1F; i < 11; i += 2)
|
||||
{
|
||||
int index;
|
||||
bool result = testArray.BinarySearch(i, out index, delegate(float key, float elem) { return (int)key - (int)elem; });
|
||||
Debug.Assert(!result);
|
||||
testArray.InsertAt(index + 1, i);
|
||||
}
|
||||
|
||||
int lastSuccesses = TestBinarySearch(testArray);
|
||||
Debug.Assert(lastSuccesses == 11);
|
||||
|
||||
for (float i = 0; i < 11; i += 1)
|
||||
{
|
||||
int index;
|
||||
bool result = testArray.BinarySearch(i, out index, delegate(float key, float elem) { return (int)key - (int)elem; });
|
||||
Debug.Assert(result);
|
||||
testArray.InsertAt(index + 1, i);
|
||||
}
|
||||
|
||||
lastSuccesses = TestBinarySearch(testArray);
|
||||
Debug.Assert(lastSuccesses == 11);
|
||||
|
||||
// We always get the last one when the equality comparision allows multiple items to match.
|
||||
for (float i = 0; i < 11; i += 1)
|
||||
{
|
||||
int index;
|
||||
bool result = testArray.BinarySearch(i, out index, delegate(float key, float elem) { return (int)key - (int)elem; });
|
||||
Debug.Assert(result);
|
||||
Debug.Assert(i == testArray[index]);
|
||||
}
|
||||
Console.WriteLine("Done");
|
||||
}
|
||||
private static int TestBinarySearch(GrowableArray<float> testArray)
|
||||
{
|
||||
int successes = 0;
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
int index;
|
||||
if (testArray.BinarySearch(i, out index, delegate(float key, float elem) { return (int)key - (int)elem; }))
|
||||
{
|
||||
successes++;
|
||||
Debug.Assert((int)testArray[index] == i);
|
||||
}
|
||||
else
|
||||
Debug.Assert(index + 1 <= testArray.Count);
|
||||
}
|
||||
return successes;
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
// This allows 'foreach' to work. We are not a true IEnumerable however.
|
||||
/// <summary>
|
||||
/// Implementation of foreach protocol
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public GrowableArrayEnumerator GetEnumerator() { return new GrowableArrayEnumerator(this); }
|
||||
/// <summary>
|
||||
/// Enumerator for foreach interface
|
||||
/// </summary>
|
||||
public struct GrowableArrayEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of IEnumerable interface
|
||||
/// </summary>
|
||||
public T Current
|
||||
{
|
||||
get { return array[cur]; }
|
||||
}
|
||||
/// <summary>
|
||||
/// implementation of IEnumerable interface
|
||||
/// </summary>
|
||||
public bool MoveNext()
|
||||
{
|
||||
cur++;
|
||||
return cur < end;
|
||||
}
|
||||
|
||||
#region private
|
||||
internal GrowableArrayEnumerator(GrowableArray<T> growableArray)
|
||||
{
|
||||
cur = -1;
|
||||
end = growableArray.arrayLength;
|
||||
array = growableArray.array;
|
||||
}
|
||||
int cur;
|
||||
int end;
|
||||
T[] array;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#region private classes
|
||||
internal class FunctorComparer<T> : IComparer<T>
|
||||
{
|
||||
public FunctorComparer(Comparison<T> comparison) { this.comparison = comparison; }
|
||||
public int Compare(T x, T y) { return comparison(x, y); }
|
||||
|
||||
private Comparison<T> comparison;
|
||||
};
|
||||
#endregion
|
||||
}
|
||||
@@ -7,7 +7,8 @@ using System.IO.Compression;
|
||||
using System.IO;
|
||||
using Microsoft.Diagnostics.Tracing.Stacks;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization.Json;
|
||||
////using System.Runtime.Serialization.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Diagnostics.Tracing.StackSources
|
||||
{
|
||||
@@ -132,7 +133,7 @@ namespace Diagnostics.Tracing.StackSources
|
||||
var zipArchive = new ZipArchive(dataStream);
|
||||
var entries = zipArchive.Entries;
|
||||
if (entries.Count != 1)
|
||||
throw new ApplicationException("The ZIP file does not have exactly 1 XML file in it,");
|
||||
throw new Exception("The ZIP file does not have exactly 1 XML file in it,");
|
||||
xmlStream = entries[0].Open();
|
||||
unzippedName = fileName.Substring(0, fileName.Length - 4);
|
||||
}
|
||||
@@ -145,7 +146,8 @@ namespace Diagnostics.Tracing.StackSources
|
||||
else
|
||||
{
|
||||
XmlReaderSettings settings = new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = true };
|
||||
reader = XmlTextReader.Create(xmlStream, settings);
|
||||
////reader = XmlTextReader.Create(xmlStream, settings);
|
||||
reader = XmlReader.Create(xmlStream);
|
||||
}
|
||||
|
||||
reader.Read(); // Skip the StackWindow element.
|
||||
@@ -253,7 +255,9 @@ namespace Diagnostics.Tracing.StackSources
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
|
||||
static private XmlReader GetJsonReader(Stream dataStream)
|
||||
{
|
||||
return JsonReaderWriterFactory.CreateJsonReader(dataStream, new XmlDictionaryReaderQuotas());
|
||||
////return JsonReaderWriterFactory.CreateJsonReader(dataStream, new XmlRead XmlDictionaryReaderQuotas());
|
||||
XmlReader reader = XmlReader.Create(dataStream);
|
||||
return reader;
|
||||
}
|
||||
|
||||
private void Read(XmlReader reader)
|
||||
|
||||
Reference in New Issue
Block a user