Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[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                         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                 static bool IsPortablePdb (FileStream stream)
49                 {
50                         const uint ppdb_signature = 0x424a5342;
51
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;
58                         }
59                 }
60
61                 internal Converter (MonoSymbolWriter mdb)
62                 {
63                         this.mdb = mdb;
64                 }
65
66                 internal static void Convert (AssemblyDefinition assembly, IEnumerable<PdbFunction> functions, MonoSymbolWriter mdb)
67                 {
68                         var converter = new Converter (mdb);
69
70                         foreach (var function in functions)
71                                 converter.ConvertFunction (function);
72
73                         mdb.WriteSymbolFile (assembly.MainModule.Mvid);
74                 }
75
76                 void ConvertFunction (PdbFunction function)
77                 {
78                         if (function.lines == null)
79                                 return;
80
81                         var method = new SourceMethod { Name = function.name, Token = (int) function.token };
82
83                         var file = GetSourceFile (mdb, function);
84
85                         var builder = mdb.OpenMethod (file.CompilationUnit, 0, method);
86
87                         ConvertSequencePoints (function, file, builder);
88
89                         ConvertVariables (function);
90
91                         mdb.CloseMethod ();
92                 }
93
94                 void ConvertSequencePoints (PdbFunction function, SourceFile file, SourceMethodBuilder builder)
95                 {
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;
108                         }
109                 }
110
111                 void ConvertVariables (PdbFunction function)
112                 {
113                         foreach (var scope in function.scopes)
114                                 ConvertScope (scope);
115                 }
116
117                 void ConvertScope (PdbScope scope)
118                 {
119                         ConvertSlots (scope, scope.slots);
120
121                         foreach (var s in scope.scopes)
122                                 ConvertScope (s);
123                 }
124
125                 void ConvertSlots (PdbScope scope, IEnumerable<PdbSlot> slots)
126                 {
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);
131                         }
132                         mdb.CloseScope ((int)(scope.address + scope.length));
133                 }
134
135                 SourceFile GetSourceFile (MonoSymbolWriter mdb, PdbFunction function)
136                 {
137                         var name = (from l in function.lines where l.file != null select l.file.name).First ();
138
139                         SourceFile file;
140                         if (files.TryGetValue (name, out file))
141                                 return file;
142
143                         var entry = mdb.DefineDocument (name);
144                         var unit = mdb.DefineCompilationUnit (entry);
145
146                         file = new SourceFile (unit, entry);
147                         files.Add (name, file);
148                         return file;
149                 }
150
151                 class SourceFile : ISourceFile {
152                         CompileUnitEntry comp_unit;
153                         SourceFileEntry entry;
154
155                         public SourceFileEntry Entry
156                         {
157                                 get { return entry; }
158                         }
159
160                         public CompileUnitEntry CompilationUnit
161                         {
162                                 get { return comp_unit; }
163                         }
164
165                         public SourceFile (CompileUnitEntry comp_unit, SourceFileEntry entry)
166                         {
167                                 this.comp_unit = comp_unit;
168                                 this.entry = entry;
169                         }
170                 }
171
172                 class SourceMethod : IMethodDef {
173
174                         public string Name { get; set; }
175
176                         public int Token { get; set; }
177                 }
178         }
179
180         public class PortablePdbNotSupportedException : Exception {
181         }
182
183         class Driver {
184
185                 static void Main (string [] args)
186                 {
187                         if (args.Length != 1)
188                                 Usage ();
189
190                         var asm = args [0];
191
192                         if (!File.Exists (asm))
193                                 Usage ();
194
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);
202                         }
203                         catch (Exception ex) {
204                                 Error (ex);
205                         }
206                 }
207
208                 static void Usage ()
209                 {
210                         Console.WriteLine ("Mono pdb to mdb debug symbol store converter");
211                         Console.WriteLine ("Usage: pdb2mdb assembly");
212
213                         Environment.Exit (1);
214                 }
215
216                 static void Error (Exception e)
217                 {
218                         Console.WriteLine ("Fatal error:");
219                         Console.WriteLine (e);
220
221                         Environment.Exit (1);
222                 }
223         }
224 }