Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invocations refactoring #85

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90a1f51
Special evaluator exception type (EvaluatorExceptionThrownException) …
nerzhulart Aug 13, 2015
b72dfaf
EvaluatorExceptionThrownException handling in ValueReference. This al…
nerzhulart Dec 19, 2016
cac578c
Evaluation exception wrapping in CreateObjectValue.
nerzhulart May 13, 2016
54a8ed0
Separate lock object. Get rid of useful Monitor.Pulse()
nerzhulart Sep 13, 2016
f3e1fee
Async operations rewritten to Tasks
nerzhulart Nov 8, 2016
ad8187f
Proper logging: before the exception body was lost, now it's logged.
nerzhulart Nov 22, 2016
5be3b5b
Invocation is awaited infinitely as it was before.
nerzhulart Jan 17, 2017
1d4a9c4
Restore GetInfo() and detailed logging on invocation
nerzhulart Jan 17, 2017
d8ef7b8
More proper evaluation aborting.
nerzhulart Jan 23, 2017
3226be3
Get rid of OperationData
nerzhulart Jan 23, 2017
e9ee3d0
Move checking for cancelled token into try-catch to guarantee that Up…
nerzhulart Feb 1, 2017
5db72cd
CorDebug Invocations rewritten to .Net Task API (commit moved from …
nerzhulart Mar 16, 2017
0aec667
CorDebug Checking for aborted state in CorMethodCall
nerzhulart Mar 16, 2017
e1f7852
More proper evaluation aborting
nerzhulart Mar 16, 2017
ae0eb0f
Trying to continue all threads if eval Abort() and RudeAbort() failed…
nerzhulart Mar 16, 2017
29b6a90
Handle exceptions in AbortImpl() (Moved from MD repo)
nerzhulart Mar 16, 2017
c594ffc
Restore RemoteFrameObject for AsyncEvaluationTracker
nerzhulart Mar 17, 2017
45b3c96
Remove unused file IAsyncOperation.cs
nerzhulart Mar 17, 2017
3f9b165
Break loop if disposed.
nerzhulart Mar 17, 2017
c6f1148
Line endings fix
nerzhulart Mar 17, 2017
e2cf427
Don't call Abort() if operation is alredy in aborting state
nerzhulart Mar 17, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 92 additions & 96 deletions Mono.Debugging.Soft/SoftDebuggerAdaptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;

using System.Threading.Tasks;
using Mono.Debugger.Soft;
using Mono.Debugging.Backend;
using Mono.Debugging.Evaluation;
Expand Down Expand Up @@ -2145,100 +2144,112 @@ static string MirrorStringToString (EvaluationContext ctx, StringMirror mirror)
}
}

class MethodCall: AsyncOperation
internal class SoftOperationResult : OperationResult<Value>
{
readonly InvokeOptions options = InvokeOptions.DisableBreakpoints | InvokeOptions.SingleThreaded;
public SoftOperationResult (Value result, bool resultIsException, Value[] outArgs) : base (result, resultIsException)
{
OutArgs = outArgs;
}

readonly ManualResetEvent shutdownEvent = new ManualResetEvent (false);
public Value[] OutArgs { get; private set; }
}

