static void Main() { var knownPids = new HashSet <uint>(); _timer = new Timer(_ => ProcessCallstacks(), null, 3000, 3000); var symbols = new SortedDictionary <ulong, string>(); using var input = Console.OpenStandardInput(); var fileHeader = input.Read <PerfPipeFileHeader>(); if (fileHeader.Magic != MagicNumber) { Console.WriteLine($"Magic number mismatch. Expected {MagicNumber:x2}, found {fileHeader.Magic:x2}"); return; } bool endOfFile = false; PerfRecordIndexes?indexes = null; while (!endOfFile) { var header = input.Read <PerfEventHeader>(); switch (header.Type) { case PerfEventType.EndOfFile: endOfFile = true; break; case PerfEventType.RecordMmap: { var perfRecordMmap = new PerfRecordMmap(input, header); var filename = ReadString(perfRecordMmap.Filename); symbols.Add(perfRecordMmap.Addr, filename); ReadSymbols(filename, perfRecordMmap.Addr, perfRecordMmap.Pgoff, perfRecordMmap.Len, symbols); if (knownPids.Add(perfRecordMmap.Pid)) { ReadPerfMap(perfRecordMmap.Pid, symbols); } break; } case PerfEventType.LostEvents: Console.WriteLine("Lost events"); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordComm: //var perfRecordComm = new PerfRecordComm(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordExit: input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordFork: //var perfRecordFork = new PerfRecordFork(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordSample: if (indexes == null) { break; } var sample = new PerfRecordSample(input, header, indexes); var symbol = "UNKNOWN"; foreach (var kvp in symbols) { if (kvp.Key < sample.Addr) { symbol = kvp.Value; } else { break; } } _callstacks.Enqueue(symbol); break; case PerfEventType.RecordMmap2: { var perfRecordMmap2 = new PerfRecordMmap2(input, header); var filename = ReadString(perfRecordMmap2.filename); symbols.Add(perfRecordMmap2.Addr, filename); ReadSymbols(filename, perfRecordMmap2.Addr, perfRecordMmap2.Pgoff, perfRecordMmap2.Len, symbols); if (knownPids.Add(perfRecordMmap2.Pid)) { ReadPerfMap(perfRecordMmap2.Pid, symbols); } break; } case PerfEventType.RecordKSymbol: //var perfRecordKSymbol = new PerfRecordKSymbol(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordBpfEvent: //var perfRecordBpfEvent = new PerfRecordBpfEvent(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordHeaderAttr: var perfRecordHeaderAttr = new PerfRecordHeaderAttr(input, header); indexes = new PerfRecordIndexes(perfRecordHeaderAttr.Attr.SampleType); break; case PerfEventType.RecordFinishedRound: input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordThreadMap: //var perfRecordThreadMap = new PerfRecordThreadMap(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordCpuMap: //var perfRecordCpuMap = new PerfRecordCpuMap(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordEventUpdate: //var perfRecordEventUpdate = new PerfRecordEventUpdate(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordTimeConv: //var perfRecordTimeConv = new PerfRecordTimeConv(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordHeaderFeature: //var perfRecordHeaderFeature = new PerfRecordHeaderFeature(input, header); input.Skip(header.GetRemainingBytes()); break; default: throw new NotSupportedException("Header not supported: " + header.Type); } } }
static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Missing pid"); return; } if (!int.TryParse(args[0], out var pid)) { Console.WriteLine("Invalid pid"); return; } var map = new Dictionary <ulong, string>(); var symbols = new SortedDictionary <ulong, string>(); var perfMapFile = $"/tmp/perf-{pid}.map"; if (File.Exists(perfMapFile)) { foreach (var line in File.ReadLines(perfMapFile)) { var values = line.Split(' ', 3); map.Add(Convert.ToUInt64(values.First(), 16), values.Last()); symbols.Add(Convert.ToUInt64(values.First(), 16), values.Last()); } } Console.WriteLine($"Loaded {map.Count} symbols from {perfMapFile}"); using var input = Console.OpenStandardInput(); var fileHeader = input.Read <PerfPipeFileHeader>(); if (fileHeader.Magic != MagicNumber) { Console.WriteLine($"Magic number mismatch. Expected {MagicNumber:x2}, found {fileHeader.Magic:x2}"); return; } bool endOfFile = false; PerfRecordIndexes indexes = null; while (!endOfFile) { var header = input.Read <PerfEventHeader>(); switch (header.Type) { case PerfEventType.EndOfFile: endOfFile = true; break; case PerfEventType.RecordMmap: var perfRecordMmap = new PerfRecordMmap(input, header); if (perfRecordMmap.Pid == pid) { var filename = ReadString(perfRecordMmap.Filename); symbols.Add(perfRecordMmap.Addr, filename); ReadSymbols(filename, perfRecordMmap.Addr, perfRecordMmap.Pgoff, perfRecordMmap.Len, symbols); } break; case PerfEventType.LostEvents: Console.WriteLine("Lost events"); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordComm: //var perfRecordComm = new PerfRecordComm(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordExit: input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordFork: //var perfRecordFork = new PerfRecordFork(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordSample: var sample = new PerfRecordSample(input, header, indexes); if (sample.Pid == pid) { Console.WriteLine("Sample:"); foreach (var frame in sample.Callchain) { if (!map.TryGetValue(frame, out var symbol)) { symbol = "UNKNOWN"; foreach (var kvp in symbols) { if (kvp.Key < frame) { symbol = kvp.Value; } else { break; } } } Console.WriteLine($"{frame:x2} {symbol}"); } } break; case PerfEventType.RecordMmap2: var perfRecordMmap2 = new PerfRecordMmap2(input, header); if (perfRecordMmap2.Pid == pid) { var filename = ReadString(perfRecordMmap2.filename); symbols.Add(perfRecordMmap2.Addr, filename); ReadSymbols(filename, perfRecordMmap2.Addr, perfRecordMmap2.Pgoff, perfRecordMmap2.Len, symbols); } break; case PerfEventType.RecordKSymbol: //var perfRecordKSymbol = new PerfRecordKSymbol(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordBpfEvent: //var perfRecordBpfEvent = new PerfRecordBpfEvent(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordHeaderAttr: var perfRecordHeaderAttr = new PerfRecordHeaderAttr(input, header); foreach (ulong value in Enum.GetValues(typeof(PerfEventSampleFormat))) { if (((ulong)perfRecordHeaderAttr.Attr.SampleType & value) == value) { Console.WriteLine((PerfEventSampleFormat)value); } } indexes = new PerfRecordIndexes(perfRecordHeaderAttr.Attr.SampleType); break; case PerfEventType.RecordFinishedRound: input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordThreadMap: //var perfRecordThreadMap = new PerfRecordThreadMap(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordCpuMap: //var perfRecordCpuMap = new PerfRecordCpuMap(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordEventUpdate: //var perfRecordEventUpdate = new PerfRecordEventUpdate(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordTimeConv: //var perfRecordTimeConv = new PerfRecordTimeConv(input, header); input.Skip(header.GetRemainingBytes()); break; case PerfEventType.RecordHeaderFeature: //var perfRecordHeaderFeature = new PerfRecordHeaderFeature(input, header); input.Skip(header.GetRemainingBytes()); break; default: throw new NotSupportedException("Header not supported: " + header.Type); } } }