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 // Enable send and receive timeouts on the connection and send a keepalive event
165 // every 'keepalive_interval' milliseconds.
168 public void SetSocketTimeouts (int send_timeout, int receive_timeout, int keepalive_interval)
170 conn.SetSocketTimeouts (send_timeout, receive_timeout, keepalive_interval);
174 // Methods to create event request objects
176 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
177 return new BreakpointEventRequest (this, method, il_offset);
180 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
182 throw new ArgumentNullException ("loc");
184 return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
187 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
188 return new StepEventRequest (this, thread);
191 public MethodEntryEventRequest CreateMethodEntryRequest () {
192 return new MethodEntryEventRequest (this);
195 public MethodExitEventRequest CreateMethodExitRequest () {
196 return new MethodExitEventRequest (this);
199 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
200 return new ExceptionEventRequest (this, exc_type, true, true);
203 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
204 return new ExceptionEventRequest (this, exc_type, caught, uncaught);
207 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
208 return new AssemblyLoadEventRequest (this);
211 public void EnableEvents (params EventType[] events) {
212 foreach (EventType etype in events) {
213 if (etype == EventType.Breakpoint)
214 throw new ArgumentException ("Breakpoint events cannot be requested using EnableEvents", "events");
215 conn.EnableEvent (etype, SuspendPolicy.All, null);
219 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
220 BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
227 public void ClearAllBreakpoints () {
228 conn.ClearAllBreakpoints ();
231 public void Disconnect () {
235 internal void queue_event_set (EventSet es) {
236 lock (queue_monitor) {
238 Monitor.Pulse (queue_monitor);
242 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
243 switch (args.ErrorCode) {
244 case ErrorCode.INVALID_OBJECT:
245 throw new ObjectCollectedException ();
246 case ErrorCode.INVALID_FRAMEID:
247 throw new InvalidStackFrameException ();
248 case ErrorCode.NOT_SUSPENDED:
249 throw new InvalidOperationException ("The vm is not suspended.");
250 case ErrorCode.NOT_IMPLEMENTED:
251 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
252 case ErrorCode.ABSENT_INFORMATION:
253 throw new AbsentInformationException ();
254 case ErrorCode.NO_SEQ_POINT_AT_IL_OFFSET:
255 throw new ArgumentException ("Cannot set breakpoint on the specified IL offset.");
257 throw new CommandException (args.ErrorCode);
261 /* Wait for the debuggee to start up and connect to it */
262 internal void connect () {
265 // Test the connection
266 version = conn.Version;
267 if (version.MajorVersion != Connection.MAJOR_VERSION)
268 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));
270 long root_domain_id = conn.RootDomain;
271 root_domain = GetDomain (root_domain_id);
274 internal void notify_vm_event (EventType evtype, SuspendPolicy spolicy, int req_id, long thread_id, string vm_uri) {
275 //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
278 case EventType.VMStart:
279 /* Notify the main thread that the debuggee started up */
280 lock (startup_monitor) {
281 Monitor.Pulse (startup_monitor);
283 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
285 case EventType.VMDeath:
286 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id) }));
288 case EventType.VMDisconnect:
289 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
292 throw new Exception ();
297 // Methods to create instances of mirror objects
301 class MirrorCache<T> {
302 static Dictionary <long, T> mirrors;
303 static object mirror_lock = new object ();
305 internal static T GetMirror (VirtualMachine vm, long id) {
308 mirrors = new Dictionary <long, T> ();
310 if (!mirrors.TryGetValue (id, out obj)) {
311 obj = CreateMirror (vm, id);
318 internal static T CreateMirror (VirtualMachine vm, long id) {
323 // FIXME: When to remove items from the cache ?
325 Dictionary <long, MethodMirror> methods;
326 object methods_lock = new object ();
328 internal MethodMirror GetMethod (long id) {
329 lock (methods_lock) {
331 methods = new Dictionary <long, MethodMirror> ();
335 if (!methods.TryGetValue (id, out obj)) {
336 obj = new MethodMirror (this, id);
343 Dictionary <long, AssemblyMirror> assemblies;
344 object assemblies_lock = new object ();
346 internal AssemblyMirror GetAssembly (long id) {
347 lock (assemblies_lock) {
348 if (assemblies == null)
349 assemblies = new Dictionary <long, AssemblyMirror> ();
353 if (!assemblies.TryGetValue (id, out obj)) {
354 obj = new AssemblyMirror (this, id);
355 assemblies [id] = obj;
361 Dictionary <long, ModuleMirror> modules;
362 object modules_lock = new object ();
364 internal ModuleMirror GetModule (long id) {
365 lock (modules_lock) {
367 modules = new Dictionary <long, ModuleMirror> ();
371 if (!modules.TryGetValue (id, out obj)) {
372 obj = new ModuleMirror (this, id);
379 Dictionary <long, AppDomainMirror> domains;
380 object domains_lock = new object ();
382 internal AppDomainMirror GetDomain (long id) {
383 lock (domains_lock) {
385 domains = new Dictionary <long, AppDomainMirror> ();
389 if (!domains.TryGetValue (id, out obj)) {
390 obj = new AppDomainMirror (this, id);
397 Dictionary <long, TypeMirror> types;
398 object types_lock = new object ();
400 internal TypeMirror GetType (long id) {
403 types = new Dictionary <long, TypeMirror> ();
407 if (!types.TryGetValue (id, out obj)) {
408 obj = new TypeMirror (this, id);
415 Dictionary <long, ObjectMirror> objects;
416 object objects_lock = new object ();
418 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
419 lock (objects_lock) {
421 objects = new Dictionary <long, ObjectMirror> ();
423 if (!objects.TryGetValue (id, out obj)) {
425 * Obtain the domain/type of the object to determine the type of
426 * object we need to create.
429 domain_id = conn.Object_GetDomain (id);
430 AppDomainMirror d = GetDomain (domain_id);
433 type_id = conn.Object_GetType (id);
434 TypeMirror t = GetType (type_id);
436 if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
437 obj = new ThreadMirror (this, id);
438 else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
439 obj = new StringMirror (this, id);
440 else if (typeof (T) == typeof (ArrayMirror))
441 obj = new ArrayMirror (this, id);
443 obj = new ObjectMirror (this, id);
450 internal T GetObject<T> (long id) where T : ObjectMirror {
451 return GetObject<T> (id, 0, 0);
454 internal ObjectMirror GetObject (long objid) {
455 return GetObject<ObjectMirror> (objid);
458 internal ThreadMirror GetThread (long id) {
459 return GetObject <ThreadMirror> (id);
462 object requests_lock = new object ();
464 internal void AddRequest (EventRequest req, int id) {
465 lock (requests_lock) {
470 internal void RemoveRequest (EventRequest req, int id) {
471 lock (requests_lock) {
472 requests.Remove (id);
476 internal EventRequest GetRequest (int id) {
477 lock (requests_lock) {
478 return requests [id];
482 internal Value DecodeValue (ValueImpl v) {
484 return new PrimitiveValue (this, v.Value);
487 case ElementType.Void:
489 case ElementType.SzArray:
490 case ElementType.Array:
491 return GetObject<ArrayMirror> (v.Objid);
492 case ElementType.String:
493 return GetObject<StringMirror> (v.Objid);
494 case ElementType.Class:
495 case ElementType.Object:
496 return GetObject (v.Objid);
497 case ElementType.ValueType:
499 return new EnumMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
501 return new StructMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
502 case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
503 return new PrimitiveValue (this, null);
505 throw new NotImplementedException ("" + v.Type);
509 internal Value[] DecodeValues (ValueImpl[] values) {
510 Value[] res = new Value [values.Length];
511 for (int i = 0; i < values.Length; ++i)
512 res [i] = DecodeValue (values [i]);
516 internal ValueImpl EncodeValue (Value v) {
517 if (v is PrimitiveValue) {
518 object val = (v as PrimitiveValue).Value;
520 return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
522 return new ValueImpl { Value = val };
523 } else if (v is ObjectMirror) {
524 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
525 } else if (v is StructMirror) {
526 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields) };
528 throw new NotSupportedException ();
532 internal ValueImpl[] EncodeValues (IList<Value> values) {
533 ValueImpl[] res = new ValueImpl [values.Count];
534 for (int i = 0; i < values.Count; ++i)
535 res [i] = EncodeValue (values [i]);
540 class EventHandler : MarshalByRefObject, IEventHandler
544 public EventHandler (VirtualMachine vm) {
548 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
549 var l = new List<Event> ();
551 for (int i = 0; i < events.Length; ++i) {
552 EventInfo ei = events [i];
553 int req_id = ei.ReqId;
554 long thread_id = ei.ThreadId;
556 long loc = ei.Location;
558 switch (ei.EventType) {
559 case EventType.VMStart:
560 vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null);
562 case EventType.VMDeath:
563 vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null);
565 case EventType.ThreadStart:
566 l.Add (new ThreadStartEvent (vm, req_id, id));
568 case EventType.ThreadDeath:
569 l.Add (new ThreadDeathEvent (vm, req_id, id));
571 case EventType.AssemblyLoad:
572 l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
574 case EventType.AssemblyUnload:
575 l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
577 case EventType.TypeLoad:
578 l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
580 case EventType.MethodEntry:
581 l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
583 case EventType.MethodExit:
584 l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
586 case EventType.Breakpoint:
587 l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
590 l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
592 case EventType.Exception:
593 l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
595 case EventType.AppDomainCreate:
596 l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
598 case EventType.AppDomainUnload:
599 l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
601 case EventType.UserBreak:
602 l.Add (new UserBreakEvent (vm, req_id, thread_id));
604 case EventType.UserLog:
605 l.Add (new UserLogEvent (vm, req_id, thread_id, ei.Level, ei.Category, ei.Message));
613 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
616 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
617 vm.notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, req_id, thread_id, vm_uri);
621 internal class CommandException : Exception {
623 public CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
624 ErrorCode = error_code;
627 public ErrorCode ErrorCode {