internal class SoftMethodCall: AsyncOperationBase<Value>
{
readonly InvokeOptions options = InvokeOptions.DisableBreakpoints | InvokeOptions.SingleThreaded;
readonly SoftEvaluationContext ctx;
readonly MethodMirror function;
readonly Value[] args;
readonly object obj;
IAsyncResult handle;
Exception exception;
IInvokeResult result;

public MethodCall (SoftEvaluationContext ctx, MethodMirror function, object obj, Value[] args, bool enableOutArgs)
readonly IInvocableMethodOwnerMirror obj;
IInvokeAsyncResult invokeAsyncResult;

public SoftMethodCall (SoftEvaluationContext ctx, MethodMirror function, IInvocableMethodOwnerMirror obj, Value[] args, bool enableOutArgs)
{
this.ctx = ctx;
this.function = function;
this.obj = obj;
this.args = args;
if (enableOutArgs) {
this.options |= InvokeOptions.ReturnOutArgs;
options |= InvokeOptions.ReturnOutArgs;
}
if (function.VirtualMachine.Version.AtLeast (2, 40)) {
this.options |= InvokeOptions.Virtual;
options |= InvokeOptions.Virtual;
}
}

public override string Description {
get {
return function.DeclaringType.FullName + "." + function.Name;
}
}

public override void Invoke ()
{
try {
var invocableMirror = obj as IInvocableMethodOwnerMirror;
if (invocableMirror != null) {
var optionsToInvoke = options;
if (obj is StructMirror) {
optionsToInvoke |= InvokeOptions.ReturnOutThis;
}
handle = invocableMirror.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, null, null);
get
{
try {
return function.DeclaringType.FullName + "." + function.Name;
}
catch (Exception e) {
DebuggerLoggingService.LogError ("Exception during getting description of method", e);
return "[Unknown method]";
}
else
throw new ArgumentException ("Soft debugger method calls cannot be invoked on objects of type " + obj.GetType ().Name);
} catch (InvocationException ex) {
ctx.Session.StackVersion++;
exception = ex;
} catch (Exception ex) {
ctx.Session.StackVersion++;
DebuggerLoggingService.LogError ("Error in soft debugger method call thread on " + GetInfo (), ex);
exception = ex;
}
}

public override void Abort ()
{
if (handle is IInvokeAsyncResult) {
var info = GetInfo ();
DebuggerLoggingService.LogMessage ("Aborting invocation of " + info);
((IInvokeAsyncResult) handle).Abort ();
// Don't wait for the abort to finish. The engine will do it.
} else {
throw new NotSupportedException ();
}
}

public override void Shutdown ()
{
shutdownEvent.Set ();
}

void EndInvoke ()
protected override Task<OperationResult<Value>> InvokeAsyncImpl ()
{
try {
result = ((IInvocableMethodOwnerMirror) obj).EndInvokeMethodWithResult (handle);
} catch (InvocationException ex) {
if (!Aborting && ex.Exception != null) {
string ename = ctx.Adapter.GetValueTypeName (ctx, ex.Exception);
var vref = ctx.Adapter.GetMember (ctx, null, ex.Exception, "Message");

exception = vref != null ? new Exception (ename + ": " + (string) vref.ObjectValue) : new Exception (ename);
return;
var optionsToInvoke = options;
if (obj is StructMirror) {
optionsToInvoke |= InvokeOptions.ReturnOutThis;
}
exception = ex;
} catch (Exception ex) {
DebuggerLoggingService.LogError ("Error in soft debugger method call thread on " + GetInfo (), ex);
exception = ex;
} finally {
ctx.Session.StackVersion++;
var tcs = new TaskCompletionSource<OperationResult<Value>> ();
invokeAsyncResult = (IInvokeAsyncResult)obj.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, ar => {
try {
if (Token.IsCancellationRequested) {
tcs.SetCanceled ();
return;
}
var endInvokeResult = obj.EndInvokeMethodWithResult (ar);
tcs.SetResult (new SoftOperationResult (endInvokeResult.Result, false, endInvokeResult.OutArgs));
}
catch (InvocationException ex) {
if (ex.Exception != null) {
tcs.SetResult (new SoftOperationResult (ex.Exception, true, null));
}
else {
tcs.SetException (new EvaluatorException ("Target method has thrown an exception but the exception object is inaccessible"));
}
}
catch (CommandException e) {
if (e.ErrorCode == ErrorCode.INVOKE_ABORTED) {
tcs.TrySetCanceled ();
}
else {
tcs.SetException (new EvaluatorException (e.Message));
}
}
catch (Exception e) {
if (e is ObjectCollectedException ||
e is InvalidStackFrameException ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this logic gone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've replaced simple Exception message to full exception object. So now you can walk through it, but before you can see only exception message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is that object shown to the user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an ObjectValue with its children that has IsException flag. It's shown in watchpad as ordinary object

e is VMNotSuspendedException ||
e is NotSupportedException ||
e is AbsentInformationException ||
e is ArgumentException) {
// user meaningfull evaluation exception -> wrap with EvaluatorException that will be properly shown in value viewer
tcs.SetException (new EvaluatorException (e.Message));
}
else {
DebuggerLoggingService.LogError (string.Format ("Unexpected exception has thrown when ending invocation of {0}", GetInfo ()), e);
tcs.SetException (e);
}
}
finally {
UpdateSessionState ();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More logic that has been removed for no apparent good reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean logic of getting exception message so look at line 2230. SoftOperationResult wraps exception object to show it like ordinary object for user

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean GetInfo(), which is used to log the method and type name when there is an invocation error.

Copy link
Contributor Author

@nerzhulart nerzhulart Jan 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that GetInfo used only for logging. Isnt't it enough to use Description for this? Now I log all this states using this property

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it is enough or not, it needs to be checked. I just don't want code to be removed if there isn't a good reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reasons to remove are simplifying and avoiding duplications.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's bring it back then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

}
}, null);
return tcs.Task;
} catch (Exception e) {
UpdateSessionState ();
DebuggerLoggingService.LogError (string.Format ("Unexpected exception has thrown when invoking {0}", GetInfo ()), e);
throw;
}
}

string GetInfo ()
{
try {
Expand All @@ -2250,41 +2261,26 @@ string GetInfo ()
else if (obj is StructMirror)
type = ((StructMirror)obj).Type;
return string.Format ("method {0} on object {1}",
function == null? "[null]" : function.FullName,
type == null? "[null]" : type.FullName);
function.FullName,
type == null? "[null]" : type.FullName);
} catch (Exception ex) {
DebuggerLoggingService.LogError ("Error getting info for SDB MethodCall", ex);
return "";
return "[Unknown method]";
}
}

public override bool WaitForCompleted (int timeout)
void UpdateSessionState ()
{
if (handle == null)
return true;
int res = WaitHandle.WaitAny (new WaitHandle[] { handle.AsyncWaitHandle, shutdownEvent }, timeout);
if (res == 0) {
EndInvoke ();
return true;
}
// Return true if shut down.
return res == 1;
}

public Value ReturnValue {
get {
if (exception != null)
throw new EvaluatorException (exception.Message);
return result.Result;
}
ctx.Session.StackVersion++;
}

public Value[] OutArgs {
get {
if (exception != null)
throw new EvaluatorException (exception.Message);
return result.OutArgs;
protected override void AbortImpl (int abortCallTimes)
{
if (invokeAsyncResult == null) {
DebuggerLoggingService.LogError ("invokeAsyncResult is null", new ArgumentNullException ("invokeAsyncResult"));
return;
}
invokeAsyncResult.Abort ();
}
}
}
15 changes: 9 additions & 6 deletions Mono.Debugging.Soft/SoftEvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,19 @@ Value RuntimeInvoke (MethodMirror method, object target, Value[] values, bool en
DC.DebuggerLoggingService.LogMessage ("Thread state before evaluation is {0}", threadState);
throw new EvaluatorException ("Evaluation is not allowed when the thread is in 'Wait' state");
}
var mc = new MethodCall (this, method, target, values, enableOutArgs);
var invocableMirror = target as IInvocableMethodOwnerMirror;
if (invocableMirror == null)
throw new ArgumentException ("Soft debugger method calls cannot be invoked on objects of type " + target.GetType ().Name);
var mc = new SoftMethodCall (this, method, invocableMirror, values, enableOutArgs);
//Since runtime is returning NOT_SUSPENDED error if two methods invokes are executed
//at same time we have to lock invoking to prevent this...
lock (method.VirtualMachine) {
Adapter.AsyncExecute (mc, Options.EvaluationTimeout);
}
if (enableOutArgs) {
outArgs = mc.OutArgs;
var result = (SoftOperationResult)Adapter.InvokeSync (mc, Options.EvaluationTimeout).ThrowIfException (this);
if (enableOutArgs) {
outArgs = result.OutArgs;
}
return result.Result;
}
return mc.ReturnValue;
}
}

