// Called when we enter the circular part of the log and the thread's true call stack // (logged with each thread's first record in each block) is read from the log. // This is enough info to generate replacement entry/exit records that were lost when // the log wrapped, but whose counterparts were not lost. internal void MakeMissingRecords(ReaderStackEntry[] circularStack) { if (NonCircularStack != null) { MissingEntryRecords = new List <Record>(); MissingExitRecords = new List <Record>(); // The deepest entry comes last in circularStack. int trueDepth = circularStack == null ? 0 : circularStack.Length; // If the true stack depth is greater than the stack depth we got // by pushing MehtodEntry records and popping MethodExit records, // we must generate the missing MethodEntry records. while (trueDepth > NonCircularStack.Count) { // This constructs an entry record. MissingEntryRecords.Add(new Record(this, circularStack[--trueDepth])); } // If the true stack depth is less than the stack depth we got // by pushing MehtodEntry records and popping MethodExit records, // we must generate the missing MethodExit records. while (trueDepth < NonCircularStack.Count) { // This constructs an exit record. MissingExitRecords.Add(new Record(NonCircularStack.Pop())); } // Now the two stacks are the same depth. // Wherever two entries don't match, we are missing // both an entry and an exit record. while (NonCircularStack.Count > 0) { Record parsedEntry = NonCircularStack.Pop(); ReaderStackEntry trueEntry = circularStack[--trueDepth]; if (parsedEntry.MsgNum == trueEntry.EntryLineNum) { // All the other ones will also match. break; } else { MissingEntryRecords.Add(new Record(this, trueEntry)); MissingExitRecords.Add(new Record(parsedEntry)); } } NonCircularStack = null; // We're done. } }
// This constructs a missing MethodEntry Record from the given ReaderStackEntry. public Record(ReaderThreadInfo threadInfo, ReaderStackEntry methodEntry) { IsEntry = true; MsgNum = 0; // TBD Time = DateTime.MinValue; // TBD Index = 0; // TBD Thread = threadInfo.Thread; ThreadName = threadInfo.ThreadName; Level = methodEntry.Level; Logger = methodEntry.Logger; StackDepth = methodEntry.Depth; MethodName = methodEntry.Method; Lines = new string[] { string.Format("{{{0}: entered (replaces record lost due to wrapping)", MethodName) }; // Each record also has a bool to indicate if it is bookmarked // and a row index it may map to. IsBookmarked = new bool[1]; RowIndices = new int[1]; }
public Record ReadRecord() { // Read the DataFlags, then the data the Flags indicate is there. // Data must be read in the same order it was written (see FileLogging.WriteData). try { DataFlags flags = GetFlags(); if (flags == DataFlags.None) { return(null); } long startPos = _fileReader.BaseStream.Position; if ((flags & DataFlags.LineNumber) != DataFlags.None) { _recordNumber = _fileReader.ReadUInt32(); } else if (!InCircularPart) { ++_recordNumber; } else { // _recordNumber was incremented by GetFlags. } if ((flags & DataFlags.Time) != DataFlags.None) { _time = new DateTime(_fileReader.ReadInt64()); } if ((flags & DataFlags.ThreadId) != DataFlags.None) { _threadId = _fileReader.ReadInt32(); // Look up or add the entry for this ThreadId. if (!_foundThreadIds.TryGetValue(_threadId, out _curThread)) { // First occurrence of this id. _curThread = new ReaderThreadInfo(); if (!_oldThreadIds.TryGetValue(_threadId, out _curThread.Thread)) { _curThread.Thread = new ThreadObject(); } _curThread.Thread.Id = _threadId; ThreadObject.AllThreads.Add(_curThread.Thread); _foundThreadIds[_threadId] = _curThread; } } if ((flags & DataFlags.ThreadName) != DataFlags.None) { // A normal thread's name can only change from null to non-null. // ThreadPool threads can alternate between null and non-null. // If a thread's name changes from non-null to null, the logger // writes string.Empty for the thread name. string threadNameStr = _fileReader.ReadString(); if (threadNameStr == string.Empty) { _curThread.ThreadName = FindOrCreateThreadName("Thread " + _curThread.Thread.Id); } else { _curThread.ThreadName = FindOrCreateThreadName(threadNameStr); } } else if (_curThread.ThreadName == null) { _curThread.ThreadName = FindOrCreateThreadName("Thread " + _curThread.Thread.Id); } if ((flags & DataFlags.TraceLevel) != DataFlags.None) { _curThread.Level = (TracerX.TraceLevel)_fileReader.ReadByte(); LevelsFound |= _curThread.Level; } if (FormatVersion < 5) { if ((flags & DataFlags.StackDepth) != DataFlags.None) { _curThread.Depth = _fileReader.ReadByte(); } else if ((flags & DataFlags.MethodExit) != DataFlags.None) { --_curThread.Depth; } } else { if ((flags & DataFlags.StackDepth) != DataFlags.None) { _curThread.Depth = _fileReader.ReadByte(); if (InCircularPart) { if (_curThread.Depth > 0) { // In format version 5, we began logging each thread's current call // stack on the thread's first line in each block (i.e. when the // StackDepth flag is set). This is the thread's true call stack at // this point in the log. It reflects MethodEntry and MethodExit // records that may have been lost when the log wrapped (as well // as those that weren't lost). ReaderStackEntry[] trueStack = new ReaderStackEntry[_curThread.Depth]; for (int i = _curThread.Depth - 1; i >= 0; --i) { ReaderStackEntry entry = new ReaderStackEntry(); entry.EntryLineNum = _fileReader.ReadUInt32(); entry.Level = (TracerX.TraceLevel)_fileReader.ReadByte(); entry.Logger = GetLogger(_fileReader.ReadString()); entry.Method = _fileReader.ReadString(); entry.Depth = (byte)i; trueStack[i] = entry; } _curThread.MakeMissingRecords(trueStack); } else { _curThread.MakeMissingRecords(null); } } } // Starting in format version 5, the viewer decrements the depth on MethodExit // lines even if it was included on the line. if ((flags & DataFlags.MethodExit) != DataFlags.None) { --_curThread.Depth; } } if ((flags & DataFlags.LoggerName) != DataFlags.None) { string loggerName = _fileReader.ReadString(); _curThread.Logger = GetLogger(loggerName); } if ((flags & DataFlags.MethodName) != DataFlags.None) { _curThread.MethodName = _fileReader.ReadString(); } if ((flags & DataFlags.Message) != DataFlags.None) { _msg = _fileReader.ReadString(); } // Construct the Record before incrementing depth. Record record = new Record(flags, _recordNumber, _time, _curThread, _msg); if ((flags & DataFlags.MethodEntry) != DataFlags.None) { // Cause future records to be indented until a MethodExit is encountered. ++_curThread.Depth; // In format version 5+, we keep track of the call stack in the noncircular // part of the log by "pushing" MethodEntry records and "popping" MethodExit records if (FormatVersion >= 5 && !InCircularPart) { _curThread.Push(record); } } else if (FormatVersion >= 5 && !InCircularPart && (flags & DataFlags.MethodExit) != DataFlags.None) { _curThread.Pop(); } BytesRead += _fileReader.BaseStream.Position - startPos; if (InCircularPart && _fileReader.BaseStream.Position >= MaxMb << 20) { // We've read to the max file size in circular mode. Wrap. _fileReader.BaseStream.Position = _circularStartPos; } ++_recordsRead; return(record); } catch (Exception ex) { // The exception is either end-of-file or a corrupt file. // Either way, we're done. Returning null tells the caller to give up. return(null); } }