public void OnDataAvailable(IDataExtensionRetrieval requiredData) { // Gather the data from all the SQL tables var threadData = requiredData.QueryOutput <ProcessedEventData <PerfettoThreadEvent> >(new DataOutputPath(PerfettoPluginConstants.ThreadCookerPath, nameof(PerfettoThreadCooker.ThreadEvents))); var processData = requiredData.QueryOutput <ProcessedEventData <PerfettoProcessRawEvent> >(new DataOutputPath(PerfettoPluginConstants.ProcessRawCookerPath, nameof(PerfettoProcessRawCooker.ProcessEvents))); var perfSampleData = requiredData.QueryOutput <ProcessedEventData <PerfettoPerfSampleEvent> >(new DataOutputPath(PerfettoPluginConstants.PerfSampleCookerPath, nameof(PerfettoPerfSampleCooker.PerfSampleEvents))); var stackProfileCallSiteData = requiredData.QueryOutput <ProcessedEventData <PerfettoStackProfileCallSiteEvent> >(new DataOutputPath(PerfettoPluginConstants.StackProfileCallSiteCookerPath, nameof(PerfettoStackProfileCallSiteCooker.StackProfileCallSiteEvents))); var stackProfileFrameData = requiredData.QueryOutput <ProcessedEventData <PerfettoStackProfileFrameEvent> >(new DataOutputPath(PerfettoPluginConstants.StackProfileFrameCookerPath, nameof(PerfettoStackProfileFrameCooker.StackProfileFrameEvents))); var stackProfileMappingData = requiredData.QueryOutput <ProcessedEventData <PerfettoStackProfileMappingEvent> >(new DataOutputPath(PerfettoPluginConstants.StackProfileMappingCookerPath, nameof(PerfettoStackProfileMappingCooker.StackProfileMappingEvents))); // stackProfileSymbolData doesn't seem to have data now in the traces we have seen //var stackProfileSymbolData = requiredData.QueryOutput<ProcessedEventData<PerfettoStackProfileSymbolEvent>>(new DataOutputPath(PerfettoPluginConstants.StackProfileSymbolCookerPath, nameof(PerfettoStackProfileSymbolCooker.StackProfileSymbolEvents))); // We need to join a bunch of tables to get the cpu samples with stack and module information var joined = from perfSample in perfSampleData join thread in threadData on perfSample.Utid equals thread.Id join threadProcess in processData on thread.Upid equals threadProcess.Upid into pd from threadProcess in pd.DefaultIfEmpty() // left outer select new { perfSample, thread, threadProcess }; // stackProfileCallSite var stackWalker = new StackWalk(stackProfileCallSiteData, stackProfileFrameData, stackProfileMappingData); // Create events out of the joined results foreach (var result in joined) { // Walk the stack StackWalkResult stackWalkResult = null; if (result.perfSample.CallsiteId.HasValue) { stackWalkResult = stackWalker.WalkStack(result.perfSample.CallsiteId.Value); } // An event can have a thread+process or just a process string processName = string.Empty; string threadName = $"{result.thread.Name} ({result.thread.Tid})"; if (result.threadProcess != null) { processName = $"{result.threadProcess.Name} ({result.threadProcess.Pid})"; } var ev = new PerfettoCpuSamplingEvent ( processName, threadName, new Timestamp(result.perfSample.RelativeTimestamp), result.perfSample.Cpu, result.perfSample.CpuMode, result.perfSample.UnwindError, stackWalkResult?.Stack, stackWalkResult?.Module, stackWalkResult?.Function ); this.CpuSamplingEvents.AddEvent(ev); } this.CpuSamplingEvents.FinalizeData(); }
public void Process() { string line; uint methodToken = 0x06000001; string filename = Path.Combine(Path.GetDirectoryName(_outputFile), Path.GetFileNameWithoutExtension(_outputFile)); Guid pdbSig; uint pdbAge; string dllPath = CreatePdbFile(out pdbSig, out pdbAge); _allMethods.Clear(); int tickCount = 0; using (var reader = new StreamReader(_filename)) { var logCreationTime = DateTime.Now; var logCreationTimeStamp = Stopwatch.GetTimestamp(); var log = TraceLog.Start(filename + ".etl", null); try { EVENT_TRACE_HEADER header; ////////////////////////////////////// header = NewHeader(); //header.TimeStamp = currentTimeStamp++; header.Guid = TraceLog.ProcessEventGuid; header.Version = 3; header.Type = 3; var processInfo = new ProcessInfo(); processInfo.UniqueProcessKey = 100; processInfo.ProcessId = ProcessId; processInfo.ParentId = 100; log.Trace(header, processInfo, Encoding.ASCII.GetBytes("node.exe"), String.Empty); ////////////////////////////////////// header = NewHeader(); header.Guid = TraceLog.ThreadEventGuid; header.Version = 3; header.Type = 3; var threadInfo = new ThreadInfo(); threadInfo.ProcessId = ProcessId; // setup the thread as if it was a reasonable real thread threadInfo.TThreadId = ThreadId; threadInfo.Affinity = 0xFF; threadInfo.Win32StartAddr = 0x8888; threadInfo.UserStackBase = 0xF0000; threadInfo.UserStackLimit = 0xF00200; threadInfo.IoPriority = 2; log.Trace(header, threadInfo); ////////////////////////////////////// header = NewHeader(); //header.TimeStamp = currentTimeStamp++; header.Guid = TraceLog.ImageEventGuid; header.Version = 2; header.Type = 3; var imgLoad = new ImageLoad(); imgLoad.ImageBase = 0x10000; imgLoad.ImageSize = 10000; imgLoad.ProcessId = ProcessId; log.Trace(header, imgLoad, dllPath); ////////////////////////////////////// header = NewHeader(); header.Guid = TraceLog.LoaderEventGuid; header.Version = 2; header.Type = 33; var moduleLoad = new ManagedModuleLoadUnload(); moduleLoad.AssemblyID = 1; moduleLoad.ModuleID = JitModuleId; log.Trace(header, moduleLoad, dllPath, dllPath, (ushort)1, // clr instance ID pdbSig, // pdb sig pdbAge, // pdb age dllPath.Substring(0, dllPath.Length - 4) + ".pdb", // path to PDB, Guid.Empty, 0, String.Empty ); while ((line = reader.ReadLine()) != null) { var records = SplitRecord(line); if (records.Length == 0) { continue; } switch (records[0]) { case "shared-library": if (records.Length < 4) { continue; // missing info? } break; case "profiler": break; case "code-creation": int shift = (_nodeVersion >= v012 ? 1 : 0); if (records.Length < shift + 5) { continue; // missing info? } var startAddr = ParseAddress(records[shift + 2]); header = NewMethodEventHeader(); var methodLoad = new MethodLoadUnload(); methodLoad.MethodID = methodToken; methodLoad.ModuleID = JitModuleId; methodLoad.MethodStartAddress = startAddr; methodLoad.MethodSize = (uint)ParseAddress(records[shift + 3]); methodLoad.MethodToken = methodToken; methodLoad.MethodFlags = 8; var funcInfo = ExtractNamespaceAndMethodName(records[shift + 4], records[1]); string functionName = funcInfo.Function; if (funcInfo.IsRecompilation) { functionName += " (recompiled)"; } _codeAddresses[new AddressRange(startAddr, methodLoad.MethodSize)] = IsMyCode(funcInfo); log.Trace(header, methodLoad, funcInfo.Namespace, functionName, String.Empty /* signature*/); methodToken++; break; case "tick": if (records.Length < 5) { continue; } var addr = ParseAddress(records[1]); header = NewTickRecord(); var profileInfo = new SampledProfile(); profileInfo.InstructionPointer = (uint)addr; profileInfo.ThreadId = ThreadId; profileInfo.Count = 1; log.Trace(header, profileInfo); if (records.Length > 2) { header = NewStackWalkRecord(); var sw = new StackWalk(); // this timestamp is independent from the timestamp in our header, // and this one is not filled in for us by ETW. So we need to fill // it in here. sw.TimeStamp = (ulong)Stopwatch.GetTimestamp(); sw.Process = ProcessId; sw.Thread = ThreadId; // tick, ip, esp, ? [always zero], ?, always zero[?], stack IPs... const int nonStackFrames = 6; // count of records, including IP, which aren't stack addresses. List <object> args = new List <object>(); args.Add(sw); var ipAddr = ParseAddress(records[1]); if (ipAddr != 0 && ShouldIncludeCode(ipAddr)) { args.Add(ipAddr); } for (int i = nonStackFrames; i < records.Length; i++) { var callerAddr = ParseAddress(records[i]); if (callerAddr != 0 && ShouldIncludeCode(callerAddr)) { args.Add(callerAddr); } } if ((records.Length - nonStackFrames) == 0) { // idle CPU time sw.Process = 0; } tickCount++; log.Trace(header, args.ToArray()); } break; default: Console.WriteLine("Unknown record type: {0}", line); break; } } header = NewHeader(); header.Guid = TraceLog.ProcessEventGuid; header.Version = 3; header.Type = 4; // DCEnd processInfo = new ProcessInfo(); processInfo.UniqueProcessKey = 100; processInfo.ProcessId = ProcessId; processInfo.ParentId = 100; log.Trace(header, processInfo, Encoding.ASCII.GetBytes("node.exe"), String.Empty); } finally { log.Stop(); } RelogTrace(_executionTime.Value, tickCount, filename + ".etl"); } // save the VSPX file using (var stream = new FileStream(filename + ".vspx", FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, false)) { var entry = archive.CreateEntry("VSProfilingData\\" + Path.GetFileName(filename) + ".etl"); using (FileStream etlStream = new FileStream(filename + ".etl", FileMode.Open, FileAccess.Read)) { using (var entryStream = entry.Open()) { etlStream.CopyTo(entryStream); } } } } }