2 // Mono.CSharp.Debugger/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
.Collections
;
37 // Parts which are actually written into the symbol file are marked with
39 // #region This is actually written to the symbol file
42 // Please do not modify these regions without previously talking to me.
44 // All changes to the file format must be synchronized in several places:
46 // a) The fields in these regions (and their order) must match the actual
47 // contents of the symbol file.
49 // This helps people to understand the symbol file format without reading
50 // too much source code, ie. you look at the appropriate region and then
51 // you know what's actually in the file.
53 // It is also required to help me enforce b).
55 // b) The regions must be kept in sync with the unmanaged code in
56 // mono/metadata/debug-mono-symfile.h
58 // When making changes to the file format, you must also increase two version
61 // i) OffsetTable.Version in this file.
62 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
64 // After doing so, recompile everything, including the debugger. Symbol files
65 // with different versions are incompatible to each other and the debugger and
66 // the runtime enfore this, so you need to recompile all your assemblies after
67 // changing the file format.
70 namespace Mono
.CompilerServices
.SymbolWriter
72 public struct OffsetTable
74 public const int Version
= 38;
75 public const long Magic
= 0x45e82623fd7fa614;
77 #region This is actually written to the symbol file
78 public int TotalFileSize
;
79 public int DataSectionOffset
;
80 public int DataSectionSize
;
81 public int SourceCount
;
82 public int SourceTableOffset
;
83 public int SourceTableSize
;
84 public int MethodCount
;
85 public int MethodTableOffset
;
86 public int MethodTableSize
;
90 internal OffsetTable (BinaryReader reader
)
92 TotalFileSize
= reader
.ReadInt32 ();
93 DataSectionOffset
= reader
.ReadInt32 ();
94 DataSectionSize
= reader
.ReadInt32 ();
95 SourceCount
= reader
.ReadInt32 ();
96 SourceTableOffset
= reader
.ReadInt32 ();
97 SourceTableSize
= reader
.ReadInt32 ();
98 MethodCount
= reader
.ReadInt32 ();
99 MethodTableOffset
= reader
.ReadInt32 ();
100 MethodTableSize
= reader
.ReadInt32 ();
101 TypeCount
= reader
.ReadInt32 ();
104 internal void Write (BinaryWriter bw
)
106 bw
.Write (TotalFileSize
);
107 bw
.Write (DataSectionOffset
);
108 bw
.Write (DataSectionSize
);
109 bw
.Write (SourceCount
);
110 bw
.Write (SourceTableOffset
);
111 bw
.Write (SourceTableSize
);
112 bw
.Write (MethodCount
);
113 bw
.Write (MethodTableOffset
);
114 bw
.Write (MethodTableSize
);
115 bw
.Write (TypeCount
);
118 public override string ToString ()
120 return String
.Format (
121 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
122 TotalFileSize
, DataSectionOffset
, DataSectionSize
, SourceCount
,
123 SourceTableOffset
, SourceTableSize
, MethodCount
, MethodTableOffset
,
124 MethodTableSize
, TypeCount
);
128 public struct LineNumberEntry
130 #region This is actually written to the symbol file
131 public readonly int Row
;
132 public readonly int Offset
;
135 public LineNumberEntry (int row
, int offset
)
138 this.Offset
= offset
;
141 public static LineNumberEntry Null
= new LineNumberEntry (0, 0);
143 internal LineNumberEntry (BinaryReader reader
)
145 Row
= reader
.ReadInt32 ();
146 Offset
= reader
.ReadInt32 ();
149 internal void Write (BinaryWriter bw
)
155 private class OffsetComparerClass
: IComparer
157 public int Compare (object a
, object b
)
159 LineNumberEntry l1
= (LineNumberEntry
) a
;
160 LineNumberEntry l2
= (LineNumberEntry
) b
;
162 if (l1
.Offset
< l2
.Offset
)
164 else if (l1
.Offset
> l2
.Offset
)
171 private class RowComparerClass
: IComparer
173 public int Compare (object a
, object b
)
175 LineNumberEntry l1
= (LineNumberEntry
) a
;
176 LineNumberEntry l2
= (LineNumberEntry
) b
;
180 else if (l1
.Row
> l2
.Row
)
187 public static readonly IComparer OffsetComparer
= new OffsetComparerClass ();
188 public static readonly IComparer RowComparer
= new RowComparerClass ();
190 public override string ToString ()
192 return String
.Format ("[Line {0}:{1}]", Row
, Offset
);
196 public class LexicalBlockEntry
199 #region This is actually written to the symbol file
200 public int StartOffset
;
201 public int EndOffset
;
204 public LexicalBlockEntry (int index
, int start_offset
)
207 this.StartOffset
= start_offset
;
210 internal LexicalBlockEntry (int index
, MyBinaryReader reader
)
213 this.StartOffset
= reader
.ReadInt32 ();
214 this.EndOffset
= reader
.ReadInt32 ();
217 public void Close (int end_offset
)
219 this.EndOffset
= end_offset
;
222 internal void Write (MyBinaryWriter bw
)
224 bw
.Write (StartOffset
);
225 bw
.Write (EndOffset
);
228 public override string ToString ()
230 return String
.Format ("[LexicalBlock {0}:{1}]", StartOffset
, EndOffset
);
234 public struct LocalVariableEntry
236 #region This is actually written to the symbol file
237 public readonly string Name
;
238 public readonly byte[] Signature
;
239 public readonly int BlockIndex
;
242 public LocalVariableEntry (string Name
, byte[] Signature
, int BlockIndex
)
245 this.Signature
= Signature
;
246 this.BlockIndex
= BlockIndex
;
249 internal LocalVariableEntry (MyBinaryReader reader
)
251 Name
= reader
.ReadString ();
252 int sig_length
= reader
.ReadLeb128 ();
253 Signature
= reader
.ReadBytes (sig_length
);
254 BlockIndex
= reader
.ReadLeb128 ();
257 internal void Write (MonoSymbolFile file
, MyBinaryWriter bw
)
260 bw
.WriteLeb128 ((int) Signature
.Length
);
261 bw
.Write (Signature
);
262 bw
.WriteLeb128 (BlockIndex
);
265 public override string ToString ()
267 return String
.Format ("[LocalVariable {0}]", Name
);
271 public class SourceFileEntry
273 #region This is actually written to the symbol file
274 public readonly int Index
;
279 int NamespaceTableOffset
;
285 ArrayList namespaces
;
288 public static int Size
{
292 public SourceFileEntry (MonoSymbolFile file
, string file_name
)
295 this.file_name
= file_name
;
296 this.Index
= file
.AddSource (this);
299 methods
= new ArrayList ();
300 namespaces
= new ArrayList ();
303 public void DefineMethod (string name
, int token
, LocalVariableEntry
[] locals
,
304 LineNumberEntry
[] lines
, LexicalBlockEntry
[] blocks
,
305 int start
, int end
, int namespace_id
)
308 throw new InvalidOperationException ();
310 MethodEntry entry
= new MethodEntry (
311 file
, this, name
, (int) token
, locals
, lines
, blocks
,
312 start
, end
, namespace_id
);
315 file
.AddMethod (entry
);
318 public int DefineNamespace (string name
, string[] using_clauses
, int parent
)
321 throw new InvalidOperationException ();
323 int index
= file
.GetNextNamespaceIndex ();
324 NamespaceEntry ns
= new NamespaceEntry (name
, index
, using_clauses
, parent
);
329 internal void WriteData (MyBinaryWriter bw
)
331 NameOffset
= (int) bw
.BaseStream
.Position
;
332 bw
.Write (file_name
);
334 ArrayList list
= new ArrayList ();
335 foreach (MethodEntry entry
in methods
)
336 list
.Add (entry
.Write (file
, bw
));
340 MethodOffset
= (int) bw
.BaseStream
.Position
;
341 foreach (MethodSourceEntry method
in list
)
344 NamespaceCount
= namespaces
.Count
;
345 NamespaceTableOffset
= (int) bw
.BaseStream
.Position
;
346 foreach (NamespaceEntry ns
in namespaces
)
350 internal void Write (BinaryWriter bw
)
354 bw
.Write (NamespaceCount
);
355 bw
.Write (NameOffset
);
356 bw
.Write (MethodOffset
);
357 bw
.Write (NamespaceTableOffset
);
360 internal SourceFileEntry (MonoSymbolFile file
, BinaryReader reader
)
364 Index
= reader
.ReadInt32 ();
365 Count
= reader
.ReadInt32 ();
366 NamespaceCount
= reader
.ReadInt32 ();
367 NameOffset
= reader
.ReadInt32 ();
368 MethodOffset
= reader
.ReadInt32 ();
369 NamespaceTableOffset
= reader
.ReadInt32 ();
371 file_name
= file
.ReadString (NameOffset
);
374 public string FileName
{
375 get { return file_name; }
378 public MethodSourceEntry
[] Methods
{
381 throw new InvalidOperationException ();
383 BinaryReader reader
= file
.BinaryReader
;
384 int old_pos
= (int) reader
.BaseStream
.Position
;
386 reader
.BaseStream
.Position
= MethodOffset
;
387 ArrayList list
= new ArrayList ();
388 for (int i
= 0; i
< Count
; i
++)
389 list
.Add (new MethodSourceEntry (reader
));
390 reader
.BaseStream
.Position
= old_pos
;
392 MethodSourceEntry
[] retval
= new MethodSourceEntry
[Count
];
393 list
.CopyTo (retval
, 0);
398 public NamespaceEntry
[] Namespaces
{
401 throw new InvalidOperationException ();
403 MyBinaryReader reader
= file
.BinaryReader
;
404 int old_pos
= (int) reader
.BaseStream
.Position
;
406 reader
.BaseStream
.Position
= NamespaceTableOffset
;
407 ArrayList list
= new ArrayList ();
408 for (int i
= 0; i
< NamespaceCount
; i
++)
409 list
.Add (new NamespaceEntry (file
, reader
));
410 reader
.BaseStream
.Position
= old_pos
;
412 NamespaceEntry
[] retval
= new NamespaceEntry
[list
.Count
];
413 list
.CopyTo (retval
, 0);
418 public override string ToString ()
420 return String
.Format ("SourceFileEntry ({0}:{1}:{2})",
421 Index
, file_name
, Count
);
425 public struct MethodSourceEntry
: IComparable
427 #region This is actually written to the symbol file
428 public readonly int Index
;
429 public readonly int FileOffset
;
430 public readonly int StartRow
;
431 public readonly int EndRow
;
434 public MethodSourceEntry (int index
, int file_offset
, int start
, int end
)
437 this.FileOffset
= file_offset
;
438 this.StartRow
= start
;
442 internal MethodSourceEntry (BinaryReader reader
)
444 Index
= reader
.ReadInt32 ();
445 FileOffset
= reader
.ReadInt32 ();
446 StartRow
= reader
.ReadInt32 ();
447 EndRow
= reader
.ReadInt32 ();
450 public static int Size
{
454 internal void Write (BinaryWriter bw
)
457 bw
.Write (FileOffset
);
462 public int CompareTo (object obj
)
464 MethodSourceEntry method
= (MethodSourceEntry
) obj
;
466 if (method
.StartRow
< StartRow
)
468 else if (method
.StartRow
> StartRow
)
474 public override string ToString ()
476 return String
.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
477 Index
, FileOffset
, StartRow
, EndRow
);
481 public struct MethodIndexEntry
483 #region This is actually written to the symbol file
484 public readonly int FileOffset
;
485 public readonly int Token
;
488 public static int Size
{
492 public MethodIndexEntry (int offset
, int token
)
494 this.FileOffset
= offset
;
498 internal MethodIndexEntry (BinaryReader reader
)
500 FileOffset
= reader
.ReadInt32 ();
501 Token
= reader
.ReadInt32 ();
504 internal void Write (BinaryWriter bw
)
506 bw
.Write (FileOffset
);
510 public override string ToString ()
512 return String
.Format ("MethodIndexEntry ({0}:{1:x})",
517 public class MethodEntry
: IComparable
519 #region This is actually written to the symbol file
520 public readonly int SourceFileIndex
;
521 public readonly int Token
;
522 public readonly int StartRow
;
523 public readonly int EndRow
;
524 public readonly int NumLocals
;
525 public readonly int NumLineNumbers
;
526 public readonly int NamespaceID
;
527 public readonly bool LocalNamesAmbiguous
;
530 int TypeIndexTableOffset
;
531 int LocalVariableTableOffset
;
532 int LineNumberTableOffset
;
533 int NumLexicalBlocks
;
534 int LexicalBlockTableOffset
;
540 public readonly SourceFileEntry SourceFile
;
541 public readonly LineNumberEntry
[] LineNumbers
;
542 public readonly int[] LocalTypeIndices
;
543 public readonly LocalVariableEntry
[] Locals
;
544 public readonly LexicalBlockEntry
[] LexicalBlocks
;
546 public readonly MonoSymbolFile SymbolFile
;
549 get { return index; }
550 set { index = value; }
553 public static int Size
{
557 internal MethodEntry (MonoSymbolFile file
, MyBinaryReader reader
, int index
)
559 this.SymbolFile
= file
;
561 SourceFileIndex
= reader
.ReadInt32 ();
562 Token
= reader
.ReadInt32 ();
563 StartRow
= reader
.ReadInt32 ();
564 EndRow
= reader
.ReadInt32 ();
565 NumLocals
= reader
.ReadInt32 ();
566 NumLineNumbers
= reader
.ReadInt32 ();
567 NameOffset
= reader
.ReadInt32 ();
568 TypeIndexTableOffset
= reader
.ReadInt32 ();
569 LocalVariableTableOffset
= reader
.ReadInt32 ();
570 LineNumberTableOffset
= reader
.ReadInt32 ();
571 NumLexicalBlocks
= reader
.ReadInt32 ();
572 LexicalBlockTableOffset
= reader
.ReadInt32 ();
573 NamespaceID
= reader
.ReadInt32 ();
574 LocalNamesAmbiguous
= reader
.ReadInt32 () != 0;
576 SourceFile
= file
.GetSourceFile (SourceFileIndex
);
578 if (LineNumberTableOffset
!= 0) {
579 long old_pos
= reader
.BaseStream
.Position
;
580 reader
.BaseStream
.Position
= LineNumberTableOffset
;
582 LineNumbers
= new LineNumberEntry
[NumLineNumbers
];
584 for (int i
= 0; i
< NumLineNumbers
; i
++)
585 LineNumbers
[i
] = new LineNumberEntry (reader
);
587 reader
.BaseStream
.Position
= old_pos
;
590 if (LocalVariableTableOffset
!= 0) {
591 long old_pos
= reader
.BaseStream
.Position
;
592 reader
.BaseStream
.Position
= LocalVariableTableOffset
;
594 Locals
= new LocalVariableEntry
[NumLocals
];
596 for (int i
= 0; i
< NumLocals
; i
++)
597 Locals
[i
] = new LocalVariableEntry (reader
);
599 reader
.BaseStream
.Position
= old_pos
;
602 if (TypeIndexTableOffset
!= 0) {
603 long old_pos
= reader
.BaseStream
.Position
;
604 reader
.BaseStream
.Position
= TypeIndexTableOffset
;
606 LocalTypeIndices
= new int [NumLocals
];
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
,
627 string name
, int token
, LocalVariableEntry
[] locals
,
628 LineNumberEntry
[] lines
, LexicalBlockEntry
[] blocks
,
629 int start_row
, int end_row
, int namespace_id
)
631 this.SymbolFile
= file
;
636 SourceFileIndex
= source
.Index
;
638 StartRow
= start_row
;
640 NamespaceID
= namespace_id
;
641 LexicalBlocks
= blocks
;
642 NumLexicalBlocks
= LexicalBlocks
!= null ? LexicalBlocks
.Length
: 0;
644 LineNumbers
= BuildLineNumberTable (lines
);
645 NumLineNumbers
= LineNumbers
.Length
;
647 file
.NumLineNumbers
+= NumLineNumbers
;
649 NumLocals
= locals
!= null ? locals
.Length
: 0;
652 if (NumLocals
<= 32) {
653 // Most of the time, the O(n^2) factor is actually
654 // less than the cost of allocating the hash table,
655 // 32 is a rough number obtained through some testing.
657 for (int i
= 0; i
< NumLocals
; i
++) {
658 string nm
= locals
[i
].Name
;
660 for (int j
= i
+ 1; j
< NumLocals
; j
++) {
661 if (locals
[j
].Name
== nm
) {
662 LocalNamesAmbiguous
= true;
663 goto locals_check_done
;
670 Hashtable local_names
= new Hashtable ();
671 foreach (LocalVariableEntry local
in locals
) {
672 if (local_names
.Contains (local
.Name
)) {
673 LocalNamesAmbiguous
= true;
676 local_names
.Add (local
.Name
, local
);
680 LocalTypeIndices
= new int [NumLocals
];
681 for (int i
= 0; i
< NumLocals
; i
++)
682 LocalTypeIndices
[i
] = file
.GetNextTypeIndex ();
685 static LineNumberEntry
[] tmp_buff
= new LineNumberEntry
[20];
687 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
688 // we aren't going "backwards" since this would counfuse the runtime's
689 // debugging code (and the debugger).
691 // In the line number table, the "offset" field most be strictly
692 // monotonic increasing; that is, the next entry must not have an offset
693 // which is equal to or less than the current one.
695 // The most common case is that our input (ie. the line number table as
696 // we get it from mcs) contains several entries with the same offset
697 // (and different line numbers) - but it may also happen that the offset
698 // is decreasing (this can be considered as an exception, such lines will
699 // simply be discarded).
700 LineNumberEntry
[] BuildLineNumberTable (LineNumberEntry
[] line_numbers
)
703 int last_offset
= -1;
706 if (line_numbers
== null)
707 return new LineNumberEntry
[0];
709 if (tmp_buff
.Length
< (line_numbers
.Length
+ 1))
710 tmp_buff
= new LineNumberEntry
[(line_numbers
.Length
+ 1) * 2];
712 for (int i
= 0; i
< line_numbers
.Length
; i
++) {
713 LineNumberEntry line
= line_numbers
[i
];
715 if (line
.Offset
> last_offset
) {
717 tmp_buff
[pos
++] = new LineNumberEntry (last_row
, last_offset
);
719 last_offset
= line
.Offset
;
720 } else if (line
.Row
> last_row
) {
726 tmp_buff
[pos
++] = new LineNumberEntry (last_row
, last_offset
);
728 LineNumberEntry
[] retval
= new LineNumberEntry
[pos
];
729 Array
.Copy (tmp_buff
, retval
, pos
);
733 internal MethodSourceEntry
Write (MonoSymbolFile file
, MyBinaryWriter bw
)
736 throw new InvalidOperationException ();
738 NameOffset
= (int) bw
.BaseStream
.Position
;
740 TypeIndexTableOffset
= (int) bw
.BaseStream
.Position
;
742 for (int i
= 0; i
< NumLocals
; i
++)
743 bw
.Write (LocalTypeIndices
[i
]);
745 LocalVariableTableOffset
= (int) bw
.BaseStream
.Position
;
746 for (int i
= 0; i
< NumLocals
; i
++)
747 Locals
[i
].Write (file
, bw
);
748 file
.LocalCount
+= NumLocals
;
750 LineNumberTableOffset
= (int) bw
.BaseStream
.Position
;
751 for (int i
= 0; i
< NumLineNumbers
; i
++)
752 LineNumbers
[i
].Write (bw
);
753 file
.LineNumberCount
+= NumLineNumbers
;
755 LexicalBlockTableOffset
= (int) bw
.BaseStream
.Position
;
756 for (int i
= 0; i
< NumLexicalBlocks
; i
++)
757 LexicalBlocks
[i
].Write (bw
);
758 file_offset
= (int) bw
.BaseStream
.Position
;
760 bw
.Write (SourceFileIndex
);
764 bw
.Write (NumLocals
);
765 bw
.Write (NumLineNumbers
);
766 bw
.Write (NameOffset
);
767 bw
.Write (TypeIndexTableOffset
);
768 bw
.Write (LocalVariableTableOffset
);
769 bw
.Write (LineNumberTableOffset
);
770 bw
.Write (NumLexicalBlocks
);
771 bw
.Write (LexicalBlockTableOffset
);
772 bw
.Write (NamespaceID
);
773 bw
.Write (LocalNamesAmbiguous
? 1 : 0);
775 return new MethodSourceEntry (index
, file_offset
, StartRow
, EndRow
);
778 internal void WriteIndex (BinaryWriter bw
)
780 new MethodIndexEntry (file_offset
, Token
).Write (bw
);
783 public int CompareTo (object obj
)
785 MethodEntry method
= (MethodEntry
) obj
;
787 if (method
.Token
< Token
)
789 else if (method
.Token
> Token
)
795 public override string ToString ()
797 return String
.Format ("[Method {0}:{1}:{2}:{3}:{4} - {6}:{7} - {5}]",
798 index
, Token
, SourceFileIndex
, StartRow
, EndRow
,
799 SourceFile
, NumLocals
, NumLineNumbers
);
803 public struct NamespaceEntry
805 #region This is actually written to the symbol file
806 public readonly string Name
;
807 public readonly int Index
;
808 public readonly int Parent
;
809 public readonly string[] UsingClauses
;
812 public NamespaceEntry (string name
, int index
, string[] using_clauses
, int parent
)
816 this.Parent
= parent
;
817 this.UsingClauses
= using_clauses
!= null ? using_clauses
: new string [0];
820 internal NamespaceEntry (MonoSymbolFile file
, MyBinaryReader reader
)
822 Name
= reader
.ReadString ();
823 Index
= reader
.ReadLeb128 ();
824 Parent
= reader
.ReadLeb128 ();
826 int count
= reader
.ReadLeb128 ();
827 UsingClauses
= new string [count
];
828 for (int i
= 0; i
< count
; i
++)
829 UsingClauses
[i
] = reader
.ReadString ();
832 internal void Write (MonoSymbolFile file
, MyBinaryWriter bw
)
835 bw
.WriteLeb128 (Index
);
836 bw
.WriteLeb128 (Parent
);
837 bw
.WriteLeb128 (UsingClauses
.Length
);
838 foreach (string uc
in UsingClauses
)
842 public override string ToString ()
844 return String
.Format ("[Namespace {0}:{1}:{2}]", Name
, Index
, Parent
);