From f2ef967aaf74c4ce199fb5ff156c818d00f94a53 Mon Sep 17 00:00:00 2001 From: Andrew Wang <waan@microsoft.com> Date: Tue, 13 Sep 2022 17:43:19 -0700 Subject: [PATCH 1/4] Fix TestThisConditional (#1354) --- test/CppTests/Tests/NatvisTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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(); From a0849cce53d3afff1054b67c90633cf09708b83a Mon Sep 17 00:00:00 2001 From: Alex Hsu <csigs@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:45:21 -0700 Subject: [PATCH 2/4] LEGO: Pull request from lego/hb_d72c5677-3f00-4225-b18e-0a1e8a8f5f0e_20220916220203345 to main (#1356) Juno: check in to lego/hb_d72c5677-3f00-4225-b18e-0a1e8a8f5f0e_20220916220203345. --- loc/lcl/CHS/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/CHT/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/CSY/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/DEU/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/ESN/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/FRA/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/ITA/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/JPN/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/KOR/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/PLK/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/PTB/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/RUS/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ loc/lcl/TRK/OpenFolderSchema.json.lcl | 36 +++++++++++++++++++++++++++ 13 files changed, 468 insertions(+) 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[控制在命中时如何处理(通常通过原始 GDB 命令)外部设置的断点。]A;允许的值为 "throw" (好像应用程序抛出了异常)和 "stop" (只会暂停调试会话)。默认值为 "throw"。]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stop]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[字符串]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[控制叫用時如何處理在外部設定的中斷點 (通常是透過原始 GDB 命令)。]A;允許的值為 "throw",其作用就像應用程式擲出例外,以及 "stop",其只會暫停偵錯工作階段。預設值為 "throw"。]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stop]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[字串]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Určuje, jak se mají zarážky nastavené externě (obvykle prostřednictvím nezpracovaných příkazů GDB) zpracovávat při průchodu.]A;Povolené hodnoty jsou throw, která se chová, jako by aplikace vyvolala výjimku, a stop, která pouze pozastaví ladicí relaci. Výchozí hodnota je throw.]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stop]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[řetězec]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Steuert, wie extern gesetzte Haltepunkte (normalerweise über rohe GDB-Befehle) behandelt werden, wenn ihnen begegnet wird.]A;Erlaubte Werte sind "throw", was sich so verhält, als ob eine Ausnahme von der Anwendung ausgelöst würde, und "stop", was die Debugsitzung nur pausiert. Der Standardwert ist "throw".]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[werfen]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[beenden]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Zeichenfolge]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Controla cómo se controlan los puntos de interrupción establecidos externamente (normalmente a través de comandos GDB sin procesar) cuando se alcanzan.]A;Los valores permitidos son "throw", que actúa como si la aplicación iniciara una excepción y "stop", que solo pausa la sesión de depuración. El valor predeterminado es "throw".]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stop]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[cadena]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Contrôle la façon dont les points d’arrêt définis en externe (généralement via des commandes GDB brutes) sont gérés en cas d’accès.]A;Les valeurs autorisées sont « throw », qui agit comme si une exception était levée par l’application, et « stop », qui suspend uniquement la session de débogage. La valeur par défaut est « throw ».]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stop]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[chaîne]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Controllare la modalità di gestione dei punti di interruzione impostati esternamente (in genere tramite comandi GDB non elaborati) quando vengono selezionati.]A;I valori consentiti sono "throw", che funziona come se fosse stata generata un'eccezione dall'applicazione e "stop", che sospende solo la sessione di debug. Il valore predefinito è "throw".]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[gettare]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[arresto]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[stringa]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[ヒットしたときに外部で設定されたブレークポイント (通常は生の GDB コマンドを使用) を処理する方法を制御します。]A;許容される値は、アプリケーションによって例外がスローされたかのように動作する "throw" と、デバッグ セッションを一時停止するだけの "stop" です。既定値は "throw" です。]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[停止]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[文字列]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[적중 시 외부에서 설정되는 중단점이(일반적으로 원시 GDB 명령을 통해) 처리되는 방식을 제어합니다.]A;허용 값은 애플리케이션에서 예외가 발생한 것처럼 동작하는 "throw"와 디버그 세션만 일시 중지하는 "stop"입니다. 기본값은 "throw"입니다.]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[중지]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[문자열]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Steruje sposobem obsługi punktów przerwania ustawianych zewnętrznie (zwykle za pośrednictwem nieprzetworzonych poleceń GDB) po trafieniu.]A;Dozwolone wartości to „throw”, które działają tak, jakby aplikacja zgłosiła wyjątek, i „stop”, co tylko wstrzymuje sesję debugowania. Wartość domyślna to „throw”.]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[rzucać]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[zatrzymaj]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[ciąg]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Controla como os pontos de interrupção definidos externamente (geralmente por meio de comandos GDB brutos) são tratados quando atingidos.]A;Os valores permitidos são "throw", que age como se uma exceção fosse lançada pelo aplicativo, e "stop", que apenas pausa a sessão de depuração. O valor padrão é "throw".]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[interromper]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[cadeia de caracteres]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[Управляет тем, как точки останова, установленные извне (обычно через необработанные команды GDB), обрабатываются при попадании.]A;Допустимые значения: "throw", который действует так, как если бы приложение выдало исключение, и "stop", который только приостанавливает сеанс отладки. Значение по умолчанию — "throw".]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[отбросить]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[остановить]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[строка]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> 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 @@ </Str> <Disp Icon="Str" /> </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.description" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[Controls how breakpoints set externally (usually via raw GDB commands) are handled when hit.]A;Allowed values are "throw", which acts as if an exception was thrown by the application, and "stop", which only pauses the debug session. The default value is "throw".]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[İsabet ettiğinde harici olarak (genellikle ham GDB komutları aracılığıyla) ayarlanan kesme noktalarının nasıl işlendiğini kontrol eder.]A;İzin verilen değerler, uygulama tarafından bir istisna oluşturulmuş gibi davranan "throw" ve yalnızca hata ayıklama oturumunu duraklatan "stop" değerleridir. Varsayılan değer "throw"dur.]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[0]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[throw]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[throw]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.enum[1]" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[stop]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[durdurma]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> + <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.unknownBreakpointHandling.type" ItemType="0" PsrId="306" Leaf="true"> + <Str Cat="Text"> + <Val><![CDATA[string]]></Val> + <Tgt Cat="Text" Stat="Loc" Orig="New"> + <Val><![CDATA[dize]]></Val> + </Tgt> + </Str> + <Disp Icon="Str" /> + </Item> <Item ItemId=";debugExtensions.cppdbg.schema.definitions.cpp_schema.properties.useExtendedRemote.description" ItemType="0" PsrId="306" Leaf="true"> <Str Cat="Text"> <Val><![CDATA[If true, use gdb extended-remote mode to connect to gdbserver.]]></Val> From fd190c03bab7f332e522aa2bfb2f8b755206c787 Mon Sep 17 00:00:00 2001 From: Andrew Wang <waan@microsoft.com> Date: Mon, 3 Oct 2022 17:26:25 -0700 Subject: [PATCH 3/4] Refactor Engine Logging and Add Natvis Diagnostic Logging (#1322) Refactor Engine Logging to be implemented through channels and add a new channel called NatvisDiagnostics --- src/AndroidDebugLauncher/InstallPaths.cs | 2 +- src/AndroidDebugLauncher/Launcher.cs | 14 +- src/DebugEngineHost.Common/HostLogChannel.cs | 141 ++++++++++++++++++ .../DebugEngineHost.ref.cs | 117 ++++++++++----- .../DebugEngineHost.VSCode.csproj | 2 + .../HostConfigurationStore.cs | 14 -- src/DebugEngineHost.VSCode/HostLogger.cs | 78 +++------- src/DebugEngineHost.VSCode/Logger.cs | 33 ---- src/DebugEngineHost/DebugEngineHost.csproj | 2 + src/DebugEngineHost/HostConfigurationStore.cs | 32 ---- src/DebugEngineHost/HostLogger.cs | 87 ++--------- src/MICore/CommandFactories/gdb.cs | 3 +- src/MICore/Debugger.cs | 10 +- src/MICore/ExceptionHelper.cs | 4 +- src/MICore/LaunchOptions.cs | 4 +- src/MICore/Logger.cs | 102 ++++++------- src/MICore/MICore.csproj | 1 - src/MICore/MIResults.cs | 3 +- src/MICore/SetMIDebugLogging.cmd | 130 ---------------- src/MICore/Transports/PipeTransport.cs | 3 +- .../Transports/RunInTerminalTransport.cs | 10 +- src/MICore/Transports/StreamTransport.cs | 4 +- .../Transports/UnixShellPortTransport.cs | 6 +- src/MICore/UnixUtilities.cs | 5 +- src/MIDebugEngine/AD7.Impl/AD7Engine.cs | 6 +- .../Engine.Impl/DebugUnixChildProcess.cs | 2 +- .../Engine.Impl/DebuggedThread.cs | 5 +- .../Engine.Impl/EngineCallback.cs | 2 +- src/MIDebugEngine/Natvis.Impl/Natvis.cs | 20 +-- src/MIDebugEngine/Natvis.Impl/NatvisNames.cs | 5 +- src/MIDebugEngineUnitTests/NatvisNamesTest.cs | 15 +- src/OpenDebugAD7/AD7DebugSession.cs | 58 ++++++- src/OpenDebugAD7/AD7Resources.Designer.cs | 18 +++ src/OpenDebugAD7/AD7Resources.resx | 8 + src/OpenDebugAD7/DebugEventLogger.cs | 2 + src/OpenDebugAD7/OpenDebug/Program.cs | 59 ++++++-- src/OpenDebugAD7/OpenDebug/Utilities.cs | 2 +- 37 files changed, 511 insertions(+), 498 deletions(-) create mode 100644 src/DebugEngineHost.Common/HostLogChannel.cs delete mode 100644 src/DebugEngineHost.VSCode/Logger.cs delete mode 100644 src/MICore/SetMIDebugLogging.cmd 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..1c3a2bab7 --- /dev/null +++ b/src/DebugEngineHost.Common/HostLogChannel.cs @@ -0,0 +1,141 @@ +// 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 + { + /// <summary> + /// Logs that are used for interactive investigation during development. + /// These logs should primarily contain information useful for debugging and have no long-term value. + /// </summary> + Verbose, + /// <summary> + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop. + /// </summary> + Warning, + /// <summary> + /// 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. + /// </summary> + Error, + /// <summary> + /// Not used for writing log messages. + /// Specifies that a logging category should not write any messages. + /// </summary> + None + } + + // This must match the interface in DebugEngineHost.ref.cs + public interface ILogChannel + { + 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<string> _log; + private StreamWriter _logFile; + private LogLevel _minLevelToBeLogged; + + private readonly object _lock = new object(); + + private HostLogChannel() { } + + public HostLogChannel(Action<string> logAction, string file, LogLevel logLevel) + { + _log = logAction; + + if (!string.IsNullOrEmpty(file)) + { + _logFile = File.CreateText(file); + } + + _minLevelToBeLogged = logLevel; + } + + /// <summary> + /// Sets the log level to the provided level. + /// </summary> + /// <param name="level">The level to set the logger.</param> + public void SetLogLevel(LogLevel level) + { + _minLevelToBeLogged = level; + } + + /// <summary> + /// + /// </summary> + /// <param name="verbosity"></param> + /// <param name="message"></param> + 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(); + } + + } + } + + /// <summary> + /// + /// </summary> + /// <param name="verbosity"></param> + /// <param name="format"></param> + /// <param name="values"></param> + 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..b4ebb6124 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(); } - /// <summary> - /// 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. - /// </summary> - /// <param name="enableLoggingSettingName">[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.</param> - /// <param name="logFileName">[Required] name of the log file to open if logging is enabled.</param> - /// <returns>[Optional] If logging is enabled, the logging object.</returns> - public HostLogger GetLogger(string enableLoggingSettingName, string logFileName) - { - throw new NotImplementedException(); - } - /// <summary> /// Read the debugger setting /// @@ -197,56 +182,116 @@ public object GetCustomLauncher(string launcherTypeName) { throw new NotImplementedException(); } -} + } -/// <summary> -/// The host logger returned from HostConfigurationStore.GetLogger. -/// </summary> -public sealed class HostLogger + /// <summary> + /// Level of logging used for HostLogChannel + /// </summary> + public enum LogLevel { /// <summary> - /// Callback for programmatic display of log messages + /// Logs that are used for interactive investigation during development. + /// These logs should primarily contain information useful for debugging and have no long-term value. /// </summary> - /// <param name="outputMessage"></param> - public delegate void OutputCallback(string outputMessage); + Verbose, + /// <summary> + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop. + /// </summary> + Warning, + /// <summary> + /// 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. + /// </summary> + Error, + /// <summary> + /// Not used for writing log messages. + /// Specifies that a logging category should not write any messages. + /// </summary> + None + } - private HostLogger() + /// <summary> + /// The channel used for logging messages. + /// Channels are used if there are multiple types of logs, + /// e.g. Engine logs and Natvis logs + /// </summary> + public interface ILogChannel + { + /// <summary> + /// Writes the given message with a newline to the log channel. + /// </summary> + /// <param name="level">The level of the log</param> + /// <param name="message">The message string to send.</param> + void WriteLine(LogLevel level, string message); + + /// <summary> + /// Writes the given formatted message with the additional values with a newline to the log channel. + /// </summary> + /// <param name="level">The level of the log</param> + /// <param name="format">Format to use.</param> + /// <param name="values">Values to use within the provided format.</param> + void WriteLine(LogLevel level, string format, params object[] values); + + /// <summary> + /// If the log is implemented as a file, this flushes the file. + /// </summary> + void Flush(); + + /// <summary> + /// If the log is implemented as a file, this closes the file. + /// </summary> + void Close(); + } + + /// <summary> + /// + /// </summary> + public static class HostLogger + { + // EnableNatvisLogger is only used in OpenDebugAD7 + + /// <summary> + /// Enables engine logging if not already enabled. + /// </summary> + /// <param name="callback">The callback to use to send the engine log.</param> + /// <param name="level">The level of the log to filter the channel on.</param> + public static void EnableHostLogging(Action<string> callback, LogLevel level = LogLevel.Verbose) { throw new NotImplementedException(); } /// <summary> - /// Writes a line to the log + /// Sets the log file to write to. /// </summary> - /// <param name="line">Line to write.</param> - public void WriteLine(string line) + /// <param name="logFile">The file to write engine logs to.</param> + public static void SetEngineLogFile(string logFile) { throw new NotImplementedException(); } + /// <summary> - /// If the log is implemented as a file, this flushes the file. + /// Gets the engine log channel created by 'EnableHostLogging' /// </summary> - public void Flush() + /// <returns>A logger object if logging is enabled, or null if it is not</returns> + public static ILogChannel GetEngineLogChannel() { throw new NotImplementedException(); } /// <summary> - /// If the log is implemented as a file, this closes the file. + /// Gets the Natvis log channel if its been created. /// </summary> - public void Close() + /// <returns>A logger object if logging is enabled, or null if it is not</returns> + public static ILogChannel GetNatvisLogChannel() { throw new NotImplementedException(); } /// <summary> - /// Get a logger after the user has explicitly configured a log file/callback + /// Clears the logging objects if enabled. /// </summary> - /// <param name="logFileName"></param> - /// <param name="callback"></param> - /// <returns>The host logger object</returns> - public static HostLogger GetLoggerFromCmd(string logFileName, HostLogger.OutputCallback callback) + public static void Reset() { throw new NotImplementedException(); } 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 @@ <ItemGroup Label="Compile Shared Interfaces"> <Compile Include="$(MIEngineRoot)\src\DebugEngineHost.Stub\Shared\Microsoft.VisualStudio.Debugger.Interop.DAP.cs" /> <Compile Include="..\DebugEngineHost.Stub\Shared\Microsoft.VisualStudio.Debugger.Interop.MI.cs" Link="Microsoft.VisualStudio.Debugger.Interop.MI.cs" /> + + <Compile Include="$(MIEngineRoot)\src\DebugEngineHost.Common\HostLogChannel.cs" /> </ItemGroup> <ItemGroup> 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); } - /// <summary> - /// 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. - /// </summary> - /// <param name="enableLoggingSettingName">[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.</param> - /// <param name="logFileName">[Required] name of the log file to open if logging is enabled. This is ignored for VSCode.</param> - /// <returns>[Optional] If logging is enabled, the logging object.</returns> - public HostLogger GetLogger(string enableLoggingSettingName, string logFileName) - { - return HostLogger.Instance; - } - /// <summary> /// Read the debugger setting /// </summary> diff --git a/src/DebugEngineHost.VSCode/HostLogger.cs b/src/DebugEngineHost.VSCode/HostLogger.cs index 71658194d..5eda359e8 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; - /// <summary>[Optional] VSCode-only host logger instance.</summary> - public static HostLogger Instance { get { return s_instance; } } - - /// <summary>[Optional] VSCode-only method for obtaining the current host logger instance.</summary> - public static void EnableHostLogging() + public static void EnableNatvisLogger(Action<string> 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; - - /// <summary>Callback for logging text to the desired output stream.</summary> - public Action<string> LogCallback { get; set; } = null; - - /// <summary>The path to the log file.</summary> - public string LogFilePath + public static void EnableHostLogging(Action<string> 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; } - /// <summary> - /// Get a logger after the user has explicitly configured a log file/callback - /// </summary> - /// <param name="logFileName"></param> - /// <param name="callback"></param> - /// <returns>The host logger object</returns> - 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/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 -{ - /// <summary> - /// VS Code only class to write to the log. This is enabled through the '--engineLogging[=file]' command line argument. - /// </summary> - 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 @@ <ItemGroup Label="Compile Shared Interfaces"> <Compile Include="$(MIEngineRoot)\src\DebugEngineHost.Stub\Shared\Microsoft.VisualStudio.Debugger.Interop.DAP.cs" /> <Compile Include="$(MIEngineRoot)\src\DebugEngineHost.Stub\Shared\Microsoft.VisualStudio.Debugger.Interop.MI.cs" /> + + <Compile Include="$(MIEngineRoot)\src\DebugEngineHost.Common\HostLogChannel.cs" /> </ItemGroup> <ItemGroup> diff --git a/src/DebugEngineHost/HostConfigurationStore.cs b/src/DebugEngineHost/HostConfigurationStore.cs index 258dc59ec..37a700617 100644 --- a/src/DebugEngineHost/HostConfigurationStore.cs +++ b/src/DebugEngineHost/HostConfigurationStore.cs @@ -81,38 +81,6 @@ public void GetExceptionCategorySettings(Guid categoryId, out HostConfigurationS categoryName = categoryKey.GetSubKeyNames().Single(); } - /// <summary> - /// Checks if logging is enabled, and if so returns a logger object. - /// </summary> - /// <param name="enableLoggingSettingName">[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.</param> - /// <param name="logFileName">[Required] name of the log file to open if logging is enabled.</param> - /// <returns>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</returns> - 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<T>(string settingName, T defaultValue) { return GetDebuggerConfigurationSetting(DebuggerSectionName, settingName, defaultValue); diff --git a/src/DebugEngineHost/HostLogger.cs b/src/DebugEngineHost/HostLogger.cs index 808a24394..413c0157f 100644 --- a/src/DebugEngineHost/HostLogger.cs +++ b/src/DebugEngineHost/HostLogger.cs @@ -3,101 +3,46 @@ 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 { - /// <summary> - /// Callback for programmatic display of log messages - /// </summary> - /// <param name="outputString"></param> - 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<string> callback, LogLevel level = LogLevel.Verbose) { - _streamWriter = streamWriter; - _callback = callback; - } - - public void WriteLine(string line) - { - lock (_locker) + if (s_engineLogChannel == null) { - if (_streamWriter != null) - _streamWriter.WriteLine(line); - _callback?.Invoke(line); + s_engineLogChannel = new HostLogChannel(callback, s_engineLogFile, level); } } - public void Flush() + public static void SetEngineLogFile(string logFile) { - lock (_locker) - { - if (_streamWriter != null) - _streamWriter.Flush(); - } + s_engineLogFile = logFile; } - public void Close() + public static ILogChannel GetEngineLogChannel() { - lock (_locker) - { - if (_streamWriter != null) - _streamWriter.Close(); - _streamWriter = null; - } + return s_engineLogChannel; } - internal static StreamWriter GetStreamForName(string logFileName, bool throwInUseError) + public static ILogChannel GetNatvisLogChannel() { - 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); - - 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; + return s_natvisLogChannel; } - /// <summary> - /// Get a logger after the user has explicitly configured a log file/callback - /// </summary> - /// <param name="logFileName"></param> - /// <param name="callback"></param> - /// <returns>The host logger object</returns> - 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/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<string[]> AutoComplete(string command, int threadId, var matchlist = res.Find<ValueListValue>("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..5efb488e4 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); - } - /// <summary> - /// 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. /// </summary> - 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; + /// <summary> + /// Optional logger to get engine diagnostics logs + /// </summary> + private ILogChannel EngineLogger => HostLogger.GetEngineLogChannel(); + /// <summary> + /// Optional logger to get natvis diagnostics logs + /// </summary> + 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<string> 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,65 @@ public static Logger EnsureInitialized(HostConfigurationStore configStore) return res; } - public static void LoadMIDebugLogger(HostConfigurationStore configStore) + public static void LoadMIDebugLogger() { - 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; - } + 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; } } /// <summary> /// If logging is enabled, writes a line of text to the log /// </summary> + /// <param name="level">[Required] The level of the log.</param> /// <param name="line">[Required] line to write</param> - public void WriteLine(string line) + public void WriteLine(LogLevel level, string line) { if (s_isEnabled) { - WriteLineImpl(line); + WriteLineImpl(level, line); } } /// <summary> /// If logging is enabled, writes a line of text to the log /// </summary> + /// <param name="level">[Required] The level of the log.</param> /// <param name="format">[Required] format string</param> /// <param name="args">arguments to use in the format string</param> - 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); } } /// <summary> /// If logging is enabled, writes a block of text which may contain newlines to the log /// </summary> + /// <param name="level">[Required] The level of the log.</param> /// <param name="prefix">[Optional] Prefix to put on the front of each line</param> /// <param name="textBlock">Block of text to write</param> - 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 +153,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 +165,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 +186,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 @@ <ItemGroup> <None Include="LaunchOptions.xsd" /> - <None Include="SetMIDebugLogging.cmd" /> <ContentWithTargetPath Include="osxlaunchhelper.scpt"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <TargetPath>vscode\osxlaunchhelper.scpt</TargetPath> 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 ^<on^|off^> [/serverlogging [full]] [/VSRootDir ^<value^>] -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 ^<value^> 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<bool> 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/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<List<ThreadContext>> 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<List<ThreadContext>> 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..31f74fb6f 100755 --- a/src/MIDebugEngine/Natvis.Impl/Natvis.cs +++ b/src/MIDebugEngine/Natvis.Impl/Natvis.cs @@ -325,7 +325,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 +355,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 +368,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 +382,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 +406,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 +451,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 +502,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 +1135,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 +1157,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 +1176,7 @@ private VisualizerInfo FindType(IVariableInformation variable) { break; } - parsedName = TypeName.Parse(var.TypeName, _process.Logger); + parsedName = TypeName.Parse(var.TypeName, _process.Logger.NatvisLogger); } return 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) /// </summary> /// <param name="fullyQualifiedName"></param> /// <returns></returns> - 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..027dfdfad 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,26 @@ 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() { } } public class NatvisNamesTest diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index 425061684..a9126074f 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -215,17 +215,38 @@ private void SetCommonDebugSettings(Dictionary<string, JToken> 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<bool>(); + 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<string>(); + 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<string, JToken> 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<bool>(); + if (natvisDiagnosticsBool) + { + m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true); + HostLogger.EnableNatvisLogger((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, message), LogLevel.Verbose); + } + } + else if (natvisDiagnostics.Type == JTokenType.String) + { + string natvisDiagnosticsString = natvisDiagnostics.Value<string>(); + if (Enum.TryParse(natvisDiagnosticsString, ignoreCase: true, out LogLevel level)) + { + m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true); + HostLogger.EnableNatvisLogger((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 { } } + /// <summary> + /// Looks up a localized string similar to Failed to parse engine logging setting: '{0}'. + /// </summary> + internal static string Warning_EngineLoggingParse { + get { + return ResourceManager.GetString("Warning_EngineLoggingParse", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to Could not detect launch url.. /// </summary> @@ -650,6 +659,15 @@ internal static string Warning_LaunchBrowserFailed { } } + /// <summary> + /// Looks up a localized string similar to Failed to parse Nativs logging setting: '{0}'. + /// </summary> + internal static string Warning_NatvisLoggingParse { + get { + return ResourceManager.GetString("Warning_NatvisLoggingParse", resourceCulture); + } + } + /// <summary> /// 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 @@ <data name="Error_InvalidStackFrameOnEvaluateExpression" xml:space="preserve"> <value>Cannot evaluate expression on the specified stack frame.</value> </data> + <data name="Warning_EngineLoggingParse" xml:space="preserve"> + <value>Failed to parse engine logging setting: '{0}'</value> + <comment>{0} is the string passed in by the user</comment> + </data> + <data name="Warning_NatvisLoggingParse" xml:space="preserve"> + <value>Failed to parse Nativs logging setting: '{0}'</value> + <comment>{0} is the string passed in by the user</comment> + </data> </root> \ 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, /// <summary>Process exit message.</summary> ProcessExit, + /// <summary>Natvis Diagnostic Messages</summary> + NatvisDiagnostics } /// <summary>Logging class to handle when and how various classes of output should be logged.</summary> diff --git a/src/OpenDebugAD7/OpenDebug/Program.cs b/src/OpenDebugAD7/OpenDebug/Program.cs index 875a91fd4..7106c36f4 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<LoggingCategory> loggingCategories = new List<LoggingCategory>(); + 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=<LogLevel>: Set's the log level for engine logging."); + Console.WriteLine(" If not specified, default log level is LogLevel.Trace"); + 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.Trace."); 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.EnableNatvisLogger((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.EnableNatvisLogger((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 { From b20b747a715a6b07aafc3a2429609d5e7449b3f1 Mon Sep 17 00:00:00 2001 From: Andrew Wang <waan@microsoft.com> Date: Mon, 10 Oct 2022 12:49:36 -0700 Subject: [PATCH 4/4] Enable NatvisDiagnostics for VS Debug Option (#1357) * Enable NatvisDiagnostics for VS Debug Option This PR enables Natvis Diagnostics for VS. If the NatvisDiagnostics in the VS Debug Options is enabled, it will output to the debug pane. --- src/DebugEngineHost.Common/HostLogChannel.cs | 2 + .../DebugEngineHost.ref.cs | 27 ++- src/DebugEngineHost.VSCode/HostLogger.cs | 2 +- .../HostNatvisProject.cs | 6 + .../HostConfigurationSection.cs | 3 + src/DebugEngineHost/HostConfigurationStore.cs | 45 +++++ src/DebugEngineHost/HostLogger.cs | 13 ++ src/DebugEngineHost/HostNatvisProject.cs | 124 ++++++++++++++ src/DebugEngineHost/HostOutputWindow.cs | 157 ++++++++++++++---- src/DebugEngineHost/RegistryMonitor.cs | 145 ++++++++++++++++ src/DebugEngineHost/Resource.Designer.cs | 11 +- src/DebugEngineHost/Resource.resx | 4 + src/MICore/Logger.cs | 5 + .../Engine.Impl/DebuggedProcess.cs | 4 +- src/MIDebugEngine/Natvis.Impl/Natvis.cs | 16 +- src/MIDebugEngineUnitTests/NatvisNamesTest.cs | 3 + src/OpenDebugAD7/AD7DebugSession.cs | 4 +- src/OpenDebugAD7/OpenDebug/Program.cs | 8 +- 18 files changed, 537 insertions(+), 42 deletions(-) create mode 100644 src/DebugEngineHost/RegistryMonitor.cs diff --git a/src/DebugEngineHost.Common/HostLogChannel.cs b/src/DebugEngineHost.Common/HostLogChannel.cs index 1c3a2bab7..14bdc1b57 100644 --- a/src/DebugEngineHost.Common/HostLogChannel.cs +++ b/src/DebugEngineHost.Common/HostLogChannel.cs @@ -37,6 +37,8 @@ public enum LogLevel // 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); diff --git a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs index b4ebb6124..9b42b62e0 100644 --- a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs +++ b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs @@ -217,6 +217,12 @@ public enum LogLevel /// </summary> public interface ILogChannel { + /// <summary> + /// Changes the log level of the channel + /// </summary> + /// <param name="newLevel">The new log level to use.</param> + void SetLogLevel(LogLevel newLevel); + /// <summary> /// Writes the given message with a newline to the log channel. /// </summary> @@ -248,8 +254,6 @@ public interface ILogChannel /// </summary> public static class HostLogger { - // EnableNatvisLogger is only used in OpenDebugAD7 - /// <summary> /// Enables engine logging if not already enabled. /// </summary> @@ -260,6 +264,16 @@ public static void EnableHostLogging(Action<string> callback, LogLevel level = L throw new NotImplementedException(); } + /// <summary> + /// Enables natvis logging if not already enabled. + /// </summary> + /// <param name="callback">The callback to use to send the natvis log.</param> + /// <param name="level">The level of the log to filter the channel on.</param> + public static void EnableNatvisDiagnostics(Action<string> callback, LogLevel level = LogLevel.Verbose) + { + throw new NotImplementedException(); + } + /// <summary> /// Sets the log file to write to. /// </summary> @@ -269,7 +283,6 @@ public static void SetEngineLogFile(string logFile) throw new NotImplementedException(); } - /// <summary> /// Gets the engine log channel created by 'EnableHostLogging' /// </summary> @@ -439,6 +452,14 @@ public static void FindNatvis(NatvisLoader loader) throw new NotImplementedException(); } + /// <summary> + /// Enable's tracking the VS 'Natvis Diagnostic Messages (C++ only)' setting. + /// </summary> + public static IDisposable WatchNatvisOptionSetting(HostConfigurationStore configStore, ILogChannel natvisLogger) + { + throw new NotImplementedException(); + } + /// <summary> /// Return the solution's root directory, null if no solution /// </summary> diff --git a/src/DebugEngineHost.VSCode/HostLogger.cs b/src/DebugEngineHost.VSCode/HostLogger.cs index 5eda359e8..6723a4aee 100644 --- a/src/DebugEngineHost.VSCode/HostLogger.cs +++ b/src/DebugEngineHost.VSCode/HostLogger.cs @@ -12,7 +12,7 @@ public static class HostLogger private static string s_engineLogFile; - public static void EnableNatvisLogger(Action<string> callback, LogLevel level = LogLevel.Verbose) + public static void EnableNatvisDiagnostics(Action<string> callback, LogLevel level = LogLevel.Verbose) { if (s_natvisLogChannel == 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/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<string> GetValueNames() { return _key.GetValueNames(); } + + public SafeRegistryHandle Handle => _key.Handle; } } diff --git a/src/DebugEngineHost/HostConfigurationStore.cs b/src/DebugEngineHost/HostConfigurationStore.cs index 37a700617..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) @@ -131,5 +136,45 @@ private object GetOptionalValue(string section, string valueName) return key.GetValue(valueName); } } + + /// <summary> + /// This method grabs the Debugger Subkey in HKCU + /// </summary> + /// <returns>The subkey of Debugger if it exists. Returns null otherwise.</returns> + public HostConfigurationSection GetCurrentUserDebuggerSection() + { + using (RegistryKey hkcuRoot = Registry.CurrentUser.OpenSubKey(_registryRoot)) + { + RegistryKey debuggerSection = hkcuRoot.OpenSubKey(DebuggerSectionName); + if (debuggerSection != null) + { + return new HostConfigurationSection(debuggerSection); + } + return null; + } + } + + /// <summary> + /// Grabs the Debugger/NatvisDiagnostic subkey in HKCU + /// </summary> + /// <returns>The NatvisDiagnostic subkey if it exists. Returns null otherwise.</returns> + 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 413c0157f..9ce19cf22 100644 --- a/src/DebugEngineHost/HostLogger.cs +++ b/src/DebugEngineHost/HostLogger.cs @@ -24,6 +24,19 @@ public static void EnableHostLogging(Action<string> callback, LogLevel level = L } } + public static void EnableNatvisDiagnostics(Action<string> callback, LogLevel level = LogLevel.Verbose) + { + if (s_natvisLogChannel== null) + { + s_natvisLogChannel = new HostLogChannel(callback, null, level); + } + } + + public static void DisableNatvisDiagnostics() + { + s_natvisLogChannel = null; + } + public static void SetEngineLogFile(string logFile) { s_engineLogFile = logFile; 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; + } + } + /// <summary> /// 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<IVsOutputWindow> outputWindowLazy = new Lazy<IVsOutputWindow>(() => + { + 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<IVsUIShell> shellLazy = new Lazy<IVsUIShell>(() => + { + 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<string, PaneInfo> panes = new Dictionary<string, PaneInfo>() + { + // The 'Debug' pane exists by default + { DefaultOutputPane, new PaneInfo(VSConstants.GUID_OutWindowDebugPane) } + }; + + /// <summary> + /// Writes text directly to the VS Output window. + /// </summary> + 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"); + } + + /// <summary> + /// Writes text directly to the VS Output window, appending a newline. + /// </summary> + public static void WriteLine(string message, string pane = DefaultOutputPane) + { + Write(string.Concat(message, Environment.NewLine), pane); + } + } + /// <summary> /// Provides direct access to the underlying output window without going through debug events /// </summary> @@ -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 + + /// <summary> + /// Filter for notifications reported by <see cref="RegistryMonitor"/>. + /// </summary> + [Flags] + public enum RegChangeNotifyFilter + { + /// <summary>Notify the caller if a subkey is added or deleted.</summary> + REG_NOTIFY_CHANGE_NAME = 1, + /// <summary>Notify the caller of changes to the attributes of the key, + /// such as the security descriptor information.</summary> + REG_NOTIFY_CHANGE_ATTRIBUTES = 2, + /// <summary>Notify the caller of changes to a value of the key. This can + /// include adding or deleting a value, or changing an existing value.</summary> + REG_NOTIFY_CHANGE_LAST_SET = 4, + /// <summary>Notify the caller of changes to the security descriptor + /// of the key.</summary> + 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; + + /// <summary> + /// Occurs when the specified registry key has changed. + /// </summary> + 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() { } } + /// <summary> + /// Looks up a localized string similar to Failed to watch RegistryKey with error code '{0}'.. + /// </summary> + internal static string Error_WatchRegistry { + get { + return ResourceManager.GetString("Error_WatchRegistry", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to Workspace index is incomplete; some .natvis files may not have been found.. /// </summary> 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 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> + <data name="Error_WatchRegistry" xml:space="preserve"> + <value>Failed to watch RegistryKey with error code '{0}'.</value> + <comment>{0} is an error code</comment> + </data> <data name="IndexIncomplete" xml:space="preserve"> <value>Workspace index is incomplete; some .natvis files may not have been found.</value> </data> diff --git a/src/MICore/Logger.cs b/src/MICore/Logger.cs index 5efb488e4..86e2db72f 100644 --- a/src/MICore/Logger.cs +++ b/src/MICore/Logger.cs @@ -74,6 +74,11 @@ public static Logger EnsureInitialized() return res; } + public static void EnableNatvisDiagnostics(LogLevel level) + { + HostLogger.EnableNatvisDiagnostics(HostOutputWindow.WriteLaunchError, level); + } + public static void LoadMIDebugLogger() { if (CmdLogInfo.enabled) 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/Natvis.Impl/Natvis.cs b/src/MIDebugEngine/Natvis.Impl/Natvis.cs index 31f74fb6f..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<FileInfo> _typeVisualizers; private DebuggedProcess _process; + private HostConfigurationStore _configStore; private Dictionary<string, VisualizerInfo> _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<FileInfo>(); _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) @@ -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/MIDebugEngineUnitTests/NatvisNamesTest.cs b/src/MIDebugEngineUnitTests/NatvisNamesTest.cs index 027dfdfad..3de8665d6 100644 --- a/src/MIDebugEngineUnitTests/NatvisNamesTest.cs +++ b/src/MIDebugEngineUnitTests/NatvisNamesTest.cs @@ -47,6 +47,9 @@ 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 a9126074f..dcea56bc0 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -270,7 +270,7 @@ private void SetCommonDebugSettings(Dictionary<string, JToken> args) if (natvisDiagnosticsBool) { m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true); - HostLogger.EnableNatvisLogger((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, message), LogLevel.Verbose); + HostLogger.EnableNatvisDiagnostics((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, message), LogLevel.Verbose); } } else if (natvisDiagnostics.Type == JTokenType.String) @@ -279,7 +279,7 @@ private void SetCommonDebugSettings(Dictionary<string, JToken> args) if (Enum.TryParse(natvisDiagnosticsString, ignoreCase: true, out LogLevel level)) { m_logger.SetLoggingConfiguration(LoggingCategory.NatvisDiagnostics, true); - HostLogger.EnableNatvisLogger((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, string.Concat("[Natvis] ", message)), level); + HostLogger.EnableNatvisDiagnostics((message) => m_logger.WriteLine(LoggingCategory.NatvisDiagnostics, string.Concat("[Natvis] ", message)), level); } } else diff --git a/src/OpenDebugAD7/OpenDebug/Program.cs b/src/OpenDebugAD7/OpenDebug/Program.cs index 7106c36f4..21eb7c36a 100644 --- a/src/OpenDebugAD7/OpenDebug/Program.cs +++ b/src/OpenDebugAD7/OpenDebug/Program.cs @@ -53,9 +53,9 @@ private static int Main(string[] argv) 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=<LogLevel>: Set's the log level for engine logging."); - Console.WriteLine(" If not specified, default log level is LogLevel.Trace"); + 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.Trace."); + 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); @@ -75,7 +75,7 @@ private static int Main(string[] argv) break; case "--natvisDiagnostics": loggingCategories.Add(LoggingCategory.NatvisDiagnostics); - HostLogger.EnableNatvisLogger((msg) => + HostLogger.EnableNatvisDiagnostics((msg) => { Console.Error.WriteLine(msg); }, LogLevel.Verbose); @@ -105,7 +105,7 @@ private static int Main(string[] argv) if (!Enum.TryParse(a.Substring("--natvisDiagnostics=".Length), out LogLevel natvisLogLevel)) { loggingCategories.Add(LoggingCategory.NatvisDiagnostics); - HostLogger.EnableNatvisLogger((msg) => + HostLogger.EnableNatvisDiagnostics((msg) => { Console.Error.WriteLine(msg); }, natvisLogLevel);