static void Main(string[] args) { bool showCPUUsage = false; string traceName = ""; foreach (string arg in args) { if (arg == "-c" || arg == "--cpuusage") { showCPUUsage = true; } else if (traceName.Length == 0) { traceName = arg; } else { Console.Error.WriteLine("error: unrecognized arguments: {0}", arg); return; } } if (traceName.Length == 0) { Console.Error.WriteLine("usage: IdentifyChromeProcesses.exe [-c] trace"); Console.Error.WriteLine("error: too few arguments"); return; } if (!File.Exists(traceName)) { Console.Error.WriteLine("File '{0}' does not exist.", traceName); return; } try { using (ITraceProcessor trace = TraceProcessor.Create(traceName)) ProcessTrace(trace, showCPUUsage, false); } catch (System.InvalidOperationException e) { // Note that wpaexporter doesn't seem to have a way to handle this, // which is one advantage of TraceProcessing. Note that traces with // lost events are "corrupt" in some sense so the results will be // unpredictable. Console.WriteLine(e.Message); Console.WriteLine("Trying again with AllowLostEvents and AllowTimeInversion specified. Results may be less reliable."); Console.WriteLine(); var settings = new TraceProcessorSettings(); settings.AllowLostEvents = true; settings.AllowTimeInversion = true; using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings)) ProcessTrace(trace, showCPUUsage, true); } }
public static int Main(string[] args) { if (args.Length != 1) { Console.Error.WriteLine("Usage: FindZombieProcess.exe <trace.etl>"); return(1); } string tracePath = args[0]; TraceProcessorSettings settings = new TraceProcessorSettings { AllowLostEvents = true }; using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings)) { IPendingResult <IHandleDataSource> pendingHandleData = trace.UseHandles(); IPendingResult <ISymbolDataSource> pendingSymbolData = trace.UseSymbols(); trace.Process(); IHandleDataSource handleData = pendingHandleData.Result; ISymbolDataSource symbolData = pendingSymbolData.Result; symbolData.LoadSymbolsForConsoleAsync(SymCachePath.Automatic, SymbolPath.Automatic).GetAwaiter().GetResult(); foreach (IProcessHandle processHandle in handleData.ProcessHandles) { // Zombie processes are processes which have exited but which still have a running process holding a handle to them if (processHandle.Process != null && !processHandle.CloseTime.HasValue && processHandle.Process.ExitTime.HasValue) { string owningProcessName = processHandle.Owner?.ImageName ?? "Unknown"; string targetProcessName = processHandle.Process?.ImageName ?? "Unknown"; Console.WriteLine($"Owning process: {owningProcessName} has handle to: {targetProcessName}"); } } return(0); } }
static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Specify the name of one trace to be summarized."); return; } var traceName = args[0]; if (!File.Exists(traceName)) { Console.Error.WriteLine("File '{0}' does not exist.", traceName); return; } var settings = new TraceProcessorSettings { // Don't print a setup message on first run. SuppressFirstTimeSetupMessage = true }; using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings)) { // Get process details, including command lines. var pendingProcessData = trace.UseProcesses(); // Get CPU performance counters, on every context switch. var pendingCounterData = trace.UseProcessorCounters(); trace.Process(); var processData = pendingProcessData.Result; var counterData = pendingCounterData.Result; var countersByProcess = FindInterestingProcesses(processData); // Accumulate data for all of the interesting processes. foreach (var entry in counterData.ContextSwitchCounterDeltas) { // This sometimes happens - handle it. if (entry.Process == null) { continue; } Counters last; if (!countersByProcess.TryGetValue(entry.Process, out last)) { continue; } // Accumulate counter values and execution time. foreach (var key in entry.RawCounterDeltas.Keys) { last.counters.TryGetValue(key, out ulong lastCount); lastCount += entry.RawCounterDeltas[key]; last.counters[key] = lastCount; } last.runTime_ns += (entry.StopTime - entry.StartTime).Nanoseconds; last.contextSwitches += 1; countersByProcess[entry.Process] = last; } // Sort the data by CPU time and print it. var sortedCounterData = new List <KeyValuePair <IProcess, Counters> >(countersByProcess); sortedCounterData.Sort((x, y) => y.Value.runTime_ns.CompareTo(x.Value.runTime_ns)); bool printHeader = true; foreach (var entry in sortedCounterData) { if (printHeader) { Console.Write("{0,-29} - CPU time (s) - context switches", "Image name"); foreach (var counterName in entry.Value.counters.Keys) { int fieldWidth = Math.Max(13, counterName.Length); Console.Write(", {0}", counterName.PadLeft(fieldWidth)); } Console.WriteLine(); printHeader = false; } // Arbitrary cutoff for what is "interesting" if (entry.Value.runTime_ns < 100 * 1000 * 1000) { continue; } Console.Write("{0,-29} - {1,8:0.00} - {2,16}", entry.Value.description, entry.Value.runTime_ns / 1e9, entry.Value.contextSwitches); foreach (var counterName in entry.Value.counters.Keys) { int fieldWidth = Math.Max(13, counterName.Length); Console.Write(", {0}", entry.Value.counters[counterName].ToString().PadLeft(fieldWidth)); } Console.WriteLine(); } } }
static void Main(string[] args) { string traceName = null; string[] moduleList = null; for (int i = 0; i < args.Length; /**/) { if (args[i] == "-modules") { ++i; if (i >= args.Length) { Console.Error.WriteLine("Missing module list after -modules."); return; } moduleList = args[i++].Split(';'); } else { if (traceName != null) { Console.Error.WriteLine("Unexpected argument '{0}'", args[i]); return; } traceName = args[i++]; } } if (traceName == null) { Console.Error.WriteLine("usage: gWindowsETLSummary.exe trace.etl [-modules module1.dll;module2.dll"); Console.Error.WriteLine("error: too few arguments"); Console.Error.WriteLine("The (case sensitive) -modules arguments are used to get inclusive CPU sampling data."); return; } if (!File.Exists(traceName)) { // Print a more friendly error message for this case. Console.Error.WriteLine("File '{0}' does not exist.", traceName); return; } Console.WriteLine("Processing {0}...", traceName); var settings = new TraceProcessorSettings { // Don't print a setup message on first run. SuppressFirstTimeSetupMessage = true }; try { using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings)) ProcessTrace(trace, moduleList); } catch (TraceLostEventsException e) { // Note that wpaexporter doesn't seem to have a way to handle this, // which is one advantage of TraceProcessing. Note that traces with // lost events are "corrupt" in some sense so the results will be // unpredictable. Console.WriteLine(e.Message); Console.WriteLine("Trying again with AllowLostEvents specified. Results may be less reliable."); Console.WriteLine(); settings.AllowLostEvents = true; using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings)) ProcessTrace(trace, moduleList); } }
private static int Main(string[] cmdLineArgs) { ParserResult <CommandLineArguments> o = Parser.Default.ParseArguments <CommandLineArguments>(cmdLineArgs); return(o.MapResult( options => { string sidecarJson = File.ReadAllText(options.SideCarFile); CLogSidecar sidecar = CLogSidecar.FromJson(sidecarJson); TextReader file = Console.In; if (!File.Exists(options.ETLFile)) { TraceLine(TraceType.Err, $"ETL File {options.ETLFile} doesnt exist"); return -1; } StreamWriter outputfile = null; if (!String.IsNullOrEmpty(options.OutputFile)) { outputfile = new StreamWriter(new FileStream(options.OutputFile, FileMode.Create)); } try { TraceProcessorSettings traceSettings = new TraceProcessorSettings { AllowLostEvents = true, AllowTimeInversion = true }; using (ITraceProcessor etwfile = TraceProcessor.Create(options.ETLFile, traceSettings)) { HashSet <Guid> ids = new HashSet <Guid>(); foreach (var m in sidecar.EventBundlesV2) { foreach (var prop in m.Value.ModuleProperites) { if (prop.Key.Equals("MANIFESTED_ETW")) { ids.Add(new Guid(prop.Value["ETW_Provider"])); } else if (prop.Key.Equals("TRACELOGGING")) { ids.Add(new Guid(prop.Value["ETW_Provider"])); } } } var events = etwfile.UseGenericEvents(ids.ToArray()); etwfile.Process(); foreach (var e in events.Result.Events) { string line = ""; try { Dictionary <string, IClogEventArg> fixedUpArgs = new Dictionary <string, IClogEventArg>(); string errorString = "ERROR"; if (null == e.Fields) { continue; } Dictionary <string, IClogEventArg> args = new Dictionary <string, IClogEventArg>(); foreach (var f in e.Fields) { args[f.Name] = new ManifestedETWEvent(f); } CLogDecodedTraceLine bundle = null; int eidAsInt = -1; foreach (var b in sidecar.EventBundlesV2) { Dictionary <string, string> keys; if (!e.IsTraceLogging) { if (!b.Value.ModuleProperites.TryGetValue("MANIFESTED_ETW", out keys)) { continue; } string eid; if (!keys.TryGetValue("EventID", out eid)) { continue; } eidAsInt = Convert.ToInt32(eid); if (eidAsInt == e.Id) { bundle = b.Value; errorString = "ERROR:" + eidAsInt; break; } } else { if (e.ActivityName.Equals(b.Key)) { bundle = b.Value; errorString = "ERROR:" + b.Key; break; } } } if (null == bundle) { continue; } Dictionary <string, string> argMap; if (e.IsTraceLogging) { argMap = new Dictionary <string, string>(); foreach (var arg in args) { argMap[arg.Key] = arg.Key; } } else { argMap = sidecar.GetTracelineMetadata(bundle, "MANIFESTED_ETW"); } var types = CLogFileProcessor.BuildTypes(sidecar.ConfigFile, null, bundle.TraceString, null, out string clean); if (0 == types.Length) { errorString = bundle.TraceString; goto toPrint; } int argIndex = 0; foreach (var type in types) { var arg = bundle.splitArgs[argIndex]; CLogEncodingCLogTypeSearch node = sidecar.ConfigFile.FindType(arg); switch (node.EncodingType) { case CLogEncodingType.Synthesized: continue; case CLogEncodingType.Skip: continue; } string lookupArgName = argMap[arg.VariableInfo.SuggestedTelemetryName]; if (!args.ContainsKey(lookupArgName)) { Console.WriteLine($"Argmap missing {lookupArgName}"); throw new Exception("InvalidType : " + node.DefinationEncoding); } if (0 != node.DefinationEncoding.CompareTo(type.TypeNode.DefinationEncoding)) { Console.WriteLine("Invalid Types in Traceline"); throw new Exception("InvalidType : " + node.DefinationEncoding); } fixedUpArgs[arg.VariableInfo.SuggestedTelemetryName] = args[lookupArgName]; ++argIndex; } toPrint: EventInformation ei = new EventInformation(); ei.Timestamp = e.Timestamp.DateTimeOffset; ei.ProcessId = e.ProcessId.ToString("x"); ei.ThreadId = e.ThreadId.ToString("x"); DecodeAndTraceToConsole(outputfile, bundle, errorString, sidecar.ConfigFile, fixedUpArgs, ei, options.ShowTimestamps, options.ShowCPUInfo); } catch (Exception) { Console.WriteLine($"Invalid TraceLine : {line}"); } } } } catch (Exception e) { CLogConsoleTrace.TraceLine(TraceType.Err, "ERROR : " + e); if (null != outputfile) { outputfile.WriteLine("ERROR : " + e); } } finally { if (null != outputfile) { outputfile.Flush(); outputfile.Close(); } } return 0; }, err => { Console.WriteLine("Bad Args : " + err); return -1; })); }
static int Main(string[] args) { if (args.Length != 1) { Console.Error.WriteLine("Usage: PotentialDelayLoads.exe <trace.etl>"); return(1); } string tracePath = args[0]; var settings = new TraceProcessorSettings { AllowLostEvents = true, }; using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings)) { IPendingResult <IReferenceSetDataSource> pendingReferenceSet = trace.UseReferenceSetData(); IPendingResult <IProcessDataSource> pendingProcesses = trace.UseProcesses(); IPendingResult <ISymbolDataSource> pendingSymbols = trace.UseSymbols(); IPendingResult <IImageSectionDataSource> pendingImageSections = trace.UseImageSections(); trace.Process(); IProcessDataSource processData = pendingProcesses.Result; IReferenceSetDataSource referenceSetData = pendingReferenceSet.Result; ISymbolDataSource symbolData = pendingSymbols.Result; IImageSectionDataSource imageSectionData = pendingImageSections.Result; symbolData.LoadSymbolsForConsoleAsync(SymCachePath.Automatic, SymbolPath.Automatic).GetAwaiter().GetResult(); // // Create a mapping of all static images loaded into all processes during the course of the trace. // This is a mapping of images to a dictionary of [processes, IsPotentialDelayLoadTarget] // Dictionary <string, Dictionary <string, bool> > potentialDelayLoads = new Dictionary <string, Dictionary <string, bool> >(); // // Keep track of the image data for all of the images we've seen loaded. We use this later to look up // section names for the offsets being accessed. // Dictionary <string, IImage> imageData = new Dictionary <string, IImage>(); foreach (var proc in processData.Processes) { foreach (var image in proc.Images) { string processName = GenerateProcessNameString(proc); if (image.LoadTime != null) { Dictionary <string, bool> processDict; if (!potentialDelayLoads.ContainsKey(image.Path)) { processDict = new Dictionary <string, bool>(); potentialDelayLoads.Add(image.Path, processDict); } else { processDict = potentialDelayLoads[image.Path]; } if (!processDict.ContainsKey(processName)) { bool eligibleForDelayLoad = (image.LoadReason == ImageLoadReason.StaticDependency); processDict.Add(processName, eligibleForDelayLoad); } // // Save off whether or not this image is a potential delay load target. We only consider // static dependencies for delay loads. // processDict[processName] = processDict[processName] && (image.LoadReason == ImageLoadReason.StaticDependency); // // Save off a pointer to the image data for this image so we can look up sections later // if (!imageData.ContainsKey(image.Path)) { imageData.Add(image.Path, image); } } } } // // Enumerate every page access. We're going to check each one to see if it was a 'code' page being accessed, // and if it was we conclude that code from this image was used during the trace by that process. Therefore, // it's not something that should be delay loaded. // foreach (IReferenceSetInterval refSetInterval in referenceSetData.Intervals) { foreach (IReferenceSetAccessedPage pageAccess in refSetInterval.PageAccesses) { // // Make sure the page was accessed from the usermode process. // if (pageAccess.ImpactedProcess == null) { continue; } // // Ignore the memory compression process. This is a system service. // if (pageAccess.ImpactedProcess.ImageName.Equals("MemCompression")) { continue; } // // Make sure we have a file path // if (pageAccess?.Page?.Path == null) { continue; } var fileBeingAccessed = pageAccess?.Page?.Path; // // Not all file paths are images (think MFT or data files). Make sure this is in our image // dictionary. // if (!imageData.ContainsKey(pageAccess.Page.Path)) { continue; } // // Make sure that this image was listed in the image data // if (!potentialDelayLoads.ContainsKey(fileBeingAccessed)) { continue; } // // Grab the image data, and use this to get the info on the page that was being accessed. // var data = imageData[pageAccess.Page.Path]; var sectionName = GetSectionNameFromPage(pageAccess, data, imageSectionData, pageAccess.ImpactedProcess); // // We really only want consider .text pages, as we want to find images where the 'code' is never // used. We have to include "unknown" as well since this is what shows up for images that we // can't find symbols for. This effectively means for images without symbols we consider all pages. // if (!(sectionName.Contains(".text") || sectionName.Contains("Unknown"))) { continue; } // // If the loader accessed the page, it's still a potential delay load candidiate. // if (IsLoaderAccessedPage(pageAccess)) { continue; } // // A .text page was accessed from somewhere other then the loader. This image isn't // a delay load candidate for this process. // string processName = GenerateProcessNameString(pageAccess.ImpactedProcess); if ((potentialDelayLoads[fileBeingAccessed]).ContainsKey(processName)) { if ((potentialDelayLoads[fileBeingAccessed])[processName]) { (potentialDelayLoads[fileBeingAccessed])[processName] = false; } } else { potentialDelayLoads[fileBeingAccessed].Add(processName, false); } } } // // Print out all potential delays loads we found. We modify the output format to be in // process->image style for easier consumption from the console. // List <Tuple <string, string> > delayLoads = new List <Tuple <string, string> >(); foreach (var imagesLoaded in potentialDelayLoads) { foreach (var processesDict in imagesLoaded.Value) { if (processesDict.Value == true) { delayLoads.Add(new Tuple <string, string>(processesDict.Key, imagesLoaded.Key)); } } } delayLoads.Sort(); foreach (var delayload in delayLoads) { Console.WriteLine("{0} can delay load {1}", delayload.Item1, delayload.Item2); } } return(0); }
public static int Main(string[] args) { if (args.Length != 1) { Console.Error.WriteLine("Usage: CyclesPerInstruction.exe <trace.etl>"); return(1); } string tracePath = args[0]; TraceProcessorSettings settings = new TraceProcessorSettings { AllowLostEvents = true }; using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings)) { IPendingResult <IProcessorCounterDataSource> pendingCounterData = trace.UseProcessorCounters(); trace.Process(); IProcessorCounterDataSource counterData = pendingCounterData.Result; if (!counterData.HasCycleCount) { Console.Error.WriteLine("Trace does not contain cycle count data."); return(2); } if (!counterData.HasInstructionCount) { Console.Error.WriteLine("Trace does not contain instruction count data."); return(2); } Dictionary <string, ulong> cyclesByProcess = new Dictionary <string, ulong>(); Dictionary <string, ulong> instructionsByProcess = new Dictionary <string, ulong>(); foreach (IProcessorCounterContextSwitchDelta delta in counterData.ContextSwitchCounterDeltas) { string processName = delta.Thread?.Process?.ImageName ?? "Unknown"; if (!cyclesByProcess.ContainsKey(processName)) { cyclesByProcess.Add(processName, 0); instructionsByProcess.Add(processName, 0); } cyclesByProcess[processName] += delta.CycleCount.Value; instructionsByProcess[processName] += delta.InstructionCount.Value; } foreach (string processName in cyclesByProcess.Keys.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)) { ulong cycles = cyclesByProcess[processName]; ulong instructions = instructionsByProcess[processName]; decimal cyclesPerInstruction = ((decimal)cycles) / instructions; Console.WriteLine($"{processName}: Cycles: {cycles}; Instructions: {instructions}; " + $"CPI: {cyclesPerInstruction:0.####}"); } return(0); } }
public static int Main(string[] args) { if (args.Length != 1) { Console.Error.WriteLine("Usage: OutstandingHandleCountByProcess.exe <trace.etl>"); return(1); } string tracePath = args[0]; TraceProcessorSettings settings = new TraceProcessorSettings { AllowLostEvents = true }; using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings)) { IPendingResult <IHandleDataSource> pendingHandleData = trace.UseHandles(); trace.Process(); IHandleDataSource handleData = pendingHandleData.Result; // Dictionary Key is Owning Process Name // Dictionary Value is a struct containing outstanding handle counts by type (Process Handles & Other Handles) Dictionary <string, HandleCounts> outstandingHandleCounts = new Dictionary <string, HandleCounts>(); foreach (IProcessHandle processHandle in handleData.ProcessHandles) { if (!processHandle.CloseTime.HasValue) { string owningProcessName = processHandle.Owner?.ImageName ?? "Unknown"; if (outstandingHandleCounts.ContainsKey(owningProcessName)) { HandleCounts handleCounts = outstandingHandleCounts[owningProcessName]; ++handleCounts.ProcessHandleCount; outstandingHandleCounts[owningProcessName] = handleCounts; } else { outstandingHandleCounts[owningProcessName] = new HandleCounts(1, 0); } } } foreach (IHandle otherHandle in handleData.OtherHandles) { if (!otherHandle.CloseTime.HasValue) { string owningProcessName = otherHandle.Owner?.ImageName ?? "Unknown"; if (outstandingHandleCounts.ContainsKey(owningProcessName)) { HandleCounts handleCounts = outstandingHandleCounts[owningProcessName]; ++handleCounts.OtherHandleCount; outstandingHandleCounts[owningProcessName] = handleCounts; } else { outstandingHandleCounts[owningProcessName] = new HandleCounts(0, 1); } } } foreach (string process in outstandingHandleCounts.Keys) { int openProcessHandleCount = outstandingHandleCounts[process].ProcessHandleCount; int openOtherHandleCount = outstandingHandleCounts[process].OtherHandleCount; Console.WriteLine($"Owning process: {process}"); Console.WriteLine($"\t{openProcessHandleCount} outstanding Process Handles" + $"\t{openOtherHandleCount} outstanding Other Handles"); } return(0); } }