Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / VirtualMachine.cs
1 using System;
2 using System.IO;
3 using System.Threading;
4 using System.Net;
5 using System.Diagnostics;
6 using System.Collections;
7 using System.Collections.Generic;
8 using Mono.Cecil.Metadata;
9
10 namespace Mono.Debugger.Soft
11 {
12         public class VirtualMachine : Mirror
13         {
14                 Queue queue;
15                 object queue_monitor;
16                 object startup_monitor;
17                 AppDomainMirror root_domain;
18                 Dictionary<int, EventRequest> requests;
19                 ITargetProcess process;
20
21                 internal Connection conn;
22
23                 VersionInfo version;
24
25                 internal VirtualMachine (ITargetProcess process, Connection conn) : base () {
26                         SetVirtualMachine (this);
27                         queue = new Queue ();
28                         queue_monitor = new Object ();
29                         startup_monitor = new Object ();
30                         requests = new Dictionary <int, EventRequest> ();
31                         this.conn = conn;
32                         this.process = process;
33                         conn.ErrorHandler += ErrorHandler;
34                 }
35
36                 // The standard output of the process is available normally through Process
37                 public StreamReader StandardOutput { get; set; }
38                 public StreamReader StandardError { get; set; }
39
40                 
41                 public Process Process {
42                         get {
43                                 ProcessWrapper pw = process as ProcessWrapper;
44                                 if (pw == null)
45                                     throw new InvalidOperationException ("Process instance not available");
46                                 return pw.Process;
47                         }
48                 }
49
50                 public ITargetProcess TargetProcess {
51                         get {
52                                 return process;
53                         }
54                 }
55
56                 public AppDomainMirror RootDomain {
57                         get {
58                                 return root_domain;
59                         }
60             }
61
62                 public EndPoint EndPoint {
63                         get {
64                                 return conn.EndPoint;
65                         }
66                 }
67
68                 public VersionInfo Version {
69                         get {
70                                 return version;
71                         }
72                 }
73
74                 EventSet current_es;
75                 int current_es_index;
76
77                 /*
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.
80                  */
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) {
85                                         if (queue.Count == 0)
86                                                 Monitor.Wait (queue_monitor);
87                                         current_es = (EventSet)queue.Dequeue ();
88                                         current_es_index = 0;
89                                 }
90                                 return current_es.Events [current_es_index ++];
91                         }
92                 }
93
94                 public Event GetNextEvent (int timeout) {
95                         throw new NotImplementedException ();
96                 }
97
98                 public EventSet GetNextEventSet () {
99                         lock (queue_monitor) {
100                                 if (queue.Count == 0)
101                                         Monitor.Wait (queue_monitor);
102
103                                 current_es = null;
104                                 current_es_index = 0;
105
106                                 return (EventSet)queue.Dequeue ();
107                         }
108                 }
109
110                 [Obsolete ("Use GetNextEventSet () instead")]
111                 public T GetNextEvent<T> () where T : Event {
112                         return GetNextEvent () as T;
113                 }
114
115                 public void Suspend () {
116                         conn.VM_Suspend ();
117             }
118
119                 public void Resume () {
120                         try {
121                                 conn.VM_Resume ();
122                         } catch (CommandException ex) {
123                                 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
124                                         throw new InvalidOperationException ("The vm is not suspended.");
125                                 else
126                                         throw;
127                         }
128             }
129
130                 public void Exit (int exitCode) {
131                         conn.VM_Exit (exitCode);
132                 }
133
134                 public void Dispose () {
135                         conn.VM_Dispose ();
136                         conn.Close ();
137                         notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, 0, 0, null);
138                 }
139
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]);
145                         return res;
146                 }
147
148                 // Same as the mirrorOf methods in JDI
149                 public PrimitiveValue CreateValue (object value) {
150                         if (value == null)
151                                 return new PrimitiveValue (vm, null);
152
153                         if (!value.GetType ().IsPrimitive)
154                                 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
155
156                         return new PrimitiveValue (vm, value);
157                 }
158
159                 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
160                         return new EnumMirror (this, type, value);
161                 }
162
163                 //
164                 // Methods to create event request objects
165                 //
166                 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
167                         return new BreakpointEventRequest (this, method, il_offset);
168                 }
169
170                 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
171                         if (loc == null)
172                                 throw new ArgumentNullException ("loc");
173                         CheckMirror (loc);
174                         return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
175                 }
176
177                 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
178                         return new StepEventRequest (this, thread);
179                 }
180
181                 public MethodEntryEventRequest CreateMethodEntryRequest () {
182                         return new MethodEntryEventRequest (this);
183                 }
184
185                 public MethodExitEventRequest CreateMethodExitRequest () {
186                         return new MethodExitEventRequest (this);
187                 }
188
189                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
190                         return new ExceptionEventRequest (this, exc_type, true, true);
191                 }
192
193                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
194                         return new ExceptionEventRequest (this, exc_type, caught, uncaught);
195                 }
196
197                 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
198                         return new AssemblyLoadEventRequest (this);
199                 }
200
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);
206                         }
207                 }
208
209                 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
210                         BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
211
212                         req.Enable ();
213
214                         return req;
215                 }
216
217                 public void ClearAllBreakpoints () {
218                         conn.ClearAllBreakpoints ();
219                 }
220                 
221                 public void Disconnect () {
222                         conn.Close ();
223                 }
224                 
225                 internal void queue_event_set (EventSet es) {
226                         lock (queue_monitor) {
227                                 queue.Enqueue (es);
228                                 Monitor.Pulse (queue_monitor);
229                         }
230                 }
231
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.");
246                         default:
247                                 throw new CommandException (args.ErrorCode);
248                         }
249                 }
250
251                 /* Wait for the debuggee to start up and connect to it */
252                 internal void connect () {
253                         conn.Connect ();
254
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));
259
260                         long root_domain_id = conn.RootDomain;
261                         root_domain = GetDomain (root_domain_id);
262                 }
263
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 + ")");
266
267                         switch (evtype) {
268                         case EventType.VMStart:
269                                 /* Notify the main thread that the debuggee started up */
270                                 lock (startup_monitor) {
271                                         Monitor.Pulse (startup_monitor);
272                                 }
273                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
274                                 break;
275                         case EventType.VMDeath:
276                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id) }));
277                                 break;
278                         case EventType.VMDisconnect:
279                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
280                                 break;
281                         default:
282                                 throw new Exception ();
283                         }
284                 }
285
286                 //
287                 // Methods to create instances of mirror objects
288                 //
289
290                 /*
291                 class MirrorCache<T> {
292                         static Dictionary <long, T> mirrors;
293                         static object mirror_lock = new object ();
294
295                         internal static T GetMirror (VirtualMachine vm, long id) {
296                                 lock (mirror_lock) {
297                                 if (mirrors == null)
298                                         mirrors = new Dictionary <long, T> ();
299                                 T obj;
300                                 if (!mirrors.TryGetValue (id, out obj)) {
301                                         obj = CreateMirror (vm, id);
302                                         mirrors [id] = obj;
303                                 }
304                                 return obj;
305                                 }
306                         }
307
308                         internal static T CreateMirror (VirtualMachine vm, long id) {
309                         }
310                 }
311                 */
312
313                 // FIXME: When to remove items from the cache ?
314
315                 Dictionary <long, MethodMirror> methods;
316                 object methods_lock = new object ();
317
318                 internal MethodMirror GetMethod (long id) {
319                         lock (methods_lock) {
320                                 if (methods == null)
321                                         methods = new Dictionary <long, MethodMirror> ();
322                                 MethodMirror obj;
323                                 if (id == 0)
324                                         return null;
325                                 if (!methods.TryGetValue (id, out obj)) {
326                                         obj = new MethodMirror (this, id);
327                                         methods [id] = obj;
328                                 }
329                                 return obj;
330                         }
331             }
332
333                 Dictionary <long, AssemblyMirror> assemblies;
334                 object assemblies_lock = new object ();
335
336                 internal AssemblyMirror GetAssembly (long id) {
337                         lock (assemblies_lock) {
338                                 if (assemblies == null)
339                                         assemblies = new Dictionary <long, AssemblyMirror> ();
340                                 AssemblyMirror obj;
341                                 if (id == 0)
342                                         return null;
343                                 if (!assemblies.TryGetValue (id, out obj)) {
344                                         obj = new AssemblyMirror (this, id);
345                                         assemblies [id] = obj;
346                                 }
347                                 return obj;
348                         }
349             }
350
351                 Dictionary <long, ModuleMirror> modules;
352                 object modules_lock = new object ();
353
354                 internal ModuleMirror GetModule (long id) {
355                         lock (modules_lock) {
356                                 if (modules == null)
357                                         modules = new Dictionary <long, ModuleMirror> ();
358                                 ModuleMirror obj;
359                                 if (id == 0)
360                                         return null;
361                                 if (!modules.TryGetValue (id, out obj)) {
362                                         obj = new ModuleMirror (this, id);
363                                         modules [id] = obj;
364                                 }
365                                 return obj;
366                         }
367             }
368
369                 Dictionary <long, AppDomainMirror> domains;
370                 object domains_lock = new object ();
371
372                 internal AppDomainMirror GetDomain (long id) {
373                         lock (domains_lock) {
374                                 if (domains == null)
375                                         domains = new Dictionary <long, AppDomainMirror> ();
376                                 AppDomainMirror obj;
377                                 if (id == 0)
378                                         return null;
379                                 if (!domains.TryGetValue (id, out obj)) {
380                                         obj = new AppDomainMirror (this, id);
381                                         domains [id] = obj;
382                                 }
383                                 return obj;
384                         }
385             }
386
387                 Dictionary <long, TypeMirror> types;
388                 object types_lock = new object ();
389
390                 internal TypeMirror GetType (long id) {
391                         lock (types_lock) {
392                                 if (types == null)
393                                         types = new Dictionary <long, TypeMirror> ();
394                                 TypeMirror obj;
395                                 if (id == 0)
396                                         return null;
397                                 if (!types.TryGetValue (id, out obj)) {
398                                         obj = new TypeMirror (this, id);
399                                         types [id] = obj;
400                                 }
401                                 return obj;
402                         }
403             }
404
405                 Dictionary <long, ObjectMirror> objects;
406                 object objects_lock = new object ();
407
408                 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
409                         lock (objects_lock) {
410                                 if (objects == null)
411                                         objects = new Dictionary <long, ObjectMirror> ();
412                                 ObjectMirror obj;
413                                 if (!objects.TryGetValue (id, out obj)) {
414                                         /*
415                                          * Obtain the domain/type of the object to determine the type of
416                                          * object we need to create.
417                                          */
418                                         if (domain_id == 0)
419                                                 domain_id = conn.Object_GetDomain (id);
420                                         AppDomainMirror d = GetDomain (domain_id);
421
422                                         if (type_id == 0)
423                                                 type_id = conn.Object_GetType (id);
424                                         TypeMirror t = GetType (type_id);
425
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);
432                                         else
433                                                 obj = new ObjectMirror (this, id);
434                                         objects [id] = obj;
435                                 }
436                                 return (T)obj;
437                         }
438             }
439
440                 internal T GetObject<T> (long id) where T : ObjectMirror {
441                         return GetObject<T> (id, 0, 0);
442                 }
443
444                 internal ObjectMirror GetObject (long objid) {
445                         return GetObject<ObjectMirror> (objid);
446                 }
447
448                 internal ThreadMirror GetThread (long id) {
449                         return GetObject <ThreadMirror> (id);
450                 }
451
452                 object requests_lock = new object ();
453
454                 internal void AddRequest (EventRequest req, int id) {
455                         lock (requests_lock) {
456                                 requests [id] = req;
457                         }
458                 }
459
460                 internal void RemoveRequest (EventRequest req, int id) {
461                         lock (requests_lock) {
462                                 requests.Remove (id);
463                         }
464                 }
465
466                 internal EventRequest GetRequest (int id) {
467                         lock (requests_lock) {
468                                 return requests [id];
469                         }
470                 }
471
472                 internal Value DecodeValue (ValueImpl v) {
473                         if (v.Value != null)
474                                 return new PrimitiveValue (this, v.Value);
475
476                         switch (v.Type) {
477                         case ElementType.Void:
478                                 return null;
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:
488                                 if (v.IsEnum)
489                                         return new EnumMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
490                                 else
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);
494                         default:
495                                 throw new NotImplementedException ("" + v.Type);
496                         }
497                 }
498
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]);
503                         return res;
504                 }
505
506                 internal ValueImpl EncodeValue (Value v) {
507                         if (v is PrimitiveValue) {
508                                 object val = (v as PrimitiveValue).Value;
509                                 if (val == null)
510                                         return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
511                                 else
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) };
517                         } else {
518                                 throw new NotSupportedException ();
519                         }
520                 }
521
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]);
526                         return res;
527                 }
528     }
529
530         class EventHandler : MarshalByRefObject, IEventHandler
531         {               
532                 VirtualMachine vm;
533
534                 public EventHandler (VirtualMachine vm) {
535                         this.vm = vm;
536                 }
537
538                 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
539                         var l = new List<Event> ();
540
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;
545                                 long id = ei.Id;
546                                 long loc = ei.Location;
547
548                                 switch (ei.EventType) {
549                                 case EventType.VMStart:
550                                         vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null);
551                                         break;
552                                 case EventType.VMDeath:
553                                         vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null);
554                                         break;
555                                 case EventType.ThreadStart:
556                                         l.Add (new ThreadStartEvent (vm, req_id, id));
557                                         break;
558                                 case EventType.ThreadDeath:
559                                         l.Add (new ThreadDeathEvent (vm, req_id, id));
560                                         break;
561                                 case EventType.AssemblyLoad:
562                                         l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
563                                         break;
564                                 case EventType.AssemblyUnload:
565                                         l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
566                                         break;
567                                 case EventType.TypeLoad:
568                                         l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
569                                         break;
570                                 case EventType.MethodEntry:
571                                         l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
572                                         break;
573                                 case EventType.MethodExit:
574                                         l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
575                                         break;
576                                 case EventType.Breakpoint:
577                                         l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
578                                         break;
579                                 case EventType.Step:
580                                         l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
581                                         break;
582                                 case EventType.Exception:
583                                         l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
584                                         break;
585                                 case EventType.AppDomainCreate:
586                                         l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
587                                         break;
588                                 case EventType.AppDomainUnload:
589                                         l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
590                                         break;
591                                 default:
592                                         break;
593                                 }
594                         }
595                         
596                         if (l.Count > 0)
597                                 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
598                 }
599
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);
602         }
603     }
604
605         internal class CommandException : Exception {
606
607                 public CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
608                         ErrorCode = error_code;
609                 }
610
611                 public ErrorCode ErrorCode {
612                         get; set;
613                 }
614         }
615 }