From 01da087e82f082ade4b5377848a32734ba996ae9 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Mon, 8 Nov 2021 16:34:15 -0800 Subject: [PATCH 1/6] Add support for Disassembly Source for GDB This PR adds Location and Line to Disassembly Response. --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 36 +++++ src/MIDebugEngine/Engine.Impl/Disassembly.cs | 138 ++++++++++++++++++- src/OpenDebugAD7/AD7DebugSession.cs | 10 ++ test/CppTests/Tests/MemoryTests.cs | 62 +++++++++ 4 files changed, 245 insertions(+), 1 deletion(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index b699a49df..d48361897 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -89,6 +89,20 @@ private DisassemblyData FetchBadInstruction(enum_DISASSEMBLY_STREAM_FIELDS dwFie dis.dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE; dis.bstrOpcode = "??"; } + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) + { + dis.dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; + dis.bstrDocumentUrl = string.Empty; + } + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) + { + dis.dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; + dis.posBeg = new TEXT_POSITION(); + dis.posEnd = new TEXT_POSITION(); + } + return dis; } @@ -162,6 +176,28 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou } } + if (!string.IsNullOrWhiteSpace(instruction.File)) + { + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; + prgDisassembly[iOp].bstrDocumentUrl = instruction.File; + } + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; + prgDisassembly[iOp].posBeg = new TEXT_POSITION() + { + dwLine = instruction.Line + }; + prgDisassembly[iOp].posEnd = new TEXT_POSITION() + { + dwLine = instruction.Line + }; + } + } + iOp++; }; diff --git a/src/MIDebugEngine/Engine.Impl/Disassembly.cs b/src/MIDebugEngine/Engine.Impl/Disassembly.cs index eb2d07993..9c74d4bce 100644 --- a/src/MIDebugEngine/Engine.Impl/Disassembly.cs +++ b/src/MIDebugEngine/Engine.Impl/Disassembly.cs @@ -305,9 +305,77 @@ private void DeleteRangeFromCache(DisassemblyBlock block) } } + private class DisasmAddressRange + { + public ulong StartAddress; + public ulong EndAddress; + Dictionary AddressToInstruction; + + public DisasmAddressRange(DisasmInstruction instruction) + { + StartAddress = instruction.Addr; + EndAddress = instruction.Addr; + AddressToInstruction = new Dictionary() + { + {instruction.Addr, instruction} + }; + } + + public void UpdateEndAddress(DisasmInstruction instruction) + { + EndAddress = instruction.Addr; + AddressToInstruction.Add(instruction.Addr, instruction); + } + + public void MapSourceToInstructions(DebuggedProcess process, TupleValue[] src_and_asm_lines) + { + /* Example response + * [ + * { + * line="15", + * file="main.cpp", + * fullname="/home/cpp/main.cpp", + * line_asm_insn=[ + * { + * address="0x0000000008001485", + * func-name="main(int, char**)", + * offset="316", + * opcodes="83 bd 4c ff ff ff 00", + * inst="cmpl $0x0,-0xb4(%rbp)" + * }, + * { + * address="0x000000000800148c", + * func-name="main(int, char**)", + * offset="323", + * opcodes="75 07", + * inst="jne 0x8001495 " + * } + * ] + * } + * ] + */ + foreach (TupleValue src_and_asm_line in src_and_asm_lines) + { + uint line = src_and_asm_line.FindUint("line"); + string file = process.GetMappedFileFromTuple(src_and_asm_line); + ValueListValue line_asm_instructions = src_and_asm_line.Find("line_asm_insn"); + foreach (ResultValue line_asm_insn in line_asm_instructions.Content) + { + ulong address = line_asm_insn.FindAddr("address"); + AddressToInstruction[address].File = file; + AddressToInstruction[address].Line = line; + } + } + } + } + // this is inefficient so we try and grab everything in one gulp internal static async Task Disassemble(DebuggedProcess process, ulong startAddr, ulong endAddr) { + // Due to GDB not returning source information when requesting outside of the range of user code. + // We first get disassembly with opcodes, then map each Symbol to an address range and attempt to retrieve source information per Symbol. + + // Mode 2 - disassembly with raw opcodes string cmd = "-data-disassemble -s " + EngineUtils.AsAddr(startAddr, process.Is64BitArch) + " -e " + EngineUtils.AsAddr(endAddr, process.Is64BitArch) + " -- 2"; Results results = await process.CmdAsync(cmd, ResultClass.None); if (results.ResultClass != ResultClass.done) @@ -315,7 +383,75 @@ internal static async Task Disassemble(DebuggedProcess proc return null; } - return DecodeDisassemblyInstructions(results.Find("asm_insns").AsArray()); + DisasmInstruction[] instructions = DecodeDisassemblyInstructions(results.Find("asm_insns").AsArray()); + + if (instructions != null && instructions.Length != 0) + { + // Map 'Symbol' (Function Name) to Address Range + Dictionary funcToAddressRange = new Dictionary(); + for (int i = 0; i < instructions.Length; i++) + { + string functionName = instructions[i].Symbol; + if (funcToAddressRange.ContainsKey(functionName)) + { + // Update the end address with this current instruction. + funcToAddressRange[functionName].UpdateEndAddress(instructions[i]); + } + else + { + // Insert new function to keep track of. + funcToAddressRange.Add(functionName, new DisasmAddressRange(instructions[i])); + } + + // endAddr will not show up in instructions, so make the last instruction we get to get the range til the end. + if (i == instructions.Length - 1) + { + funcToAddressRange[functionName].EndAddress = endAddr; + } + } + + foreach (string functionName in funcToAddressRange.Keys) + { + DisasmAddressRange range = funcToAddressRange[functionName]; + + // Mode 5 - mixed source and disassembly with raw opcodes + cmd = "-data-disassemble -s " + EngineUtils.AsAddr(range.StartAddress, process.Is64BitArch) + " -e " + EngineUtils.AsAddr(range.EndAddress, process.Is64BitArch) + " -- 5"; + results = await process.CmdAsync(cmd, ResultClass.None); + if (results.ResultClass != ResultClass.done) + { + return null; + } + try + { + /* Example response + * asm_insns=[ + * src_and_asm_line={ + * line="15", + * file="main.cpp", + * fullname="/home/cpp/main.cpp", + * line_asm_insn=[ ... ] + * } + * ] + */ + ResultListValue asm_insns = results.Find("asm_insns"); + if (asm_insns != null) + { + TupleValue[] values = asm_insns.FindAll("src_and_asm_line"); + if (values != null) + { + range.MapSourceToInstructions(process, values); + } + } + } + catch + { + // asm_insns can be ValueListValue if it does not contain source information. + } + } + } + + + return instructions; } // this is inefficient so we try and grab everything in one gulp diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index f0b08d439..ddab6696b 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -2076,6 +2076,16 @@ protected override void HandleDisassembleRequestAsync(IRequestResponder instructions = runner.Disassemble(ip, 1); + + // Validate that we got one instructions. + Assert.Single(instructions); + + // Test Source Information for Disasembly + IDisassemblyInstruction dismInstr = instructions.First(); + Assert.Equal(33, dismInstr.Line); + Assert.NotNull(dismInstr.Location); + Assert.Contains(SinkHelper.Main, dismInstr.Location.path); + + this.Comment("Continue until end"); + runner.Expects.ExitedEvent() + .TerminatedEvent() + .AfterContinue(); + + runner.DisconnectAndVerify(); + } + } + #endregion } } From 3d0ae2fdd1b595165f253b11b49a7ed1c04a91cb Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 9 Nov 2021 18:15:40 -0800 Subject: [PATCH 2/6] Addressing some PR issues --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 91 ++++++++++++++------ src/MIDebugEngine/Engine.Impl/Disassembly.cs | 84 ++++++++---------- src/OpenDebugAD7/AD7DebugSession.cs | 16 ++-- 3 files changed, 112 insertions(+), 79 deletions(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index d48361897..9f3cc4f47 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -14,6 +14,9 @@ internal class AD7DisassemblyStream : IDebugDisassemblyStream2 private AD7Engine _engine; private ulong _addr; private enum_DISASSEMBLY_STREAM_SCOPE _scope; + private string _pLastDocument; + private int m_dwLastSourceLine; + private bool m_lastSourceInfoStale; internal AD7DisassemblyStream(AD7Engine engine, enum_DISASSEMBLY_STREAM_SCOPE scope, IDebugCodeContext2 pCodeContext) { @@ -21,6 +24,9 @@ internal AD7DisassemblyStream(AD7Engine engine, enum_DISASSEMBLY_STREAM_SCOPE sc _scope = scope; AD7MemoryAddress addr = pCodeContext as AD7MemoryAddress; _addr = addr.Address; + _pLastDocument = null; + m_dwLastSourceLine = -1; + m_lastSourceInfoStale = true; } #region IDebugDisassemblyStream2 Members @@ -90,19 +96,6 @@ private DisassemblyData FetchBadInstruction(enum_DISASSEMBLY_STREAM_FIELDS dwFie dis.bstrOpcode = "??"; } - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) - { - dis.dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; - dis.bstrDocumentUrl = string.Empty; - } - - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) - { - dis.dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; - dis.posBeg = new TEXT_POSITION(); - dis.posEnd = new TEXT_POSITION(); - } - return dis; } @@ -132,8 +125,12 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou prgDisassembly[iOp] = FetchBadInstruction(dwFields); } - foreach (DisasmInstruction instruction in instructions) + IEnumerator instructionEnumerator = instructions.GetEnumerator(); + for (int idx = 0; idx < dwInstructions; idx++) { + instructionEnumerator.MoveNext(); + DisasmInstruction instruction = instructionEnumerator.Current; + if (iOp >= dwInstructions) { break; @@ -176,28 +173,64 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou } } - if (!string.IsNullOrWhiteSpace(instruction.File)) + if (idx == 0 && m_lastSourceInfoStale) { - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) + m_lastSourceInfoStale = false; + + ulong previousInstructionAddress = instruction.Addr - 1; + + if (previousInstructionAddress >= 0 && previousInstructionAddress < (ulong)prgDisassembly.Length) { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; - prgDisassembly[iOp].bstrDocumentUrl = instruction.File; + DisassemblyData previousInstruction = prgDisassembly[previousInstructionAddress]; + m_dwLastSourceLine = (int)previousInstruction.posBeg.dwLine - 1; + _pLastDocument = previousInstruction.bstrDocumentUrl; } + } - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) + { + uint startLine = instruction.Line - 1; + + prgDisassembly[iOp].posBeg = new TEXT_POSITION() { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; - prgDisassembly[iOp].posBeg = new TEXT_POSITION() - { - dwLine = instruction.Line - }; - prgDisassembly[iOp].posEnd = new TEXT_POSITION() + dwLine = startLine, + dwColumn = 0 + }; + prgDisassembly[iOp].posEnd = new TEXT_POSITION() + { + dwLine = instruction.Line, + dwColumn = 0 + }; + + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; + + if (m_dwLastSourceLine != instruction.Line) + { + prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE; + } + } + + bool newDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(instruction.File, StringComparison.Ordinal); + + if (!string.IsNullOrWhiteSpace(instruction.File)) + { + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) + { + if (newDocument || idx == 0) { - dwLine = instruction.Line - }; + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; + prgDisassembly[iOp].bstrDocumentUrl = string.Concat("file://", instruction.File); + } } } + m_dwLastSourceLine = (int)instruction.Line; + if (newDocument) + { + prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_DOCUMENTCHANGE; + _pLastDocument = instruction.File; + } + iOp++; }; @@ -253,6 +286,10 @@ private int SeekBack(long iInstructions) public int Seek(enum_SEEK_START dwSeekStart, IDebugCodeContext2 pCodeContext, ulong uCodeLocationId, long iInstructions) { + _pLastDocument = null; + m_lastSourceInfoStale = true; + m_dwLastSourceLine = -1; + if (dwSeekStart == enum_SEEK_START.SEEK_START_CODECONTEXT) { AD7MemoryAddress addr = pCodeContext as AD7MemoryAddress; diff --git a/src/MIDebugEngine/Engine.Impl/Disassembly.cs b/src/MIDebugEngine/Engine.Impl/Disassembly.cs index 9c74d4bce..5d5215fb9 100644 --- a/src/MIDebugEngine/Engine.Impl/Disassembly.cs +++ b/src/MIDebugEngine/Engine.Impl/Disassembly.cs @@ -307,15 +307,17 @@ private void DeleteRangeFromCache(DisassemblyBlock block) private class DisasmAddressRange { + public readonly string Symbol; public ulong StartAddress; public ulong EndAddress; - Dictionary AddressToInstruction; + private readonly Dictionary _AddressToInstruction; public DisasmAddressRange(DisasmInstruction instruction) { + Symbol = instruction.Symbol; StartAddress = instruction.Addr; - EndAddress = instruction.Addr; - AddressToInstruction = new Dictionary() + EndAddress = instruction.Addr + 1; + _AddressToInstruction = new Dictionary() { {instruction.Addr, instruction} }; @@ -323,8 +325,8 @@ public DisasmAddressRange(DisasmInstruction instruction) public void UpdateEndAddress(DisasmInstruction instruction) { - EndAddress = instruction.Addr; - AddressToInstruction.Add(instruction.Addr, instruction); + EndAddress = instruction.Addr + 1; + _AddressToInstruction.Add(instruction.Addr, instruction); } public void MapSourceToInstructions(DebuggedProcess process, TupleValue[] src_and_asm_lines) @@ -362,8 +364,8 @@ public void MapSourceToInstructions(DebuggedProcess process, TupleValue[] src_an foreach (ResultValue line_asm_insn in line_asm_instructions.Content) { ulong address = line_asm_insn.FindAddr("address"); - AddressToInstruction[address].File = file; - AddressToInstruction[address].Line = line; + _AddressToInstruction[address].File = file; + _AddressToInstruction[address].Line = line; } } } @@ -387,66 +389,56 @@ internal static async Task Disassemble(DebuggedProcess proc if (instructions != null && instructions.Length != 0) { + IList ranges = new List(); // Map 'Symbol' (Function Name) to Address Range - Dictionary funcToAddressRange = new Dictionary(); - for (int i = 0; i < instructions.Length; i++) + DisasmAddressRange currentRange = new DisasmAddressRange(instructions[0]); + for (int i = 1; i < instructions.Length; i++) { - string functionName = instructions[i].Symbol; - if (funcToAddressRange.ContainsKey(functionName)) + if (currentRange.Symbol == instructions[i].Symbol) { - // Update the end address with this current instruction. - funcToAddressRange[functionName].UpdateEndAddress(instructions[i]); + currentRange.UpdateEndAddress(instructions[i]); } else { - // Insert new function to keep track of. - funcToAddressRange.Add(functionName, new DisasmAddressRange(instructions[i])); - } + ranges.Add(currentRange); - // endAddr will not show up in instructions, so make the last instruction we get to get the range til the end. - if (i == instructions.Length - 1) - { - funcToAddressRange[functionName].EndAddress = endAddr; + // Start new range + currentRange = new DisasmAddressRange(instructions[i]); } } - foreach (string functionName in funcToAddressRange.Keys) - { - DisasmAddressRange range = funcToAddressRange[functionName]; + // Add the last range + ranges.Add(currentRange); + foreach (DisasmAddressRange dismAddressRange in ranges) + { // Mode 5 - mixed source and disassembly with raw opcodes - cmd = "-data-disassemble -s " + EngineUtils.AsAddr(range.StartAddress, process.Is64BitArch) + " -e " + EngineUtils.AsAddr(range.EndAddress, process.Is64BitArch) + " -- 5"; + cmd = "-data-disassemble -s " + EngineUtils.AsAddr(dismAddressRange.StartAddress, process.Is64BitArch) + " -e " + EngineUtils.AsAddr(dismAddressRange.EndAddress, process.Is64BitArch) + " -- 5"; results = await process.CmdAsync(cmd, ResultClass.None); if (results.ResultClass != ResultClass.done) { return null; } - try + + /* Example response + * asm_insns=[ + * src_and_asm_line={ + * line="15", + * file="main.cpp", + * fullname="/home/cpp/main.cpp", + * line_asm_insn=[ ... ] + * } + * ] + */ + ResultListValue asm_insns = results.TryFind("asm_insns"); + if (asm_insns != null) { - /* Example response - * asm_insns=[ - * src_and_asm_line={ - * line="15", - * file="main.cpp", - * fullname="/home/cpp/main.cpp", - * line_asm_insn=[ ... ] - * } - * ] - */ - ResultListValue asm_insns = results.Find("asm_insns"); - if (asm_insns != null) + TupleValue[] values = asm_insns.FindAll("src_and_asm_line"); + if (values != null) { - TupleValue[] values = asm_insns.FindAll("src_and_asm_line"); - if (values != null) - { - range.MapSourceToInstructions(process, values); - } + dismAddressRange.MapSourceToInstructions(process, values); } } - catch - { - // asm_insns can be ValueListValue if it does not contain source information. - } } } diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index ddab6696b..4934e9dca 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -2064,12 +2064,9 @@ protected override void HandleDisassembleRequestAsync(IRequestResponder Date: Tue, 16 Nov 2021 15:03:59 -0800 Subject: [PATCH 3/6] Addressing PR issues --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 170 ++++++++++--------- 1 file changed, 93 insertions(+), 77 deletions(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index 9f3cc4f47..c6dc48c93 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -17,6 +17,7 @@ internal class AD7DisassemblyStream : IDebugDisassemblyStream2 private string _pLastDocument; private int m_dwLastSourceLine; private bool m_lastSourceInfoStale; + private bool m_skipNextInstruction; internal AD7DisassemblyStream(AD7Engine engine, enum_DISASSEMBLY_STREAM_SCOPE scope, IDebugCodeContext2 pCodeContext) { @@ -101,6 +102,16 @@ private DisassemblyData FetchBadInstruction(enum_DISASSEMBLY_STREAM_FIELDS dwFie public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, out uint pdwInstructionsRead, DisassemblyData[] prgDisassembly) { + if (m_lastSourceInfoStale && !m_skipNextInstruction) + { + SeekBack(1); + } + + if (m_skipNextInstruction) + { + dwInstructions += 1; + } + uint iOp = 0; IEnumerable instructions = null; @@ -125,114 +136,114 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou prgDisassembly[iOp] = FetchBadInstruction(dwFields); } - IEnumerator instructionEnumerator = instructions.GetEnumerator(); - for (int idx = 0; idx < dwInstructions; idx++) + using (IEnumerator instructionEnumerator = instructions.GetEnumerator()) { - instructionEnumerator.MoveNext(); - DisasmInstruction instruction = instructionEnumerator.Current; - - if (iOp >= dwInstructions) + int idx = 0; + if (m_skipNextInstruction) { - break; - } - _addr = instruction.Addr; + instructionEnumerator.MoveNext(); + DisasmInstruction instruction = instructionEnumerator.Current; - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS) != 0) - { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS; - prgDisassembly[iOp].bstrAddress = instruction.AddressString; - } + m_dwLastSourceLine = (int)instruction.Line - 1; + _pLastDocument = string.Concat("file://", instruction.File); - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID) != 0) - { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID; - prgDisassembly[iOp].uCodeLocationId = instruction.Addr; + idx++; } - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL) != 0) + for (; idx < dwInstructions; idx++) { - if (instruction.Offset == 0) + instructionEnumerator.MoveNext(); + DisasmInstruction instruction = instructionEnumerator.Current; + + if (iOp >= dwInstructions) { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL; - prgDisassembly[iOp].bstrSymbol = instruction.Symbol ?? string.Empty; + break; } - } + _addr = instruction.Addr; - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE) != 0) - { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE; - prgDisassembly[iOp].bstrOpcode = instruction.Opcode; - } - - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODEBYTES) != 0) - { - if (!string.IsNullOrWhiteSpace(instruction.CodeBytes)) + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS) != 0) { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODEBYTES; - prgDisassembly[iOp].bstrCodeBytes = instruction.CodeBytes; + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS; + prgDisassembly[iOp].bstrAddress = instruction.AddressString; } - } - - if (idx == 0 && m_lastSourceInfoStale) - { - m_lastSourceInfoStale = false; - ulong previousInstructionAddress = instruction.Addr - 1; + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID) != 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODELOCATIONID; + prgDisassembly[iOp].uCodeLocationId = instruction.Addr; + } - if (previousInstructionAddress >= 0 && previousInstructionAddress < (ulong)prgDisassembly.Length) + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL) != 0) { - DisassemblyData previousInstruction = prgDisassembly[previousInstructionAddress]; - m_dwLastSourceLine = (int)previousInstruction.posBeg.dwLine - 1; - _pLastDocument = previousInstruction.bstrDocumentUrl; + if (instruction.Offset == 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_SYMBOL; + prgDisassembly[iOp].bstrSymbol = instruction.Symbol ?? string.Empty; + } } - } - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0) - { - uint startLine = instruction.Line - 1; + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE) != 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE; + prgDisassembly[iOp].bstrOpcode = instruction.Opcode; + } - prgDisassembly[iOp].posBeg = new TEXT_POSITION() + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODEBYTES) != 0) { - dwLine = startLine, - dwColumn = 0 - }; - prgDisassembly[iOp].posEnd = new TEXT_POSITION() + if (!string.IsNullOrWhiteSpace(instruction.CodeBytes)) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_CODEBYTES; + prgDisassembly[iOp].bstrCodeBytes = instruction.CodeBytes; + } + } + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && instruction.Line != 0) { - dwLine = instruction.Line, - dwColumn = 0 - }; + uint startLine = instruction.Line - 1; + + prgDisassembly[iOp].posBeg = new TEXT_POSITION() + { + dwLine = startLine, + dwColumn = 0 + }; + prgDisassembly[iOp].posEnd = new TEXT_POSITION() + { + dwLine = instruction.Line, + dwColumn = 0 + }; - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; - if (m_dwLastSourceLine != instruction.Line) - { - prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE; + if (m_dwLastSourceLine != instruction.Line && instruction.Line != 0) + { + prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE; + } } - } - bool newDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(instruction.File, StringComparison.Ordinal); + bool newDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(instruction.File, StringComparison.Ordinal); - if (!string.IsNullOrWhiteSpace(instruction.File)) - { - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) + if (!string.IsNullOrWhiteSpace(instruction.File)) { - if (newDocument || idx == 0) + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) { - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; - prgDisassembly[iOp].bstrDocumentUrl = string.Concat("file://", instruction.File); + if (newDocument || idx == 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; + prgDisassembly[iOp].bstrDocumentUrl = string.Concat("file://", instruction.File); + } } } - } - m_dwLastSourceLine = (int)instruction.Line; - if (newDocument) - { - prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_DOCUMENTCHANGE; - _pLastDocument = instruction.File; - } + m_dwLastSourceLine = (int)instruction.Line; + if (newDocument) + { + prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_DOCUMENTCHANGE; + _pLastDocument = instruction.File; + } - iOp++; - }; + iOp++; + } + } if (iOp < dwInstructions) { @@ -277,6 +288,11 @@ private int SeekForward(long iInstructions) private int SeekBack(long iInstructions) { + if (m_lastSourceInfoStale) + { + iInstructions += 1; + m_skipNextInstruction = true; + } _engine.DebuggedProcess.WorkerThread.RunOperation(async () => { _addr = await _engine.DebuggedProcess.Disassembly.SeekBack(_addr, (int)iInstructions); From 143435d64c47e6681c94e82de3dfa4066caf7ad4 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 23 Nov 2021 14:43:11 -0800 Subject: [PATCH 4/6] Show single line source --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 92 ++++++++++++-------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index c6dc48c93..d29515e15 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -15,9 +15,9 @@ internal class AD7DisassemblyStream : IDebugDisassemblyStream2 private ulong _addr; private enum_DISASSEMBLY_STREAM_SCOPE _scope; private string _pLastDocument; - private int m_dwLastSourceLine; - private bool m_lastSourceInfoStale; - private bool m_skipNextInstruction; + private uint _dwLastSourceLine; + private bool _lastSourceInfoStale; + private bool _skipNextInstruction; internal AD7DisassemblyStream(AD7Engine engine, enum_DISASSEMBLY_STREAM_SCOPE scope, IDebugCodeContext2 pCodeContext) { @@ -26,8 +26,8 @@ internal AD7DisassemblyStream(AD7Engine engine, enum_DISASSEMBLY_STREAM_SCOPE sc AD7MemoryAddress addr = pCodeContext as AD7MemoryAddress; _addr = addr.Address; _pLastDocument = null; - m_dwLastSourceLine = -1; - m_lastSourceInfoStale = true; + _dwLastSourceLine = 0; + _lastSourceInfoStale = true; } #region IDebugDisassemblyStream2 Members @@ -102,12 +102,12 @@ private DisassemblyData FetchBadInstruction(enum_DISASSEMBLY_STREAM_FIELDS dwFie public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, out uint pdwInstructionsRead, DisassemblyData[] prgDisassembly) { - if (m_lastSourceInfoStale && !m_skipNextInstruction) + if (_lastSourceInfoStale && !_skipNextInstruction) { SeekBack(1); } - if (m_skipNextInstruction) + if (_skipNextInstruction) { dwInstructions += 1; } @@ -124,8 +124,17 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou // bad address range, return '??' for (iOp = 0; iOp < dwInstructions; _addr++, ++iOp) { + if (_skipNextInstruction) + { + _addr++; + dwInstructions--; + _skipNextInstruction = false; + } prgDisassembly[iOp] = FetchBadInstruction(dwFields); } + _lastSourceInfoStale = false; + _dwLastSourceLine = 0; + _pLastDocument = null; pdwInstructionsRead = iOp; return Constants.S_OK; } @@ -139,26 +148,25 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou using (IEnumerator instructionEnumerator = instructions.GetEnumerator()) { int idx = 0; - if (m_skipNextInstruction) + if (_skipNextInstruction) { + _skipNextInstruction = false; + instructionEnumerator.MoveNext(); DisasmInstruction instruction = instructionEnumerator.Current; - m_dwLastSourceLine = (int)instruction.Line - 1; - _pLastDocument = string.Concat("file://", instruction.File); + _dwLastSourceLine = instruction.Line != 0 ? instruction.Line - 1 : 0; + _pLastDocument = instruction.File; + dwInstructions--; idx++; } - for (; idx < dwInstructions; idx++) + for (; idx < dwInstructions + 1 && iOp < dwInstructions; idx++) { instructionEnumerator.MoveNext(); DisasmInstruction instruction = instructionEnumerator.Current; - if (iOp >= dwInstructions) - { - break; - } _addr = instruction.Addr; if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_ADDRESS) != 0) @@ -197,45 +205,59 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou } } - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && instruction.Line != 0) + + string currentFile = instruction.File; + uint currentLine = instruction.Line - 1; + bool isNewDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(currentFile, StringComparison.Ordinal); + bool isNewLine = currentLine != _dwLastSourceLine; + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && currentLine != 0) { - uint startLine = instruction.Line - 1; + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; prgDisassembly[iOp].posBeg = new TEXT_POSITION() { - dwLine = startLine, + dwLine = currentLine, dwColumn = 0 }; prgDisassembly[iOp].posEnd = new TEXT_POSITION() { - dwLine = instruction.Line, + dwLine = currentLine, dwColumn = 0 }; - prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; - - if (m_dwLastSourceLine != instruction.Line && instruction.Line != 0) - { - prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE; - } + // Update last seen source line. + _dwLastSourceLine = currentLine; } - bool newDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(instruction.File, StringComparison.Ordinal); + // Show source if we have line and file information and if there is a new line. + if (currentLine != 0 && currentFile != null && isNewLine) + { + prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_HASSOURCE; + } - if (!string.IsNullOrWhiteSpace(instruction.File)) + if (!string.IsNullOrWhiteSpace(currentFile)) { if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL) != 0) { - if (newDocument || idx == 0) + // idx 0 is the previous instruction. The first requested instruction + // is at idx 1. + if (isNewDocument || idx == 1) { prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_DOCUMENTURL; - prgDisassembly[iOp].bstrDocumentUrl = string.Concat("file://", instruction.File); + prgDisassembly[iOp].bstrDocumentUrl = string.Concat("file://", currentFile); } } } - m_dwLastSourceLine = (int)instruction.Line; - if (newDocument) + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_BYTEOFFSET) != 0) + { + prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_BYTEOFFSET; + // For the disassembly window, offset should be set to 0 to show source and non-zero to hide it. + prgDisassembly[iOp].dwByteOffset = isNewLine ? 0 : uint.MaxValue; + } + + if (isNewDocument) { prgDisassembly[iOp].dwFlags |= enum_DISASSEMBLY_FLAGS.DF_DOCUMENTCHANGE; _pLastDocument = instruction.File; @@ -288,10 +310,10 @@ private int SeekForward(long iInstructions) private int SeekBack(long iInstructions) { - if (m_lastSourceInfoStale) + if (_lastSourceInfoStale) { iInstructions += 1; - m_skipNextInstruction = true; + _skipNextInstruction = true; } _engine.DebuggedProcess.WorkerThread.RunOperation(async () => { @@ -303,8 +325,8 @@ private int SeekBack(long iInstructions) public int Seek(enum_SEEK_START dwSeekStart, IDebugCodeContext2 pCodeContext, ulong uCodeLocationId, long iInstructions) { _pLastDocument = null; - m_lastSourceInfoStale = true; - m_dwLastSourceLine = -1; + _lastSourceInfoStale = true; + _dwLastSourceLine = 0; if (dwSeekStart == enum_SEEK_START.SEEK_START_CODECONTEXT) { From f10e4b663a37f25b7ef0c92ebb4542fbedfb8748 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 23 Nov 2021 15:09:16 -0800 Subject: [PATCH 5/6] Handle showing multi-source line code --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index d29515e15..3e774fd43 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -213,11 +213,15 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && currentLine != 0) { + // If we have a new line and the current line is greater than the previously seen source line. + // Try to grab the last seen source line + 1 and show a group of source code. + // Else, just show the single line. + uint startLine = isNewLine && currentLine > _dwLastSourceLine ? _dwLastSourceLine + 1 : currentLine; prgDisassembly[iOp].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION; prgDisassembly[iOp].posBeg = new TEXT_POSITION() { - dwLine = currentLine, + dwLine = startLine, dwColumn = 0 }; prgDisassembly[iOp].posEnd = new TEXT_POSITION() From b7870f554426b4b15d4806d3c0767d1d20375dc8 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Mon, 13 Dec 2021 19:07:02 -0800 Subject: [PATCH 6/6] Handle out of order file lines --- src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs index 3e774fd43..cef41e69e 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Disassembly.cs @@ -211,7 +211,22 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou bool isNewDocument = string.IsNullOrEmpty(_pLastDocument) || !_pLastDocument.Equals(currentFile, StringComparison.Ordinal); bool isNewLine = currentLine != _dwLastSourceLine; - if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && currentLine != 0) + /* GDB will return a lines of sources not in sequential order. + * We ignore lines with lower line numbers and have it captured in a different group of source. + * + * Example MI Response: + * src_and_asm_line={line="22",file="main.cpp",fullname="/home/waan/cpp/main.cpp",line_asm_insn=[ + * {address="0x00007fc4b52b54c5",func-name="main(int, char**)",offset="124",opcodes="48 8d 95 40 ff ff ff",inst="lea -0xc0(%rbp),%rdx" } + * ] + * },src_and_asm_line={line="21",file="main.cpp",fullname="/home/waan/cpp/main.cpp",line_asm_insn=[ + * {address="0x00007fc4b52b54df",func-name="main(int, char**)",offset="150",opcodes="c7 85 48 ff ff ff 02 00 00 00",inst="movl $0x2,-0xb8(%rbp)"} + * ] + * },src_and_asm_line={line="22",file="main.cpp",fullname="/home/waan/cpp/main.cpp",line_asm_insn=[ + * {address="0x00007fc4b52b54e9",func-name="main(int, char**)",offset="160",opcodes="48 8d 85 48 ff ff ff",inst="lea -0xb8(%rbp),%rax"} + * } + */ + + if ((dwFields & enum_DISASSEMBLY_STREAM_FIELDS.DSF_POSITION) != 0 && currentLine != 0 && currentLine >= _dwLastSourceLine) { // If we have a new line and the current line is greater than the previously seen source line. // Try to grab the last seen source line + 1 and show a group of source code.