[pdb2mdb] Detect portable PDB and fail gracefully instead of crashing (#4261)
[mono-project.git] / mcs / tools / pdb2mdb / Driver.cs
blob5aa1fcf0b5b06bd3e9be20fb30f9da4514a6f9cb
1 //
2 // Driver.cs
3 //
4 // Author:
5 // Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2009 Novell, Inc. (http://www.novell.com)
8 //
10 using System;
11 using System.Collections.Generic;
12 using System.IO;
13 using System.Linq;
15 using Microsoft.Cci;
16 using Microsoft.Cci.Pdb;
18 using Mono.Cecil;
20 using Mono.CompilerServices.SymbolWriter;
22 namespace Pdb2Mdb {
24 public class Converter {
26 MonoSymbolWriter mdb;
27 Dictionary<string, SourceFile> files = new Dictionary<string, SourceFile> ();
29 public static void Convert (string filename)
31 var asm = AssemblyDefinition.ReadAssembly (filename);
33 var pdb = asm.Name.Name + ".pdb";
34 pdb = Path.Combine (Path.GetDirectoryName (filename), pdb);
36 if (!File.Exists (pdb))
37 throw new FileNotFoundException ("PDB file doesn't exist: " + pdb);
39 using (var stream = File.OpenRead (pdb)) {
40 if (IsPortablePdb (stream))
41 throw new PortablePdbNotSupportedException ();
43 var funcs = PdbFile.LoadFunctions (stream, true);
44 Converter.Convert (asm, funcs, new MonoSymbolWriter (filename));
48 static bool IsPortablePdb (FileStream stream)
50 const uint ppdb_signature = 0x424a5342;
52 var position = stream.Position;
53 try {
54 var reader = new BinaryReader (stream);
55 return reader.ReadUInt32 () == ppdb_signature;
56 } finally {
57 stream.Position = position;
61 internal Converter (MonoSymbolWriter mdb)
63 this.mdb = mdb;
66 internal static void Convert (AssemblyDefinition assembly, IEnumerable<PdbFunction> functions, MonoSymbolWriter mdb)
68 var converter = new Converter (mdb);
70 foreach (var function in functions)
71 converter.ConvertFunction (function);
73 mdb.WriteSymbolFile (assembly.MainModule.Mvid);
76 void ConvertFunction (PdbFunction function)
78 if (function.lines == null)
79 return;
81 var method = new SourceMethod { Name = function.name, Token = (int) function.token };
83 var file = GetSourceFile (mdb, function);
85 var builder = mdb.OpenMethod (file.CompilationUnit, 0, method);
87 ConvertSequencePoints (function, file, builder);
89 ConvertVariables (function);
91 mdb.CloseMethod ();
94 void ConvertSequencePoints (PdbFunction function, SourceFile file, SourceMethodBuilder builder)
96 int last_line = 0;
97 foreach (var line in function.lines.SelectMany (lines => lines.lines)) {
98 // 0xfeefee is an MS convention, we can't pass it into mdb files, so we use the last non-hidden line
99 bool is_hidden = line.lineBegin == 0xfeefee;
100 builder.MarkSequencePoint (
101 (int) line.offset,
102 file.CompilationUnit.SourceFile,
103 is_hidden ? last_line : (int) line.lineBegin,
104 (int) line.colBegin, is_hidden ? -1 : (int)line.lineEnd, is_hidden ? -1 : (int)line.colEnd,
105 is_hidden);
106 if (!is_hidden)
107 last_line = (int) line.lineBegin;
111 void ConvertVariables (PdbFunction function)
113 foreach (var scope in function.scopes)
114 ConvertScope (scope);
117 void ConvertScope (PdbScope scope)
119 ConvertSlots (scope, scope.slots);
121 foreach (var s in scope.scopes)
122 ConvertScope (s);
125 void ConvertSlots (PdbScope scope, IEnumerable<PdbSlot> slots)
127 int scope_idx = mdb.OpenScope ((int)scope.address);
128 foreach (var slot in slots) {
129 mdb.DefineLocalVariable ((int) slot.slot, slot.name);
130 mdb.DefineScopeVariable (scope_idx, (int)slot.slot);
132 mdb.CloseScope ((int)(scope.address + scope.length));
135 SourceFile GetSourceFile (MonoSymbolWriter mdb, PdbFunction function)
137 var name = (from l in function.lines where l.file != null select l.file.name).First ();
139 SourceFile file;
140 if (files.TryGetValue (name, out file))
141 return file;
143 var entry = mdb.DefineDocument (name);
144 var unit = mdb.DefineCompilationUnit (entry);
146 file = new SourceFile (unit, entry);
147 files.Add (name, file);
148 return file;
151 class SourceFile : ISourceFile {
152 CompileUnitEntry comp_unit;
153 SourceFileEntry entry;
155 public SourceFileEntry Entry
157 get { return entry; }
160 public CompileUnitEntry CompilationUnit
162 get { return comp_unit; }
165 public SourceFile (CompileUnitEntry comp_unit, SourceFileEntry entry)
167 this.comp_unit = comp_unit;
168 this.entry = entry;
172 class SourceMethod : IMethodDef {
174 public string Name { get; set; }
176 public int Token { get; set; }
180 public class PortablePdbNotSupportedException : Exception {
183 class Driver {
185 static void Main (string [] args)
187 if (args.Length != 1)
188 Usage ();
190 var asm = args [0];
192 if (!File.Exists (asm))
193 Usage ();
195 try {
196 Converter.Convert (asm);
197 } catch (FileNotFoundException ex) {
198 Usage ();
199 } catch (PortablePdbNotSupportedException) {
200 Console.WriteLine ("Error: A portable PDB can't be converted to mdb.");
201 Environment.Exit (2);
203 catch (Exception ex) {
204 Error (ex);
208 static void Usage ()
210 Console.WriteLine ("Mono pdb to mdb debug symbol store converter");
211 Console.WriteLine ("Usage: pdb2mdb assembly");
213 Environment.Exit (1);
216 static void Error (Exception e)
218 Console.WriteLine ("Fatal error:");
219 Console.WriteLine (e);
221 Environment.Exit (1);