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.Reflection.Emit;
34 using System.Collections;
39 // Parts which are actually written into the symbol file are marked with
41 // #region This is actually written to the symbol file
44 // Please do not modify these regions without previously talking to me.
46 // All changes to the file format must be synchronized in several places:
48 // a) The fields in these regions (and their order) must match the actual
49 // contents of the symbol file.
51 // This helps people to understand the symbol file format without reading
52 // too much source code, ie. you look at the appropriate region and then
53 // you know what's actually in the file.
55 // It is also required to help me enforce b).
57 // b) The regions must be kept in sync with the unmanaged code in
58 // mono/metadata/debug-mono-symfile.h
60 // When making changes to the file format, you must also increase two version
63 // i) OffsetTable.Version in this file.
64 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
66 // After doing so, recompile everything, including the debugger. Symbol files
67 // with different versions are incompatible to each other and the debugger and
68 // the runtime enfore this, so you need to recompile all your assemblies after
69 // changing the file format.
72 namespace Mono.CSharp.Debugger
74 public struct OffsetTable
76 public const int Version = 37;
77 public const long Magic = 0x45e82623fd7fa614;
79 #region This is actually written to the symbol file
80 public int TotalFileSize;
81 public int DataSectionOffset;
82 public int DataSectionSize;
83 public int SourceCount;
84 public int SourceTableOffset;
85 public int SourceTableSize;
86 public int MethodCount;
87 public int MethodTableOffset;
88 public int MethodTableSize;
92 internal OffsetTable (BinaryReader reader)
94 TotalFileSize = reader.ReadInt32 ();
95 DataSectionOffset = reader.ReadInt32 ();
96 DataSectionSize = reader.ReadInt32 ();
97 SourceCount = reader.ReadInt32 ();
98 SourceTableOffset = reader.ReadInt32 ();
99 SourceTableSize = reader.ReadInt32 ();
100 MethodCount = reader.ReadInt32 ();
101 MethodTableOffset = reader.ReadInt32 ();
102 MethodTableSize = reader.ReadInt32 ();
103 TypeCount = reader.ReadInt32 ();
106 internal void Write (BinaryWriter bw)
108 bw.Write (TotalFileSize);
109 bw.Write (DataSectionOffset);
110 bw.Write (DataSectionSize);
111 bw.Write (SourceCount);
112 bw.Write (SourceTableOffset);
113 bw.Write (SourceTableSize);
114 bw.Write (MethodCount);
115 bw.Write (MethodTableOffset);
116 bw.Write (MethodTableSize);
117 bw.Write (TypeCount);
120 public override string ToString ()
122 return String.Format (
123 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
124 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
125 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
126 MethodTableSize, TypeCount);
130 public struct LineNumberEntry
132 #region This is actually written to the symbol file
133 public readonly int Row;
134 public readonly int Offset;
137 public LineNumberEntry (int row, int offset)
140 this.Offset = offset;
143 public static LineNumberEntry Null = new LineNumberEntry (0, 0);
145 internal LineNumberEntry (BinaryReader reader)
147 Row = reader.ReadInt32 ();
148 Offset = reader.ReadInt32 ();
151 internal void Write (BinaryWriter bw)
157 private class OffsetComparerClass : IComparer
159 public int Compare (object a, object b)
161 LineNumberEntry l1 = (LineNumberEntry) a;
162 LineNumberEntry l2 = (LineNumberEntry) b;
164 if (l1.Offset < l2.Offset)
166 else if (l1.Offset > l2.Offset)
173 private class RowComparerClass : IComparer
175 public int Compare (object a, object b)
177 LineNumberEntry l1 = (LineNumberEntry) a;
178 LineNumberEntry l2 = (LineNumberEntry) b;
182 else if (l1.Row > l2.Row)
189 public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
190 public static readonly IComparer RowComparer = new RowComparerClass ();
192 public override string ToString ()
194 return String.Format ("[Line {0}:{1}]", Row, Offset);
198 public class LexicalBlockEntry
201 #region This is actually written to the symbol file
202 public int StartOffset;
203 public int EndOffset;
206 public LexicalBlockEntry (int index, int start_offset)
209 this.StartOffset = start_offset;
212 internal LexicalBlockEntry (int index, BinaryReader reader)
215 this.StartOffset = reader.ReadInt32 ();
216 this.EndOffset = reader.ReadInt32 ();
219 public void Close (int end_offset)
221 this.EndOffset = end_offset;
224 internal void Write (BinaryWriter bw)
226 bw.Write (StartOffset);
227 bw.Write (EndOffset);
230 public override string ToString ()
232 return String.Format ("[LexicalBlock {0}:{1}]", StartOffset, EndOffset);
236 public struct LocalVariableEntry
238 #region This is actually written to the symbol file
239 public readonly string Name;
240 public readonly FieldAttributes Attributes;
241 public readonly byte[] Signature;
242 public readonly int BlockIndex;
245 public LocalVariableEntry (string Name, FieldAttributes Attributes, byte[] Signature,
249 this.Attributes = Attributes;
250 this.Signature = Signature;
251 this.BlockIndex = BlockIndex;
254 internal LocalVariableEntry (BinaryReader reader)
256 int name_length = reader.ReadInt32 ();
257 byte[] name = reader.ReadBytes (name_length);
258 Name = Encoding.UTF8.GetString (name);
259 Attributes = (FieldAttributes) reader.ReadInt32 ();
260 int sig_length = reader.ReadInt32 ();
261 Signature = reader.ReadBytes (sig_length);
262 BlockIndex = reader.ReadInt32 ();
265 internal void Write (MonoSymbolFile file, BinaryWriter bw)
267 file.WriteString (bw, Name);
268 bw.Write ((int) Attributes);
269 bw.Write ((int) Signature.Length);
270 bw.Write (Signature);
271 bw.Write (BlockIndex);
274 public override string ToString ()
276 return String.Format ("[LocalVariable {0}:{1}]", Name, Attributes);
280 public class SourceFileEntry
282 #region This is actually written to the symbol file
283 public readonly int Index;
288 int NamespaceTableOffset;
294 ArrayList namespaces;
297 public static int Size {
301 internal SourceFileEntry (MonoSymbolFile file, string file_name)
304 this.file_name = file_name;
305 this.Index = file.AddSource (this);
308 methods = new ArrayList ();
309 namespaces = new ArrayList ();
312 public void DefineMethod (MethodBase method, int token, LocalVariableEntry[] locals,
313 LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
314 int start, int end, int namespace_id)
317 throw new InvalidOperationException ();
319 MethodEntry entry = new MethodEntry (
320 file, this, method, token, locals, lines, blocks, start, end, namespace_id);
323 file.AddMethod (entry);
326 public int DefineNamespace (string name, string[] using_clauses, int parent)
329 throw new InvalidOperationException ();
331 int index = file.GetNextNamespaceIndex ();
332 NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
337 internal void WriteData (BinaryWriter bw)
339 NameOffset = (int) bw.BaseStream.Position;
340 file.WriteString (bw, file_name);
342 ArrayList list = new ArrayList ();
343 foreach (MethodEntry entry in methods)
344 list.Add (entry.Write (file, bw));
348 MethodOffset = (int) bw.BaseStream.Position;
349 foreach (MethodSourceEntry method in list)
352 NamespaceCount = namespaces.Count;
353 NamespaceTableOffset = (int) bw.BaseStream.Position;
354 foreach (NamespaceEntry ns in namespaces)
358 internal void Write (BinaryWriter bw)
362 bw.Write (NamespaceCount);
363 bw.Write (NameOffset);
364 bw.Write (MethodOffset);
365 bw.Write (NamespaceTableOffset);
368 internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
372 Index = reader.ReadInt32 ();
373 Count = reader.ReadInt32 ();
374 NamespaceCount = reader.ReadInt32 ();
375 NameOffset = reader.ReadInt32 ();
376 MethodOffset = reader.ReadInt32 ();
377 NamespaceTableOffset = reader.ReadInt32 ();
379 file_name = file.ReadString (NameOffset);
382 public string FileName {
383 get { return file_name; }
386 public MethodSourceEntry[] Methods {
389 throw new InvalidOperationException ();
391 BinaryReader reader = file.BinaryReader;
392 int old_pos = (int) reader.BaseStream.Position;
394 reader.BaseStream.Position = MethodOffset;
395 ArrayList list = new ArrayList ();
396 for (int i = 0; i < Count; i ++)
397 list.Add (new MethodSourceEntry (reader));
398 reader.BaseStream.Position = old_pos;
400 MethodSourceEntry[] retval = new MethodSourceEntry [Count];
401 list.CopyTo (retval, 0);
406 public NamespaceEntry[] Namespaces {
409 throw new InvalidOperationException ();
411 BinaryReader reader = file.BinaryReader;
412 int old_pos = (int) reader.BaseStream.Position;
414 reader.BaseStream.Position = NamespaceTableOffset;
415 ArrayList list = new ArrayList ();
416 for (int i = 0; i < NamespaceCount; i ++)
417 list.Add (new NamespaceEntry (file, reader));
418 reader.BaseStream.Position = old_pos;
420 NamespaceEntry[] retval = new NamespaceEntry [list.Count];
421 list.CopyTo (retval, 0);
426 public override string ToString ()
428 return String.Format ("SourceFileEntry ({0}:{1}:{2})",
429 Index, file_name, Count);
433 public struct MethodSourceEntry : IComparable
435 #region This is actually written to the symbol file
436 public readonly int Index;
437 public readonly int FileOffset;
438 public readonly int StartRow;
439 public readonly int EndRow;
442 public MethodSourceEntry (int index, int file_offset, int start, int end)
445 this.FileOffset = file_offset;
446 this.StartRow = start;
450 internal MethodSourceEntry (BinaryReader reader)
452 Index = reader.ReadInt32 ();
453 FileOffset = reader.ReadInt32 ();
454 StartRow = reader.ReadInt32 ();
455 EndRow = reader.ReadInt32 ();
458 public static int Size {
462 internal void Write (BinaryWriter bw)
465 bw.Write (FileOffset);
470 public int CompareTo (object obj)
472 MethodSourceEntry method = (MethodSourceEntry) obj;
474 if (method.StartRow < StartRow)
476 else if (method.StartRow > StartRow)
482 public override string ToString ()
484 return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
485 Index, FileOffset, StartRow, EndRow);
489 public struct MethodIndexEntry
491 #region This is actually written to the symbol file
492 public readonly int FileOffset;
493 public readonly int Token;
496 public static int Size {
500 public MethodIndexEntry (int offset, int token)
502 this.FileOffset = offset;
506 internal MethodIndexEntry (BinaryReader reader)
508 FileOffset = reader.ReadInt32 ();
509 Token = reader.ReadInt32 ();
512 internal void Write (BinaryWriter bw)
514 bw.Write (FileOffset);
518 public override string ToString ()
520 return String.Format ("MethodIndexEntry ({0}:{1:x})",
525 public class MethodEntry
527 #region This is actually written to the symbol file
528 public readonly int SourceFileIndex;
529 public readonly int Token;
530 public readonly int StartRow;
531 public readonly int EndRow;
532 public readonly int ClassTypeIndex;
533 public readonly int NumParameters;
534 public readonly int NumLocals;
535 public readonly int NumLineNumbers;
536 public readonly int NamespaceID;
537 public readonly bool LocalNamesAmbiguous;
540 int TypeIndexTableOffset;
541 int LocalVariableTableOffset;
542 int LineNumberTableOffset;
543 int NumLexicalBlocks;
544 int LexicalBlockTableOffset;
550 public readonly int Index;
551 public readonly SourceFileEntry SourceFile;
552 public readonly LineNumberEntry[] LineNumbers;
553 public readonly int[] ParamTypeIndices;
554 public readonly int[] LocalTypeIndices;
555 public readonly LocalVariableEntry[] Locals;
556 public readonly Type[] LocalTypes;
557 public readonly LexicalBlockEntry[] LexicalBlocks;
559 public readonly MonoSymbolFile SymbolFile;
561 public static int Size {
569 public MethodBase MethodBase {
570 get { return MonoDebuggerSupport.GetMethod (SymbolFile.Assembly, Token); }
573 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
575 this.SymbolFile = file;
577 SourceFileIndex = reader.ReadInt32 ();
578 Token = reader.ReadInt32 ();
579 StartRow = reader.ReadInt32 ();
580 EndRow = reader.ReadInt32 ();
581 ClassTypeIndex = reader.ReadInt32 ();
582 NumParameters = reader.ReadInt32 ();
583 NumLocals = reader.ReadInt32 ();
584 NumLineNumbers = reader.ReadInt32 ();
585 NameOffset = reader.ReadInt32 ();
586 TypeIndexTableOffset = reader.ReadInt32 ();
587 LocalVariableTableOffset = reader.ReadInt32 ();
588 LineNumberTableOffset = reader.ReadInt32 ();
589 NumLexicalBlocks = reader.ReadInt32 ();
590 LexicalBlockTableOffset = reader.ReadInt32 ();
591 NamespaceID = reader.ReadInt32 ();
592 LocalNamesAmbiguous = reader.ReadInt32 () != 0;
594 name = file.ReadString (NameOffset);
596 SourceFile = file.GetSourceFile (SourceFileIndex);
598 if (LineNumberTableOffset != 0) {
599 long old_pos = reader.BaseStream.Position;
600 reader.BaseStream.Position = LineNumberTableOffset;
602 LineNumbers = new LineNumberEntry [NumLineNumbers];
604 for (int i = 0; i < NumLineNumbers; i++)
605 LineNumbers [i] = new LineNumberEntry (reader);
607 reader.BaseStream.Position = old_pos;
610 if (LocalVariableTableOffset != 0) {
611 long old_pos = reader.BaseStream.Position;
612 reader.BaseStream.Position = LocalVariableTableOffset;
614 Locals = new LocalVariableEntry [NumLocals];
615 LocalTypes = new Type [NumLocals];
617 Assembly ass = file.Assembly;
619 for (int i = 0; i < NumLocals; i++) {
620 Locals [i] = new LocalVariableEntry (reader);
621 LocalTypes [i] = MonoDebuggerSupport.GetLocalTypeFromSignature (
622 ass, Locals [i].Signature);
625 reader.BaseStream.Position = old_pos;
628 if (TypeIndexTableOffset != 0) {
629 long old_pos = reader.BaseStream.Position;
630 reader.BaseStream.Position = TypeIndexTableOffset;
632 ParamTypeIndices = new int [NumParameters];
633 LocalTypeIndices = new int [NumLocals];
635 for (int i = 0; i < NumParameters; i++)
636 ParamTypeIndices [i] = reader.ReadInt32 ();
637 for (int i = 0; i < NumLocals; i++)
638 LocalTypeIndices [i] = reader.ReadInt32 ();
640 reader.BaseStream.Position = old_pos;
643 if (LexicalBlockTableOffset != 0) {
644 long old_pos = reader.BaseStream.Position;
645 reader.BaseStream.Position = LexicalBlockTableOffset;
647 LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
648 for (int i = 0; i < NumLexicalBlocks; i++)
649 LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
651 reader.BaseStream.Position = old_pos;
655 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
656 int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
657 LexicalBlockEntry[] blocks, int start_row, int end_row,
660 this.SymbolFile = file;
661 Index = file.GetNextMethodIndex ();
664 SourceFileIndex = source.Index;
666 StartRow = start_row;
668 NamespaceID = namespace_id;
669 LexicalBlocks = blocks;
670 NumLexicalBlocks = LexicalBlocks.Length;
672 LineNumbers = BuildLineNumberTable (lines);
673 NumLineNumbers = LineNumbers.Length;
675 ParameterInfo[] parameters = method.GetParameters ();
676 if (parameters == null)
677 parameters = new ParameterInfo [0];
681 NumParameters = parameters.Length;
682 ParamTypeIndices = new int [NumParameters];
683 for (int i = 0; i < NumParameters; i++)
684 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
686 NumLocals = locals.Length;
689 if (NumLocals <= 32) {
690 // Most of the time, the O(n^2) factor is actually
691 // less than the cost of allocating the hash table,
692 // 32 is a rough number obtained through some testing.
694 for (int i = 0; i < NumLocals; i ++) {
695 string nm = locals [i].Name;
697 for (int j = i + 1; j < NumLocals; j ++) {
698 if (locals [j].Name == nm) {
699 LocalNamesAmbiguous = true;
700 goto locals_check_done;
707 Hashtable local_names = new Hashtable ();
708 foreach (LocalVariableEntry local in locals) {
709 if (local_names.Contains (local.Name)) {
710 LocalNamesAmbiguous = true;
713 local_names.Add (local.Name, local);
717 LocalTypeIndices = new int [NumLocals];
718 for (int i = 0; i < NumLocals; i++)
719 LocalTypeIndices [i] = file.GetNextTypeIndex ();
721 ClassTypeIndex = file.DefineType (method.ReflectedType);
724 static LineNumberEntry [] tmp_buff = new LineNumberEntry [20];
726 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
727 // we aren't going "backwards" since this would counfuse the runtime's
728 // debugging code (and the debugger).
730 // In the line number table, the "offset" field most be strictly
731 // monotonic increasing; that is, the next entry must not have an offset
732 // which is equal to or less than the current one.
734 // The most common case is that our input (ie. the line number table as
735 // we get it from mcs) contains several entries with the same offset
736 // (and different line numbers) - but it may also happen that the offset
737 // is decreasing (this can be considered as an exception, such lines will
738 // simply be discarded).
739 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
742 int last_offset = -1;
745 if (tmp_buff.Length < (line_numbers.Length + 1))
746 tmp_buff = new LineNumberEntry [(line_numbers.Length + 1) * 2];
748 for (int i = 0; i < line_numbers.Length; i++) {
749 LineNumberEntry line = line_numbers [i];
751 if (line.Offset > last_offset) {
753 tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
755 last_offset = line.Offset;
756 } else if (line.Row > last_row) {
762 tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
764 LineNumberEntry [] retval = new LineNumberEntry [pos];
765 Array.Copy (tmp_buff, retval, pos);
769 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
771 NameOffset = (int) bw.BaseStream.Position;
772 file.WriteString (bw, name);
774 TypeIndexTableOffset = (int) bw.BaseStream.Position;
776 for (int i = 0; i < NumParameters; i++)
777 bw.Write (ParamTypeIndices [i]);
778 for (int i = 0; i < NumLocals; i++)
779 bw.Write (LocalTypeIndices [i]);
781 LocalVariableTableOffset = (int) bw.BaseStream.Position;
782 for (int i = 0; i < NumLocals; i++)
783 Locals [i].Write (file, bw);
784 file.LocalCount += NumLocals;
786 LineNumberTableOffset = (int) bw.BaseStream.Position;
787 for (int i = 0; i < NumLineNumbers; i++)
788 LineNumbers [i].Write (bw);
789 file.LineNumberCount += NumLineNumbers;
791 LexicalBlockTableOffset = (int) bw.BaseStream.Position;
792 for (int i = 0; i < NumLexicalBlocks; i++)
793 LexicalBlocks [i].Write (bw);
794 file_offset = (int) bw.BaseStream.Position;
796 bw.Write (SourceFileIndex);
800 bw.Write (ClassTypeIndex);
801 bw.Write (NumParameters);
802 bw.Write (NumLocals);
803 bw.Write (NumLineNumbers);
804 bw.Write (NameOffset);
805 bw.Write (TypeIndexTableOffset);
806 bw.Write (LocalVariableTableOffset);
807 bw.Write (LineNumberTableOffset);
808 bw.Write (NumLexicalBlocks);
809 bw.Write (LexicalBlockTableOffset);
810 bw.Write (NamespaceID);
811 bw.Write (LocalNamesAmbiguous ? 1 : 0);
813 return new MethodSourceEntry (Index, file_offset, StartRow, EndRow);
816 internal void WriteIndex (BinaryWriter bw)
818 new MethodIndexEntry (file_offset, Token).Write (bw);
821 public override string ToString ()
823 return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {6}:{7}:{8}:{9} - {5}]",
824 Index, Token, SourceFileIndex, StartRow, EndRow,
825 SourceFile, ClassTypeIndex, NumParameters,
826 NumLocals, NumLineNumbers);
830 public struct NamespaceEntry
832 #region This is actually written to the symbol file
833 public readonly string Name;
834 public readonly int Index;
835 public readonly int Parent;
836 public readonly string[] UsingClauses;
839 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
843 this.Parent = parent;
844 this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
847 internal NamespaceEntry (MonoSymbolFile file, BinaryReader reader)
849 Name = file.ReadString ();
850 Index = reader.ReadInt32 ();
851 Parent = reader.ReadInt32 ();
853 int count = reader.ReadInt32 ();
854 UsingClauses = new string [count];
855 for (int i = 0; i < count; i++)
856 UsingClauses [i] = file.ReadString ();
859 internal void Write (MonoSymbolFile file, BinaryWriter bw)
861 file.WriteString (bw, Name);
864 bw.Write (UsingClauses.Length);
865 foreach (string uc in UsingClauses)
866 file.WriteString (bw, uc);
869 public override string ToString ()
871 return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);