0619a6f1cad0bec570a73e16882202ad168a862d
[mono.git] / mcs / tools / mono-symbolicate / symbolicate.cs
1 using System;
2 using System.IO;
3 using System.Text;
4 using System.Linq;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using Mono.Options;
8
9 namespace Mono
10 {
11         public class Symbolicate
12         {
13                 class Command {
14                         public readonly int MinArgCount;
15                         public readonly int MaxArgCount;
16                         public readonly Action<List<string>> Action;
17
18                         public Command (Action<List<string>> action, int minArgCount = 0, int maxArgCount = int.MaxValue)
19                         {
20                                 Action = action;
21                                 MinArgCount = minArgCount;
22                                 MaxArgCount = maxArgCount;
23                         }
24                 }
25
26                 static Logger logger;
27
28                 public static int Main (String[] args)
29                 {
30                         var showHelp = false;
31                         List<string> extra = null;
32
33                         Command cmd = null;
34
35                         if (args[0] == "store-symbols")
36                                 cmd = new Command (StoreSymbolsAction, 2);
37
38                         if (cmd != null) {
39                                 args = args.Skip (1).ToArray ();
40                         } else {
41                                 cmd = new Command (SymbolicateAction, 2, 2);
42                         }
43
44                         var logLevel = Logger.Level.Warning;
45
46                         var options = new OptionSet {
47                                 { "h|help", "Show this help", v => showHelp = true },
48                                 { "q", "Quiet, warnings are not displayed", v => logLevel = Logger.Level.Error },
49                                 { "v", "Verbose, log debug messages", v => logLevel = Logger.Level.Debug },
50                         };
51
52                         try {
53                                 extra = options.Parse (args);
54                         } catch (OptionException e) {
55                                 Console.WriteLine ("Option error: {0}", e.Message);
56                                 showHelp = true;
57                         }
58
59                         if (showHelp || extra == null || extra.Count < cmd.MinArgCount || extra.Count > cmd.MaxArgCount) {
60                                 Console.Error.WriteLine ("Usage: symbolicate <msym dir> <input file>");
61                                 Console.Error.WriteLine ("       symbolicate store-symbols <msym dir> [<dir>]+");
62                                 Console.WriteLine ();
63                                 Console.WriteLine ("Available options:");
64                                 options.WriteOptionDescriptions (Console.Out);
65                                 return 1;
66                         }
67
68                         logger = new Logger (logLevel, msg => Console.Error.WriteLine (msg));
69
70                         cmd.Action (extra);
71
72                         return 0;
73                 }
74
75                 private static void SymbolicateAction (List<string> args)
76                 {
77                         var msymDir = args [0];
78                         var inputFile = args [1];
79
80                         var symbolManager = new SymbolManager (msymDir, logger);
81
82                         using (StreamReader r = new StreamReader (inputFile)) {
83                                 var sb = Process (r, symbolManager);
84                                 Console.Write (sb.ToString ());
85                         }
86                 }
87
88                 private static void StoreSymbolsAction (List<string> args)
89                 {
90                         var msymDir = args[0];
91                         var lookupDirs = args.Skip (1).ToArray ();
92
93                         var symbolManager = new SymbolManager (msymDir, logger);
94
95                         symbolManager.StoreSymbols (lookupDirs);
96                 }
97
98                 public static StringBuilder Process (StreamReader reader, SymbolManager symbolManager)
99                 {
100                         List<StackFrameData> stackFrames = new List<StackFrameData>();
101                         List<StackTraceMetadata> metadata = new List<StackTraceMetadata>();
102                         StringBuilder sb = new StringBuilder ();
103                         bool linesEnded = false;
104
105                         for (var line = reader.ReadLine (); line != null; line = reader.ReadLine ()) {
106                                 StackFrameData sfData;
107                                 if (!linesEnded && StackFrameData.TryParse (line, out sfData)) {
108                                         stackFrames.Add (sfData);
109                                         continue;
110                                 }
111
112                                 if (stackFrames.Count > 0) {
113                                         linesEnded = true;
114
115                                         StackTraceMetadata stMetadata;
116                                         if (StackTraceMetadata.TryParse (line, out stMetadata)) {
117                                                 metadata.Add (stMetadata);
118                                                 continue;
119                                         }
120
121                                         DumpStackTrace (symbolManager, sb, stackFrames, metadata);
122                 
123                                         // Clear lists for next stack trace
124                                         stackFrames.Clear ();
125                                         metadata.Clear ();
126                                 }
127
128                                 linesEnded = false;
129
130                                 // Append last line
131                                 sb.AppendLine (line);
132                         }
133
134                         if (stackFrames.Count > 0)
135                                 DumpStackTrace (symbolManager, sb, stackFrames, metadata);
136
137                         return sb;
138                 }
139
140                 private static void DumpStackTrace (SymbolManager symbolManager, StringBuilder sb, List<StackFrameData> stackFrames, List<StackTraceMetadata> metadata)
141                 {
142                         string aotid = null;
143                         var aotidMetadata = metadata.FirstOrDefault ( m => m.Id == "AOTID" );
144                         if (aotidMetadata != null)
145                                 aotid = aotidMetadata.Value;
146
147                         var linesMvid = ProcessLinesMVID (metadata);
148                         var lineNumber = 0;
149                         foreach (var sfData in stackFrames) {
150                                 string mvid = null;
151                                 if (linesMvid.ContainsKey (lineNumber))
152                                         mvid = linesMvid [lineNumber++];
153
154                                 symbolManager.TryResolveLocation (sfData, mvid, aotid);
155
156                                 sb.AppendLine (sfData.ToString ());
157                         }
158
159                         foreach (var m in metadata)
160                                 sb.AppendLine (m.Line);
161                 }
162
163                 private static Dictionary<int, string> ProcessLinesMVID (List<StackTraceMetadata> metadata)
164                 {
165                         var linesMvid = new Dictionary<int, string> ();
166                         var mvidData = metadata.Where ( m => m.Id == "MVID" ).Select ( m => m.Value );
167                         foreach (var m in mvidData) {
168                                 var s1 = m.Split (new char[] {' '}, 2);
169                                 var mvid = s1 [0];
170                                 var lines = s1 [1].Split (',');
171                                 foreach (var line in lines)
172                                         linesMvid.Add (int.Parse (line), mvid);
173                         }
174
175                         return linesMvid;
176                 }
177         }
178 }