Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / tools / pdb2mdb / Driver.cs
1 //
2 // Driver.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2009 Novell, Inc. (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections.Generic;
12 using System.IO;
13 using System.Linq;
14
15 using Microsoft.Cci;
16 using Microsoft.Cci.Pdb;
17
18 using Mono.Cecil;
19
20 using Mono.CompilerServices.SymbolWriter;
21
22 namespace Pdb2Mdb {
23
24         public class Converter {
25
26                 MonoSymbolWriter mdb;
27                 Dictionary<string, SourceFile> files = new Dictionary<string, SourceFile> ();
28
29                 public static void Convert (string filename)
30                 {
31                         using (var asm = AssemblyDefinition.ReadAssembly (filename)) {
32
33                                 var pdb = asm.Name.Name + ".pdb";
34                                 pdb = Path.Combine (Path.GetDirectoryName (filename), pdb);
35
36                                 if (!File.Exists (pdb))
37                                         throw new FileNotFoundException ("PDB file doesn't exist: " + pdb);
38
39                                 using (var stream = File.OpenRead (pdb)) {
40                                         if (IsPortablePdb (stream))
41                                                 throw new PortablePdbNotSupportedException ();
42
43                                         var funcs = PdbFile.LoadFunctions (stream, true);
44                                         Converter.Convert (asm, funcs, new MonoSymbolWriter (filename));
45                                 }
46                         }
47                 }
48
49                 static bool IsPortablePdb (FileStream stream)
50                 {
51                         const uint ppdb_signature = 0x424a5342;
52
53                         var position = stream.Position;
54                         try {
55                                 var reader = new BinaryReader (stream);
56                                 return reader.ReadUInt32 () == ppdb_signature;
57                         } finally {
58                                 stream.Position = position;
59                         }
60                 }
61
62                 internal Converter (MonoSymbolWriter mdb)
63                 {
64                         this.mdb = mdb;
65                 }
66
67                 internal static void Convert (AssemblyDefinition assembly, IEnumerable<PdbFunction> functions, MonoSymbolWriter mdb)
68                 {
69                         var converter = new Converter (mdb);
70
71                         foreach (var function in functions)
72                                 converter.ConvertFunction (function);
73
74                         mdb.WriteSymbolFile (assembly.MainModule.Mvid);
75                 }
76
77                 void ConvertFunction (PdbFunction function)
78                 {
79                         if (function.lines == null)
80                                 return;
81
82                         var method = new SourceMethod { Name = function.name, Token = (int) function.token };
83
84                         var file = GetSourceFile (mdb, function);
85
86                         var builder = mdb.OpenMethod (file.CompilationUnit, 0, method);
87
88                         ConvertSequencePoints (function, file, builder);
89
90                         ConvertVariables (function);
91
92                         mdb.CloseMethod ();
93                 }
94
95                 void ConvertSequencePoints (PdbFunction function, SourceFile file, SourceMethodBuilder builder)
96                 {
97                         int last_line = 0;
98                         foreach (var line in function.lines.SelectMany (lines => lines.lines)) {
99                                 // 0xfeefee is an MS convention, we can't pass it into mdb files, so we use the last non-hidden line
100                                 bool is_hidden = line.lineBegin == 0xfeefee;
101                                 builder.MarkSequencePoint (
102                                         (int) line.offset,
103                                         file.CompilationUnit.SourceFile,
104                                         is_hidden ? last_line : (int) line.lineBegin,
105                                         (int) line.colBegin, is_hidden ? -1 : (int)line.lineEnd, is_hidden ? -1 : (int)line.colEnd,
106                                         is_hidden);
107                                 if (!is_hidden)
108                                         last_line = (int) line.lineBegin;
109                         }
110                 }
111
112                 void ConvertVariables (PdbFunction function)
113                 {
114                         foreach (var scope in function.scopes)
115                                 ConvertScope (scope);
116                 }
117
118                 void ConvertScope (PdbScope scope)
119                 {
120                         ConvertSlots (scope, scope.slots);
121
122                         foreach (var s in scope.scopes)
123                                 ConvertScope (s);
124                 }
125
126                 void ConvertSlots (PdbScope scope, IEnumerable<PdbSlot> slots)
127                 {
128                         int scope_idx = mdb.OpenScope ((int)scope.address);
129                         foreach (var slot in slots) {
130                                 mdb.DefineLocalVariable ((int) slot.slot, slot.name);
131                                 mdb.DefineScopeVariable (scope_idx, (int)slot.slot);
132                         }
133                         mdb.CloseScope ((int)(scope.address + scope.length));
134                 }
135
136                 SourceFile GetSourceFile (MonoSymbolWriter mdb, PdbFunction function)
137                 {
138                         var name = (from l in function.lines where l.file != null select l.file.name).First ();
139
140                         SourceFile file;
141                         if (files.TryGetValue (name, out file))
142                                 return file;
143
144                         var entry = mdb.DefineDocument (name);
145                         var unit = mdb.DefineCompilationUnit (entry);
146
147                         file = new SourceFile (unit, entry);
148                         files.Add (name, file);
149                         return file;
150                 }
151
152                 class SourceFile : ISourceFile {
153                         CompileUnitEntry comp_unit;
154                         SourceFileEntry entry;
155
156                         public SourceFileEntry Entry
157                         {
158                                 get { return entry; }
159                         }
160
161                         public CompileUnitEntry CompilationUnit
162                         {
163                                 get { return comp_unit; }
164                         }
165
166                         public SourceFile (CompileUnitEntry comp_unit, SourceFileEntry entry)
167                         {
168                                 this.comp_unit = comp_unit;
169                                 this.entry = entry;
170                         }
171                 }
172
173                 class SourceMethod : IMethodDef {
174
175                         public string Name { get; set; }
176
177                         public int Token { get; set; }
178                 }
179         }
180
181         public class PortablePdbNotSupportedException : Exception {
182         }
183
184         class Driver {
185
186                 static void Main (string [] args)
187                 {
188                         if (args.Length != 1)
189                                 Usage ();
190
191                         var asm = args [0];
192
193                         if (!File.Exists (asm))
194                                 Usage ();
195
196                         try {
197                                 Converter.Convert (asm);
198                         } catch (FileNotFoundException ex) {
199                                 Usage ();
200                         } catch (PortablePdbNotSupportedException) {
201                                 Console.WriteLine ("Error: A portable PDB can't be converted to mdb.");
202                                 Environment.Exit (2);
203                         }
204                         catch (Exception ex) {
205                                 Error (ex);
206                         }
207                 }
208
209                 static void Usage ()
210                 {
211                         Console.WriteLine ("Mono pdb to mdb debug symbol store converter");
212                         Console.WriteLine ("Usage: pdb2mdb assembly");
213
214                         Environment.Exit (1);
215                 }
216
217                 static void Error (Exception e)
218                 {
219                         Console.WriteLine ("Fatal error:");
220                         Console.WriteLine (e);
221
222                         Environment.Exit (1);
223                 }
224         }
225 }