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();
        }
Beispiel #2
0
        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);
                        }
                    }
                }
            }
        }