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 {
64 var tcpConn = conn as TcpConnection;
66 return tcpConn.EndPoint;
71 public VersionInfo Version {
81 * It is impossible to determine when to resume when using this method, since
82 * the debuggee is suspended only once per event-set, not event.
84 [Obsolete ("Use GetNextEventSet () instead")]
85 public Event GetNextEvent () {
86 lock (queue_monitor) {
87 if (current_es == null || current_es_index == current_es.Events.Length) {
89 Monitor.Wait (queue_monitor);
90 current_es = (EventSet)queue.Dequeue ();
93 return current_es.Events [current_es_index ++];
97 public Event GetNextEvent (int timeout) {
98 throw new NotImplementedException ();
101 public EventSet GetNextEventSet () {
102 lock (queue_monitor) {
103 if (queue.Count == 0)
104 Monitor.Wait (queue_monitor);
107 current_es_index = 0;
109 return (EventSet)queue.Dequeue ();
113 [Obsolete ("Use GetNextEventSet () instead")]
114 public T GetNextEvent<T> () where T : Event {
115 return GetNextEvent () as T;
118 public void Suspend () {
122 public void Resume () {
125 } catch (CommandException ex) {
126 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
127 throw new InvalidOperationException ("The vm is not suspended.");
133 public void Exit (int exitCode) {
134 conn.VM_Exit (exitCode);
137 public void Detach () {
140 notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, 0, 0, null);
143 [Obsolete ("This method was poorly named; use the Detach() method instead")]
144 public void Dispose ()
149 public void ForceDisconnect ()
151 conn.ForceDisconnect ();
154 public IList<ThreadMirror> GetThreads () {
155 long[] ids = vm.conn.VM_GetThreads ();
156 ThreadMirror[] res = new ThreadMirror [ids.Length];
157 for (int i = 0; i < ids.Length; ++i)
158 res [i] = GetThread (ids [i]);
162 // Same as the mirrorOf methods in JDI
163 public PrimitiveValue CreateValue (object value) {
165 return new PrimitiveValue (vm, null);
167 if (!value.GetType ().IsPrimitive)
168 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
170 return new PrimitiveValue (vm, value);
173 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
174 return new EnumMirror (this, type, value);
178 // Enable send and receive timeouts on the connection and send a keepalive event
179 // every 'keepalive_interval' milliseconds.
182 public void SetSocketTimeouts (int send_timeout, int receive_timeout, int keepalive_interval)
184 conn.SetSocketTimeouts (send_timeout, receive_timeout, keepalive_interval);
188 // Methods to create event request objects
190 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
191 return new BreakpointEventRequest (this, method, il_offset);
194 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
196 throw new ArgumentNullException ("loc");
198 return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
201 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
202 return new StepEventRequest (this, thread);
205 public MethodEntryEventRequest CreateMethodEntryRequest () {
206 return new MethodEntryEventRequest (this);
209 public MethodExitEventRequest CreateMethodExitRequest () {
210 return new MethodExitEventRequest (this);
213 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
214 return new ExceptionEventRequest (this, exc_type, true, true);
217 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
218 return new ExceptionEventRequest (this, exc_type, caught, uncaught);
221 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
222 return new AssemblyLoadEventRequest (this);
225 public TypeLoadEventRequest CreateTypeLoadRequest () {
226 return new TypeLoadEventRequest (this);
229 public void EnableEvents (params EventType[] events) {
230 foreach (EventType etype in events) {
231 if (etype == EventType.Breakpoint)
232 throw new ArgumentException ("Breakpoint events cannot be requested using EnableEvents", "events");
233 conn.EnableEvent (etype, SuspendPolicy.All, null);
237 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
238 BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
245 public void ClearAllBreakpoints () {
246 conn.ClearAllBreakpoints ();
249 public void Disconnect () {
254 // Return a list of TypeMirror objects for all loaded types which reference the
255 // source file FNAME. Might return false positives.
256 // Since protocol version 2.7.
258 public IList<TypeMirror> GetTypesForSourceFile (string fname, bool ignoreCase) {
259 long[] ids = conn.VM_GetTypesForSourceFile (fname, ignoreCase);
260 var res = new TypeMirror [ids.Length];
261 for (int i = 0; i < ids.Length; ++i)
262 res [i] = GetType (ids [i]);
267 // Return a list of TypeMirror objects for all loaded types named 'NAME'.
268 // NAME should be in the the same for as with Assembly.GetType ().
269 // Since protocol version 2.9.
271 public IList<TypeMirror> GetTypes (string name, bool ignoreCase) {
272 long[] ids = conn.VM_GetTypes (name, ignoreCase);
273 var res = new TypeMirror [ids.Length];
274 for (int i = 0; i < ids.Length; ++i)
275 res [i] = GetType (ids [i]);
279 internal void queue_event_set (EventSet es) {
280 lock (queue_monitor) {
282 Monitor.Pulse (queue_monitor);
286 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
287 switch (args.ErrorCode) {
288 case ErrorCode.INVALID_OBJECT:
289 throw new ObjectCollectedException ();
290 case ErrorCode.INVALID_FRAMEID:
291 throw new InvalidStackFrameException ();
292 case ErrorCode.NOT_SUSPENDED:
293 throw new InvalidOperationException ("The vm is not suspended.");
294 case ErrorCode.NOT_IMPLEMENTED:
295 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
296 case ErrorCode.ABSENT_INFORMATION:
297 throw new AbsentInformationException ();
298 case ErrorCode.NO_SEQ_POINT_AT_IL_OFFSET:
299 throw new ArgumentException ("Cannot set breakpoint on the specified IL offset.");
301 throw new CommandException (args.ErrorCode);
305 /* Wait for the debuggee to start up and connect to it */
306 internal void connect () {
309 // Test the connection
310 version = conn.Version;
311 if (version.MajorVersion != Connection.MAJOR_VERSION)
312 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));
314 long root_domain_id = conn.RootDomain;
315 root_domain = GetDomain (root_domain_id);
318 internal void notify_vm_event (EventType evtype, SuspendPolicy spolicy, int req_id, long thread_id, string vm_uri) {
319 //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
322 case EventType.VMStart:
323 /* Notify the main thread that the debuggee started up */
324 lock (startup_monitor) {
325 Monitor.Pulse (startup_monitor);
327 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
329 case EventType.VMDeath:
330 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id) }));
332 case EventType.VMDisconnect:
333 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
336 throw new Exception ();
341 // Methods to create instances of mirror objects
345 class MirrorCache<T> {
346 static Dictionary <long, T> mirrors;
347 static object mirror_lock = new object ();
349 internal static T GetMirror (VirtualMachine vm, long id) {
352 mirrors = new Dictionary <long, T> ();
354 if (!mirrors.TryGetValue (id, out obj)) {
355 obj = CreateMirror (vm, id);
362 internal static T CreateMirror (VirtualMachine vm, long id) {
367 // FIXME: When to remove items from the cache ?
369 Dictionary <long, MethodMirror> methods;
370 object methods_lock = new object ();
372 internal MethodMirror GetMethod (long id) {
373 lock (methods_lock) {
375 methods = new Dictionary <long, MethodMirror> ();
379 if (!methods.TryGetValue (id, out obj)) {
380 obj = new MethodMirror (this, id);
387 Dictionary <long, AssemblyMirror> assemblies;
388 object assemblies_lock = new object ();
390 internal AssemblyMirror GetAssembly (long id) {
391 lock (assemblies_lock) {
392 if (assemblies == null)
393 assemblies = new Dictionary <long, AssemblyMirror> ();
397 if (!assemblies.TryGetValue (id, out obj)) {
398 obj = new AssemblyMirror (this, id);
399 assemblies [id] = obj;
405 Dictionary <long, ModuleMirror> modules;
406 object modules_lock = new object ();
408 internal ModuleMirror GetModule (long id) {
409 lock (modules_lock) {
411 modules = new Dictionary <long, ModuleMirror> ();
415 if (!modules.TryGetValue (id, out obj)) {
416 obj = new ModuleMirror (this, id);
423 Dictionary <long, AppDomainMirror> domains;
424 object domains_lock = new object ();
426 internal AppDomainMirror GetDomain (long id) {
427 lock (domains_lock) {
429 domains = new Dictionary <long, AppDomainMirror> ();
433 if (!domains.TryGetValue (id, out obj)) {
434 obj = new AppDomainMirror (this, id);
441 Dictionary <long, TypeMirror> types;
442 object types_lock = new object ();
444 internal TypeMirror GetType (long id) {
447 types = new Dictionary <long, TypeMirror> ();
451 if (!types.TryGetValue (id, out obj)) {
452 obj = new TypeMirror (this, id);
459 internal TypeMirror[] GetTypes (long[] ids) {
460 var res = new TypeMirror [ids.Length];
461 for (int i = 0; i < ids.Length; ++i)
462 res [i] = GetType (ids [i]);
466 Dictionary <long, ObjectMirror> objects;
467 object objects_lock = new object ();
469 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
470 lock (objects_lock) {
472 objects = new Dictionary <long, ObjectMirror> ();
474 if (!objects.TryGetValue (id, out obj)) {
476 * Obtain the domain/type of the object to determine the type of
477 * object we need to create.
479 if (domain_id == 0 || type_id == 0) {
480 if (conn.Version.AtLeast (2, 5)) {
481 var info = conn.Object_GetInfo (id);
482 domain_id = info.domain_id;
483 type_id = info.type_id;
486 domain_id = conn.Object_GetDomain (id);
488 type_id = conn.Object_GetType (id);
491 AppDomainMirror d = GetDomain (domain_id);
492 TypeMirror t = GetType (type_id);
494 if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
495 obj = new ThreadMirror (this, id, t, d);
496 else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
497 obj = new StringMirror (this, id, t, d);
498 else if (typeof (T) == typeof (ArrayMirror))
499 obj = new ArrayMirror (this, id, t, d);
501 obj = new ObjectMirror (this, id, t, d);
508 internal T GetObject<T> (long id) where T : ObjectMirror {
509 return GetObject<T> (id, 0, 0);
512 internal ObjectMirror GetObject (long objid) {
513 return GetObject<ObjectMirror> (objid);
516 internal ThreadMirror GetThread (long id) {
517 return GetObject <ThreadMirror> (id);
520 object requests_lock = new object ();
522 internal void AddRequest (EventRequest req, int id) {
523 lock (requests_lock) {
528 internal void RemoveRequest (EventRequest req, int id) {
529 lock (requests_lock) {
530 requests.Remove (id);
534 internal EventRequest GetRequest (int id) {
535 lock (requests_lock) {
536 return requests [id];
540 internal Value DecodeValue (ValueImpl v) {
542 return new PrimitiveValue (this, v.Value);
545 case ElementType.Void:
547 case ElementType.SzArray:
548 case ElementType.Array:
549 return GetObject<ArrayMirror> (v.Objid);
550 case ElementType.String:
551 return GetObject<StringMirror> (v.Objid);
552 case ElementType.Class:
553 case ElementType.Object:
554 return GetObject (v.Objid);
555 case ElementType.ValueType:
557 return new EnumMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
559 return new StructMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
560 case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
561 return new PrimitiveValue (this, null);
563 throw new NotImplementedException ("" + v.Type);
567 internal Value[] DecodeValues (ValueImpl[] values) {
568 Value[] res = new Value [values.Length];
569 for (int i = 0; i < values.Length; ++i)
570 res [i] = DecodeValue (values [i]);
574 internal ValueImpl EncodeValue (Value v) {
575 if (v is PrimitiveValue) {
576 object val = (v as PrimitiveValue).Value;
578 return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
580 return new ValueImpl { Value = val };
581 } else if (v is ObjectMirror) {
582 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
583 } else if (v is StructMirror) {
584 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields) };
586 throw new NotSupportedException ();
590 internal ValueImpl[] EncodeValues (IList<Value> values) {
591 ValueImpl[] res = new ValueImpl [values.Count];
592 for (int i = 0; i < values.Count; ++i)
593 res [i] = EncodeValue (values [i]);
597 internal void CheckProtocolVersion (int major, int minor) {
598 if (!conn.Version.AtLeast (major, minor))
599 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
603 class EventHandler : MarshalByRefObject, IEventHandler
607 public EventHandler (VirtualMachine vm) {
611 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
612 var l = new List<Event> ();
614 for (int i = 0; i < events.Length; ++i) {
615 EventInfo ei = events [i];
616 int req_id = ei.ReqId;
617 long thread_id = ei.ThreadId;
619 long loc = ei.Location;
621 switch (ei.EventType) {
622 case EventType.VMStart:
623 vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null);
625 case EventType.VMDeath:
626 vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null);
628 case EventType.ThreadStart:
629 l.Add (new ThreadStartEvent (vm, req_id, id));
631 case EventType.ThreadDeath:
632 l.Add (new ThreadDeathEvent (vm, req_id, id));
634 case EventType.AssemblyLoad:
635 l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
637 case EventType.AssemblyUnload:
638 l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
640 case EventType.TypeLoad:
641 l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
643 case EventType.MethodEntry:
644 l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
646 case EventType.MethodExit:
647 l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
649 case EventType.Breakpoint:
650 l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
653 l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
655 case EventType.Exception:
656 l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
658 case EventType.AppDomainCreate:
659 l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
661 case EventType.AppDomainUnload:
662 l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
664 case EventType.UserBreak:
665 l.Add (new UserBreakEvent (vm, req_id, thread_id));
667 case EventType.UserLog:
668 l.Add (new UserLogEvent (vm, req_id, thread_id, ei.Level, ei.Category, ei.Message));
676 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
679 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
680 vm.notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, req_id, thread_id, vm_uri);
684 public class CommandException : Exception {
686 internal CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
687 ErrorCode = error_code;
690 public ErrorCode ErrorCode {