// Resets all state data that might be associated with an earlier binary file // and prepares for the next (specified) opening of the file. internal void ResetBinaryFileStateData(int fileNum, DataFlags flags) { BinaryFileState.LastFileNumber = fileNum; BinaryFileState.LastTraceLevel = TraceLevel.Off; BinaryFileState.LastThreadName = null; BinaryFileState.LastMethod = string.Empty; BinaryFileState.LastLogger = null; BinaryFileState.LastBlock = 0; // For simplicity, abandon the stack rather than deal with the fact that there // may be a deep call stack when the file is closed and reopened. BinaryFileState.StackDepth = 0; for (StackEntry stackEntry = TopStackEntry; stackEntry != null; stackEntry = stackEntry.Caller) { if (stackEntry.BinaryFileState == BinaryFileState) { stackEntry.Destinations &= ~Destinations.BinaryFile; stackEntry.BinaryFileState = null; } } // I'm not sure why we wouldn't always do this. if ((flags & DataFlags.MethodEntry) == 0) { BinaryFileState.CurrentMethod = string.Empty; } }
internal StackEntry(Logger logger, TraceLevel level, string method, StackEntry caller, Destinations destinations) { Logger = logger; Level = level; MethodName = method; Caller = caller; Destinations = destinations; }
// Search down the stack for the next caller for the specified destination. private string GetCaller(Destinations destination) { for (StackEntry caller = TopStackEntry.Caller; caller != null; caller = caller.Caller) { if ((caller.Destinations & destination) == destination) { return(caller.MethodName); } } return(""); }
// Search down the stack for the next caller for the specified text file. private string GetCaller(TextFileState fileData) { // For text files, the stack depth is decremented BEFORE this is called, // so check stackdepth > 0. if (fileData.StackDepth > 0) { for (StackEntry caller = TopStackEntry.Caller; caller != null; caller = caller.Caller) { if (caller.TextFileState == fileData) { return(caller.MethodName); } } Debug.Assert(false, "A caller should have been found."); } return(""); }
// Log the exit of a method call to each destination indicated by TopStackEntry. internal void LogCallExit() { if ((TopStackEntry.Destinations & Destinations.EventHandler) != 0) { // Although this LogMsg() call raises a cancellable event, method-exit messages aren't really cancellable because // we must "balance" the original method-entry message. --EventHandlerState.StackDepth; EventHandlerLogging.LogMsg(TopStackEntry.Logger, this, TopStackEntry.Level, TopStackEntry.MethodName + " exiting", false, true); EventHandlerState.CurrentMethod = GetCaller(Destinations.EventHandler); } if ((TopStackEntry.Destinations & Destinations.BinaryFile) != 0) { // Make sure BinaryFileState corresponds to the BinaryFile instance // the method-exit should be logged to (the same BinaryFile the // method-entry for TopStackEntry was logged to). BinaryFileState = TopStackEntry.BinaryFileState; if (TopStackEntry.Logger.BinaryFile.LogExit(this)) { // BinaryFileState.StackDepth depth is decremented after logging so any meta-logging has the right depth. // GetCaller() also depends on the stack depth. BinaryFileState.CurrentMethod = GetCaller(BinaryFileState); --BinaryFileState.StackDepth; } } if ((TopStackEntry.Destinations & Destinations.TextFile) != 0) { // Make sure BinaryFileState corresponds to the BinaryFile instance // the method-exit should be logged to (the same BinaryFile the // method-entry for TopStackEntry was logged to). TextFileState = TopStackEntry.TextFileState; --TextFileState.StackDepth; TopStackEntry.Logger.TextFile.LogMsg(TopStackEntry.Logger, this, TopStackEntry.Level, TopStackEntry.MethodName + " exiting"); TextFileState.CurrentMethod = GetCaller(TextFileState); } if ((TopStackEntry.Destinations & Destinations.Console) != 0) { --ConsoleState.StackDepth; ConsoleLogging.LogMsg(TopStackEntry.Logger, this, TopStackEntry.Level, TopStackEntry.MethodName + " exiting"); ConsoleState.CurrentMethod = GetCaller(Destinations.Console); } if ((TopStackEntry.Destinations & Destinations.Debug) != 0) { --DebugState.StackDepth; DebugLogging.LogMsg(TopStackEntry.Logger, this, TopStackEntry.Level, TopStackEntry.MethodName + " exiting"); DebugState.CurrentMethod = GetCaller(Destinations.Debug); } if ((TopStackEntry.Destinations & Destinations.EventLog) != 0) { --EventLogState.StackDepth; //EventLogging.LogMsg(TopStackEntry.Logger, this, TopStackEntry.Level, TopStackEntry.MethodName + " exiting"); EventLogState.CurrentMethod = GetCaller(Destinations.EventLog); } LastLoggerAnyDest = TopStackEntry.Logger; TopStackEntry = TopStackEntry.Caller; --MasterStackDepth; }
// Possibly logs the entry of a method call. Returns true if the message is logged, meaning // the corresponding method-exit should also be logged when it occurs. internal bool LogCallEntry(Logger logger, TraceLevel level, string method, Destinations destinations) { bool result = true; if (MasterStackDepth < byte.MaxValue) { if ((destinations & Destinations.EventHandler) == Destinations.EventHandler) { string originalMethod = EventHandlerState.CurrentMethod; EventHandlerState.CurrentMethod = method; // The LogMsg method returns true if the event is cancelled. result = !EventHandlerLogging.LogMsg(logger, this, level, method + " entered", true, false); if (result) { ++EventHandlerState.StackDepth; } else { // Event was cancelled, so restore the original method. EventHandlerState.CurrentMethod = originalMethod; } } if (result) { StackEntry stackEntry = new StackEntry(logger, level, method, TopStackEntry, destinations); // Don't put the stack entry on the stack (i.e. set TopStackEntry) until after the method-entry // is logged because of code in BinaryFile.WriteLine(). if ((destinations & Destinations.BinaryFile) == Destinations.BinaryFile) { // This may be the first output to be written to the file, so commit to it. logger.CommitToBinaryFile(); // Lookup the BinaryFileState that applies to this thread and file. stackEntry.BinaryFileState = GetBinaryFileState(logger.BinaryFile); BinaryFileState.CurrentMethod = method; logger.BinaryFile.LogEntry(this, stackEntry); ++BinaryFileState.StackDepth; } if ((destinations & Destinations.TextFile) == Destinations.TextFile) { // This may be the first output to be written to the file, so commit to it. logger.CommitToTextFile(); stackEntry.TextFileState = GetTextFileState(logger.TextFile); TextFileState.CurrentMethod = method; logger.TextFile.LogMsg(logger, this, level, method + " entered"); ++TextFileState.StackDepth; } if ((destinations & Destinations.Console) == Destinations.Console) { ConsoleState.CurrentMethod = method; ConsoleLogging.LogMsg(logger, this, level, method + " entered"); ++ConsoleState.StackDepth; } if ((destinations & Destinations.Debug) == Destinations.Debug) { DebugState.CurrentMethod = method; DebugLogging.LogMsg(logger, this, level, method + " entered"); ++DebugState.StackDepth; } if ((destinations & Destinations.EventLog) == Destinations.EventLog) { // Method-entry messages seem like a dumb thing to put in the event log, // but we'll keep track of the stack anyway. EventLogState.CurrentMethod = method; //EventLogging.LogMsg(logger, this, level, method + " entered"); ++EventLogState.StackDepth; } ++MasterStackDepth; TopStackEntry = stackEntry; } } else { // Already at max depth, so don't log this. Return false so // the corresponding future call to LogCallExit won't happen either. result = false; } LastLoggerAnyDest = logger; return(result); }
// This is what actually writes the output. The dataFlags parameter specifies what to write. private void WriteData(DataFlags dataFlags, ThreadData threadData, BinaryFileState fileThreadState, Logger logger, TraceLevel lineLevel, string msg) { ++_lineCnt; // Write the flags first so the viewer will know what else the record contains. _logfile.Write((ushort)dataFlags); if (CircularStarted) { _logfile.Write(_curBlock); if ((dataFlags & DataFlags.BlockStart) != DataFlags.None) { // This will be the first record in the block. // This stuff helps the viewer find the first chronological block // even after wrapping. Writting _lastBlockPosition forms a linked // list of blocks that the viewer can follow. //System.Diagnostics.Debug.Print("Block {0} starting at line {1}, position {2}", _curBlock, _lineCnt, _logfile.BaseStream.Position); _logfile.Write(_lineCnt); _logfile.Write(_lastBlockPosition); } } if ((dataFlags & DataFlags.Time) != DataFlags.None) { _logfile.Write(_curTime.Ticks); } if ((dataFlags & DataFlags.ThreadId) != DataFlags.None) { _logfile.Write(threadData.TracerXID); } if ((dataFlags & DataFlags.ThreadName) != DataFlags.None) { // ThreadPool thread names get reset to null when a thread is returned // to the pool and reused later. if (_hasPassword) { _encryptor.Encrypt(threadData.Name ?? string.Empty); } else { _logfile.Write(threadData.Name ?? string.Empty); } } if ((dataFlags & DataFlags.TraceLevel) != DataFlags.None) { _logfile.Write((byte)lineLevel); } // In format version 5 and later, the viewer subtracts 1 from the stack depth on // MethodExit lines instead of the logger, so just write the depth as-is. if ((dataFlags & DataFlags.StackDepth) != DataFlags.None) { _logfile.Write(fileThreadState.StackDepth); if (CircularStarted) { // In the circular part, include the thread's call stack with the first line // logged for each thread in each block. This enables the viewer to // regenerate method entry/exit lines lost due to wrapping. // Added in format version 5. int count = 0; for (StackEntry stackEntry = threadData.TopStackEntry; stackEntry != null; stackEntry = stackEntry.Caller) { if (stackEntry.BinaryFileState == fileThreadState) { ++count; _logfile.Write(stackEntry.EntryLine); // Changed to ulong in version 6. _logfile.Write((byte)stackEntry.Level); if (_hasPassword) { Debug.Assert(stackEntry.Logger.Name != null); _encryptor.Encrypt(stackEntry.Logger.Name); Debug.Assert(stackEntry.MethodName != null); _encryptor.Encrypt(stackEntry.MethodName); } else { _logfile.Write(stackEntry.Logger.Name); _logfile.Write(stackEntry.MethodName); } } } // The StackDepth we wrote previously is how the viewer will know how many // stack entries to read. System.Diagnostics.Debug.Assert(count == fileThreadState.StackDepth); } } if ((dataFlags & DataFlags.LoggerName) != DataFlags.None) { if (_hasPassword) { _encryptor.Encrypt(logger.Name); } else { _logfile.Write(logger.Name); } } if ((dataFlags & DataFlags.MethodName) != DataFlags.None) { if (_hasPassword) { _encryptor.Encrypt(fileThreadState.CurrentMethod); } else { _logfile.Write(fileThreadState.CurrentMethod); } fileThreadState.LastMethod = fileThreadState.CurrentMethod; } if ((dataFlags & DataFlags.Message) != DataFlags.None) { if (_hasPassword) { _encryptor.Encrypt(msg ?? ""); } else { _logfile.Write(msg); } } _lastBlock = _curBlock; _lastThread = threadData; fileThreadState.LastBlock = _curBlock; fileThreadState.LastThreadName = threadData.Name; fileThreadState.LastTraceLevel = lineLevel; fileThreadState.LastLogger = logger; }
// Log the entry (start) of a method call. // stackEntry is not yet on the stack. internal void LogEntry(ThreadData threadData, StackEntry stackEntry) { // Remember the line number where the MethodEntry flag is written so // we can write it into the log when the method exits. stackEntry.EntryLine = WriteLine(DataFlags.MethodEntry, threadData, stackEntry.Logger, stackEntry.Level, DateTime.MinValue, null, false); }
// Possibly logs the entry of a method call and/or changes the current thread's name. // Returns true if the message is logged or the thread name is changed, meaning the // corresponding method-exit should also be logged (and/or the thread name changed back) // when LogCallExit() is called. internal bool LogCallEntry(Logger logger, TraceLevel level, string method, Destinations destinations, string threadName) { bool result = true; bool cancelled = false; string originalThreadName; if (MasterStackDepth < byte.MaxValue) { if (threadName == null) { // _doNotRestore is a special value that prevents LogCallExit() from // restoring the thread name. originalThreadName = _doNotRestore; } else { // Because the user specified a thread name, we arrange for the current // thread name to be restored by LogCallExit() before changing it. originalThreadName = _name; _name = threadName; } // The EventHandler destination is processed first because the event it raises is cancellable. // If the event's handler cancels the event, the method call won't be logged to the other // destinations. However, the thread name can still be changed. if ((destinations & Destinations.EventHandler) == Destinations.EventHandler) { string originalMethod = EventHandlerState.CurrentMethod; EventHandlerState.CurrentMethod = method; // The LogMsg method returns true if the event is cancelled. cancelled = EventHandlerLogging.LogMsg(logger, this, level, method + " entered", true, false); if (cancelled) { // Event was cancelled, so restore the original method. EventHandlerState.CurrentMethod = originalMethod; } else { ++EventHandlerState.StackDepth; } } if (!cancelled || threadName != null) { StackEntry stackEntry = new StackEntry(logger, level, method, TopStackEntry, destinations, originalThreadName); // Don't put the stack entry on the stack (i.e. set TopStackEntry) until after the method-entry // is logged because of code in BinaryFile.WriteLine(). // Note that each destination effectively has it's own stack because a given method entry isn't // necessarily logged to all of them. if ((destinations & Destinations.BinaryFile) == Destinations.BinaryFile) { // This may be the first output to be written to the file, so commit to it. logger.CommitToBinaryFile(); // Lookup the BinaryFileState that applies to this thread and file. stackEntry.BinaryFileState = GetBinaryFileState(logger.BinaryFile); BinaryFileState.CurrentMethod = method; logger.BinaryFile.LogEntry(this, stackEntry); ++BinaryFileState.StackDepth; } if ((destinations & Destinations.TextFile) == Destinations.TextFile) { // This may be the first output to be written to the file, so commit to it. logger.CommitToTextFile(); stackEntry.TextFileState = GetTextFileState(logger.TextFile); TextFileState.CurrentMethod = method; logger.TextFile.LogMsg(logger, this, level, method + " entered"); ++TextFileState.StackDepth; } if ((destinations & Destinations.Console) == Destinations.Console) { ConsoleState.CurrentMethod = method; ConsoleLogging.LogMsg(logger, this, level, method + " entered"); ++ConsoleState.StackDepth; } if ((destinations & Destinations.Debug) == Destinations.Debug) { DebugState.CurrentMethod = method; DebugLogging.LogMsg(logger, this, level, method + " entered"); ++DebugState.StackDepth; } if ((destinations & Destinations.EventLog) == Destinations.EventLog) { // Method-entry messages seem like a dumb thing to put in the event log, // but we'll keep track of the stack anyway. EventLogState.CurrentMethod = method; //EventLogging.LogMsg(logger, this, level, method + " entered"); ++EventLogState.StackDepth; } ++MasterStackDepth; TopStackEntry = stackEntry; result = true; } } else { // Already at max depth, so don't log this. Return false so // the corresponding future call to LogCallExit won't happen either. result = false; } LastLoggerAnyDest = logger; return(result); }