2 // System.Diagnostics.SymbolStore/MonoSymbolTable.cs
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2002 Ximian, Inc. http://www.ximian.com
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Collections;
18 // Parts which are actually written into the symbol file are marked with
20 // #region This is actually written to the symbol file
23 // Please do not modify these regions without previously talking to me.
25 // All changes to the file format must be synchronized in several places:
27 // a) The fields in these regions (and their order) must match the actual
28 // contents of the symbol file.
30 // This helps people to understand the symbol file format without reading
31 // too much source code, ie. you look at the appropriate region and then
32 // you know what's actually in the file.
34 // It is also required to help me enforce b).
36 // b) The regions must be kept in sync with the unmanaged code in
37 // mono/metadata/debug-mono-symfile.h
39 // When making changes to the file format, you must also increase two version
42 // i) OffsetTable.Version in this file.
43 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
45 // After doing so, recompile everything, including the debugger. Symbol files
46 // with different versions are incompatible to each other and the debugger and
47 // the runtime enfore this, so you need to recompile all your assemblies after
48 // changing the file format.
51 namespace Mono.CSharp.Debugger
53 public struct OffsetTable
55 public const int Version = 35;
56 public const long Magic = 0x45e82623fd7fa614;
58 #region This is actually written to the symbol file
59 public int TotalFileSize;
60 public int DataSectionOffset;
61 public int DataSectionSize;
62 public int SourceCount;
63 public int SourceTableOffset;
64 public int SourceTableSize;
65 public int MethodCount;
66 public int MethodTableOffset;
67 public int MethodTableSize;
71 internal OffsetTable (BinaryReader reader)
73 TotalFileSize = reader.ReadInt32 ();
74 DataSectionOffset = reader.ReadInt32 ();
75 DataSectionSize = reader.ReadInt32 ();
76 SourceCount = reader.ReadInt32 ();
77 SourceTableOffset = reader.ReadInt32 ();
78 SourceTableSize = reader.ReadInt32 ();
79 MethodCount = reader.ReadInt32 ();
80 MethodTableOffset = reader.ReadInt32 ();
81 MethodTableSize = reader.ReadInt32 ();
82 TypeCount = reader.ReadInt32 ();
85 internal void Write (BinaryWriter bw)
87 bw.Write (TotalFileSize);
88 bw.Write (DataSectionOffset);
89 bw.Write (DataSectionSize);
90 bw.Write (SourceCount);
91 bw.Write (SourceTableOffset);
92 bw.Write (SourceTableSize);
93 bw.Write (MethodCount);
94 bw.Write (MethodTableOffset);
95 bw.Write (MethodTableSize);
99 public override string ToString ()
101 return String.Format (
102 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
103 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
104 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
105 MethodTableSize, TypeCount);
109 public struct LineNumberEntry
111 #region This is actually written to the symbol file
112 public readonly int Row;
113 public readonly int Offset;
116 public LineNumberEntry (int row, int offset)
119 this.Offset = offset;
122 public static LineNumberEntry Null = new LineNumberEntry (0, 0);
124 internal LineNumberEntry (BinaryReader reader)
126 Row = reader.ReadInt32 ();
127 Offset = reader.ReadInt32 ();
130 internal void Write (BinaryWriter bw)
136 private class OffsetComparerClass : IComparer
138 public int Compare (object a, object b)
140 LineNumberEntry l1 = (LineNumberEntry) a;
141 LineNumberEntry l2 = (LineNumberEntry) b;
143 if (l1.Offset < l2.Offset)
145 else if (l1.Offset > l2.Offset)
152 private class RowComparerClass : IComparer
154 public int Compare (object a, object b)
156 LineNumberEntry l1 = (LineNumberEntry) a;
157 LineNumberEntry l2 = (LineNumberEntry) b;
161 else if (l1.Row > l2.Row)
168 public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
169 public static readonly IComparer RowComparer = new RowComparerClass ();
171 public override string ToString ()
173 return String.Format ("[Line {0}:{1}]", Row, Offset);
177 public class LexicalBlockEntry
180 #region This is actually written to the symbol file
181 public int StartOffset;
182 public int EndOffset;
185 public LexicalBlockEntry (int index, int start_offset)
188 this.StartOffset = start_offset;
191 internal LexicalBlockEntry (int index, BinaryReader reader)
194 this.StartOffset = reader.ReadInt32 ();
195 this.EndOffset = reader.ReadInt32 ();
198 public void Close (int end_offset)
200 this.EndOffset = end_offset;
203 internal void Write (BinaryWriter bw)
205 bw.Write (StartOffset);
206 bw.Write (EndOffset);
209 public override string ToString ()
211 return String.Format ("[LexicalBlock {0}:{1}]", StartOffset, EndOffset);
215 public struct LocalVariableEntry
217 #region This is actually written to the symbol file
218 public readonly string Name;
219 public readonly FieldAttributes Attributes;
220 public readonly byte[] Signature;
221 public readonly int BlockIndex;
224 public LocalVariableEntry (string Name, FieldAttributes Attributes, byte[] Signature,
228 this.Attributes = Attributes;
229 this.Signature = Signature;
230 this.BlockIndex = BlockIndex;
233 internal LocalVariableEntry (BinaryReader reader)
235 int name_length = reader.ReadInt32 ();
236 byte[] name = reader.ReadBytes (name_length);
237 Name = Encoding.UTF8.GetString (name);
238 Attributes = (FieldAttributes) reader.ReadInt32 ();
239 int sig_length = reader.ReadInt32 ();
240 Signature = reader.ReadBytes (sig_length);
241 BlockIndex = reader.ReadInt32 ();
244 internal void Write (MonoSymbolFile file, BinaryWriter bw)
246 file.WriteString (bw, Name);
247 bw.Write ((int) Attributes);
248 bw.Write ((int) Signature.Length);
249 bw.Write (Signature);
250 bw.Write (BlockIndex);
253 public override string ToString ()
255 return String.Format ("[LocalVariable {0}:{1}]", Name, Attributes);
259 public class SourceFileEntry
261 #region This is actually written to the symbol file
262 public readonly int Index;
267 int NamespaceTableOffset;
273 ArrayList namespaces;
276 public static int Size {
280 internal SourceFileEntry (MonoSymbolFile file, string file_name)
283 this.file_name = file_name;
284 this.Index = file.AddSource (this);
287 methods = new ArrayList ();
288 namespaces = new ArrayList ();
291 public void DefineMethod (MethodBase method, int token, LocalVariableEntry[] locals,
292 LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
293 int start, int end, int namespace_id)
296 throw new InvalidOperationException ();
298 MethodEntry entry = new MethodEntry (
299 file, this, method, token, locals, lines, blocks, start, end, namespace_id);
302 file.AddMethod (entry);
305 public int DefineNamespace (string name, string[] using_clauses, int parent)
308 throw new InvalidOperationException ();
310 int index = file.GetNextNamespaceIndex ();
311 NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
316 internal void WriteData (BinaryWriter bw)
318 NameOffset = (int) bw.BaseStream.Position;
319 file.WriteString (bw, file_name);
321 ArrayList list = new ArrayList ();
322 foreach (MethodEntry entry in methods)
323 list.Add (entry.Write (file, bw));
327 MethodOffset = (int) bw.BaseStream.Position;
328 foreach (MethodSourceEntry method in list)
331 NamespaceCount = namespaces.Count;
332 NamespaceTableOffset = (int) bw.BaseStream.Position;
333 foreach (NamespaceEntry ns in namespaces)
337 internal void Write (BinaryWriter bw)
341 bw.Write (NamespaceCount);
342 bw.Write (NameOffset);
343 bw.Write (MethodOffset);
344 bw.Write (NamespaceTableOffset);
347 internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
351 Index = reader.ReadInt32 ();
352 Count = reader.ReadInt32 ();
353 NamespaceCount = reader.ReadInt32 ();
354 NameOffset = reader.ReadInt32 ();
355 MethodOffset = reader.ReadInt32 ();
356 NamespaceTableOffset = reader.ReadInt32 ();
358 file_name = file.ReadString (NameOffset);
361 public string FileName {
362 get { return file_name; }
365 public MethodSourceEntry[] Methods {
368 throw new InvalidOperationException ();
370 BinaryReader reader = file.BinaryReader;
371 int old_pos = (int) reader.BaseStream.Position;
373 reader.BaseStream.Position = MethodOffset;
374 ArrayList list = new ArrayList ();
375 for (int i = 0; i < Count; i ++)
376 list.Add (new MethodSourceEntry (reader));
377 reader.BaseStream.Position = old_pos;
379 MethodSourceEntry[] retval = new MethodSourceEntry [Count];
380 list.CopyTo (retval, 0);
385 public override string ToString ()
387 return String.Format ("SourceFileEntry ({0}:{1}:{2})",
388 Index, file_name, Count);
392 public struct MethodSourceEntry : IComparable
394 #region This is actually written to the symbol file
395 public readonly int Index;
396 public readonly int FileOffset;
397 public readonly int StartRow;
398 public readonly int EndRow;
401 public MethodSourceEntry (int index, int file_offset, int start, int end)
404 this.FileOffset = file_offset;
405 this.StartRow = start;
409 internal MethodSourceEntry (BinaryReader reader)
411 Index = reader.ReadInt32 ();
412 FileOffset = reader.ReadInt32 ();
413 StartRow = reader.ReadInt32 ();
414 EndRow = reader.ReadInt32 ();
417 public static int Size {
421 internal void Write (BinaryWriter bw)
424 bw.Write (FileOffset);
429 public int CompareTo (object obj)
431 MethodSourceEntry method = (MethodSourceEntry) obj;
433 if (method.StartRow < StartRow)
435 else if (method.StartRow > StartRow)
441 public override string ToString ()
443 return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
444 Index, FileOffset, StartRow, EndRow);
448 public struct MethodIndexEntry
450 #region This is actually written to the symbol file
451 public readonly int FileOffset;
452 public readonly int FullNameOffset;
453 public readonly int Token;
456 public static int Size {
460 public MethodIndexEntry (int offset, int name_offset, int token)
462 this.FileOffset = offset;
463 this.FullNameOffset = name_offset;
467 internal MethodIndexEntry (BinaryReader reader)
469 FileOffset = reader.ReadInt32 ();
470 FullNameOffset = reader.ReadInt32 ();
471 Token = reader.ReadInt32 ();
474 internal void Write (BinaryWriter bw)
476 bw.Write (FileOffset);
477 bw.Write (FullNameOffset);
481 public override string ToString ()
483 return String.Format ("MethodIndexEntry ({0}:{1}:{2:x})",
484 FileOffset, FullNameOffset, Token);
488 public class MethodEntry
490 #region This is actually written to the symbol file
491 public readonly int SourceFileIndex;
492 public readonly int Token;
493 public readonly int StartRow;
494 public readonly int EndRow;
495 public readonly int ClassTypeIndex;
496 public readonly int NumParameters;
497 public readonly int NumLocals;
498 public readonly int NumLineNumbers;
499 public readonly int NamespaceID;
500 public readonly bool LocalNamesAmbiguous;
504 int TypeIndexTableOffset;
505 int LocalVariableTableOffset;
506 int LineNumberTableOffset;
507 int NumLexicalBlocks;
508 int LexicalBlockTableOffset;
514 public readonly int Index;
515 public readonly SourceFileEntry SourceFile;
516 public readonly LineNumberEntry[] LineNumbers;
517 public readonly int[] ParamTypeIndices;
518 public readonly int[] LocalTypeIndices;
519 public readonly LocalVariableEntry[] Locals;
520 public readonly Type[] LocalTypes;
521 public readonly LexicalBlockEntry[] LexicalBlocks;
523 public readonly MonoSymbolFile SymbolFile;
525 public static int Size {
533 public MethodBase MethodBase {
534 get { return MonoDebuggerSupport.GetMethod (SymbolFile.Assembly, Token); }
537 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
539 this.SymbolFile = file;
541 SourceFileIndex = reader.ReadInt32 ();
542 Token = reader.ReadInt32 ();
543 StartRow = reader.ReadInt32 ();
544 EndRow = reader.ReadInt32 ();
545 ClassTypeIndex = reader.ReadInt32 ();
546 NumParameters = reader.ReadInt32 ();
547 NumLocals = reader.ReadInt32 ();
548 NumLineNumbers = reader.ReadInt32 ();
549 NameOffset = reader.ReadInt32 ();
550 FullNameOffset = reader.ReadInt32 ();
551 TypeIndexTableOffset = reader.ReadInt32 ();
552 LocalVariableTableOffset = reader.ReadInt32 ();
553 LineNumberTableOffset = reader.ReadInt32 ();
554 NumLexicalBlocks = reader.ReadInt32 ();
555 LexicalBlockTableOffset = reader.ReadInt32 ();
556 NamespaceID = reader.ReadInt32 ();
557 LocalNamesAmbiguous = reader.ReadInt32 () != 0;
559 name = file.ReadString (NameOffset);
561 SourceFile = file.GetSourceFile (SourceFileIndex);
563 if (LineNumberTableOffset != 0) {
564 long old_pos = reader.BaseStream.Position;
565 reader.BaseStream.Position = LineNumberTableOffset;
567 LineNumbers = new LineNumberEntry [NumLineNumbers];
569 for (int i = 0; i < NumLineNumbers; i++)
570 LineNumbers [i] = new LineNumberEntry (reader);
572 reader.BaseStream.Position = old_pos;
575 if (LocalVariableTableOffset != 0) {
576 long old_pos = reader.BaseStream.Position;
577 reader.BaseStream.Position = LocalVariableTableOffset;
579 Locals = new LocalVariableEntry [NumLocals];
580 LocalTypes = new Type [NumLocals];
582 Assembly ass = file.Assembly;
584 for (int i = 0; i < NumLocals; i++) {
585 Locals [i] = new LocalVariableEntry (reader);
586 LocalTypes [i] = MonoDebuggerSupport.GetLocalTypeFromSignature (
587 ass, Locals [i].Signature);
590 reader.BaseStream.Position = old_pos;
593 if (TypeIndexTableOffset != 0) {
594 long old_pos = reader.BaseStream.Position;
595 reader.BaseStream.Position = TypeIndexTableOffset;
597 ParamTypeIndices = new int [NumParameters];
598 LocalTypeIndices = new int [NumLocals];
600 for (int i = 0; i < NumParameters; i++)
601 ParamTypeIndices [i] = reader.ReadInt32 ();
602 for (int i = 0; i < NumLocals; i++)
603 LocalTypeIndices [i] = reader.ReadInt32 ();
605 reader.BaseStream.Position = old_pos;
608 if (LexicalBlockTableOffset != 0) {
609 long old_pos = reader.BaseStream.Position;
610 reader.BaseStream.Position = LexicalBlockTableOffset;
612 LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
613 for (int i = 0; i < NumLexicalBlocks; i++)
614 LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
616 reader.BaseStream.Position = old_pos;
620 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
621 int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
622 LexicalBlockEntry[] blocks, int start_row, int end_row,
625 this.SymbolFile = file;
626 Index = file.GetNextMethodIndex ();
629 SourceFileIndex = source.Index;
631 StartRow = start_row;
633 NamespaceID = namespace_id;
634 LexicalBlocks = blocks;
635 NumLexicalBlocks = LexicalBlocks.Length;
637 LineNumbers = BuildLineNumberTable (lines);
638 NumLineNumbers = LineNumbers.Length;
640 ParameterInfo[] parameters = method.GetParameters ();
641 if (parameters == null)
642 parameters = new ParameterInfo [0];
646 NumParameters = parameters.Length;
647 ParamTypeIndices = new int [NumParameters];
648 for (int i = 0; i < NumParameters; i++)
649 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
651 NumLocals = locals.Length;
654 if (NumLocals <= 32) {
655 // Most of the time, the O(n^2) factor is actually
656 // less than the cost of allocating the hash table,
657 // 32 is a rough number obtained through some testing.
659 for (int i = 0; i < NumLocals; i ++) {
660 string nm = locals [i].Name;
662 for (int j = i + 1; j < NumLocals; j ++) {
663 if (locals [j].Name == nm) {
664 LocalNamesAmbiguous = true;
665 goto locals_check_done;
672 Hashtable local_names = new Hashtable ();
673 foreach (LocalVariableEntry local in locals) {
674 if (local_names.Contains (local.Name)) {
675 LocalNamesAmbiguous = true;
678 local_names.Add (local.Name, local);
682 LocalTypeIndices = new int [NumLocals];
683 for (int i = 0; i < NumLocals; i++)
684 LocalTypeIndices [i] = file.GetNextTypeIndex ();
686 ClassTypeIndex = file.DefineType (method.ReflectedType);
689 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
690 // we aren't going "backwards" since this would counfuse the runtime's
691 // debugging code (and the debugger).
693 // In the line number table, the "offset" field most be strictly
694 // monotonic increasing; that is, the next entry must not have an offset
695 // which is equal to or less than the current one.
697 // The most common case is that our input (ie. the line number table as
698 // we get it from mcs) contains several entries with the same offset
699 // (and different line numbers) - but it may also happen that the offset
700 // is decreasing (this can be considered as an exception, such lines will
701 // simply be discarded).
702 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
704 ArrayList list = new ArrayList ();
705 int last_offset = -1;
708 for (int i = 0; i < line_numbers.Length; i++) {
709 LineNumberEntry line = (LineNumberEntry) line_numbers [i];
711 if (line.Offset > last_offset) {
713 list.Add (new LineNumberEntry (last_row, last_offset));
715 last_offset = line.Offset;
716 } else if (line.Row > last_row) {
722 list.Add (new LineNumberEntry (last_row, last_offset));
724 LineNumberEntry[] retval = new LineNumberEntry [list.Count];
725 list.CopyTo (retval, 0);
729 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
731 NameOffset = (int) bw.BaseStream.Position;
732 file.WriteString (bw, name);
734 TypeIndexTableOffset = (int) bw.BaseStream.Position;
736 for (int i = 0; i < NumParameters; i++)
737 bw.Write (ParamTypeIndices [i]);
738 for (int i = 0; i < NumLocals; i++)
739 bw.Write (LocalTypeIndices [i]);
741 LocalVariableTableOffset = (int) bw.BaseStream.Position;
742 for (int i = 0; i < NumLocals; i++)
743 Locals [i].Write (file, bw);
744 file.LocalCount += NumLocals;
746 LineNumberTableOffset = (int) bw.BaseStream.Position;
747 for (int i = 0; i < NumLineNumbers; i++)
748 LineNumbers [i].Write (bw);
749 file.LineNumberCount += NumLineNumbers;
751 LexicalBlockTableOffset = (int) bw.BaseStream.Position;
752 for (int i = 0; i < NumLexicalBlocks; i++)
753 LexicalBlocks [i].Write (bw);
754 file_offset = (int) bw.BaseStream.Position;
756 bw.Write (SourceFileIndex);
760 bw.Write (ClassTypeIndex);
761 bw.Write (NumParameters);
762 bw.Write (NumLocals);
763 bw.Write (NumLineNumbers);
764 bw.Write (NameOffset);
765 bw.Write (FullNameOffset);
766 bw.Write (TypeIndexTableOffset);
767 bw.Write (LocalVariableTableOffset);
768 bw.Write (LineNumberTableOffset);
769 bw.Write (NumLexicalBlocks);
770 bw.Write (LexicalBlockTableOffset);
771 bw.Write (NamespaceID);
772 bw.Write (LocalNamesAmbiguous ? 1 : 0);
774 return new MethodSourceEntry (Index, file_offset, StartRow, EndRow);
777 internal void WriteIndex (BinaryWriter bw)
779 new MethodIndexEntry (file_offset, FullNameOffset, Token).Write (bw);
782 public override string ToString ()
784 return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {7}:{8}:{9}:{10} - {5}]",
785 Index, Token, SourceFileIndex, StartRow, EndRow,
786 SourceFile, ClassTypeIndex, NumParameters,
787 NumLocals, NumLineNumbers);
791 public struct NamespaceEntry
793 #region This is actually written to the symbol file
794 public readonly string Name;
795 public readonly int Index;
796 public readonly int Parent;
797 public readonly string[] UsingClauses;
800 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
804 this.Parent = parent;
805 this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
808 internal void Write (MonoSymbolFile file, BinaryWriter bw)
810 file.WriteString (bw, Name);
813 bw.Write (UsingClauses.Length);
814 foreach (string uc in UsingClauses)
815 file.WriteString (bw, uc);
818 public override string ToString ()
820 return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);