/symbols/pdb/Mono.Cecil.Pdb/PdbReader.cs

http://github.com/jbevain/cecil · C# · 211 lines · 148 code · 48 blank · 15 comment · 25 complexity · c3380f937dd127126cd50b7a07aa915b 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.Collections.Generic;
  14. using Microsoft.Cci.Pdb;
  15. using Mono.Cecil.Cil;
  16. namespace Mono.Cecil.Pdb {
  17. public class PdbReader : ISymbolReader {
  18. int age;
  19. Guid guid;
  20. readonly Disposable<Stream> pdb_file;
  21. readonly Dictionary<string, Document> documents = new Dictionary<string, Document> ();
  22. readonly Dictionary<uint, PdbFunction> functions = new Dictionary<uint, PdbFunction> ();
  23. internal PdbReader (Disposable<Stream> file)
  24. {
  25. this.pdb_file = file;
  26. }
  27. /*
  28. uint Magic = 0x53445352;
  29. Guid Signature;
  30. uint Age;
  31. string FileName;
  32. */
  33. public bool ProcessDebugHeader (ImageDebugDirectory directory, byte [] header)
  34. {
  35. if (directory.Type != 2) //IMAGE_DEBUG_TYPE_CODEVIEW
  36. return false;
  37. if (directory.MajorVersion != 0 || directory.MinorVersion != 0)
  38. return false;
  39. if (header.Length < 24)
  40. return false;
  41. var magic = ReadInt32 (header, 0);
  42. if (magic != 0x53445352)
  43. return false;
  44. var guid_bytes = new byte [16];
  45. Buffer.BlockCopy (header, 4, guid_bytes, 0, 16);
  46. this.guid = new Guid (guid_bytes);
  47. this.age = ReadInt32 (header, 20);
  48. return PopulateFunctions ();
  49. }
  50. static int ReadInt32 (byte [] bytes, int start)
  51. {
  52. return (bytes [start]
  53. | (bytes [start + 1] << 8)
  54. | (bytes [start + 2] << 16)
  55. | (bytes [start + 3] << 24));
  56. }
  57. bool PopulateFunctions ()
  58. {
  59. using (pdb_file) {
  60. Dictionary<uint, PdbTokenLine> tokenToSourceMapping;
  61. string sourceServerData;
  62. int age;
  63. Guid guid;
  64. var funcs = PdbFile.LoadFunctions (pdb_file.value, out tokenToSourceMapping, out sourceServerData, out age, out guid);
  65. if (this.guid != guid)
  66. return false;
  67. foreach (PdbFunction function in funcs)
  68. functions.Add (function.token, function);
  69. }
  70. return true;
  71. }
  72. public MethodDebugInformation Read (MethodDefinition method)
  73. {
  74. var method_token = method.MetadataToken;
  75. PdbFunction function;
  76. if (!functions.TryGetValue (method_token.ToUInt32 (), out function))
  77. return null;
  78. var symbol = new MethodDebugInformation (method);
  79. ReadSequencePoints (function, symbol);
  80. if (function.scopes.Length > 1)
  81. throw new NotSupportedException ();
  82. else if (function.scopes.Length == 1)
  83. symbol.scope = ReadScopeAndLocals (function.scopes [0], symbol);
  84. return symbol;
  85. }
  86. static Collection<ScopeDebugInformation> ReadScopeAndLocals (PdbScope [] scopes, MethodDebugInformation info)
  87. {
  88. var symbols = new Collection<ScopeDebugInformation> (scopes.Length);
  89. foreach (PdbScope scope in scopes)
  90. if (scope != null)
  91. symbols.Add (ReadScopeAndLocals (scope, info));
  92. return symbols;
  93. }
  94. static ScopeDebugInformation ReadScopeAndLocals (PdbScope scope, MethodDebugInformation info)
  95. {
  96. var parent = new ScopeDebugInformation ();
  97. parent.Start = new InstructionOffset ((int) scope.offset);
  98. parent.End = new InstructionOffset ((int) (scope.offset + scope.length));
  99. if (!scope.slots.IsNullOrEmpty()) {
  100. parent.variables = new Collection<VariableDebugInformation> (scope.slots.Length);
  101. foreach (PdbSlot slot in scope.slots) {
  102. var index = (int) slot.slot;
  103. var variable = new VariableDebugInformation (index, slot.name);
  104. if (slot.flags == 4)
  105. variable.IsDebuggerHidden = true;
  106. parent.variables.Add (variable);
  107. }
  108. }
  109. if (!scope.constants.IsNullOrEmpty ()) {
  110. parent.constants = new Collection<ConstantDebugInformation> (scope.constants.Length);
  111. foreach (var constant in scope.constants) {
  112. parent.constants.Add (new ConstantDebugInformation (
  113. constant.name,
  114. (TypeReference) info.method.Module.LookupToken ((int) constant.token),
  115. constant.value));
  116. }
  117. }
  118. parent.scopes = ReadScopeAndLocals (scope.scopes, info);
  119. return parent;
  120. }
  121. void ReadSequencePoints (PdbFunction function, MethodDebugInformation info)
  122. {
  123. if (function.lines == null)
  124. return;
  125. info.sequence_points = new Collection<SequencePoint> ();
  126. foreach (PdbLines lines in function.lines)
  127. ReadLines (lines, info);
  128. }
  129. void ReadLines (PdbLines lines, MethodDebugInformation info)
  130. {
  131. var document = GetDocument (lines.file);
  132. foreach (var line in lines.lines)
  133. ReadLine (line, document, info);
  134. }
  135. static void ReadLine (PdbLine line, Document document, MethodDebugInformation info)
  136. {
  137. var sequence_point = new SequencePoint ((int) line.offset, document);
  138. sequence_point.StartLine = (int) line.lineBegin;
  139. sequence_point.StartColumn = (int) line.colBegin;
  140. sequence_point.EndLine = (int) line.lineEnd;
  141. sequence_point.EndColumn = (int) line.colEnd;
  142. info.sequence_points.Add (sequence_point);
  143. }
  144. Document GetDocument (PdbSource source)
  145. {
  146. string name = source.name;
  147. Document document;
  148. if (documents.TryGetValue (name, out document))
  149. return document;
  150. document = new Document (name) {
  151. Language = source.language.ToLanguage (),
  152. LanguageVendor = source.vendor.ToVendor (),
  153. Type = source.doctype.ToType (),
  154. };
  155. documents.Add (name, document);
  156. return document;
  157. }
  158. public void Dispose ()
  159. {
  160. pdb_file.Dispose ();
  161. }
  162. }
  163. }