NativePdbReader.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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 ILRuntime.Mono.Collections.Generic;
  14. using Microsoft.Cci.Pdb;
  15. using ILRuntime.Mono.Cecil.Cil;
  16. namespace ILRuntime.Mono.Cecil.Pdb {
  17. public class NativePdbReader : 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. readonly Dictionary<PdbScope, ImportDebugInformation> imports = new Dictionary<PdbScope, ImportDebugInformation> ();
  24. internal NativePdbReader (Disposable<Stream> file)
  25. {
  26. this.pdb_file = file;
  27. }
  28. public ISymbolWriterProvider GetWriterProvider ()
  29. {
  30. return new NativePdbWriterProvider ();
  31. }
  32. /*
  33. uint Magic = 0x53445352;
  34. Guid Signature;
  35. uint Age;
  36. string FileName;
  37. */
  38. public bool ProcessDebugHeader (ImageDebugHeader header)
  39. {
  40. if (!header.HasEntries)
  41. return false;
  42. var entry = header.GetCodeViewEntry ();
  43. if (entry == null)
  44. return false;
  45. var directory = entry.Directory;
  46. if (directory.Type != ImageDebugType.CodeView)
  47. return false;
  48. var data = entry.Data;
  49. if (data.Length < 24)
  50. return false;
  51. var magic = ReadInt32 (data, 0);
  52. if (magic != 0x53445352)
  53. return false;
  54. var guid_bytes = new byte [16];
  55. Buffer.BlockCopy (data, 4, guid_bytes, 0, 16);
  56. this.guid = new Guid (guid_bytes);
  57. this.age = ReadInt32 (data, 20);
  58. return PopulateFunctions ();
  59. }
  60. static int ReadInt32 (byte [] bytes, int start)
  61. {
  62. return (bytes [start]
  63. | (bytes [start + 1] << 8)
  64. | (bytes [start + 2] << 16)
  65. | (bytes [start + 3] << 24));
  66. }
  67. bool PopulateFunctions ()
  68. {
  69. using (pdb_file) {
  70. var info = PdbFile.LoadFunctions (pdb_file.value);
  71. if (this.guid != info.Guid)
  72. return false;
  73. foreach (PdbFunction function in info.Functions)
  74. functions.Add (function.token, function);
  75. }
  76. return true;
  77. }
  78. public MethodDebugInformation Read (MethodDefinition method)
  79. {
  80. var method_token = method.MetadataToken;
  81. PdbFunction function;
  82. if (!functions.TryGetValue (method_token.ToUInt32 (), out function))
  83. return null;
  84. var symbol = new MethodDebugInformation (method);
  85. ReadSequencePoints (function, symbol);
  86. symbol.scope = !function.scopes.IsNullOrEmpty ()
  87. ? ReadScopeAndLocals (function.scopes [0], symbol)
  88. : new ScopeDebugInformation { Start = new InstructionOffset (0), End = new InstructionOffset ((int) function.length) };
  89. if (function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod != method.MetadataToken.ToUInt32 () && function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod != 0)
  90. symbol.scope.import = GetImport (function.tokenOfMethodWhoseUsingInfoAppliesToThisMethod, method.Module);
  91. if (function.scopes.Length > 1) {
  92. for (int i = 1; i < function.scopes.Length; i++) {
  93. var s = ReadScopeAndLocals (function.scopes [i], symbol);
  94. if (!AddScope (symbol.scope.Scopes, s))
  95. symbol.scope.Scopes.Add (s);
  96. }
  97. }
  98. if (function.iteratorScopes != null) {
  99. var state_machine = new StateMachineScopeDebugInformation ();
  100. foreach (var iterator_scope in function.iteratorScopes) {
  101. state_machine.Scopes.Add (new StateMachineScope ((int) iterator_scope.Offset, (int) (iterator_scope.Offset + iterator_scope.Length + 1)));
  102. }
  103. symbol.CustomDebugInformations.Add (state_machine);
  104. }
  105. if (function.synchronizationInformation != null) {
  106. var async_debug_info = new AsyncMethodBodyDebugInformation ((int) function.synchronizationInformation.GeneratedCatchHandlerOffset);
  107. foreach (var synchronization_point in function.synchronizationInformation.synchronizationPoints) {
  108. async_debug_info.Yields.Add (new InstructionOffset ((int) synchronization_point.SynchronizeOffset));
  109. async_debug_info.Resumes.Add (new InstructionOffset ((int) synchronization_point.ContinuationOffset));
  110. async_debug_info.ResumeMethods.Add (method);
  111. }
  112. symbol.CustomDebugInformations.Add (async_debug_info);
  113. symbol.StateMachineKickOffMethod = (MethodDefinition) method.Module.LookupToken ((int) function.synchronizationInformation.kickoffMethodToken);
  114. }
  115. return symbol;
  116. }
  117. Collection<ScopeDebugInformation> ReadScopeAndLocals (PdbScope [] scopes, MethodDebugInformation info)
  118. {
  119. var symbols = new Collection<ScopeDebugInformation> (scopes.Length);
  120. foreach (PdbScope scope in scopes)
  121. if (scope != null)
  122. symbols.Add (ReadScopeAndLocals (scope, info));
  123. return symbols;
  124. }
  125. ScopeDebugInformation ReadScopeAndLocals (PdbScope scope, MethodDebugInformation info)
  126. {
  127. var parent = new ScopeDebugInformation ();
  128. parent.Start = new InstructionOffset ((int) scope.offset);
  129. parent.End = new InstructionOffset ((int) (scope.offset + scope.length));
  130. if (!scope.slots.IsNullOrEmpty ()) {
  131. parent.variables = new Collection<VariableDebugInformation> (scope.slots.Length);
  132. foreach (PdbSlot slot in scope.slots) {
  133. if ((slot.flags & 1) != 0) // parameter names
  134. continue;
  135. var index = (int) slot.slot;
  136. var variable = new VariableDebugInformation (index, slot.name);
  137. if ((slot.flags & 4) != 0)
  138. variable.IsDebuggerHidden = true;
  139. parent.variables.Add (variable);
  140. }
  141. }
  142. if (!scope.constants.IsNullOrEmpty ()) {
  143. parent.constants = new Collection<ConstantDebugInformation> (scope.constants.Length);
  144. foreach (var constant in scope.constants) {
  145. var type = info.Method.Module.Read (constant, (c, r) => r.ReadConstantSignature (new MetadataToken (c.token)));
  146. var value = constant.value;
  147. // Object "null" is encoded as integer
  148. if (type != null && !type.IsValueType && value is int && (int) value == 0)
  149. value = null;
  150. parent.constants.Add (new ConstantDebugInformation (constant.name, type, value));
  151. }
  152. }
  153. if (!scope.usedNamespaces.IsNullOrEmpty ()) {
  154. ImportDebugInformation import;
  155. if (imports.TryGetValue (scope, out import)) {
  156. parent.import = import;
  157. } else {
  158. import = GetImport (scope, info.Method.Module);
  159. imports.Add (scope, import);
  160. parent.import = import;
  161. }
  162. }
  163. parent.scopes = ReadScopeAndLocals (scope.scopes, info);
  164. return parent;
  165. }
  166. static bool AddScope (Collection<ScopeDebugInformation> scopes, ScopeDebugInformation scope)
  167. {
  168. foreach (var sub_scope in scopes) {
  169. if (sub_scope.HasScopes && AddScope (sub_scope.Scopes, scope))
  170. return true;
  171. if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) {
  172. sub_scope.Scopes.Add (scope);
  173. return true;
  174. }
  175. }
  176. return false;
  177. }
  178. ImportDebugInformation GetImport (uint token, ModuleDefinition module)
  179. {
  180. PdbFunction function;
  181. if (!functions.TryGetValue (token, out function))
  182. return null;
  183. if (function.scopes.Length != 1)
  184. return null;
  185. var scope = function.scopes [0];
  186. ImportDebugInformation import;
  187. if (imports.TryGetValue (scope, out import))
  188. return import;
  189. import = GetImport (scope, module);
  190. imports.Add (scope, import);
  191. return import;
  192. }
  193. static ImportDebugInformation GetImport (PdbScope scope, ModuleDefinition module)
  194. {
  195. if (scope.usedNamespaces.IsNullOrEmpty ())
  196. return null;
  197. var import = new ImportDebugInformation ();
  198. foreach (var used_namespace in scope.usedNamespaces) {
  199. if (string.IsNullOrEmpty (used_namespace))
  200. continue;
  201. ImportTarget target = null;
  202. var value = used_namespace.Substring (1);
  203. switch (used_namespace [0]) {
  204. case 'U':
  205. target = new ImportTarget (ImportTargetKind.ImportNamespace) { @namespace = value };
  206. break;
  207. case 'T': {
  208. var type = TypeParser.ParseType (module, value);
  209. if (type != null)
  210. target = new ImportTarget (ImportTargetKind.ImportType) { type = type };
  211. break;
  212. }
  213. case 'A':
  214. var index = used_namespace.IndexOf (' ');
  215. if (index < 0) {
  216. target = new ImportTarget (ImportTargetKind.ImportNamespace) { @namespace = used_namespace };
  217. break;
  218. }
  219. var alias_value = used_namespace.Substring (1, index - 1);
  220. var alias_target_value = used_namespace.Substring (index + 2);
  221. switch (used_namespace [index + 1]) {
  222. case 'U':
  223. target = new ImportTarget (ImportTargetKind.DefineNamespaceAlias) { alias = alias_value, @namespace = alias_target_value };
  224. break;
  225. case 'T':
  226. var type = TypeParser.ParseType (module, alias_target_value);
  227. if (type != null)
  228. target = new ImportTarget (ImportTargetKind.DefineTypeAlias) { alias = alias_value, type = type };
  229. break;
  230. }
  231. break;
  232. case '*':
  233. target = new ImportTarget (ImportTargetKind.ImportNamespace) { @namespace = value };
  234. break;
  235. case '@':
  236. if (!value.StartsWith ("P:"))
  237. continue;
  238. target = new ImportTarget (ImportTargetKind.ImportNamespace) { @namespace = value.Substring (2) };
  239. break;
  240. }
  241. if (target != null)
  242. import.Targets.Add (target);
  243. }
  244. return import;
  245. }
  246. void ReadSequencePoints (PdbFunction function, MethodDebugInformation info)
  247. {
  248. if (function.lines == null)
  249. return;
  250. info.sequence_points = new Collection<SequencePoint> ();
  251. foreach (PdbLines lines in function.lines)
  252. ReadLines (lines, info);
  253. }
  254. void ReadLines (PdbLines lines, MethodDebugInformation info)
  255. {
  256. var document = GetDocument (lines.file);
  257. foreach (var line in lines.lines)
  258. ReadLine (line, document, info);
  259. }
  260. static void ReadLine (PdbLine line, Document document, MethodDebugInformation info)
  261. {
  262. var sequence_point = new SequencePoint ((int) line.offset, document);
  263. sequence_point.StartLine = (int) line.lineBegin;
  264. sequence_point.StartColumn = (int) line.colBegin;
  265. sequence_point.EndLine = (int) line.lineEnd;
  266. sequence_point.EndColumn = (int) line.colEnd;
  267. info.sequence_points.Add (sequence_point);
  268. }
  269. Document GetDocument (PdbSource source)
  270. {
  271. string name = source.name;
  272. Document document;
  273. if (documents.TryGetValue (name, out document))
  274. return document;
  275. document = new Document (name) {
  276. LanguageGuid = source.language,
  277. LanguageVendorGuid = source.vendor,
  278. TypeGuid = source.doctype,
  279. HashAlgorithmGuid = source.checksumAlgorithm,
  280. Hash = source.checksum,
  281. };
  282. documents.Add (name, document);
  283. return document;
  284. }
  285. public void Dispose ()
  286. {
  287. pdb_file.Dispose ();
  288. }
  289. }
  290. }