Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Karlaš <david.karlas@xamarin.com>2015-01-14 22:31:49 +0300
committerDavid Karlaš <david.karlas@xamarin.com>2015-01-14 22:32:04 +0300
commitaf2ecb3305d213b204c229856b6000e45ffc2ffe (patch)
tree26ac3ede906e10a2f09fab5b69af07b75581b139 /main/src/addins/MonoDevelop.Debugger
parent8750ddd3794d7817bc41e479951f00755dc9db9f (diff)
Bug 2852 - Debugger cannot remap source paths
Diffstat (limited to 'main/src/addins/MonoDevelop.Debugger')
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj1
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs180
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs34
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs128
4 files changed, 325 insertions, 18 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
index 01ef9011cf..8f2307d023 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
@@ -148,6 +148,7 @@
<Compile Include="MonoDevelop.Debugger\DebuggerEngineBackend.cs" />
<Compile Include="MonoDevelop.Debugger\BreakpointPropertiesDialog.cs" />
<Compile Include="MonoDevelop.Debugger\TextEntryWithCodeCompletion.cs" />
+ <Compile Include="MonoDevelop.Debugger\SourceCodeLookup.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MonoDevelop.Debugger.addin.xml">
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
index 19a1df9ded..90973bb76f 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
@@ -39,6 +39,12 @@ using TextEditor = Mono.TextEditor.TextEditor;
using Mono.TextEditor;
using Mono.Debugging.Client;
using Mono.TextEditor.Highlighting;
+using Gtk;
+using MonoDevelop.Ide.Gui.Dialogs;
+using MonoDevelop.Ide;
+using System.Security.Cryptography;
+using Gdk;
+using MonoDevelop.Components;
namespace MonoDevelop.Debugger
{
@@ -87,6 +93,68 @@ namespace MonoDevelop.Debugger
DebuggingService.StoppedEvent += OnStop;
}
+
+ OverlayMessageWindow messageOverlayWindow;
+
+ void ShowLoadSourceFile (StackFrame sf)
+ {
+ if (messageOverlayWindow != null) {
+ messageOverlayWindow.Destroy ();
+ messageOverlayWindow = null;
+ }
+ messageOverlayWindow = new OverlayMessageWindow ();
+
+ var hbox = new HBox ();
+ hbox.Spacing = 8;
+ var label = new Label (string.Format ("{0} not found. Find source file at alternative location.", Path.GetFileName (sf.SourceLocation.FileName)));
+ hbox.TooltipText = sf.SourceLocation.FileName;
+ var color = (HslColor)editor.ColorStyle.NotificationText.Foreground;
+ label.ModifyFg (StateType.Normal, color);
+
+ int w, h;
+ label.Layout.GetPixelSize (out w, out h);
+
+ hbox.PackStart (label, true, true, 0);
+ var openButton = new Button (Gtk.Stock.Open);
+ openButton.WidthRequest = 60;
+ hbox.PackEnd (openButton, false, false, 0);
+
+ var container = new HBox ();
+ const int containerPadding = 8;
+ container.PackStart (hbox, true, true, containerPadding);
+ messageOverlayWindow.Child = container;
+ messageOverlayWindow.ShowOverlay (editor);
+
+ messageOverlayWindow.SizeFunc = () => openButton.SizeRequest ().Width + w + hbox.Spacing * 5 + containerPadding * 2;
+ openButton.Clicked += delegate {
+ var dlg = new OpenFileDialog (GettextCatalog.GetString ("File to Open"), Gtk.FileChooserAction.Open) {
+ TransientFor = IdeApp.Workbench.RootWindow,
+ ShowEncodingSelector = true,
+ ShowViewerSelector = true
+ };
+ if (!dlg.Run ())
+ return;
+ var newFilePath = dlg.SelectedFile;
+ try {
+ if (File.Exists (newFilePath)) {
+ if (SourceCodeLookup.CheckFileMd5 (newFilePath, sf.SourceLocation.FileHash)) {
+ SourceCodeLookup.AddLoadedFile (newFilePath, sf.SourceLocation.FileName);
+ sf.UpdateSourceFile (newFilePath);
+ if (IdeApp.Workbench.OpenDocument (newFilePath, null, sf.SourceLocation.Line, 1, OpenDocumentOptions.Debugger) != null) {
+ this.WorkbenchWindow.CloseWindow (false);
+ }
+ } else {
+ MessageService.ShowWarning ("File checksum doesn't match.");
+ }
+ } else {
+ MessageService.ShowWarning ("File not found.");
+ }
+ } catch (Exception) {
+ MessageService.ShowWarning ("Error opening file");
+ }
+ };
+ }
+
public override string TabPageLabel {
get {
return GettextCatalog.GetString ("Disassembly");
@@ -116,13 +184,24 @@ namespace MonoDevelop.Debugger
editor.Document.RemoveMarker (currentDebugLineMarker);
if (DebuggingService.CurrentFrame == null) {
+ if (messageOverlayWindow != null) {
+ messageOverlayWindow.Destroy ();
+ messageOverlayWindow = null;
+ }
sw.Sensitive = false;
return;
}
sw.Sensitive = true;
-
- StackFrame sf = DebuggingService.CurrentFrame;
+ var sf = DebuggingService.CurrentFrame;
+ if (!string.IsNullOrWhiteSpace (sf.SourceLocation.FileName) && sf.SourceLocation.Line != -1 && sf.SourceLocation.FileHash != null) {
+ ShowLoadSourceFile (sf);
+ } else {
+ if (messageOverlayWindow != null) {
+ messageOverlayWindow.Destroy ();
+ messageOverlayWindow = null;
+ }
+ }
if (!string.IsNullOrEmpty (sf.SourceLocation.FileName) && File.Exists (sf.SourceLocation.FileName))
FillWithSource ();
else
@@ -324,6 +403,10 @@ namespace MonoDevelop.Debugger
{
addressLines.Clear ();
currentFile = null;
+ if (messageOverlayWindow != null) {
+ messageOverlayWindow.Destroy ();
+ messageOverlayWindow = null;
+ }
sw.Sensitive = false;
autoRefill = false;
editor.Document.Text = string.Empty;
@@ -420,4 +503,97 @@ namespace MonoDevelop.Debugger
return st;
}
}
+
+ //Copy pasted from SourceEditor
+ class OverlayMessageWindow : Gtk.EventBox
+ {
+ const int border = 8;
+
+ public Func<int> SizeFunc;
+
+ TextEditor textEditor;
+
+ public OverlayMessageWindow ()
+ {
+ AppPaintable = true;
+ }
+
+ public void ShowOverlay (TextEditor textEditor)
+ {
+ this.textEditor = textEditor;
+ this.ShowAll ();
+ textEditor.AddTopLevelWidget (this, 0, 0);
+ textEditor.SizeAllocated += HandleSizeAllocated;
+ var child = (TextEditor.EditorContainerChild)textEditor [this];
+ child.FixedPosition = true;
+ }
+
+ protected override void OnDestroyed ()
+ {
+ base.OnDestroyed ();
+ if (textEditor != null) {
+ textEditor.SizeAllocated -= HandleSizeAllocated;
+ textEditor = null;
+ }
+ }
+
+ protected override void OnSizeRequested (ref Requisition requisition)
+ {
+ base.OnSizeRequested (ref requisition);
+
+ if (wRequest > 0) {
+ requisition.Width = wRequest;
+ }
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+ Resize (allocation);
+ }
+
+ int wRequest = -1;
+
+ void HandleSizeAllocated (object o, Gtk.SizeAllocatedArgs args)
+ {
+ if (SizeFunc != null) {
+ var req = Math.Min (SizeFunc (), textEditor.Allocation.Width - border * 2);
+ if (req != wRequest) {
+ wRequest = req;
+ QueueResize ();
+ }
+ } else {
+ if (Allocation.Width > textEditor.Allocation.Width - border * 2) {
+ if (textEditor.Allocation.Width - border * 2 > 0) {
+ QueueResize ();
+ }
+ }
+ }
+ Resize (Allocation);
+ }
+
+ void Resize (Gdk.Rectangle alloc)
+ {
+ textEditor.MoveTopLevelWidget (this, (textEditor.Allocation.Width - alloc.Width) / 2, textEditor.Allocation.Height - alloc.Height - 8);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ using (var cr = CairoHelper.Create (evnt.Window)) {
+ cr.LineWidth = 1;
+ cr.Rectangle (0, 0, Allocation.Width, Allocation.Height);
+ cr.SetSourceColor (textEditor.ColorStyle.NotificationText.Background);
+ cr.Fill ();
+ cr.RoundedRectangle (0, 0, Allocation.Width, Allocation.Height, 3);
+ cr.SetSourceColor (textEditor.ColorStyle.NotificationText.Background);
+ cr.FillPreserve ();
+
+ cr.SetSourceColor (textEditor.ColorStyle.NotificationBorder.Color);
+ cr.Stroke ();
+ }
+
+ return base.OnExposeEvent (evnt);
+ }
+
+ }
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs
index ec225d84e7..bf7644f869 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs
@@ -64,7 +64,7 @@ namespace MonoDevelop.Debugger
void OnFrameChanged (object s, EventArgs a)
{
- if (disassemblyDoc != null && DebuggingService.IsFeatureSupported (DebuggerFeatures.Disassembly))
+ if (disassemblyDoc != null && IdeApp.Workbench.ActiveDocument == disassemblyDoc && DebuggingService.IsFeatureSupported (DebuggerFeatures.Disassembly))
disassemblyView.Update ();
var frame = DebuggingService.CurrentFrame;
@@ -73,11 +73,19 @@ namespace MonoDevelop.Debugger
FilePath file = frame.SourceLocation.FileName;
int line = frame.SourceLocation.Line;
-
- if (!file.IsNullOrEmpty && System.IO.File.Exists (file) && line != -1) {
- Document doc = IdeApp.Workbench.OpenDocument (file, line, 1, OpenDocumentOptions.Debugger);
- if (doc != null)
- return;
+ if (line != -1) {
+ if (!file.IsNullOrEmpty && System.IO.File.Exists (file)) {
+ if (IdeApp.Workbench.OpenDocument (file, null, line, 1, OpenDocumentOptions.Debugger) != null)
+ return;
+ }
+ if (frame.SourceLocation.FileHash != null) {
+ var newFilePath = SourceCodeLookup.FindSourceFile (file, frame.SourceLocation.FileHash);
+ if (newFilePath != null) {
+ frame.UpdateSourceFile (newFilePath);
+ if (IdeApp.Workbench.OpenDocument (newFilePath, null, line, 1, OpenDocumentOptions.Debugger) != null)
+ return;
+ }
+ }
}
// If we don't have an address space, we can't disassemble
@@ -115,16 +123,10 @@ namespace MonoDevelop.Debugger
if (bt != null) {
for (int n=0; n<bt.FrameCount; n++) {
StackFrame sf = bt.GetFrame (n);
- if (!sf.IsExternalCode && sf.SourceLocation.Line != -1) {
- bool found = !string.IsNullOrEmpty (sf.SourceLocation.FileName)
- && System.IO.File.Exists (sf.SourceLocation.FileName);
- if (found) {
- if (n != DebuggingService.CurrentFrameIndex)
- DebuggingService.CurrentFrameIndex = n;
- break;
- } else {
- LoggingService.LogWarning ("Debugger could not find file '{0}'", sf.SourceLocation.FileName);
- }
+ if (sf.SourceLocation.Line != -1) {
+ if (n != DebuggingService.CurrentFrameIndex)
+ DebuggingService.CurrentFrameIndex = n;
+ break;
}
}
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs
new file mode 100644
index 0000000000..c6655cdef5
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs
@@ -0,0 +1,128 @@
+//
+// SourceCodeLookup.cs
+//
+// Author:
+// David Karlaš <david.karlas@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using MonoDevelop.Core;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Linq;
+using MonoDevelop.Ide;
+
+namespace MonoDevelop.Debugger
+{
+ static class SourceCodeLookup
+ {
+ readonly static List<Tuple<FilePath,FilePath>> possiblePaths = new List<Tuple<FilePath, FilePath>> ();
+ readonly static Dictionary<FilePath,FilePath> directMapping = new Dictionary<FilePath, FilePath> ();
+
+ /// <summary>
+ /// Finds the source file.
+ /// </summary>
+ /// <returns>The source file.</returns>
+ /// <param name="originalFile">File from .mdb/.pdb.</param>
+ /// <param name="hash">Hash of original file stored in .mdb/.pdb.</param>
+ public static FilePath FindSourceFile (FilePath originalFile, byte[] hash)
+ {
+ if (directMapping.ContainsKey (originalFile))
+ return directMapping [originalFile];
+ foreach (var folder in possiblePaths) {
+ //file = /tmp/ci_build/mono/System/Net/Http/HttpClient.cs
+ var relativePath = originalFile.ToRelative (folder.Item1);
+ //relativePath = System/Net/Http/HttpClient.cs
+ var newFile = folder.Item2.Combine (relativePath);
+ //newPossiblePath = C:\GIT\mono_source\System\Net\Http\HttpClient.cs
+ if (CheckFileMd5 (newFile, hash)) {
+ directMapping.Add (originalFile, newFile);
+ return newFile;
+ }
+ }
+ foreach (var document in IdeApp.Workbench.Documents.Where((d) => d.FileName.FileName == originalFile.FileName)) {
+ //Check if it's already added to avoid MD5 checking
+ if (!directMapping.ContainsKey (originalFile)) {
+ if (CheckFileMd5 (document.FileName, hash)) {
+ AddLoadedFile (document.FileName, originalFile);
+ return document.FileName;
+ }
+ }
+ }
+ foreach (var bp in DebuggingService.Breakpoints.GetBreakpoints().Where((bp) => Path.GetFileName(bp.FileName) == originalFile.FileName)) {
+ //Check if it's already added to avoid MD5 checking
+ if (!directMapping.ContainsKey (originalFile)) {
+ if (CheckFileMd5 (bp.FileName, hash)) {
+ AddLoadedFile (bp.FileName, originalFile);
+ return bp.FileName;
+ }
+ }
+ }
+ return FilePath.Null;
+ }
+
+ public static bool CheckFileMd5 (FilePath file, byte[] hash)
+ {
+ if (File.Exists (file)) {
+ using (var fs = File.OpenRead (file)) {
+ using (var md5 = MD5.Create ()) {
+ if (md5.ComputeHash (fs).SequenceEqual (hash)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Call this method when user succesfully opens file so we can reuse this path
+ /// for other files from same project.
+ /// Notice that it's caller job to verify hash matches for performance reasons.
+ /// </summary>
+ /// <param name="file">File path which user picked.</param>
+ /// <param name = "originalFile">Original file path from .pdb/.mdb.</param>
+ public static void AddLoadedFile (FilePath file, FilePath originalFile)
+ {
+ if (directMapping.ContainsKey (originalFile))
+ return;
+ directMapping.Add (originalFile, file);
+ //file = C:\GIT\mono_source\System\Text\UTF8Encoding.cs
+ //originalFile = /tmp/ci_build/mono/System/Text/UTF8Encoding.cs
+ var fileParent = file.ParentDirectory;
+ var originalParent = originalFile.ParentDirectory;
+ if (fileParent == originalParent) {
+ //This can happen if file was renamed
+ possiblePaths.Add (new Tuple<FilePath, FilePath> (originalParent, fileParent));
+ } else {
+ while (fileParent.FileName == originalParent.FileName) {
+ fileParent = fileParent.ParentDirectory;
+ originalParent = originalParent.ParentDirectory;
+ }
+ //fileParent = C:\GIT\mono_source\
+ //originalParent = /tmp/ci_build/mono/
+ possiblePaths.Add (new Tuple<FilePath, FilePath> (originalParent, fileParent));
+ }
+ }
+ }
+}
+