/Debugger/Debugger.Core/Eval.cs

http://github.com/icsharpcode/ILSpy · C# · 416 lines · 320 code · 48 blank · 48 comment · 72 complexity · b4259bf572d296eb116bcdd172538d0f MD5 · raw file

  1. // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2. // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Runtime.InteropServices;
  6. using Debugger.MetaData;
  7. using Debugger.Interop.CorDebug;
  8. namespace Debugger
  9. {
  10. public enum EvalState {
  11. Evaluating,
  12. EvaluatedSuccessfully,
  13. EvaluatedException,
  14. EvaluatedNoResult,
  15. EvaluatedTimeOut,
  16. };
  17. /// <summary>
  18. /// This class holds information about function evaluation.
  19. /// </summary>
  20. public class Eval: DebuggerObject
  21. {
  22. delegate void EvalStarter(Eval eval);
  23. AppDomain appDomain;
  24. Process process;
  25. string description;
  26. ICorDebugEval corEval;
  27. Thread thread;
  28. Value result;
  29. EvalState state;
  30. public AppDomain AppDomain {
  31. get { return appDomain; }
  32. }
  33. public Process Process {
  34. get { return process; }
  35. }
  36. public string Description {
  37. get { return description; }
  38. }
  39. public ICorDebugEval CorEval {
  40. get { return corEval; }
  41. }
  42. public ICorDebugEval2 CorEval2 {
  43. get { return (ICorDebugEval2)corEval; }
  44. }
  45. /// <exception cref="GetValueException">Evaluating...</exception>
  46. public Value Result {
  47. get {
  48. switch(this.State) {
  49. case EvalState.Evaluating: throw new GetValueException("Evaluating...");
  50. case EvalState.EvaluatedSuccessfully: return result;
  51. case EvalState.EvaluatedException: return result;
  52. case EvalState.EvaluatedNoResult: return null;
  53. case EvalState.EvaluatedTimeOut: throw new GetValueException("Timeout");
  54. default: throw new DebuggerException("Unknown state");
  55. }
  56. }
  57. }
  58. public EvalState State {
  59. get { return state; }
  60. }
  61. public bool Evaluated {
  62. get {
  63. return state == EvalState.EvaluatedSuccessfully ||
  64. state == EvalState.EvaluatedException ||
  65. state == EvalState.EvaluatedNoResult ||
  66. state == EvalState.EvaluatedTimeOut;
  67. }
  68. }
  69. Eval(AppDomain appDomain, string description, EvalStarter evalStarter)
  70. {
  71. this.appDomain = appDomain;
  72. this.process = appDomain.Process;
  73. this.description = description;
  74. this.state = EvalState.Evaluating;
  75. this.thread = GetEvaluationThread(appDomain);
  76. this.corEval = thread.CorThread.CreateEval();
  77. try {
  78. evalStarter(this);
  79. } catch (COMException e) {
  80. if ((uint)e.ErrorCode == 0x80131C26) {
  81. throw new GetValueException("Can not evaluate in optimized code");
  82. } else if ((uint)e.ErrorCode == 0x80131C28) {
  83. throw new GetValueException("Object is in wrong AppDomain");
  84. } else if ((uint)e.ErrorCode == 0x8013130A) {
  85. // Happens on getting of Sytem.Threading.Thread.ManagedThreadId; See SD2-1116
  86. throw new GetValueException("Function does not have IL code");
  87. } else if ((uint)e.ErrorCode == 0x80131C23) {
  88. // The operation failed because it is a GC unsafe point. (Exception from HRESULT: 0x80131C23)
  89. // This can probably happen when we break and the thread is in native code
  90. throw new GetValueException("Thread is in GC unsafe point");
  91. } else if ((uint)e.ErrorCode == 0x80131C22) {
  92. // The operation is illegal because of a stack overflow.
  93. throw new GetValueException("Can not evaluate after stack overflow");
  94. } else if ((uint)e.ErrorCode == 0x80131313) {
  95. // Func eval cannot work. Bad starting point.
  96. // Reproduction circumstancess are unknown
  97. throw new GetValueException("Func eval cannot work. Bad starting point.");
  98. } else {
  99. #if DEBUG
  100. throw; // Expose for more diagnostics
  101. #else
  102. throw new GetValueException(e.Message);
  103. #endif
  104. }
  105. }
  106. appDomain.Process.ActiveEvals.Add(this);
  107. if (appDomain.Process.Options.SuspendOtherThreads) {
  108. appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] { thread }, CorDebugThreadState.THREAD_SUSPEND);
  109. } else {
  110. appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, this.Process.UnsuspendedThreads, CorDebugThreadState.THREAD_RUN);
  111. }
  112. }
  113. static Thread GetEvaluationThread(AppDomain appDomain)
  114. {
  115. appDomain.Process.AssertPaused();
  116. Thread st = appDomain.Process.SelectedThread;
  117. if (st != null && !st.Suspended && !st.IsInNativeCode && st.IsAtSafePoint && st.CorThread.GetAppDomain().GetID() == appDomain.ID) {
  118. return st;
  119. }
  120. foreach(Thread t in appDomain.Process.Threads) {
  121. if (!t.Suspended && !t.IsInNativeCode && t.IsAtSafePoint && t.CorThread.GetAppDomain().GetID() == appDomain.ID) {
  122. return t;
  123. }
  124. }
  125. throw new GetValueException("No suitable thread for evaluation");
  126. }
  127. internal bool IsCorEval(ICorDebugEval corEval)
  128. {
  129. return this.corEval == corEval;
  130. }
  131. /// <exception cref="DebuggerException">Evaluation can not be stopped</exception>
  132. /// <exception cref="GetValueException">Process exited</exception>
  133. Value WaitForResult()
  134. {
  135. // Note that aborting is not supported for suspended threads
  136. try {
  137. process.WaitForPause(TimeSpan.FromMilliseconds(500));
  138. if (!Evaluated) {
  139. process.TraceMessage("Aborting eval: " + Description);
  140. this.CorEval.Abort();
  141. process.WaitForPause(TimeSpan.FromMilliseconds(2500));
  142. if (!Evaluated) {
  143. process.TraceMessage("Rude aborting eval: " + Description);
  144. this.CorEval2.RudeAbort();
  145. process.WaitForPause(TimeSpan.FromMilliseconds(5000));
  146. if (!Evaluated) {
  147. throw new DebuggerException("Evaluation can not be stopped");
  148. }
  149. }
  150. // Note that this sets Evaluated to true
  151. state = EvalState.EvaluatedTimeOut;
  152. }
  153. process.AssertPaused();
  154. return this.Result;
  155. } catch (ProcessExitedException) {
  156. throw new GetValueException("Process exited");
  157. }
  158. }
  159. internal void NotifyEvaluationComplete(bool successful)
  160. {
  161. // Eval result should be ICorDebugHandleValue so it should survive Continue()
  162. if (state == EvalState.EvaluatedTimeOut) {
  163. return;
  164. }
  165. if (corEval.GetResult() == null) {
  166. state = EvalState.EvaluatedNoResult;
  167. } else {
  168. if (successful) {
  169. state = EvalState.EvaluatedSuccessfully;
  170. } else {
  171. state = EvalState.EvaluatedException;
  172. }
  173. result = new Value(AppDomain, corEval.GetResult());
  174. }
  175. }
  176. /// <summary> Synchronously calls a function and returns its return value </summary>
  177. public static Value InvokeMethod(DebugMethodInfo method, Value thisValue, Value[] args)
  178. {
  179. if (method.BackingField != null) {
  180. method.Process.TraceMessage("Using backing field for " + method.FullName);
  181. return Value.GetMemberValue(thisValue, method.BackingField, args);
  182. }
  183. return AsyncInvokeMethod(method, thisValue, args).WaitForResult();
  184. }
  185. public static Eval AsyncInvokeMethod(DebugMethodInfo method, Value thisValue, Value[] args)
  186. {
  187. return new Eval(
  188. method.AppDomain,
  189. "Function call: " + method.FullName,
  190. delegate(Eval eval) {
  191. MethodInvokeStarter(eval, method, thisValue, args);
  192. }
  193. );
  194. }
  195. /// <exception cref="GetValueException"><c>GetValueException</c>.</exception>
  196. static void MethodInvokeStarter(Eval eval, DebugMethodInfo method, Value thisValue, Value[] args)
  197. {
  198. List<ICorDebugValue> corArgs = new List<ICorDebugValue>();
  199. args = args ?? new Value[0];
  200. if (args.Length != method.ParameterCount) {
  201. throw new GetValueException("Invalid parameter count");
  202. }
  203. if (!method.IsStatic) {
  204. if (thisValue == null)
  205. throw new GetValueException("'this' is null");
  206. if (thisValue.IsNull)
  207. throw new GetValueException("Null reference");
  208. // if (!(thisValue.IsObject)) // eg Can evaluate on array
  209. if (!method.DeclaringType.IsInstanceOfType(thisValue)) {
  210. throw new GetValueException(
  211. "Can not evaluate because the object is not of proper type. " +
  212. "Expected: " + method.DeclaringType.FullName + " Seen: " + thisValue.Type.FullName
  213. );
  214. }
  215. corArgs.Add(thisValue.CorValue);
  216. }
  217. for(int i = 0; i < args.Length; i++) {
  218. Value arg = args[i];
  219. DebugType paramType = (DebugType)method.GetParameters()[i].ParameterType;
  220. if (!arg.Type.CanImplicitelyConvertTo(paramType))
  221. throw new GetValueException("Inncorrect parameter type");
  222. // Implicitely convert to correct primitve type
  223. if (paramType.IsPrimitive && args[i].Type != paramType) {
  224. object oldPrimVal = arg.PrimitiveValue;
  225. object newPrimVal = Convert.ChangeType(oldPrimVal, paramType.PrimitiveType);
  226. arg = CreateValue(method.AppDomain, newPrimVal);
  227. }
  228. // It is importatnt to pass the parameted in the correct form (boxed/unboxed)
  229. if (paramType.IsValueType) {
  230. corArgs.Add(arg.CorGenericValue);
  231. } else {
  232. if (args[i].Type.IsValueType) {
  233. corArgs.Add(arg.Box().CorValue);
  234. } else {
  235. corArgs.Add(arg.CorValue);
  236. }
  237. }
  238. }
  239. ICorDebugType[] genericArgs = ((DebugType)method.DeclaringType).GenericArgumentsAsCorDebugType;
  240. eval.CorEval2.CallParameterizedFunction(
  241. method.CorFunction,
  242. (uint)genericArgs.Length, genericArgs,
  243. (uint)corArgs.Count, corArgs.ToArray()
  244. );
  245. }
  246. public static Value CreateValue(AppDomain appDomain, object value)
  247. {
  248. if (value == null) {
  249. ICorDebugClass corClass = appDomain.ObjectType.CorType.GetClass();
  250. Thread thread = GetEvaluationThread(appDomain);
  251. ICorDebugEval corEval = thread.CorThread.CreateEval();
  252. ICorDebugValue corValue = corEval.CreateValue((uint)CorElementType.CLASS, corClass);
  253. return new Value(appDomain, corValue);
  254. } else if (value is string) {
  255. return Eval.NewString(appDomain, (string)value);
  256. } else {
  257. if (!value.GetType().IsPrimitive)
  258. throw new DebuggerException("Value must be primitve type. Seen " + value.GetType());
  259. Value val = Eval.NewObjectNoConstructor(DebugType.CreateFromType(appDomain.Mscorlib, value.GetType()));
  260. val.PrimitiveValue = value;
  261. return val;
  262. }
  263. }
  264. /*
  265. // The following function create values only for the purpuse of evalutaion
  266. // They actually do not allocate memory on the managed heap
  267. // The advantage is that it does not continue the process
  268. /// <exception cref="DebuggerException">Can not create string this way</exception>
  269. public static Value CreateValue(Process process, object value)
  270. {
  271. if (value is string) throw new DebuggerException("Can not create string this way");
  272. CorElementType corElemType;
  273. ICorDebugClass corClass = null;
  274. if (value != null) {
  275. corElemType = DebugType.TypeNameToCorElementType(value.GetType().FullName);
  276. } else {
  277. corElemType = CorElementType.CLASS;
  278. corClass = DebugType.Create(process, null, typeof(object).FullName).CorType.Class;
  279. }
  280. ICorDebugEval corEval = CreateCorEval(process);
  281. ICorDebugValue corValue = corEval.CreateValue((uint)corElemType, corClass);
  282. Value v = new Value(process, new Expressions.PrimitiveExpression(value), corValue);
  283. if (value != null) {
  284. v.PrimitiveValue = value;
  285. }
  286. return v;
  287. }
  288. */
  289. #region Convenience methods
  290. public static Value NewString(AppDomain appDomain, string textToCreate)
  291. {
  292. return AsyncNewString(appDomain, textToCreate).WaitForResult();
  293. }
  294. #endregion
  295. public static Eval AsyncNewString(AppDomain appDomain, string textToCreate)
  296. {
  297. return new Eval(
  298. appDomain,
  299. "New string: " + textToCreate,
  300. delegate(Eval eval) {
  301. eval.CorEval2.NewStringWithLength(textToCreate, (uint)textToCreate.Length);
  302. }
  303. );
  304. }
  305. #region Convenience methods
  306. public static Value NewArray(DebugType type, uint length, uint? lowerBound)
  307. {
  308. return AsyncNewArray(type, length, lowerBound).WaitForResult();
  309. }
  310. #endregion
  311. public static Eval AsyncNewArray(DebugType type, uint length, uint? lowerBound)
  312. {
  313. lowerBound = lowerBound ?? 0;
  314. return new Eval(
  315. type.AppDomain,
  316. "New array: " + type + "[" + length + "]",
  317. delegate(Eval eval) {
  318. // Multi-dimensional arrays not supported in .NET 2.0
  319. eval.CorEval2.NewParameterizedArray(type.CorType, 1, new uint[] { length }, new uint[] { lowerBound.Value });
  320. }
  321. );
  322. }
  323. #region Convenience methods
  324. public static Value NewObject(DebugMethodInfo constructor, Value[] constructorArguments)
  325. {
  326. return AsyncNewObject(constructor, constructorArguments).WaitForResult();
  327. }
  328. #endregion
  329. public static Eval AsyncNewObject(DebugMethodInfo constructor, Value[] constructorArguments)
  330. {
  331. ICorDebugValue[] constructorArgsCorDebug = ValuesAsCorDebug(constructorArguments);
  332. return new Eval(
  333. constructor.AppDomain,
  334. "New object: " + constructor.FullName,
  335. delegate(Eval eval) {
  336. eval.CorEval2.NewParameterizedObject(
  337. constructor.CorFunction,
  338. (uint)constructor.DeclaringType.GetGenericArguments().Length, ((DebugType)constructor.DeclaringType).GenericArgumentsAsCorDebugType,
  339. (uint)constructorArgsCorDebug.Length, constructorArgsCorDebug);
  340. }
  341. );
  342. }
  343. #region Convenience methods
  344. public static Value NewObjectNoConstructor(DebugType debugType)
  345. {
  346. return AsyncNewObjectNoConstructor(debugType).WaitForResult();
  347. }
  348. #endregion
  349. public static Eval AsyncNewObjectNoConstructor(DebugType debugType)
  350. {
  351. return new Eval(
  352. debugType.AppDomain,
  353. "New object: " + debugType.FullName,
  354. delegate(Eval eval) {
  355. eval.CorEval2.NewParameterizedObjectNoConstructor(debugType.CorType.GetClass(), (uint)debugType.GetGenericArguments().Length, debugType.GenericArgumentsAsCorDebugType);
  356. }
  357. );
  358. }
  359. static ICorDebugValue[] ValuesAsCorDebug(Value[] values)
  360. {
  361. ICorDebugValue[] valuesAsCorDebug = new ICorDebugValue[values.Length];
  362. for(int i = 0; i < values.Length; i++) {
  363. valuesAsCorDebug[i] = values[i].CorValue;
  364. }
  365. return valuesAsCorDebug;
  366. }
  367. }
  368. }