diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx index 787a4f0294..f29cd9261e 100644 --- a/data/resources/StringResources.resx +++ b/data/resources/StringResources.resx @@ -5728,6 +5728,15 @@ Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler, WixNetFxExtension< Object Graph + + Memory + + + Jump to address: + + + Refresh + Parallel Stacks diff --git a/data/resources/image/BitmapResources/BitmapResources.res b/data/resources/image/BitmapResources/BitmapResources.res index 1cb487ede6..3e8469ea86 100644 --- a/data/resources/image/BitmapResources/BitmapResources.res +++ b/data/resources/image/BitmapResources/BitmapResources.res @@ -46,6 +46,8 @@ ParallelStacks.ZoomControl = PadIcons\ZoomControl.png OutputPad.Toolbar.ClearOutputWindow = OutputPadIcons\ClearOutputWindow.png OutputPad.Toolbar.ToggleWordWrap = OutputPadIcons\ToggleWordWrap.png +#Memory pad +MemoryPad.Icon = PadIcons\memory.png Icons.16x16.OpenFolderBitmap = ProjectBrowserIcons\Folder.Open.png Icons.16x16.ClosedFolderBitmap = ProjectBrowserIcons\Folder.Closed.png diff --git a/data/resources/image/BitmapResources/PadIcons/memory.png b/data/resources/image/BitmapResources/PadIcons/memory.png new file mode 100644 index 0000000000..879019b26b Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/memory.png differ diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin index 210f63b594..28c0df125a 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin +++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin @@ -117,6 +117,13 @@ class = "ICSharpCode.SharpDevelop.Gui.Pads.ConsolePad" defaultPosition = "Bottom, Hidden" /> + + + + + + + + + diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj index 6de0fd70b0..ddced0ecf8 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj +++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj @@ -102,6 +102,7 @@ CallStackPad.xaml Code + @@ -120,6 +121,7 @@ WatchListAutoCompleteCell.xaml + DrawSurface.xaml Code diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs new file mode 100644 index 0000000000..8b37bbffb0 --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs @@ -0,0 +1,64 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Windows.Controls; +using System.Windows.Input; + +using ICSharpCode.Core; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + public sealed class JumpToAddressCommand : AbstractComboBoxCommand + { + MemoryPad pad; + ComboBox comboBox; + + protected override void OnOwnerChanged(EventArgs e) + { + this.pad = this.Owner as MemoryPad; + if (this.pad == null) + return; + + comboBox = this.ComboBox as ComboBox; + + if (this.comboBox == null) + return; + + comboBox.KeyUp += (s, ea) => { if (ea.Key == Key.Enter) Run(); }; + comboBox.IsEditable = true; + comboBox.Width = 130; + + base.OnOwnerChanged(e); + } + + public override void Run() + { + if (this.pad != null && this.comboBox != null) { + pad.JumpToAddress(comboBox.Text); + } + base.Run(); + } + } + + public sealed class RefreshMemoryCommand : AbstractCommand + { + MemoryPad pad; + + protected override void OnOwnerChanged(EventArgs e) + { + this.pad = this.Owner as MemoryPad; + if (this.pad == null) + return; + + base.OnOwnerChanged(e); + } + + public override void Run() + { + if (this.pad == null) + return; + + this.pad.Refresh(true); + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs new file mode 100644 index 0000000000..1ba1f5735d --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs @@ -0,0 +1,169 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; + +using Debugger; +using Debugger.Interop; +using ICSharpCode.Core; +using ICSharpCode.Core.Presentation; +using ICSharpCode.SharpDevelop.Debugging; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + public sealed class MemoryPad : DebuggerPad + { + Dictionary addressesMapping = new Dictionary(); + ConsoleControl console; + int addressStep = 16; + + Process debuggedProcess; + + public MemoryPad() + { + this.console = new ConsoleControl(); + this.panel.Children.Add(console); + this.console.Encoding = Encoding.Default; + RefreshPad(); + this.console.SetReadonly(); + } + + protected override ToolBar BuildToolBar() + { + return ToolBarService.CreateToolBar(panel, this, "/SharpDevelop/Pads/MemoryPad/ToolBar"); + } + + protected override void SelectProcess(Process process) + { + if (debuggedProcess != null) { + debuggedProcess.Paused -= OnProcessPaused; + } + debuggedProcess = process; + if (debuggedProcess != null) { + debuggedProcess.Paused += OnProcessPaused; + } + } + + public override void RefreshPad() + { + Refresh(); + base.RefreshPad(); + } + + public void JumpToAddress(string address) + { + try { + if (address.StartsWith("0x")) + address = address.Substring(2); + + long addr = Int64.Parse(address, NumberStyles.AllowHexSpecifier); + long mod = addr % addressStep; + + int line; + if (addressesMapping.ContainsKey(addr - mod)) + line = addressesMapping[addr - mod]; + else + line = 1; + + console.SelectText(line, 0, 8); + console.JumpToLine(line); + } catch (System.Exception ex) { + #if DEBUG + LoggingService.Error(ex.Message); + #endif + } + } + + public void Refresh(bool force = false) + { + if (debuggedProcess == null || debugger.IsProcessRunning) + return; + + if (!force && addressesMapping.Count > 0) + return; + + if (force) { + addressesMapping.Clear(); + console.Clear(); + } + + long address; + byte[] memory = debuggedProcess.ReadProcessMemory(out address); + + if (memory == null) + return; + + int index = 0; + int div = memory.Length / addressStep; + int mod = memory.Length % addressStep; + + while (index < div) { + StringBuilder sb = new StringBuilder(); + addressesMapping.Add(address, index + 1); + // write address + sb.Append(address.ToString("X8"));address += addressStep; + sb.Append(" "); + + // write bytes + for (int i = 0; i < addressStep; ++i) { + sb.Append(memory[index * addressStep + i].ToString("X2") + " "); + } + // write chars + StringBuilder sb1 = new StringBuilder(); + for (int i = 0; i < addressStep; ++i) { + sb1.Append(((char)memory[index * addressStep + i]).ToString()); + } + string s = sb1.ToString(); + s = Regex.Replace(s, @"\r\n", string.Empty); + s = Regex.Replace(s, @"\n", string.Empty); + s = Regex.Replace(s, @"\r", string.Empty); + sb.Append(s); + sb.Append(Environment.NewLine); + + // start writing in console + console.Append(sb.ToString()); + + index++; + } + + if (mod != 0) { + // write the rest of memory + StringBuilder sb = new StringBuilder(); + addressesMapping.Add(address, index + 1); + // write address + sb.Append(address.ToString("X8")); + sb.Append(" "); + + // write bytes + for (int i = 0; i < mod; ++i) { + sb.Append(memory[index * addressStep + i].ToString("X2") + " "); + } + // write chars + StringBuilder sb1 = new StringBuilder(); + for (int i = 0; i < mod; ++i) { + sb1.Append(((char)memory[index * addressStep + i]).ToString()); + } + string s = sb1.ToString(); + s = Regex.Replace(s, @"\r\n", string.Empty); + s = Regex.Replace(s, @"\n", string.Empty); + s = Regex.Replace(s, @"\r", string.Empty); + sb.Append(s); + + sb.Append(Environment.NewLine); + + // start writing in console + console.Append(sb.ToString()); + } + } + + private void OnProcessPaused(object sender, ProcessEventArgs e) + { + Refresh(); + } + } +} diff --git a/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs b/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs index 0a541f1e61..5c99cf079c 100644 --- a/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs +++ b/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs @@ -7,8 +7,37 @@ using System; using System.Runtime.InteropServices; using System.Text; +using Debugger.Interop.CorDebug; + namespace Debugger.Interop -{ +{ + [StructLayout(LayoutKind.Sequential)] + public struct MEMORY_BASIC_INFORMATION + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public uint AllocationProtect; + public IntPtr RegionSize; + public uint State; + public uint Protect; + public uint Type; + } + + [Flags] + public enum ProcessAccessFlags : uint + { + All = 0x001F0FFF, + Terminate = 0x00000001, + CreateThread = 0x00000002, + VMOperation = 0x00000008, + VMRead = 0x00000010, + VMWrite = 0x00000020, + DupHandle = 0x00000040, + SetInformation = 0x00000200, + QueryInformation = 0x00000400, + Synchronize = 0x00100000 + } + public static class NativeMethods { [DllImport("kernel32.dll")] @@ -22,6 +51,69 @@ namespace Debugger.Interop [DllImport("mscoree.dll", CharSet=CharSet.Unicode)] public static extern int GetRequestedRuntimeVersion(string exeFilename, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pVersion, Int32 cchBuffer, out Int32 dwLength); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool VirtualQueryEx(IntPtr hProcess, + IntPtr lpAddress, + out MEMORY_BASIC_INFORMATION lpBuffer, + uint dwLength); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, + UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool ReadProcessMemory( + IntPtr hProcess, + IntPtr lpBaseAddress, + [Out] byte[] lpBuffer, + int dwSize, + out int lpNumberOfBytesRead + ); + + public static byte[] ReadProcessMemory(this Process process, out long baseAddress) + { + uint handle = process.CorProcess.GetHandle(); + + var proc = System.Diagnostics.Process.GetProcessById((int)process.Id); + baseAddress = proc.MainModule.BaseAddress.ToInt64(); + long addr = baseAddress; + + byte[] memory = null; + + while (true) + { + byte[] temp = new byte[1024]; + int outSize; + bool success = ReadProcessMemory(new IntPtr(handle), new IntPtr(addr), temp, temp.Length, out outSize); + + addr += 1024; + + if (outSize == 0) + break; + + if (memory == null) { + memory = new byte[outSize]; + Array.Copy(temp, memory, outSize); + } else { + // expand memory + byte[] newTemp = new byte[memory.Length]; + Array.Copy(memory, newTemp, memory.Length); + + memory = new byte[memory.Length + outSize]; + Array.Copy(newTemp, memory, newTemp.Length); + Array.Copy(temp, 0, memory, newTemp.Length, outSize); + } + + if (!success) // break when we cannot read anymore + break; + } + + return memory; + } } } diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs index eec5f36165..b788876e84 100644 --- a/src/AddIns/Debugger/Debugger.Core/Process.cs +++ b/src/AddIns/Debugger/Debugger.Core/Process.cs @@ -362,6 +362,10 @@ namespace Debugger get { return pauseSession == null; } } + public uint Id { + get { return corProcess.GetID(); } + } + public bool IsPaused { get { return !IsRunning; } } diff --git a/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs b/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs index 762743bf1a..a1e0e5208a 100755 --- a/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs @@ -1,19 +1,21 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) -using ICSharpCode.AvalonEdit; -using ICSharpCode.Core.Presentation; using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; + +using ICSharpCode.AvalonEdit; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Core; +using ICSharpCode.Core.Presentation; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.AvalonEdit; @@ -303,6 +305,21 @@ namespace ICSharpCode.SharpDevelop.Gui } } + public Encoding Encoding { + get { + return this.editor.Encoding; + } + set { + this.editor.Encoding = value; + } + } + + public void SelectText(int line, int column, int length) + { + int offset = this.editor.Document.GetOffset(new TextLocation(line, column)); + this.editor.Select(offset, length); + } + public void SetHighlighting(string language) { editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition(language); @@ -338,6 +355,11 @@ namespace ICSharpCode.SharpDevelop.Gui this.editor.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; } + public void JumpToLine(int line) + { + this.editor.ScrollToLine(line); + } + public int CommandOffset { get { return readOnlyRegion.EndOffset; } } @@ -369,6 +391,11 @@ namespace ICSharpCode.SharpDevelop.Gui if (handler != null) handler(this, e); } + + public void Clear() + { + editor.Clear(); + } } public class BeginReadOnlySectionProvider : IReadOnlySectionProvider diff --git a/src/Main/StartUp/Project/Resources/BitmapResources.resources b/src/Main/StartUp/Project/Resources/BitmapResources.resources index 31f13c839a..929a830270 100644 Binary files a/src/Main/StartUp/Project/Resources/BitmapResources.resources and b/src/Main/StartUp/Project/Resources/BitmapResources.resources differ