using System;
using System.IO;
+using System.Text;
+using System.Linq;
+using System.Collections.Generic;
using System.Globalization;
-using System.Text.RegularExpressions;
+using Mono.Options;
-namespace Symbolicate
+namespace Mono
{
- public class Program
+ public class Symbolicate
{
- static Regex regex = new Regex (@"\w*at (?<Method>.+) *(\[0x(?<IL>.+)\]|<0x.+ \+ 0x(?<NativeOffset>.+)>( (?<MethodIndex>\d+)|)) in <filename unknown>:0");
+ class Command {
+ public readonly int MinArgCount;
+ public readonly int MaxArgCount;
+ public readonly Action<List<string>> Action;
+
+ public Command (Action<List<string>> action, int minArgCount = 0, int maxArgCount = int.MaxValue)
+ {
+ Action = action;
+ MinArgCount = minArgCount;
+ MaxArgCount = maxArgCount;
+ }
+ }
+
+ static Logger logger;
public static int Main (String[] args)
{
- if (args.Length < 2) {
- Console.Error.WriteLine ("Usage: symbolicate <assembly path> <input file> [lookup directories]");
- return 1;
- }
-
- var assemblyPath = args [0];
- var inputFile = args [1];
+ var showHelp = false;
+ List<string> extra = null;
- var locProvider = new LocationProvider ();
+ Command cmd = null;
- for (var i = 2; i < args.Length; i++)
- locProvider.AddDirectory (args [i]);
+ var logLevel = Logger.Level.Warning;
- locProvider.AddAssembly (assemblyPath);
+ var options = new OptionSet {
+ { "h|help", "Show this help", v => showHelp = true },
+ { "q", "Quiet, warnings are not displayed", v => logLevel = Logger.Level.Error },
+ { "v", "Verbose, log debug messages", v => logLevel = Logger.Level.Debug },
+ };
- using (StreamReader r = new StreamReader (inputFile)) {
- for (var line = r.ReadLine (); line != null; line = r.ReadLine ()) {
- line = SymbolicateLine (line, locProvider);
- Console.WriteLine (line);
- }
+ try {
+ extra = options.Parse (args);
+ } catch (OptionException e) {
+ Console.WriteLine ("Option error: {0}", e.Message);
+ showHelp = true;
}
- return 0;
- }
-
- static string SymbolicateLine (string line, LocationProvider locProvider)
- {
- var match = regex.Match (line);
- if (!match.Success)
- return line;
+ if (extra.Count > 0 && extra[0] == "store-symbols")
+ cmd = new Command (StoreSymbolsAction, 2);
- string typeFullName, methodSignature;
- var methodStr = match.Groups ["Method"].Value.Trim ();
- if (!ExtractSignatures (methodStr, out typeFullName, out methodSignature))
- return line;
+ if (cmd != null) {
+ extra.RemoveAt (0);
+ } else {
+ cmd = new Command (SymbolicateAction, 2, 2);
+ }
- var isOffsetIL = !string.IsNullOrEmpty (match.Groups ["IL"].Value);
- var offsetVarName = (isOffsetIL)? "IL" : "NativeOffset";
- var offset = int.Parse (match.Groups [offsetVarName].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ if (showHelp || extra == null || extra.Count < cmd.MinArgCount || extra.Count > cmd.MaxArgCount) {
+ Console.Error.WriteLine ("Usage: symbolicate [options] <msym dir> <input file>");
+ Console.Error.WriteLine (" symbolicate [options] store-symbols <msym dir> [<dir>]+");
+ Console.WriteLine ();
+ Console.WriteLine ("Available options:");
+ options.WriteOptionDescriptions (Console.Out);
+ return 1;
+ }
- uint methodIndex = 0xffffff;
- if (!string.IsNullOrEmpty (match.Groups ["MethodIndex"].Value))
- methodIndex = uint.Parse (match.Groups ["MethodIndex"].Value, CultureInfo.InvariantCulture);
+ logger = new Logger (logLevel, msg => Console.Error.WriteLine (msg));
- var loc = locProvider.TryGetLocation (typeFullName, methodSignature, offset, isOffsetIL, methodIndex);
- if (loc == null)
- return line;
+ cmd.Action (extra);
- return line.Replace ("<filename unknown>:0", string.Format ("{0}:{1}", loc.Document.Url, loc.StartLine));
+ return 0;
}
- static bool ExtractSignatures (string str, out string typeFullName, out string methodSignature)
+ private static void SymbolicateAction (List<string> args)
{
- var methodNameEnd = str.IndexOf ('(');
- if (methodNameEnd == -1) {
- typeFullName = methodSignature = null;
- return false;
- }
+ var msymDir = args [0];
+ var inputFile = args [1];
- var typeNameEnd = str.LastIndexOf ('.', methodNameEnd);
- if (typeNameEnd == -1) {
- typeFullName = methodSignature = null;
- return false;
- }
+ var symbolManager = new SymbolManager (msymDir, logger);
- // Adjustment for Type..ctor ()
- if (typeNameEnd > 0 && str [typeNameEnd - 1] == '.') {
- --typeNameEnd;
+ using (StreamReader r = new StreamReader (inputFile)) {
+ for (var line = r.ReadLine (); line != null; line = r.ReadLine ()) {
+ StackFrameData sfData;
+ if (StackFrameData.TryParse (line, out sfData) &&
+ symbolManager.TryResolveLocation (sfData)) {
+ Console.WriteLine (sfData.ToString ());
+ continue;
+ }
+
+ Console.WriteLine (line);
+ }
}
+ }
+
+ private static void StoreSymbolsAction (List<string> args)
+ {
+ var msymDir = args[0];
+ var lookupDirs = args.Skip (1).ToArray ();
- typeFullName = str.Substring (0, typeNameEnd);
- // Remove generic parameters
- typeFullName = Regex.Replace (typeFullName, @"\[[^\[\]]*\]", "");
+ var symbolManager = new SymbolManager (msymDir, logger);
- methodSignature = str.Substring (typeNameEnd + 1);
- return true;
+ symbolManager.StoreSymbols (lookupDirs);
}
}
-}
\ No newline at end of file
+}