/symbols/mdb/Mono.Cecil.Mdb/MdbReader.cs

http://github.com/jbevain/cecil · C# · 207 lines · 151 code · 47 blank · 9 comment · 23 complexity · e86acffd88489648590ff299528970ce MD5 · raw file

  1. //
  2. // Author:
  3. // Jb Evain (jbevain@gmail.com)
  4. //
  5. // Copyright (c) 2008 - 2015 Jb Evain
  6. // Copyright (c) 2008 - 2011 Novell, Inc.
  7. //
  8. // Licensed under the MIT/X11 license.
  9. //
  10. using System;
  11. using System.Collections.Generic;
  12. using System.IO;
  13. using Mono.Cecil.Cil;
  14. using Mono.Collections.Generic;
  15. using Mono.CompilerServices.SymbolWriter;
  16. namespace Mono.Cecil.Mdb {
  17. public sealed class MdbReaderProvider : ISymbolReaderProvider {
  18. public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
  19. {
  20. Mixin.CheckModule (module);
  21. Mixin.CheckFileName (fileName);
  22. return new MdbReader (module, MonoSymbolFile.ReadSymbolFile (Mixin.GetMdbFileName (fileName)));
  23. }
  24. public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
  25. {
  26. Mixin.CheckModule (module);
  27. Mixin.CheckStream (symbolStream);
  28. return new MdbReader (module, MonoSymbolFile.ReadSymbolFile (symbolStream));
  29. }
  30. }
  31. public sealed class MdbReader : ISymbolReader {
  32. readonly ModuleDefinition module;
  33. readonly MonoSymbolFile symbol_file;
  34. readonly Dictionary<string, Document> documents;
  35. public MdbReader (ModuleDefinition module, MonoSymbolFile symFile)
  36. {
  37. this.module = module;
  38. this.symbol_file = symFile;
  39. this.documents = new Dictionary<string, Document> ();
  40. }
  41. public ISymbolWriterProvider GetWriterProvider ()
  42. {
  43. return new MdbWriterProvider ();
  44. }
  45. public bool ProcessDebugHeader (ImageDebugHeader header)
  46. {
  47. return symbol_file.Guid == module.Mvid;
  48. }
  49. public MethodDebugInformation Read (MethodDefinition method)
  50. {
  51. var method_token = method.MetadataToken;
  52. var entry = symbol_file.GetMethodByToken (method_token.ToInt32 ());
  53. if (entry == null)
  54. return null;
  55. var info = new MethodDebugInformation (method);
  56. info.code_size = ReadCodeSize (method);
  57. var scopes = ReadScopes (entry, info);
  58. ReadLineNumbers (entry, info);
  59. ReadLocalVariables (entry, scopes);
  60. return info;
  61. }
  62. static int ReadCodeSize (MethodDefinition method)
  63. {
  64. return method.Module.Read (method, (m, reader) => reader.ReadCodeSize (m));
  65. }
  66. static void ReadLocalVariables (MethodEntry entry, ScopeDebugInformation [] scopes)
  67. {
  68. var locals = entry.GetLocals ();
  69. foreach (var local in locals) {
  70. var variable = new VariableDebugInformation (local.Index, local.Name);
  71. var index = local.BlockIndex;
  72. if (index < 0 || index >= scopes.Length)
  73. continue;
  74. var scope = scopes [index];
  75. if (scope == null)
  76. continue;
  77. scope.Variables.Add (variable);
  78. }
  79. }
  80. void ReadLineNumbers (MethodEntry entry, MethodDebugInformation info)
  81. {
  82. var table = entry.GetLineNumberTable ();
  83. info.sequence_points = new Collection<SequencePoint> (table.LineNumbers.Length);
  84. for (var i = 0; i < table.LineNumbers.Length; i++) {
  85. var line = table.LineNumbers [i];
  86. if (i > 0 && table.LineNumbers [i - 1].Offset == line.Offset)
  87. continue;
  88. info.sequence_points.Add (LineToSequencePoint (line));
  89. }
  90. }
  91. Document GetDocument (SourceFileEntry file)
  92. {
  93. var file_name = file.FileName;
  94. Document document;
  95. if (documents.TryGetValue (file_name, out document))
  96. return document;
  97. document = new Document (file_name) {
  98. Hash = file.Checksum,
  99. };
  100. documents.Add (file_name, document);
  101. return document;
  102. }
  103. static ScopeDebugInformation [] ReadScopes (MethodEntry entry, MethodDebugInformation info)
  104. {
  105. var blocks = entry.GetCodeBlocks ();
  106. var scopes = new ScopeDebugInformation [blocks.Length + 1];
  107. info.scope = scopes [0] = new ScopeDebugInformation {
  108. Start = new InstructionOffset (0),
  109. End = new InstructionOffset (info.code_size),
  110. };
  111. foreach (var block in blocks) {
  112. if (block.BlockType != CodeBlockEntry.Type.Lexical && block.BlockType != CodeBlockEntry.Type.CompilerGenerated)
  113. continue;
  114. var scope = new ScopeDebugInformation ();
  115. scope.Start = new InstructionOffset (block.StartOffset);
  116. scope.End = new InstructionOffset (block.EndOffset);
  117. scopes [block.Index + 1] = scope;
  118. if (!AddScope (info.scope.Scopes, scope))
  119. info.scope.Scopes.Add (scope);
  120. }
  121. return scopes;
  122. }
  123. static bool AddScope (Collection<ScopeDebugInformation> scopes, ScopeDebugInformation scope)
  124. {
  125. foreach (var sub_scope in scopes) {
  126. if (sub_scope.HasScopes && AddScope (sub_scope.Scopes, scope))
  127. return true;
  128. if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) {
  129. sub_scope.Scopes.Add (scope);
  130. return true;
  131. }
  132. }
  133. return false;
  134. }
  135. SequencePoint LineToSequencePoint (LineNumberEntry line)
  136. {
  137. var source = symbol_file.GetSourceFile (line.File);
  138. return new SequencePoint (line.Offset, GetDocument (source)) {
  139. StartLine = line.Row,
  140. EndLine = line.EndRow,
  141. StartColumn = line.Column,
  142. EndColumn = line.EndColumn,
  143. };
  144. }
  145. public void Dispose ()
  146. {
  147. symbol_file.Dispose ();
  148. }
  149. }
  150. static class MethodEntryExtensions {
  151. public static bool HasColumnInfo (this MethodEntry entry)
  152. {
  153. return (entry.MethodFlags & MethodEntry.Flags.ColumnsInfoIncluded) != 0;
  154. }
  155. public static bool HasEndInfo (this MethodEntry entry)
  156. {
  157. return (entry.MethodFlags & MethodEntry.Flags.EndInfoIncluded) != 0;
  158. }
  159. }
  160. }