[mono-symbolicate] Fixes Directory Exists/Delete race condition.
[mono.git] / mcs / tools / mono-symbolicate / SymbolManager.cs
1 using System;
2 using System.IO;
3 using System.Linq;
4 using System.Text;
5 using System.Collections.Generic;
6 using Mono.Cecil;
7 using Mono.Cecil.Cil;
8 using Mono.Collections.Generic;
9
10 namespace Mono
11 {
12         public class SymbolManager
13         {
14                 string msymDir;
15                 Logger logger;
16
17                 public SymbolManager (string msymDir, Logger logger) {
18                         this.msymDir = msymDir;
19                         this.logger = logger;
20                 }
21
22                 internal bool TryResolveLocation (StackFrameData sfData, string mvid, string aotid)
23                 {
24                         if (mvid == null)
25                                 return false;
26
27                         var assemblyLocProvider = GetOrCreateAssemblyLocationProvider (mvid);
28                         if (assemblyLocProvider == null)
29                                 return false;
30
31                         SeqPointInfo seqPointInfo = null;
32                         if (!sfData.IsILOffset && aotid != null)
33                                 seqPointInfo = GetOrCreateSeqPointInfo (aotid);
34
35                         return assemblyLocProvider.TryResolveLocation (sfData, seqPointInfo);
36                 }
37
38                 Dictionary<string, AssemblyLocationProvider> assemblies = new Dictionary<string, AssemblyLocationProvider> ();
39
40                 private AssemblyLocationProvider GetOrCreateAssemblyLocationProvider (string mvid)
41                 {
42                         if (assemblies.ContainsKey (mvid))
43                                 return assemblies[mvid];
44
45                         var mvidDir = Path.Combine (msymDir, mvid);
46                         if (!Directory.Exists (mvidDir)) {
47                                 logger.LogWarning ("MVID directory does not exist: {0}", mvidDir);
48                                 return  null;
49                         }
50
51                         string assemblyPath = null;
52                         var exeFiles = Directory.GetFiles (mvidDir, "*.exe");
53                         var dllFiles = Directory.GetFiles (mvidDir, "*.dll");
54
55                         if (exeFiles.Length + dllFiles.Length != 1) {
56                                 logger.LogError ("MVID directory should include one assembly: {0}", mvidDir);
57                                 return null;
58                         }
59
60                         assemblyPath = (exeFiles.Length > 0)? exeFiles[0] : dllFiles[0];
61
62                         var locProvider = new AssemblyLocationProvider (assemblyPath, logger);
63
64                         assemblies.Add (mvid, locProvider);
65
66                         return locProvider;
67                 }
68
69                 Dictionary<string, SeqPointInfo> seqPointInfos = new Dictionary<string, SeqPointInfo> ();
70
71                 private SeqPointInfo GetOrCreateSeqPointInfo (string aotid)
72                 {
73                         if (seqPointInfos.ContainsKey (aotid))
74                                 return seqPointInfos[aotid];
75
76                         var aotidDir = Path.Combine (msymDir, aotid);
77                         if (!Directory.Exists (aotidDir)) {
78                                 logger.LogError ("AOTID directory does not exist: {0}", aotidDir);
79                                 return null;
80                         }
81
82                         string msymFile = null;
83                         var msymFiles = Directory.GetFiles(aotidDir, "*.msym");
84                         msymFile = msymFiles[0];
85
86                         var seqPointInfo = SeqPointInfo.Read (msymFile);
87
88                         seqPointInfos.Add (aotid, seqPointInfo);
89
90                         return seqPointInfo;
91                 }
92
93                 public void StoreSymbols (params string[] lookupDirs)
94                 {
95                         foreach (var dir in lookupDirs) {
96                                 var exeFiles = Directory.GetFiles (dir, "*.exe");
97                                 var dllFiles = Directory.GetFiles (dir, "*.dll");
98                                 var assemblies = exeFiles.Concat (dllFiles);
99                                 foreach (var assemblyPath in assemblies) {
100                                         var mdbPath = assemblyPath + ".mdb";
101                                         if (!File.Exists (mdbPath)) {
102                                                 logger.LogWarning ("Directory {0} contains {1} but no mdb {2}.", dir, Path.GetFileName (assemblyPath), Path.GetFileName (mdbPath));
103                                                 // assemblies without mdb files are useless
104                                                 continue;
105                                         }
106
107                                         var assembly = AssemblyDefinition.ReadAssembly (assemblyPath);
108
109                                         var mvid = assembly.MainModule.Mvid.ToString ("N");
110                                         var mvidDir = Path.Combine (msymDir, mvid);
111
112                                         if (Directory.Exists (mvidDir)) {
113                                                 try {
114                                                         Directory.Delete (mvidDir, true);
115                                                 } catch (DirectoryNotFoundException e) {}
116                                         }
117
118                                         Directory.CreateDirectory (mvidDir);
119
120                                         var mvidAssemblyPath = Path.Combine (mvidDir, Path.GetFileName (assemblyPath));
121                                         File.Copy (assemblyPath, mvidAssemblyPath);
122
123                                         var mvidMdbPath = Path.Combine (mvidDir, Path.GetFileName (mdbPath));
124                                         File.Copy (mdbPath, mvidMdbPath);
125
126                                         // TODO create MVID dir for non main modules with links to main module MVID
127                                 }
128                         }
129                 }
130         }
131 }