mirror of
https://github.com/Kae7in/perfview
synced 2026-08-09 12:09:44 +00:00
Re-add of everything that got screwed over.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using PerfViewExtensibility;
|
||||
using Microsoft.Diagnostics.Tracing;
|
||||
using Microsoft.Diagnostics.Tracing.Etlx;
|
||||
////Microsoft.Diagnostics.Tracing.Parsers.Kernel;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
namespace PerfViewService.Controllers
|
||||
{
|
||||
[Route("api/[controller]/open")]
|
||||
public class DataController : Controller
|
||||
{
|
||||
// GET: api/data
|
||||
[HttpGet]
|
||||
public string Get()
|
||||
{
|
||||
// Dictionary containing entire hierarchy
|
||||
Dictionary<string, object> tree = new Dictionary<string, object>();
|
||||
tree.Add("status", "OK");
|
||||
|
||||
// Top level items
|
||||
List<object> items = new List<object>();
|
||||
tree.Add("items", items);
|
||||
|
||||
// First top level item
|
||||
Dictionary<string, object> item = new Dictionary<string, object>();
|
||||
items.Add(item);
|
||||
item.Add("name", "TraceInfo");
|
||||
item.Add("type", "htmlReport");
|
||||
|
||||
// Second top level item
|
||||
item = new Dictionary<string, object>();
|
||||
items.Add(item);
|
||||
item.Add("name", "Cpu Stacks");
|
||||
item.Add("type", "stackViewer");
|
||||
|
||||
// First top level folder
|
||||
item = new Dictionary<string, object>();
|
||||
items.Add(item);
|
||||
item.Add("name", "Test Folder 1");
|
||||
item.Add("type", "file");
|
||||
List<object> subItems = new List<object>();
|
||||
item.Add("items", subItems);
|
||||
|
||||
// Folder subitem
|
||||
Dictionary<string, object> subItem = new Dictionary<string, object>();
|
||||
subItems.Add(subItem);
|
||||
subItem.Add("name", "Dump");
|
||||
subItem.Add("type", "otherType");
|
||||
|
||||
// Folder subitem
|
||||
subItem = new Dictionary<string, object>();
|
||||
subItems.Add(subItem);
|
||||
subItem.Add("name", "Trace");
|
||||
subItem.Add("type", "otherType");
|
||||
|
||||
// Third top level item
|
||||
item = new Dictionary<string, object>();
|
||||
items.Add(item);
|
||||
item.Add("name", "Another Top Level Item");
|
||||
item.Add("type", "otherType");
|
||||
|
||||
string json = JsonConvert.SerializeObject(tree, Formatting.Indented);
|
||||
|
||||
if (json == null)
|
||||
{
|
||||
// Form a response using HttpResponseException
|
||||
}
|
||||
|
||||
string etlFileName = @"C:\Users\t-kahoop\Development\perfview\src\PerfView\bin\Debug\PerfViewData.etl.zip";
|
||||
var etlFile = PerfViewExtensibility.CommandEnvironment.OpenETLFile(etlFileName);
|
||||
TraceEvents events = etlFile.TraceLog.Events;
|
||||
var traceEventSource = events.GetSource();
|
||||
traceEventSource.Process();
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// GET api/values/5
|
||||
[HttpGet("{id}")]
|
||||
public string Get(int id)
|
||||
{
|
||||
return "value";
|
||||
}
|
||||
|
||||
// POST api/values
|
||||
[HttpPost]
|
||||
public void Post([FromBody]string value)
|
||||
{
|
||||
}
|
||||
|
||||
// PUT api/values/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody]string value)
|
||||
{
|
||||
}
|
||||
|
||||
// DELETE api/values/5
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using Microsoft.Diagnostics.Utilities;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Some applications need to make files that are associated with the application
|
||||
/// but also have affinity with other files on the disk. This class helps manage this
|
||||
/// </summary>
|
||||
static class CacheFiles
|
||||
{
|
||||
public static float KeepTimeInDays { get; set; }
|
||||
public static string CacheDir
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_CacheDir == null)
|
||||
{
|
||||
////var exeAssembly = Assembly.GetExecutingAssembly();
|
||||
var exeAssembly = typeof(CacheFiles).GetTypeInfo().Assembly;
|
||||
var exePath = exeAssembly.ManifestModule.FullyQualifiedName;
|
||||
var exeName = Path.GetFileNameWithoutExtension(exePath);
|
||||
var tempDir = Environment.GetEnvironmentVariable("TEMP");
|
||||
if (tempDir == null)
|
||||
tempDir = ".";
|
||||
s_CacheDir = Path.Combine(tempDir, exeName);
|
||||
Directory.CreateDirectory(s_CacheDir);
|
||||
}
|
||||
return s_CacheDir;
|
||||
}
|
||||
set { s_CacheDir = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a path name for the file 'baseFilePath' (which can be a path name to anywhere).
|
||||
/// which has the extension 'extension'. It will always return something in 'CacheDir'
|
||||
/// and thus might go away.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that the file 'baseFilePath' is assumed to exist.
|
||||
/// </remarks>
|
||||
public static string FindFile(string baseFilePath, string extension = "")
|
||||
{
|
||||
// TODO FIX NOW add collision detection
|
||||
|
||||
// We expect the original file to exist
|
||||
Debug.Assert(File.Exists(baseFilePath));
|
||||
|
||||
var baseFileName = Path.GetFileNameWithoutExtension(baseFilePath);
|
||||
var baseFileInfo = new FileInfo(baseFilePath);
|
||||
|
||||
// The hash is a combination of full path, size and last write timestamp
|
||||
var hashData = Tuple.Create(Path.GetFullPath(baseFilePath), baseFileInfo.Length, baseFileInfo.LastWriteTimeUtc);
|
||||
int hash = hashData.GetHashCode();
|
||||
|
||||
string ret = Path.Combine(CacheDir, baseFileName + "_" + hash.ToString("x") + extension);
|
||||
if (File.Exists(ret))
|
||||
{
|
||||
// See if it is up to date.
|
||||
if (File.GetLastWriteTimeUtc(ret) < baseFileInfo.LastWriteTimeUtc)
|
||||
FileUtilities.ForceDelete(ret);
|
||||
else
|
||||
{
|
||||
// Set the last access time so we can clean up files based on their last usage.
|
||||
// However if someone else is actual using the file, we don't want an 'in use'
|
||||
// exception, so treat this part as optional.
|
||||
try { File.SetLastAccessTimeUtc(ret, DateTime.UtcNow); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
if (!s_didCleanup)
|
||||
{
|
||||
s_didCleanup = true;
|
||||
Cleanup();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static public void Cleanup()
|
||||
{
|
||||
CleanupDirectory(CacheDir, KeepTimeInDays);
|
||||
}
|
||||
|
||||
static public void CleanupDirectory(string directory, float keepTimeInDays)
|
||||
{
|
||||
if (keepTimeInDays == 0)
|
||||
keepTimeInDays = 5;
|
||||
|
||||
var nowUTC = DateTime.UtcNow;
|
||||
foreach (var file in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if ((nowUTC - File.GetLastAccessTimeUtc(file)).TotalDays > keepTimeInDays)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileUtilities.ForceDelete(file);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the output file is up to date with respect to the input file (exists and created after it).
|
||||
/// </summary>
|
||||
public static bool UpToDate(string outputFile, string inputFile)
|
||||
{
|
||||
return File.Exists(outputFile) && File.GetLastWriteTimeUtc(inputFile) <= File.GetLastWriteTimeUtc(outputFile);
|
||||
}
|
||||
|
||||
#region private
|
||||
static string s_CacheDir;
|
||||
static bool s_didCleanup;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/****************************************************************************/
|
||||
/* DirectoryUtilities.cs */
|
||||
/****************************************************************************/
|
||||
|
||||
/* Copyright (c) Microsoft Corporation. All rights reserved. */
|
||||
/* AUTHOR: Vance Morrison
|
||||
* Date : 10/20/2007 */
|
||||
/****************************************************************************/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
/******************************************************************************/
|
||||
/// <summary>
|
||||
/// General purpose utilities dealing with archiveFile system directories.
|
||||
/// </summary>
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
static class DirectoryUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// SafeCopy sourceDirectory to directoryToVersion recursively. The target directory does
|
||||
/// no need to exist
|
||||
/// </summary>
|
||||
public static void Copy(string sourceDirectory, string targetDirectory)
|
||||
{
|
||||
Copy(sourceDirectory, targetDirectory, SearchOption.AllDirectories);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SafeCopy all files from sourceDirectory to directoryToVersion. If searchOptions == AllDirectories
|
||||
/// then the copy is recursive, otherwise it is just one level. The target directory does not
|
||||
/// need to exist.
|
||||
/// </summary>
|
||||
public static void Copy(string sourceDirectory, string targetDirectory, SearchOption searchOptions)
|
||||
{
|
||||
if (!Directory.Exists(targetDirectory))
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
|
||||
foreach (string sourceFile in Directory.GetFiles(sourceDirectory))
|
||||
{
|
||||
string targetFile = Path.Combine(targetDirectory, Path.GetFileName(sourceFile));
|
||||
FileUtilities.ForceCopy(sourceFile, targetFile);
|
||||
}
|
||||
if (searchOptions == SearchOption.AllDirectories)
|
||||
{
|
||||
foreach (string sourceDir in Directory.GetDirectories(sourceDirectory))
|
||||
{
|
||||
string targetDir = Path.Combine(targetDirectory, Path.GetFileName(sourceDir));
|
||||
Copy(sourceDir, targetDir, searchOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean is sort of a 'safe' recursive delete of a directory. It either deletes the
|
||||
/// files or moves them to '*.deleting' names. It deletes directories that are completely
|
||||
/// empty. Thus it will do a recursive delete when that is possible. There will only
|
||||
/// be *.deleting files after this returns. It returns the number of files and directories
|
||||
/// that could not be deleted.
|
||||
/// </summary>
|
||||
public static int Clean(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
return 0;
|
||||
|
||||
int ret = 0;
|
||||
foreach (string file in Directory.GetFiles(directory))
|
||||
if (!FileUtilities.ForceDelete(file))
|
||||
ret++;
|
||||
|
||||
foreach (string subDir in Directory.GetDirectories(directory))
|
||||
ret += Clean(subDir);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the oldest directories directly under 'directoryPath' so that
|
||||
/// only 'numberToKeep' are left.
|
||||
/// </summary>
|
||||
/// <param variable="directoryPath">Directory to removed old files from.</param>
|
||||
/// <param variable="numberToKeep">The number of files to keep.</param>
|
||||
/// <returns> true if there were no errors deleting files</returns>
|
||||
public static bool DeleteOldest(string directoryPath, int numberToKeep)
|
||||
{
|
||||
if (!Directory.Exists(directoryPath))
|
||||
return true;
|
||||
|
||||
string[] dirs = Directory.GetDirectories(directoryPath);
|
||||
int numToDelete = dirs.Length - numberToKeep;
|
||||
if (numToDelete <= 0)
|
||||
return true;
|
||||
|
||||
Array.Sort<string>(dirs, delegate(string x, string y)
|
||||
{
|
||||
return File.GetLastWriteTimeUtc(x).CompareTo(File.GetLastWriteTimeUtc(y));
|
||||
});
|
||||
|
||||
bool ret = true;
|
||||
for (int i = 0; i < numToDelete; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(dirs[i]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO trace message;
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DirectoryUtilities.GetFiles is basicaly the same as Directory.GetFiles
|
||||
/// however it returns IEnumerator, which means that it lazy. This is very important
|
||||
/// for large directory trees. A searchPattern can be specified (Windows wildcard conventions)
|
||||
/// that can be used to filter the set of archiveFile names returned.
|
||||
///
|
||||
/// Suggested Usage
|
||||
///
|
||||
/// foreach(string fileName in DirectoryUtilities.GetFiles("c:\", "*.txt")){
|
||||
/// Console.WriteLine(fileName);
|
||||
/// }
|
||||
///
|
||||
/// </summary>
|
||||
/// <param variable="directoryPath">The base directory to enumerate</param>
|
||||
/// <param variable="searchPattern">A pattern to filter the names (windows filename wildcards * ?)</param>
|
||||
/// <param variable="searchOptions">Indicate if the search is recursive or not. </param>
|
||||
/// <returns>The enumerator for all archiveFile names in the directory (recursively). </returns>
|
||||
public static IEnumerable<string> GetFiles(string directoryPath, string searchPattern, SearchOption searchOptions)
|
||||
{
|
||||
|
||||
string[] fileNames = Directory.GetFiles(directoryPath, searchPattern, SearchOption.TopDirectoryOnly);
|
||||
Array.Sort<string>(fileNames, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string fileName in fileNames)
|
||||
{
|
||||
yield return fileName;
|
||||
}
|
||||
|
||||
if (searchOptions == SearchOption.AllDirectories)
|
||||
{
|
||||
string[] subDirNames = Directory.GetDirectories(directoryPath);
|
||||
Array.Sort<string>(subDirNames);
|
||||
foreach (string subDir in subDirNames)
|
||||
{
|
||||
foreach (string fileName in DirectoryUtilities.GetFiles(subDir, searchPattern, searchOptions))
|
||||
{
|
||||
yield return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a lazy enumerable for every path in 'directoryName' that matchs 'searchPattern' (default is *)MO
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetFiles(string directoryName, string searchPattern = "*")
|
||||
{
|
||||
return GetFiles(directoryName, searchPattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,259 @@
|
||||
/****************************************************************************/
|
||||
/* FileUtilities.cs */
|
||||
/****************************************************************************/
|
||||
|
||||
/* Copyright (c) Microsoft Corporation. All rights reserved. */
|
||||
/* AUTHOR: Vance Morrison
|
||||
* Date : 10/20/2007 */
|
||||
/****************************************************************************/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
/******************************************************************************/
|
||||
/// <summary>
|
||||
/// General purpose utilities dealing with archiveFile system files.
|
||||
/// </summary>
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
static class FileUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// GetLines works much like File.ReadAllLines, however instead of returning a
|
||||
/// array of lines, it returns a IEnumerable so that the archiveFile is not read all
|
||||
/// at once. This allows 'foreach' syntax to be used on very large files.
|
||||
///
|
||||
/// Suggested Usage
|
||||
///
|
||||
/// foreach(string lineNumber in FileUtilities.GetLines("largeFile.txt")){
|
||||
/// Console.WriteLine(lineNumber);
|
||||
/// }
|
||||
/// </summary>
|
||||
/// <param variable="fileName">The base directory to enumerate.</param>
|
||||
/// <returns>The enumerator for all lines in the archiveFile.</returns>
|
||||
public static IEnumerable<string> ReadAllLines(string fileName)
|
||||
{
|
||||
StreamReader stream = File.OpenText(fileName);
|
||||
while (!stream.EndOfStream)
|
||||
{
|
||||
yield return stream.ReadLine();
|
||||
}
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given archiveFile specifications possibly with wildcards in them
|
||||
/// Returns an enumerator that returns each expanded archiveFile name in turn.
|
||||
///
|
||||
/// If searchOpt is AllDirectories it does a recursive match.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> ExpandWildcards(string[] fileSpecifications, SearchOption searchOpt = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
foreach (string fileSpec in fileSpecifications)
|
||||
{
|
||||
string dir = Path.GetDirectoryName(fileSpec);
|
||||
if (dir.Length == 0)
|
||||
dir = ".";
|
||||
string file = Path.GetFileName(fileSpec);
|
||||
foreach (string fileName in DirectoryUtilities.GetFiles(dir, file, searchOpt))
|
||||
yield return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete works much like File.Delete, except that it will succeed if the
|
||||
/// archiveFile does not exist, and will rename the archiveFile so that even if the archiveFile
|
||||
/// is locked the original archiveFile variable will be made available.
|
||||
///
|
||||
/// It renames the archiveFile with a '[num].deleting'. These files might be left
|
||||
/// behind.
|
||||
///
|
||||
/// It returns true if it was completely successful. If there is a *.deleting
|
||||
/// archiveFile left behind, it returns false.
|
||||
/// </summary>
|
||||
/// <param variable="fileName">The variable of the archiveFile to delete</param>
|
||||
public static bool ForceDelete(string fileName)
|
||||
{
|
||||
if (Directory.Exists(fileName))
|
||||
return DirectoryUtilities.Clean(fileName) != 0;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
return true;
|
||||
|
||||
// First move the archiveFile out of the way, so that even if it is locked
|
||||
// The original archiveFile is still gone.
|
||||
string fileToDelete = fileName;
|
||||
bool tryToDeleteOtherFiles = true;
|
||||
if (!fileToDelete.EndsWith(".deleting", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tryToDeleteOtherFiles = false;
|
||||
int i = 0;
|
||||
for (i = 0; ; i++)
|
||||
{
|
||||
fileToDelete = fileName + "." + i.ToString() + ".deleting";
|
||||
if (!File.Exists(fileToDelete))
|
||||
break;
|
||||
tryToDeleteOtherFiles = true;
|
||||
}
|
||||
try
|
||||
{
|
||||
File.Move(fileName, fileToDelete);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
fileToDelete = fileName;
|
||||
}
|
||||
}
|
||||
bool ret = TryDelete(fileToDelete);
|
||||
if (tryToDeleteOtherFiles)
|
||||
{
|
||||
// delete any old *.deleting files that may have been left around
|
||||
string deletePattern = Path.GetFileName(fileName) + @".*.deleting";
|
||||
foreach (string deleteingFile in Directory.GetFiles(Path.GetDirectoryName(fileName), deletePattern))
|
||||
TryDelete(deleteingFile);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to delete 'fileName' catching any exception. Returns true if successful. It will delete read-only files.
|
||||
/// </summary>
|
||||
public static bool TryDelete(string fileName)
|
||||
{
|
||||
bool ret = false;
|
||||
if (!File.Exists(fileName))
|
||||
return true;
|
||||
try
|
||||
{
|
||||
FileAttributes attribs = File.GetAttributes(fileName);
|
||||
if ((attribs & FileAttributes.ReadOnly) != 0)
|
||||
{
|
||||
attribs &= ~FileAttributes.ReadOnly;
|
||||
File.SetAttributes(fileName, attribs);
|
||||
}
|
||||
File.Delete(fileName);
|
||||
ret = true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SafeCopy sourceFile to destinationFile. If the destination exists
|
||||
/// used ForceDelete to get rid of it first.
|
||||
/// </summary>
|
||||
public static void ForceCopy(string sourceFile, string destinationFile)
|
||||
{
|
||||
ForceDelete(destinationFile); // will return immediate if the destination does not exist.
|
||||
File.Copy(sourceFile, destinationFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves sourceFile to destinationFile. If the destination exists
|
||||
/// used ForceDelete to get rid of it first.
|
||||
/// </summary>
|
||||
public static void ForceMove(string sourceFile, string destinationFile)
|
||||
{
|
||||
ForceDelete(destinationFile); // will return immediate if the destination does not exist.
|
||||
File.Move(sourceFile, destinationFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the two file have exactly the same content (as a stream of bytes).
|
||||
/// </summary>
|
||||
public static bool Equals(string fileName1, string fileName2)
|
||||
{
|
||||
byte[] buffer1 = new byte[8192];
|
||||
byte[] buffer2 = new byte[8192];
|
||||
using (FileStream file1 = File.Open(fileName1, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
using (FileStream file2 = File.Open(fileName2, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
int count1 = file1.Read(buffer1, 0, buffer1.Length);
|
||||
int count2 = file2.Read(buffer2, 0, buffer2.Length);
|
||||
if (count1 != count2)
|
||||
return false;
|
||||
for (int i = 0; i < count1; i++)
|
||||
if (buffer1[i] != buffer2[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utilities associated with file name paths.
|
||||
/// </summary>
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
static class PathUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Given a path and a superdirectory path relativeToDirectory compute the relative path (the path from) relativeToDirectory
|
||||
/// </summary>
|
||||
static public string PathRelativeTo(string path, string relativeToDirectory)
|
||||
{
|
||||
Debug.Assert(!relativeToDirectory.EndsWith("\\"));
|
||||
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
string fullCurrentDirectory = Path.GetFullPath(relativeToDirectory);
|
||||
|
||||
int commonToSlashIndex = -1;
|
||||
for (int i = 0; i < fullPath.Length; i++)
|
||||
{
|
||||
char cFullPath = fullPath[i];
|
||||
if (i >= fullCurrentDirectory.Length)
|
||||
{
|
||||
if (cFullPath == '\\')
|
||||
commonToSlashIndex = i;
|
||||
break;
|
||||
}
|
||||
char cCurrentDirectory = fullCurrentDirectory[i];
|
||||
|
||||
if (cCurrentDirectory != cFullPath)
|
||||
{
|
||||
if (char.IsLower(cCurrentDirectory))
|
||||
cCurrentDirectory = (char)(cCurrentDirectory - (char)('a' - 'A'));
|
||||
if (char.IsLower(cFullPath))
|
||||
cFullPath = (char)(cFullPath - (char)('a' - 'A'));
|
||||
if (cCurrentDirectory != cFullPath)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cCurrentDirectory == '\\')
|
||||
commonToSlashIndex = i;
|
||||
}
|
||||
|
||||
// There is no common prefix between the two paths, we give up.
|
||||
if (commonToSlashIndex < 0)
|
||||
return path;
|
||||
|
||||
string returnVal = "";
|
||||
int nextSlash = commonToSlashIndex;
|
||||
for (; ; )
|
||||
{
|
||||
if (nextSlash >= fullCurrentDirectory.Length)
|
||||
break;
|
||||
if (returnVal.Length > 0)
|
||||
returnVal += "\\";
|
||||
returnVal += @"..";
|
||||
if (nextSlash + 1 == fullCurrentDirectory.Length)
|
||||
break;
|
||||
nextSlash = fullCurrentDirectory.IndexOf('\\', nextSlash + 1);
|
||||
if (nextSlash < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
string rest = fullPath.Substring(commonToSlashIndex + 1);
|
||||
returnVal = Path.Combine(returnVal, rest);
|
||||
Debug.Assert(string.Compare(Path.GetFullPath(Path.Combine(relativeToDirectory, returnVal)), fullPath, StringComparison.OrdinalIgnoreCase) == 0);
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
.assembly extern 'mscorlib'
|
||||
{
|
||||
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
|
||||
.ver 4:0:0:0
|
||||
}
|
||||
.assembly 'Microsoft.Diagnostics.Utilities'
|
||||
{
|
||||
.custom instance void ['mscorlib']'System'.'CLSCompliantAttribute'::.ctor(bool) = (01 00 00 00 00)
|
||||
.custom instance void ['mscorlib']'System.Runtime.InteropServices'.'ComVisibleAttribute'::.ctor(bool) = (01 00 00 00 00)
|
||||
.permissionset reqmin = (2E 01 80 84 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 41 74 74 72 69 62 75 74 65 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 15 01 54 02 10 53 6B 69 70 56 65 72 69 66 69 63 61 74 69 6F 6E 01)
|
||||
.publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 07 D1 FA 57 C4 AE D9 F0 A3 2E 84 AA 0F AE FD 0D E9 E8 FD 6A EC 8F 87 FB 03 76 6C 83 4C 99 92 1E B2 3B E7 9A D9 D5 DC C1 DD 9A D2 36 13 21 02 90 0B 72 3C F9 80 95 7F C4 E1 77 10 8F C6 07 77 4F 29 E8 32 0E 92 EA 05 EC E4 E8 21 C0 A5 EF E8 F1 64 5C 4C 0C 93 C1 AB 99 28 5D 62 2C AA 65 2C 1D FA D6 3D 74 5D 6F 2D E5 F1 7E 5E AF 0F C4 96 3D 26 1C 8A 12 43 65 18 20 6D C0 93 34 4D 5A D2 93)
|
||||
.hash algorithm 0x00008004
|
||||
.ver 14:0:0:0
|
||||
}
|
||||
.namespace 'Microsoft.Diagnostics.Utilities'
|
||||
{
|
||||
.class public abstract sealed 'DirectoryUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
void 'Copy'(string 'sourceDirectory', string 'targetDirectory')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
void 'Copy'(string 'sourceDirectory', string 'targetDirectory', valuetype ['mscorlib']'System.IO'.'SearchOption' 'searchOptions')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
int32 'Clean'(string 'directory')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
bool 'DeleteOldest'(string 'directoryPath', int32 'numberToKeep')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
class ['mscorlib']'System.Collections.Generic'.'IEnumerable`1'<string> 'GetFiles'(string 'directoryPath', string 'searchPattern', valuetype ['mscorlib']'System.IO'.'SearchOption' 'searchOptions')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
class ['mscorlib']'System.Collections.Generic'.'IEnumerable`1'<string> 'GetFiles'(string 'directoryName', [opt] string 'searchPattern')
|
||||
{
|
||||
.param [2] = "*"
|
||||
ret
|
||||
}
|
||||
}
|
||||
.class public abstract sealed 'EnvironmentUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig specialname
|
||||
bool 'get_Is64BitProcess'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig specialname
|
||||
bool 'get_Is64BitOperatingSystem'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.property bool 'Is64BitOperatingSystem'()
|
||||
{
|
||||
.get bool 'Microsoft.Diagnostics.Utilities'.'EnvironmentUtilities'::'get_Is64BitOperatingSystem'()
|
||||
}
|
||||
.property bool 'Is64BitProcess'()
|
||||
{
|
||||
.get bool 'Microsoft.Diagnostics.Utilities'.'EnvironmentUtilities'::'get_Is64BitProcess'()
|
||||
}
|
||||
}
|
||||
.class public abstract sealed 'FileUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
class ['mscorlib']'System.Collections.Generic'.'IEnumerable`1'<string> 'ReadAllLines'(string 'fileName')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
class ['mscorlib']'System.Collections.Generic'.'IEnumerable`1'<string> 'ExpandWildcards'(string[] 'fileSpecifications', [opt] valuetype ['mscorlib']'System.IO'.'SearchOption' 'searchOpt')
|
||||
{
|
||||
.param [2] = int32(0x00000000)
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
bool 'ForceDelete'(string 'fileName')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
bool 'TryDelete'(string 'fileName')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
void 'ForceCopy'(string 'sourceFile', string 'destinationFile')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
void 'ForceMove'(string 'sourceFile', string 'destinationFile')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
bool 'Equals'(string 'fileName1', string 'fileName2')
|
||||
{
|
||||
ret
|
||||
}
|
||||
}
|
||||
.class public abstract sealed 'PathUtil'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
string 'PathRelativeTo'(string 'path', string 'relativeToDirectory')
|
||||
{
|
||||
ret
|
||||
}
|
||||
}
|
||||
.class public abstract sealed 'StreamUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
void 'CopyFromFile'(string 'fromFilePath', class ['mscorlib']'System.IO'.'Stream' 'toStream')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
void 'CopyToFile'(class ['mscorlib']'System.IO'.'Stream' 'fromStream', string 'toFilePath')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
int32 'CopyStream'(class ['mscorlib']'System.IO'.'Stream' 'fromStream', class ['mscorlib']'System.IO'.'Stream' 'toStream')
|
||||
{
|
||||
ret
|
||||
}
|
||||
}
|
||||
.class public abstract sealed 'StringUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
bool 'IsNullOrWhiteSpace'(string 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
}
|
||||
.class public sealed 'TeeTextWriter'
|
||||
extends ['mscorlib']'System.IO'.'TextWriter'
|
||||
{
|
||||
.method public hidebysig specialname
|
||||
instance void '.ctor'(class ['mscorlib']'System.IO'.'TextWriter' 'stream1', class ['mscorlib']'System.IO'.'TextWriter' 'stream2')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public hidebysig specialname
|
||||
instance class ['mscorlib']'System.IO'.'TextWriter' 'get_Stream1'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public hidebysig specialname
|
||||
instance class ['mscorlib']'System.IO'.'TextWriter' 'get_Stream2'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public virtual hidebysig specialname
|
||||
instance class ['mscorlib']'System.Text'.'Encoding' 'get_Encoding'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public virtual hidebysig
|
||||
instance void 'Write'(char 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public virtual hidebysig
|
||||
instance void 'Write'(char[] 'buffer', int32 'index', int32 'count')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public virtual hidebysig
|
||||
instance void 'Write'(string 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public virtual hidebysig
|
||||
instance void 'Flush'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method family virtual hidebysig
|
||||
instance void 'Dispose'(bool 'disposing')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.property instance class ['mscorlib']'System.Text'.'Encoding' 'Encoding'()
|
||||
{
|
||||
.get instance class ['mscorlib']'System.Text'.'Encoding' 'Microsoft.Diagnostics.Utilities'.'TeeTextWriter'::'get_Encoding'()
|
||||
}
|
||||
.property instance class ['mscorlib']'System.IO'.'TextWriter' 'Stream1'()
|
||||
{
|
||||
.get instance class ['mscorlib']'System.IO'.'TextWriter' 'Microsoft.Diagnostics.Utilities'.'TeeTextWriter'::'get_Stream1'()
|
||||
}
|
||||
.property instance class ['mscorlib']'System.IO'.'TextWriter' 'Stream2'()
|
||||
{
|
||||
.get instance class ['mscorlib']'System.IO'.'TextWriter' 'Microsoft.Diagnostics.Utilities'.'TeeTextWriter'::'get_Stream2'()
|
||||
}
|
||||
}
|
||||
.class public 'XmlUtilities'
|
||||
extends ['mscorlib']'System'.'Object'
|
||||
{
|
||||
.method public static hidebysig
|
||||
string 'OpenXmlElement'(string 'xmlElement')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlEscape'(object 'obj', [opt] bool 'quote')
|
||||
{
|
||||
.param [2] = bool(false)
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuote'(object 'obj')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuote'(int32 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuoteHex'(uint32 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuoteHex'(uint64 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuoteHex'(int32 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public static hidebysig
|
||||
string 'XmlQuoteHex'(int64 'value')
|
||||
{
|
||||
ret
|
||||
}
|
||||
.method public hidebysig specialname
|
||||
instance void '.ctor'()
|
||||
{
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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.IO;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// General utilities associated with streams.
|
||||
/// </summary>
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
static class StreamUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Open the 'fromFilePath' and write its contents to 'toStream'
|
||||
/// </summary>
|
||||
public static void CopyFromFile(string fromFilePath, Stream toStream)
|
||||
{
|
||||
using (Stream fromStream = File.OpenRead(fromFilePath))
|
||||
CopyStream(fromStream, toStream);
|
||||
}
|
||||
/// <summary>
|
||||
/// Open the 'toFilePath' for writing and write the contents of 'fromStream' to it
|
||||
/// </summary>
|
||||
public static void CopyToFile(Stream fromStream, string toFilePath)
|
||||
{
|
||||
using (Stream toStream = File.Create(toFilePath))
|
||||
CopyStream(fromStream, toStream);
|
||||
}
|
||||
/// <summary>
|
||||
/// CopyStream simply copies 'fromStream' to 'toStream'
|
||||
/// </summary>
|
||||
public static int CopyStream(Stream fromStream, Stream toStream)
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
int totalBytes = 0;
|
||||
for (; ; )
|
||||
{
|
||||
int count = fromStream.Read(buffer, 0, buffer.Length);
|
||||
if (count == 0)
|
||||
break;
|
||||
toStream.Write(buffer, 0, count);
|
||||
totalBytes += count;
|
||||
}
|
||||
return totalBytes;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
static class StringUtilities
|
||||
{
|
||||
public static bool IsNullOrWhiteSpace(string value)
|
||||
{
|
||||
// Implementation copied from /ndp/clr/src/bcl/System/string.cs
|
||||
|
||||
if (value == null) return true;
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
if (!Char.IsWhiteSpace(value[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// This class returns a stream, which when written to, will write to two other streams.
|
||||
/// </summary>
|
||||
public sealed class TeeTextWriter : TextWriter
|
||||
{
|
||||
public TeeTextWriter(TextWriter stream1, TextWriter stream2)
|
||||
{
|
||||
m_stream1 = stream1;
|
||||
m_stream2 = stream2;
|
||||
}
|
||||
public TextWriter Stream1 { get { return m_stream1; } }
|
||||
public TextWriter Stream2 { get { return m_stream2; } }
|
||||
|
||||
public override System.Text.Encoding Encoding { get { return m_stream1.Encoding; } }
|
||||
public override void Write(char value)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_stream1.Write(value); m_stream2.Write(value);
|
||||
}
|
||||
}
|
||||
public override void Write(char[] buffer, int index, int count) {
|
||||
lock (this)
|
||||
{
|
||||
Write(new string(buffer, index, count));
|
||||
}
|
||||
}
|
||||
public override void Write(string value)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_stream1.Write(value); m_stream2.Write(value);
|
||||
}
|
||||
}
|
||||
public override void Flush()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_stream1.Flush(); m_stream2.Flush();
|
||||
}
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposing) { m_stream1.Dispose(); m_stream2.Dispose(); }
|
||||
}
|
||||
}
|
||||
|
||||
TextWriter m_stream1;
|
||||
TextWriter m_stream2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PerfLibRootPath>$(MSBuildThisFileDirectory)\..</PerfLibRootPath>
|
||||
</PropertyGroup>
|
||||
<!--Target CLR v2-->
|
||||
<PropertyGroup>
|
||||
<AllowCrossTargeting>true</AllowCrossTargeting>
|
||||
<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(PerfLibRootPath)\PerfLib.settings.targets" />
|
||||
<PropertyGroup Condition=" '$(BuildInRazzle)' == 'true' ">
|
||||
<UseVsVersion>true</UseVsVersion>
|
||||
<CreateMetaAssembly>true</CreateMetaAssembly>
|
||||
<PublishMetaAssemblyVisibility>Inter</PublishMetaAssemblyVisibility>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(BuildInRazzle)' != 'true' ">
|
||||
<IgnoreDualBuildAssemblyInfo>false</IgnoreDualBuildAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E32C9FC3-8524-4511-8ACB-235AC136DC22}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Microsoft.Diagnostics.Utilities</RootNamespace>
|
||||
<AssemblyName>Microsoft.Diagnostics.Utilities</AssemblyName>
|
||||
<AssemblyAttributeClsCompliant>false</AssemblyAttributeClsCompliant>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants>UTILITIES_PUBLIC;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DirectoryUtilities.cs" />
|
||||
<Compile Include="EnvironmentUtilities.cs" />
|
||||
<Compile Include="FileUtilities.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="StreamUtilities.cs" />
|
||||
<Compile Include="StringUtilities.cs" />
|
||||
<Compile Include="XmlUtilities.cs" />
|
||||
<Compile Include="TeeTextWriter.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(PerfLibRootPath)\PerfLib.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,489 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Globalization;
|
||||
using System.IO.Compression;
|
||||
using System.IO;
|
||||
using Microsoft.Diagnostics.Tracing.Stacks;
|
||||
using System.Collections.Generic;
|
||||
////using System.Runtime.Serialization.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Diagnostics.Tracing.StackSources
|
||||
{
|
||||
public class XmlStackSourceWriter
|
||||
{
|
||||
public static void WriteStackViewAsZippedXml(StackSource source, string fileName, Action<XmlWriter> additionalData = null)
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
File.Delete(fileName);
|
||||
using (var archive = ZipFile.Open(fileName, ZipArchiveMode.Create))
|
||||
{
|
||||
var entry = archive.CreateEntry(Path.GetFileNameWithoutExtension(fileName));
|
||||
using (var entryStream = entry.Open())
|
||||
WriteStackViewAsXml(source, entryStream, additionalData);
|
||||
}
|
||||
}
|
||||
public static void WriteStackViewAsXml(StackSource source, string fileName, Action<XmlWriter> additionalData = null)
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
File.Delete(fileName);
|
||||
using (var writeStream = File.Create(fileName))
|
||||
WriteStackViewAsXml(source, writeStream, additionalData);
|
||||
}
|
||||
public static void WriteStackViewAsXml(StackSource source, Stream writeStream, Action<XmlWriter> additionalData = null)
|
||||
{
|
||||
using (var writer = XmlWriter.Create(writeStream, new XmlWriterSettings() { Indent = true, IndentChars = " " }))
|
||||
{
|
||||
writer.WriteStartElement("StackWindow");
|
||||
XmlStackSourceWriter.WriteStacks(source, writer);
|
||||
|
||||
if (additionalData != null)
|
||||
additionalData(writer);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteStacks(StackSource source, XmlWriter writer)
|
||||
{
|
||||
writer.WriteStartElement("StackSource");
|
||||
writer.WriteStartElement("Frames");
|
||||
writer.WriteAttributeString("Count", source.CallFrameIndexLimit.ToString());
|
||||
for (int i = 0; i < source.CallFrameIndexLimit; i++)
|
||||
{
|
||||
writer.WriteStartElement("Frame");
|
||||
writer.WriteAttributeString("ID", i.ToString());
|
||||
var frameName = source.GetFrameName((StackSourceFrameIndex)i, true);
|
||||
|
||||
writer.WriteString(frameName);
|
||||
writer.WriteEndElement(); // Frame
|
||||
}
|
||||
writer.WriteEndElement(); // Frames
|
||||
|
||||
writer.WriteStartElement("Stacks");
|
||||
writer.WriteAttributeString("Count", source.CallStackIndexLimit.ToString());
|
||||
for (int i = 0; i < source.CallStackIndexLimit; i++)
|
||||
{
|
||||
writer.WriteStartElement("Stack");
|
||||
writer.WriteAttributeString("ID", i.ToString());
|
||||
var FrameID = source.GetFrameIndex((StackSourceCallStackIndex)i);
|
||||
var callerID = source.GetCallerIndex((StackSourceCallStackIndex)i);
|
||||
writer.WriteAttributeString("CallerID", ((int)callerID).ToString());
|
||||
writer.WriteAttributeString("FrameID", ((int)FrameID).ToString());
|
||||
writer.WriteEndElement(); // Stack
|
||||
}
|
||||
writer.WriteEndElement(); // Stacks
|
||||
|
||||
writer.WriteStartElement("Samples");
|
||||
writer.WriteAttributeString("Count", source.SampleIndexLimit.ToString());
|
||||
// We use the invariant culture, otherwise if we encode in France and decode
|
||||
// in English we get parse errors (this happened!);
|
||||
var invariantCulture = CultureInfo.InvariantCulture;
|
||||
source.ForEach(delegate (StackSourceSample sample)
|
||||
{
|
||||
// <Sample ID="1" Time="3432.23" StackID="2" Metric="1" EventKind="CPUSample" />
|
||||
writer.WriteStartElement("Sample");
|
||||
writer.WriteAttributeString("ID", ((int)sample.SampleIndex).ToString());
|
||||
writer.WriteAttributeString("Time", sample.TimeRelativeMSec.ToString("f3", invariantCulture));
|
||||
writer.WriteAttributeString("StackID", ((int)sample.StackIndex).ToString());
|
||||
if (sample.Metric != 1)
|
||||
{
|
||||
var asInt = (int)sample.Metric;
|
||||
if (sample.Metric == asInt)
|
||||
writer.WriteAttributeString("Metric", asInt.ToString());
|
||||
else
|
||||
writer.WriteAttributeString("Metric", sample.Metric.ToString("f3", invariantCulture));
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
});
|
||||
writer.WriteEndElement(); // Samples
|
||||
writer.WriteEndElement(); // StackSource
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a very reasonable XML encoding of a stack source.
|
||||
///
|
||||
/// </summary>
|
||||
public class XmlStackSource : StackSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a Stack Source from an XML file created with XmlStackSourceWriter.
|
||||
/// If 'readElement' is non-null Any XML Elements that are not recognised to it so
|
||||
/// that that information can be parsed by upper level logic. When that routine
|
||||
/// returns it must have skipped past that element (so reader points at whatever
|
||||
/// is after the End element tag).
|
||||
///
|
||||
/// If the filename ends in .zip, the file is assumed to be a ZIPPed XML file and
|
||||
/// it is first Unziped and then processed.
|
||||
///
|
||||
/// If the file ends in .json or .json.zip it can also read that (using JsonReaderWriterFactory.CreateJsonReader)
|
||||
/// see https://msdn.microsoft.com/en-us/library/bb412170.aspx?f=255&MSPPError=-2147217396 for
|
||||
/// more on this mapping.
|
||||
/// </summary>
|
||||
public XmlStackSource(string fileName, Action<XmlReader> readElement = null)
|
||||
{
|
||||
using (Stream dataStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
{
|
||||
var xmlStream = dataStream;
|
||||
string unzippedName = fileName;
|
||||
if (fileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var zipArchive = new ZipArchive(dataStream);
|
||||
var entries = zipArchive.Entries;
|
||||
if (entries.Count != 1)
|
||||
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);
|
||||
}
|
||||
|
||||
XmlReader reader;
|
||||
if (unzippedName.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
reader = GetJsonReader(dataStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
XmlReaderSettings settings = new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = true };
|
||||
////reader = XmlTextReader.Create(xmlStream, settings);
|
||||
reader = XmlReader.Create(xmlStream);
|
||||
}
|
||||
|
||||
reader.Read(); // Skip the StackWindow element.
|
||||
bool readStackSource = false;
|
||||
for (;;)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
if (reader.Name == "StackSource")
|
||||
{
|
||||
if (!readStackSource)
|
||||
{
|
||||
Read(reader);
|
||||
readStackSource = true;
|
||||
}
|
||||
}
|
||||
else if (readElement != null)
|
||||
readElement(reader);
|
||||
else
|
||||
reader.Read();
|
||||
}
|
||||
else if (!reader.Read())
|
||||
break;
|
||||
}
|
||||
if (m_interner != null)
|
||||
{
|
||||
// Transfer the interned names to the m_frames array.
|
||||
// Go from high to low so at most one reallocation happens.
|
||||
for (int i = m_interner.FrameCount - 1; 0 <= i; --i)
|
||||
{
|
||||
StackSourceFrameIndex frameIdx = m_interner.FrameStartIndex + i;
|
||||
m_frames.Set((int)frameIdx, m_interner.GetFrameName(frameIdx, true));
|
||||
}
|
||||
|
||||
for (int i = m_interner.CallStackCount - 1; 0 <= i; --i)
|
||||
{
|
||||
StackSourceCallStackIndex stackIdx = m_interner.CallStackStartIndex + i;
|
||||
m_stacks.Set((int)stackIdx, new Frame(
|
||||
(int)m_interner.GetFrameIndex(stackIdx),
|
||||
(int)m_interner.GetCallerIndex(stackIdx)));
|
||||
}
|
||||
|
||||
m_interner = null; // we are done with it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO intern modules
|
||||
public override void ForEach(Action<StackSourceSample> callback)
|
||||
{
|
||||
for (int i = 0; i < m_samples.Count; i++)
|
||||
callback(m_samples[i]);
|
||||
}
|
||||
public override bool SamplesImmutable { get { return true; } }
|
||||
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex)
|
||||
{
|
||||
return (StackSourceCallStackIndex)m_stacks[(int)callStackIndex].callerID;
|
||||
}
|
||||
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex)
|
||||
{
|
||||
return (StackSourceFrameIndex)m_stacks[(int)callStackIndex].frameID;
|
||||
}
|
||||
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName)
|
||||
{
|
||||
string ret = m_frames.Get((int)frameIndex);
|
||||
if (!verboseName)
|
||||
{
|
||||
var shortName = m_shortFrameNames.Get((int)frameIndex);
|
||||
if (shortName == null)
|
||||
{
|
||||
shortName = ret;
|
||||
// Strip off anything before the last \\ before the !
|
||||
var exclaimIdx = ret.IndexOf('!');
|
||||
if (0 < exclaimIdx)
|
||||
{
|
||||
// Becomes 0 if it fails, which is what we want.
|
||||
var startIdx = ret.LastIndexOf('\\', exclaimIdx - 1, exclaimIdx - 1) + 1;
|
||||
shortName = ret.Substring(startIdx);
|
||||
}
|
||||
m_shortFrameNames.Set((int)frameIndex, shortName);
|
||||
}
|
||||
ret = shortName;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public override int CallStackIndexLimit { get { return m_stacks.Count; } }
|
||||
public override int CallFrameIndexLimit { get { return m_frames.Count; } }
|
||||
|
||||
public override int SampleIndexLimit
|
||||
{
|
||||
get { return m_samples.Count; }
|
||||
}
|
||||
public override double SampleTimeRelativeMSecLimit
|
||||
{
|
||||
get { return m_maxTime; }
|
||||
}
|
||||
public override StackSourceSample GetSampleByIndex(StackSourceSampleIndex sampleIndex)
|
||||
{
|
||||
return m_samples[(int)sampleIndex];
|
||||
}
|
||||
|
||||
#region
|
||||
// To avoid loading the System.Runtime.Serialization Dll which is only needed in the JSON case, we have
|
||||
// the actual reference to that in this method that will not be called unless it is needed.
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
|
||||
static private XmlReader GetJsonReader(Stream dataStream)
|
||||
{
|
||||
////return JsonReaderWriterFactory.CreateJsonReader(dataStream, new XmlRead XmlDictionaryReaderQuotas());
|
||||
XmlReader reader = XmlReader.Create(dataStream);
|
||||
return reader;
|
||||
}
|
||||
|
||||
private void Read(XmlReader reader)
|
||||
{
|
||||
Stack<string> frameStack = null;
|
||||
// We use the invarient culture, otherwise if we encode in france and decode
|
||||
// in english we get parse errors (this happened!);
|
||||
var invariantCulture = CultureInfo.InvariantCulture;
|
||||
var inputDepth = reader.Depth;
|
||||
var depthForSamples = 0;
|
||||
while (reader.Read())
|
||||
{
|
||||
PROCESS_NODE:
|
||||
switch (reader.NodeType)
|
||||
{
|
||||
case XmlNodeType.Element:
|
||||
if (reader.Name == "Sample")
|
||||
{
|
||||
var sample = new StackSourceSample(this);
|
||||
sample.Metric = 1;
|
||||
if (reader.MoveToFirstAttribute())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (reader.Name == "Time")
|
||||
sample.TimeRelativeMSec = double.Parse(reader.ReadContentAsString(), invariantCulture);
|
||||
else if (reader.Name == "StackID")
|
||||
sample.StackIndex = (StackSourceCallStackIndex)reader.ReadContentAsInt();
|
||||
else if (reader.Name == "Metric")
|
||||
sample.Metric = float.Parse(reader.ReadContentAsString(), invariantCulture);
|
||||
} while (reader.MoveToNextAttribute());
|
||||
}
|
||||
sample.SampleIndex = (StackSourceSampleIndex)m_curSample;
|
||||
m_samples.Set(m_curSample++, sample);
|
||||
if (sample.TimeRelativeMSec > m_maxTime)
|
||||
m_maxTime = sample.TimeRelativeMSec;
|
||||
|
||||
// See if there is a literal stack present as the body of
|
||||
if (!reader.Read())
|
||||
break;
|
||||
if (reader.NodeType != XmlNodeType.Text)
|
||||
goto PROCESS_NODE;
|
||||
|
||||
string rawStack = reader.Value.Trim();
|
||||
if (0 < rawStack.Length)
|
||||
{
|
||||
InitInterner();
|
||||
|
||||
StackSourceCallStackIndex stackIdx = StackSourceCallStackIndex.Invalid;
|
||||
string[] frames = rawStack.Split('\n');
|
||||
for (int i = frames.Length - 1; 0 <= i; --i)
|
||||
{
|
||||
var frameIdx = m_interner.FrameIntern(frames[i].Trim());
|
||||
stackIdx = m_interner.CallStackIntern(frameIdx, stackIdx);
|
||||
}
|
||||
sample.StackIndex = stackIdx;
|
||||
}
|
||||
}
|
||||
else if (reader.Name == "Stack")
|
||||
{
|
||||
int stackID = -1;
|
||||
int callerID = -1;
|
||||
int frameID = -1;
|
||||
if (reader.MoveToFirstAttribute())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (reader.Name == "ID")
|
||||
stackID = reader.ReadContentAsInt();
|
||||
else if (reader.Name == "FrameID")
|
||||
frameID = reader.ReadContentAsInt();
|
||||
else if (reader.Name == "CallerID")
|
||||
callerID = reader.ReadContentAsInt();
|
||||
} while (reader.MoveToNextAttribute());
|
||||
if (0 <= stackID)
|
||||
m_stacks.Set(stackID, new Frame(frameID, callerID));
|
||||
}
|
||||
|
||||
}
|
||||
else if (reader.Name == "Frame")
|
||||
{
|
||||
var frameID = -1;
|
||||
if (reader.MoveToFirstAttribute())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (reader.Name == "ID")
|
||||
frameID = reader.ReadContentAsInt();
|
||||
} while (reader.MoveToNextAttribute());
|
||||
}
|
||||
reader.Read(); // Move on to body of the element
|
||||
var frameName = reader.ReadContentAsString();
|
||||
m_frames.Set(frameID, frameName);
|
||||
}
|
||||
else if (reader.Name == "Frames")
|
||||
{
|
||||
var count = reader.GetAttribute("Count");
|
||||
if (count != null && m_frames.Count == 0)
|
||||
m_frames = new GrowableArray<string>(int.Parse(count));
|
||||
}
|
||||
else if (reader.Name == "Stacks")
|
||||
{
|
||||
var count = reader.GetAttribute("Count");
|
||||
if (count != null && m_stacks.Count == 0)
|
||||
m_stacks = new GrowableArray<Frame>(int.Parse(count));
|
||||
#if DEBUG
|
||||
for (int i = 0; i < m_stacks.Count; i++)
|
||||
m_stacks[i] = new Frame(int.MinValue, int.MinValue);
|
||||
#endif
|
||||
}
|
||||
else if (reader.Name == "Samples")
|
||||
{
|
||||
var count = reader.GetAttribute("Count");
|
||||
if (count != null && m_samples.Count == 0)
|
||||
m_samples = new GrowableArray<StackSourceSample>(int.Parse(count));
|
||||
depthForSamples = reader.Depth;
|
||||
}
|
||||
// This is the logic for the JSON case. These are the anonymous object representing a sample.
|
||||
else if (reader.Name == "item")
|
||||
{
|
||||
// THis is an item which is an element of the 'Samples' array.
|
||||
if (reader.Depth == depthForSamples + 1)
|
||||
{
|
||||
var sample = new StackSourceSample(this);
|
||||
sample.Metric = 1;
|
||||
|
||||
InitInterner();
|
||||
int depthForSample = reader.Depth;
|
||||
if (frameStack == null)
|
||||
frameStack = new Stack<string>();
|
||||
frameStack.Clear();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
PROCESS_NODE_SAMPLE:
|
||||
if (reader.Depth <= depthForSample)
|
||||
break;
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
if (reader.Name == "Time")
|
||||
{
|
||||
sample.TimeRelativeMSec = reader.ReadElementContentAsDouble();
|
||||
goto PROCESS_NODE_SAMPLE;
|
||||
}
|
||||
else if (reader.Name == "Metric")
|
||||
{
|
||||
sample.Metric = (float) reader.ReadElementContentAsDouble();
|
||||
goto PROCESS_NODE_SAMPLE;
|
||||
}
|
||||
else if (reader.Name == "item")
|
||||
{
|
||||
// Item is a string under stack under the sample.
|
||||
if (reader.Depth == depthForSample + 2)
|
||||
{
|
||||
frameStack.Push(reader.ReadElementContentAsString());
|
||||
goto PROCESS_NODE_SAMPLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the order of the frames in the stack.
|
||||
sample.StackIndex = StackSourceCallStackIndex.Invalid;
|
||||
while (0 < frameStack.Count)
|
||||
{
|
||||
var frameIdx = m_interner.FrameIntern(frameStack.Pop());
|
||||
sample.StackIndex = m_interner.CallStackIntern(frameIdx, sample.StackIndex);
|
||||
}
|
||||
|
||||
if (sample.TimeRelativeMSec > m_maxTime)
|
||||
m_maxTime = sample.TimeRelativeMSec;
|
||||
sample.SampleIndex = (StackSourceSampleIndex)m_curSample;
|
||||
m_samples.Set(m_curSample++, sample);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XmlNodeType.EndElement:
|
||||
if (reader.Depth <= inputDepth)
|
||||
{
|
||||
reader.Read();
|
||||
goto Done;
|
||||
}
|
||||
break;
|
||||
case XmlNodeType.Text:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Done:;
|
||||
#if DEBUG
|
||||
for (int i = 0; i < m_samples.Count; i++)
|
||||
Debug.Assert(m_samples[i] != null);
|
||||
for (int i = 0; i < m_frames.Count; i++)
|
||||
Debug.Assert(m_frames[i] != null);
|
||||
for (int i = 0; i < m_stacks.Count; i++)
|
||||
{
|
||||
Debug.Assert(m_stacks[i].frameID >= 0);
|
||||
Debug.Assert(m_stacks[i].callerID >= -1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitInterner()
|
||||
{
|
||||
if (m_interner == null)
|
||||
{
|
||||
m_interner = new StackSourceInterner(5000, 1000, 5,
|
||||
(StackSourceFrameIndex)m_frames.Count,
|
||||
(StackSourceCallStackIndex)m_stacks.Count);
|
||||
}
|
||||
}
|
||||
|
||||
struct Frame
|
||||
{
|
||||
public Frame(int frameID, int callerID) { this.frameID = frameID; this.callerID = callerID; }
|
||||
public int frameID;
|
||||
public int callerID;
|
||||
}
|
||||
|
||||
GrowableArray<string> m_shortFrameNames;
|
||||
GrowableArray<string> m_frames;
|
||||
GrowableArray<Frame> m_stacks;
|
||||
GrowableArray<StackSourceSample> m_samples;
|
||||
StackSourceInterner m_interner; // If the XML has samples with explicit stacks, then this is non-null and used to intern them.
|
||||
int m_curSample;
|
||||
double m_maxTime;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Diagnostics.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// The important thing about these general utilities is that they have only dependencies on mscorlib and
|
||||
/// System (they can be used from anywhere).
|
||||
/// </summary>
|
||||
#if UTILITIES_PUBLIC
|
||||
public
|
||||
#endif
|
||||
class XmlUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Given an XML element, remove the closing operator for it, so you can add new child elements to it by concatination.
|
||||
/// </summary>
|
||||
public static string OpenXmlElement(string xmlElement)
|
||||
{
|
||||
if (xmlElement.EndsWith("/>"))
|
||||
return xmlElement.Substring(0, xmlElement.Length - 2) + ">";
|
||||
int endTagIndex = xmlElement.LastIndexOf("</");
|
||||
Debug.Assert(endTagIndex > 0);
|
||||
while (endTagIndex > 0 && Char.IsWhiteSpace(xmlElement[endTagIndex - 1]))
|
||||
--endTagIndex;
|
||||
return xmlElement.Substring(0, endTagIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given an object 'obj' do ToString() on it, and then transform it so that all speical XML characters are escaped and return the result.
|
||||
/// If 'quote' is true also surround the resulting object with double quotes.
|
||||
/// </summary>
|
||||
public static string XmlEscape(object obj, bool quote = false)
|
||||
{
|
||||
string str = obj.ToString();
|
||||
StringBuilder sb = null;
|
||||
string entity = null;
|
||||
int copied = 0;
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
{
|
||||
switch (str[i])
|
||||
{
|
||||
case '&':
|
||||
entity = "&";
|
||||
goto APPEND;
|
||||
case '"':
|
||||
entity = """;
|
||||
goto APPEND;
|
||||
case '\'':
|
||||
entity = "'";
|
||||
goto APPEND;
|
||||
case '<':
|
||||
entity = "<";
|
||||
goto APPEND;
|
||||
case '>':
|
||||
entity = ">";
|
||||
goto APPEND;
|
||||
APPEND:
|
||||
{
|
||||
if (sb == null)
|
||||
{
|
||||
sb = new StringBuilder();
|
||||
if (quote)
|
||||
sb.Append('"');
|
||||
}
|
||||
while (copied < i)
|
||||
sb.Append(str[copied++]);
|
||||
sb.Append(entity);
|
||||
copied++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sb != null)
|
||||
{
|
||||
while (copied < str.Length)
|
||||
sb.Append(str[copied++]);
|
||||
if (quote)
|
||||
sb.Append('"');
|
||||
return sb.ToString();
|
||||
}
|
||||
if (quote)
|
||||
str = "\"" + str + "\"";
|
||||
return str;
|
||||
}
|
||||
/// <summary>
|
||||
/// A shortcut for XmlEscape(obj, true) (that is ToString the object, escape XML chars, and then surround with double quotes.
|
||||
/// </summary>
|
||||
public static string XmlQuote(object obj)
|
||||
{
|
||||
return XmlEscape(obj, true);
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a doubly quoted string for the decimal integer value
|
||||
/// </summary>
|
||||
public static string XmlQuote(int value)
|
||||
{
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a double quoted string for the hexidecimal value of 'value'
|
||||
/// </summary>
|
||||
public static string XmlQuoteHex(uint value)
|
||||
{
|
||||
return "\"0x" + value.ToString("x").PadLeft(8, '0') + "\"";
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a double quoted string for the hexidecimal value of 'value'
|
||||
/// </summary>
|
||||
public static string XmlQuoteHex(ulong value)
|
||||
{
|
||||
return "\"0x" + value.ToString("x").PadLeft(8, '0') + "\"";
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a double quoted string for the hexidecimal value of 'value'
|
||||
/// </summary>
|
||||
public static string XmlQuoteHex(int value)
|
||||
{
|
||||
return XmlQuoteHex((uint)value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a double quoted string for the hexidecimal value of 'value'
|
||||
/// </summary>
|
||||
public static string XmlQuoteHex(long value)
|
||||
{
|
||||
return XmlQuoteHex((ulong)value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
////using Utilities;
|
||||
|
||||
// Welcome to the Utilities code base. This _README.cs file is your table of contents.
|
||||
//
|
||||
// You will notice that the code is littered with code: qualifiers. If you install the 'hyperAddin' for
|
||||
// Visual Studio, these qualifiers turn into hyperlinks that allow easy cross references. The hyperAddin is
|
||||
// available on http://toolbox/sites/HyperAddin/default.aspx inside microsoft or externally at
|
||||
// http://www.codeplex.com/hyperAddin but the easiest way to install it (inside microsoft) is to do
|
||||
// //
|
||||
// -------------------------------------------------------------------------------------
|
||||
// General Utilities:
|
||||
//
|
||||
// * file:Command.cs - holds code:Command an independent class that knows how to launch threads and collect its
|
||||
// output
|
||||
//
|
||||
// * file:CommandLineUtilities.cs holds useful utilities for parsing command lines.
|
||||
//
|
||||
// * file:ResourceUtilities.cs holds functions for getting at managed resources. Used to retrive
|
||||
// UserGuide.htm.
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>3a0a3256-f32a-4ba5-b3d5-e7d888629ecf</ProjectGuid>
|
||||
<RootNamespace>PerfViewService</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace PerfViewService
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Welcome to ASP.NET Core</title>
|
||||
<style>
|
||||
html {
|
||||
background: #f1f1f1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #fff;
|
||||
color: #505050;
|
||||
font: 14px 'Segoe UI', tahoma, arial, helvetica, sans-serif;
|
||||
margin: 1%;
|
||||
min-height: 95.5%;
|
||||
border: 1px solid silver;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#header {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-size: 44px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 10px 30px 10px 30px;
|
||||
}
|
||||
|
||||
#header span {
|
||||
margin: 0;
|
||||
padding: 0 30px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header p {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
background: #007acc;
|
||||
padding: 0 30px;
|
||||
line-height: 50px;
|
||||
margin-top: 25px;
|
||||
|
||||
}
|
||||
|
||||
#header p a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
padding-right: 35px;
|
||||
background: no-repeat right bottom url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAWCAMAAAAcqPc3AAAANlBMVEUAAAAAeswfitI9mthXp91us+KCvuaTx+mjz+2x1u+83PLH4vTR5/ba7Pjj8Pns9fv1+v3////wy3dWAAAAAXRSTlMAQObYZgAAAHxJREFUeNp9kVcSwCAIRMHUYoH7XzaxOxJ9P8oyQ1uIqNPwh3s2aLmIM2YtqrLcQIeQEylhuCeUOlhgve5yoBCfWmlnlgkN4H8ykbpaE7gR03AbUHiwoOxUH9Xp+ubd41p1HF3mBPrfC87BHeTdaB3ceeKL9HGpcvX9zu6+DdMWT9KQPvYAAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
#main {
|
||||
padding: 5px 30px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.section {
|
||||
width: 21.7%;
|
||||
float: left;
|
||||
margin: 0 0 0 4%;
|
||||
}
|
||||
|
||||
.section h2 {
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid silver;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.section.first {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.section.first h2 {
|
||||
font-size: 24px;
|
||||
text-transform: none;
|
||||
margin-bottom: 25px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.section.first li {
|
||||
border-top: 1px solid silver;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.section.last {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #267cb2;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
#footer p {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="header">
|
||||
<h1>Welcome to ASP.NET Core</h1>
|
||||
<span>
|
||||
We've made some big updates in this release, so it’s <b>important</b> that you spend
|
||||
a few minutes to learn what’s new.
|
||||
</span>
|
||||
<p>You've created a new ASP.NET Core project. <a href="http://go.microsoft.com/fwlink/?LinkId=518016">Learn what's new</a></p>
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<div class="section first">
|
||||
<h2>This application consists of:</h2>
|
||||
<ul>
|
||||
<li>Sample pages using ASP.NET Core MVC</li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518007">Gulp</a> and <a href="http://go.microsoft.com/fwlink/?LinkId=518004">Bower</a> for managing client-side libraries</li>
|
||||
<li>Theming using <a href="http://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>How to</h2>
|
||||
<ul>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398600">Add a Controller and View</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699562">Add an appsetting in config and access it in app.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699315">Manage User Secrets using Secret Manager.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699316">Use logging to log a message.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699317">Add packages using NuGet.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699318">Add client packages using Bower.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699319">Target development, staging or production environment.</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>Overview</h2>
|
||||
<ul>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of what is ASP.NET Core</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699320">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398602">Working with Data</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398603">Security</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699321">Client side development</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699322">Develop on different platforms</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699323">Read more on the documentation site</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section last">
|
||||
<h2>Run & Deploy</h2>
|
||||
<ul>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517851">Run your app</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517853">Run tools such as EF migrations and more</a></li>
|
||||
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure Web Apps</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p>We would love to hear your <a href="http://go.microsoft.com/fwlink/?LinkId=518015">feedback</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:6207/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"PerfViewService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:5000/Views/static/index.html",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
|
||||
namespace PerfViewService
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
|
||||
if (env.IsEnvironment("Development"))
|
||||
{
|
||||
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
|
||||
builder.AddApplicationInsightsSettings(developerMode: true);
|
||||
}
|
||||
|
||||
builder.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
services.AddApplicationInsightsTelemetry(Configuration);
|
||||
|
||||
services.AddMvc();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
loggerFactory.AddDebug();
|
||||
|
||||
app.UseApplicationInsightsRequestTelemetry();
|
||||
|
||||
app.UseApplicationInsightsExceptionTelemetry();
|
||||
|
||||
app.UseMvc();
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions()
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(
|
||||
Path.Combine(Directory.GetCurrentDirectory(), @"Views")),
|
||||
RequestPath = new Microsoft.AspNetCore.Http.PathString("/Views")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
function Delegate() {
|
||||
var self = this;
|
||||
var domain = "http://localhost:5000";
|
||||
|
||||
this.log = function log(status) {
|
||||
$("#statusBar span").text(status);
|
||||
}
|
||||
|
||||
this.httpGet = function httpGet(url, callback) {
|
||||
url = domain + url;
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.onreadystatechange = function() {
|
||||
if (xmlHttp.readyState == 4) {
|
||||
if (xmlHttp.status == 200) {
|
||||
self.log("GET " + url + " Complete");
|
||||
callback(JSON.parse(xmlHttp.responseText));
|
||||
} else {
|
||||
self.log("GET " + url + " " + xmlHttp.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
xmlHttp.open("GET", url, true);
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
}
|
||||
|
||||
delegate = new Delegate();
|
||||
@@ -0,0 +1 @@
|
||||
The libraries directory contains libraries and other resources that are not node modules.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 62 B |
Binary file not shown.
|
After Width: | Height: | Size: 87 B |
@@ -0,0 +1,23 @@
|
||||
/* Put this inside a @media qualifier so Netscape 4 ignores it */
|
||||
@media screen, print {
|
||||
/* Turn off list bullets */
|
||||
ul.mktree li { list-style: none; }
|
||||
/* Control how "spaced out" the tree is */
|
||||
ul.mktree, ul.mktree ul , ul.mktree li { margin-left:10px; padding:0px; }
|
||||
/* Provide space for our own "bullet" inside the LI */
|
||||
ul.mktree li .bullet { padding-left: 15px; }
|
||||
/* Show "bullets" in the links, depending on the class of the LI that the link's in */
|
||||
ul.mktree li.liOpen .bullet { cursor: pointer; background: url(minus.gif) center left no-repeat; }
|
||||
ul.mktree li.liClosed .bullet { cursor: pointer; background: url(plus.gif) center left no-repeat; }
|
||||
ul.mktree li.liBullet .bullet { cursor: default; background: url(bullet.gif) center left no-repeat; }
|
||||
/* Sublists are visible or not based on class of parent LI */
|
||||
ul.mktree li.liOpen ul { display: block; }
|
||||
ul.mktree li.liClosed ul { display: none; }
|
||||
|
||||
/* Format menu items differently depending on what level of the tree they are in */
|
||||
/* Uncomment this if you want your fonts to decrease in size the deeper they are in the tree */
|
||||
/*
|
||||
ul.mktree li ul li { font-size: 90% }
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c)2005-2009 Matt Kruse (javascripttoolbox.com)
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* This basically means you can use this code however you want for
|
||||
* free, but don't claim to have written it yourself!
|
||||
* Donations always accepted: http://www.JavascriptToolbox.com/donate/
|
||||
*
|
||||
* Please do not link to the .js files on javascripttoolbox.com from
|
||||
* your site. Copy the files locally to your server instead.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
This code is inspired by and extended from Stuart Langridge's aqlist code:
|
||||
http://www.kryogenix.org/code/browser/aqlists/
|
||||
Stuart Langridge, November 2002
|
||||
sil@kryogenix.org
|
||||
Inspired by Aaron's labels.js (http://youngpup.net/demos/labels/)
|
||||
and Dave Lindquist's menuDropDown.js (http://www.gazingus.org/dhtml/?id=109)
|
||||
*/
|
||||
|
||||
// Automatically attach a listener to the window onload, to convert the trees
|
||||
addEvent(window,"load",convertTrees);
|
||||
|
||||
// Utility function to add an event listener
|
||||
function addEvent(o,e,f){
|
||||
if (o.addEventListener){ o.addEventListener(e,f,false); return true; }
|
||||
else if (o.attachEvent){ return o.attachEvent("on"+e,f); }
|
||||
else { return false; }
|
||||
}
|
||||
|
||||
// utility function to set a global variable if it is not already set
|
||||
function setDefault(name,val) {
|
||||
if (typeof(window[name])=="undefined" || window[name]==null) {
|
||||
window[name]=val;
|
||||
}
|
||||
}
|
||||
|
||||
// Full expands a tree with a given ID
|
||||
function expandTree(treeId) {
|
||||
var ul = document.getElementById(treeId);
|
||||
if (ul == null) { return false; }
|
||||
expandCollapseList(ul,nodeOpenClass);
|
||||
}
|
||||
|
||||
// Fully collapses a tree with a given ID
|
||||
function collapseTree(treeId) {
|
||||
var ul = document.getElementById(treeId);
|
||||
if (ul == null) { return false; }
|
||||
expandCollapseList(ul,nodeClosedClass);
|
||||
}
|
||||
|
||||
// Expands enough nodes to expose an LI with a given ID
|
||||
function expandToItem(treeId,itemId) {
|
||||
var ul = document.getElementById(treeId);
|
||||
if (ul == null) { return false; }
|
||||
var ret = expandCollapseList(ul,nodeOpenClass,itemId);
|
||||
if (ret) {
|
||||
var o = document.getElementById(itemId);
|
||||
if (o.scrollIntoView) {
|
||||
o.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Performs 3 functions:
|
||||
// a) Expand all nodes
|
||||
// b) Collapse all nodes
|
||||
// c) Expand all nodes to reach a certain ID
|
||||
function expandCollapseList(ul,cName,itemId) {
|
||||
if (!ul.childNodes || ul.childNodes.length==0) { return false; }
|
||||
// Iterate LIs
|
||||
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
|
||||
var item = ul.childNodes[itemi];
|
||||
if (itemId!=null && item.id==itemId) { return true; }
|
||||
if (item.nodeName == "LI") {
|
||||
// Iterate things in this LI
|
||||
var subLists = false;
|
||||
for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
|
||||
var sitem = item.childNodes[sitemi];
|
||||
if (sitem.nodeName=="UL") {
|
||||
subLists = true;
|
||||
var ret = expandCollapseList(sitem,cName,itemId);
|
||||
if (itemId!=null && ret) {
|
||||
item.className=cName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (subLists && itemId==null) {
|
||||
item.className = cName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search the document for UL elements with the correct CLASS name, then process them
|
||||
function convertTrees() {
|
||||
setDefault("treeClass","mktree");
|
||||
setDefault("nodeClosedClass","liClosed");
|
||||
setDefault("nodeOpenClass","liOpen");
|
||||
setDefault("nodeBulletClass","liBullet");
|
||||
setDefault("nodeLinkClass","bullet");
|
||||
setDefault("preProcessTrees",true);
|
||||
if (preProcessTrees) {
|
||||
if (!document.createElement) { return; } // Without createElement, we can't do anything
|
||||
var uls = document.getElementsByTagName("ul");
|
||||
if (uls==null) { return; }
|
||||
var uls_length = uls.length;
|
||||
for (var uli=0;uli<uls_length;uli++) {
|
||||
var ul=uls[uli];
|
||||
if (ul.nodeName=="UL" && ul.className==treeClass) {
|
||||
processList(ul);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function treeNodeOnclick() {
|
||||
this.parentNode.className = (this.parentNode.className==nodeOpenClass) ? nodeClosedClass : nodeOpenClass;
|
||||
return false;
|
||||
}
|
||||
function retFalse() {
|
||||
return false;
|
||||
}
|
||||
// Process a UL tag and all its children, to convert to a tree
|
||||
function processList(ul) {
|
||||
if (!ul.childNodes || ul.childNodes.length==0) { return; }
|
||||
// Iterate LIs
|
||||
var childNodesLength = ul.childNodes.length;
|
||||
for (var itemi=0;itemi<childNodesLength;itemi++) {
|
||||
var item = ul.childNodes[itemi];
|
||||
if (item.nodeName == "LI") {
|
||||
// Iterate things in this LI
|
||||
var subLists = false;
|
||||
var itemChildNodesLength = item.childNodes.length;
|
||||
for (var sitemi=0;sitemi<itemChildNodesLength;sitemi++) {
|
||||
var sitem = item.childNodes[sitemi];
|
||||
if (sitem.nodeName=="UL") {
|
||||
subLists = true;
|
||||
processList(sitem);
|
||||
}
|
||||
}
|
||||
var s= document.createElement("SPAN");
|
||||
var t= '\u00A0'; //
|
||||
s.className = nodeLinkClass;
|
||||
if (subLists) {
|
||||
// This LI has UL's in it, so it's a +/- node
|
||||
if (item.className==null || item.className=="") {
|
||||
item.className = nodeClosedClass;
|
||||
}
|
||||
// If it's just text, make the text work as the link also
|
||||
if (item.firstChild.nodeName=="#text") {
|
||||
t = t+item.firstChild.nodeValue;
|
||||
item.removeChild(item.firstChild);
|
||||
}
|
||||
s.onclick = treeNodeOnclick;
|
||||
}
|
||||
else {
|
||||
// No sublists, so it's just a bullet node
|
||||
item.className = nodeBulletClass;
|
||||
s.onclick = retFalse;
|
||||
}
|
||||
s.appendChild(document.createTextNode(t));
|
||||
item.insertBefore(s,item.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 89 B |
@@ -0,0 +1,35 @@
|
||||
var app = require('electron').app
|
||||
const BrowserWindow = require('electron').BrowserWindow;
|
||||
|
||||
|
||||
|
||||
let win;
|
||||
|
||||
function createWindow() {
|
||||
|
||||
win = new BrowserWindow({width: 800, height: 600});
|
||||
|
||||
win.loadURL('file://' + __dirname + '/static/index.html');
|
||||
|
||||
win.webContents.openDevTools();
|
||||
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
app.on('ready', createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (win === null) {
|
||||
createWindow();
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "perfview",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"basiccontext": "^3.5.1",
|
||||
"jquery": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-prebuilt": "^1.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "electron main.js"
|
||||
},
|
||||
"author": "Kaelin D. Hooper",
|
||||
"license": "ISC"
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
font-family: Verdana, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Nav Bar Views
|
||||
-------------
|
||||
*/
|
||||
/* Wrapper for Nav Bar Views */
|
||||
.nav {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Wrapper for Dropdown Navbar and Helpful Links */
|
||||
.nav .navItems {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
background-color: #4169e1;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
.nav .navItems li {
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.nav .top-level-dropdown {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
flex-grow: 1;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.nav ul {
|
||||
padding-left: .2em;
|
||||
}
|
||||
|
||||
.nav .top-level-dropdown a, .nav .helpfulLinks a {
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
font-family: Helvetica, sans-serif;
|
||||
padding: .7em;
|
||||
color: white;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav .top-level-dropdown a:hover { color: #000000; }
|
||||
.nav .top-level-dropdown > li:hover { background: #CCCCCC; }
|
||||
|
||||
.nav .top-level-dropdown li:hover > ul {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.nav .second-level-dropdown {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: none;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.nav .second-level-dropdown > li {
|
||||
background: #999999;
|
||||
}
|
||||
|
||||
.nav .second-level-dropdown > li:hover { background: #CCCCCC; }
|
||||
|
||||
.nav .helpfulLinks {
|
||||
margin: 0;
|
||||
justify-content: flex-end;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/*.nav .helpfulLinks li:hover {
|
||||
background: #CCCCCC;
|
||||
}*/
|
||||
|
||||
.nav .helpfulLinks li a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
/*------------*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Tree View and Welcome View
|
||||
-----------------------
|
||||
*/
|
||||
/* Wrapper for Side Bar View and Welcome View */
|
||||
.wrapper {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer {
|
||||
min-width: 20em;
|
||||
border-right-style: solid;
|
||||
border-right-width: .15em;
|
||||
border-right-color: #4169e1;
|
||||
}
|
||||
|
||||
/* Directory Selector and Filter */
|
||||
.wrapper #sideBarContainer #dirSelect {
|
||||
margin: 0;
|
||||
height: 1.5em;
|
||||
background-color: darkgrey;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer #dirSelect {
|
||||
background-color: white;
|
||||
border: none;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer #dirSelect::-webkit-calendar-picker-indicator {
|
||||
opacity: 100;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer #filterContainer {
|
||||
height: 2em;
|
||||
background-color: lightgrey;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer form {
|
||||
display: flex;
|
||||
padding-top: .3em;
|
||||
margin-left: .2em;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer form a {
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
.wrapper #sideBarContainer form input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* Tree Hierarchy */
|
||||
.wrapper #treeContainer ul li {
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
/* Welcome View */
|
||||
.wrapper #welcome {
|
||||
flex-grow: 1; /* TODO: May not need this since implementing '.wrapper' */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 40em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.wrapper #welcome p {
|
||||
font-size: .9em;
|
||||
margin: .3em;
|
||||
}
|
||||
/*--------------------*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Footer View
|
||||
-----------
|
||||
*/
|
||||
#footer {
|
||||
list-style: none;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
font-size: .9em;
|
||||
margin: 0;
|
||||
|
||||
background: #4169e1;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
#footer #statusBar {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#footer #toggle {
|
||||
min-width: 17em;
|
||||
}
|
||||
|
||||
#footer #statusBar span {
|
||||
line-height: 2em;
|
||||
margin-left: .7em;
|
||||
}
|
||||
|
||||
#footer #toggle span {
|
||||
line-height: 2em;
|
||||
}
|
||||
/*----------*/
|
||||
@@ -0,0 +1,212 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="home.css">
|
||||
|
||||
<!-- basicContext Library -->
|
||||
<link rel="stylesheet" href="../node_modules/basiccontext/dist/basicContext.min.css">
|
||||
<!-- <link rel="stylesheet" href="node_modules/basiccontext/dist/themes/default.min.css"> -->
|
||||
<link rel="stylesheet" href="../node_modules/basiccontext/dist/themes/bright.min.css">
|
||||
<link rel="stylesheet" href="../node_modules/basiccontext/dist/addons/popin.min.css">
|
||||
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="nav"></div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div id="sideBarContainer">
|
||||
|
||||
<input id="dirSelect" list="directories" type="text">
|
||||
<datalist id="directories">
|
||||
<option value="dir0"/>
|
||||
<option value="dir1"/>
|
||||
<option value="dir2"/>
|
||||
</datalist>
|
||||
|
||||
|
||||
<div id="filterContainer">
|
||||
<form action="#">
|
||||
<a href="#">Filter</a>
|
||||
<input type="text" name="fname" value="" onkeydown="">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="treeContainer">
|
||||
<ul class="mktree" id="mainTree">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="welcome">
|
||||
<p>Welcome to PerfView!
|
||||
PerfView is an application designed to simplify the collection and analysis of performance data.
|
||||
Pressing the F1 key will give you help for current view. Any blue hyperlinks in the UI also take you to help. For an overview of PerfView see the User's Guide . If you wish to ask a question that the documentation did not answer or if you have feedback or a bug report, see the feedback instructions in the documentation.
|
||||
If you are new to PerfView , we strongly recommend reading the tutorial or watch the tutorial videos . It should only take 10-20 minutes to walk through. Also recommend is reading what perfView can do for you .
|
||||
Optimizing Time, or Unmanaged Memory: Collecting ETW Profile Data
|
||||
PerfView can collect a time based ETW trace for investigating CPU and Server Response Time, as well as Wall Clock / Blocked Time , Unmanaged memory or Managed memory . For these cases PerfView has two ways of collecting ETW trace data :
|
||||
• Run a command and collect data while the command is running. Collect->Run (Alt-R)
|
||||
• Collect data machine wide by starting and stopping collection manually. Collect->Collect (Alt-C)
|
||||
Optimizing Managed Memory: Collecting .NET Runtime Heap Data
|
||||
PerfView can also collect data about the GC heap of a .NET Runtime process. See GC Heap Quick Start for more details.
|
||||
• Take a heap snapshot of an existing process Memory->Take Heap Snapshot (Alt-S)
|
||||
To Take a heap snapshot from a Process Dump simply double click on the .DMP file in the file view to the left.
|
||||
|
||||
Viewing Existing Profile Data
|
||||
The result of data collection is data stored in data files of varying extensions. The left pane is a list of file names in the current directory that PerfView knows how to open. Simply double click on the file of interest to view the data. You can change the current directory by either double clicking on a subdirectory or typing the name in the textbox in the upper left.
|
||||
If the file contains more than one view of the data then double clicking on the object populates children nodes in the treeview. Double clicking on one of the children will open the viewer for that view. Right clicking and selecting 'Help on Item' will take you to the help for that particular entry in the viewer. There are two main viewers of profile data:
|
||||
• A stack viewer which allows aggregation and filtering of events with associated stack traces.
|
||||
• An event viewer which allows filtering of time based events.
|
||||
|
||||
Diffing Profile Data
|
||||
PerfView has the ability to determine the difference between two traces, which is very useful in doing regression analysis. To use the Diff functionality first view both traces in separate stack view windows and (if necessary) filter out all but the region that represents an equivalent amount of work in each view. Then use the Diff menu on the stack view window of the 'regressed' trace and to select the other window as its baseline. See Diff Help for more.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<footer id="footer"></footer>
|
||||
|
||||
|
||||
|
||||
<!-- ****************** SCRIPTS BELOW ****************** -->
|
||||
|
||||
|
||||
<!-- Do NOT Delete
|
||||
This allows for script tags and requires('') to work both in the browser and i Electron -->
|
||||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
|
||||
|
||||
|
||||
|
||||
<!-- Let's get some jQuery going here. -->
|
||||
<script type = "text/javascript" src = "../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<!-- alternate require method that works for Electron
|
||||
<script>window.$ = window.jQuery = require('./node_modules/jquery/dist/jquery.min.js')</script> -->
|
||||
|
||||
|
||||
<!-- Template Including -->
|
||||
<script>
|
||||
$.get('templates/navbar.html', function(data) {
|
||||
var template = $(data).children();
|
||||
$('.nav').append(template);
|
||||
resizeWelcomeDiv();
|
||||
});
|
||||
|
||||
$.get('templates/footer.html', function(data) {
|
||||
var template = $(data).children();
|
||||
$('#footer').append(template);
|
||||
resizeWelcomeDiv();
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../js/Delegate.js"></script>
|
||||
|
||||
|
||||
<!-- Allows for resizing of #welcome if there is too much text.
|
||||
This is unfortunately how it has to be done. Even Facebook.com has
|
||||
to do something like this for their footer (chat windows) bar. -->
|
||||
<script>
|
||||
var navBar, footer;
|
||||
|
||||
/* Try to avoid constantly traversing the DOM as much as we can. */
|
||||
function initVars() {
|
||||
navBar = document.getElementsByClassName("nav")[0];
|
||||
footer = document.getElementById("footer");
|
||||
resizeWelcomeDiv();
|
||||
}
|
||||
|
||||
function resizeWelcomeDiv() {
|
||||
var pageHeight = window.innerHeight;
|
||||
navBarHeight = navBar.offsetHeight;
|
||||
footerHeight = footer.offsetHeight;
|
||||
|
||||
var result = pageHeight - (navBarHeight + footerHeight);
|
||||
|
||||
// console.log("pageHeight - (navBarHeight + footerHeight): " + pageHeight + " - (" + navBarHeight + " + " + footerHeight + ") = " + result);
|
||||
document.getElementById("welcome").style.height = result + "px";
|
||||
}
|
||||
|
||||
window.onload = initVars;
|
||||
window.onresize = resizeWelcomeDiv;
|
||||
</script>
|
||||
|
||||
|
||||
<!-- basicContext Library -->
|
||||
<script src="../node_modules/basiccontext/dist/basicContext.min.js"></script>
|
||||
<!-- alternate require method that works for Electron
|
||||
<script>window.basicContext = require('./node_modules/basiccontext/dist/basicContext.min.js')</script> -->
|
||||
|
||||
<!-- basicContext Menu Test -->
|
||||
<script>
|
||||
document.querySelector('#treeContainer').addEventListener('contextmenu', function(e) {
|
||||
var clicked = function() { alert('Item clicked!') }
|
||||
let items = [
|
||||
{ title: 'Help on Item', icon: 'ion-help-buoy', fn: clicked, disabled: true },
|
||||
{ title: 'Refresh Dir', icon: 'ion-refresh', fn: clicked },
|
||||
{ title: 'Open', icon: 'ion-android-open', fn: clicked },
|
||||
{ title: 'Close', icon: 'ion-close', fn: clicked },
|
||||
{ title: 'Delete', icon: 'ion-android-remove-circle', fn: clicked },
|
||||
{ title: 'Rename', icon: 'ion-edit', fn: clicked },
|
||||
{ title: 'Merge', icon: 'ion-merge', fn: clicked },
|
||||
{ title: 'Make Local Symbol Dir', icon: 'ion-plus-round', fn: clicked },
|
||||
{ title: 'Zip', icon: 'ion-ios-briefcase', fn: clicked },
|
||||
{ title: 'UnZip', icon: 'ion-ios-briefcase-outline', fn: clicked }
|
||||
]
|
||||
|
||||
basicContext.show(items, e)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<!-- MKTREE Library -->
|
||||
<script type="text/javascript" src="../libraries/mktree/mktree.js"></script>
|
||||
<link rel="stylesheet" href="../libraries/mktree/mktree.css" type="text/css">
|
||||
|
||||
|
||||
<!-- GET Tree View Hierarchy -->
|
||||
<script>
|
||||
// GET all directories and items and append them to tree view
|
||||
$(document).ready( function() {
|
||||
treeView = delegate.httpGet("/api/data/open", function (response) {
|
||||
items = response.items;
|
||||
var htmlFromJSON = createTreeFromJSON(items);
|
||||
$("#mainTree").append(htmlFromJSON);
|
||||
delegate.log("Completed: View");
|
||||
});
|
||||
});
|
||||
|
||||
// Recursively parse JSON tree
|
||||
function createTreeFromJSON(items) {
|
||||
var htmlToAppend = "";
|
||||
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
|
||||
if (item.type == "dir"
|
||||
|| item.type == "directory"
|
||||
|| item.type == "file"
|
||||
|| item.type == "folder") {
|
||||
// It's a file
|
||||
htmlToAppend += "<li>" + item.name
|
||||
+ "<ul>\n"
|
||||
+ createTreeFromJSON(item.items)
|
||||
+ "\n</ul>\n" +
|
||||
"</li>";
|
||||
} else {
|
||||
// It's an item
|
||||
htmlToAppend += "<li>" + item.name + "</li>";
|
||||
}
|
||||
}
|
||||
|
||||
return htmlToAppend;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<!-- Do NOT Delete
|
||||
This allows for script tags and requires('') to work both in the browser and i Electron -->
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<footer id="footer">
|
||||
<div id="statusBar">
|
||||
<span>Completed: View (Elapsed Time: 0.070 sec)</span>
|
||||
</div>
|
||||
|
||||
<div id="toggle">
|
||||
<span>Ready</span>
|
||||
<a href="#">Log</a>
|
||||
<a href="#">Cancel</a>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,68 @@
|
||||
<div class="nav">
|
||||
<ul class="navItems top-level-dropdown">
|
||||
<li>
|
||||
<a href="#">File</a>
|
||||
<ul class="second-level-dropdown">
|
||||
<li><a href="#">Help</a></li>
|
||||
<li><a href="#">Clear Temp Files</a></li>
|
||||
<li><a href="#">Clear User Config</a></li>
|
||||
<li><a href="#">Set Symbol Path</a></li>
|
||||
<li><a href="#">User Command</a></li>
|
||||
<li><a href="#">Exit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Collect</a>
|
||||
<ul class="second-level-dropdown">
|
||||
<li><a href="#">Run</a></li>
|
||||
<li><a href="#">Collect</a></li>
|
||||
<li><a href="#">Abort</a></li>
|
||||
<li><a href="#">Merge</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Memory</a>
|
||||
<ul class="second-level-dropdown">
|
||||
<li><a href="#">Take Heap Snapshot</a></li>
|
||||
<li><a href="#">Take Heap Snapshot From Dump</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Size</a>
|
||||
<ul class="second-level-dropdown">
|
||||
<li><a href="#">Directory Size</a></li>
|
||||
<li><a href="#">Image Size</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Help</a>
|
||||
<ul class="second-level-dropdown">
|
||||
<li><a href="#">Help on Main View</a></li>
|
||||
<li><a href="#">Users Guide</a></li>
|
||||
<li><a href="#">Getting Started Videos</a></li>
|
||||
<li><a href="#">Get the Latest Version</a></li>
|
||||
<li><a href="#">Give Feedback or Ask a Question</a></li>
|
||||
<li><a href="#">Reference Guide</a></li>
|
||||
<li><a href="#">Command Line Help</a></li>
|
||||
<li><a href="#">User Command Help</a></li>
|
||||
<li><a href="#">Multi-Scenarios Analysis</a></li>
|
||||
<li><a href="#">Extending Perfview</a></li>
|
||||
<li><a href="#">Viewing Linux Data</a></li>
|
||||
<li><a href="#">Viewing External Data</a></li>
|
||||
<li><a href="#">Using EventSources</a></li>
|
||||
<li><a href="#">Release Notes</a></li>
|
||||
<li><a href="#">About</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navItems helpfulLinks">
|
||||
<li><a href="#">Main View Help</a></li>
|
||||
<li><a href="#">Tutorial</a></li>
|
||||
<li><a href="#">Troubleshooting</a></li>
|
||||
<li><a href="#">FAQ</a></li>
|
||||
<li><a href="#">Tips</a></li>
|
||||
<li><a href="#">Video</a></li>
|
||||
<li><a href="#">Feedback</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0-rc2-3002702",
|
||||
"type": "platform"
|
||||
},
|
||||
"Microsoft.ApplicationInsights.AspNetCore": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
|
||||
"Newtonsoft.Json": "8.0.3",
|
||||
"Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",
|
||||
"TraceEventCore": "1.0.41"
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
|
||||
"version": "1.0.0-preview1-final",
|
||||
"imports": "portable-net45+win8+dnxcore50"
|
||||
}
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"imports": [
|
||||
"dotnet5.6",
|
||||
"dnxcore50",
|
||||
"portable-net45+win8"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"buildOptions": {
|
||||
"emitEntryPoint": true,
|
||||
"preserveCompilationContext": true,
|
||||
"allowUnsafe": true
|
||||
},
|
||||
|
||||
"runtimeOptions": {
|
||||
"gcServer": true
|
||||
},
|
||||
|
||||
"publishOptions": {
|
||||
"include": [
|
||||
"wwwroot",
|
||||
"Views",
|
||||
"appsettings.json",
|
||||
"web.config"
|
||||
]
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<!--
|
||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
||||
-->
|
||||
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
|
||||
</handlers>
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user