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;
515 public readonly int Index;
516 public readonly SourceFileEntry SourceFile;
517 public readonly LineNumberEntry[] LineNumbers;
518 public readonly int[] ParamTypeIndices;
519 public readonly int[] LocalTypeIndices;
520 public readonly LocalVariableEntry[] Locals;
521 public readonly Type[] LocalTypes;
522 public readonly LexicalBlockEntry[] LexicalBlocks;
524 public readonly MonoSymbolFile SymbolFile;
526 public static int Size {
534 public string FullName {
535 get { return full_name; }
538 public MethodBase MethodBase {
539 get { return SymbolFile.Assembly.MonoDebugger_GetMethod (Token); }
542 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
544 this.SymbolFile = file;
546 SourceFileIndex = reader.ReadInt32 ();
547 Token = reader.ReadInt32 ();
548 StartRow = reader.ReadInt32 ();
549 EndRow = reader.ReadInt32 ();
550 ClassTypeIndex = reader.ReadInt32 ();
551 NumParameters = reader.ReadInt32 ();
552 NumLocals = reader.ReadInt32 ();
553 NumLineNumbers = reader.ReadInt32 ();
554 NameOffset = reader.ReadInt32 ();
555 FullNameOffset = reader.ReadInt32 ();
556 TypeIndexTableOffset = reader.ReadInt32 ();
557 LocalVariableTableOffset = reader.ReadInt32 ();
558 LineNumberTableOffset = reader.ReadInt32 ();
559 NumLexicalBlocks = reader.ReadInt32 ();
560 LexicalBlockTableOffset = reader.ReadInt32 ();
561 NamespaceID = reader.ReadInt32 ();
562 LocalNamesAmbiguous = reader.ReadInt32 () != 0;
564 name = file.ReadString (NameOffset);
565 full_name = file.ReadString (FullNameOffset);
567 SourceFile = file.GetSourceFile (SourceFileIndex);
569 if (LineNumberTableOffset != 0) {
570 long old_pos = reader.BaseStream.Position;
571 reader.BaseStream.Position = LineNumberTableOffset;
573 LineNumbers = new LineNumberEntry [NumLineNumbers];
575 for (int i = 0; i < NumLineNumbers; i++)
576 LineNumbers [i] = new LineNumberEntry (reader);
578 reader.BaseStream.Position = old_pos;
581 if (LocalVariableTableOffset != 0) {
582 long old_pos = reader.BaseStream.Position;
583 reader.BaseStream.Position = LocalVariableTableOffset;
585 Locals = new LocalVariableEntry [NumLocals];
586 LocalTypes = new Type [NumLocals];
588 Assembly ass = file.Assembly;
590 for (int i = 0; i < NumLocals; i++) {
591 Locals [i] = new LocalVariableEntry (reader);
592 LocalTypes [i] = ass.MonoDebugger_GetLocalTypeFromSignature (
593 Locals [i].Signature);
596 reader.BaseStream.Position = old_pos;
599 if (TypeIndexTableOffset != 0) {
600 long old_pos = reader.BaseStream.Position;
601 reader.BaseStream.Position = TypeIndexTableOffset;
603 ParamTypeIndices = new int [NumParameters];
604 LocalTypeIndices = new int [NumLocals];
606 for (int i = 0; i < NumParameters; i++)
607 ParamTypeIndices [i] = reader.ReadInt32 ();
608 for (int i = 0; i < NumLocals; i++)
609 LocalTypeIndices [i] = reader.ReadInt32 ();
611 reader.BaseStream.Position = old_pos;
614 if (LexicalBlockTableOffset != 0) {
615 long old_pos = reader.BaseStream.Position;
616 reader.BaseStream.Position = LexicalBlockTableOffset;
618 LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
619 for (int i = 0; i < NumLexicalBlocks; i++)
620 LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
622 reader.BaseStream.Position = old_pos;
626 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
627 int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
628 LexicalBlockEntry[] blocks, int start_row, int end_row,
631 this.SymbolFile = file;
632 Index = file.GetNextMethodIndex ();
635 SourceFileIndex = source.Index;
637 StartRow = start_row;
639 NamespaceID = namespace_id;
640 LexicalBlocks = blocks;
641 NumLexicalBlocks = LexicalBlocks.Length;
643 LineNumbers = BuildLineNumberTable (lines);
644 NumLineNumbers = LineNumbers.Length;
646 ParameterInfo[] parameters = method.GetParameters ();
647 if (parameters == null)
648 parameters = new ParameterInfo [0];
650 if (parameters.Length == 0)
651 full_name = method.DeclaringType.FullName + "." + method.Name + "()";
652 else if (parameters.Length == 1)
653 full_name = method.DeclaringType.FullName + "." + method.Name + "(" + parameters [0].ParameterType.FullName + ")";
654 else if (parameters.Length == 2)
655 full_name = method.DeclaringType.FullName + "." + method.Name + "(" + parameters [0].ParameterType.FullName + "," + parameters [1].ParameterType.FullName + ")";
657 StringBuilder sb = new StringBuilder ();
658 sb.Append (method.DeclaringType.FullName);
660 sb.Append (method.Name);
662 for (int i = 0; i < parameters.Length; i++) {
665 sb.Append (parameters [i].ParameterType.FullName);
668 full_name = sb.ToString ();
673 NumParameters = parameters.Length;
674 ParamTypeIndices = new int [NumParameters];
675 for (int i = 0; i < NumParameters; i++)
676 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
678 NumLocals = locals.Length;
681 if (NumLocals <= 32) {
682 // Most of the time, the O(n^2) factor is actually
683 // less than the cost of allocating the hash table,
684 // 32 is a rough number obtained through some testing.
686 for (int i = 0; i < NumLocals; i ++) {
687 string nm = locals [i].Name;
689 for (int j = i + 1; j < NumLocals; j ++) {
690 if (locals [j].Name == nm) {
691 LocalNamesAmbiguous = true;
692 goto locals_check_done;
699 Hashtable local_names = new Hashtable ();
700 foreach (LocalVariableEntry local in locals) {
701 if (local_names.Contains (local.Name)) {
702 LocalNamesAmbiguous = true;
705 local_names.Add (local.Name, local);
709 LocalTypeIndices = new int [NumLocals];
710 for (int i = 0; i < NumLocals; i++)
711 LocalTypeIndices [i] = file.GetNextTypeIndex ();
713 ClassTypeIndex = file.DefineType (method.ReflectedType);
716 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
717 // we aren't going "backwards" since this would counfuse the runtime's
718 // debugging code (and the debugger).
720 // In the line number table, the "offset" field most be strictly
721 // monotonic increasing; that is, the next entry must not have an offset
722 // which is equal to or less than the current one.
724 // The most common case is that our input (ie. the line number table as
725 // we get it from mcs) contains several entries with the same offset
726 // (and different line numbers) - but it may also happen that the offset
727 // is decreasing (this can be considered as an exception, such lines will
728 // simply be discarded).
729 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
731 ArrayList list = new ArrayList ();
732 int last_offset = -1;
735 for (int i = 0; i < line_numbers.Length; i++) {
736 LineNumberEntry line = (LineNumberEntry) line_numbers [i];
738 if (line.Offset > last_offset) {
740 list.Add (new LineNumberEntry (last_row, last_offset));
742 last_offset = line.Offset;
743 } else if (line.Row > last_row) {
749 list.Add (new LineNumberEntry (last_row, last_offset));
751 LineNumberEntry[] retval = new LineNumberEntry [list.Count];
752 list.CopyTo (retval, 0);
756 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
758 NameOffset = (int) bw.BaseStream.Position;
759 file.WriteString (bw, name);
761 FullNameOffset = (int) bw.BaseStream.Position;
762 file.WriteString (bw, full_name);
764 TypeIndexTableOffset = (int) bw.BaseStream.Position;
766 for (int i = 0; i < NumParameters; i++)
767 bw.Write (ParamTypeIndices [i]);
768 for (int i = 0; i < NumLocals; i++)
769 bw.Write (LocalTypeIndices [i]);
771 LocalVariableTableOffset = (int) bw.BaseStream.Position;
772 for (int i = 0; i < NumLocals; i++)
773 Locals [i].Write (file, bw);
774 file.LocalCount += NumLocals;
776 LineNumberTableOffset = (int) bw.BaseStream.Position;
777 for (int i = 0; i < NumLineNumbers; i++)
778 LineNumbers [i].Write (bw);
779 file.LineNumberCount += NumLineNumbers;
781 LexicalBlockTableOffset = (int) bw.BaseStream.Position;
782 for (int i = 0; i < NumLexicalBlocks; i++)
783 LexicalBlocks [i].Write (bw);
784 file_offset = (int) bw.BaseStream.Position;
786 bw.Write (SourceFileIndex);
790 bw.Write (ClassTypeIndex);
791 bw.Write (NumParameters);
792 bw.Write (NumLocals);
793 bw.Write (NumLineNumbers);
794 bw.Write (NameOffset);
795 bw.Write (FullNameOffset);
796 bw.Write (TypeIndexTableOffset);
797 bw.Write (LocalVariableTableOffset);
798 bw.Write (LineNumberTableOffset);
799 bw.Write (NumLexicalBlocks);
800 bw.Write (LexicalBlockTableOffset);
801 bw.Write (NamespaceID);
802 bw.Write (LocalNamesAmbiguous ? 1 : 0);
804 return new MethodSourceEntry (Index, file_offset, StartRow, EndRow);
807 internal void WriteIndex (BinaryWriter bw)
809 new MethodIndexEntry (file_offset, FullNameOffset, Token).Write (bw);
812 public override string ToString ()
814 return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {7}:{8}:{9}:{10} - {5} - {6}]",
815 Index, Token, SourceFileIndex, StartRow, EndRow,
816 SourceFile, FullName, ClassTypeIndex, NumParameters,
817 NumLocals, NumLineNumbers);
821 public struct NamespaceEntry
823 #region This is actually written to the symbol file
824 public readonly string Name;
825 public readonly int Index;
826 public readonly int Parent;
827 public readonly string[] UsingClauses;
830 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
834 this.Parent = parent;
835 this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
838 internal void Write (MonoSymbolFile file, BinaryWriter bw)
840 file.WriteString (bw, Name);
843 bw.Write (UsingClauses.Length);
844 foreach (string uc in UsingClauses)
845 file.WriteString (bw, uc);
848 public override string ToString ()
850 return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);