/// <summary> /// Peeks ahead on source until we see \s+\d+/\d (that is space num/num) and returns the index to first (space) /// character in the pattern. Returns -1 if not found. /// /// We need this complex regular expression because process names in linux can have spaces and numbers and slashes /// in them For example here is a real process name (rs:action 13 qu) or (kworker/1:3) /// </summary> private static int FindSpaceNumSlash(FastStream source) { uint idx = 0; startOver: int firstSpaceIdx = -1; bool seenDigit = false; for (; ;) { idx++; if (idx >= source.MaxPeek - 1) { return(-1); } byte val = source.Peek(idx); if (firstSpaceIdx < 0) { if (char.IsWhiteSpace((char)val)) { firstSpaceIdx = (int)idx; } else { goto startOver; } } else if (!seenDigit) { if (char.IsDigit((char)val)) { seenDigit = true; } else if (!char.IsWhiteSpace((char)val)) { goto startOver; } } else { if (val == '/' && char.IsDigit((char)source.Peek(idx + 1))) { return(firstSpaceIdx); } else if (!char.IsDigit((char)val)) { goto startOver; } } } }
private List <Frame> ReadFramesForSample(string command, int processID, int threadID, Frame threadTimeFrame, FastStream source) { List <Frame> frames = new List <Frame>(); if (threadTimeFrame != null) { frames.Add(threadTimeFrame); } while (!this.IsEndOfSample(source, source.Current, source.Peek(1))) { StackFrame stackFrame = this.ReadFrame(source); if (this.mapper != null && (stackFrame.Module == "unknown" || stackFrame.Symbol == "unknown")) { string[] moduleSymbol = this.mapper.ResolveSymbols(processID, stackFrame.Module, stackFrame); stackFrame = new StackFrame(stackFrame.Address, moduleSymbol[0], moduleSymbol[1]); } frames.Add(stackFrame); } frames.Add(new ThreadFrame(threadID, "Thread")); frames.Add(new ProcessFrame(command)); return(frames); }
public void PeekingWhileEnough() { FastStream stream = this.GetTestStream(); SkipBOM(stream); Assert.Equal("2", ((char)stream.Peek(1)).ToString()); }
private bool TryGetCompleteBuffer(FastStream source, uint startLook, double portion, int maxLength, out uint length) { Contract.Requires(source != null, nameof(source)); length = (uint)(startLook * portion); if (source.Peek(startLook) == 0) { return(true); } uint lastNewLine = length; for (uint i = length; i < maxLength; i++) { byte current = source.Peek(i); if (this.parser.IsEndOfSample(source, current, source.Peek(i + 1))) { length = i; return(true); } if (current == '\n') { lastNewLine = length; } } if (portion < 0.5) { length = lastNewLine; return(false); } return(this.TryGetCompleteBuffer(source, startLook, portion * 0.8, maxLength, out length)); }
private IEnumerable <LinuxEvent> NextEvent(Regex regex, FastStream source) { string line = string.Empty; while (true) { source.SkipWhiteSpace(); if (source.EndOfStream) { break; } EventKind eventKind = EventKind.Cpu; StringBuilder sb = new StringBuilder(); // Command - Stops at first number AFTER whitespace while (!this.IsNumberChar((char)source.Current)) { sb.Append(' '); source.ReadAsciiStringUpToTrue(sb, delegate(byte c) { return(!char.IsWhiteSpace((char)c)); }); source.SkipWhiteSpace(); } string comm = sb.ToString().Trim(); sb.Clear(); // Process ID int pid = source.ReadInt(); source.MoveNext(); // Move past the "/" // Thread ID int tid = source.ReadInt(); // CPU source.SkipWhiteSpace(); source.MoveNext(); // Move past the "[" int cpu = source.ReadInt(); source.MoveNext(); // Move past the "]" // Time source.SkipWhiteSpace(); source.ReadAsciiStringUpTo(':', sb); double time = double.Parse(sb.ToString()) * 1000; // To convert to MSec sb.Clear(); source.MoveNext(); // Move past ":" // Time Property source.SkipWhiteSpace(); int timeProp = -1; if (this.IsNumberChar((char)source.Current)) { timeProp = source.ReadInt(); } // Event Name source.SkipWhiteSpace(); source.ReadAsciiStringUpTo(':', sb); string eventName = sb.ToString(); sb.Clear(); source.MoveNext(); // Event Properties // I mark a position here because I need to check what type of event this is without screwing up the stream var markedPosition = source.MarkPosition(); source.ReadAsciiStringUpTo('\n', sb); string eventDetails = sb.ToString().Trim(); sb.Clear(); if (eventDetails.Length >= SchedulerEvent.Name.Length && eventDetails.Substring(0, SchedulerEvent.Name.Length) == SchedulerEvent.Name) { eventKind = EventKind.Scheduler; } // Now that we know the header of the trace, we can decide whether or not to skip it given our pattern if (regex != null && !regex.IsMatch(eventName)) { while (true) { source.MoveNext(); if (this.IsEndOfSample(source, source.Current, source.Peek(1))) { break; } } yield return(null); } else { LinuxEvent linuxEvent; Frame threadTimeFrame = null; // For the sake of immutability, I have to do a similar if-statement twice. I'm trying to figure out a better way // but for now this will do. ScheduleSwitch schedSwitch = null; if (eventKind == EventKind.Scheduler) { source.RestoreToMark(markedPosition); schedSwitch = this.ReadScheduleSwitch(source); source.SkipUpTo('\n'); } IEnumerable <Frame> frames = this.ReadFramesForSample(comm, pid, tid, threadTimeFrame, source); if (eventKind == EventKind.Scheduler) { linuxEvent = new SchedulerEvent(comm, tid, pid, time, timeProp, cpu, eventName, eventDetails, frames, schedSwitch); } else { linuxEvent = new CpuEvent(comm, tid, pid, time, timeProp, cpu, eventName, eventDetails, frames); } yield return(linuxEvent); } } }
public bool IsEndOfSample(FastStream source) { return(this.IsEndOfSample(source, source.Current, source.Peek(1))); }
/// <summary> /// This routine should be called at the start of a line after you have skipped whitespace. /// /// Logically a line starts with PROCESS_COMMAND PID/TID [CPU] TIME: /// /// However PROCESS_COMMAND is unfortunately free form, including the fact hat it can have / or numbers in it. /// For example here is a real PROCESS_COMMAND examples (rs:action 13 qu) or (kworker/1:3) /// Thus it gets tricky to know when the command stops and the PID/TID starts. /// /// We use the following regular expression to determine the end of the command /// /// \s*\d+/\d OR /// ^\d+/\d THIS PATTERN IS NEEDED BECAUSE THE PROCESS_COMMAND MAY BE EMPTY. /// /// This routine peeks forward looking for this pattern, and returns either the index to the start of it or -1 if not found. /// </summary> private static int FindEndOfProcessCommand(FastStream source) { uint idx = 0; startOver: int firstSpaceIdx = -1; bool seenDigit = false; // Deal with the case where the COMMAND is empty. // Thus we have ANY spaces before the proceed ID Thread ID Num/Num. // We can deal with this case by 'jump starting the state machine state if it starts with a digit. if (char.IsDigit((char)source.Peek(0))) { firstSpaceIdx = 0; seenDigit = true; } for (; ;) { idx++; if (idx >= source.MaxPeek - 1) { return(-1); } byte val = source.Peek(idx); if (val == '\n') { Debug.Assert(false, "Could not parse process command"); return(-1); } if (firstSpaceIdx < 0) { if (char.IsWhiteSpace((char)val)) { firstSpaceIdx = (int)idx; } else { goto startOver; } } else if (!seenDigit) { if (char.IsDigit((char)val)) { seenDigit = true; } else if (!char.IsWhiteSpace((char)val)) { goto startOver; } } else { if (val == '/' && char.IsDigit((char)source.Peek(idx + 1))) { return(firstSpaceIdx); } else if (!char.IsDigit((char)val)) { goto startOver; } } } }
public void PeekingWithNoBuffer() { FastStream stream = this.GetTestStream(); Assert.Equal("1", ((char)stream.Peek(4)).ToString()); }
private List <Frame> ReadFramesForSample(string command, int processID, int threadID, Frame threadTimeFrame, FastStream source) { List <Frame> frames = new List <Frame>(); if (threadTimeFrame != null) { frames.Add(threadTimeFrame); } while (!IsEndOfSample(source, source.Current, source.Peek(1))) { StackFrame stackFrame = ReadFrame(source); if (mapper != null && (stackFrame.Module == "unknown" || stackFrame.Symbol == "unknown")) { string[] moduleSymbol = mapper.ResolveSymbols(processID, stackFrame.Module, stackFrame); stackFrame = new StackFrame(stackFrame.Address, moduleSymbol[0], moduleSymbol[1]); } if (stackFrame.Module.StartsWith("jitted-") && stackFrame.Module.EndsWith(".so") && stackFrame.Symbol.EndsWith(")")) { // Jitted or R2R code. Replace the module with the IL module name, and shorten the symbol. // Example: uint8[] [System.Private.CoreLib] Internal.IO.File::ReadAllBytes(string) // Example: instance uint8[] [System.Private.CoreLib] Internal.IO.File::ReadAllBytes(string) // Start at the end of the string, which should be ')'. Walk until we find the matching '('. string symbol = stackFrame.Symbol; int currentIndex = symbol.Length - 1; int endIndex = 0; int parenDepth = 0; while (currentIndex >= endIndex) { char current = symbol[currentIndex]; if (current == ')') { // We know that we'll immediately increment the paren depth from 0 to 1 on the first loop iteration because // the conditions on the if statement above require it. parenDepth++; } else if (current == '(') { parenDepth--; } if (parenDepth <= 0) { // We found the open paren that matches the last close paren. break; } currentIndex--; } // Continue walking until we find the first whitespace char. This is the beginning of the full function name (with namespace). while (currentIndex >= endIndex && symbol[currentIndex] != ' ') { currentIndex--; } // Make sure we actually hit a ' ' char. if (symbol[currentIndex] != ' ') { goto abort; } // Save the symbol name. string newSymbol = symbol.Substring(currentIndex + 1, (symbol.Length - currentIndex - 1)); // Find the beginning of the module name by looking for ']'. while (currentIndex >= endIndex && symbol[currentIndex] != ']') { currentIndex--; } // Make sure we actually hit a ']' char. if (symbol[currentIndex] != ']') { goto abort; } int moduleEndIndex = currentIndex; // Find the matching '[' char. while (currentIndex >= endIndex && symbol[currentIndex] != '[') { currentIndex--; } // Make sure we actually hit a '[' char. if (symbol[currentIndex] != '[') { goto abort; } // Save the module name. string newModuleName = symbol.Substring(currentIndex + 1, (moduleEndIndex - currentIndex - 1)); stackFrame = new StackFrame(stackFrame.Address, newModuleName, newSymbol, stackFrame.OptimizationTier); } abort: frames.Add(stackFrame); } frames.Add(new ThreadFrame(threadID, "Thread")); frames.Add(new ProcessFrame(processID, command)); return(frames); }
private IEnumerable <LinuxEvent> NextEvent(Regex regex, FastStream source) { string line = string.Empty; while (true) { source.SkipWhiteSpace(); if (source.EndOfStream) { break; } EventKind eventKind = EventKind.Cpu; StringBuilder sb = new StringBuilder(); // Fetch Command (processName) - Stops when it sees the pattern \s+\d+/\d int idx = FindEndOfProcessCommand(source); if (idx < 0) { break; } source.ReadFixedString(idx, sb); source.SkipWhiteSpace(); string processCommand = sb.ToString(); sb.Clear(); // Process ID int pid = source.ReadInt(); // Detect whether or not the Thread ID is present. int tid = pid; if (source.Peek(0) == '/') { // Thread ID source.MoveNext(); // Move past the "/" tid = source.ReadInt(); } // CPU source.SkipWhiteSpace(); int cpu = -1; if (source.Peek(0) == '[') { source.MoveNext(); // Move past the "[" cpu = source.ReadInt(); source.MoveNext(); // Move past the "]" } // Time source.SkipWhiteSpace(); source.ReadAsciiStringUpTo(':', sb); double time = double.Parse(sb.ToString(), CultureInfo.InvariantCulture) * 1000; // To convert to MSec sb.Clear(); source.MoveNext(); // Move past ":" // Time Property source.SkipWhiteSpace(); int timeProp = -1; if (IsNumberChar((char)source.Current)) { timeProp = source.ReadInt(); } // Event Name source.SkipWhiteSpace(); source.ReadAsciiStringUpTo(':', sb); string eventName = sb.ToString(); sb.Clear(); source.MoveNext(); // Event Properties // I mark a position here because I need to check what type of event this is without screwing up the stream var markedPosition = source.MarkPosition(); source.ReadAsciiStringUpTo('\n', sb); string eventDetails = sb.ToString().Trim(); sb.Clear(); if (eventDetails.Length >= SchedulerEvent.Name.Length && eventDetails.Substring(0, SchedulerEvent.Name.Length) == SchedulerEvent.Name) { eventKind = EventKind.Scheduler; } else if (eventDetails.Length > ThreadExitEvent.Name.Length && eventDetails.Substring(0, ThreadExitEvent.Name.Length) == ThreadExitEvent.Name) { eventKind = EventKind.ThreadExit; } // Now that we know the header of the trace, we can decide whether or not to skip it given our pattern if (regex != null && !regex.IsMatch(eventName)) { while (true) { source.MoveNext(); if (IsEndOfSample(source, source.Current, source.Peek(1))) { break; } } yield return(null); } else { LinuxEvent linuxEvent; Frame threadTimeFrame = null; // For the sake of immutability, I have to do a similar if-statement twice. I'm trying to figure out a better way // but for now this will do. ScheduleSwitch schedSwitch = null; if (eventKind == EventKind.Scheduler) { source.RestoreToMark(markedPosition); schedSwitch = ReadScheduleSwitch(source); source.SkipUpTo('\n'); } ThreadExit exit = null; if (eventKind == EventKind.ThreadExit) { source.RestoreToMark(markedPosition); exit = ReadExit(source); source.SkipUpTo('\n'); } IEnumerable <Frame> frames = ReadFramesForSample(processCommand, pid, tid, threadTimeFrame, source); if (eventKind == EventKind.Scheduler) { linuxEvent = new SchedulerEvent(processCommand, tid, pid, time, timeProp, cpu, eventName, eventDetails, frames, schedSwitch); } else if (eventKind == EventKind.ThreadExit) { linuxEvent = new ThreadExitEvent(processCommand, tid, pid, time, timeProp, cpu, eventName, eventDetails, frames, exit); } else { linuxEvent = new CpuEvent(processCommand, tid, pid, time, timeProp, cpu, eventName, eventDetails, frames); } yield return(linuxEvent); } } }