NativePdbWriter.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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 System.Linq;
  14. using System.Text;
  15. using ILRuntime.Mono.Cecil.Cil;
  16. using ILRuntime.Mono.Cecil.PE;
  17. using ILRuntime.Mono.Collections.Generic;
  18. namespace ILRuntime.Mono.Cecil.Pdb {
  19. public class NativePdbWriter : ISymbolWriter {
  20. readonly ModuleDefinition module;
  21. readonly MetadataBuilder metadata;
  22. readonly SymWriter writer;
  23. readonly Dictionary<string, SymDocumentWriter> documents;
  24. readonly Dictionary<ImportDebugInformation, MetadataToken> import_info_to_parent;
  25. internal NativePdbWriter (ModuleDefinition module, SymWriter writer)
  26. {
  27. this.module = module;
  28. this.metadata = module.metadata_builder;
  29. this.writer = writer;
  30. this.documents = new Dictionary<string, SymDocumentWriter> ();
  31. this.import_info_to_parent = new Dictionary<ImportDebugInformation, MetadataToken> ();
  32. }
  33. public ISymbolReaderProvider GetReaderProvider ()
  34. {
  35. return new NativePdbReaderProvider ();
  36. }
  37. public ImageDebugHeader GetDebugHeader ()
  38. {
  39. ImageDebugDirectory directory;
  40. var data = writer.GetDebugInfo (out directory);
  41. directory.TimeDateStamp = (int) module.timestamp;
  42. return new ImageDebugHeader (new ImageDebugHeaderEntry (directory, data));
  43. }
  44. public void Write (MethodDebugInformation info)
  45. {
  46. var method_token = info.method.MetadataToken;
  47. var sym_token = method_token.ToInt32 ();
  48. if (!info.HasSequencePoints && info.scope == null && !info.HasCustomDebugInformations && info.StateMachineKickOffMethod == null)
  49. return;
  50. writer.OpenMethod (sym_token);
  51. if (!info.sequence_points.IsNullOrEmpty ())
  52. DefineSequencePoints (info.sequence_points);
  53. var import_parent = new MetadataToken ();
  54. if (info.scope != null)
  55. DefineScope (info.scope, info, out import_parent);
  56. DefineCustomMetadata (info, import_parent);
  57. writer.CloseMethod ();
  58. }
  59. void DefineCustomMetadata (MethodDebugInformation info, MetadataToken import_parent)
  60. {
  61. var metadata = new CustomMetadataWriter (this.writer);
  62. if (import_parent.RID != 0) {
  63. metadata.WriteForwardInfo (import_parent);
  64. } else if (info.scope != null && info.scope.Import != null && info.scope.Import.HasTargets) {
  65. metadata.WriteUsingInfo (info.scope.Import);
  66. }
  67. if (info.Method.HasCustomAttributes) {
  68. foreach (var attribute in info.Method.CustomAttributes) {
  69. const string compiler_services = "System.Runtime.CompilerServices";
  70. var attribute_type = attribute.AttributeType;
  71. if (!attribute_type.IsTypeOf (compiler_services, "IteratorStateMachineAttribute") && !attribute_type.IsTypeOf (compiler_services, "AsyncStateMachineAttribute"))
  72. continue;
  73. var type = attribute.ConstructorArguments [0].Value as TypeReference;
  74. if (type == null)
  75. continue;
  76. metadata.WriteForwardIterator (type);
  77. }
  78. }
  79. if (info.HasCustomDebugInformations) {
  80. var state_machine = info.CustomDebugInformations.FirstOrDefault (cdi => cdi.Kind == CustomDebugInformationKind.StateMachineScope) as StateMachineScopeDebugInformation;
  81. if (state_machine != null)
  82. metadata.WriteIteratorScopes (state_machine, info);
  83. }
  84. metadata.WriteCustomMetadata ();
  85. DefineAsyncCustomMetadata (info);
  86. }
  87. void DefineAsyncCustomMetadata (MethodDebugInformation info)
  88. {
  89. if (!info.HasCustomDebugInformations)
  90. return;
  91. foreach (var custom_info in info.CustomDebugInformations) {
  92. var async_debug_info = custom_info as AsyncMethodBodyDebugInformation;
  93. if (async_debug_info == null)
  94. continue;
  95. using (var stream = new MemoryStream ()) {
  96. var async_metadata = new BinaryStreamWriter (stream);
  97. async_metadata.WriteUInt32 (info.StateMachineKickOffMethod != null ? info.StateMachineKickOffMethod.MetadataToken.ToUInt32 () : 0);
  98. async_metadata.WriteUInt32 ((uint) async_debug_info.CatchHandler.Offset);
  99. async_metadata.WriteUInt32 ((uint) async_debug_info.Resumes.Count);
  100. for (int i = 0; i < async_debug_info.Resumes.Count; ++i) {
  101. async_metadata.WriteUInt32 ((uint) async_debug_info.Yields [i].Offset);
  102. async_metadata.WriteUInt32 (async_debug_info.resume_methods [i].MetadataToken.ToUInt32 ());
  103. async_metadata.WriteUInt32 ((uint) async_debug_info.Resumes [i].Offset);
  104. }
  105. writer.DefineCustomMetadata ("asyncMethodInfo", stream.ToArray ());
  106. }
  107. }
  108. }
  109. void DefineScope (ScopeDebugInformation scope, MethodDebugInformation info, out MetadataToken import_parent)
  110. {
  111. var start_offset = scope.Start.Offset;
  112. var end_offset = scope.End.IsEndOfMethod
  113. ? info.code_size
  114. : scope.End.Offset;
  115. import_parent = new MetadataToken (0u);
  116. writer.OpenScope (start_offset);
  117. if (scope.Import != null && scope.Import.HasTargets && !import_info_to_parent.TryGetValue (info.scope.Import, out import_parent)) {
  118. foreach (var target in scope.Import.Targets) {
  119. switch (target.Kind) {
  120. case ImportTargetKind.ImportNamespace:
  121. writer.UsingNamespace ("U" + target.@namespace);
  122. break;
  123. case ImportTargetKind.ImportType:
  124. writer.UsingNamespace ("T" + TypeParser.ToParseable (target.type));
  125. break;
  126. case ImportTargetKind.DefineNamespaceAlias:
  127. writer.UsingNamespace ("A" + target.Alias + " U" + target.@namespace);
  128. break;
  129. case ImportTargetKind.DefineTypeAlias:
  130. writer.UsingNamespace ("A" + target.Alias + " T" + TypeParser.ToParseable (target.type));
  131. break;
  132. }
  133. }
  134. import_info_to_parent.Add (info.scope.Import, info.method.MetadataToken);
  135. }
  136. var sym_token = info.local_var_token.ToInt32 ();
  137. if (!scope.variables.IsNullOrEmpty ()) {
  138. for (int i = 0; i < scope.variables.Count; i++) {
  139. var variable = scope.variables [i];
  140. DefineLocalVariable (variable, sym_token, start_offset, end_offset);
  141. }
  142. }
  143. if (!scope.constants.IsNullOrEmpty ()) {
  144. for (int i = 0; i < scope.constants.Count; i++) {
  145. var constant = scope.constants [i];
  146. DefineConstant (constant);
  147. }
  148. }
  149. if (!scope.scopes.IsNullOrEmpty ()) {
  150. for (int i = 0; i < scope.scopes.Count; i++) {
  151. MetadataToken _;
  152. DefineScope (scope.scopes [i], info, out _);
  153. }
  154. }
  155. writer.CloseScope (end_offset);
  156. }
  157. void DefineSequencePoints (Collection<SequencePoint> sequence_points)
  158. {
  159. for (int i = 0; i < sequence_points.Count; i++) {
  160. var sequence_point = sequence_points [i];
  161. writer.DefineSequencePoints (
  162. GetDocument (sequence_point.Document),
  163. new [] { sequence_point.Offset },
  164. new [] { sequence_point.StartLine },
  165. new [] { sequence_point.StartColumn },
  166. new [] { sequence_point.EndLine },
  167. new [] { sequence_point.EndColumn });
  168. }
  169. }
  170. void DefineLocalVariable (VariableDebugInformation variable, int local_var_token, int start_offset, int end_offset)
  171. {
  172. writer.DefineLocalVariable2 (
  173. variable.Name,
  174. variable.Attributes,
  175. local_var_token,
  176. variable.Index,
  177. 0,
  178. 0,
  179. start_offset,
  180. end_offset);
  181. }
  182. void DefineConstant (ConstantDebugInformation constant)
  183. {
  184. var row = metadata.AddStandAloneSignature (metadata.GetConstantTypeBlobIndex (constant.ConstantType));
  185. var token = new MetadataToken (TokenType.Signature, row);
  186. writer.DefineConstant2 (constant.Name, constant.Value, token.ToInt32 ());
  187. }
  188. SymDocumentWriter GetDocument (Document document)
  189. {
  190. if (document == null)
  191. return null;
  192. SymDocumentWriter doc_writer;
  193. if (documents.TryGetValue (document.Url, out doc_writer))
  194. return doc_writer;
  195. doc_writer = writer.DefineDocument (
  196. document.Url,
  197. document.LanguageGuid,
  198. document.LanguageVendorGuid,
  199. document.TypeGuid);
  200. if (!document.Hash.IsNullOrEmpty ())
  201. doc_writer.SetCheckSum (document.HashAlgorithmGuid, document.Hash);
  202. documents [document.Url] = doc_writer;
  203. return doc_writer;
  204. }
  205. public void Dispose ()
  206. {
  207. var entry_point = module.EntryPoint;
  208. if (entry_point != null)
  209. writer.SetUserEntryPoint (entry_point.MetadataToken.ToInt32 ());
  210. writer.Close ();
  211. }
  212. }
  213. enum CustomMetadataType : byte {
  214. UsingInfo = 0,
  215. ForwardInfo = 1,
  216. IteratorScopes = 3,
  217. ForwardIterator = 4,
  218. }
  219. class CustomMetadataWriter : IDisposable {
  220. readonly SymWriter sym_writer;
  221. readonly MemoryStream stream;
  222. readonly BinaryStreamWriter writer;
  223. int count;
  224. const byte version = 4;
  225. public CustomMetadataWriter (SymWriter sym_writer)
  226. {
  227. this.sym_writer = sym_writer;
  228. this.stream = new MemoryStream ();
  229. this.writer = new BinaryStreamWriter (stream);
  230. writer.WriteByte (version);
  231. writer.WriteByte (0); // count
  232. writer.Align (4);
  233. }
  234. public void WriteUsingInfo (ImportDebugInformation import_info)
  235. {
  236. Write (CustomMetadataType.UsingInfo, () => {
  237. writer.WriteUInt16 ((ushort) 1);
  238. writer.WriteUInt16 ((ushort) import_info.Targets.Count);
  239. });
  240. }
  241. public void WriteForwardInfo (MetadataToken import_parent)
  242. {
  243. Write (CustomMetadataType.ForwardInfo, () => writer.WriteUInt32 (import_parent.ToUInt32 ()));
  244. }
  245. public void WriteIteratorScopes (StateMachineScopeDebugInformation state_machine, MethodDebugInformation debug_info)
  246. {
  247. Write (CustomMetadataType.IteratorScopes, () => {
  248. var scopes = state_machine.Scopes;
  249. writer.WriteInt32 (scopes.Count);
  250. foreach (var scope in scopes) {
  251. var start = scope.Start.Offset;
  252. var end = scope.End.IsEndOfMethod ? debug_info.code_size : scope.End.Offset;
  253. writer.WriteInt32 (start);
  254. writer.WriteInt32 (end - 1);
  255. }
  256. });
  257. }
  258. public void WriteForwardIterator (TypeReference type)
  259. {
  260. Write (CustomMetadataType.ForwardIterator, () => writer.WriteBytes(Encoding.Unicode.GetBytes(type.Name)));
  261. }
  262. void Write (CustomMetadataType type, Action write)
  263. {
  264. count++;
  265. writer.WriteByte (version);
  266. writer.WriteByte ((byte) type);
  267. writer.Align (4);
  268. var length_position = writer.Position;
  269. writer.WriteUInt32 (0);
  270. write ();
  271. writer.Align (4);
  272. var end = writer.Position;
  273. var length = end - length_position + 4; // header is 4 bytes long
  274. writer.Position = length_position;
  275. writer.WriteInt32 (length);
  276. writer.Position = end;
  277. }
  278. public void WriteCustomMetadata ()
  279. {
  280. if (count == 0)
  281. return;
  282. writer.BaseStream.Position = 1;
  283. writer.WriteByte ((byte) count);
  284. writer.Flush ();
  285. sym_writer.DefineCustomMetadata ("MD2", stream.ToArray ());
  286. }
  287. public void Dispose ()
  288. {
  289. stream.Dispose ();
  290. }
  291. }
  292. }