using System; using System.IO; using System.Text; using System.Linq; using System.Collections.Generic; using System.Globalization; using Mono.Options; namespace Mono { public class Symbolicate { class Command { public readonly int MinArgCount; public readonly int MaxArgCount; public readonly Action> Action; public Command (Action> action, int minArgCount = 0, int maxArgCount = int.MaxValue) { Action = action; MinArgCount = minArgCount; MaxArgCount = maxArgCount; } } public static int Main (String[] args) { var showHelp = false; List extra = null; Command cmd = null; if (args[0] == "store-symbols") cmd = new Command (StoreSymbolsAction, 2); if (cmd != null) { args = args.Skip (1).ToArray (); } else { cmd = new Command (SymbolicateAction, 2, 2); } var options = new OptionSet { { "h|help", "Show this help", v => showHelp = true }, }; try { extra = options.Parse (args); } catch (OptionException e) { Console.WriteLine ("Option error: {0}", e.Message); showHelp = true; } if (showHelp || extra == null || extra.Count < cmd.MinArgCount || extra.Count > cmd.MaxArgCount) { Console.Error.WriteLine ("Usage: symbolicate "); Console.Error.WriteLine (" symbolicate store-symbols []+"); Console.WriteLine (); Console.WriteLine ("Available options:"); options.WriteOptionDescriptions (Console.Out); return 1; } cmd.Action (extra); return 0; } private static void SymbolicateAction (List args) { var msymDir = args [0]; var inputFile = args [1]; var symbolManager = new SymbolManager (msymDir); using (StreamReader r = new StreamReader (inputFile)) { var sb = Process (r, symbolManager); Console.Write (sb.ToString ()); } } private static void StoreSymbolsAction (List args) { var msymDir = args[0]; var lookupDirs = args.Skip (1).ToArray (); var symbolManager = new SymbolManager (msymDir); symbolManager.StoreSymbols (lookupDirs); } public static StringBuilder Process (StreamReader reader, SymbolManager symbolManager) { List stackFrames = new List(); List metadata = new List(); StringBuilder sb = new StringBuilder (); bool linesEnded = false; for (var line = reader.ReadLine (); line != null; line = reader.ReadLine ()) { StackFrameData sfData; if (!linesEnded && StackFrameData.TryParse (line, out sfData)) { stackFrames.Add (sfData); continue; } if (stackFrames.Count > 0) { linesEnded = true; StackTraceMetadata stMetadata; if (StackTraceMetadata.TryParse (line, out stMetadata)) { metadata.Add (stMetadata); continue; } DumpStackTrace (symbolManager, sb, stackFrames, metadata); // Clear lists for next stack trace stackFrames.Clear (); metadata.Clear (); } linesEnded = false; // Append last line sb.AppendLine (line); } if (stackFrames.Count > 0) DumpStackTrace (symbolManager, sb, stackFrames, metadata); return sb; } private static void DumpStackTrace (SymbolManager symbolManager, StringBuilder sb, List stackFrames, List metadata) { string aotid = null; var aotidMetadata = metadata.FirstOrDefault ( m => m.Id == "AOTID" ); if (aotidMetadata != null) aotid = aotidMetadata.Value; var linesMvid = ProcessLinesMVID (metadata); var lineNumber = 0; foreach (var sfData in stackFrames) { string mvid = null; if (linesMvid.ContainsKey (lineNumber)) mvid = linesMvid [lineNumber++]; symbolManager.TryResolveLocation (sfData, mvid, aotid); sb.AppendLine (sfData.ToString ()); } foreach (var m in metadata) sb.AppendLine (m.Line); } private static Dictionary ProcessLinesMVID (List metadata) { var linesMvid = new Dictionary (); var mvidData = metadata.Where ( m => m.Id == "MVID" ).Select ( m => m.Value ); foreach (var m in mvidData) { var s1 = m.Split (new char[] {' '}, 2); var mvid = s1 [0]; var lines = s1 [1].Split (','); foreach (var line in lines) linesMvid.Add (int.Parse (line), mvid); } return linesMvid; } } }