New tests.
[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                 IProcess process;
20
21                 internal Connection conn;
22
23                 internal VirtualMachine (IProcess process, Connection conn) : base () {
24                         SetVirtualMachine (this);
25                         queue = new Queue ();
26                         queue_monitor = new Object ();
27                         startup_monitor = new Object ();
28                         requests = new Dictionary <int, EventRequest> ();
29                         this.conn = conn;
30                         this.process = process;
31                         conn.ErrorHandler += ErrorHandler;
32                 }
33
34                 // The standard output of the process is available normally through Process
35                 public StreamReader StandardOutput { get; set; }
36                 public StreamReader StandardError { get; set; }
37
38                 public IProcess Process {
39                         get {
40                                 return process;
41                         }
42                 }
43
44                 public AppDomainMirror RootDomain {
45                         get {
46                                 return root_domain;
47                         }
48             }
49
50                 public EndPoint EndPoint {
51                         get {
52                                 return conn.EndPoint;
53                         }
54                 }
55
56                 public Event GetNextEvent () {
57                         lock (queue_monitor) {
58                                 if (queue.Count == 0)
59                                         Monitor.Wait (queue_monitor);
60                                 return (Event)queue.Dequeue ();
61                         }
62                 }
63
64                 public Event GetNextEvent (int timeout) {
65                         throw new NotImplementedException ();
66                 }
67
68                 public T GetNextEvent<T> () where T : Event {
69                         return GetNextEvent () as T;
70                 }
71
72                 public void Suspend () {
73                         conn.VM_Suspend ();
74             }
75
76                 public void Resume () {
77                         try {
78                                 conn.VM_Resume ();
79                         } catch (CommandException ex) {
80                                 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
81                                         throw new InvalidOperationException ("The vm is not suspended.");
82                                 else
83                                         throw;
84                         }
85             }
86
87                 public void Exit (int exitCode) {
88                         conn.VM_Exit (exitCode);
89                 }
90
91                 public void Dispose () {
92                         conn.VM_Dispose ();
93                         conn.Close ();
94                         notify_vm_event (EventType.VMDisconnect, 0, 0, null);
95                 }
96
97                 public IList<ThreadMirror> GetThreads () {
98                         long[] ids = vm.conn.VM_GetThreads ();
99                         ThreadMirror[] res = new ThreadMirror [ids.Length];
100                         for (int i = 0; i < ids.Length; ++i)
101                                 res [i] = GetThread (ids [i]);
102                         return res;
103                 }
104
105                 // Same as the mirrorOf methods in JDI
106                 public PrimitiveValue CreateValue (object value) {
107                         if (value == null)
108                                 return new PrimitiveValue (vm, null);
109
110                         if (!value.GetType ().IsPrimitive)
111                                 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
112
113                         return new PrimitiveValue (vm, value);
114                 }
115
116                 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
117                         return new EnumMirror (this, type, value);
118                 }
119
120                 //
121                 // Methods to create event request objects
122                 //
123                 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
124                         return new BreakpointEventRequest (this, method, il_offset);
125                 }
126
127                 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
128                         if (loc == null)
129                                 throw new ArgumentNullException ("loc");
130                         CheckMirror (loc);
131                         return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
132                 }
133
134                 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
135                         return new StepEventRequest (this, thread);
136                 }
137
138                 public MethodEntryEventRequest CreateMethodEntryRequest () {
139                         return new MethodEntryEventRequest (this);
140                 }
141
142                 public MethodExitEventRequest CreateMethodExitRequest () {
143                         return new MethodExitEventRequest (this);
144                 }
145
146                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
147                         return new ExceptionEventRequest (this, exc_type, true, true);
148                 }
149
150                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
151                         return new ExceptionEventRequest (this, exc_type, caught, uncaught);
152                 }
153
154                 public void EnableEvents (params EventType[] events) {
155                         foreach (EventType etype in events)
156                                 conn.EnableEvent (etype, SuspendPolicy.All, null);
157                 }
158
159                 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
160                         BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
161
162                         req.Enable ();
163
164                         return req;
165                 }
166
167                 public void ClearAllBreakpoints () {
168                         conn.ClearAllBreakpoints ();
169                 }
170
171                 internal void queue_event (Event e) {
172                         lock (queue_monitor) {
173                                 queue.Enqueue (e);
174                                 Monitor.Pulse (queue_monitor);
175                         }
176                 }
177
178                 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
179                         switch (args.ErrorCode) {
180                         case ErrorCode.INVALID_OBJECT:
181                                 throw new ObjectCollectedException ();
182                         case ErrorCode.INVALID_FRAMEID:
183                                 throw new InvalidStackFrameException ();
184                         case ErrorCode.NOT_SUSPENDED:
185                                 throw new InvalidOperationException ("The vm is not suspended.");
186                         case ErrorCode.NOT_IMPLEMENTED:
187                                 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
188                         default:
189                                 throw new CommandException (args.ErrorCode);
190                         }
191                 }
192
193                 /* Wait for the debuggee to start up and connect to it */
194                 internal void connect () {
195                         conn.Connect ();
196
197                         // Test the connection
198                         VersionInfo ver = conn.Version;
199                         if (ver.MajorVersion != Connection.MAJOR_VERSION)
200                                 throw new NotSupportedException (String.Format ("The debuggee implements protocol version {0}.{1}, while {2}.{3} is required.", ver.MajorVersion, ver.MinorVersion, Connection.MAJOR_VERSION, Connection.MINOR_VERSION));
201
202                         long root_domain_id = conn.RootDomain;
203                         root_domain = GetDomain (root_domain_id);
204                 }
205
206                 internal void notify_vm_event (EventType evtype, int req_id, long thread_id, string vm_uri) {
207                         //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
208
209                         switch (evtype) {
210                         case EventType.VMStart:
211                                 /* Notify the main thread that the debuggee started up */
212                                 lock (startup_monitor) {
213                                         Monitor.Pulse (startup_monitor);
214                                 }
215                                 queue_event (new VMStartEvent (vm, req_id, thread_id));
216                                 break;
217                         case EventType.VMDeath:
218                                 queue_event (new VMDeathEvent (vm, req_id));
219                                 break;
220                         case EventType.VMDisconnect:
221                                 queue_event (new VMDisconnectEvent (vm, req_id));
222                                 break;
223                         default:
224                                 throw new Exception ();
225                         }
226                 }
227
228                 //
229                 // Methods to create instances of mirror objects
230                 //
231
232                 /*
233                 class MirrorCache<T> {
234                         static Dictionary <long, T> mirrors;
235                         static object mirror_lock = new object ();
236
237                         internal static T GetMirror (VirtualMachine vm, long id) {
238                                 lock (mirror_lock) {
239                                 if (mirrors == null)
240                                         mirrors = new Dictionary <long, T> ();
241                                 T obj;
242                                 if (!mirrors.TryGetValue (id, out obj)) {
243                                         obj = CreateMirror (vm, id);
244                                         mirrors [id] = obj;
245                                 }
246                                 return obj;
247                                 }
248                         }
249
250                         internal static T CreateMirror (VirtualMachine vm, long id) {
251                         }
252                 }
253                 */
254
255                 // FIXME: When to remove items from the cache ?
256
257                 Dictionary <long, MethodMirror> methods;
258                 object methods_lock = new object ();
259
260                 internal MethodMirror GetMethod (long id) {
261                         lock (methods_lock) {
262                                 if (methods == null)
263                                         methods = new Dictionary <long, MethodMirror> ();
264                                 MethodMirror obj;
265                                 if (id == 0)
266                                         return null;
267                                 if (!methods.TryGetValue (id, out obj)) {
268                                         obj = new MethodMirror (this, id);
269                                         methods [id] = obj;
270                                 }
271                                 return obj;
272                         }
273             }
274
275                 Dictionary <long, AssemblyMirror> assemblies;
276                 object assemblies_lock = new object ();
277
278                 internal AssemblyMirror GetAssembly (long id) {
279                         lock (assemblies_lock) {
280                                 if (assemblies == null)
281                                         assemblies = new Dictionary <long, AssemblyMirror> ();
282                                 AssemblyMirror obj;
283                                 if (id == 0)
284                                         return null;
285                                 if (!assemblies.TryGetValue (id, out obj)) {
286                                         obj = new AssemblyMirror (this, id);
287                                         assemblies [id] = obj;
288                                 }
289                                 return obj;
290                         }
291             }
292
293                 Dictionary <long, ModuleMirror> modules;
294                 object modules_lock = new object ();
295
296                 internal ModuleMirror GetModule (long id) {
297                         lock (modules_lock) {
298                                 if (modules == null)
299                                         modules = new Dictionary <long, ModuleMirror> ();
300                                 ModuleMirror obj;
301                                 if (id == 0)
302                                         return null;
303                                 if (!modules.TryGetValue (id, out obj)) {
304                                         obj = new ModuleMirror (this, id);
305                                         modules [id] = obj;
306                                 }
307                                 return obj;
308                         }
309             }
310
311                 Dictionary <long, AppDomainMirror> domains;
312                 object domains_lock = new object ();
313
314                 internal AppDomainMirror GetDomain (long id) {
315                         lock (domains_lock) {
316                                 if (domains == null)
317                                         domains = new Dictionary <long, AppDomainMirror> ();
318                                 AppDomainMirror obj;
319                                 if (id == 0)
320                                         return null;
321                                 if (!domains.TryGetValue (id, out obj)) {
322                                         obj = new AppDomainMirror (this, id);
323                                         domains [id] = obj;
324                                 }
325                                 return obj;
326                         }
327             }
328
329                 Dictionary <long, TypeMirror> types;
330                 object types_lock = new object ();
331
332                 internal TypeMirror GetType (long id) {
333                         lock (types_lock) {
334                                 if (types == null)
335                                         types = new Dictionary <long, TypeMirror> ();
336                                 TypeMirror obj;
337                                 if (id == 0)
338                                         return null;
339                                 if (!types.TryGetValue (id, out obj)) {
340                                         obj = new TypeMirror (this, id);
341                                         types [id] = obj;
342                                 }
343                                 return obj;
344                         }
345             }
346
347                 Dictionary <long, ObjectMirror> objects;
348                 object objects_lock = new object ();
349
350                 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
351                         lock (objects_lock) {
352                                 if (objects == null)
353                                         objects = new Dictionary <long, ObjectMirror> ();
354                                 ObjectMirror obj;
355                                 if (!objects.TryGetValue (id, out obj)) {
356                                         /*
357                                          * Obtain the domain/type of the object to determine the type of
358                                          * object we need to create.
359                                          */
360                                         if (domain_id == 0)
361                                                 domain_id = conn.Object_GetDomain (id);
362                                         AppDomainMirror d = GetDomain (domain_id);
363
364                                         if (type_id == 0)
365                                                 type_id = conn.Object_GetType (id);
366                                         TypeMirror t = GetType (type_id);
367
368                                         if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
369                                                 obj = new ThreadMirror (this, id);
370                                         else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
371                                                 obj = new StringMirror (this, id);
372                                         else if (typeof (T) == typeof (ArrayMirror))
373                                                 obj = new ArrayMirror (this, id);
374                                         else
375                                                 obj = new ObjectMirror (this, id);
376                                         objects [id] = obj;
377                                 }
378                                 return (T)obj;
379                         }
380             }
381
382                 internal T GetObject<T> (long id) where T : ObjectMirror {
383                         return GetObject<T> (id, 0, 0);
384                 }
385
386                 internal ObjectMirror GetObject (long objid) {
387                         return GetObject<ObjectMirror> (objid);
388                 }
389
390                 internal ThreadMirror GetThread (long id) {
391                         return GetObject <ThreadMirror> (id);
392                 }
393
394                 object requests_lock = new object ();
395
396                 internal void AddRequest (EventRequest req, int id) {
397                         lock (requests_lock) {
398                                 requests [id] = req;
399                         }
400                 }
401
402                 internal void RemoveRequest (EventRequest req, int id) {
403                         lock (requests_lock) {
404                                 requests.Remove (id);
405                         }
406                 }
407
408                 internal EventRequest GetRequest (int id) {
409                         lock (requests_lock) {
410                                 return requests [id];
411                         }
412                 }
413
414                 internal Value DecodeValue (ValueImpl v) {
415                         if (v.Value != null)
416                                 return new PrimitiveValue (this, v.Value);
417
418                         switch (v.Type) {
419                         case ElementType.Void:
420                                 return null;
421                         case ElementType.SzArray:
422                         case ElementType.Array:
423                                 return GetObject<ArrayMirror> (v.Objid);
424                         case ElementType.String:
425                                 return GetObject<StringMirror> (v.Objid);
426                         case ElementType.Class:
427                         case ElementType.Object:
428                                 return GetObject (v.Objid);
429                         case ElementType.ValueType:
430                                 if (v.IsEnum)
431                                         return new EnumMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
432                                 else
433                                         return new StructMirror (this, GetType (v.Klass), DecodeValues (v.Fields));
434                         case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
435                                 return new PrimitiveValue (this, null);
436                         default:
437                                 throw new NotImplementedException ("" + v.Type);
438                         }
439                 }
440
441                 internal Value[] DecodeValues (ValueImpl[] values) {
442                         Value[] res = new Value [values.Length];
443                         for (int i = 0; i < values.Length; ++i)
444                                 res [i] = DecodeValue (values [i]);
445                         return res;
446                 }
447
448                 internal ValueImpl EncodeValue (Value v) {
449                         if (v is PrimitiveValue) {
450                                 object val = (v as PrimitiveValue).Value;
451                                 if (val == null)
452                                         return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
453                                 else
454                                         return new ValueImpl { Value = val };
455                         } else if (v is ObjectMirror) {
456                                 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
457                         } else if (v is StructMirror) {
458                                 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields) };
459                         } else {
460                                 throw new NotSupportedException ();
461                         }
462                 }
463
464                 internal ValueImpl[] EncodeValues (IList<Value> values) {
465                         ValueImpl[] res = new ValueImpl [values.Count];
466                         for (int i = 0; i < values.Count; ++i)
467                                 res [i] = EncodeValue (values [i]);
468                         return res;
469                 }
470     }
471
472         class EventHandler : MarshalByRefObject, IEventHandler
473         {               
474                 VirtualMachine vm;
475
476                 public EventHandler (VirtualMachine vm) {
477                         this.vm = vm;
478                 }
479
480                 public void VMStart (int req_id, long thread_id, string vm_uri) {
481                         vm.notify_vm_event (EventType.VMStart, req_id, thread_id, vm_uri);
482         }
483
484                 public void VMDeath (int req_id, long thread_id, string vm_uri) {
485                         vm.notify_vm_event (EventType.VMDeath, req_id, thread_id, vm_uri);
486         }
487
488                 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
489                         vm.notify_vm_event (EventType.VMDisconnect, req_id, thread_id, vm_uri);
490         }
491
492                 public void ThreadStart (int req_id, long thread_id, long id) {
493                         vm.queue_event (new ThreadStartEvent (vm, req_id, id));
494         }
495
496                 public void ThreadDeath (int req_id, long thread_id, long id) {
497                         vm.queue_event (new ThreadDeathEvent (vm, req_id, id));
498         }
499
500                 public void AssemblyLoad (int req_id, long thread_id, long id) {
501                         vm.queue_event (new AssemblyLoadEvent (vm, req_id, thread_id, id));
502         }
503
504                 public void AssemblyUnload (int req_id, long thread_id, long id) {
505                         vm.queue_event (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
506         }
507
508                 public void TypeLoad (int req_id, long thread_id, long id) {
509                         vm.queue_event (new TypeLoadEvent (vm, req_id, thread_id, id));
510         }
511
512                 public void MethodEntry (int req_id, long thread_id, long id) {
513                         vm.queue_event (new MethodEntryEvent (vm, req_id, thread_id, id));
514         }
515
516                 public void MethodExit (int req_id, long thread_id, long id) {
517                         vm.queue_event (new MethodExitEvent (vm, req_id, thread_id, id));
518         }
519
520                 public void Breakpoint (int req_id, long thread_id, long id, long loc) {
521                         vm.queue_event (new BreakpointEvent (vm, req_id, thread_id, id, loc));
522         }
523
524                 public void Step (int req_id, long thread_id, long id, long loc) {
525                         vm.queue_event (new StepEvent (vm, req_id, thread_id, id, loc));
526         }
527
528                 public void Exception (int req_id, long thread_id, long id, long loc) {
529                         vm.queue_event (new ExceptionEvent (vm, req_id, thread_id, id, loc));
530         }
531
532                 public void AppDomainCreate (int req_id, long thread_id, long id) {
533                         vm.queue_event (new AppDomainCreateEvent (vm, req_id, thread_id, id));
534         }
535
536                 public void AppDomainUnload (int req_id, long thread_id, long id) {
537                         vm.queue_event (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
538         }
539     }
540
541         internal class CommandException : Exception {
542
543                 public CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
544                         ErrorCode = error_code;
545                 }
546
547                 public ErrorCode ErrorCode {
548                         get; set;
549                 }
550         }
551 }