2 // System.Diagnostics.SymbolStore/MonoSymbolTable.cs
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2002 Ximian, Inc. http://www.ximian.com
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System.Reflection;
33 using System.Collections;
38 // Parts which are actually written into the symbol file are marked with
40 // #region This is actually written to the symbol file
43 // Please do not modify these regions without previously talking to me.
45 // All changes to the file format must be synchronized in several places:
47 // a) The fields in these regions (and their order) must match the actual
48 // contents of the symbol file.
50 // This helps people to understand the symbol file format without reading
51 // too much source code, ie. you look at the appropriate region and then
52 // you know what's actually in the file.
54 // It is also required to help me enforce b).
56 // b) The regions must be kept in sync with the unmanaged code in
57 // mono/metadata/debug-mono-symfile.h
59 // When making changes to the file format, you must also increase two version
62 // i) OffsetTable.Version in this file.
63 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
65 // After doing so, recompile everything, including the debugger. Symbol files
66 // with different versions are incompatible to each other and the debugger and
67 // the runtime enfore this, so you need to recompile all your assemblies after
68 // changing the file format.
71 namespace Mono.CSharp.Debugger
73 public struct OffsetTable
75 public const int Version = 37;
76 public const long Magic = 0x45e82623fd7fa614;
78 #region This is actually written to the symbol file
79 public int TotalFileSize;
80 public int DataSectionOffset;
81 public int DataSectionSize;
82 public int SourceCount;
83 public int SourceTableOffset;
84 public int SourceTableSize;
85 public int MethodCount;
86 public int MethodTableOffset;
87 public int MethodTableSize;
91 internal OffsetTable (BinaryReader reader)
93 TotalFileSize = reader.ReadInt32 ();
94 DataSectionOffset = reader.ReadInt32 ();
95 DataSectionSize = reader.ReadInt32 ();
96 SourceCount = reader.ReadInt32 ();
97 SourceTableOffset = reader.ReadInt32 ();
98 SourceTableSize = reader.ReadInt32 ();
99 MethodCount = reader.ReadInt32 ();
100 MethodTableOffset = reader.ReadInt32 ();
101 MethodTableSize = reader.ReadInt32 ();
102 TypeCount = reader.ReadInt32 ();
105 internal void Write (BinaryWriter bw)
107 bw.Write (TotalFileSize);
108 bw.Write (DataSectionOffset);
109 bw.Write (DataSectionSize);
110 bw.Write (SourceCount);
111 bw.Write (SourceTableOffset);
112 bw.Write (SourceTableSize);
113 bw.Write (MethodCount);
114 bw.Write (MethodTableOffset);
115 bw.Write (MethodTableSize);
116 bw.Write (TypeCount);
119 public override string ToString ()
121 return String.Format (
122 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
123 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
124 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
125 MethodTableSize, TypeCount);
129 public struct LineNumberEntry
131 #region This is actually written to the symbol file
132 public readonly int Row;
133 public readonly int Offset;
136 public LineNumberEntry (int row, int offset)
139 this.Offset = offset;
142 public static LineNumberEntry Null = new LineNumberEntry (0, 0);
144 internal LineNumberEntry (BinaryReader reader)
146 Row = reader.ReadInt32 ();
147 Offset = reader.ReadInt32 ();
150 internal void Write (BinaryWriter bw)
156 private class OffsetComparerClass : IComparer
158 public int Compare (object a, object b)
160 LineNumberEntry l1 = (LineNumberEntry) a;
161 LineNumberEntry l2 = (LineNumberEntry) b;
163 if (l1.Offset < l2.Offset)
165 else if (l1.Offset > l2.Offset)
172 private class RowComparerClass : IComparer
174 public int Compare (object a, object b)
176 LineNumberEntry l1 = (LineNumberEntry) a;
177 LineNumberEntry l2 = (LineNumberEntry) b;
181 else if (l1.Row > l2.Row)
188 public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
189 public static readonly IComparer RowComparer = new RowComparerClass ();
191 public override string ToString ()
193 return String.Format ("[Line {0}:{1}]", Row, Offset);
197 public class LexicalBlockEntry
200 #region This is actually written to the symbol file
201 public int StartOffset;
202 public int EndOffset;
205 public LexicalBlockEntry (int index, int start_offset)
208 this.StartOffset = start_offset;
211 internal LexicalBlockEntry (int index, BinaryReader reader)
214 this.StartOffset = reader.ReadInt32 ();
215 this.EndOffset = reader.ReadInt32 ();
218 public void Close (int end_offset)
220 this.EndOffset = end_offset;
223 internal void Write (BinaryWriter bw)
225 bw.Write (StartOffset);
226 bw.Write (EndOffset);
229 public override string ToString ()
231 return String.Format ("[LexicalBlock {0}:{1}]", StartOffset, EndOffset);
235 public struct LocalVariableEntry
237 #region This is actually written to the symbol file
238 public readonly string Name;
239 public readonly FieldAttributes Attributes;
240 public readonly byte[] Signature;
241 public readonly int BlockIndex;
244 public LocalVariableEntry (string Name, FieldAttributes Attributes, byte[] Signature,
248 this.Attributes = Attributes;
249 this.Signature = Signature;
250 this.BlockIndex = BlockIndex;
253 internal LocalVariableEntry (BinaryReader reader)
255 int name_length = reader.ReadInt32 ();
256 byte[] name = reader.ReadBytes (name_length);
257 Name = Encoding.UTF8.GetString (name);
258 Attributes = (FieldAttributes) reader.ReadInt32 ();
259 int sig_length = reader.ReadInt32 ();
260 Signature = reader.ReadBytes (sig_length);
261 BlockIndex = reader.ReadInt32 ();
264 internal void Write (MonoSymbolFile file, BinaryWriter bw)
266 file.WriteString (bw, Name);
267 bw.Write ((int) Attributes);
268 bw.Write ((int) Signature.Length);
269 bw.Write (Signature);
270 bw.Write (BlockIndex);
273 public override string ToString ()
275 return String.Format ("[LocalVariable {0}:{1}]", Name, Attributes);
279 public class SourceFileEntry
281 #region This is actually written to the symbol file
282 public readonly int Index;
287 int NamespaceTableOffset;
293 ArrayList namespaces;
296 public static int Size {
300 internal SourceFileEntry (MonoSymbolFile file, string file_name)
303 this.file_name = file_name;
304 this.Index = file.AddSource (this);
307 methods = new ArrayList ();
308 namespaces = new ArrayList ();
311 public void DefineMethod (MethodBase method, int token, LocalVariableEntry[] locals,
312 LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
313 int start, int end, int namespace_id)
316 throw new InvalidOperationException ();
318 MethodEntry entry = new MethodEntry (
319 file, this, method, token, locals, lines, blocks, start, end, namespace_id);
322 file.AddMethod (entry);
325 public int DefineNamespace (string name, string[] using_clauses, int parent)
328 throw new InvalidOperationException ();
330 int index = file.GetNextNamespaceIndex ();
331 NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
336 internal void WriteData (BinaryWriter bw)
338 NameOffset = (int) bw.BaseStream.Position;
339 file.WriteString (bw, file_name);
341 ArrayList list = new ArrayList ();
342 foreach (MethodEntry entry in methods)
343 list.Add (entry.Write (file, bw));
347 MethodOffset = (int) bw.BaseStream.Position;
348 foreach (MethodSourceEntry method in list)
351 NamespaceCount = namespaces.Count;
352 NamespaceTableOffset = (int) bw.BaseStream.Position;
353 foreach (NamespaceEntry ns in namespaces)
357 internal void Write (BinaryWriter bw)
361 bw.Write (NamespaceCount);
362 bw.Write (NameOffset);
363 bw.Write (MethodOffset);
364 bw.Write (NamespaceTableOffset);
367 internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
371 Index = reader.ReadInt32 ();
372 Count = reader.ReadInt32 ();
373 NamespaceCount = reader.ReadInt32 ();
374 NameOffset = reader.ReadInt32 ();
375 MethodOffset = reader.ReadInt32 ();
376 NamespaceTableOffset = reader.ReadInt32 ();
378 file_name = file.ReadString (NameOffset);
381 public string FileName {
382 get { return file_name; }
385 public MethodSourceEntry[] Methods {
388 throw new InvalidOperationException ();
390 BinaryReader reader = file.BinaryReader;
391 int old_pos = (int) reader.BaseStream.Position;
393 reader.BaseStream.Position = MethodOffset;
394 ArrayList list = new ArrayList ();
395 for (int i = 0; i < Count; i ++)
396 list.Add (new MethodSourceEntry (reader));
397 reader.BaseStream.Position = old_pos;
399 MethodSourceEntry[] retval = new MethodSourceEntry [Count];
400 list.CopyTo (retval, 0);
405 public NamespaceEntry[] Namespaces {
408 throw new InvalidOperationException ();
410 BinaryReader reader = file.BinaryReader;
411 int old_pos = (int) reader.BaseStream.Position;
413 reader.BaseStream.Position = NamespaceTableOffset;
414 ArrayList list = new ArrayList ();
415 for (int i = 0; i < NamespaceCount; i ++)
416 list.Add (new NamespaceEntry (file, reader));
417 reader.BaseStream.Position = old_pos;
419 NamespaceEntry[] retval = new NamespaceEntry [list.Count];
420 list.CopyTo (retval, 0);
425 public override string ToString ()
427 return String.Format ("SourceFileEntry ({0}:{1}:{2})",
428 Index, file_name, Count);
432 public struct MethodSourceEntry : IComparable
434 #region This is actually written to the symbol file
435 public readonly int Index;
436 public readonly int FileOffset;
437 public readonly int StartRow;
438 public readonly int EndRow;
441 public MethodSourceEntry (int index, int file_offset, int start, int end)
444 this.FileOffset = file_offset;
445 this.StartRow = start;
449 internal MethodSourceEntry (BinaryReader reader)
451 Index = reader.ReadInt32 ();
452 FileOffset = reader.ReadInt32 ();
453 StartRow = reader.ReadInt32 ();
454 EndRow = reader.ReadInt32 ();
457 public static int Size {
461 internal void Write (BinaryWriter bw)
464 bw.Write (FileOffset);
469 public int CompareTo (object obj)
471 MethodSourceEntry method = (MethodSourceEntry) obj;
473 if (method.StartRow < StartRow)
475 else if (method.StartRow > StartRow)
481 public override string ToString ()
483 return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
484 Index, FileOffset, StartRow, EndRow);
488 public struct MethodIndexEntry
490 #region This is actually written to the symbol file
491 public readonly int FileOffset;
492 public readonly int Token;
495 public static int Size {
499 public MethodIndexEntry (int offset, int token)
501 this.FileOffset = offset;
505 internal MethodIndexEntry (BinaryReader reader)
507 FileOffset = reader.ReadInt32 ();
508 Token = reader.ReadInt32 ();
511 internal void Write (BinaryWriter bw)
513 bw.Write (FileOffset);
517 public override string ToString ()
519 return String.Format ("MethodIndexEntry ({0}:{1:x})",
524 public class MethodEntry
526 #region This is actually written to the symbol file
527 public readonly int SourceFileIndex;
528 public readonly int Token;
529 public readonly int StartRow;
530 public readonly int EndRow;
531 public readonly int ClassTypeIndex;
532 public readonly int NumParameters;
533 public readonly int NumLocals;
534 public readonly int NumLineNumbers;
535 public readonly int NamespaceID;
536 public readonly bool LocalNamesAmbiguous;
539 int TypeIndexTableOffset;
540 int LocalVariableTableOffset;
541 int LineNumberTableOffset;
542 int NumLexicalBlocks;
543 int LexicalBlockTableOffset;
549 public readonly int Index;
550 public readonly SourceFileEntry SourceFile;
551 public readonly LineNumberEntry[] LineNumbers;
552 public readonly int[] ParamTypeIndices;
553 public readonly int[] LocalTypeIndices;
554 public readonly LocalVariableEntry[] Locals;
555 public readonly Type[] LocalTypes;
556 public readonly LexicalBlockEntry[] LexicalBlocks;
558 public readonly MonoSymbolFile SymbolFile;
560 public static int Size {
568 public MethodBase MethodBase {
569 get { return MonoDebuggerSupport.GetMethod (SymbolFile.Assembly, Token); }
572 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
574 this.SymbolFile = file;
576 SourceFileIndex = reader.ReadInt32 ();
577 Token = reader.ReadInt32 ();
578 StartRow = reader.ReadInt32 ();
579 EndRow = reader.ReadInt32 ();
580 ClassTypeIndex = reader.ReadInt32 ();
581 NumParameters = reader.ReadInt32 ();
582 NumLocals = reader.ReadInt32 ();
583 NumLineNumbers = reader.ReadInt32 ();
584 NameOffset = reader.ReadInt32 ();
585 TypeIndexTableOffset = reader.ReadInt32 ();
586 LocalVariableTableOffset = reader.ReadInt32 ();
587 LineNumberTableOffset = reader.ReadInt32 ();
588 NumLexicalBlocks = reader.ReadInt32 ();
589 LexicalBlockTableOffset = reader.ReadInt32 ();
590 NamespaceID = reader.ReadInt32 ();
591 LocalNamesAmbiguous = reader.ReadInt32 () != 0;
593 name = file.ReadString (NameOffset);
595 SourceFile = file.GetSourceFile (SourceFileIndex);
597 if (LineNumberTableOffset != 0) {
598 long old_pos = reader.BaseStream.Position;
599 reader.BaseStream.Position = LineNumberTableOffset;
601 LineNumbers = new LineNumberEntry [NumLineNumbers];
603 for (int i = 0; i < NumLineNumbers; i++)
604 LineNumbers [i] = new LineNumberEntry (reader);
606 reader.BaseStream.Position = old_pos;
609 if (LocalVariableTableOffset != 0) {
610 long old_pos = reader.BaseStream.Position;
611 reader.BaseStream.Position = LocalVariableTableOffset;
613 Locals = new LocalVariableEntry [NumLocals];
614 LocalTypes = new Type [NumLocals];
616 Assembly ass = file.Assembly;
618 for (int i = 0; i < NumLocals; i++) {
619 Locals [i] = new LocalVariableEntry (reader);
620 LocalTypes [i] = MonoDebuggerSupport.GetLocalTypeFromSignature (
621 ass, Locals [i].Signature);
624 reader.BaseStream.Position = old_pos;
627 if (TypeIndexTableOffset != 0) {
628 long old_pos = reader.BaseStream.Position;
629 reader.BaseStream.Position = TypeIndexTableOffset;
631 ParamTypeIndices = new int [NumParameters];
632 LocalTypeIndices = new int [NumLocals];
634 for (int i = 0; i < NumParameters; i++)
635 ParamTypeIndices [i] = reader.ReadInt32 ();
636 for (int i = 0; i < NumLocals; i++)
637 LocalTypeIndices [i] = reader.ReadInt32 ();
639 reader.BaseStream.Position = old_pos;
642 if (LexicalBlockTableOffset != 0) {
643 long old_pos = reader.BaseStream.Position;
644 reader.BaseStream.Position = LexicalBlockTableOffset;
646 LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
647 for (int i = 0; i < NumLexicalBlocks; i++)
648 LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
650 reader.BaseStream.Position = old_pos;
654 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
655 int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
656 LexicalBlockEntry[] blocks, int start_row, int end_row,
659 this.SymbolFile = file;
660 Index = file.GetNextMethodIndex ();
663 SourceFileIndex = source.Index;
665 StartRow = start_row;
667 NamespaceID = namespace_id;
668 LexicalBlocks = blocks;
669 NumLexicalBlocks = LexicalBlocks.Length;
671 LineNumbers = BuildLineNumberTable (lines);
672 NumLineNumbers = LineNumbers.Length;
674 ParameterInfo[] parameters = method.GetParameters ();
675 if (parameters == null)
676 parameters = new ParameterInfo [0];
680 NumParameters = parameters.Length;
681 ParamTypeIndices = new int [NumParameters];
682 for (int i = 0; i < NumParameters; i++)
683 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
685 NumLocals = locals.Length;
688 if (NumLocals <= 32) {
689 // Most of the time, the O(n^2) factor is actually
690 // less than the cost of allocating the hash table,
691 // 32 is a rough number obtained through some testing.
693 for (int i = 0; i < NumLocals; i ++) {
694 string nm = locals [i].Name;
696 for (int j = i + 1; j < NumLocals; j ++) {
697 if (locals [j].Name == nm) {
698 LocalNamesAmbiguous = true;
699 goto locals_check_done;
706 Hashtable local_names = new Hashtable ();
707 foreach (LocalVariableEntry local in locals) {
708 if (local_names.Contains (local.Name)) {
709 LocalNamesAmbiguous = true;
712 local_names.Add (local.Name, local);
716 LocalTypeIndices = new int [NumLocals];
717 for (int i = 0; i < NumLocals; i++)
718 LocalTypeIndices [i] = file.GetNextTypeIndex ();
720 ClassTypeIndex = file.DefineType (method.ReflectedType);
723 static LineNumberEntry [] tmp_buff = new LineNumberEntry [20];
725 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
726 // we aren't going "backwards" since this would counfuse the runtime's
727 // debugging code (and the debugger).
729 // In the line number table, the "offset" field most be strictly
730 // monotonic increasing; that is, the next entry must not have an offset
731 // which is equal to or less than the current one.
733 // The most common case is that our input (ie. the line number table as
734 // we get it from mcs) contains several entries with the same offset
735 // (and different line numbers) - but it may also happen that the offset
736 // is decreasing (this can be considered as an exception, such lines will
737 // simply be discarded).
738 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
741 int last_offset = -1;
744 if (tmp_buff.Length < (line_numbers.Length + 1))
745 tmp_buff = new LineNumberEntry [(line_numbers.Length + 1) * 2];
747 for (int i = 0; i < line_numbers.Length; i++) {
748 LineNumberEntry line = line_numbers [i];
750 if (line.Offset > last_offset) {
752 tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
754 last_offset = line.Offset;
755 } else if (line.Row > last_row) {
761 tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
763 LineNumberEntry [] retval = new LineNumberEntry [pos];
764 Array.Copy (tmp_buff, retval, pos);
768 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
770 NameOffset = (int) bw.BaseStream.Position;
771 file.WriteString (bw, name);
773 TypeIndexTableOffset = (int) bw.BaseStream.Position;
775 for (int i = 0; i < NumParameters; i++)
776 bw.Write (ParamTypeIndices [i]);
777 for (int i = 0; i < NumLocals; i++)
778 bw.Write (LocalTypeIndices [i]);
780 LocalVariableTableOffset = (int) bw.BaseStream.Position;
781 for (int i = 0; i < NumLocals; i++)
782 Locals [i].Write (file, bw);
783 file.LocalCount += NumLocals;
785 LineNumberTableOffset = (int) bw.BaseStream.Position;
786 for (int i = 0; i < NumLineNumbers; i++)
787 LineNumbers [i].Write (bw);
788 file.LineNumberCount += NumLineNumbers;
790 LexicalBlockTableOffset = (int) bw.BaseStream.Position;
791 for (int i = 0; i < NumLexicalBlocks; i++)
792 LexicalBlocks [i].Write (bw);
793 file_offset = (int) bw.BaseStream.Position;
795 bw.Write (SourceFileIndex);
799 bw.Write (ClassTypeIndex);
800 bw.Write (NumParameters);
801 bw.Write (NumLocals);
802 bw.Write (NumLineNumbers);
803 bw.Write (NameOffset);
804 bw.Write (TypeIndexTableOffset);
805 bw.Write (LocalVariableTableOffset);
806 bw.Write (LineNumberTableOffset);
807 bw.Write (NumLexicalBlocks);
808 bw.Write (LexicalBlockTableOffset);
809 bw.Write (NamespaceID);
810 bw.Write (LocalNamesAmbiguous ? 1 : 0);
812 return new MethodSourceEntry (Index, file_offset, StartRow, EndRow);
815 internal void WriteIndex (BinaryWriter bw)
817 new MethodIndexEntry (file_offset, Token).Write (bw);
820 public override string ToString ()
822 return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {6}:{7}:{8}:{9} - {5}]",
823 Index, Token, SourceFileIndex, StartRow, EndRow,
824 SourceFile, ClassTypeIndex, NumParameters,
825 NumLocals, NumLineNumbers);
829 public struct NamespaceEntry
831 #region This is actually written to the symbol file
832 public readonly string Name;
833 public readonly int Index;
834 public readonly int Parent;
835 public readonly string[] UsingClauses;
838 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
842 this.Parent = parent;
843 this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
846 internal NamespaceEntry (MonoSymbolFile file, BinaryReader reader)
848 Name = file.ReadString ();
849 Index = reader.ReadInt32 ();
850 Parent = reader.ReadInt32 ();
852 int count = reader.ReadInt32 ();
853 UsingClauses = new string [count];
854 for (int i = 0; i < count; i++)
855 UsingClauses [i] = file.ReadString ();
858 internal void Write (MonoSymbolFile file, BinaryWriter bw)
860 file.WriteString (bw, Name);
863 bw.Write (UsingClauses.Length);
864 foreach (string uc in UsingClauses)
865 file.WriteString (bw, uc);
868 public override string ToString ()
870 return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);