diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..744144fa1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,57 @@ +# EditorConfig is awesome: http://EditorConfig.org +# This configuration is respected by multiple editors +# Using basic settings to unify indentation in repo + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +trim_trailing_whitespace = true + +# XML Formats + +[*.{xml,csproj,androidproj,vcxproj,vcxproj.filters,nuspec,vsct,resx,config,targets,props,xsd,natvis,playlist}] +indent_size = 2 + +# JSON and YML +[*.yml] +[*.json] +indent_size = 2 + +# .NET + +[*.cs] +file_header_template = // Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information. +dotnet_sort_system_directives_first = false + +# static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = warning +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = warning +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +dotnet_style_readonly_field = true:warning + +# Unix Shell scripts +[*.sh] +charset = utf-8 +end_of_line = lf + +[*.sln] +indent_style = tab \ No newline at end of file diff --git a/src/MICore/JsonLaunchOptions.cs b/src/MICore/JsonLaunchOptions.cs index 616113ff6..7c0dea543 100644 --- a/src/MICore/JsonLaunchOptions.cs +++ b/src/MICore/JsonLaunchOptions.cs @@ -486,6 +486,12 @@ public partial class PipeTransportOptions [JsonProperty("pipeArgs", DefaultValueHandling = DefaultValueHandling.Ignore)] public List PipeArgs { get; private set; } + /// + /// Command line arguments passed to the pipe program to execute a remote command. + /// + [JsonProperty("pipeCmd", DefaultValueHandling = DefaultValueHandling.Ignore)] + public List PipeCmd { get; private set; } + /// /// The full path to the debugger on the target machine, for example /usr/bin/gdb. /// diff --git a/src/MICore/LaunchOptions.cs b/src/MICore/LaunchOptions.cs index c5b135440..2dbef35a3 100644 --- a/src/MICore/LaunchOptions.cs +++ b/src/MICore/LaunchOptions.cs @@ -106,6 +106,7 @@ static internal PipeLaunchOptions CreateFromJson(JObject parsedOptions) string pipeCwd = pipeTransport.PipeCwd; string pipeProgram = pipeTransport.PipeProgram; List pipeArgs = pipeTransport.PipeArgs; + List pipeCmd = pipeTransport.PipeCmd; string debuggerPath = pipeTransport.DebuggerPath; bool quoteArgs = pipeTransport.QuoteArgs.GetValueOrDefault(true); Dictionary pipeEnv = pipeTransport.PipeEnv; @@ -128,6 +129,7 @@ static internal PipeLaunchOptions CreateFromJson(JObject parsedOptions) { pipeProgram = platformSpecificTransportOptions.PipeProgram ?? pipeProgram; pipeArgs = platformSpecificTransportOptions.PipeArgs ?? pipeArgs; + pipeCmd = platformSpecificTransportOptions.PipeCmd ?? pipeCmd; pipeCwd = platformSpecificTransportOptions.PipeCwd ?? pipeCwd; pipeEnv = platformSpecificTransportOptions.PipeEnv ?? pipeEnv; debuggerPath = platformSpecificTransportOptions.DebuggerPath ?? pipeTransport.DebuggerPath; @@ -137,7 +139,7 @@ static internal PipeLaunchOptions CreateFromJson(JObject parsedOptions) PipeLaunchOptions pipeOptions = new PipeLaunchOptions( pipePath: pipeProgram, pipeArguments: EnsurePipeArguments(pipeArgs, debuggerPath, gdbPathDefault, quoteArgs), - pipeCommandArguments: ParseArguments(pipeArgs, quoteArgs), + pipeCommandArguments: ParseArguments(pipeCmd, quoteArgs), pipeCwd: pipeCwd, pipeEnvironment: GetEnvironmentEntries(pipeEnv) ); @@ -1909,6 +1911,7 @@ public void InitializeLaunchOptions(Json.LaunchOptions.LaunchOptions launch) public void InitializeAttachOptions(Json.LaunchOptions.AttachOptions attach) { + this.DebuggerMIMode = ConvertMIModeString(RequireAttribute(attach.MIMode, nameof(attach.MIMode))); this.ProcessId = attach.ProcessId; } diff --git a/src/MIDebugEngine.sln b/src/MIDebugEngine.sln index e07eb2419..b170503a3 100755 --- a/src/MIDebugEngine.sln +++ b/src/MIDebugEngine.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{2865 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CF02407C-BF37-4D51-83F4-845A7F36C101}" ProjectSection(SolutionItems) = preProject + ..\.editorconfig = ..\.editorconfig IDECodeAnalysis.ruleset = IDECodeAnalysis.ruleset EndProjectSection EndProject @@ -59,7 +60,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Debu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MIDebugPackage", "MIDebugPackage\MIDebugPackage.csproj", "{A8D8E02F-4258-4F2E-88B6-A50FEBD5AD8C}" ProjectSection(ProjectDependencies) = postProject - {15BCBEF4-1C2B-412B-925B-34A049097E62} = {15BCBEF4-1C2B-412B-925B-34A049097E62} + {15BCBEF4-1C2B-412B-925B-34A049097E62} = {15BCBEF4-1C2B-412B-925B-34A049097E62} EndProjectSection EndProject Global diff --git a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs index 7865a3887..a99f43dd4 100755 --- a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs @@ -839,7 +839,7 @@ public int EnumCodeContexts(IDebugDocumentPosition2 docPosition, out IEnumDebugC { var codeCxt = new AD7MemoryAddress(this, a, null); TEXT_POSITION pos; - pos.dwLine = line; + pos.dwLine = line - 1; pos.dwColumn = 0; MITextPosition textPosition = new MITextPosition(documentName, pos, pos); codeCxt.SetDocumentContext(new AD7DocumentContext(textPosition, codeCxt)); diff --git a/src/MIDebugEngine/AD7.Impl/AD7Events.cs b/src/MIDebugEngine/AD7.Impl/AD7Events.cs index 2b031bfd4..022d24908 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Events.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Events.cs @@ -407,7 +407,7 @@ public AD7ExceptionEvent(string name, string description, uint code, Guid? excep { _name = name; _code = code; - _description = description ?? name; + _description = string.IsNullOrEmpty(description) ? name : description; _category = exceptionCategory ?? EngineConstants.EngineId; switch (state) diff --git a/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs b/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs index f478379ad..318e50e2e 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs @@ -2,11 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Text; using Microsoft.VisualStudio.Debugger.Interop; using MICore; -using Microsoft.MIDebugEngine.Natvis; namespace Microsoft.MIDebugEngine { @@ -14,7 +11,7 @@ namespace Microsoft.MIDebugEngine // IDebugMemoryContext2 represents a position in the address space of the machine running the program being debugged. // IDebugCodeContext2 represents the starting position of a code instruction. // For most run-time architectures today, a code context can be thought of as an address in a program's execution stream. - internal class AD7MemoryAddress : IDebugCodeContext2 + internal sealed class AD7MemoryAddress : IDebugCodeContext2 { private readonly AD7Engine _engine; private readonly ulong _address; @@ -42,6 +39,9 @@ public void SetDocumentContext(IDebugDocumentContext2 docContext) // Adds a specified value to the current context's address to create a new context. public int Add(ulong dwCount, out IDebugMemoryContext2 newAddress) { + // NB: this is not correct for IDebugCodeContext2 according to the docs + // https://docs.microsoft.com/en-us/visualstudio/extensibility/debugger/reference/idebugcodecontext2#remarks + // But it's not used in practice (instead: IDebugDisassemblyStream2.Seek) newAddress = new AD7MemoryAddress(_engine, (uint)dwCount + _address, null); return Constants.S_OK; } @@ -160,19 +160,22 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) { pinfo[0].dwFields = 0; - if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0) + if ((dwFields & (enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS | enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE)) != 0) { - pinfo[0].bstrAddress = EngineUtils.AsAddr(_address, _engine.DebuggedProcess.Is64BitArch); - pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS; + string addr = EngineUtils.AsAddr(_address, _engine.DebuggedProcess.Is64BitArch); + if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0) + { + pinfo[0].bstrAddress = addr; + pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS; + } + if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE) != 0) + { + pinfo[0].bstrAddressAbsolute = addr; + pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE; + } } - // Fields not supported by the sample if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSOFFSET) != 0) { } - if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE) != 0) - { - pinfo[0].bstrAddressAbsolute = EngineUtils.AsAddr(_address, _engine.DebuggedProcess.Is64BitArch); - pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE; - } if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL) != 0) { DebuggedModule module = _engine.DebuggedProcess.ResolveAddress(_address); @@ -195,7 +198,6 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_FUNCTION; } } - if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_FUNCTIONOFFSET) != 0) { } return Constants.S_OK; } @@ -210,10 +212,10 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) } // Gets the user-displayable name for this context - // This is not supported by the sample engine. public int GetName(out string pbstrName) { - throw new NotImplementedException(); + pbstrName = _functionName ?? Engine.GetAddressDescription(_address); + return Constants.S_OK; } // Subtracts a specified value from the current context's address to create a new context. diff --git a/src/MIDebugEngine/AD7.Impl/AD7StackFrame.cs b/src/MIDebugEngine/AD7.Impl/AD7StackFrame.cs index 82ec4c1e5..9033f3740 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7StackFrame.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7StackFrame.cs @@ -14,7 +14,7 @@ namespace Microsoft.MIDebugEngine { // Represents a logical stack frame on the thread stack. // Also implements the IDebugExpressionContext interface, which allows expression evaluation and watch windows. - internal class AD7StackFrame : IDebugStackFrame2, IDebugExpressionContext2 + internal sealed class AD7StackFrame : IDebugStackFrame2, IDebugExpressionContext2 { public AD7Engine Engine { get; private set; } public AD7Thread Thread { get; private set; } @@ -53,11 +53,7 @@ public AD7StackFrame(AD7Engine engine, AD7Thread thread, ThreadContext threadCon if (_textPosition != null) { _documentCxt = new AD7DocumentContext(_textPosition, _codeCxt); - - if (_codeCxt != null) - { - _codeCxt.SetDocumentContext(_documentCxt); - } + _codeCxt?.SetDocumentContext(_documentCxt); } } diff --git a/src/MIDebugPackage/OpenFolderSchema.json b/src/MIDebugPackage/OpenFolderSchema.json index a74fef6c9..b1483a9bd 100644 --- a/src/MIDebugPackage/OpenFolderSchema.json +++ b/src/MIDebugPackage/OpenFolderSchema.json @@ -61,10 +61,14 @@ "type": "object", "description": "Optional source file mappings passed to the debug engine. Format: '{ \"\": \"\" }' OR '{ \"\": { \"editorPath\": \"\", \"useForBreakpoints\": true } }'. \nExample: '{ \"/home/user/foo\": \"C:\\foo\" }' OR '{ \"/home/user/foo\": { \"editorPath\": \"c:\\foo\", \"useForBreakpoints\": true } }'.", "additionalProperties": { - "anyOf": { - "type": "string", - "$ref": "#/definitions/cpp_schema/definitions/sourceFileMapOptions" - } + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/cpp_schema/definitions/sourceFileMapOptions" + } + ] } }, "MIMode": { @@ -205,6 +209,13 @@ "type": "string" } }, + "pipeCmd": { + "type": "array", + "description": "Arguments passed to the pipe executable to run a remote Unix (ex: bash or similar) shell command. This is used for running 'kill' on the remote system, or other commands that the debugger may need. If not specified, the debugger will do its best without the shell. But some features, such as setting breakpoints in run mode, may not work. This string should contain the string '{0}' which will be replaced with the command to execute.", + "items": { + "type": "string" + } + }, "debuggerPath": { "type": "string", "description": "The full path to the debugger on the target machine, for example /usr/bin/gdb." diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index 9921e0019..f57fa43d9 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -27,7 +28,7 @@ namespace OpenDebugAD7 { - internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEventCallback2 + internal sealed class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEventCallback2 { // This is a general purpose lock. Don't hold it across long operations. private readonly object m_lock = new object(); @@ -44,6 +45,10 @@ internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEven private readonly DebugEventLogger m_logger; private readonly Dictionary> m_breakpoints; + + private readonly ConcurrentDictionary m_gotoCodeContexts = new ConcurrentDictionary(); + private int m_nextContextId = 1; + private Dictionary m_functionBreakpoints; private readonly HandleCollection m_frameHandles; @@ -350,6 +355,7 @@ public void BeforeContinue() m_isStopped = false; m_variableManager.Reset(); m_frameHandles.Reset(); + m_gotoCodeContexts.Clear(); } public void Stopped(IDebugThread2 thread) @@ -793,6 +799,7 @@ protected override void HandleInitializeRequestAsync(IRequestResponder responder) + { + responder.SetError(new ProtocolException(AD7Resources.Error_NotImplementedSetNextStatement)); + } + + protected override void HandleGotoTargetsRequestAsync(IRequestResponder responder) + { + var response = new GotoTargetsResponse(); + + var source = responder.Arguments.Source; + + // Virtual documents don't have paths + if (source.Path == null) + { + responder.SetResponse(response); + return; + } + + try + { + string convertedPath = m_pathConverter.ConvertClientPathToDebugger(source.Path); + int line = m_pathConverter.ConvertClientLineToDebugger(responder.Arguments.Line); + var docPos = new AD7DocumentPosition(m_sessionConfig, convertedPath, line); + + var targets = new List(); + + IEnumDebugCodeContexts2 codeContextsEnum; + if (m_program.EnumCodeContexts(docPos, out codeContextsEnum) == HRConstants.S_OK) + { + var codeContexts = new IDebugCodeContext2[1]; + uint nProps = 0; + while (codeContextsEnum.Next(1, codeContexts, ref nProps) == HRConstants.S_OK) + { + var codeContext = codeContexts[0]; + + string contextName; + codeContext.GetName(out contextName); + + line = responder.Arguments.Line; + IDebugDocumentContext2 documentContext; + if (codeContext.GetDocumentContext(out documentContext) == HRConstants.S_OK) + { + var startPos = new TEXT_POSITION[1]; + var endPos = new TEXT_POSITION[1]; + if (documentContext.GetStatementRange(startPos, endPos) == HRConstants.S_OK) + line = m_pathConverter.ConvertDebuggerLineToClient((int)startPos[0].dwLine); + } + + string instructionPointerReference = null; + CONTEXT_INFO[] contextInfo = new CONTEXT_INFO[1]; + if (codeContext.GetInfo(enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS, contextInfo) == HRConstants.S_OK && + contextInfo[0].dwFields.HasFlag(enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS)) + { + instructionPointerReference = contextInfo[0].bstrAddress; + } + + int codeContextId = m_nextContextId++; + m_gotoCodeContexts.TryAdd(codeContextId, codeContext); + + targets.Add(new GotoTarget() + { + Id = codeContextId, + Label = contextName, + Line = line, + InstructionPointerReference = instructionPointerReference + }); + } + } + + response.Targets = targets; + } + catch (AD7Exception e) + { + responder.SetError(new ProtocolException(e.Message)); + return; + } + + responder.SetResponse(response); + } + protected override void HandleStackTraceRequestAsync(IRequestResponder responder) { int threadReference = responder.Arguments.ThreadId; @@ -1457,11 +1544,13 @@ protected override void HandleStackTraceRequestAsync(IRequestResponder + /// Looks up a localized string similar to Set next statement is not supported by the current debugger.. + /// + internal static string Error_NotImplementedSetNextStatement { + get { + return ResourceManager.GetString("Error_NotImplementedSetNextStatement", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0}: property '{1}' is invalid.. /// @@ -433,16 +442,6 @@ internal static string Locals_Scope_Name { } } - /// - /// Looks up a localized string similar to Registers. - /// - internal static string Registers_Scope_Name - { - get { - return ResourceManager.GetString("Registers_Scope_Name", resourceCulture); - } - } - /// /// Looks up a localized string similar to Debugger failed to signal process termination.. /// @@ -534,6 +533,15 @@ internal static string ProcessExitMessage { } } + /// + /// Looks up a localized string similar to Registers. + /// + internal static string Registers_Scope_Name { + get { + return ResourceManager.GetString("Registers_Scope_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not detect launch url.. /// diff --git a/src/OpenDebugAD7/AD7Resources.resx b/src/OpenDebugAD7/AD7Resources.resx index 33a8db067..bc916064d 100644 --- a/src/OpenDebugAD7/AD7Resources.resx +++ b/src/OpenDebugAD7/AD7Resources.resx @@ -298,4 +298,7 @@ Unable to retrieve stack trace. {0} + + Set next statement is not supported by the current debugger. + \ No newline at end of file