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