3 using System.Threading;
5 using System.Diagnostics;
6 using System.Collections;
7 using System.Collections.Generic;
8 using Mono.Cecil.Metadata;
10 namespace Mono.Debugger.Soft
12 public class VirtualMachine : Mirror
16 object startup_monitor;
17 AppDomainMirror root_domain;
18 Dictionary<int, EventRequest> requests;
19 ITargetProcess process;
21 internal Connection conn;
25 internal VirtualMachine (ITargetProcess process, Connection conn) : base () {
26 SetVirtualMachine (this);
28 queue_monitor = new Object ();
29 startup_monitor = new Object ();
30 requests = new Dictionary <int, EventRequest> ();
32 this.process = process;
33 conn.ErrorHandler += ErrorHandler;
36 // The standard output of the process is available normally through Process
37 public StreamReader StandardOutput { get; set; }
38 public StreamReader StandardError { get; set; }
41 public Process Process {
43 ProcessWrapper pw = process as ProcessWrapper;
45 throw new InvalidOperationException ("Process instance not available");
50 public ITargetProcess TargetProcess {
56 public AppDomainMirror RootDomain {
62 public EndPoint EndPoint {
68 public VersionInfo Version {
78 * It is impossible to determine when to resume when using this method, since
79 * the debuggee is suspended only once per event-set, not event.
81 [Obsolete ("Use GetNextEventSet () instead")]
82 public Event GetNextEvent () {
83 lock (queue_monitor) {
84 if (current_es == null || current_es_index == current_es.Events.Length) {
86 Monitor.Wait (queue_monitor);
87 current_es = (EventSet)queue.Dequeue ();
90 return current_es.Events [current_es_index ++];
94 public Event GetNextEvent (int timeout) {
95 throw new NotImplementedException ();
98 public EventSet GetNextEventSet () {
99 lock (queue_monitor) {
100 if (queue.Count == 0)
101 Monitor.Wait (queue_monitor);
104 current_es_index = 0;
106 return (EventSet)queue.Dequeue ();
110 [Obsolete ("Use GetNextEventSet () instead")]
111 public T GetNextEvent<T> () where T : Event {
112 return GetNextEvent () as T;
115 public void Suspend () {
119 public void Resume () {
122 } catch (CommandException ex) {
123 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
124 throw new InvalidOperationException ("The vm is not suspended.");
130 public void Exit (int exitCode) {
131 conn.VM_Exit (exitCode);
134 public void Dispose () {
137 notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, 0, 0, null);
140 public IList<ThreadMirror> GetThreads () {
141 long[] ids = vm.conn.VM_GetThreads ();
142 ThreadMirror[] res = new ThreadMirror [ids.Length];
143 for (int i = 0; i < ids.Length; ++i)
144 res [i] = GetThread (ids [i]);
148 // Same as the mirrorOf methods in JDI
149 public PrimitiveValue CreateValue (object value) {
151 return new PrimitiveValue (vm, null);
153 if (!value.GetType ().IsPrimitive)
154 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
156 return new PrimitiveValue (vm, value);
159 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
160 return new EnumMirror (this, type, value);
164 // Methods to create event request objects
166 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
167 return new BreakpointEventRequest (this, method, il_offset);
170 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
172 throw new ArgumentNullException ("loc");
174 return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
177 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
178 return new StepEventRequest (this, thread);
181 public MethodEntryEventRequest CreateMethodEntryRequest () {
182 return new MethodEntryEventRequest (this);
185 public MethodExitEventRequest CreateMethodExitRequest () {
186 return new MethodExitEventRequest (this);
189 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
190 return new ExceptionEventRequest (this, exc_type, true, true);
193 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
194 return new ExceptionEventRequest (this, exc_type, caught, uncaught);
197 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
198 return new AssemblyLoadEventRequest (this);
201 public void EnableEvents (params EventType[] events) {
202 foreach (EventType etype in events) {
203 if (etype == EventType.Breakpoint)
204 throw new ArgumentException ("Breakpoint events cannot be requested using EnableEvents", "events");
205 conn.EnableEvent (etype, SuspendPolicy.All, null);
209 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
210 BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
217 public void ClearAllBreakpoints () {
218 conn.ClearAllBreakpoints ();
221 public void Disconnect () {
225 internal void queue_event_set (EventSet es) {
226 lock (queue_monitor) {
228 Monitor.Pulse (queue_monitor);
232 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
233 switch (args.ErrorCode) {
234 case ErrorCode.INVALID_OBJECT:
235 throw new ObjectCollectedException ();
236 case ErrorCode.INVALID_FRAMEID:
237 throw new InvalidStackFrameException ();
238 case ErrorCode.NOT_SUSPENDED:
239 throw new InvalidOperationException ("The vm is not suspended.");
240 case ErrorCode.NOT_IMPLEMENTED:
241 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
242 case ErrorCode.ABSENT_INFORMATION:
243 throw new AbsentInformationException ();
244 case ErrorCode.NO_SEQ_POINT_AT_IL_OFFSET:
245 throw new ArgumentException ("Cannot set breakpoint on the specified IL offset.");
247 throw new CommandException (args.ErrorCode);
251 /* Wait for the debuggee to start up and connect to it */
252 internal void connect () {
255 // Test the connection
256 version = conn.Version;
257 if (version.MajorVersion != Connection.MAJOR_VERSION)
258 throw new NotSupportedException (String.Format ("The debuggee implements protocol version {0}.{1}, while {2}.{3} is required.", version.MajorVersion, version.MinorVersion, Connection.MAJOR_VERSION, Connection.MINOR_VERSION));
260 long root_domain_id = conn.RootDomain;
261 root_domain = GetDomain (root_domain_id);
264 internal void notify_vm_event (EventType evtype, SuspendPolicy spolicy, int req_id, long thread_id, string vm_uri) {
265 //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
268 case EventType.VMStart:
269 /* Notify the main thread that the debuggee started up */
270 lock (startup_monitor) {
271 Monitor.Pulse (startup_monitor);
273 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
275 case EventType.VMDeath:
276 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id) }));
278 case EventType.VMDisconnect:
279 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
282 throw new Exception ();
287 // Methods to create instances of mirror objects
291 class MirrorCache<T> {
292 static Dictionary <long, T> mirrors;
293 static object mirror_lock = new object ();
295 internal static T GetMirror (VirtualMachine vm, long id) {
298 mirrors = new Dictionary <long, T> ();
300 if (!mirrors.TryGetValue (id, out obj)) {
301 obj = CreateMirror (vm, id);
308 internal static T CreateMirror (VirtualMachine vm, long id) {
313 // FIXME: When to remove items from the cache ?
315 Dictionary <long, MethodMirror> methods;
316 object methods_lock = new object ();
318 internal MethodMirror GetMethod (long id) {
319 lock (methods_lock) {
321 methods = new Dictionary <long, MethodMirror> ();
325 if (!methods.TryGetValue (id, out obj)) {
326 obj = new MethodMirror (this, id);
333 Dictionary <long, AssemblyMirror> assemblies;
334 object assemblies_lock = new object ();
336 internal AssemblyMirror GetAssembly (long id) {
337 lock (assemblies_lock) {
338 if (assemblies == null)
339 assemblies = new Dictionary <long, AssemblyMirror> ();
343 if (!assemblies.TryGetValue (id, out obj)) {
344 obj = new AssemblyMirror (this, id);
345 assemblies [id] = obj;
351 Dictionary <long, ModuleMirror> modules;
352 object modules_lock = new object ();
354 internal ModuleMirror GetModule (long id) {
355 lock (modules_lock) {
357 modules = new Dictionary <long, ModuleMirror> ();
361 if (!modules.TryGetValue (id, out obj)) {
362 obj = new ModuleMirror (this, id);
369 Dictionary <long, AppDomainMirror> domains;
370 object domains_lock = new object ();
372 internal AppDomainMirror GetDomain (long id) {
373 lock (domains_lock) {
375 domains = new Dictionary <long, AppDomainMirror> ();
379 if (!domains.TryGetValue (id, out obj)) {
380 obj = new AppDomainMirror (this, id);
387 Dictionary <long, TypeMirror> types;
388 object types_lock = new object ();
390 internal TypeMirror GetType (long id) {
393 types = new Dictionary <long, TypeMirror> ();
397 if (!types.TryGetValue (id, out obj)) {
398 obj = new TypeMirror (this, id);
405 Dictionary <long, ObjectMirror> objects;
406 object objects_lock = new object ();
408 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
409 lock (objects_lock) {
411 objects = new Dictionary <long, ObjectMirror> ();
413 if (!objects.TryGetValue (id, out obj)) {
415 * Obtain the domain/type of the object to determine the type of
416 * object we need to create.
419 domain_id = conn.Object_GetDomain (id);
420 AppDomainMirror d = GetDomain (domain_id);
423 type_id = conn.Object_GetType (id);
424 TypeMirror t = GetType (type_id);
426 if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
427 obj = new ThreadMirror (this, id);
428 else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
429 obj = new StringMirror (this, id);
430 else if (typeof (T) == typeof (ArrayMirror))
431 obj = new ArrayMirror (this, id);
433 obj = new ObjectMirror (this, id);
440 internal T GetObject<T> (long id) where T : ObjectMirror {
441 return GetObject<T> (id, 0, 0);
444 internal ObjectMirror GetObject (long objid) {
445 return GetObject<ObjectMirror> (objid);
448 internal ThreadMirror GetThread (long id) {
449 return GetObject <ThreadMirror> (id);
452 object requests_lock = new object ();
454 internal void AddRequest (EventRequest req, int id) {
455 lock (requests_lock) {
460 internal void RemoveRequest (EventRequest req, int id) {
461 lock (requests_lock) {
462 requests.Remove (id);
466 internal EventRequest GetRequest (int id) {
467 lock (requests_lock) {
468 return requests [id];
472 internal Value DecodeValue (ValueImpl v) {
474 return new PrimitiveValue (this, v.Value);
477 case ElementType.Void:
479 case ElementType.SzArray:
480 case ElementType.Array:
481 return GetObject<ArrayMirror> (v.Objid);
482 case ElementType.String:
483 return GetObject<StringMirror> (v.Objid);
484 case ElementType.Class:
485 case ElementType.Object:
486 return GetObject (v.Objid);
487 case ElementType.ValueType:
489 return new EnumMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
491 return new StructMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
492 case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
493 return new PrimitiveValue (this, null);
495 throw new NotImplementedException ("" + v.Type);
499 internal Value[] DecodeValues (ValueImpl[] values) {
500 Value[] res = new Value [values.Length];
501 for (int i = 0; i < values.Length; ++i)
502 res [i] = DecodeValue (values [i]);
506 internal ValueImpl EncodeValue (Value v) {
507 if (v is PrimitiveValue) {
508 object val = (v as PrimitiveValue).Value;
510 return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
512 return new ValueImpl { Value = val };
513 } else if (v is ObjectMirror) {
514 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
515 } else if (v is StructMirror) {
516 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields) };
518 throw new NotSupportedException ();
522 internal ValueImpl[] EncodeValues (IList<Value> values) {
523 ValueImpl[] res = new ValueImpl [values.Count];
524 for (int i = 0; i < values.Count; ++i)
525 res [i] = EncodeValue (values [i]);
530 class EventHandler : MarshalByRefObject, IEventHandler
534 public EventHandler (VirtualMachine vm) {
538 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
539 var l = new List<Event> ();
541 for (int i = 0; i < events.Length; ++i) {
542 EventInfo ei = events [i];
543 int req_id = ei.ReqId;
544 long thread_id = ei.ThreadId;
546 long loc = ei.Location;
548 switch (ei.EventType) {
549 case EventType.VMStart:
550 vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null);
552 case EventType.VMDeath:
553 vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null);
555 case EventType.ThreadStart:
556 l.Add (new ThreadStartEvent (vm, req_id, id));
558 case EventType.ThreadDeath:
559 l.Add (new ThreadDeathEvent (vm, req_id, id));
561 case EventType.AssemblyLoad:
562 l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
564 case EventType.AssemblyUnload:
565 l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
567 case EventType.TypeLoad:
568 l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
570 case EventType.MethodEntry:
571 l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
573 case EventType.MethodExit:
574 l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
576 case EventType.Breakpoint:
577 l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
580 l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
582 case EventType.Exception:
583 l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
585 case EventType.AppDomainCreate:
586 l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
588 case EventType.AppDomainUnload:
589 l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
597 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
600 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
601 vm.notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, req_id, thread_id, vm_uri);
605 internal class CommandException : Exception {
607 public CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
608 ErrorCode = error_code;
611 public ErrorCode ErrorCode {