--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Security;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+ public class DiagnosticLoggingService : IDiagnosticLoggingService
+ {
+ private const string ListenerName = "SOS.LoggingListener";
+ private IConsoleService _consoleService;
+ private IConsoleFileLoggingService _fileLoggingService;
+ private StreamWriter _writer;
+
+ public static DiagnosticLoggingService Instance { get; } = new DiagnosticLoggingService();
+
+ private DiagnosticLoggingService()
+ {
+ }
+
+ #region IDiagnosticLoggingService
+
+ /// <summary>
+ /// Returns true if logging to console or file
+ /// </summary>
+ public bool IsEnabled => Trace.Listeners[ListenerName] is not null;
+
+ /// <summary>
+ /// The file path if logging to file.
+ /// </summary>
+ public string FilePath => (_writer?.BaseStream as FileStream)?.Name;
+
+ /// <summary>
+ /// Enable diagnostics logging.
+ /// </summary>
+ /// <param name="filePath">log file path or null if log to console</param>
+ /// <remarks>see File.Open for possible exceptions thrown</remarks>
+ public void Enable(string filePath)
+ {
+ if (filePath is not null)
+ {
+ FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
+ CloseLogging();
+ _writer = new StreamWriter(stream) {
+ AutoFlush = true
+ };
+ _fileLoggingService?.AddStream(stream);
+ }
+ if (Trace.Listeners[ListenerName] is null)
+ {
+ Trace.Listeners.Add(new LoggingListener(this));
+ Trace.AutoFlush = true;
+ }
+ }
+
+ /// <summary>
+ /// Disable diagnostics logging (close if logging to file).
+ /// </summary>
+ public void Disable()
+ {
+ CloseLogging();
+ Trace.Listeners.Remove(ListenerName);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Initializes the diagnostic logging service. Reads the DOTNET_ENABLED_SOS_LOGGING
+ /// environment variable to log to console or file.
+ /// </summary>
+ /// <param name="logfile"></param>
+ public static void Initialize(string logfile = null)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(logfile))
+ {
+ logfile = Environment.GetEnvironmentVariable("DOTNET_ENABLED_SOS_LOGGING");
+ }
+ if (!string.IsNullOrWhiteSpace(logfile))
+ {
+ Instance.Enable(logfile == "1" ? null : logfile);
+ }
+ }
+ catch (Exception ex) when ( ex is IOException || ex is NotSupportedException || ex is SecurityException || ex is UnauthorizedAccessException)
+ {
+ }
+ }
+
+ /// <summary>
+ /// Sets the console service and the console file logging control service.
+ /// </summary>
+ /// <param name="consoleService">This is used for to log to the console</param>
+ /// <param name="fileLoggingService">This is used to hook the command console output to write the diagnostic log file.</param>
+ public void SetConsole(IConsoleService consoleService, IConsoleFileLoggingService fileLoggingService = null)
+ {
+ _consoleService = consoleService;
+ _fileLoggingService = fileLoggingService;
+ }
+
+ private void CloseLogging()
+ {
+ if (_writer is not null)
+ {
+ _fileLoggingService?.RemoveStream(_writer.BaseStream);
+ _writer.Flush();
+ _writer.Close();
+ _writer = null;
+ }
+ }
+
+ class LoggingListener : TraceListener
+ {
+ private readonly DiagnosticLoggingService _diagnosticLoggingService;
+
+ internal LoggingListener(DiagnosticLoggingService diagnosticLoggingService)
+ : base(ListenerName)
+ {
+ _diagnosticLoggingService = diagnosticLoggingService;
+ }
+
+ public override void Close()
+ {
+ _diagnosticLoggingService.CloseLogging();
+ base.Close();
+ }
+
+ public override void Write(string message)
+ {
+ if (_diagnosticLoggingService._writer is not null)
+ {
+ try
+ {
+ _diagnosticLoggingService._writer.Write(message);
+ return;
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ _diagnosticLoggingService._consoleService?.Write(message);
+ }
+
+ public override void WriteLine(string message)
+ {
+ if (_diagnosticLoggingService._writer is not null)
+ {
+ try
+ {
+ _diagnosticLoggingService._writer.WriteLine(message);
+ return;
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ _diagnosticLoggingService._consoleService?.WriteLine(message);
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ /// <summary>
+ /// Log to file console service wrapper
+ /// </summary>
+ public class FileLoggingConsoleService : IConsoleService, IConsoleFileLoggingService, IDisposable
+ {
+ private readonly IConsoleService _consoleService;
+ private readonly List<StreamWriter> _writers;
+ private FileStream _consoleStream;
+
+ public FileLoggingConsoleService(IConsoleService consoleService)
+ {
+ _consoleService = consoleService;
+ _writers = new List<StreamWriter>();
+ }
+
+ public void Dispose() => Disable();
+
+ #region IConsoleFileLoggingService
+
+ /// <summary>
+ /// The log file path if enabled, otherwise null.
+ /// </summary>
+ public string FilePath => _consoleStream?.Name;
+
+ /// <summary>
+ /// Enable console file logging.
+ /// </summary>
+ /// <param name="filePath">log file path</param>
+ /// <remarks>see File.Open for more exceptions</remarks>
+ public void Enable(string filePath)
+ {
+ FileStream consoleStream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
+ Disable();
+ AddStream(consoleStream);
+ _consoleStream = consoleStream;
+ }
+
+ /// <summary>
+ /// Disable/close console file logging
+ /// </summary>
+ public void Disable()
+ {
+ if (_consoleStream is not null)
+ {
+ RemoveStream(_consoleStream);
+ _consoleStream.Close();
+ _consoleStream = null;
+ }
+ }
+
+ /// <summary>
+ /// Add to the list of file streams to write the console output.
+ /// </summary>
+ /// <param name="stream">Stream to add. Lifetime managed by caller.</param>
+ public void AddStream(Stream stream)
+ {
+ Debug.Assert(stream is not null);
+ _writers.Add(new StreamWriter(stream) {
+ AutoFlush = true
+ });
+ }
+
+ /// <summary>
+ /// Remove the specified file stream from the writers.
+ /// </summary>
+ /// <param name="stream">Stream passed to add. Stream not closed or disposed.</param>
+ public void RemoveStream(Stream stream)
+ {
+ if (stream is not null)
+ {
+ foreach (StreamWriter writer in _writers)
+ {
+ if (writer.BaseStream == stream)
+ {
+ _writers.Remove(writer);
+ break;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region IConsoleService
+
+ public void Write(string text)
+ {
+ _consoleService.Write(text);
+ foreach (StreamWriter writer in _writers)
+ {
+ try
+ {
+ writer.Write(text);
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ }
+
+ public void WriteWarning(string text)
+ {
+ _consoleService.WriteWarning(text);
+ foreach (StreamWriter writer in _writers)
+ {
+ try
+ {
+ writer.Write(text);
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ }
+
+ public void WriteError(string text)
+ {
+ _consoleService.WriteError(text);
+ foreach (StreamWriter writer in _writers)
+ {
+ try
+ {
+ writer.Write(text);
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ }
+
+ public bool SupportsDml => _consoleService.SupportsDml;
+
+ public void WriteDml(string text)
+ {
+ _consoleService.WriteDml(text);
+ foreach (StreamWriter writer in _writers)
+ {
+ try
+ {
+ // TODO: unwrap the DML?
+ writer.Write(text);
+ }
+ catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException)
+ {
+ }
+ }
+ }
+
+ public CancellationToken CancellationToken
+ {
+ get { return _consoleService.CancellationToken; }
+ set { _consoleService.CancellationToken = value; }
+ }
+
+ public int WindowWidth => _consoleService.WindowWidth;
+
+ #endregion
+ }
+}
{
_host = host;
OnChangeEvent = new ServiceEvent();
+ // dbgeng's console can not handle the async logging (Tracer output on another thread than the main one).
+ Tracer.Enable = host.HostType != HostType.DbgEng;
}
#region ISymbolService
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.SymbolStore;
+using System.Diagnostics;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ /// <summary>
+ /// Simple trace/logging support.
+ /// </summary>
+ public sealed class Tracer : ITracer
+ {
+ public static bool Enable { get; set; }
+
+ public static ITracer Instance { get; } = Enable ? new Tracer() : new NullTracer();
+
+ private Tracer()
+ {
+ }
+
+ public void WriteLine(string message)
+ {
+ Trace.WriteLine(message);
+ Trace.Flush();
+ }
+
+ public void WriteLine(string format, params object[] arguments)
+ {
+ WriteLine(string.Format(format, arguments));
+ }
+
+ public void Information(string message)
+ {
+ Trace.TraceInformation(message);
+ Trace.Flush();
+ }
+
+ public void Information(string format, params object[] arguments)
+ {
+ Trace.TraceInformation(format, arguments);
+ Trace.Flush();
+ }
+
+ public void Warning(string message)
+ {
+ Trace.TraceWarning(message);
+ Trace.Flush();
+ }
+
+ public void Warning(string format, params object[] arguments)
+ {
+ Trace.TraceWarning(format, arguments);
+ Trace.Flush();
+ }
+
+ public void Error(string message)
+ {
+ Trace.TraceError(message);
+ Trace.Flush();
+ }
+
+ public void Error(string format, params object[] arguments)
+ {
+ Trace.TraceError(format, arguments);
+ Trace.Flush();
+ }
+
+ public void Verbose(string message)
+ {
+ Information(message);
+ }
+
+ public void Verbose(string format, params object[] arguments)
+ {
+ Information(format, arguments);
+ }
+
+ sealed class NullTracer : ITracer
+ {
+ internal NullTracer()
+ {
+ }
+
+ public void WriteLine(string message)
+ {
+ }
+
+ public void WriteLine(string format, params object[] arguments)
+ {
+ }
+
+ public void Information(string message)
+ {
+ }
+
+ public void Information(string format, params object[] arguments)
+ {
+ }
+
+ public void Warning(string message)
+ {
+ }
+
+ public void Warning(string format, params object[] arguments)
+ {
+ }
+
+ public void Error(string message)
+ {
+ }
+
+ public void Error(string format, params object[] arguments)
+ {
+ }
+
+ public void Verbose(string message)
+ {
+ }
+
+ public void Verbose(string format, params object[] arguments)
+ {
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Console file logging control service
+ /// </summary>
+ public interface IConsoleFileLoggingService
+ {
+ /// <summary>
+ /// The log file path if enabled, otherwise null.
+ /// </summary>
+ string FilePath { get; }
+
+ /// <summary>
+ /// Enable console file logging.
+ /// </summary>
+ /// <param name="filePath">log file path</param>
+ /// <remarks>see File.Open for possible exceptions thrown</remarks>
+ void Enable(string filePath);
+
+ /// <summary>
+ /// Disable/close console file logging.
+ /// </summary>
+ void Disable();
+
+ /// <summary>
+ /// Add to the list of file streams to write the console output.
+ /// </summary>
+ /// <param name="stream">Stream to add. Lifetime managed by caller.</param>
+ void AddStream(Stream stream);
+
+ /// <summary>
+ /// Remove the specified file stream from the writers.
+ /// </summary>
+ /// <param name="stream">Stream passed to add. Stream not closed or disposed.</param>
+ void RemoveStream(Stream stream);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Service to control the internal diagnostic (Trace) logging.
+ /// </summary>
+ public interface IDiagnosticLoggingService
+ {
+ /// <summary>
+ /// Returns true if logging to console or file
+ /// </summary>
+ bool IsEnabled { get; }
+
+ /// <summary>
+ /// The file path if logging to file.
+ /// </summary>
+ string FilePath { get; }
+
+ /// <summary>
+ /// Enable diagnostics logging.
+ /// </summary>
+ /// <param name="filePath">log file path or null if log to console</param>
+ /// <remarks>see File.Open for possible exceptions thrown</remarks>
+ void Enable(string filePath);
+
+ /// <summary>
+ /// Disable diagnostics logging (close if logging to file).
+ /// </summary>
+ void Disable();
+ }
+}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-
-namespace Microsoft.Diagnostics.DebugServices
-{
- /// <summary>
- /// Simple trace/logging support.
- /// </summary>
- public sealed class Tracer : Microsoft.SymbolStore.ITracer
- {
- public static Microsoft.SymbolStore.ITracer Instance { get; } = new Tracer();
-
- private Tracer()
- {
- }
-
- public void WriteLine(string message)
- {
- Trace.WriteLine(message);
- Trace.Flush();
- }
-
- public void WriteLine(string format, params object[] arguments)
- {
- WriteLine(string.Format(format, arguments));
- }
-
- public void Information(string message)
- {
- Trace.TraceInformation(message);
- Trace.Flush();
- }
-
- public void Information(string format, params object[] arguments)
- {
- Trace.TraceInformation(format, arguments);
- Trace.Flush();
- }
-
- public void Warning(string message)
- {
- Trace.TraceWarning(message);
- Trace.Flush();
- }
-
- public void Warning(string format, params object[] arguments)
- {
- Trace.TraceWarning(format, arguments);
- Trace.Flush();
- }
-
- public void Error(string message)
- {
- Trace.TraceError(message);
- Trace.Flush();
- }
-
- public void Error(string format, params object[] arguments)
- {
- Trace.TraceError(format, arguments);
- Trace.Flush();
- }
-
- public void Verbose(string message)
- {
- Information(message);
- }
-
- public void Verbose(string format, params object[] arguments)
- {
- Information(format, arguments);
- }
- }
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+ [Command(Name = "logopen", Help = "Enable console file logging", Flags = CommandFlags.Global)]
+ [Command(Name = "logclose", DefaultOptions = "--disable", Help = "Disable console file logging", Flags = CommandFlags.Global)]
+ public class ConsoleLoggingCommand : CommandBase
+ {
+ public IConsoleFileLoggingService FileLoggingService { get; set; }
+
+ [Argument(Name = "path", Help = "Log file path.")]
+ public string FilePath { get; set; }
+
+ [Option(Name = "--disable", Help = "Disable console file logging.")]
+ public bool Disable { get; set; }
+
+ public override void Invoke()
+ {
+ if (FileLoggingService is null)
+ {
+ throw new DiagnosticsException("Console logging is not supported");
+ }
+ if (Disable)
+ {
+ FileLoggingService.Disable();
+ }
+ else if (!string.IsNullOrWhiteSpace(FilePath))
+ {
+ FileLoggingService.Enable(FilePath);
+ }
+ string filePath = FileLoggingService.FilePath;
+ if (filePath is not null)
+ {
+ WriteLine($"Console is logging to {filePath}");
+ }
+ else
+ {
+ WriteLine("Console logging is disabled");
+ }
+ }
+ }
+}
using Microsoft.Diagnostics.DebugServices;
using System;
using System.Diagnostics;
+using System.IO;
+using System.Security;
namespace Microsoft.Diagnostics.ExtensionCommands
{
- [Command(Name = "logging", Help = "Enable/disable internal logging", Flags = CommandFlags.Global)]
+ [Command(Name = "logging", Help = "Enable/disable internal diagnostic logging", Flags = CommandFlags.Global)]
public class LoggingCommand : CommandBase
{
- [Option(Name = "enable", Help = "Enable internal logging.")]
+ public IDiagnosticLoggingService DiagnosticLoggingService { get; set; }
+
+ [Argument(Name = "path", Help = "Log file path.")]
+ public string FilePath { get; set; }
+
+ [Option(Name = "--enable", Aliases = new string[] { "enable", "-e" }, Help = "Enable internal logging.")]
public bool Enable { get; set; }
- [Option(Name = "disable", Help = "Disable internal logging.")]
+ [Option(Name = "--disable", Aliases = new string[] { "disable", "-d"}, Help = "Disable internal logging.")]
public bool Disable { get; set; }
- private const string ListenerName = "Analyze.LoggingListener";
-
public override void Invoke()
{
- if (Enable) {
- EnableLogging();
- }
- else if (Disable) {
- DisableLogging();
- }
- WriteLine("Logging is {0}", Trace.Listeners[ListenerName] != null ? "enabled" : "disabled");
- }
-
- public static void Initialize()
- {
- if (Environment.GetEnvironmentVariable("DOTNET_ENABLED_SOS_LOGGING") == "1")
- {
- EnableLogging();
- }
- }
-
- public static void EnableLogging()
- {
- if (Trace.Listeners[ListenerName] == null)
+ if (DiagnosticLoggingService is null)
{
- Trace.Listeners.Add(new LoggingListener());
- Trace.AutoFlush = true;
+ throw new DiagnosticsException("Diagnostic logging is not supported");
}
- }
-
- public static void DisableLogging()
- {
- Trace.Listeners.Remove(ListenerName);
- }
-
- class LoggingListener : TraceListener
- {
- internal LoggingListener()
- : base(ListenerName)
+ if (Disable)
{
+ DiagnosticLoggingService.Disable();
}
-
- public override void Write(string message)
+ else if (Enable || !string.IsNullOrWhiteSpace(FilePath))
{
- System.Console.Write(message);
+ DiagnosticLoggingService.Enable(FilePath);
}
+ WriteLine("Logging is {0}", DiagnosticLoggingService.IsEnabled ? "enabled" : "disabled");
- public override void WriteLine(string message)
+ if (!string.IsNullOrWhiteSpace(DiagnosticLoggingService.FilePath))
{
- System.Console.WriteLine(message);
+ WriteLine(DiagnosticLoggingService.FilePath);
}
}
}
/// Start input processing and command dispatching
/// </summary>
/// <param name="dispatchCommand">Called to dispatch a command on ENTER</param>
- public void Start(Action<string, CancellationToken> dispatchCommand)
+ public void Start(Action<string, string, CancellationToken> dispatchCommand)
{
m_lastCommandLine = null;
m_interactiveConsole = !Console.IsInputRedirected;
RefreshLine();
}
- /// <summary>
- /// Writes a message with a new line to console.
- /// </summary>
- public void WriteLine(string format, params object[] parameters)
- {
- WriteLine(OutputType.Normal, format, parameters);
- }
-
/// <summary>
/// Writes a message with a new line to console.
/// </summary>
m_refreshingLine = false;
}
- private void ProcessKeyInfo(ConsoleKeyInfo keyInfo, Action<string, CancellationToken> dispatchCommand)
+ private void ProcessKeyInfo(ConsoleKeyInfo keyInfo, Action<string, string, CancellationToken> dispatchCommand)
{
int activeLineLen = m_activeLine.Length;
}
}
- private bool Dispatch(string newCommand, Action<string, CancellationToken> dispatchCommand)
+ private bool Dispatch(string newCommand, Action<string, string, CancellationToken> dispatchCommand)
{
bool result = true;
CommandStarting();
}
try
{
- WriteLine(OutputType.Normal, "{0}{1}", m_prompt, newCommand);
- dispatchCommand(newCommand, m_interruptExecutingCommand.Token);
+ dispatchCommand(m_prompt, newCommand, m_interruptExecutingCommand.Token);
m_lastCommandLine = newCommand;
}
catch (OperationCanceledException)
// ctrl-c interrupted the command
m_lastCommandLine = null;
}
- catch (Exception ex) when (!(ex is NullReferenceException || ex is ArgumentNullException || ex is ArgumentException))
+ catch (Exception ex)
{
+ // Most exceptions should not excape the command dispatch, but just in case
WriteLine(OutputType.Error, "ERROR: {0}", ex.Message);
Trace.TraceError(ex.ToString());
m_lastCommandLine = null;
return sb.ToString();
}
- internal string GetLogSuffix()
+ public string LogSuffix
{
- string version = RuntimeFrameworkVersion;
-
- // The log name can't contain wild cards, which are used in some testing scenarios.
- // TODO: The better solution would be to sanitize the file name properly, in case
- // there's a key being used that contains a character that is not a valid file
- // name charater.
- if (!string.IsNullOrEmpty(version) && version.Contains('*'))
+ get
{
- version = _truncatedRuntimeFrameworkVersion;
- }
+ string version = RuntimeFrameworkVersion;
- return GetStringViewWithVersion(version);
+ // The log name can't contain wild cards, which are used in some testing scenarios.
+ // TODO: The better solution would be to sanitize the file name properly, in case
+ // there's a key being used that contains a character that is not a valid file
+ // name charater.
+ if (!string.IsNullOrEmpty(version) && version.Contains('*'))
+ {
+ version = _truncatedRuntimeFrameworkVersion;
+ }
+
+ return GetStringViewWithVersion(version);
+ }
}
public IReadOnlyDictionary<string, string> AllSettings
ConsoleTestOutputHelper consoleLogger = null;
if (!string.IsNullOrEmpty(config.LogDirPath))
{
- string logFileName = $"{ testName }.{ config.GetLogSuffix() }.log";
+ string logFileName = $"{testName}.{config.LogSuffix}.log";
string logPath = Path.Combine(config.LogDirPath, logFileName);
fileLogger = new FileTestOutputHelper(logPath, FileMode.Append);
}
if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework")) {
AssemblyResolver.Enable();
}
- LoggingCommand.Initialize();
+ DiagnosticLoggingService.Initialize();
}
/// <summary>
private HostServices()
{
_serviceProvider = new ServiceProvider();
+ _serviceProvider.AddService<IDiagnosticLoggingService>(DiagnosticLoggingService.Instance);
_symbolService = new SymbolService(this);
_symbolService.DefaultTimeout = DefaultTimeout;
_symbolService.DefaultRetryCount = DefaultRetryCount;
try
{
var consoleService = new ConsoleServiceFromDebuggerServices(DebuggerServices);
- _serviceProvider.AddService<IConsoleService>(consoleService);
+ var fileLoggingConsoleService = new FileLoggingConsoleService(consoleService);
+ DiagnosticLoggingService.Instance.SetConsole(consoleService, fileLoggingConsoleService);
+ _serviceProvider.AddService<IConsoleService>(fileLoggingConsoleService);
+ _serviceProvider.AddService<IConsoleFileLoggingService>(fileLoggingConsoleService);
_contextService = new ContextServiceFromDebuggerServices(this, DebuggerServices);
_serviceProvider.AddService<IContextService>(_contextService);
vars.Add("%DUMP_NAME%", dumpFileName);
}
vars.Add("%DEBUG_ROOT%", debuggeeConfig.BinaryDirPath);
+ vars.Add("%TEST_NAME%", information.TestName);
+ vars.Add("%LOG_PATH%", information.TestConfiguration.LogDirPath);
+ vars.Add("%LOG_SUFFIX%", information.TestConfiguration.LogSuffix);
vars.Add("%SOS_PATH%", information.TestConfiguration.SOSPath());
vars.Add("%DESKTOP_RUNTIME_PATH%", information.TestConfiguration.DesktopRuntimePath());
CONTINUE
-EXTCOMMAND:clrmodules
-VERIFY:\s*<HEXVAL>.*
-
EXTCOMMAND:modules -v
VERIFY:\s*<HEXVAL>\s+<HEXVAL>.*
VERIFY:(\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*)?
VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
+EXTCOMMAND:logopen %LOG_PATH%/%TEST_NAME%.%LOG_SUFFIX%.consolelog
+EXTCOMMAND:logging %LOG_PATH%/%TEST_NAME%.%LOG_SUFFIX%.diaglog
+
+EXTCOMMAND:clrmodules -v
+VERIFY:\s*<HEXVAL>.*
+
SOSCOMMAND:SyncBlk
# On Linux/MacOS we sometimes get "Error requesting SyncBlk data" error from the DAC.
IFDEF:WINDOWS
SOSCOMMAND:DumpGCData
SOSCOMMAND:DumpRuntimeTypes
+
+EXTCOMMAND:logclose
+EXTCOMMAND:logging --disable
public class Analyzer : IHost
{
private readonly ServiceProvider _serviceProvider;
- private readonly ConsoleService _consoleProvider;
+ private readonly ConsoleService _consoleService;
+ private readonly FileLoggingConsoleService _fileLoggingConsoleService;
private readonly CommandService _commandService;
private readonly SymbolService _symbolService;
private readonly ContextService _contextService;
public Analyzer()
{
- LoggingCommand.Initialize();
+ DiagnosticLoggingService.Initialize();
_serviceProvider = new ServiceProvider();
- _consoleProvider = new ConsoleService();
+ _consoleService = new ConsoleService();
+ _fileLoggingConsoleService = new FileLoggingConsoleService(_consoleService);
_commandService = new CommandService();
_symbolService = new SymbolService(this);
_contextService = new ContextService(this);
+ DiagnosticLoggingService.Instance.SetConsole(_fileLoggingConsoleService, _fileLoggingConsoleService);
_serviceProvider.AddService<IHost>(this);
- _serviceProvider.AddService<IConsoleService>(_consoleProvider);
+ _serviceProvider.AddService<IConsoleService>(_fileLoggingConsoleService);
+ _serviceProvider.AddService<IConsoleFileLoggingService>(_fileLoggingConsoleService);
+ _serviceProvider.AddService<IDiagnosticLoggingService>(DiagnosticLoggingService.Instance);
_serviceProvider.AddService<ICommandService>(_commandService);
_serviceProvider.AddService<ISymbolService>(_symbolService);
_serviceProvider.AddService<IContextService>(_contextService);
_serviceProvider.AddServiceFactory<SOSLibrary>(() => SOSLibrary.Create(this));
- _contextService.ServiceProvider.AddServiceFactory<ClrMDHelper>(() =>
- {
+ _contextService.ServiceProvider.AddServiceFactory<ClrMDHelper>(() => {
ClrRuntime clrRuntime = _contextService.Services.GetService<ClrRuntime>();
return clrRuntime != null ? new ClrMDHelper(clrRuntime) : null;
});
_commandService.AddCommands(new Assembly[] { typeof(ClrMDHelper).Assembly });
_commandService.AddCommands(new Assembly[] { typeof(SOSHost).Assembly });
_commandService.AddCommands(typeof(HelpCommand), (services) => new HelpCommand(_commandService, services));
- _commandService.AddCommands(typeof(ExitCommand), (services) => new ExitCommand(_consoleProvider.Stop));
+ _commandService.AddCommands(typeof(ExitCommand), (services) => new ExitCommand(_consoleService.Stop));
_commandService.AddCommands(typeof(SOSCommand), (services) => new SOSCommand(_commandService, services));
}
public Task<int> Analyze(FileInfo dump_path, string[] command)
{
- _consoleProvider.WriteLine($"Loading core dump: {dump_path} ...");
+ _fileLoggingConsoleService.WriteLine($"Loading core dump: {dump_path} ...");
// Attempt to load the persisted command history
string dotnetHome = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".dotnet");
try
{
string[] history = File.ReadAllLines(historyFileName);
- _consoleProvider.AddCommandHistory(history);
+ _consoleService.AddCommandHistory(history);
}
catch (Exception ex) when
(ex is IOException ||
foreach (string cmd in command)
{
_commandService.Execute(cmd, _contextService.Services);
- if (_consoleProvider.Shutdown)
+ if (_consoleService.Shutdown)
{
break;
}
}
}
- if (!_consoleProvider.Shutdown && (!Console.IsOutputRedirected || Console.IsInputRedirected))
+ if (!_consoleService.Shutdown && (!Console.IsOutputRedirected || Console.IsInputRedirected))
{
// Start interactive command line processing
- _consoleProvider.WriteLine("Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.");
- _consoleProvider.WriteLine("Type 'quit' or 'exit' to exit the session.");
+ _fileLoggingConsoleService.WriteLine("Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.");
+ _fileLoggingConsoleService.WriteLine("Type 'quit' or 'exit' to exit the session.");
- _consoleProvider.Start((string commandLine, CancellationToken cancellation) =>
+ _consoleService.Start((string prompt, string commandLine, CancellationToken cancellation) =>
{
+ _fileLoggingConsoleService.WriteLine("{0}{1}", prompt, commandLine);
_commandService.Execute(commandLine, _contextService.Services);
});
}
ex is InvalidOperationException ||
ex is NotSupportedException)
{
- _consoleProvider.WriteLine(OutputType.Error, $"{ex.Message}");
+ _fileLoggingConsoleService.WriteError($"{ex.Message}");
return Task.FromResult(1);
}
finally
// Persist the current command history
try
{
- File.WriteAllLines(historyFileName, _consoleProvider.GetCommandHistory());
+ File.WriteAllLines(historyFileName, _consoleService.GetCommandHistory());
}
catch (Exception ex) when
(ex is IOException ||
}
catch (Exception ex) when (ex is IOException || ex is ArgumentException || ex is BadImageFormatException || ex is System.Security.SecurityException)
{
- _consoleProvider.WriteLineError($"Extension load {extensionPath} FAILED {ex.Message}");
+ _fileLoggingConsoleService.WriteLineError($"Extension load {extensionPath} FAILED {ex.Message}");
}
if (assembly is not null)
{
_commandService.AddCommands(assembly);
- _consoleProvider.WriteLine($"Extension loaded {extensionPath}");
+ _fileLoggingConsoleService.WriteLine($"Extension loaded {extensionPath}");
}
}
}