Expand Down
83 changes: 19 additions & 64 deletions Mono.Debugging.Win32/CorDebuggerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public CorDebuggerSession(char[] badPathChars)
get { return evaluationTimestamp; }
}

internal CorProcess Process
{
get
{
return process;
}
}

public override void Dispose ( )
{
Expand Down Expand Up @@ -1417,49 +1424,16 @@ public CorValue RuntimeInvoke (CorEvaluationContext ctx, CorFunction function, C
arguments.CopyTo (args, 1);
}

CorMethodCall mc = new CorMethodCall ();
CorValue exception = null;
CorEval eval = ctx.Eval;

DebugEventHandler<CorEvalEventArgs> completeHandler = delegate (object o, CorEvalEventArgs eargs) {
OnEndEvaluating ();
mc.DoneEvent.Set ();
eargs.Continue = false;
};

DebugEventHandler<CorEvalEventArgs> exceptionHandler = delegate(object o, CorEvalEventArgs eargs) {
OnEndEvaluating ();
exception = eargs.Eval.Result;
mc.DoneEvent.Set ();
eargs.Continue = false;
};

process.OnEvalComplete += completeHandler;
process.OnEvalException += exceptionHandler;

mc.OnInvoke = delegate {
if (function.GetMethodInfo (this).Name == ".ctor")
eval.NewParameterizedObject (function, typeArgs, args);
else
eval.CallParameterizedFunction (function, typeArgs, args);
process.SetAllThreadsDebugState (CorDebugThreadState.THREAD_SUSPEND, ctx.Thread);
ClearEvalStatus ();
OnStartEvaluating ();
process.Continue (false);
};
mc.OnAbort = delegate {
eval.Abort ();
};
mc.OnGetDescription = delegate {
MethodInfo met = function.GetMethodInfo (ctx.Session);
if (met != null)
return met.DeclaringType.FullName + "." + met.Name;
else
return "<Unknown>";
};

var methodCall = new CorMethodCall (ctx, function, typeArgs, args);
try {
ObjectAdapter.AsyncExecute (mc, ctx.Options.EvaluationTimeout);
var result = ObjectAdapter.InvokeSync (methodCall, ctx.Options.EvaluationTimeout);
if (result.ResultIsException) {
var vref = new CorValRef (result.Result);
throw new EvaluatorExceptionThrownException (vref, ObjectAdapter.GetValueTypeName (ctx, vref));
}

WaitUntilStopped ();
return result.Result;
}
catch (COMException ex) {
// eval exception is a 'good' exception that should be shown in value box
Expand All @@ -1469,35 +1443,16 @@ public CorValue RuntimeInvoke (CorEvaluationContext ctx, CorFunction function, C
throw evalException;
throw;
}
finally {
process.OnEvalComplete -= completeHandler;
process.OnEvalException -= exceptionHandler;
}

WaitUntilStopped ();
if (exception != null) {
/* ValueReference<CorValue, CorType> msg = ctx.Adapter.GetMember (ctx, val, "Message");
if (msg != null) {
string s = msg.ObjectValue as string;
mc.ExceptionMessage = s;
}
else
mc.ExceptionMessage = "Evaluation failed.";*/
CorValRef vref = new CorValRef (exception);
throw new EvaluatorException ("Evaluation failed: " + ObjectAdapter.GetValueTypeName (ctx, vref));
}

return eval.Result;
}

void OnStartEvaluating ( )
internal void OnStartEvaluating ( )
{
lock (debugLock) {
evaluating = true;
}
}

void OnEndEvaluating ( )
internal void OnEndEvaluating ( )
{
lock (debugLock) {
evaluating = false;
Expand Down Expand Up @@ -1603,7 +1558,7 @@ public void WaitUntilStopped ()
}
}

void ClearEvalStatus ( )
internal void ClearEvalStatus ( )
{
foreach (CorProcess p in dbg.Processes) {
if (p.Id == processId) {
Expand Down
Loading