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