2 // Mono.CSharp.Debugger/MonoSymbolFile.cs
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2003 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 SRE = System.Reflection.Emit;
34 using System.Collections.Generic;
36 using System.Threading;
39 namespace Mono.CompilerServices.SymbolWriter
41 public class MonoSymbolFileException : Exception
43 public MonoSymbolFileException ()
47 public MonoSymbolFileException (string message, params object[] args)
48 : base (String.Format (message, args))
52 internal class MyBinaryWriter : BinaryWriter
54 public MyBinaryWriter (Stream stream)
58 public void WriteLeb128 (int value)
60 base.Write7BitEncodedInt (value);
64 internal class MyBinaryReader : BinaryReader
66 public MyBinaryReader (Stream stream)
70 public int ReadLeb128 ()
72 return base.Read7BitEncodedInt ();
75 public string ReadString (int offset)
77 long old_pos = BaseStream.Position;
78 BaseStream.Position = offset;
80 string text = ReadString ();
82 BaseStream.Position = old_pos;
87 public interface ISourceFile
89 SourceFileEntry Entry {
94 public interface ICompileUnit
96 CompileUnitEntry Entry {
101 public interface IMethodDef
113 internal class MonoDebuggerSupport
115 static GetMethodTokenFunc get_method_token;
116 static GetGuidFunc get_guid;
117 static GetLocalIndexFunc get_local_index;
119 delegate int GetMethodTokenFunc (MethodBase method);
120 delegate Guid GetGuidFunc (Module module);
121 delegate int GetLocalIndexFunc (SRE.LocalBuilder local);
123 static Delegate create_delegate (Type type, Type delegate_type, string name)
125 MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
126 BindingFlags.NonPublic);
128 throw new Exception ("Can't find " + name);
130 return Delegate.CreateDelegate (delegate_type, mi);
133 static MonoDebuggerSupport ()
135 get_method_token = (GetMethodTokenFunc) create_delegate (
136 typeof (Assembly), typeof (GetMethodTokenFunc),
137 "MonoDebugger_GetMethodToken");
139 get_guid = (GetGuidFunc) create_delegate (
140 typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid");
142 get_local_index = (GetLocalIndexFunc) create_delegate (
143 typeof (SRE.LocalBuilder), typeof (GetLocalIndexFunc),
144 "Mono_GetLocalIndex");
147 public static int GetMethodToken (MethodBase method)
149 return get_method_token (method);
152 public static Guid GetGuid (Module module)
154 return get_guid (module);
157 public static int GetLocalIndex (SRE.LocalBuilder local)
159 return get_local_index (local);
164 public class MonoSymbolFile : IDisposable
166 List<MethodEntry> methods = new List<MethodEntry> ();
167 List<SourceFileEntry> sources = new List<SourceFileEntry> ();
168 List<CompileUnitEntry> comp_units = new List<CompileUnitEntry> ();
169 Dictionary<Type, int> type_hash = new Dictionary<Type, int> ();
170 Dictionary<int, AnonymousScopeEntry> anonymous_scopes;
174 int last_method_index;
175 int last_namespace_index;
177 public readonly string FileName = "<dynamic>";
178 public readonly int MajorVersion = OffsetTable.MajorVersion;
179 public readonly int MinorVersion = OffsetTable.MinorVersion;
181 public int NumLineNumbers;
183 internal MonoSymbolFile ()
185 ot = new OffsetTable ();
188 internal int AddSource (SourceFileEntry source)
190 sources.Add (source);
191 return sources.Count;
194 internal int AddCompileUnit (CompileUnitEntry entry)
196 comp_units.Add (entry);
197 return comp_units.Count;
200 internal int DefineType (Type type)
203 if (type_hash.TryGetValue (type, out index))
206 index = ++last_type_index;
207 type_hash.Add (type, index);
211 internal void AddMethod (MethodEntry entry)
216 public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token,
217 ScopeVariable[] scope_vars, LocalVariableEntry[] locals,
218 LineNumberEntry[] lines, CodeBlockEntry[] code_blocks,
219 string real_name, MethodEntry.Flags flags,
223 throw new InvalidOperationException ();
225 MethodEntry method = new MethodEntry (
226 this, comp_unit, token, scope_vars, locals, lines, code_blocks,
227 real_name, flags, namespace_id);
232 internal void DefineAnonymousScope (int id)
235 throw new InvalidOperationException ();
237 if (anonymous_scopes == null)
238 anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
240 anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
243 internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
244 CapturedVariable.CapturedKind kind)
247 throw new InvalidOperationException ();
249 AnonymousScopeEntry scope = anonymous_scopes [scope_id];
250 scope.AddCapturedVariable (name, captured_name, kind);
253 internal void DefineCapturedScope (int scope_id, int id, string captured_name)
256 throw new InvalidOperationException ();
258 AnonymousScopeEntry scope = anonymous_scopes [scope_id];
259 scope.AddCapturedScope (id, captured_name);
262 internal int GetNextTypeIndex ()
264 return ++last_type_index;
267 internal int GetNextMethodIndex ()
269 return ++last_method_index;
272 internal int GetNextNamespaceIndex ()
274 return ++last_namespace_index;
277 void Write (MyBinaryWriter bw, Guid guid)
279 // Magic number and file version.
280 bw.Write (OffsetTable.Magic);
281 bw.Write (MajorVersion);
282 bw.Write (MinorVersion);
284 bw.Write (guid.ToByteArray ());
287 // Offsets of file sections; we must write this after we're done
288 // writing the whole file, so we just reserve the space for it here.
290 long offset_table_offset = bw.BaseStream.Position;
291 ot.Write (bw, MajorVersion, MinorVersion);
294 // Sort the methods according to their tokens and update their index.
297 for (int i = 0; i < methods.Count; i++)
298 ((MethodEntry) methods [i]).Index = i + 1;
301 // Write data sections.
303 ot.DataSectionOffset = (int) bw.BaseStream.Position;
304 foreach (SourceFileEntry source in sources)
305 source.WriteData (bw);
306 foreach (CompileUnitEntry comp_unit in comp_units)
307 comp_unit.WriteData (bw);
308 foreach (MethodEntry method in methods)
309 method.WriteData (this, bw);
310 ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
313 // Write the method index table.
315 ot.MethodTableOffset = (int) bw.BaseStream.Position;
316 for (int i = 0; i < methods.Count; i++) {
317 MethodEntry entry = (MethodEntry) methods [i];
320 ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
323 // Write source table.
325 ot.SourceTableOffset = (int) bw.BaseStream.Position;
326 for (int i = 0; i < sources.Count; i++) {
327 SourceFileEntry source = (SourceFileEntry) sources [i];
330 ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
333 // Write compilation unit table.
335 ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
336 for (int i = 0; i < comp_units.Count; i++) {
337 CompileUnitEntry unit = (CompileUnitEntry) comp_units [i];
340 ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
343 // Write anonymous scope table.
345 ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0;
346 ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position;
347 if (anonymous_scopes != null) {
348 foreach (AnonymousScopeEntry scope in anonymous_scopes.Values)
351 ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
354 // Fixup offset table.
356 ot.TypeCount = last_type_index;
357 ot.MethodCount = methods.Count;
358 ot.SourceCount = sources.Count;
359 ot.CompileUnitCount = comp_units.Count;
362 // Write offset table.
364 ot.TotalFileSize = (int) bw.BaseStream.Position;
365 bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
366 ot.Write (bw, MajorVersion, MinorVersion);
367 bw.Seek (0, SeekOrigin.End);
370 Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
371 "{3} methods.", NumLineNumbers, LineNumberSize,
372 ExtendedLineNumberSize, methods.Count);
376 public void CreateSymbolFile (Guid guid, FileStream fs)
379 throw new InvalidOperationException ();
381 Write (new MyBinaryWriter (fs), guid);
384 MyBinaryReader reader;
385 Dictionary<int, SourceFileEntry> source_file_hash;
386 Dictionary<int, CompileUnitEntry> compile_unit_hash;
388 List<MethodEntry> method_list;
389 Dictionary<int, MethodEntry> method_token_hash;
390 Dictionary<string, int> source_name_hash;
394 MonoSymbolFile (string filename)
396 this.FileName = filename;
397 FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
398 reader = new MyBinaryReader (stream);
401 long magic = reader.ReadInt64 ();
402 int major_version = reader.ReadInt32 ();
403 int minor_version = reader.ReadInt32 ();
405 if (magic != OffsetTable.Magic)
406 throw new MonoSymbolFileException (
407 "Symbol file `{0}' is not a valid " +
408 "Mono symbol file", filename);
409 if (major_version != OffsetTable.MajorVersion)
410 throw new MonoSymbolFileException (
411 "Symbol file `{0}' has version {1}, " +
412 "but expected {2}", filename, major_version,
413 OffsetTable.MajorVersion);
414 if (minor_version != OffsetTable.MinorVersion)
415 throw new MonoSymbolFileException (
416 "Symbol file `{0}' has version {1}.{2}, " +
417 "but expected {3}.{4}", filename, major_version,
418 minor_version, OffsetTable.MajorVersion,
419 OffsetTable.MinorVersion);
421 MajorVersion = major_version;
422 MinorVersion = minor_version;
423 guid = new Guid (reader.ReadBytes (16));
425 ot = new OffsetTable (reader, major_version, minor_version);
427 throw new MonoSymbolFileException (
428 "Cannot read symbol file `{0}'", filename);
431 source_file_hash = new Dictionary<int, SourceFileEntry> ();
432 compile_unit_hash = new Dictionary<int, CompileUnitEntry> ();
435 void CheckGuidMatch (Guid other, string filename, string assembly)
440 throw new MonoSymbolFileException (
441 "Symbol file `{0}' does not match assembly `{1}'",
446 protected MonoSymbolFile (string filename, Mono.Cecil.AssemblyDefinition assembly) : this (filename)
448 Guid mvid = assembly.MainModule.Mvid;
450 CheckGuidMatch (mvid, filename, assembly.MainModule.Image.FileInformation.FullName);
453 public static MonoSymbolFile ReadSymbolFile (Mono.Cecil.AssemblyDefinition assembly, string filename)
455 string name = filename + ".mdb";
457 return new MonoSymbolFile (name, assembly);
460 protected MonoSymbolFile (string filename, Assembly assembly) : this (filename)
462 // Check that the MDB file matches the assembly, if we have been
463 // passed an assembly.
464 if (assembly == null)
467 Module[] modules = assembly.GetModules ();
468 Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]);
470 CheckGuidMatch (assembly_guid, filename, assembly.Location);
473 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
475 string filename = assembly.Location;
476 string name = filename + ".mdb";
478 return new MonoSymbolFile (name, assembly);
482 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
484 return new MonoSymbolFile (mdbFilename, null);
487 public int CompileUnitCount {
488 get { return ot.CompileUnitCount; }
491 public int SourceCount {
492 get { return ot.SourceCount; }
495 public int MethodCount {
496 get { return ot.MethodCount; }
499 public int TypeCount {
500 get { return ot.TypeCount; }
503 public int AnonymousScopeCount {
504 get { return ot.AnonymousScopeCount; }
507 public int NamespaceCount {
508 get { return last_namespace_index; }
515 public OffsetTable OffsetTable {
519 internal int LineNumberCount = 0;
520 internal int LocalCount = 0;
521 internal int StringSize = 0;
523 internal int LineNumberSize = 0;
524 internal int ExtendedLineNumberSize = 0;
526 public SourceFileEntry GetSourceFile (int index)
528 if ((index < 1) || (index > ot.SourceCount))
529 throw new ArgumentException ();
531 throw new InvalidOperationException ();
534 SourceFileEntry source;
535 if (source_file_hash.TryGetValue (index, out source))
538 long old_pos = reader.BaseStream.Position;
540 reader.BaseStream.Position = ot.SourceTableOffset +
541 SourceFileEntry.Size * (index - 1);
542 source = new SourceFileEntry (this, reader);
543 source_file_hash.Add (index, source);
545 reader.BaseStream.Position = old_pos;
550 public SourceFileEntry[] Sources {
553 throw new InvalidOperationException ();
555 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
556 for (int i = 0; i < SourceCount; i++)
557 retval [i] = GetSourceFile (i + 1);
562 public CompileUnitEntry GetCompileUnit (int index)
564 if ((index < 1) || (index > ot.CompileUnitCount))
565 throw new ArgumentException ();
567 throw new InvalidOperationException ();
570 CompileUnitEntry unit;
571 if (compile_unit_hash.TryGetValue (index, out unit))
574 long old_pos = reader.BaseStream.Position;
576 reader.BaseStream.Position = ot.CompileUnitTableOffset +
577 CompileUnitEntry.Size * (index - 1);
578 unit = new CompileUnitEntry (this, reader);
579 compile_unit_hash.Add (index, unit);
581 reader.BaseStream.Position = old_pos;
586 public CompileUnitEntry[] CompileUnits {
589 throw new InvalidOperationException ();
591 CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
592 for (int i = 0; i < CompileUnitCount; i++)
593 retval [i] = GetCompileUnit (i + 1);
601 if (method_token_hash != null)
604 method_token_hash = new Dictionary<int, MethodEntry> ();
605 method_list = new List<MethodEntry> ();
607 long old_pos = reader.BaseStream.Position;
608 reader.BaseStream.Position = ot.MethodTableOffset;
610 for (int i = 0; i < MethodCount; i++) {
611 MethodEntry entry = new MethodEntry (this, reader, i + 1);
612 method_token_hash.Add (entry.Token, entry);
613 method_list.Add (entry);
616 reader.BaseStream.Position = old_pos;
620 public MethodEntry GetMethodByToken (int token)
623 throw new InvalidOperationException ();
628 method_token_hash.TryGetValue (token, out me);
633 public MethodEntry GetMethod (int index)
635 if ((index < 1) || (index > ot.MethodCount))
636 throw new ArgumentException ();
638 throw new InvalidOperationException ();
642 return (MethodEntry) method_list [index - 1];
646 public MethodEntry[] Methods {
649 throw new InvalidOperationException ();
653 MethodEntry[] retval = new MethodEntry [MethodCount];
654 method_list.CopyTo (retval, 0);
660 public int FindSource (string file_name)
663 throw new InvalidOperationException ();
666 if (source_name_hash == null) {
667 source_name_hash = new Dictionary<string, int> ();
669 for (int i = 0; i < ot.SourceCount; i++) {
670 SourceFileEntry source = GetSourceFile (i + 1);
671 source_name_hash.Add (source.FileName, i);
676 if (!source_name_hash.TryGetValue (file_name, out value))
682 public AnonymousScopeEntry GetAnonymousScope (int id)
685 throw new InvalidOperationException ();
687 AnonymousScopeEntry scope;
689 if (anonymous_scopes != null) {
690 anonymous_scopes.TryGetValue (id, out scope);
694 anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
695 reader.BaseStream.Position = ot.AnonymousScopeTableOffset;
696 for (int i = 0; i < ot.AnonymousScopeCount; i++) {
697 scope = new AnonymousScopeEntry (reader);
698 anonymous_scopes.Add (scope.ID, scope);
701 return anonymous_scopes [id];
705 internal MyBinaryReader BinaryReader {
708 throw new InvalidOperationException ();
714 public void Dispose ()
719 protected virtual void Dispose (bool disposing)
722 if (reader != null) {