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 () {
124 InvalidateThreadAndFrameCaches ();
126 } catch (CommandException ex) {
127 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
128 throw new VMNotSuspendedException ();
134 public void Exit (int exitCode) {
135 conn.VM_Exit (exitCode);
138 public void Detach () {
141 notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, 0, 0, null, 0);
144 [Obsolete ("This method was poorly named; use the Detach() method instead")]
145 public void Dispose ()
150 public void ForceDisconnect ()
152 conn.ForceDisconnect ();
155 HashSet<ThreadMirror> threadsToInvalidate = new HashSet<ThreadMirror> ();
156 ThreadMirror[] threadCache;
157 object threadCacheLocker = new object ();
159 void InvalidateThreadAndFrameCaches () {
160 lock (threadsToInvalidate) {
161 foreach (var thread in threadsToInvalidate)
162 thread.InvalidateFrames ();
163 threadsToInvalidate.Clear ();
168 internal void InvalidateThreadCache () {
172 internal void AddThreadToInvalidateList (ThreadMirror threadMirror)
174 lock (threadsToInvalidate) {
175 threadsToInvalidate.Add (threadMirror);
179 public IList<ThreadMirror> GetThreads () {
180 var threads = threadCache;
181 if (threads == null) {
183 var fetchingEvent = new ManualResetEvent (false);
184 vm.conn.VM_GetThreads ((threadsIds) => {
186 threadCache = threads = new ThreadMirror [threadsIds.Length];
187 fetchingEvent.Set ();
189 if (WaitHandle.WaitAny (new []{ vm.conn.DisconnectedEvent, fetchingEvent }) == 0) {
190 throw new VMDisconnectedException ();
192 for (int i = 0; i < ids.Length; ++i)
193 threads [i] = GetThread (ids [i]);
194 //Uncomment lines below if you want to re-fetch threads if new threads were started/stopped while
195 //featching threads... This is probably more correct but might cause deadlock of this method if runtime
196 //is starting/stopping threads nonstop, need way to prevent this(counting number of recursions?)
197 //possiblity before uncommenting
198 //if (threadCache != threads) {//While fetching threads threadCache was invalidated(thread was created/destoyed)
199 // return GetThreads ();
207 // Same as the mirrorOf methods in JDI
208 public PrimitiveValue CreateValue (object value) {
210 return new PrimitiveValue (vm, null);
212 if (!value.GetType ().IsPrimitive)
213 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
215 return new PrimitiveValue (vm, value);
218 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
219 return new EnumMirror (this, type, value);
223 // Enable send and receive timeouts on the connection and send a keepalive event
224 // every 'keepalive_interval' milliseconds.
227 public void SetSocketTimeouts (int send_timeout, int receive_timeout, int keepalive_interval)
229 conn.SetSocketTimeouts (send_timeout, receive_timeout, keepalive_interval);
233 // Methods to create event request objects
235 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
236 return new BreakpointEventRequest (this, method, il_offset);
239 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
241 throw new ArgumentNullException ("loc");
243 return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
246 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
247 return new StepEventRequest (this, thread);
250 public MethodEntryEventRequest CreateMethodEntryRequest () {
251 return new MethodEntryEventRequest (this);
254 public MethodExitEventRequest CreateMethodExitRequest () {
255 return new MethodExitEventRequest (this);
258 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
259 return new ExceptionEventRequest (this, exc_type, true, true);
262 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
263 return new ExceptionEventRequest (this, exc_type, caught, uncaught);
266 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
267 return new AssemblyLoadEventRequest (this);
270 public TypeLoadEventRequest CreateTypeLoadRequest () {
271 return new TypeLoadEventRequest (this);
274 public void EnableEvents (params EventType[] events) {
275 EnableEvents (events, SuspendPolicy.All);
278 public void EnableEvents (EventType[] events, SuspendPolicy suspendPolicy) {
279 foreach (EventType etype in events) {
280 if (etype == EventType.Breakpoint)
281 throw new ArgumentException ("Breakpoint events cannot be requested using EnableEvents", "events");
282 conn.EnableEvent (etype, suspendPolicy, null);
286 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
287 BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
294 public void ClearAllBreakpoints () {
295 conn.ClearAllBreakpoints ();
298 public void Disconnect () {
303 // Return a list of TypeMirror objects for all loaded types which reference the
304 // source file FNAME. Might return false positives.
305 // Since protocol version 2.7.
307 public IList<TypeMirror> GetTypesForSourceFile (string fname, bool ignoreCase) {
308 long[] ids = conn.VM_GetTypesForSourceFile (fname, ignoreCase);
309 var res = new TypeMirror [ids.Length];
310 for (int i = 0; i < ids.Length; ++i)
311 res [i] = GetType (ids [i]);
316 // Return a list of TypeMirror objects for all loaded types named 'NAME'.
317 // NAME should be in the the same for as with Assembly.GetType ().
318 // Since protocol version 2.9.
320 public IList<TypeMirror> GetTypes (string name, bool ignoreCase) {
321 long[] ids = conn.VM_GetTypes (name, ignoreCase);
322 var res = new TypeMirror [ids.Length];
323 for (int i = 0; i < ids.Length; ++i)
324 res [i] = GetType (ids [i]);
328 internal void queue_event_set (EventSet es) {
329 lock (queue_monitor) {
331 Monitor.Pulse (queue_monitor);
335 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
336 switch (args.ErrorCode) {
337 case ErrorCode.INVALID_OBJECT:
338 throw new ObjectCollectedException ();
339 case ErrorCode.INVALID_FRAMEID:
340 throw new InvalidStackFrameException ();
341 case ErrorCode.NOT_SUSPENDED:
342 throw new VMNotSuspendedException ();
343 case ErrorCode.NOT_IMPLEMENTED:
344 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
345 case ErrorCode.ABSENT_INFORMATION:
346 throw new AbsentInformationException ();
347 case ErrorCode.NO_SEQ_POINT_AT_IL_OFFSET:
348 throw new ArgumentException ("Cannot set breakpoint on the specified IL offset.");
350 throw new CommandException (args.ErrorCode);
354 /* Wait for the debuggee to start up and connect to it */
355 internal void connect () {
358 // Test the connection
359 version = conn.Version;
360 if (version.MajorVersion != Connection.MAJOR_VERSION)
361 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));
363 long root_domain_id = conn.RootDomain;
364 root_domain = GetDomain (root_domain_id);
367 internal void notify_vm_event (EventType evtype, SuspendPolicy spolicy, int req_id, long thread_id, string vm_uri, int exit_code) {
368 //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
371 case EventType.VMStart:
372 /* Notify the main thread that the debuggee started up */
373 lock (startup_monitor) {
374 Monitor.Pulse (startup_monitor);
376 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
378 case EventType.VMDeath:
379 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id, exit_code) }));
381 case EventType.VMDisconnect:
382 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
385 throw new Exception ();
390 // Methods to create instances of mirror objects
394 class MirrorCache<T> {
395 static Dictionary <long, T> mirrors;
396 static object mirror_lock = new object ();
398 internal static T GetMirror (VirtualMachine vm, long id) {
401 mirrors = new Dictionary <long, T> ();
403 if (!mirrors.TryGetValue (id, out obj)) {
404 obj = CreateMirror (vm, id);
411 internal static T CreateMirror (VirtualMachine vm, long id) {
416 // FIXME: When to remove items from the cache ?
418 Dictionary <long, MethodMirror> methods;
419 object methods_lock = new object ();
421 internal MethodMirror GetMethod (long id) {
422 lock (methods_lock) {
424 methods = new Dictionary <long, MethodMirror> ();
428 if (!methods.TryGetValue (id, out obj)) {
429 obj = new MethodMirror (this, id);
436 Dictionary <long, AssemblyMirror> assemblies;
437 object assemblies_lock = new object ();
439 internal AssemblyMirror GetAssembly (long id) {
440 lock (assemblies_lock) {
441 if (assemblies == null)
442 assemblies = new Dictionary <long, AssemblyMirror> ();
446 if (!assemblies.TryGetValue (id, out obj)) {
447 obj = new AssemblyMirror (this, id);
448 assemblies [id] = obj;
454 Dictionary <long, ModuleMirror> modules;
455 object modules_lock = new object ();
457 internal ModuleMirror GetModule (long id) {
458 lock (modules_lock) {
460 modules = new Dictionary <long, ModuleMirror> ();
464 if (!modules.TryGetValue (id, out obj)) {
465 obj = new ModuleMirror (this, id);
472 Dictionary <long, AppDomainMirror> domains;
473 object domains_lock = new object ();
475 internal AppDomainMirror GetDomain (long id) {
476 lock (domains_lock) {
478 domains = new Dictionary <long, AppDomainMirror> ();
482 if (!domains.TryGetValue (id, out obj)) {
483 obj = new AppDomainMirror (this, id);
490 internal void InvalidateAssemblyCaches () {
491 lock (domains_lock) {
492 foreach (var d in domains.Values)
493 d.InvalidateAssembliesCache ();
497 Dictionary <long, TypeMirror> types;
498 object types_lock = new object ();
500 internal TypeMirror GetType (long id) {
503 types = new Dictionary <long, TypeMirror> ();
507 if (!types.TryGetValue (id, out obj)) {
508 obj = new TypeMirror (this, id);
515 internal TypeMirror[] GetTypes (long[] ids) {
516 var res = new TypeMirror [ids.Length];
517 for (int i = 0; i < ids.Length; ++i)
518 res [i] = GetType (ids [i]);
522 Dictionary <long, ObjectMirror> objects;
523 object objects_lock = new object ();
525 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
526 lock (objects_lock) {
528 objects = new Dictionary <long, ObjectMirror> ();
530 if (!objects.TryGetValue (id, out obj)) {
532 * Obtain the domain/type of the object to determine the type of
533 * object we need to create.
535 if (domain_id == 0 || type_id == 0) {
536 if (conn.Version.AtLeast (2, 5)) {
537 var info = conn.Object_GetInfo (id);
538 domain_id = info.domain_id;
539 type_id = info.type_id;
542 domain_id = conn.Object_GetDomain (id);
544 type_id = conn.Object_GetType (id);
547 AppDomainMirror d = GetDomain (domain_id);
548 TypeMirror t = GetType (type_id);
550 if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
551 obj = new ThreadMirror (this, id, t, d);
552 else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
553 obj = new StringMirror (this, id, t, d);
554 else if (typeof (T) == typeof (ArrayMirror))
555 obj = new ArrayMirror (this, id, t, d);
557 obj = new ObjectMirror (this, id, t, d);
564 internal T GetObject<T> (long id) where T : ObjectMirror {
565 return GetObject<T> (id, 0, 0);
568 internal ObjectMirror GetObject (long objid) {
569 return GetObject<ObjectMirror> (objid);
572 internal ThreadMirror GetThread (long id) {
573 return GetObject <ThreadMirror> (id);
576 Dictionary <long, FieldInfoMirror> fields;
577 object fields_lock = new object ();
579 internal FieldInfoMirror GetField (long id) {
582 fields = new Dictionary <long, FieldInfoMirror> ();
586 if (!fields.TryGetValue (id, out obj)) {
587 obj = new FieldInfoMirror (this, id);
594 object requests_lock = new object ();
596 internal void AddRequest (EventRequest req, int id) {
597 lock (requests_lock) {
602 internal void RemoveRequest (EventRequest req, int id) {
603 lock (requests_lock) {
604 requests.Remove (id);
608 internal EventRequest GetRequest (int id) {
609 lock (requests_lock) {
610 return requests [id];
614 internal Value DecodeValue (ValueImpl v) {
615 return DecodeValue (v, null);
618 internal Value DecodeValue (ValueImpl v, Dictionary<int, Value> parent_vtypes) {
620 return new PrimitiveValue (this, v.Value);
623 case ElementType.Void:
625 case ElementType.SzArray:
626 case ElementType.Array:
627 return GetObject<ArrayMirror> (v.Objid);
628 case ElementType.String:
629 return GetObject<StringMirror> (v.Objid);
630 case ElementType.Class:
631 case ElementType.Object:
632 return GetObject (v.Objid);
633 case ElementType.ValueType:
634 if (parent_vtypes == null)
635 parent_vtypes = new Dictionary<int, Value> ();
638 vtype = new EnumMirror (this, GetType (v.Klass), (Value[])null);
640 vtype = new StructMirror (this, GetType (v.Klass), (Value[])null);
641 parent_vtypes [parent_vtypes.Count] = vtype;
642 vtype.SetFields (DecodeValues (v.Fields, parent_vtypes));
643 parent_vtypes.Remove (parent_vtypes.Count - 1);
645 case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
646 return new PrimitiveValue (this, null);
647 case (ElementType)ValueTypeId.VALUE_TYPE_ID_PARENT_VTYPE:
648 return parent_vtypes [v.Index];
650 throw new NotImplementedException ("" + v.Type);
654 internal Value[] DecodeValues (ValueImpl[] values) {
655 Value[] res = new Value [values.Length];
656 for (int i = 0; i < values.Length; ++i)
657 res [i] = DecodeValue (values [i]);
661 internal Value[] DecodeValues (ValueImpl[] values, Dictionary<int, Value> parent_vtypes) {
662 Value[] res = new Value [values.Length];
663 for (int i = 0; i < values.Length; ++i)
664 res [i] = DecodeValue (values [i], parent_vtypes);
668 internal ValueImpl EncodeValue (Value v, List<Value> duplicates = null) {
669 if (v is PrimitiveValue) {
670 object val = (v as PrimitiveValue).Value;
672 return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
674 return new ValueImpl { Value = val };
675 } else if (v is ObjectMirror) {
676 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
677 } else if (v is StructMirror) {
678 if (duplicates == null)
679 duplicates = new List<Value> ();
680 if (duplicates.Contains (v))
681 return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
684 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields, duplicates) };
686 throw new NotSupportedException ();
690 internal ValueImpl[] EncodeValues (IList<Value> values, List<Value> duplicates = null) {
691 ValueImpl[] res = new ValueImpl [values.Count];
692 for (int i = 0; i < values.Count; ++i)
693 res [i] = EncodeValue (values [i], duplicates);
697 internal void CheckProtocolVersion (int major, int minor) {
698 if (!conn.Version.AtLeast (major, minor))
699 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
703 class EventHandler : MarshalByRefObject, IEventHandler
707 public EventHandler (VirtualMachine vm) {
711 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
712 var l = new List<Event> ();
714 for (int i = 0; i < events.Length; ++i) {
715 EventInfo ei = events [i];
716 int req_id = ei.ReqId;
717 long thread_id = ei.ThreadId;
719 long loc = ei.Location;
721 switch (ei.EventType) {
722 case EventType.VMStart:
723 vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null, 0);
725 case EventType.VMDeath:
726 vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null, ei.ExitCode);
728 case EventType.ThreadStart:
729 vm.InvalidateThreadCache ();
730 l.Add (new ThreadStartEvent (vm, req_id, id));
732 case EventType.ThreadDeath:
733 vm.InvalidateThreadCache ();
734 l.Add (new ThreadDeathEvent (vm, req_id, id));
736 case EventType.AssemblyLoad:
737 vm.InvalidateAssemblyCaches ();
738 l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
740 case EventType.AssemblyUnload:
741 vm.InvalidateAssemblyCaches ();
742 l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
744 case EventType.TypeLoad:
745 l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
747 case EventType.MethodEntry:
748 l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
750 case EventType.MethodExit:
751 l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
753 case EventType.Breakpoint:
754 l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
757 l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
759 case EventType.Exception:
760 l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
762 case EventType.AppDomainCreate:
763 l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
765 case EventType.AppDomainUnload:
766 l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
768 case EventType.UserBreak:
769 l.Add (new UserBreakEvent (vm, req_id, thread_id));
771 case EventType.UserLog:
772 l.Add (new UserLogEvent (vm, req_id, thread_id, ei.Level, ei.Category, ei.Message));
778 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
781 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
782 vm.notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, req_id, thread_id, vm_uri, 0);
786 public class CommandException : Exception {
788 internal CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
789 ErrorCode = error_code;
792 public ErrorCode ErrorCode {
797 public class VMNotSuspendedException : InvalidOperationException
799 public VMNotSuspendedException () : base ("The vm is not suspended.")