diff --git a/loc/lcl/CHS/OpenFolderSchema.json.lcl b/loc/lcl/CHS/OpenFolderSchema.json.lcl
index a17ab9b24..1d907d5d2 100644
--- a/loc/lcl/CHS/OpenFolderSchema.json.lcl
+++ b/loc/lcl/CHS/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/CHT/OpenFolderSchema.json.lcl b/loc/lcl/CHT/OpenFolderSchema.json.lcl
index 0ce4d648b..5072a7d00 100644
--- a/loc/lcl/CHT/OpenFolderSchema.json.lcl
+++ b/loc/lcl/CHT/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/CSY/OpenFolderSchema.json.lcl b/loc/lcl/CSY/OpenFolderSchema.json.lcl
index d3dd4ad6f..796e5db74 100644
--- a/loc/lcl/CSY/OpenFolderSchema.json.lcl
+++ b/loc/lcl/CSY/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/DEU/OpenFolderSchema.json.lcl b/loc/lcl/DEU/OpenFolderSchema.json.lcl
index b8941d421..e89c066ea 100644
--- a/loc/lcl/DEU/OpenFolderSchema.json.lcl
+++ b/loc/lcl/DEU/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/ESN/OpenFolderSchema.json.lcl b/loc/lcl/ESN/OpenFolderSchema.json.lcl
index 8a9a7376d..3e06b2806 100644
--- a/loc/lcl/ESN/OpenFolderSchema.json.lcl
+++ b/loc/lcl/ESN/OpenFolderSchema.json.lcl
@@ -814,6 +814,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/FRA/OpenFolderSchema.json.lcl b/loc/lcl/FRA/OpenFolderSchema.json.lcl
index 27f67fc05..613cfd697 100644
--- a/loc/lcl/FRA/OpenFolderSchema.json.lcl
+++ b/loc/lcl/FRA/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/ITA/OpenFolderSchema.json.lcl b/loc/lcl/ITA/OpenFolderSchema.json.lcl
index 7443d1a3b..96eae5bef 100644
--- a/loc/lcl/ITA/OpenFolderSchema.json.lcl
+++ b/loc/lcl/ITA/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/JPN/OpenFolderSchema.json.lcl b/loc/lcl/JPN/OpenFolderSchema.json.lcl
index 68e3dd5f7..07566f91b 100644
--- a/loc/lcl/JPN/OpenFolderSchema.json.lcl
+++ b/loc/lcl/JPN/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/KOR/OpenFolderSchema.json.lcl b/loc/lcl/KOR/OpenFolderSchema.json.lcl
index 2aa095794..ecdc13638 100644
--- a/loc/lcl/KOR/OpenFolderSchema.json.lcl
+++ b/loc/lcl/KOR/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/PLK/OpenFolderSchema.json.lcl b/loc/lcl/PLK/OpenFolderSchema.json.lcl
index e716460bb..e6f347966 100644
--- a/loc/lcl/PLK/OpenFolderSchema.json.lcl
+++ b/loc/lcl/PLK/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/PTB/OpenFolderSchema.json.lcl b/loc/lcl/PTB/OpenFolderSchema.json.lcl
index ad42d83f9..aaecef349 100644
--- a/loc/lcl/PTB/OpenFolderSchema.json.lcl
+++ b/loc/lcl/PTB/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/RUS/OpenFolderSchema.json.lcl b/loc/lcl/RUS/OpenFolderSchema.json.lcl
index 03db31052..3e4c12882 100644
--- a/loc/lcl/RUS/OpenFolderSchema.json.lcl
+++ b/loc/lcl/RUS/OpenFolderSchema.json.lcl
@@ -814,6 +814,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/loc/lcl/TRK/OpenFolderSchema.json.lcl b/loc/lcl/TRK/OpenFolderSchema.json.lcl
index 8ceb87e84..7553f7bec 100644
--- a/loc/lcl/TRK/OpenFolderSchema.json.lcl
+++ b/loc/lcl/TRK/OpenFolderSchema.json.lcl
@@ -865,6 +865,42 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
diff --git a/src/AndroidDebugLauncher/InstallPaths.cs b/src/AndroidDebugLauncher/InstallPaths.cs
index 72a1b9f12..70c456b10 100644
--- a/src/AndroidDebugLauncher/InstallPaths.cs
+++ b/src/AndroidDebugLauncher/InstallPaths.cs
@@ -69,7 +69,7 @@ public static InstallPaths Resolve(CancellationToken token, AndroidLaunchOptions
ThrowExternalFileNotFoundException(ndkReleaseVersionFile, LauncherResources.ProductName_NDK);
}
- logger.WriteLine("Using NDK '{0}' from path '{1}'", ndkReleaseId, ndkRoot);
+ logger.WriteLine(Microsoft.DebugEngineHost.LogLevel.Verbose, "Using NDK '{0}' from path '{1}'", ndkReleaseId, ndkRoot);
// 32 vs 64-bit doesn't matter when comparing
var r11 = new NdkReleaseId(11, 'a');
diff --git a/src/AndroidDebugLauncher/Launcher.cs b/src/AndroidDebugLauncher/Launcher.cs
index 1ca37f537..0730aef67 100644
--- a/src/AndroidDebugLauncher/Launcher.cs
+++ b/src/AndroidDebugLauncher/Launcher.cs
@@ -52,7 +52,7 @@ void IPlatformAppLauncher.Initialize(HostConfigurationStore configStore, IDevice
_eventCallback = eventCallback;
RegistryRoot.Set(configStore.RegistryRoot);
- Logger = MICore.Logger.EnsureInitialized(configStore);
+ Logger = MICore.Logger.EnsureInitialized();
}
void IPlatformAppLauncher.SetLaunchOptions(string exePath, string args, string dir, object launcherXmlOptions, TargetEngine targetEngine)
@@ -747,7 +747,7 @@ private Task StartGdbServer(string gdbServerRemotePath, string workingDirectory,
debugMessage.Replace("\r", "\\r");
debugMessage.Replace("\n", "\\n");
debugMessage.Replace("\t", "\\t");
- Logger.WriteLine(debugMessage.ToString());
+ Logger.WriteLine(LogLevel.Verbose, debugMessage.ToString());
}
// Here is the expected output from GDB Server --
@@ -769,7 +769,7 @@ private Task StartGdbServer(string gdbServerRemotePath, string workingDirectory,
_gdbServerExecCancellationSource.Token.ThrowIfCancellationRequested();
- Logger.WriteLine("ADB<-{0}", gdbServerCommand);
+ Logger.WriteLine(LogLevel.Verbose, "ADB<-{0}", gdbServerCommand);
Task serverExitedOrCanceled = _shell.ExecAsync(gdbServerCommand, _gdbServerExecCancellationSource.Token, outputHandler);
int completedTask = Task.WaitAny(serverReady.Task, serverExitedOrCanceled);
@@ -781,7 +781,7 @@ private Task StartGdbServer(string gdbServerRemotePath, string workingDirectory,
// they fail, try again with TCP.
if (useUnixSocket && HasGdbServerInvalidSocketError(errorOutput))
{
- Logger.WriteLine("Retrying GDB Server launch using TCP socket.");
+ Logger.WriteLine(LogLevel.Verbose, "Retrying GDB Server launch using TCP socket.");
return StartGdbServer(gdbServerRemotePath, workingDirectory, /*useUnixSocket:*/ false, out gdbServerSocketDescription);
}
@@ -842,11 +842,11 @@ private string ExecCommand(string command)
{
Debug.Assert(_shell != null, "ExecCommand called before m_shell is set");
- Logger.WriteLine("ADB<-{0}", command);
+ Logger.WriteLine(LogLevel.Verbose, "ADB<-{0}", command);
string response = ExecCommandNoLog(command);
- Logger.WriteTextBlock("ADB->", response);
+ Logger.WriteTextBlock(LogLevel.Verbose, "ADB->", response);
return response;
}
@@ -892,7 +892,7 @@ void IPlatformAppLauncher.OnResume()
}
catch (JDbg.JdwpException e)
{
- Logger.WriteLine("JdwpException: {0}", e.Message);
+ Logger.WriteLine(LogLevel.Warning, "JdwpException: {0}", e.Message);
string message = LauncherResources.Warning_JDbgResumeFailure;
diff --git a/src/DebugEngineHost.Common/HostLogChannel.cs b/src/DebugEngineHost.Common/HostLogChannel.cs
new file mode 100644
index 000000000..14bdc1b57
--- /dev/null
+++ b/src/DebugEngineHost.Common/HostLogChannel.cs
@@ -0,0 +1,143 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+/*
+ * NOTE: This file is shared between DebugEngineHost and DebugEngineHost.VSCode
+ */
+
+using System;
+using System.Globalization;
+using System.IO;
+
+namespace Microsoft.DebugEngineHost
+{
+ public enum LogLevel
+ {
+ ///
+ /// Logs that are used for interactive investigation during development.
+ /// These logs should primarily contain information useful for debugging and have no long-term value.
+ ///
+ Verbose,
+ ///
+ /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop.
+ ///
+ Warning,
+ ///
+ /// Logs that highlight when the current flow of execution is stopped due to a failure.
+ /// These should indicate a failure in the current activity, not an application-wide failure.
+ ///
+ Error,
+ ///
+ /// Not used for writing log messages.
+ /// Specifies that a logging category should not write any messages.
+ ///
+ None
+ }
+
+ // This must match the interface in DebugEngineHost.ref.cs
+ public interface ILogChannel
+ {
+ void SetLogLevel(LogLevel level);
+
+ void WriteLine(LogLevel level, string message);
+
+ void WriteLine(LogLevel level, string format, params object[] values);
+
+ void Flush();
+
+ void Close();
+ }
+
+ public class HostLogChannel : ILogChannel
+ {
+ private readonly Action _log;
+ private StreamWriter _logFile;
+ private LogLevel _minLevelToBeLogged;
+
+ private readonly object _lock = new object();
+
+ private HostLogChannel() { }
+
+ public HostLogChannel(Action logAction, string file, LogLevel logLevel)
+ {
+ _log = logAction;
+
+ if (!string.IsNullOrEmpty(file))
+ {
+ _logFile = File.CreateText(file);
+ }
+
+ _minLevelToBeLogged = logLevel;
+ }
+
+ ///
+ /// Sets the log level to the provided level.
+ ///
+ /// The level to set the logger.
+ public void SetLogLevel(LogLevel level)
+ {
+ _minLevelToBeLogged = level;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void WriteLine(LogLevel level, string message)
+ {
+ if (level >= _minLevelToBeLogged)
+ {
+ lock (_lock)
+ {
+ string prefix = string.Empty;
+ // Only indicate level if not verbose.
+ if (level != LogLevel.Verbose)
+ {
+ prefix = string.Format(CultureInfo.InvariantCulture, "[{0}] ", level.ToString());
+ }
+ string levelMsg = string.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, message);
+ _log?.Invoke(levelMsg);
+ _logFile?.WriteLine(levelMsg);
+ _logFile?.Flush();
+ }
+
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void WriteLine(LogLevel level, string format, params object[] values)
+ {
+ if (level >= _minLevelToBeLogged)
+ {
+ lock (_lock)
+ {
+ string message = string.Format(CultureInfo.CurrentCulture, format, values);
+ this.WriteLine(level, message);
+ }
+ }
+ }
+
+ public void Flush()
+ {
+ lock (_lock)
+ {
+ _logFile?.Flush();
+ }
+ }
+
+ public void Close()
+ {
+ lock (_lock)
+ {
+ _logFile?.Close();
+ _logFile = null;
+ }
+ }
+ }
+}
diff --git a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs
index 9d528c1bd..9b42b62e0 100644
--- a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs
+++ b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs
@@ -162,21 +162,6 @@ public void GetExceptionCategorySettings(Guid categoryId, out HostConfigurationS
throw new NotImplementedException();
}
- ///
- /// Checks if logging is enabled, and if so returns a logger object.
- ///
- /// In VS, this is wired up to read from the registry and return a logger which writes a log file to %TMP%\log-file-name.
- /// In VS Code, this will check if the '--engineLogging' switch is enabled, and if so return a logger that will write to the Console.
- ///
- /// [Optional] In VS, the name of the settings key to check if logging is enabled.
- /// If not specified, this will check 'EnableLogging' in the AD7 Metrics.
- /// [Required] name of the log file to open if logging is enabled.
- /// [Optional] If logging is enabled, the logging object.
- public HostLogger GetLogger(string enableLoggingSettingName, string logFileName)
- {
- throw new NotImplementedException();
- }
-
///
/// Read the debugger setting
///
@@ -197,56 +182,129 @@ public object GetCustomLauncher(string launcherTypeName)
{
throw new NotImplementedException();
}
-}
+ }
+
+ ///
+ /// Level of logging used for HostLogChannel
+ ///
+ public enum LogLevel
+ {
+ ///
+ /// Logs that are used for interactive investigation during development.
+ /// These logs should primarily contain information useful for debugging and have no long-term value.
+ ///
+ Verbose,
+ ///
+ /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop.
+ ///
+ Warning,
+ ///
+ /// Logs that highlight when the current flow of execution is stopped due to a failure.
+ /// These should indicate a failure in the current activity, not an application-wide failure.
+ ///
+ Error,
+ ///
+ /// Not used for writing log messages.
+ /// Specifies that a logging category should not write any messages.
+ ///
+ None
+ }
+
+ ///
+ /// The channel used for logging messages.
+ /// Channels are used if there are multiple types of logs,
+ /// e.g. Engine logs and Natvis logs
+ ///
+ public interface ILogChannel
+ {
+ ///
+ /// Changes the log level of the channel
+ ///
+ /// The new log level to use.
+ void SetLogLevel(LogLevel newLevel);
+
+ ///
+ /// Writes the given message with a newline to the log channel.
+ ///
+ /// The level of the log
+ /// The message string to send.
+ void WriteLine(LogLevel level, string message);
+
+ ///
+ /// Writes the given formatted message with the additional values with a newline to the log channel.
+ ///
+ /// The level of the log
+ /// Format to use.
+ /// Values to use within the provided format.
+ void WriteLine(LogLevel level, string format, params object[] values);
-///
-/// The host logger returned from HostConfigurationStore.GetLogger.
-///
-public sealed class HostLogger
+ ///
+ /// If the log is implemented as a file, this flushes the file.
+ ///
+ void Flush();
+
+ ///
+ /// If the log is implemented as a file, this closes the file.
+ ///
+ void Close();
+ }
+
+ ///
+ ///
+ ///
+ public static class HostLogger
{
///
- /// Callback for programmatic display of log messages
+ /// Enables engine logging if not already enabled.
///
- ///
- public delegate void OutputCallback(string outputMessage);
+ /// The callback to use to send the engine log.
+ /// The level of the log to filter the channel on.
+ public static void EnableHostLogging(Action callback, LogLevel level = LogLevel.Verbose)
+ {
+ throw new NotImplementedException();
+ }
- private HostLogger()
+ ///
+ /// Enables natvis logging if not already enabled.
+ ///
+ /// The callback to use to send the natvis log.
+ /// The level of the log to filter the channel on.
+ public static void EnableNatvisDiagnostics(Action callback, LogLevel level = LogLevel.Verbose)
{
throw new NotImplementedException();
}
///
- /// Writes a line to the log
+ /// Sets the log file to write to.
///
- /// Line to write.
- public void WriteLine(string line)
+ /// The file to write engine logs to.
+ public static void SetEngineLogFile(string logFile)
{
throw new NotImplementedException();
}
///
- /// If the log is implemented as a file, this flushes the file.
+ /// Gets the engine log channel created by 'EnableHostLogging'
///
- public void Flush()
+ /// A logger object if logging is enabled, or null if it is not
+ public static ILogChannel GetEngineLogChannel()
{
throw new NotImplementedException();
}
///
- /// If the log is implemented as a file, this closes the file.
+ /// Gets the Natvis log channel if its been created.
///
- public void Close()
+ /// A logger object if logging is enabled, or null if it is not
+ public static ILogChannel GetNatvisLogChannel()
{
throw new NotImplementedException();
}
///
- /// Get a logger after the user has explicitly configured a log file/callback
+ /// Clears the logging objects if enabled.
///
- ///
- ///
- /// The host logger object
- public static HostLogger GetLoggerFromCmd(string logFileName, HostLogger.OutputCallback callback)
+ public static void Reset()
{
throw new NotImplementedException();
}
@@ -394,6 +452,14 @@ public static void FindNatvis(NatvisLoader loader)
throw new NotImplementedException();
}
+ ///
+ /// Enable's tracking the VS 'Natvis Diagnostic Messages (C++ only)' setting.
+ ///
+ public static IDisposable WatchNatvisOptionSetting(HostConfigurationStore configStore, ILogChannel natvisLogger)
+ {
+ throw new NotImplementedException();
+ }
+
///
/// Return the solution's root directory, null if no solution
///
diff --git a/src/DebugEngineHost.VSCode/DebugEngineHost.VSCode.csproj b/src/DebugEngineHost.VSCode/DebugEngineHost.VSCode.csproj
index eef5e304b..428c8b4ca 100644
--- a/src/DebugEngineHost.VSCode/DebugEngineHost.VSCode.csproj
+++ b/src/DebugEngineHost.VSCode/DebugEngineHost.VSCode.csproj
@@ -23,6 +23,8 @@
+
+
diff --git a/src/DebugEngineHost.VSCode/HostConfigurationStore.cs b/src/DebugEngineHost.VSCode/HostConfigurationStore.cs
index 8eaf45de3..9736c3735 100644
--- a/src/DebugEngineHost.VSCode/HostConfigurationStore.cs
+++ b/src/DebugEngineHost.VSCode/HostConfigurationStore.cs
@@ -64,20 +64,6 @@ public void GetExceptionCategorySettings(Guid categoryId, out HostConfigurationS
categoryConfigSection = new HostConfigurationSection(category.DefaultTriggers);
}
- ///
- /// Checks if logging is enabled, and if so returns a logger object.
- ///
- /// In VS, this is wired up to read from the registry and return a logger which writes a log file to %TMP%\log-file-name.
- /// In VS Code, this will check if the '--engineLogging' switch is enabled, and if so return a logger that wil write to the logger output.
- ///
- /// [Optional] In VS, the name of the settings key to check if logging is enabled. If not specified, this will check 'EnableLogging' in the AD7 Metrics.
- /// [Required] name of the log file to open if logging is enabled. This is ignored for VSCode.
- /// [Optional] If logging is enabled, the logging object.
- public HostLogger GetLogger(string enableLoggingSettingName, string logFileName)
- {
- return HostLogger.Instance;
- }
-
///
/// Read the debugger setting
///
diff --git a/src/DebugEngineHost.VSCode/HostLogger.cs b/src/DebugEngineHost.VSCode/HostLogger.cs
index 71658194d..6723a4aee 100644
--- a/src/DebugEngineHost.VSCode/HostLogger.cs
+++ b/src/DebugEngineHost.VSCode/HostLogger.cs
@@ -5,85 +5,51 @@
namespace Microsoft.DebugEngineHost
{
- public sealed class HostLogger
+ public static class HostLogger
{
- public delegate void OutputCallback(string outputMessage);
+ private static ILogChannel s_natvisLogChannel;
+ private static ILogChannel s_engineLogChannel;
- private static HostLogger s_instance;
- private static readonly object s_lock = new object();
+ private static string s_engineLogFile;
- /// [Optional] VSCode-only host logger instance.
- public static HostLogger Instance { get { return s_instance; } }
-
- /// [Optional] VSCode-only method for obtaining the current host logger instance.
- public static void EnableHostLogging()
+ public static void EnableNatvisDiagnostics(Action callback, LogLevel level = LogLevel.Verbose)
{
- if (s_instance == null)
+ if (s_natvisLogChannel == null)
{
- lock (s_lock)
- {
- if (s_instance == null)
- {
- s_instance = new HostLogger();
- }
- }
+ // TODO: Support writing natvis logs to a file.
+ s_natvisLogChannel = new HostLogChannel(callback, null, level);
}
}
- private string _logFilePath = null;
- private System.IO.StreamWriter _logFile = null;
-
- /// Callback for logging text to the desired output stream.
- public Action LogCallback { get; set; } = null;
-
- /// The path to the log file.
- public string LogFilePath
+ public static void EnableHostLogging(Action callback, LogLevel level = LogLevel.Verbose)
{
- get
+ if (s_engineLogChannel == null)
{
- return _logFilePath;
- }
- set
- {
- _logFile?.Dispose();
- _logFilePath = value;
-
- if (!String.IsNullOrEmpty(_logFilePath))
- {
- _logFile = System.IO.File.CreateText(_logFilePath);
- }
+ s_engineLogChannel = new HostLogChannel(callback, s_engineLogFile, level);
}
}
- private HostLogger() { }
-
- public void WriteLine(string line)
+ public static void SetEngineLogFile(string logFile)
{
- lock (s_lock)
- {
- _logFile?.WriteLine(line);
- _logFile?.Flush();
- LogCallback?.Invoke(line);
- }
+ s_engineLogFile = logFile;
}
- public void Flush()
+ public static ILogChannel GetEngineLogChannel()
{
+ return s_engineLogChannel;
}
- public void Close()
+ public static ILogChannel GetNatvisLogChannel()
{
+ return s_natvisLogChannel;
}
- ///
- /// Get a logger after the user has explicitly configured a log file/callback
- ///
- ///
- ///
- /// The host logger object
- public static HostLogger GetLoggerFromCmd(string logFileName, HostLogger.OutputCallback callback)
+ public static void Reset()
{
- throw new NotImplementedException();
+ s_natvisLogChannel?.Close();
+ s_natvisLogChannel = null;
+ s_engineLogChannel?.Close();
+ s_engineLogChannel = null;
}
}
}
diff --git a/src/DebugEngineHost.VSCode/HostNatvisProject.cs b/src/DebugEngineHost.VSCode/HostNatvisProject.cs
index 7456f3b1d..e24c4f812 100644
--- a/src/DebugEngineHost.VSCode/HostNatvisProject.cs
+++ b/src/DebugEngineHost.VSCode/HostNatvisProject.cs
@@ -14,6 +14,12 @@ public static void FindNatvis(NatvisLoader loader)
// In-solution natvis is not supported for VS Code now, so do nothing.
}
+ public static IDisposable WatchNatvisOptionSetting(HostConfigurationStore configStore, ILogChannel natvisLogger)
+ {
+ // VS Code does not have a registry setting for Natvis Diagnostics
+ return null;
+ }
+
public static string FindSolutionRoot()
{
// This was added in MIEngine to support breakpoint sourcefile mapping.
diff --git a/src/DebugEngineHost.VSCode/Logger.cs b/src/DebugEngineHost.VSCode/Logger.cs
deleted file mode 100644
index 19dcaa868..000000000
--- a/src/DebugEngineHost.VSCode/Logger.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Microsoft.DebugEngineHost
-{
- ///
- /// VS Code only class to write to the log. This is enabled through the '--engineLogging[=file]' command line argument.
- ///
- public static class Logger
- {
- public static void WriteFrame([CallerMemberName]string caller = null)
- {
- CoreWrite(caller);
- }
-
- public static void WriteLine(string s)
- {
- CoreWrite(s);
- }
-
- private static void CoreWrite(string line)
- {
- HostLogger.Instance?.WriteLine(line);
- }
- }
-}
diff --git a/src/DebugEngineHost/DebugEngineHost.csproj b/src/DebugEngineHost/DebugEngineHost.csproj
index ba45d7f48..5a09a365d 100755
--- a/src/DebugEngineHost/DebugEngineHost.csproj
+++ b/src/DebugEngineHost/DebugEngineHost.csproj
@@ -29,6 +29,8 @@
+
+
diff --git a/src/DebugEngineHost/HostConfigurationSection.cs b/src/DebugEngineHost/HostConfigurationSection.cs
index c076e8e18..7f3f3f402 100644
--- a/src/DebugEngineHost/HostConfigurationSection.cs
+++ b/src/DebugEngineHost/HostConfigurationSection.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -42,5 +43,7 @@ public IEnumerable GetValueNames()
{
return _key.GetValueNames();
}
+
+ public SafeRegistryHandle Handle => _key.Handle;
}
}
diff --git a/src/DebugEngineHost/HostConfigurationStore.cs b/src/DebugEngineHost/HostConfigurationStore.cs
index 258dc59ec..1f491a3d9 100644
--- a/src/DebugEngineHost/HostConfigurationStore.cs
+++ b/src/DebugEngineHost/HostConfigurationStore.cs
@@ -8,8 +8,10 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
+using static Microsoft.VisualStudio.Shell.RegistrationAttribute;
namespace Microsoft.DebugEngineHost
{
@@ -17,9 +19,12 @@ public sealed class HostConfigurationStore
{
private const string DebuggerSectionName = "Debugger";
private const string LaunchersSectionName = "MILaunchers";
+ private const string NatvisDiagnosticsSectionName = "NatvisDiagnostics";
private string _engineId;
private string _registryRoot;
+
+ // HKLM RegistryKey
private RegistryKey _configKey;
public HostConfigurationStore(string registryRoot)
@@ -81,38 +86,6 @@ public void GetExceptionCategorySettings(Guid categoryId, out HostConfigurationS
categoryName = categoryKey.GetSubKeyNames().Single();
}
- ///
- /// Checks if logging is enabled, and if so returns a logger object.
- ///
- /// [Optional] In VS, the name of the settings key to check if logging is enabled. If not specified, this will check 'Logging' in the AD7 Metrics.
- /// [Required] name of the log file to open if logging is enabled.
- /// If no error then logging object. If file cannot be openened then throw an exception. Otherwise return an empty logger - the user can explictly reconfigure it later
- public HostLogger GetLogger(string enableLoggingSettingName, string logFileName)
- {
- if (string.IsNullOrEmpty(logFileName))
- {
- throw new ArgumentNullException(nameof(logFileName));
- }
- object enableLoggingValue;
- if (!string.IsNullOrEmpty(enableLoggingSettingName))
- {
- enableLoggingValue = GetOptionalValue(DebuggerSectionName, enableLoggingSettingName);
- }
- else
- {
- enableLoggingValue = GetEngineMetric("EnableLogging");
- }
-
- if (enableLoggingValue == null ||
- !(enableLoggingValue is int) ||
- ((int)enableLoggingValue) == 0)
- {
- return null;
- }
-
- return new HostLogger(HostLogger.GetStreamForName(logFileName, throwInUseError:false));
- }
-
public T GetDebuggerConfigurationSetting(string settingName, T defaultValue)
{
return GetDebuggerConfigurationSetting(DebuggerSectionName, settingName, defaultValue);
@@ -163,5 +136,45 @@ private object GetOptionalValue(string section, string valueName)
return key.GetValue(valueName);
}
}
+
+ ///
+ /// This method grabs the Debugger Subkey in HKCU
+ ///
+ /// The subkey of Debugger if it exists. Returns null otherwise.
+ public HostConfigurationSection GetCurrentUserDebuggerSection()
+ {
+ using (RegistryKey hkcuRoot = Registry.CurrentUser.OpenSubKey(_registryRoot))
+ {
+ RegistryKey debuggerSection = hkcuRoot.OpenSubKey(DebuggerSectionName);
+ if (debuggerSection != null)
+ {
+ return new HostConfigurationSection(debuggerSection);
+ }
+ return null;
+ }
+ }
+
+ ///
+ /// Grabs the Debugger/NatvisDiagnostic subkey in HKCU
+ ///
+ /// The NatvisDiagnostic subkey if it exists. Returns null otherwise.
+ public HostConfigurationSection GetNatvisDiagnosticSection()
+ {
+ using (RegistryKey hkcuRoot = Registry.CurrentUser.OpenSubKey(_registryRoot))
+ {
+ using (RegistryKey debuggerSection = hkcuRoot.OpenSubKey(DebuggerSectionName))
+ {
+ if (debuggerSection != null)
+ {
+ RegistryKey natvisDiagnosticKey = debuggerSection.OpenSubKey(NatvisDiagnosticsSectionName);
+ if (natvisDiagnosticKey != null)
+ {
+ return new HostConfigurationSection(natvisDiagnosticKey);
+ }
+ }
+ }
+ }
+ return null;
+ }
}
}
diff --git a/src/DebugEngineHost/HostLogger.cs b/src/DebugEngineHost/HostLogger.cs
index 808a24394..9ce19cf22 100644
--- a/src/DebugEngineHost/HostLogger.cs
+++ b/src/DebugEngineHost/HostLogger.cs
@@ -3,101 +3,59 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.DebugEngineHost
{
- public sealed class HostLogger
+ public static class HostLogger
{
- ///
- /// Callback for programmatic display of log messages
- ///
- ///
- public delegate void OutputCallback(string outputString);
+ private static ILogChannel s_natvisLogChannel;
+ private static ILogChannel s_engineLogChannel;
- private StreamWriter _streamWriter;
- private OutputCallback _callback;
- private readonly object _locker = new object();
+ private static string s_engineLogFile;
- internal HostLogger(StreamWriter streamWriter = null, OutputCallback callback = null)
+ public static void EnableHostLogging(Action callback, LogLevel level = LogLevel.Verbose)
{
- _streamWriter = streamWriter;
- _callback = callback;
+ if (s_engineLogChannel == null)
+ {
+ s_engineLogChannel = new HostLogChannel(callback, s_engineLogFile, level);
+ }
}
- public void WriteLine(string line)
+ public static void EnableNatvisDiagnostics(Action callback, LogLevel level = LogLevel.Verbose)
{
- lock (_locker)
+ if (s_natvisLogChannel== null)
{
- if (_streamWriter != null)
- _streamWriter.WriteLine(line);
- _callback?.Invoke(line);
+ s_natvisLogChannel = new HostLogChannel(callback, null, level);
}
}
- public void Flush()
+ public static void DisableNatvisDiagnostics()
{
- lock (_locker)
- {
- if (_streamWriter != null)
- _streamWriter.Flush();
- }
+ s_natvisLogChannel = null;
}
- public void Close()
+ public static void SetEngineLogFile(string logFile)
{
- lock (_locker)
- {
- if (_streamWriter != null)
- _streamWriter.Close();
- _streamWriter = null;
- }
+ s_engineLogFile = logFile;
}
- internal static StreamWriter GetStreamForName(string logFileName, bool throwInUseError)
+ public static ILogChannel GetEngineLogChannel()
{
- if (string.IsNullOrEmpty(logFileName))
- {
- return null;
- }
- string tempDirectory = Path.GetTempPath();
- StreamWriter writer = null;
- if (Path.IsPathRooted(logFileName) || (!string.IsNullOrEmpty(tempDirectory) && Directory.Exists(tempDirectory)))
- {
- string filePath = Path.Combine(tempDirectory, logFileName);
+ return s_engineLogChannel;
+ }
- try
- {
- FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.Read);
- writer = new StreamWriter(stream);
- }
- catch (IOException)
- {
- if (throwInUseError)
- throw;
- // ignore failures from the log being in use by another process
- }
- }
- else
- {
- throw new ArgumentOutOfRangeException(nameof(logFileName));
- }
- return writer;
+ public static ILogChannel GetNatvisLogChannel()
+ {
+ return s_natvisLogChannel;
}
- ///
- /// Get a logger after the user has explicitly configured a log file/callback
- ///
- ///
- ///
- /// The host logger object
- public static HostLogger GetLoggerFromCmd(string logFileName, HostLogger.OutputCallback callback)
+ public static void Reset()
{
- StreamWriter writer = HostLogger.GetStreamForName(logFileName, throwInUseError: true);
- return new HostLogger(writer, callback);
+ s_natvisLogChannel = null;
+ s_engineLogChannel = null;
}
}
}
diff --git a/src/DebugEngineHost/HostNatvisProject.cs b/src/DebugEngineHost/HostNatvisProject.cs
index 5ccf3974d..4c969b652 100644
--- a/src/DebugEngineHost/HostNatvisProject.cs
+++ b/src/DebugEngineHost/HostNatvisProject.cs
@@ -18,9 +18,26 @@
using Microsoft.VisualStudio.Workspace;
using Microsoft.VisualStudio.Workspace.Indexing;
using Microsoft.VisualStudio.Workspace.VSIntegration.Contracts;
+using Microsoft.Win32;
namespace Microsoft.DebugEngineHost
{
+ internal class RegisterMonitorWrapper : IDisposable
+ {
+ public RegistryMonitor CurrentMonitor { get; set; }
+
+ internal RegisterMonitorWrapper(RegistryMonitor currentMonitor)
+ {
+ CurrentMonitor = currentMonitor;
+ }
+
+ public void Dispose()
+ {
+ CurrentMonitor.Dispose();
+ CurrentMonitor = null;
+ }
+ }
+
///
/// Provides interactions with the host's source workspace to locate and load any natvis files
/// in the project.
@@ -49,6 +66,113 @@ public static void FindNatvis(NatvisLoader loader)
paths.ForEach((s) => loader(s));
}
+ public static IDisposable WatchNatvisOptionSetting(HostConfigurationStore configStore, ILogChannel natvisLogger)
+ {
+ RegisterMonitorWrapper rmw = null;
+
+ HostConfigurationSection natvisDiagnosticSection = configStore.GetNatvisDiagnosticSection();
+ if (natvisDiagnosticSection != null)
+ {
+ // DiagnosticSection exists, set current log level and watch for changes.
+ SetNatvisLogLevel(natvisDiagnosticSection);
+
+ rmw = new RegisterMonitorWrapper(CreateAndStartNatvisDiagnosticMonitor(natvisDiagnosticSection, natvisLogger));
+ }
+ else
+ {
+ // NatvisDiagnostic section has not been created, we need to watch for the creation.
+ HostConfigurationSection debuggerSection = configStore.GetCurrentUserDebuggerSection();
+
+ if (debuggerSection != null)
+ {
+ // We only care about the debugger subkey's keys since we are waiting for the NatvisDiagnostics
+ // section to be created.
+ RegistryMonitor rm = new RegistryMonitor(debuggerSection, false, natvisLogger);
+
+ rmw = new RegisterMonitorWrapper(rm);
+
+ rm.RegChanged += (sender, e) =>
+ {
+ HostConfigurationSection checkForSection = configStore.GetNatvisDiagnosticSection();
+
+ if (checkForSection != null)
+ {
+ // NatvisDiagnostic section found. Update the logger
+ SetNatvisLogLevel(checkForSection);
+
+ // Remove debugger section tracking
+ IDisposable disposable = rmw.CurrentMonitor;
+
+ // Watch NatvisDiagnostic section
+ rmw = new RegisterMonitorWrapper(CreateAndStartNatvisDiagnosticMonitor(natvisDiagnosticSection, natvisLogger));
+
+ disposable.Dispose();
+ }
+ };
+
+ rm.Start();
+ }
+ }
+
+
+ return rmw;
+ }
+
+ private static RegistryMonitor CreateAndStartNatvisDiagnosticMonitor(HostConfigurationSection natvisDiagnosticSection, ILogChannel natvisLogger)
+ {
+ RegistryMonitor rm = new RegistryMonitor(natvisDiagnosticSection, true, natvisLogger);
+
+ rm.RegChanged += (sender, e) =>
+ {
+ SetNatvisLogLevel(natvisDiagnosticSection);
+ };
+
+ rm.Start();
+
+ return rm;
+ }
+
+ private static void SetNatvisLogLevel(HostConfigurationSection natvisDiagnosticSection)
+ {
+ string level = natvisDiagnosticSection.GetValue("Level") as string;
+ if (level != null)
+ {
+ level = level.ToLower(CultureInfo.InvariantCulture);
+ }
+ LogLevel logLevel;
+ switch (level)
+ {
+ case "off":
+ logLevel = LogLevel.None;
+ break;
+ case "error":
+ logLevel = LogLevel.Error;
+ break;
+ case "warning":
+ logLevel = LogLevel.Warning;
+ break;
+ case "verbose":
+ logLevel = LogLevel.Verbose;
+ break;
+ default: // Unknown, default to Warning
+ logLevel = LogLevel.Warning;
+ break;
+ }
+
+ if (logLevel == LogLevel.None)
+ {
+ HostLogger.DisableNatvisDiagnostics();
+ }
+ else
+ {
+ HostLogger.EnableNatvisDiagnostics((message) => {
+ string formattedMessage = string.Format(CultureInfo.InvariantCulture, "Natvis: {0}", message);
+ HostOutputWindow.WriteLaunchError(formattedMessage);
+ }, logLevel);
+ HostLogger.GetNatvisLogChannel().SetLogLevel(logLevel);
+ }
+ }
+
public static string FindSolutionRoot()
{
string path = null;
diff --git a/src/DebugEngineHost/HostOutputWindow.cs b/src/DebugEngineHost/HostOutputWindow.cs
index e1136f04b..1d2b63632 100644
--- a/src/DebugEngineHost/HostOutputWindow.cs
+++ b/src/DebugEngineHost/HostOutputWindow.cs
@@ -2,13 +2,139 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
-
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.DebugEngineHost
{
+ internal static class VsOutputWindowWrapper
+ {
+ private static Lazy outputWindowLazy = new Lazy(() =>
+ {
+ IVsOutputWindow outputWindow = null;
+ try
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+ outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow;
+ }
+ catch (Exception)
+ {
+ Debug.Fail("Could not get OutputWindow service.");
+ }
+ return outputWindow;
+ }, LazyThreadSafetyMode.PublicationOnly);
+
+ private static Lazy shellLazy = new Lazy(() =>
+ {
+ IVsUIShell shell = null;
+ try
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+ shell = Package.GetGlobalService(typeof(SVsUIShell)) as IVsUIShell;
+ }
+ catch (Exception)
+ {
+ Debug.Fail("Could not get VSShell service.");
+ }
+ return shell;
+ // Use "PublicationOnly", because the implementation of GetService does its own locking
+ }, LazyThreadSafetyMode.PublicationOnly);
+
+ private class PaneInfo
+ {
+ public PaneInfo(Guid paneId)
+ {
+ this.paneId = paneId;
+ this.Shown = false;
+ }
+
+ internal Guid paneId;
+ internal bool Shown { get; set; }
+ }
+
+ private const string DefaultOutputPane = "Debug";
+
+ private static Dictionary panes = new Dictionary()
+ {
+ // The 'Debug' pane exists by default
+ { DefaultOutputPane, new PaneInfo(VSConstants.GUID_OutWindowDebugPane) }
+ };
+
+ ///
+ /// Writes text directly to the VS Output window.
+ ///
+ public static void Write(string message, string pane = DefaultOutputPane)
+ {
+ ThreadHelper.JoinableTaskFactory.RunAsync(async delegate
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ try
+ {
+ // Get the Output window
+ IVsOutputWindow outputWindow = outputWindowLazy.Value;
+ if (outputWindow == null)
+ {
+ return;
+ }
+
+ // Get the pane guid
+ PaneInfo paneInfo;
+ if (!panes.TryGetValue(pane, out paneInfo))
+ {
+ // Pane didn't exist, create it
+ paneInfo = new PaneInfo(Guid.NewGuid());
+ panes.Add(pane, paneInfo);
+ }
+
+ // Get the pane
+ IVsOutputWindowPane outputPane;
+ if (outputWindow.GetPane(ref paneInfo.paneId, out outputPane) != VSConstants.S_OK)
+ {
+ // Failed to get the pane - might need to create it first
+ outputWindow.CreatePane(ref paneInfo.paneId, pane, fInitVisible: 1, fClearWithSolution: 1);
+ outputWindow.GetPane(ref paneInfo.paneId, out outputPane);
+ }
+
+ // The first time we output text to a pane, ensure it's visible
+ if (!paneInfo.Shown)
+ {
+ paneInfo.Shown = true;
+
+ // Switch to the pane of the Output window
+ outputPane.Activate();
+
+ // Show the output window
+ IVsUIShell shell = shellLazy.Value;
+ if (shell != null)
+ {
+ object inputVariant = null;
+ shell.PostExecCommand(VSConstants.GUID_VSStandardCommandSet97, (uint)VSConstants.VSStd97CmdID.OutputWindow, 0, ref inputVariant);
+ }
+ }
+
+ // Output the text
+ outputPane.OutputString(message);
+ }
+ catch (Exception)
+ {
+ Debug.Fail("Failed to write to output pane.");
+ }
+ }).FileAndForget("VS/Diagnostics/Debugger/DebugEngineHost/VsOutputWindowWrapper/Write");
+ }
+
+ ///
+ /// Writes text directly to the VS Output window, appending a newline.
+ ///
+ public static void WriteLine(string message, string pane = DefaultOutputPane)
+ {
+ Write(string.Concat(message, Environment.NewLine), pane);
+ }
+ }
+
///
/// Provides direct access to the underlying output window without going through debug events
///
@@ -19,32 +145,7 @@ private static class VsImpl
{
internal static void SetText(string outputMessage)
{
- int hr;
-
- var outputWindow = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow));
- if (outputWindow == null)
- return;
-
- IVsOutputWindowPane pane;
- Guid guidDebugOutputPane = VSConstants.GUID_OutWindowDebugPane;
- hr = outputWindow.GetPane(ref guidDebugOutputPane, out pane);
- if (hr < 0)
- return;
-
- pane.Clear();
- pane.Activate();
-
- hr = pane.OutputString(outputMessage);
- if (hr < 0)
- return;
-
- var shell = (IVsUIShell)Package.GetGlobalService(typeof(SVsUIShell));
- if (shell == null)
- return;
-
- Guid commandSet = VSConstants.GUID_VSStandardCommandSet97;
- object inputVariant = null;
- shell.PostExecCommand(commandSet, (uint)VSConstants.VSStd97CmdID.OutputWindow, 0, ref inputVariant);
+ VsOutputWindowWrapper.WriteLine(outputMessage);
}
}
@@ -63,4 +164,4 @@ public static void WriteLaunchError(string outputMessage)
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/DebugEngineHost/RegistryMonitor.cs b/src/DebugEngineHost/RegistryMonitor.cs
new file mode 100644
index 000000000..6c6f1dcc0
--- /dev/null
+++ b/src/DebugEngineHost/RegistryMonitor.cs
@@ -0,0 +1,145 @@
+// // Copyright (c) Microsoft. All rights reserved.
+// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.Remoting.Messaging;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using static System.Collections.Specialized.BitVector32;
+
+namespace Microsoft.DebugEngineHost
+{
+ internal class RegistryMonitor : IDisposable
+ {
+ #region Native Methods
+
+ ///
+ /// Filter for notifications reported by .
+ ///
+ [Flags]
+ public enum RegChangeNotifyFilter
+ {
+ /// Notify the caller if a subkey is added or deleted.
+ REG_NOTIFY_CHANGE_NAME = 1,
+ /// Notify the caller of changes to the attributes of the key,
+ /// such as the security descriptor information.
+ REG_NOTIFY_CHANGE_ATTRIBUTES = 2,
+ /// Notify the caller of changes to a value of the key. This can
+ /// include adding or deleting a value, or changing an existing value.
+ REG_NOTIFY_CHANGE_LAST_SET = 4,
+ /// Notify the caller of changes to the security descriptor
+ /// of the key.
+ REG_NOTIFY_CHANGE_SECURITY = 8,
+ }
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ private static extern int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool bWatchSubtree,
+ RegChangeNotifyFilter dwNotifyFilter, IntPtr hEvent,
+ bool fAsynchronous);
+
+ #endregion
+
+ private HostConfigurationSection _section;
+ private readonly bool _watchSubtree;
+
+ // Set when registry value is changed
+ private AutoResetEvent m_changeEvent;
+
+ // Set when monitoring is stopped
+ private AutoResetEvent m_stoppedEvent;
+
+ ///
+ /// Occurs when the specified registry key has changed.
+ ///
+ public event EventHandler RegChanged;
+
+ private readonly ILogChannel _nativsLogger;
+
+ public RegistryMonitor(HostConfigurationSection section, bool watchSubtree, ILogChannel nativsLogger)
+ {
+ _section = section;
+ _watchSubtree = watchSubtree;
+ _nativsLogger = nativsLogger;
+ }
+
+ public void Start()
+ {
+ Thread registryMonitor = new Thread(Monitor);
+ registryMonitor.IsBackground = true;
+ registryMonitor.Name = "Microsoft.DebugEngineHost.RegistryMonitor";
+ registryMonitor.Start();
+ }
+
+ public void Stop()
+ {
+ if (m_stoppedEvent != null)
+ {
+ m_stoppedEvent.Set();
+ }
+ }
+
+ // The handle is owned by change event instance which lives while we use the handle.
+ [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")]
+ private void Monitor()
+ {
+ bool stopped = false;
+ try
+ {
+ m_stoppedEvent = new AutoResetEvent(false);
+ m_changeEvent = new AutoResetEvent(false);
+
+ IntPtr handle = m_changeEvent.SafeWaitHandle.DangerousGetHandle();
+
+ int errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
+ if (errorCode != 0) // 0 is ERROR_SUCCESS
+ {
+ _nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
+ }
+ else
+ {
+ while (!stopped)
+ {
+ int waitResult = WaitHandle.WaitAny(new WaitHandle[] { m_stoppedEvent, m_changeEvent });
+
+ if (waitResult == 0)
+ {
+ stopped = true;
+ }
+ else
+ {
+ errorCode = RegNotifyChangeKeyValue(_section.Handle, _watchSubtree, RegChangeNotifyFilter.REG_NOTIFY_CHANGE_NAME | RegChangeNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, handle, true);
+ if (errorCode != 0) // 0 is ERROR_SUCCESS
+ {
+ _nativsLogger?.WriteLine(LogLevel.Error, Resource.Error_WatchRegistry, errorCode);
+ break;
+ }
+ RegChanged?.Invoke(this, null);
+ }
+ }
+ }
+ }
+ finally
+ {
+ _section.Dispose();
+ m_stoppedEvent?.Dispose();
+ m_changeEvent?.Dispose();
+
+ m_stoppedEvent = null;
+ m_changeEvent = null;
+ }
+ }
+
+ public void Dispose()
+ {
+ m_stoppedEvent?.Dispose();
+ }
+ }
+}
diff --git a/src/DebugEngineHost/Resource.Designer.cs b/src/DebugEngineHost/Resource.Designer.cs
index a6ce3bf9e..4671d7f72 100644
--- a/src/DebugEngineHost/Resource.Designer.cs
+++ b/src/DebugEngineHost/Resource.Designer.cs
@@ -19,7 +19,7 @@ namespace Microsoft.DebugEngineHost {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
@@ -60,6 +60,15 @@ internal Resource() {
}
}
+ ///
+ /// Looks up a localized string similar to Failed to watch RegistryKey with error code '{0}'..
+ ///
+ internal static string Error_WatchRegistry {
+ get {
+ return ResourceManager.GetString("Error_WatchRegistry", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Workspace index is incomplete; some .natvis files may not have been found..
///
diff --git a/src/DebugEngineHost/Resource.resx b/src/DebugEngineHost/Resource.resx
index a9188fcdb..d4f310d72 100644
--- a/src/DebugEngineHost/Resource.resx
+++ b/src/DebugEngineHost/Resource.resx
@@ -117,6 +117,10 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Failed to watch RegistryKey with error code '{0}'.
+ {0} is an error code
+
Workspace index is incomplete; some .natvis files may not have been found.
diff --git a/src/MICore/CommandFactories/gdb.cs b/src/MICore/CommandFactories/gdb.cs
index 92c28938b..35d256e45 100644
--- a/src/MICore/CommandFactories/gdb.cs
+++ b/src/MICore/CommandFactories/gdb.cs
@@ -10,6 +10,7 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Globalization;
+using Microsoft.DebugEngineHost;
namespace MICore
{
@@ -298,7 +299,7 @@ public override async Task AutoComplete(string command, int threadId,
var matchlist = res.Find("matches");
if (int.Parse(res.FindString("max_completions_reached"), CultureInfo.InvariantCulture) != 0)
- _debugger.Logger.WriteLine("We reached max-completions!");
+ _debugger.Logger.WriteLine(LogLevel.Verbose, "We reached max-completions!");
return matchlist?.AsStrings;
}
diff --git a/src/MICore/Debugger.cs b/src/MICore/Debugger.cs
index de2796da2..cd26301f6 100755
--- a/src/MICore/Debugger.cs
+++ b/src/MICore/Debugger.cs
@@ -172,7 +172,7 @@ public Debugger(LaunchOptions launchOptions, Logger logger)
protected void SetDebuggerPid(int debuggerPid)
{
// Used for testing
- Logger.WriteLine(string.Concat("DebuggerPid=", debuggerPid));
+ Logger.WriteLine(LogLevel.Verbose, string.Concat("DebuggerPid=", debuggerPid));
_localDebuggerPid = debuggerPid;
}
@@ -201,7 +201,7 @@ private void RetryBreak(object o)
{
if (_waitingToStop && _retryCount < BREAK_RETRY_MAX)
{
- Logger.WriteLine("Debugger failed to break. Trying again.");
+ Logger.WriteLine(LogLevel.Verbose, "Debugger failed to break. Trying again.");
CmdBreak(BreakRequest.Internal);
_retryCount++;
}
@@ -935,7 +935,7 @@ void ITransportCallback.OnStdOutLine(string line)
void ITransportCallback.OnStdErrorLine(string line)
{
- Logger.WriteLine("STDERR: " + line);
+ Logger.WriteLine(LogLevel.Warning, "STDERR: " + line);
if (_initialErrors != null)
{
@@ -1047,7 +1047,7 @@ void SendUnsupportedWindowsGdbEvent(string version)
void ITransportCallback.AppendToInitializationLog(string line)
{
- Logger.WriteLine(line);
+ Logger.WriteLine(LogLevel.Verbose, line);
if (_initializationLog != null)
{
@@ -1240,7 +1240,7 @@ public void ProcessStdOutLine(string line)
if (waitingOperation != null)
{
Results results = _miResults.ParseCommandOutput(noprefix);
- Logger.WriteLine(id.ToString(CultureInfo.InvariantCulture) + ": elapsed time " + ((int)(DateTime.Now - waitingOperation.StartTime).TotalMilliseconds).ToString(CultureInfo.InvariantCulture));
+ Logger.WriteLine(LogLevel.Verbose, id.ToString(CultureInfo.InvariantCulture) + ": elapsed time " + ((int)(DateTime.Now - waitingOperation.StartTime).TotalMilliseconds).ToString(CultureInfo.InvariantCulture));
waitingOperation.OnComplete(results, this.MICommandFactory);
return;
}
diff --git a/src/MICore/ExceptionHelper.cs b/src/MICore/ExceptionHelper.cs
index 5563ff4cb..7295142b8 100644
--- a/src/MICore/ExceptionHelper.cs
+++ b/src/MICore/ExceptionHelper.cs
@@ -32,8 +32,8 @@ public static bool BeforeCatch(Exception currentException, Logger logger, bool r
{
HostTelemetry.ReportCurrentException(currentException, "Microsoft.MIDebugEngine");
- logger?.WriteLine("EXCEPTION: " + currentException.GetType());
- logger?.WriteTextBlock("EXCEPTION: ", currentException.StackTrace);
+ logger?.WriteLine(LogLevel.Error, "EXCEPTION: ", currentException.GetType());
+ logger?.WriteTextBlock(LogLevel.Error, "EXCEPTION: ", currentException.StackTrace);
}
catch
{
diff --git a/src/MICore/LaunchOptions.cs b/src/MICore/LaunchOptions.cs
index 835005b83..6ba4b10c1 100644
--- a/src/MICore/LaunchOptions.cs
+++ b/src/MICore/LaunchOptions.cs
@@ -1260,7 +1260,7 @@ public static LaunchOptions GetInstance(HostConfigurationStore configStore, stri
if (string.IsNullOrEmpty(options))
throw new InvalidLaunchOptionsException(MICoreResources.Error_StringIsNullOrEmpty);
- logger?.WriteTextBlock("LaunchOptions", options);
+ logger?.WriteTextBlock(LogLevel.Verbose, "LaunchOptions", options);
LaunchOptions launchOptions = null;
Guid clsidLauncher = Guid.Empty;
@@ -1500,7 +1500,7 @@ internal static SupplementalLaunchOptions GetOptionsFromFile(Logger logger)
{
try
{
- logger?.WriteTextBlock("SupplementalOptions", suppOptions);
+ logger?.WriteTextBlock(LogLevel.Verbose, "SupplementalOptions", suppOptions);
XmlReader xmlRrd = OpenXml(suppOptions);
XmlSerializer serializer = GetXmlSerializer(typeof(Xml.LaunchOptions.SupplementalLaunchOptions));
return (Xml.LaunchOptions.SupplementalLaunchOptions)Deserialize(serializer, xmlRrd);
diff --git a/src/MICore/Logger.cs b/src/MICore/Logger.cs
index 05e8e9ca9..86e2db72f 100644
--- a/src/MICore/Logger.cs
+++ b/src/MICore/Logger.cs
@@ -15,41 +15,45 @@
namespace MICore
{
- public interface ILogger
- {
- void WriteLine(string line);
-
- void WriteLine(string format, params object[] args);
- }
-
///
- /// Class which implements logging. The logging is control by a registry key. If enabled, logging goes to %TMP%\Microsoft.MIDebug.log
+ /// Class which implements logging.
///
- public class Logger : ILogger
+ public class Logger
{
private static bool s_isInitialized;
private static bool s_isEnabled;
private static DateTime s_initTime;
- // NOTE: We never clean this up
- private static HostLogger s_logger;
+ ///
+ /// Optional logger to get engine diagnostics logs
+ ///
+ private ILogChannel EngineLogger => HostLogger.GetEngineLogChannel();
+ ///
+ /// Optional logger to get natvis diagnostics logs
+ ///
+ public ILogChannel NatvisLogger => HostLogger.GetNatvisLogChannel();
private static int s_count;
- private int _id;
+ private readonly int _id;
+
+ #region Command Window
+
public class LogInfo
{
public string logFile;
- public HostLogger.OutputCallback logToOutput;
+ public Action logToOutput;
public bool enabled;
};
- private static LogInfo s_cmdLogInfo = new LogInfo();
+
+ private readonly static LogInfo s_cmdLogInfo = new LogInfo();
public static LogInfo CmdLogInfo { get { return s_cmdLogInfo; } }
+ #endregion
private Logger()
{
_id = Interlocked.Increment(ref s_count);
}
- public static Logger EnsureInitialized(HostConfigurationStore configStore)
+ public static Logger EnsureInitialized()
{
Logger res = new Logger();
if (!s_isInitialized)
@@ -57,8 +61,8 @@ public static Logger EnsureInitialized(HostConfigurationStore configStore)
s_isInitialized = true;
s_initTime = DateTime.Now;
- LoadMIDebugLogger(configStore);
- res.WriteLine("Initialized log at: " + s_initTime.ToString(CultureInfo.InvariantCulture));
+ LoadMIDebugLogger();
+ res.WriteLine(LogLevel.Verbose, "Initialized log at: " + s_initTime.ToString(CultureInfo.InvariantCulture));
}
#if DEBUG
@@ -70,75 +74,70 @@ public static Logger EnsureInitialized(HostConfigurationStore configStore)
return res;
}
- public static void LoadMIDebugLogger(HostConfigurationStore configStore)
+ public static void EnableNatvisDiagnostics(LogLevel level)
{
- if (s_logger == null)
- {
- if (CmdLogInfo.enabled)
- { // command configured log file
- s_logger = HostLogger.GetLoggerFromCmd(CmdLogInfo.logFile, CmdLogInfo.logToOutput);
- }
- else
- { // use default logging
- s_logger = configStore.GetLogger("EnableMIDebugLogger", "Microsoft.MIDebug.log");
- }
- if (s_logger != null)
- {
- s_isEnabled = true;
- }
+ HostLogger.EnableNatvisDiagnostics(HostOutputWindow.WriteLaunchError, level);
+ }
+
+ public static void LoadMIDebugLogger()
+ {
+ if (CmdLogInfo.enabled)
+ { // command configured log file
+ HostLogger.Reset();
+ HostLogger.SetEngineLogFile(CmdLogInfo.logFile);
+ HostLogger.EnableHostLogging(CmdLogInfo.logToOutput);
}
+
+ s_isEnabled = true;
}
public static void Reset()
{
- HostLogger logger;
if (CmdLogInfo.enabled)
{
- logger = HostLogger.GetLoggerFromCmd(CmdLogInfo.logFile, CmdLogInfo.logToOutput);
- logger = Interlocked.Exchange(ref s_logger, logger);
- logger?.Close();
- if (s_logger != null)
- {
- s_isEnabled = true;
- }
+ HostLogger.Reset();
+ s_isEnabled = false;
}
}
///
/// If logging is enabled, writes a line of text to the log
///
+ /// [Required] The level of the log.
/// [Required] line to write
- public void WriteLine(string line)
+ public void WriteLine(LogLevel level, string line)
{
if (s_isEnabled)
{
- WriteLineImpl(line);
+ WriteLineImpl(level, line);
}
}
///
/// If logging is enabled, writes a line of text to the log
///
+ /// [Required] The level of the log.
/// [Required] format string
/// arguments to use in the format string
- public void WriteLine(string format, params object[] args)
+ public void WriteLine(LogLevel level, string format, params object[] args)
{
if (s_isEnabled)
{
- WriteLineImpl(format, args);
+ WriteLineImpl(level, format, args);
}
}
///
/// If logging is enabled, writes a block of text which may contain newlines to the log
///
+ /// [Required] The level of the log.
/// [Optional] Prefix to put on the front of each line
/// Block of text to write
- public void WriteTextBlock(string prefix, string textBlock)
+ public void WriteTextBlock(LogLevel level, string prefix, string textBlock)
{
if (s_isEnabled)
{
- WriteTextBlockImpl(prefix, textBlock);
+ WriteTextBlockImpl(level, prefix, textBlock);
}
}
@@ -159,10 +158,10 @@ public static bool IsEnabled
}
[MethodImpl(MethodImplOptions.NoInlining)] // Disable inlining since logging is off by default, and we want to allow the public method to be inlined
- private void WriteLineImpl(string line)
+ private void WriteLineImpl(LogLevel level, string line)
{
string fullLine = String.Format(CultureInfo.CurrentCulture, "{2}: ({0}) {1}", (int)(DateTime.Now - s_initTime).TotalMilliseconds, line, _id);
- s_logger?.WriteLine(fullLine);
+ HostLogger.GetEngineLogChannel()?.WriteLine(level, fullLine);
#if DEBUG
Debug.WriteLine("MS_MIDebug: " + fullLine);
#endif
@@ -171,17 +170,17 @@ private void WriteLineImpl(string line)
[MethodImpl(MethodImplOptions.NoInlining)] // Disable inlining since logging is off by default, and we want to allow the public method to be inlined
private static void FlushImpl()
{
- s_logger?.Flush();
+ HostLogger.GetEngineLogChannel()?.Flush();
}
[MethodImpl(MethodImplOptions.NoInlining)] // Disable inlining since logging is off by default, and we want to allow the public method to be inlined
- private void WriteLineImpl(string format, object[] args)
+ private void WriteLineImpl(LogLevel level, string format, object[] args)
{
- WriteLineImpl(string.Format(CultureInfo.CurrentCulture, format, args));
+ WriteLineImpl(level, string.Format(CultureInfo.CurrentCulture, format, args));
}
[MethodImpl(MethodImplOptions.NoInlining)] // Disable inlining since logging is off by default, and we want to allow the public method to be inlined
- private void WriteTextBlockImpl(string prefix, string textBlock)
+ private void WriteTextBlockImpl(LogLevel level, string prefix, string textBlock)
{
using (var reader = new StringReader(textBlock))
{
@@ -192,9 +191,9 @@ private void WriteTextBlockImpl(string prefix, string textBlock)
break;
if (!string.IsNullOrEmpty(prefix))
- WriteLineImpl(prefix + line);
+ WriteLineImpl(level, prefix + line);
else
- WriteLineImpl(line);
+ WriteLineImpl(level, line);
}
}
}
diff --git a/src/MICore/MICore.csproj b/src/MICore/MICore.csproj
index 149488eba..abdc47283 100755
--- a/src/MICore/MICore.csproj
+++ b/src/MICore/MICore.csproj
@@ -65,7 +65,6 @@
-
PreserveNewest
vscode\osxlaunchhelper.scpt
diff --git a/src/MICore/MIResults.cs b/src/MICore/MIResults.cs
index 0e3a45704..0b8e4b9a2 100644
--- a/src/MICore/MIResults.cs
+++ b/src/MICore/MIResults.cs
@@ -9,6 +9,7 @@
using System.Diagnostics;
using System.Collections;
using System.Globalization;
+using Microsoft.DebugEngineHost;
namespace MICore
{
@@ -1199,7 +1200,7 @@ private void ParseError(string message, Span input)
string result = CreateErrorMessageFromSpan(input);
Debug.Fail(message + ": " + result);
- Logger?.WriteLine(String.Format(CultureInfo.CurrentCulture, "MI parsing error: {0}: \"{1}\"", message, result));
+ Logger?.WriteLine(LogLevel.Error, String.Format(CultureInfo.CurrentCulture, "MI parsing error: {0}: \"{1}\"", message, result));
}
diff --git a/src/MICore/SetMIDebugLogging.cmd b/src/MICore/SetMIDebugLogging.cmd
deleted file mode 100644
index 23396aa7c..000000000
--- a/src/MICore/SetMIDebugLogging.cmd
+++ /dev/null
@@ -1,130 +0,0 @@
-@echo off
-setlocal
-
-if /i "%~1"=="" goto Help
-if /i "%~1"=="-?" goto Help
-if /i "%~1"=="/?" goto Help
-
-set LoggingValue=
-set Exp=0
-set ServerLogging=
-set VSRootDir=
-set MIEngineRelativeDir=Common7\IDE\CommonExtensions\Microsoft\MDD\Debugger
-set MIEngineRelativePath=%MIEngineRelativeDir%\Microsoft.MIDebugEngine.dll
-
-:ArgLoop
-if /i "%~1"=="on" set LoggingValue=1& goto ArgLoopCondition
-if /i "%~1"=="off" set LoggingValue=0& goto ArgLoopCondition
-if /i "%~1"=="/VSRootDir" goto SetVSRoot
-if /i "%~1"=="-VSRootDir" goto SetVSRoot
-if /i "%~1"=="-serverlogging" goto SetServerLogging
-if /i "%~1"=="/serverlogging" goto SetServerLogging
-echo ERROR: Unknown argument '%~1'& exit /b -1
-
-:SetVSRoot
-shift
-if "%~1"=="" echo ERROR: Expected version number.
-set VSRootDir=%~1
-if not exist "%VSRootDir%" echo ERROR: '/VSRootDir' value '%VSRootDir%' does not exist & exit /b -1
-if not exist "%VSRootDir%\%MIEngineRelativePath%" echo ERROR: '/VSRootDir' value '%VSRootDir%' does not contain MIEngine (%VSRootDir%\%MIEngineRelativePath%) & exit /b -1
-goto ArgLoopCondition
-
-:SetServerLogging
-REM Documentation on GDBServer command line arguments: http://www.sourceware.org/gdb/onlinedocs/gdb/Server.html
-set ServerLogging=--debug
-if /i "%~2"=="full" shift & set ServerLogging=--debug --remote-debug
-goto ArgLoopCondition
-
-:ArgLoopCondition
-shift
-if NOT "%~1"=="" goto :ArgLoop
-
-if "%LoggingValue%"=="" echo ERROR: 'on' or 'off' must be specified.& exit /b -1
-if /i NOT "%LoggingValue%"=="1" if NOT "%ServerLogging%"=="" echo ERROR: '/serverlogging' can only be used with 'on'& exit /b -1
-
-set SetLoggingError=
-if NOT "%VSRootDir%"=="" call :SetLogging "%VSRootDir%" & goto Done
-
-REM If '/VSRootDir' is NOT specified, try ALL the default locations
-set ProgRoot=%ProgramFiles(x86)%
-if "%ProgRoot%"=="" set ProgRoot=%ProgramFiles%
-set VSVersionFound=
-set MIEngineFound=
-call :TryVSPath "%ProgRoot%\Microsoft Visual Studio 14.0"
-call :TryVSPaths "%ProgRoot%\Microsoft Visual Studio\2017\*"
-if "%VSVersionFound%"=="" echo ERROR: Visual Studio 2015+ is not installed, or not installed to the default location. Use '/VSRootDir' to specify the directory. & exit /b -1
-if "%MIEngineFound%"=="" echo ERROR: The found version(s) of Visual Studio do not have the MIEngine installed. & exit /b -1
-goto Done
-
-:Done
- echo.
- if NOT "%SetLoggingError%"=="" exit /b -1
- echo SetMIDebugLogging.cmd succeeded. Restart Visual Studio to take effect.
- if "%LoggingValue%"=="1" echo Logging will be saved to %TMP%\Microsoft.MIDebug.log.
- exit /b 0
-
-:TryVSPaths
- for /d %%d in (%1) do call :TryVSPath "%%d"
- goto :EOF
-
-:TryVSPath
- REM Arg1: path to VS Root
-
- if NOT "%SetLoggingError%"=="" goto :EOF
- if not exist "%~1" goto :EOF
- set VSVersionFound=1
- if not exist "%~1\%MIEngineRelativePath%" goto :EOF
- set MIEngineFound=1
- goto SetLogging
-
-:SetLogging
- REM Arg1: path to VS Root
- set PkgDefFile=%~1\%MIEngineRelativeDir%\logging.pkgdef
-
- if NOT exist "%PkgDefFile%" goto SetLogging_NoPkgDef
- del "%PkgDefFile%"
- if NOT exist "%PkgDefFile%" goto SetLogging_NoPkgDef
- echo ERROR: Failed to remove "%PkgDefFile%". Ensure this script is run as an administrator.
- set SetLoggingError=1
- goto :EOF
- :SetLogging_NoPkgDef
-
- if "%LoggingValue%"=="0" goto UpdateConfiguration
-
- :EnableLogging
- echo [$RootKey$\Debugger]> "%PkgDefFile%"
- if exist "%PkgDefFile%" goto EnableLogging_PkgDefCreated
- echo ERROR: Failed to create "%PkgDefFile%". Ensure this script is run as an administrator.
- set SetLoggingError=1
- goto :EOF
- :EnableLogging_PkgDefCreated
-
- echo "EnableMIDebugLogger"=dword:00000001>> "%PkgDefFile%"
- if NOT "%ServerLogging%"=="" echo "GDBServerLoggingArguments"="%ServerLogging%">> "%PkgDefFile%"
-
- :UpdateConfiguration
- echo Setting logging for %1
- call "%~1\Common7\IDE\devenv.com" /updateconfiguration
- if "%ERRORLEVEL%"=="0" goto :EOF
- echo ERROR: '"%~1\Common7\IDE\devenv.com" /updateconfiguration' failed with error %ERRORLEVEL%.
- set SetLoggingError=1
- goto :EOF
-
-:Help
-echo SetMIDebugLogging.cmd ^ [/serverlogging [full]] [/VSRootDir ^]
-echo.
-echo SetMIDebugLogging.cmd is used to enable/disable logging for the Microsoft
-echo MI debug engine.
-echo.
-echo Logging will be saved to %TMP%\Microsoft.MIDebug.log.
-echo.
-echo /serverlogging [full] Enables logging from gdbserver. This option is only
-echo supported when enabling logging ('on'). 'full' logging will
-echo turn on packet logging in addition to normal logging.
-echo.
-echo /VSRootDir ^ sets the path to the root of Visual Studio
-echo (ex: C:\Program Files (x86)\Microsoft Visual Studio 14.0). If not
-echo specified, the default install directories for all versions of VS
-echo 2015+ will be used.
-echo.
-:eof
diff --git a/src/MICore/Transports/PipeTransport.cs b/src/MICore/Transports/PipeTransport.cs
index b71e53b26..e19b6b137 100644
--- a/src/MICore/Transports/PipeTransport.cs
+++ b/src/MICore/Transports/PipeTransport.cs
@@ -11,6 +11,7 @@
using System.Threading.Tasks;
using System.Globalization;
using System.Runtime.InteropServices;
+using Microsoft.DebugEngineHost;
namespace MICore
{
@@ -369,7 +370,7 @@ public override int ExecuteSyncCommand(string commandDescription, string command
Process proc = new Process();
proc.StartInfo.FileName = _pipePath;
proc.StartInfo.Arguments = PipeLaunchOptions.ReplaceDebuggerCommandToken(_cmdArgs, commandText, true);
- Logger.WriteLine("Running process {0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments);
+ Logger.WriteLine(LogLevel.Verbose, "Running process {0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments);
proc.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(_pipePath);
proc.EnableRaisingEvents = false;
proc.StartInfo.RedirectStandardInput = false;
diff --git a/src/MICore/Transports/RunInTerminalTransport.cs b/src/MICore/Transports/RunInTerminalTransport.cs
index f4fce7818..4418f105d 100644
--- a/src/MICore/Transports/RunInTerminalTransport.cs
+++ b/src/MICore/Transports/RunInTerminalTransport.cs
@@ -132,7 +132,7 @@ public override async void Init(ITransportCallback transportCallback, LaunchOpti
debuggerCmd,
localOptions.GetMiDebuggerArgs());
- logger?.WriteTextBlock("DbgCmd:", launchDebuggerCommand);
+ logger?.WriteTextBlock(LogLevel.Verbose, "DbgCmd:", launchDebuggerCommand);
using (FileStream dbgCmdStream = new FileStream(dbgCmdScript, FileMode.CreateNew))
using (StreamWriter dbgCmdWriter = new StreamWriter(dbgCmdStream, encNoBom) { AutoFlush = true })
@@ -175,7 +175,7 @@ public override async void Init(ITransportCallback transportCallback, LaunchOpti
throw new InvalidOperationException(error);
},
logger);
- logger?.WriteLine("Wait for connection completion.");
+ logger?.WriteLine(LogLevel.Verbose, "Wait for connection completion.");
if (_waitForConnection != null)
{
@@ -216,7 +216,7 @@ private void LogDebuggerErrors()
string line = this.GetLineFromStream(_errorStream, _streamReadPidCancellationTokenSource.Token);
if (line == null)
break;
- Logger?.WriteTextBlock("dbgerr:", line);
+ Logger?.WriteTextBlock(LogLevel.Error, "dbgerr:", line);
}
}
}
@@ -244,7 +244,7 @@ private void LaunchSuccess(int? pid)
{
shellPid = int.Parse(readShellPidTask.Result, CultureInfo.InvariantCulture);
// Used for testing
- Logger?.WriteLine(string.Concat("ShellPid=", shellPid));
+ Logger?.WriteLine(LogLevel.Verbose, string.Concat("ShellPid=", shellPid));
}
else
{
@@ -300,7 +300,7 @@ private void ShellExited(object sender, EventArgs e)
((Process)sender).Exited -= ShellExited;
}
- Logger?.WriteLine("Shell exited, stop debugging");
+ Logger?.WriteLine(LogLevel.Verbose, "Shell exited, stop debugging");
this.Callback.OnDebuggerProcessExit(null);
Close();
diff --git a/src/MICore/Transports/StreamTransport.cs b/src/MICore/Transports/StreamTransport.cs
index 53618e5ab..9de2f3c32 100644
--- a/src/MICore/Transports/StreamTransport.cs
+++ b/src/MICore/Transports/StreamTransport.cs
@@ -71,7 +71,7 @@ private void TransportLoop()
break;
line = line.TrimEnd();
- Logger?.WriteLine("->" + line);
+ Logger?.WriteLine(LogLevel.Verbose, "->" + line);
Logger?.Flush();
try
@@ -144,7 +144,7 @@ protected void Echo(string cmd)
{
if (!String.IsNullOrWhiteSpace(cmd))
{
- Logger?.WriteLine("<-" + cmd);
+ Logger?.WriteLine(LogLevel.Verbose, "<-" + cmd);
Logger?.Flush();
}
diff --git a/src/MICore/Transports/UnixShellPortTransport.cs b/src/MICore/Transports/UnixShellPortTransport.cs
index c1801a13c..95cd72933 100644
--- a/src/MICore/Transports/UnixShellPortTransport.cs
+++ b/src/MICore/Transports/UnixShellPortTransport.cs
@@ -37,7 +37,7 @@ public KillCommandCallback(Logger logger)
public void OnOutputLine(string line)
{
- _logger?.WriteLine("[kill] ->" + line);
+ _logger?.WriteLine(LogLevel.Verbose, "[kill] ->" + line);
}
public void OnExit(string exitCode)
@@ -74,7 +74,7 @@ public void Close()
public void Send(string cmd)
{
- _logger?.WriteLine("<-" + cmd);
+ _logger?.WriteLine(LogLevel.Verbose, "<-" + cmd);
_logger?.Flush();
_asyncCommand.WriteLine(cmd);
}
@@ -104,7 +104,7 @@ void IDebugUnixShellCommandCallback.OnOutputLine(string line)
_callback.OnStdOutLine(line);
}
- _logger?.WriteLine("->" + line);
+ _logger?.WriteLine(LogLevel.Verbose, "->" + line);
_logger?.Flush();
}
diff --git a/src/MICore/UnixUtilities.cs b/src/MICore/UnixUtilities.cs
index 6de8664f9..0496723aa 100644
--- a/src/MICore/UnixUtilities.cs
+++ b/src/MICore/UnixUtilities.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Microsoft.DebugEngineHost;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -116,7 +117,7 @@ internal static string MakeFifo(string identifier = null, Logger logger = null)
if (result != 0)
{
// Failed to create the fifo. Bail.
- logger?.WriteLine("Failed to create fifo");
+ logger?.WriteLine(LogLevel.Error, "Failed to create fifo");
throw new ArgumentException("MakeFifo failed to create fifo at path {0}", path);
}
@@ -203,7 +204,7 @@ internal static void OutputNonEmptyString(string str, string prefix, Logger logg
{
if (!String.IsNullOrWhiteSpace(str) && logger != null)
{
- logger.WriteLine(prefix + str);
+ logger.WriteLine(LogLevel.Verbose, prefix + str);
}
}
diff --git a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs
index 0535572a6..ad050bd4f 100755
--- a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs
+++ b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs
@@ -189,7 +189,7 @@ public int Attach(IDebugProgram2[] portProgramArray, IDebugProgramNode2[] progra
{
Debug.Assert(_ad7ProgramId == Guid.Empty);
- Logger.LoadMIDebugLogger(_configStore);
+ Logger.LoadMIDebugLogger();
if (celtPrograms != 1)
{
@@ -498,7 +498,7 @@ public int SetMetric(string pszMetric, object varValue)
public int SetRegistryRoot(string registryRoot)
{
_configStore = new HostConfigurationStore(registryRoot);
- Logger = Logger.EnsureInitialized(_configStore);
+ Logger = Logger.EnsureInitialized();
return Constants.S_OK;
}
@@ -539,7 +539,7 @@ int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, stri
Debug.Assert(_ad7ProgramId == Guid.Empty);
// Check if the logger was enabled late.
- Logger.LoadMIDebugLogger(_configStore);
+ Logger.LoadMIDebugLogger();
process = null;
diff --git a/src/MIDebugEngine/Engine.Impl/DebugUnixChildProcess.cs b/src/MIDebugEngine/Engine.Impl/DebugUnixChildProcess.cs
index b883c5d98..6c50d58f6 100644
--- a/src/MIDebugEngine/Engine.Impl/DebugUnixChildProcess.cs
+++ b/src/MIDebugEngine/Engine.Impl/DebugUnixChildProcess.cs
@@ -232,7 +232,7 @@ public async Task Stopped(Results results, int tid)
else
{
// sometimes gdb misses the breakpoint at exec and execution will proceed to a breakpoint in the child
- _process.Logger.WriteLine("Missed catching the exec after vfork. Spawning the child's debugger.");
+ _process.Logger.WriteLine(LogLevel.Verbose, "Missed catching the exec after vfork. Spawning the child's debugger.");
s.State = State.AtExec;
goto missedExec;
}
diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
index 5debd856c..24141685c 100755
--- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
+++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
@@ -65,7 +65,7 @@ public DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngin
_deleteEntryPointBreakpoint = false;
MICommandFactory = MICommandFactory.GetInstance(launchOptions.DebuggerMIMode, this);
_waitDialog = (MICommandFactory.SupportsStopOnDynamicLibLoad() && launchOptions.WaitDynamicLibLoad) ? new HostWaitDialog(ResourceStrings.LoadingSymbolMessage, ResourceStrings.LoadingSymbolCaption) : null;
- Natvis = new Natvis.Natvis(this, launchOptions.ShowDisplayString);
+ Natvis = new Natvis.Natvis(this, launchOptions.ShowDisplayString, configStore);
// we do NOT have real Win32 process IDs, so we use a guid
AD_PROCESS_ID pid = new AD_PROCESS_ID();
@@ -1033,6 +1033,8 @@ private void Dispose()
_waitDialog.Dispose();
}
+ Natvis?.Dispose();
+
Logger.Flush();
}
diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedThread.cs b/src/MIDebugEngine/Engine.Impl/DebuggedThread.cs
index 4d9362de8..ff739b61e 100644
--- a/src/MIDebugEngine/Engine.Impl/DebuggedThread.cs
+++ b/src/MIDebugEngine/Engine.Impl/DebuggedThread.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using MICore;
+using Microsoft.DebugEngineHost;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -136,7 +137,7 @@ internal async Task
> StackFrames(DebuggedThread thread)
}
catch (UnexpectedMIResultException)
{
- _debugger.Logger.WriteLine("Stack walk failed on thread: " + thread.TargetId);
+ _debugger.Logger.WriteLine(LogLevel.Error, "Stack walk failed on thread: " + thread.TargetId);
_stateChange = true; // thread may have been deleted. Force a resync
}
lock (_threadList)
@@ -291,7 +292,7 @@ private async Task> WalkStack(DebuggedThread thread)
TupleValue[] frameinfo = await _debugger.MICommandFactory.StackListFrames(thread.Id, 0, 1000);
if (frameinfo == null)
{
- _debugger.Logger.WriteLine("Failed to get frame info");
+ _debugger.Logger.WriteLine(LogLevel.Error, "Failed to get frame info");
}
else
{
diff --git a/src/MIDebugEngine/Engine.Impl/EngineCallback.cs b/src/MIDebugEngine/Engine.Impl/EngineCallback.cs
index d87a541e8..e009a3fa6 100644
--- a/src/MIDebugEngine/Engine.Impl/EngineCallback.cs
+++ b/src/MIDebugEngine/Engine.Impl/EngineCallback.cs
@@ -31,7 +31,7 @@ public void Send(IDebugEvent2 eventObject, string iidEvent, IDebugProgram2 progr
Guid riidEvent = new Guid(iidEvent);
if (!(eventObject is AD7OutputDebugStringEvent))
{
- _engine.Logger.WriteLine("Send Event {0}", eventObject.GetType().Name);
+ _engine.Logger.WriteLine(LogLevel.Verbose, "Send Event {0}", eventObject.GetType().Name);
}
EngineUtils.RequireOk(eventObject.GetAttributes(out attributes));
EngineUtils.RequireOk(_eventCallback.Event(_engine, null, program, thread, eventObject, ref riidEvent, attributes));
diff --git a/src/MIDebugEngine/Natvis.Impl/Natvis.cs b/src/MIDebugEngine/Natvis.Impl/Natvis.cs
index 9927883c9..81b52ee84 100755
--- a/src/MIDebugEngine/Natvis.Impl/Natvis.cs
+++ b/src/MIDebugEngine/Natvis.Impl/Natvis.cs
@@ -157,7 +157,7 @@ public VisualizerId(string name,int id)
}
};
- public class Natvis
+ public class Natvis : IDisposable
{
private class AliasInfo
{
@@ -227,6 +227,7 @@ public VisualizerInfo(VisualizerType viz, TypeName name)
private static Regex s_expression = new Regex(@"^\{[^\}]*\}");
private List _typeVisualizers;
private DebuggedProcess _process;
+ private HostConfigurationStore _configStore;
private Dictionary _vizCache;
private uint _depth;
public HostWaitDialog WaitDialog { get; private set; }
@@ -237,6 +238,8 @@ public VisualizerInfo(VisualizerType viz, TypeName name)
private const int MAX_FORMAT_DEPTH = 10;
private const int MAX_ALIAS_CHAIN = 10;
+ private IDisposable _natvisSettingWatcher;
+
public enum DisplayStringsState
{
On,
@@ -245,7 +248,7 @@ public enum DisplayStringsState
}
public DisplayStringsState ShowDisplayStrings { get; set; }
- internal Natvis(DebuggedProcess process, bool showDisplayString)
+ internal Natvis(DebuggedProcess process, bool showDisplayString, HostConfigurationStore configStore)
{
_typeVisualizers = new List();
_process = process;
@@ -254,12 +257,14 @@ internal Natvis(DebuggedProcess process, bool showDisplayString)
ShowDisplayStrings = showDisplayString ? DisplayStringsState.On : DisplayStringsState.ForVisualizedItems; // don't compute display strings unless explicitly requested
_depth = 0;
Cache = new VisualizationCache();
+ _configStore = configStore;
}
public void Initialize(string fileName)
{
try
{
+ _natvisSettingWatcher = HostNatvisProject.WatchNatvisOptionSetting(_configStore, _process.Logger.NatvisLogger);
HostNatvisProject.FindNatvis((s) => LoadFile(s));
}
catch (FileNotFoundException)
@@ -325,7 +330,7 @@ private bool LoadFile(string path)
XmlSerializer serializer = new XmlSerializer(typeof(AutoVisualizer));
if (!File.Exists(path))
{
- _process.WriteOutput(String.Format(CultureInfo.CurrentCulture, ResourceStrings.FileNotFound, path));
+ _process.Logger.NatvisLogger?.WriteLine(LogLevel.Error, ResourceStrings.FileNotFound, path);
return false;
}
XmlReaderSettings settings = new XmlReaderSettings();
@@ -355,7 +360,7 @@ private bool LoadFile(string path)
if (o is VisualizerType)
{
VisualizerType v = (VisualizerType)o;
- TypeName t = TypeName.Parse(v.Name, _process.Logger);
+ TypeName t = TypeName.Parse(v.Name, _process.Logger.NatvisLogger);
if (t != null)
{
lock (_typeVisualizers)
@@ -368,7 +373,7 @@ private bool LoadFile(string path)
{
foreach (var a in v.AlternativeType)
{
- t = TypeName.Parse(a.Name, _process.Logger);
+ t = TypeName.Parse(a.Name, _process.Logger.NatvisLogger);
if (t != null)
{
lock (_typeVisualizers)
@@ -382,7 +387,7 @@ private bool LoadFile(string path)
else if (o is AliasType)
{
AliasType a = (AliasType)o;
- TypeName t = TypeName.Parse(a.Name, _process.Logger);
+ TypeName t = TypeName.Parse(a.Name, _process.Logger.NatvisLogger);
if (t != null)
{
lock (_typeVisualizers)
@@ -406,7 +411,7 @@ private bool LoadFile(string path)
catch (Exception exception)
{
// don't allow natvis failures to stop debugging
- _process.WriteOutput(String.Format(CultureInfo.CurrentCulture, ResourceStrings.ErrorReadingFile, exception.Message, path));
+ _process.Logger.NatvisLogger?.WriteLine(LogLevel.Error, ResourceStrings.ErrorReadingFile, exception.Message, path);
return false;
}
}
@@ -451,7 +456,7 @@ private bool LoadFile(string path)
{
// don't allow natvis to mess up debugging
// eat any exceptions and return the variable's value
- _process.Logger.WriteLine("natvis FormatDisplayString: " + e.Message);
+ _process.Logger.NatvisLogger?.WriteLine(LogLevel.Error, "FormatDisplayString: " + e.Message);
}
finally
{
@@ -502,7 +507,7 @@ internal IVariableInformation[] Expand(IVariableInformation variable)
}
catch (Exception e)
{
- _process.Logger.WriteLine("natvis Expand: " + e.Message); // TODO: add telemetry
+ _process.Logger.WriteLine(LogLevel.Error, "natvis Expand: " + e.Message); // TODO: add telemetry
return variable.Children;
}
}
@@ -1135,7 +1140,7 @@ private VisualizerInfo Scan(TypeName name, IVariableInformation variable)
}
string newName = ReplaceNamesInExpression(alias.Alias.Value, null, scopedNames);
- name = TypeName.Parse(newName, _process.Logger);
+ name = TypeName.Parse(newName, _process.Logger.NatvisLogger);
aliasChain++;
if (aliasChain > MAX_ALIAS_CHAIN)
{
@@ -1157,7 +1162,7 @@ private VisualizerInfo FindType(IVariableInformation variable)
{
return _vizCache[variable.TypeName];
}
- TypeName parsedName = TypeName.Parse(variable.TypeName, _process.Logger);
+ TypeName parsedName = TypeName.Parse(variable.TypeName, _process.Logger.NatvisLogger);
IVariableInformation var = variable;
while (parsedName != null)
{
@@ -1176,7 +1181,7 @@ private VisualizerInfo FindType(IVariableInformation variable)
{
break;
}
- parsedName = TypeName.Parse(var.TypeName, _process.Logger);
+ parsedName = TypeName.Parse(var.TypeName, _process.Logger.NatvisLogger);
}
return null;
}
@@ -1365,5 +1370,12 @@ private string GetDisplayNameFromArrayIndex(uint arrayIndex, int rank, uint[] di
return displayName.ToString();
}
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+
+ _natvisSettingWatcher?.Dispose();
+ _natvisSettingWatcher = null;
+ }
}
}
diff --git a/src/MIDebugEngine/Natvis.Impl/NatvisNames.cs b/src/MIDebugEngine/Natvis.Impl/NatvisNames.cs
index 7c145a6d8..85972bdd8 100644
--- a/src/MIDebugEngine/Natvis.Impl/NatvisNames.cs
+++ b/src/MIDebugEngine/Natvis.Impl/NatvisNames.cs
@@ -9,6 +9,7 @@
using System.Text.RegularExpressions;
using MICore;
using System.Globalization;
+using Microsoft.DebugEngineHost;
namespace Microsoft.MIDebugEngine.Natvis
{
@@ -138,7 +139,7 @@ public bool Match(TypeName t)
///
///
///
- public static TypeName Parse(string fullyQualifiedName, ILogger logger)
+ public static TypeName Parse(string fullyQualifiedName, ILogChannel logger)
{
if (String.IsNullOrEmpty(fullyQualifiedName))
return null;
@@ -146,7 +147,7 @@ public static TypeName Parse(string fullyQualifiedName, ILogger logger)
TypeName t = MatchTypeName(fullyQualifiedName.Trim(), out rest);
if (!String.IsNullOrWhiteSpace(rest))
{
- logger.WriteLine("Natvis failed to parse typename: {0}", fullyQualifiedName);
+ logger.WriteLine(LogLevel.Error, "Natvis failed to parse typename: {0}", fullyQualifiedName);
return null;
}
return t;
diff --git a/src/MIDebugEngineUnitTests/NatvisNamesTest.cs b/src/MIDebugEngineUnitTests/NatvisNamesTest.cs
index a59dfc1bd..3de8665d6 100644
--- a/src/MIDebugEngineUnitTests/NatvisNamesTest.cs
+++ b/src/MIDebugEngineUnitTests/NatvisNamesTest.cs
@@ -4,10 +4,11 @@
using MICore;
using Xunit.Abstractions;
using System.Globalization;
+using Microsoft.DebugEngineHost;
namespace MIDebugEngineUnitTests
{
- class TestLogger : ILogger
+ class TestLogger : ILogChannel
{
private static TestLogger s_instance;
@@ -26,22 +27,29 @@ public static TestLogger Instance
private ITestOutputHelper _output;
- private TestLogger() {}
-
internal void RegisterTestOutputHelper(ITestOutputHelper output)
{
_output = output;
}
- public void WriteLine(string line)
+ public void WriteLine(LogLevel level, string line)
{
_output?.WriteLine(line);
}
- public void WriteLine(string format, params object[] args)
+ public void WriteLine(LogLevel level, string format, params object[] args)
{
_output?.WriteLine(string.Format(CultureInfo.InvariantCulture, format, args));
}
+
+ // Unused as ITestOutputHelper does not have a Flush implementation
+ public void Flush() { }
+
+ // Unused as ITestOutputHelper does not have a Close implementation
+ public void Close() { }
+
+ // Unused as ITestOutputHelper does not have LogLevels
+ public void SetLogLevel(LogLevel level) { }
}
public class NatvisNamesTest
diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs
index 425061684..dcea56bc0 100644
--- a/src/OpenDebugAD7/AD7DebugSession.cs
+++ b/src/OpenDebugAD7/AD7DebugSession.cs
@@ -215,17 +215,38 @@ private void SetCommonDebugSettings(Dictionary args)
if (logging != null)
{
+ HostLogger.Reset();
+
m_logger.SetLoggingConfiguration(LoggingCategory.Exception, logging.GetValueAsBool("exceptions").GetValueOrDefault(true));
m_logger.SetLoggingConfiguration(LoggingCategory.Module, logging.GetValueAsBool("moduleLoad").GetValueOrDefault(true));
m_logger.SetLoggingConfiguration(LoggingCategory.StdOut, logging.GetValueAsBool("programOutput").GetValueOrDefault(true));
m_logger.SetLoggingConfiguration(LoggingCategory.StdErr, logging.GetValueAsBool("programOutput").GetValueOrDefault(true));
- bool? engineLogging = logging.GetValueAsBool("engineLogging");
- if (engineLogging.HasValue)
+ JToken engineLogging = logging.GetValue("engineLogging", StringComparison.OrdinalIgnoreCase);
+ if (engineLogging != null)
{
- m_logger.SetLoggingConfiguration(LoggingCategory.EngineLogging, engineLogging.Value);
- HostLogger.EnableHostLogging();
- HostLogger.Instance.LogCallback = s => m_logger.WriteLine(LoggingCategory.EngineLogging, s);
+ if (engineLogging.Type == JTokenType.Boolean)
+ {
+ bool engineLoggingBool = engineLogging.Value();
+ if (engineLoggingBool)
+ {
+ m_logger.SetLoggingConfiguration(LoggingCategory.EngineLogging, true);
+ HostLogger.EnableHostLogging((message) => m_logger.WriteLine(LoggingCategory.EngineLogging, message), LogLevel.Verbose);
+ }
+ }
+ else if (engineLogging.Type == JTokenType.String)
+ {
+ string engineLoggingString = engineLogging.Value();
+ if (Enum.TryParse(engineLoggingString, ignoreCase: true, out LogLevel level))
+ {
+ m_logger.SetLoggingConfiguration(LoggingCategory.EngineLogging, true);
+ HostLogger.EnableHostLogging((message) => m_logger.WriteLine(LoggingCategory.EngineLogging, message), level);
+ }
+ }
+ else
+ {
+ m_logger.WriteLine(LoggingCategory.EngineLogging, string.Format(CultureInfo.CurrentCulture, AD7Resources.Warning_EngineLoggingParse, engineLogging.ToString()));
+ }
}
bool? trace = logging.GetValueAsBool("trace");
@@ -239,6 +260,33 @@ private void SetCommonDebugSettings(Dictionary args)
{
m_logger.SetLoggingConfiguration(LoggingCategory.AdapterResponse, traceResponse.Value);
}
+
+ JToken natvisDiagnostics = logging.GetValue("natvisDiagnostics", StringComparison.OrdinalIgnoreCase);
+ if (natvisDiagnostics != null)
+ {
+ if (natvisDiagnostics.Type == JTokenType.Boolean)
+ {
+ bool natvisDiagnosticsBool = natvisDiagnostics.Value();
+ if (natvisDiagnosticsBool)
+ {
+ m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true);
+ HostLogger.EnableNatvisDiagnostics((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, message), LogLevel.Verbose);
+ }
+ }
+ else if (natvisDiagnostics.Type == JTokenType.String)
+ {
+ string natvisDiagnosticsString = natvisDiagnostics.Value();
+ if (Enum.TryParse(natvisDiagnosticsString, ignoreCase: true, out LogLevel level))
+ {
+ m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true);
+ HostLogger.EnableNatvisDiagnostics((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, string.Concat("[Natvis] ", message)), level);
+ }
+ }
+ else
+ {
+ m_logger.WriteLine(LoggingCategory.EngineLogging, string.Format(CultureInfo.CurrentCulture, AD7Resources.Warning_NatvisLoggingParse, natvisDiagnostics.ToString()));
+ }
+ }
}
}
diff --git a/src/OpenDebugAD7/AD7Resources.Designer.cs b/src/OpenDebugAD7/AD7Resources.Designer.cs
index eea617771..abb7d62bf 100644
--- a/src/OpenDebugAD7/AD7Resources.Designer.cs
+++ b/src/OpenDebugAD7/AD7Resources.Designer.cs
@@ -632,6 +632,15 @@ internal static string Registers_Scope_Name {
}
}
+ ///
+ /// Looks up a localized string similar to Failed to parse engine logging setting: '{0}'.
+ ///
+ internal static string Warning_EngineLoggingParse {
+ get {
+ return ResourceManager.GetString("Warning_EngineLoggingParse", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Could not detect launch url..
///
@@ -650,6 +659,15 @@ internal static string Warning_LaunchBrowserFailed {
}
}
+ ///
+ /// Looks up a localized string similar to Failed to parse Nativs logging setting: '{0}'.
+ ///
+ internal static string Warning_NatvisLoggingParse {
+ get {
+ return ResourceManager.GetString("Warning_NatvisLoggingParse", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to WARNING: Unable to terminate '{0}' process. {1}
///.
diff --git a/src/OpenDebugAD7/AD7Resources.resx b/src/OpenDebugAD7/AD7Resources.resx
index 60be8f97f..e50d73343 100644
--- a/src/OpenDebugAD7/AD7Resources.resx
+++ b/src/OpenDebugAD7/AD7Resources.resx
@@ -338,4 +338,12 @@
Cannot evaluate expression on the specified stack frame.
+
+ Failed to parse engine logging setting: '{0}'
+ {0} is the string passed in by the user
+
+
+ Failed to parse Nativs logging setting: '{0}'
+ {0} is the string passed in by the user
+
\ No newline at end of file
diff --git a/src/OpenDebugAD7/DebugEventLogger.cs b/src/OpenDebugAD7/DebugEventLogger.cs
index 45ef9c66d..612039b97 100644
--- a/src/OpenDebugAD7/DebugEventLogger.cs
+++ b/src/OpenDebugAD7/DebugEventLogger.cs
@@ -35,6 +35,8 @@ internal enum LoggingCategory
Module,
/// Process exit message.
ProcessExit,
+ /// Natvis Diagnostic Messages
+ NatvisDiagnostics
}
/// Logging class to handle when and how various classes of output should be logged.
diff --git a/src/OpenDebugAD7/OpenDebug/Program.cs b/src/OpenDebugAD7/OpenDebug/Program.cs
index 875a91fd4..21eb7c36a 100644
--- a/src/OpenDebugAD7/OpenDebug/Program.cs
+++ b/src/OpenDebugAD7/OpenDebug/Program.cs
@@ -27,6 +27,9 @@ private static int Main(string[] argv)
{
int port = -1;
List loggingCategories = new List();
+ bool enableEngineLogger = false;
+ LogLevel level = LogLevel.Verbose;
+ string logFilePath = string.Empty;
// parse command line arguments
foreach (var a in argv)
@@ -49,6 +52,10 @@ private static int Main(string[] argv)
Console.WriteLine("--trace=response: print requests and response from VS Code to the console.");
Console.WriteLine("--engineLogging[=filePath]: Enable logging from the debug engine. If not");
Console.WriteLine(" specified, the log will go to the console.");
+ Console.WriteLine("--engineLogLevel=: Set's the log level for engine logging.");
+ Console.WriteLine(" If not specified, default log level is LogLevel.Verbose");
+ Console.WriteLine("--natvisDiagnostics[=logLevel]: Enable logging for natvis. If not");
+ Console.WriteLine(" specified, the log will go to the console. Default logging level is LogLevel.Verbose.");
Console.WriteLine("--server[=port_num] : Start the debug adapter listening for requests on the");
Console.WriteLine(" specified TCP/IP port instead of stdin/out. If port is not specified");
Console.WriteLine(" TCP {0} will be used.", DEFAULT_PORT);
@@ -64,8 +71,14 @@ private static int Main(string[] argv)
loggingCategories.Add(LoggingCategory.AdapterResponse);
break;
case "--engineLogging":
- loggingCategories.Add(LoggingCategory.EngineLogging);
- HostLogger.EnableHostLogging();
+ enableEngineLogger = true;
+ break;
+ case "--natvisDiagnostics":
+ loggingCategories.Add(LoggingCategory.NatvisDiagnostics);
+ HostLogger.EnableNatvisDiagnostics((msg) =>
+ {
+ Console.Error.WriteLine(msg);
+ }, LogLevel.Verbose);
break;
case "--server":
port = DEFAULT_PORT;
@@ -87,17 +100,28 @@ private static int Main(string[] argv)
return -1;
}
}
- else if (a.StartsWith("--engineLogging=", StringComparison.Ordinal))
+ else if (a.StartsWith("--natvisDiagnostics=", StringComparison.Ordinal))
{
- HostLogger.EnableHostLogging();
- try
+ if (!Enum.TryParse(a.Substring("--natvisDiagnostics=".Length), out LogLevel natvisLogLevel))
{
- HostLogger.Instance.LogFilePath = a.Substring("--engineLogging=".Length);
+ loggingCategories.Add(LoggingCategory.NatvisDiagnostics);
+ HostLogger.EnableNatvisDiagnostics((msg) =>
+ {
+ Console.Error.WriteLine(msg);
+ }, natvisLogLevel);
}
- catch (Exception e)
+ }
+ else if (a.StartsWith("--engineLogging=", StringComparison.Ordinal))
+ {
+ enableEngineLogger = true;
+ logFilePath = a.Substring("--engineLogging=".Length);
+ HostLogger.SetEngineLogFile(logFilePath);
+ }
+ else if (a.StartsWith("--engineLogLevel=", StringComparison.Ordinal))
+ {
+ if (!Enum.TryParse(a.Substring("--engineLogLevel=".Length), out level))
{
- Console.Error.WriteLine("OpenDebugAD7: ERROR: Unable to open log file. " + e.Message);
- return -1;
+ level = LogLevel.Verbose;
}
}
else if (a.StartsWith("--adapterDirectory=", StringComparison.Ordinal))
@@ -119,6 +143,23 @@ private static int Main(string[] argv)
}
}
+ if (enableEngineLogger)
+ {
+ loggingCategories.Add(LoggingCategory.EngineLogging);
+ try
+ {
+ HostLogger.EnableHostLogging((msg) =>
+ {
+ Console.Error.WriteLine(msg);
+ }, level);
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine("OpenDebugAD7: ERROR: Unable to open log file. " + e.Message);
+ return -1;
+ }
+ }
+
if (port > 0)
{
// TCP/IP server
diff --git a/src/OpenDebugAD7/OpenDebug/Utilities.cs b/src/OpenDebugAD7/OpenDebug/Utilities.cs
index 39f44b116..d2d0d39a6 100644
--- a/src/OpenDebugAD7/OpenDebug/Utilities.cs
+++ b/src/OpenDebugAD7/OpenDebug/Utilities.cs
@@ -336,7 +336,7 @@ public static void ReportException(Exception e)
try
{
HostTelemetry.ReportCurrentException(e, null);
- Logger.WriteLine("EXCEPTION: " + e.ToString());
+ HostLogger.GetEngineLogChannel()?.WriteLine(LogLevel.Error, "EXCEPTION: " + e.ToString());
}
catch
{
diff --git a/test/CppTests/Tests/NatvisTests.cs b/test/CppTests/Tests/NatvisTests.cs
index 37bc46b8f..69b2526c4 100644
--- a/test/CppTests/Tests/NatvisTests.cs
+++ b/test/CppTests/Tests/NatvisTests.cs
@@ -36,6 +36,9 @@ public NatvisTests(ITestOutputHelper outputHelper) : base(outputHelper)
private const string NatvisName = "natvis";
private const string NatvisSourceName = "main.cpp";
+
+ // These line numbers will need to change if src/natvis/main.cpp changes
+ private const int SimpleClassAssignmentLine = 47;
private const int ReturnSourceLine = 51;
[Theory]
@@ -369,7 +372,7 @@ public void TestThisConditional(ITestSettings settings)
runner.RunCommand(launch);
this.Comment("Set Breakpoint before assigning 'simpleClass' and end of method.");
- SourceBreakpoints writerBreakpoints = debuggee.Breakpoints(NatvisSourceName, new int[] { 46, ReturnSourceLine });
+ SourceBreakpoints writerBreakpoints = debuggee.Breakpoints(NatvisSourceName, new int[] { SimpleClassAssignmentLine, ReturnSourceLine });
runner.SetBreakpoints(writerBreakpoints);
runner.Expects.StoppedEvent(StoppedReason.Breakpoint).AfterConfigurationDone();