// Reads the rest of a record after GetFlags() has read the flags. // TODO: This really shouldn't modify any class members until it completes successfully. private Record ReadRecordData(DataFlags flags, ulong thisRecordNum, bool reReading) { long startPos = _fileReader.BaseStream.Position; if ((flags & DataFlags.Time) != DataFlags.None) { LastRecordTimeUtc = new DateTime(_fileReader.ReadInt64()); } if ((flags & DataFlags.ThreadId) != DataFlags.None) { ReadThreadID(); } 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; if (Reader.Key == null) { threadNameStr = _fileReader.ReadString(); } else { threadNameStr = Decrypt(); } if (threadNameStr == string.Empty) { _curThread.ThreadName = _reader.FindOrCreateThreadName("Thread " + _curThread.Thread.Id); } else { _curThread.ThreadName = _reader.FindOrCreateThreadName(threadNameStr); } } else if (_curThread.ThreadName == null) { _curThread.ThreadName = _reader.FindOrCreateThreadName("Thread " + _curThread.Thread.Id); } if ((flags & DataFlags.TraceLevel) != DataFlags.None) { _curThread.Level = (TracerX.TraceLevel)_fileReader.ReadByte(); _reader.LevelsFound |= _curThread.Level; } if ((flags & DataFlags.StackDepth) != DataFlags.None) { _curThread.Depth = _fileReader.ReadByte(); if (InCircularPart) { ReadStack(); } } // 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 && !reReading) { --_curThread.Depth; } if ((flags & DataFlags.LoggerName) != DataFlags.None) { string loggerName; if (Reader.Key == null) { loggerName = _fileReader.ReadString(); } else { loggerName = Decrypt(); } _curThread.Logger = _reader.GetLogger(loggerName); } if ((flags & DataFlags.MethodName) != DataFlags.None) { string methodName; if (Reader.Key == null) { methodName = _fileReader.ReadString(); } else { methodName = Decrypt(); } _curThread.MethodName = _reader.GetMethod(methodName); } if ((flags & DataFlags.Message) != DataFlags.None) { if (Reader.Key == null) { _msg = _fileReader.ReadString(); } else { _msg = Decrypt(); } } // Construct the Record before incrementing depth. Record record = new Record(flags, thisRecordNum, LastRecordTimeUtc, _curThread, this, _msg); if (!reReading) { 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 // by "pushing" MethodEntry records and "popping" MethodExit records if (_reader.FormatVersion >= 5) { _curThread.Push(record); } } else if (_reader.FormatVersion >= 5 && (flags & DataFlags.MethodExit) != DataFlags.None) { _curThread.Pop(); } _reader.BytesRead += _fileReader.BaseStream.Position - startPos; } if (InCircularPart && _fileReader.BaseStream.Position >= _maxFilePos) { // We've read to the point where the log wraps. Wrap(); } return(record); }
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); } }