Skip to content

Commit

Permalink
[gh-4] Initial version of breakpoint persister.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Jan 2, 2015
1 parent 694f9bd commit c1a4cfe
Show file tree
Hide file tree
Showing 17 changed files with 245 additions and 110 deletions.
2 changes: 1 addition & 1 deletion #testing/ExceptionTest.VS2013/ExceptionTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace ExceptionTest
public static class Program
{
public static void Main(string[] args) {
if (args.Length == 0 || args[0] != "noloop")
if (args.Length == 0 || args[0] != "noloop")
StandaloneExceptionLoop();

FirstChanceException();
Expand Down
2 changes: 1 addition & 1 deletion ExceptionBreaker.OptionsUITester/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace ExceptionBreaker.OptionsUITester {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
view.Model = new OptionsViewModel(
view.ViewModel = new OptionsViewModel(
new OptionsPageData(),
new ObservableValue<IEnumerable<string>>(new string[] {
"System.Exception",
Expand Down
20 changes: 13 additions & 7 deletions ExceptionBreaker/Breakpoints/BreakpointEventProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using ExceptionBreaker.Core;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Debugger.Interop;
Expand All @@ -10,13 +9,13 @@
namespace ExceptionBreaker.Breakpoints {
public class BreakpointEventProcessor : IDebugEventCallback2, IDisposable {
private readonly IVsDebugger _debugger;
private readonly ExceptionBreakChangeStore _store;
private readonly BreakpointExtraDataProvider _extraDataProvider;
private readonly ExceptionBreakManager _breakManager;
private readonly IDiagnosticLogger _logger;

public BreakpointEventProcessor(IVsDebugger debugger, ExceptionBreakChangeStore store, ExceptionBreakManager breakManager, IDiagnosticLogger logger) {
public BreakpointEventProcessor(IVsDebugger debugger, BreakpointExtraDataProvider extraDataProvider, ExceptionBreakManager breakManager, IDiagnosticLogger logger) {
_debugger = debugger;
_store = store;
_extraDataProvider = extraDataProvider;
_breakManager = breakManager;
_logger = logger;

Expand All @@ -28,7 +27,7 @@ public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2
ProcessEvent(pEvent);
}
catch (Exception ex) {
_logger.WriteLine("Unexpected exception: " + ex);
_logger.WriteLine("Unexpected exception: {0}", ex);
}
finally {
VSInteropHelper.Release(pEngine);
Expand All @@ -47,8 +46,15 @@ private void ProcessEvent(IDebugEvent2 pEvent) {

_logger.WriteLine("Event: Breakpoint reached.");
foreach (var breakpoint in breakpointEvent.GetBreakpointsAsArraySafe()) {
var change = _store.GetChange(breakpoint);
_logger.WriteLine("Change is.... {0}", change);
var extraData = _extraDataProvider.GetData(breakpoint);
if (extraData == null || extraData.ExceptionBreakChange == ExceptionBreakChange.NoChange)
continue;

var change = extraData.ExceptionBreakChange;
_logger.WriteLine("Breakpoint requires exception state change: {0}.", change);
_breakManager.CurrentState = change == ExceptionBreakChange.SetBreakOnAll
? ExceptionBreakState.BreakOnAll
: ExceptionBreakState.BreakOnNone;
}
}

Expand Down
15 changes: 15 additions & 0 deletions ExceptionBreaker/Breakpoints/BreakpointExtraData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExceptionBreaker.Breakpoints {
public class BreakpointExtraData {
public BreakpointExtraData() {
Version = 1; // for serialization tracking
}

public int Version { get; set; }
public ExceptionBreakChange ExceptionBreakChange { get; set; }
}
}
84 changes: 84 additions & 0 deletions ExceptionBreaker/Breakpoints/BreakpointExtraDataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AshMind.Extensions;
using EnvDTE80;
using ExceptionBreaker.Core;
using JetBrains.Annotations;
using Microsoft.VisualStudio.Debugger.Interop;
using Newtonsoft.Json;

namespace ExceptionBreaker.Breakpoints {
public class BreakpointExtraDataProvider : ISolutionDataPersister {
private readonly BreakpointKeyProvider _keyProvider;
private readonly BreakpointFinder _finder;
private readonly JsonSerializer _jsonSerializer;
private readonly IDiagnosticLogger _logger;
private readonly ConcurrentDictionary<string, BreakpointExtraData> _store = new ConcurrentDictionary<string, BreakpointExtraData>(StringComparer.InvariantCultureIgnoreCase);

public BreakpointExtraDataProvider(BreakpointKeyProvider keyProvider, BreakpointFinder finder, JsonSerializer jsonSerializer, IDiagnosticLogger logger) {
_keyProvider = keyProvider;
_finder = finder;
_jsonSerializer = jsonSerializer;
_logger = logger;
}

[NotNull]
public BreakpointExtraData GetData([NotNull] Breakpoint2 breakpoint) {
return GetData(_keyProvider.GetKey(breakpoint));
}

[CanBeNull]
public BreakpointExtraData GetData([NotNull] IDebugBoundBreakpoint2 breakpoint) {
var key = _keyProvider.GetKey(breakpoint);
if (key == null)
return null;

return GetData(key);
}

[NotNull]
private BreakpointExtraData GetData([NotNull] string key) {
return _store.GetOrAdd(key, k => new BreakpointExtraData());
}

#region ISolutionDataPersister Members

string ISolutionDataPersister.Key {
// due to VS limitation, this has to be shorted than 31 char and contain no '.'
get { return "XB-BreakpointExtraData"; }
}

void ISolutionDataPersister.SaveTo(Stream stream) {
_logger.WriteLine("Breakpoints: saving extra data.");
var existingBreakpointKeys = _finder.GetAllBreakpoints().Select(b => _keyProvider.GetKey(b));
foreach (var key in _store.Keys.Except(existingBreakpointKeys).ToArray()) {
BreakpointExtraData _;
_store.TryRemove(key, out _);
_logger.WriteLine(" Skipped '{0}' — no corresponding breakpoint.", key);
}

using (var writer = new StreamWriter(stream)) {
_jsonSerializer.Serialize(writer, _store);
}
_logger.WriteLine(" Saved {0} extra data entries to the solution.", _store.Count);
}

void ISolutionDataPersister.LoadFrom(Stream stream) {
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader)) {
var loaded = _jsonSerializer.Deserialize<IDictionary<string, BreakpointExtraData>>(jsonReader);
_store.Clear();
_logger.WriteLine("Breakpoints: loading extra data.");
foreach (var pair in loaded) {
_store[pair.Key] = pair.Value;
_logger.WriteLine(" Loaded '{0}': change = {1}.", pair.Key, pair.Value.ExceptionBreakChange);
}
}
}

#endregion
}
}
4 changes: 4 additions & 0 deletions ExceptionBreaker/Breakpoints/BreakpointFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public BreakpointFinder(DTE dte) {
_dte = dte;
}

public IEnumerable<Breakpoint2> GetAllBreakpoints() {
return _dte.Debugger.Breakpoints.Cast<Breakpoint2>();
}

public Breakpoint2 GetBreakpointForCommand() {
var path = _dte.ActiveDocument.FullName;
var line = ((TextSelection)_dte.ActiveDocument.Selection).CurrentLine;
Expand Down
37 changes: 37 additions & 0 deletions ExceptionBreaker/Breakpoints/BreakpointKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using EnvDTE80;
using ExceptionBreaker.Core;
using JetBrains.Annotations;
using Microsoft.VisualStudio.Debugger.Interop;

namespace ExceptionBreaker.Breakpoints {
public class BreakpointKeyProvider {
[NotNull]
public string GetKey(Breakpoint2 breakpoint) {
return string.Intern(breakpoint.File + ":" + breakpoint.FileLine + ":" + breakpoint.FileColumn);
}

[CanBeNull]
public string GetKey(IDebugBoundBreakpoint2 breakpoint) {
var resolution = breakpoint.GetBreakpointResolutionSafe();
var resolutionInfo = resolution.GetResolutionInfoSafe((int)enum_BPRESI_FIELDS.BPRESI_BPRESLOCATION);
var location = resolutionInfo.bpResLocation;
if (location.bpType != (uint)enum_BP_TYPE.BPT_CODE)
return null;

var context = (IDebugCodeContext2)Marshal.GetObjectForIUnknown(location.unionmember1);
var documentContext = context.GetDocumentContextSafe();
var fileName = documentContext.GetNameSafe((uint)enum_GETNAME_TYPE.GN_FILENAME);

var position = new TEXT_POSITION[1];
var hr = documentContext.GetStatementRange(position, null);
VSInteropHelper.Validate(hr);

return string.Intern(fileName + ":" + (position[0].dwLine + 1) + ":" + (position[0].dwColumn + 1));
}
}
}
20 changes: 10 additions & 10 deletions ExceptionBreaker/Breakpoints/BreakpointSetupExceptionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,35 @@
namespace ExceptionBreaker.Breakpoints {
public class BreakpointSetupExceptionsController {
private readonly BreakpointFinder _breakpointFinder;
private readonly ExceptionBreakChangeStore _store;
private readonly BreakpointExtraDataProvider _extraDataProvider;
private readonly IDiagnosticLogger _logger;
private readonly MenuCommand _command;

public BreakpointSetupExceptionsController(CommandInitializer commandInitializer,
BreakpointFinder breakpointFinder,
ExceptionBreakChangeStore store,
BreakpointExtraDataProvider extraDataProvider,
IDiagnosticLogger logger)
{
_breakpointFinder = breakpointFinder;
_store = store;
_extraDataProvider = extraDataProvider;
_logger = logger;
_command = commandInitializer.InitializeCommand(command_Callback, command_BeforeQueryStatus);
}

private void command_BeforeQueryStatus(object sender, EventArgs e) {
try {

var breakpoint = _breakpointFinder.GetBreakpointForCommand();
var change = _store.GetChange(breakpoint);
var extraData = _extraDataProvider.GetData(breakpoint);

var @checked = (change != ExceptionBreakChange.NoChange);
var @checked = (extraData != null && extraData.ExceptionBreakChange != ExceptionBreakChange.NoChange);
if (@checked == _command.Checked)
return;

_logger.WriteLine("Breakpoint context menu command: change of state, checked = {0}.", @checked);
_command.Checked = @checked;
}
catch (Exception ex) {
_logger.WriteLine("Unexpected exception: " + ex);
_logger.WriteLine("Unexpected exception: {0}", ex);
}
}

Expand All @@ -50,15 +49,16 @@ private void command_Callback(object sender, EventArgs e) {
RequestAndUpdateExceptionSettings(breakpoint);
}
catch (Exception ex) {
_logger.WriteLine("Unexpected exception: " + ex);
_logger.WriteLine("Unexpected exception: {0}", ex);
MessageBox.Show(ex.Message, "Error in ExceptionBreaker extension", MessageBoxButton.OK, MessageBoxImage.Error);
}
}

private void RequestAndUpdateExceptionSettings(Breakpoint2 breakpoint) {
var extraData = _extraDataProvider.GetData(breakpoint);
var dialog = new BreakpointExceptionsDialog {
ViewModel = new BreakpointExceptionSettings {
Change = _store.GetChange(breakpoint),
Change = extraData != null ? extraData.ExceptionBreakChange : ExceptionBreakChange.NoChange,
ContinueExecution = !breakpoint.BreakWhenHit
}
};
Expand All @@ -69,7 +69,7 @@ private void RequestAndUpdateExceptionSettings(Breakpoint2 breakpoint) {
}

_logger.WriteLine("Updating breakpoint settings: continue execution = {0}, change = {1}.", dialog.ViewModel.ContinueExecution, dialog.ViewModel.Change);
_store.SetChange(breakpoint, dialog.ViewModel.Change);
extraData.ExceptionBreakChange = dialog.ViewModel.Change;
breakpoint.BreakWhenHit = !dialog.ViewModel.ContinueExecution;
}
}
Expand Down
64 changes: 0 additions & 64 deletions ExceptionBreaker/Breakpoints/ExceptionBreakChangeStore.cs

This file was deleted.

4 changes: 4 additions & 0 deletions ExceptionBreaker/Core/ExtensionLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ public void WriteLine(string format, params object[] args) {
public void WriteLine(string format, object arg1) {
WriteLine(string.Format(format, arg1));
}

public void WriteLine(string format, object arg1, object arg2) {
WriteLine(string.Format(format, arg1, arg2));
}
}
}
2 changes: 2 additions & 0 deletions ExceptionBreaker/Core/IDiagnosticLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public interface IDiagnosticLogger {
void WriteLine(string format, params object[] args);
[StringFormatMethod("format")]
void WriteLine(string format, object arg1);
[StringFormatMethod("format")]
void WriteLine(string format, object arg1, object arg2);
}
}
12 changes: 12 additions & 0 deletions ExceptionBreaker/Core/ISolutionDataPersister.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ExceptionBreaker.Core {
public interface ISolutionDataPersister {
string Key { get; }
void SaveTo(Stream stream);
void LoadFrom(Stream stream);
}
}
2 changes: 1 addition & 1 deletion ExceptionBreaker/Core/VSInteropHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static void Validate(int hresult) {
public static void Release(object comObject) {
if (comObject == null)
return;

Marshal.ReleaseComObject(comObject);
}
}
Expand Down
Loading

0 comments on commit c1a4cfe

Please sign in to comment.