2004-10-20 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / class / Mono.CSharp.Debugger / MonoSymbolFile.cs
1 //
2 // Mono.CSharp.Debugger/MonoSymbolFile.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc.  http://www.ximian.com
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Reflection;
33 using System.Collections;
34 using System.Text;
35 using System.Threading;
36 using System.IO;
37         
38 namespace Mono.CompilerServices.SymbolWriter
39 {
40         public class MonoSymbolFileException : Exception
41         {
42                 public MonoSymbolFileException ()
43                         : base ()
44                 { }
45
46                 public MonoSymbolFileException (string message, params object[] args)
47                         : base (String.Format (message, args))
48                 { }
49         }
50
51         internal class MyMemoryStream : Stream
52         {
53                 int length;
54                 int real_length;
55                 int position;
56
57                 int chunk_size = 4096;
58                 ArrayList chunks = new ArrayList ();
59
60                 private struct Chunk {
61                         public readonly int Offset;
62                         public readonly int Length;
63                         public byte[] Buffer;
64
65                         public Chunk (int offset, int length)
66                         {
67                                 this.Offset = offset;
68                                 this.Length = length;
69                                 this.Buffer = new Byte [length];
70                         }
71                 }
72
73                 public override long Position {
74                         get { return position; }
75
76                         set {
77                                 if (value > length)
78                                         throw new ArgumentOutOfRangeException ();
79
80                                 position = (int) value;
81                         }
82                 }
83
84                 public override long Length {
85                         get { return length; }
86                 }
87
88                 public override bool CanRead {
89                         get { return true; }
90                 }
91
92                 public override bool CanWrite {
93                         get { return true; }
94                 }
95
96                 public override bool CanSeek {
97                         get { return true; }
98                 }
99
100                 public override void SetLength (long new_length)
101                 {
102                         if (new_length < length)
103                                 throw new ArgumentException ();
104
105                         while (new_length >= real_length) {
106                                 Chunk new_chunk = new Chunk (real_length, chunk_size);
107                                 chunks.Add (new_chunk);
108                                 real_length += chunk_size;
109                         }
110
111                         length = (int) new_length;
112                 }
113
114                 public override void Flush ()
115                 { }
116
117                 public override long Seek (long offset, SeekOrigin origin)
118                 {
119                         int ref_point;
120
121                         switch (origin) {
122                         case SeekOrigin.Begin:
123                                 ref_point = 0;
124                                 break;
125                         case SeekOrigin.Current:
126                                 ref_point = position;
127                                 break;
128                         case SeekOrigin.End:
129                                 ref_point = length;
130                                 break;
131                         default:
132                                 throw new ArgumentException ("Invalid SeekOrigin");
133                         }
134
135                         if ((ref_point + offset < 0) || (offset > real_length))
136                                 throw new ArgumentOutOfRangeException ();
137
138                         position = ref_point + (int) offset;
139
140                         return position;
141                 }
142
143                 Chunk FindChunk (int offset)
144                 {
145                         return (Chunk) chunks [offset / chunk_size];
146                 }
147
148                 public override int Read (byte[] buffer, int offset, int count)
149                 {
150                         int old_count = count;
151
152                         while (count > 0) {
153                                 Chunk chunk = FindChunk (position);
154                                 int coffset = position - chunk.Offset;
155                                 int rest = chunk.Length - coffset;
156                                 int size = System.Math.Min (count, rest);
157
158                                 Array.Copy (chunk.Buffer, coffset, buffer, offset, size);
159                                 position += size;
160                                 offset += size;
161                                 count -= size;
162                         }
163
164                         return old_count;
165                 }
166
167                 public override void Write (byte[] buffer, int offset, int count)
168                 {
169                         if (position + count > length)
170                                 SetLength (position + count);
171
172                         while (count > 0) {
173                                 Chunk chunk = FindChunk (position);
174                                 int coffset = position - chunk.Offset;
175                                 int rest = chunk.Length - coffset;
176                                 int size = System.Math.Min (count, rest);
177
178                                 Array.Copy (buffer, offset, chunk.Buffer, coffset, size);
179                                 position += size;
180                                 offset += size;
181                                 count -= size;
182                         }
183                 }
184
185                 public byte[] GetContents ()
186                 {
187                         byte[] retval = new byte [length];
188                         position = 0;
189                         Read (retval, 0, length);
190                         return retval;
191                 }
192         }
193
194         internal class MyBinaryWriter : BinaryWriter
195         {
196                 public MyBinaryWriter (Stream stream)
197                         : base (stream)
198                 { }
199
200                 public void WriteLeb128 (int value)
201                 {
202                         base.Write7BitEncodedInt (value);
203                 }
204         }
205
206         internal class MyBinaryReader : BinaryReader
207         {
208                 public MyBinaryReader (Stream stream)
209                         : base (stream)
210                 { }
211
212                 public int ReadLeb128 ()
213                 {
214                         return base.Read7BitEncodedInt ();
215                 }
216         }
217
218         public class MonoDebuggerSupport
219         {
220                 static GetTypeFunc get_type;
221                 static GetMethodTokenFunc get_method_token;
222                 static GetMethodFunc get_method;
223                 static GetLocalTypeFromSignatureFunc local_type_from_sig;
224                 static GetGuidFunc get_guid;
225                 static CheckRuntimeVersionFunc check_runtime_version;
226
227                 delegate Type GetTypeFunc (Assembly assembly, int token);
228                 delegate int GetMethodTokenFunc (Assembly assembly, MethodBase method);
229                 delegate MethodBase GetMethodFunc (Assembly assembly, int token);
230                 delegate Type GetLocalTypeFromSignatureFunc (Assembly assembly, byte[] sig);
231                 delegate Guid GetGuidFunc (Module module);
232                 delegate string CheckRuntimeVersionFunc (string filename);
233
234                 static Delegate create_delegate (Type type, Type delegate_type, string name)
235                 {
236                         MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
237                                                         BindingFlags.NonPublic);
238                         if (mi == null)
239                                 throw new Exception ("Can't find " + name);
240
241                         return Delegate.CreateDelegate (delegate_type, mi);
242                 }
243
244                 static MonoDebuggerSupport ()
245                 {
246                         get_type = (GetTypeFunc) create_delegate (
247                                 typeof (Assembly), typeof (GetTypeFunc),
248                                 "MonoDebugger_GetType");
249
250                         get_method_token = (GetMethodTokenFunc) create_delegate (
251                                 typeof (Assembly), typeof (GetMethodTokenFunc),
252                                 "MonoDebugger_GetMethodToken");
253
254                         get_method = (GetMethodFunc) create_delegate (
255                                 typeof (Assembly), typeof (GetMethodFunc),
256                                 "MonoDebugger_GetMethod");
257
258                         local_type_from_sig = (GetLocalTypeFromSignatureFunc) create_delegate (
259                                 typeof (Assembly), typeof (GetLocalTypeFromSignatureFunc),
260                                 "MonoDebugger_GetLocalTypeFromSignature");
261
262                         get_guid = (GetGuidFunc) create_delegate (
263                                 typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid");
264
265                         check_runtime_version = (CheckRuntimeVersionFunc) create_delegate (
266                                 typeof (Assembly), typeof (CheckRuntimeVersionFunc),
267                                 "MonoDebugger_CheckRuntimeVersion");
268                 }
269
270                 public static Type GetType (Assembly assembly, int token)
271                 {
272                         return get_type (assembly, token);
273                 }
274
275                 public static int GetMethodToken (MethodBase method)
276                 {
277                         return get_method_token (method.ReflectedType.Assembly, method);
278                 }
279
280                 public static MethodBase GetMethod (Assembly assembly, int token)
281                 {
282                         return get_method (assembly, token);
283                 }
284
285                 public static Type GetLocalTypeFromSignature (Assembly assembly, byte[] sig)
286                 {
287                         return local_type_from_sig (assembly, sig);
288                 }
289
290                 public static string CheckRuntimeVersion (string filename)
291                 {
292                         return check_runtime_version (filename);
293                 }
294
295                 public static Guid GetGuid (Module module)
296                 {
297                         return get_guid (module);
298                 }
299         }
300
301         public class MonoSymbolFile : IDisposable
302         {
303                 ArrayList methods = new ArrayList ();
304                 ArrayList sources = new ArrayList ();
305                 Hashtable method_source_hash = new Hashtable ();
306                 Hashtable type_hash = new Hashtable ();
307
308                 OffsetTable ot;
309                 int last_type_index;
310                 int last_method_index;
311                 int last_source_index;
312                 int last_namespace_index;
313
314                 public int NumLineNumbers;
315
316                 public MonoSymbolFile ()
317                 { }
318
319                 internal int AddSource (SourceFileEntry source)
320                 {
321                         sources.Add (source);
322                         return ++last_source_index;
323                 }
324
325                 internal int DefineType (Type type)
326                 {
327                         if (type_hash.Contains (type))
328                                 return (int) type_hash [type];
329
330                         int index = ++last_type_index;
331                         type_hash.Add (type, index);
332                         return index;
333                 }
334
335                 internal void AddMethod (MethodEntry entry)
336                 {
337                         methods.Add (entry);
338                 }
339
340                 internal int GetNextTypeIndex ()
341                 {
342                         return ++last_type_index;
343                 }
344
345                 internal int GetNextMethodIndex ()
346                 {
347                         return ++last_method_index;
348                 }
349
350                 internal int GetNextNamespaceIndex ()
351                 {
352                         return ++last_namespace_index;
353                 }
354
355                 byte [] stringBuffer;
356                 int maxCharsPerRound;
357                 static Encoding enc = Encoding.UTF8;
358                 
359                 internal string ReadString (int offset)
360                 {
361                         int old_pos = (int) reader.BaseStream.Position;
362                         reader.BaseStream.Position = offset;
363
364                         string text = reader.ReadString ();
365
366                         reader.BaseStream.Position = old_pos;
367                         return text;
368                 }
369
370                 void Write (MyBinaryWriter bw, Guid guid)
371                 {
372                         // Magic number and file version.
373                         bw.Write (OffsetTable.Magic);
374                         bw.Write (OffsetTable.Version);
375
376                         bw.Write (guid.ToByteArray ());
377
378                         //
379                         // Offsets of file sections; we must write this after we're done
380                         // writing the whole file, so we just reserve the space for it here.
381                         //
382                         long offset_table_offset = bw.BaseStream.Position;
383                         ot.Write (bw);
384
385                         //
386                         // Sort the methods according to their tokens and update their index.
387                         //
388                         methods.Sort ();
389                         for (int i = 0; i < methods.Count; i++)
390                                 ((MethodEntry) methods [i]).Index = i + 1;
391
392                         //
393                         // Write data sections.
394                         //
395                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
396                         foreach (SourceFileEntry source in sources)
397                                 source.WriteData (bw);
398                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
399
400                         //
401                         // Write the method index table.
402                         //
403                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
404                         for (int i = 0; i < methods.Count; i++) {
405                                 MethodEntry entry = (MethodEntry) methods [i];
406                                 entry.WriteIndex (bw);
407                         }
408                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
409
410                         //
411                         // Write source table.
412                         //
413                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
414                         for (int i = 0; i < sources.Count; i++) {
415                                 SourceFileEntry source = (SourceFileEntry) sources [i];
416                                 source.Write (bw);
417                         }
418                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
419
420                         //
421                         // Fixup offset table.
422                         //
423                         ot.TypeCount = last_type_index;
424                         ot.MethodCount = methods.Count;
425                         ot.SourceCount = sources.Count;
426
427                         //
428                         // Write offset table.
429                         //
430                         ot.TotalFileSize = (int) bw.BaseStream.Position;
431                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
432                         ot.Write (bw);
433                         bw.Seek (0, SeekOrigin.End);
434                 }
435
436                 public byte[] CreateSymbolFile (Guid guid)
437                 {
438                         if (reader != null)
439                                 throw new InvalidOperationException ();
440
441                         using (MyMemoryStream stream = new MyMemoryStream ()) {
442                                 Write (new MyBinaryWriter (stream), guid);
443                                 return stream.GetContents ();
444                         }
445                 }
446
447                 Assembly assembly;
448                 MyBinaryReader reader;
449                 Hashtable method_hash;
450                 Hashtable source_file_hash;
451
452                 Hashtable method_token_hash;
453                 Hashtable source_name_hash;
454
455                 protected MonoSymbolFile (string filename, Assembly assembly)
456                 {
457                         this.assembly = assembly;
458
459                         FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
460                         reader = new MyBinaryReader (stream);
461
462                         Guid guid;
463
464                         try {
465                                 long magic = reader.ReadInt64 ();
466                                 long version = reader.ReadInt32 ();
467                                 if (magic != OffsetTable.Magic)
468                                         throw new MonoSymbolFileException (
469                                                 "Symbol file `{0}' is not a valid " +
470                                                 "Mono symbol file", filename);
471                                 if (version != OffsetTable.Version)
472                                         throw new MonoSymbolFileException (
473                                                 "Symbol file `{0}' has version {1}, " +
474                                                 "but expected {2}", filename, version,
475                                                 OffsetTable.Version);
476
477                                 guid = new Guid (reader.ReadBytes (16));
478
479                                 ot = new OffsetTable (reader);
480                         } catch {
481                                 throw new MonoSymbolFileException (
482                                         "Cannot read symbol file `{0}'", filename);
483                         }
484
485                         Module[] modules = assembly.GetModules ();
486                         Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]);
487
488                         if (guid != assembly_guid)
489                                 throw new MonoSymbolFileException (
490                                         "Symbol file `{0}' does not match assembly `{1}'",
491                                         filename, assembly.Location);
492
493                         method_hash = new Hashtable ();
494                         source_file_hash = new Hashtable ();
495                 }
496
497                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
498                 {
499                         string filename = assembly.Location;
500                         string name = filename + ".mdb";
501
502                         return new MonoSymbolFile (name, assembly);
503                 }
504
505                 public Assembly Assembly {
506                         get { return assembly; }
507                 }
508
509                 public int SourceCount {
510                         get { return ot.SourceCount; }
511                 }
512
513                 public int MethodCount {
514                         get { return ot.MethodCount; }
515                 }
516
517                 public int TypeCount {
518                         get { return ot.TypeCount; }
519                 }
520
521                 public int NamespaceCount {
522                         get { return last_namespace_index; }
523                 }
524
525                 internal int LineNumberCount = 0;
526                 internal int LocalCount = 0;
527                 internal int StringSize = 0;
528
529                 public SourceFileEntry GetSourceFile (int index)
530                 {
531                         if ((index < 1) || (index > ot.SourceCount))
532                                 throw new ArgumentException ();
533                         if (reader == null)
534                                 throw new InvalidOperationException ();
535
536                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
537                         if (source != null)
538                                 return source;
539
540                         reader.BaseStream.Position = ot.SourceTableOffset +
541                                 SourceFileEntry.Size * (index - 1);
542                         source = new SourceFileEntry (this, reader);
543                         source_file_hash.Add (index, source);
544                         return source;
545                 }
546
547                 public SourceFileEntry[] Sources {
548                         get {
549                                 if (reader == null)
550                                         throw new InvalidOperationException ();
551
552                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
553                                 for (int i = 0; i < SourceCount; i++)
554                                         retval [i] = GetSourceFile (i + 1);
555                                 return retval;
556                         }
557                 }
558
559                 public MethodIndexEntry GetMethodIndexEntry (int index)
560                 {
561                         int old_pos = (int) reader.BaseStream.Position;
562                         reader.BaseStream.Position = ot.MethodTableOffset +
563                                 MethodIndexEntry.Size * (index - 1);
564                         MethodIndexEntry ie = new MethodIndexEntry (reader);
565                         reader.BaseStream.Position = old_pos;
566                         return ie;
567                 }
568
569                 public MethodEntry GetMethodByToken (int token)
570                 {
571                         if (reader == null)
572                                 throw new InvalidOperationException ();
573
574                         if (method_token_hash == null) {
575                                 method_token_hash = new Hashtable ();
576
577                                 for (int i = 0; i < MethodCount; i++) {
578                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
579
580                                         method_token_hash.Add (ie.Token, i + 1);
581                                 }
582                         }
583
584                         object value = method_token_hash [token];
585                         if (value == null)
586                                 return null;
587
588                         return GetMethod ((int) value);
589                 }
590
591                 public MethodEntry GetMethod (MethodBase method)
592                 {
593                         if (reader == null)
594                                 throw new InvalidOperationException ();
595                         int token = MonoDebuggerSupport.GetMethodToken (method);
596                         return GetMethodByToken (token);
597                 }
598
599                 public MethodEntry GetMethod (int index)
600                 {
601                         if ((index < 1) || (index > ot.MethodCount))
602                                 throw new ArgumentException ();
603                         if (reader == null)
604                                 throw new InvalidOperationException ();
605
606                         MethodEntry entry = (MethodEntry) method_hash [index];
607                         if (entry != null)
608                                 return entry;
609
610                         MethodIndexEntry ie = GetMethodIndexEntry (index);
611                         reader.BaseStream.Position = ie.FileOffset;
612
613                         entry = new MethodEntry (this, reader, index);
614                         method_hash.Add (index, entry);
615                         return entry;
616                 }
617
618                 public MethodEntry[] Methods {
619                         get {
620                                 if (reader == null)
621                                         throw new InvalidOperationException ();
622
623                                 MethodEntry[] retval = new MethodEntry [MethodCount];
624                                 for (int i = 0; i < MethodCount; i++)
625                                         retval [i] = GetMethod (i + 1);
626                                 return retval;
627                         }
628                 }
629
630                 public MethodSourceEntry GetMethodSource (int index)
631                 {
632                         if ((index < 1) || (index > ot.MethodCount))
633                                 throw new ArgumentException ();
634                         if (reader == null)
635                                 throw new InvalidOperationException ();
636
637                         object entry = method_source_hash [index];
638                         if (entry != null)
639                                 return (MethodSourceEntry) entry;
640
641                         MethodEntry method = GetMethod (index);
642                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
643                                 if (source.Index == index) {
644                                         method_source_hash.Add (index, source);
645                                         return source;
646                                 }
647                         }
648
649                         throw new MonoSymbolFileException ("Internal error.");
650                 }
651
652                 public int FindSource (string file_name)
653                 {
654                         if (reader == null)
655                                 throw new InvalidOperationException ();
656
657                         if (source_name_hash == null) {
658                                 source_name_hash = new Hashtable ();
659
660                                 for (int i = 0; i < ot.SourceCount; i++) {
661                                         SourceFileEntry source = GetSourceFile (i + 1);
662
663                                         source_name_hash.Add (source.FileName, i);
664                                 }
665                         }
666
667                         object value = source_name_hash [file_name];
668                         if (value == null)
669                                 return -1;
670                         return (int) value;
671                 }
672
673                 internal MyBinaryReader BinaryReader {
674                         get {
675                                 if (reader == null)
676                                         throw new InvalidOperationException ();
677
678                                 return reader;
679                         }
680                 }
681
682                 void IDisposable.Dispose ()
683                 {
684                         Dispose (true);
685                 }
686
687                 protected virtual void Dispose (bool disposing)
688                 {
689                         if (disposing) {
690                                 if (reader != null) {
691                                         reader.Close ();
692                                         reader = null;
693                                 }
694                         }
695                 }
696         }
697 }