From b13659259f4e317d3f3541c70d8a4cc12806bc16 Mon Sep 17 00:00:00 2001 From: "j-h.choi" Date: Wed, 24 Feb 2021 16:07:54 +0900 Subject: [PATCH] [Tool] Added option to directly enter the log string Change-Id: I7d73d17dfd7eb283675d639b7079e5d685d86196 --- tools/Extractor/README.md | 60 +++-- .../dotnet-extractor/dotnet-extractor/CodeBlock.cs | 5 +- .../dotnet-extractor/dotnet-extractor/Extractor.cs | 6 +- .../dotnet-extractor/LineNumber.cs | 261 +++++++++++++-------- 4 files changed, 211 insertions(+), 121 deletions(-) diff --git a/tools/Extractor/README.md b/tools/Extractor/README.md index 360656d..b777807 100644 --- a/tools/Extractor/README.md +++ b/tools/Extractor/README.md @@ -3,50 +3,70 @@ ### Usage ``` -dotnet-extractor line-number [Log] [Options] +dotnet-extractor [Commands] ``` -#### Log -* Filepath - Path to the exception log file (Filename extension: xxxxx.log) +``` +dotnet-extractor convert [Options] +``` + +#### Commands +* convert + Get the line number from the token value in the stacktrace + +#### Interactive shell +* Input string + Input the exception log string directly #### Options * -h, --help Show this help message +* -i, --input [Input path] + Path to the exception log file (File extension: xxxxx.log) + * -a, --assembly [Path1:Path2:...] Multiple paths with assembly directories separated by colon(':') * -p, --pdb [Pdb path] Path to the pdb directory (Can be omitted if it is the same as the assembly directory path) -* -o, --out [Output path] +* -o, --output [Output path] Path to the output file (Default: Output to console. If omitted, the xxxxx.out file is created in the same location as the log file) #### Example +* If you enter the exception log string directly +``` + # dotnet extractor convert + ### Dotnet Extractor Tool (v1.0) ### + Enter the exception log string: + I/DOTNET_LAUNCHER(12345): at TestApp.Program.TestMethod1() in TestApp.Tizen.dll: token 0x6000001+0x5 + I/DOTNET_LAUNCHER(12345): at TestApp.Program.OnCreate() in TestApp.Tizen.dll: token 0x6000002+0x1 +``` + * If both assembly and pdb are in the current directory ``` - # dotnet extractor line-number /tmp/Exception1.log + # dotnet extractor convert --input /tmp/Exception1.log ``` * If both assembly and pdb are in the same directory specified ``` - # dotnet extractor /tmp/Exception2.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp/:/usr/share/dotnet.tizen/ + # dotnet extractor convert --input /tmp/Exception2.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp.Tizen/:/usr/share/dotnet.tizen/ ``` * If assembly and pdb are separated in each directory ``` - # dotnet extractor /tmp/Exception3.log --assembly /usr/share/dotnet.tizen/framework/ --pdb /tmp/pdbs/ + # dotnet extractor convert --input /tmp/Exception3.log --assembly /usr/share/dotnet.tizen/framework/ --pdb /tmp/pdbs/ ``` #### Others * Log format ``` - I/DOTNET_LAUNCHER(27298): System.NullReferenceException: Object reference not set to an instance of an object. - I/DOTNET_LAUNCHER(27298): at TestApp.Program.TestMethod() in TestApp.dll: method_token(0x6000001), il_offset(0x5) - I/DOTNET_LAUNCHER(27298): at TestApp.Program.OnCreate() in TestApp.dll: method_token(0x6000002), il_offset(0x1) + I/DOTNET_LAUNCHER(12345): System.NullReferenceException: Object reference not set to an instance of an object. + I/DOTNET_LAUNCHER(12345): at TestApp.Program.TestMethod1() in TestApp.Tizen.dll: token 0x6000001+0x5 + I/DOTNET_LAUNCHER(12345): at TestApp.Program.OnCreate() in TestApp.Tizen.dll: token 0x6000002+0x1 ------- Format ------- - I/DOTNET_LAUNCHER(00000): at {typeName}.{methodName}() in {moduleName}: method_token{methodToken}, il_offset{ilOffset} + I/DOTNET_LAUNCHER(00000): at {typeName}.{methodName}({parameters}) in {moduleName}: token {methodToken}+{ilOffset} ``` ---- @@ -54,19 +74,19 @@ dotnet-extractor line-number [Log] [Options] #### Sample ``` sh-3.2# cat /tmp/Exception.log -I/DOTNET_LAUNCHER(27298): System.NullReferenceException: Object reference not set to an instance of an object. -I/DOTNET_LAUNCHER(27298): at LineNumberExtract.Program.ExceptionMethod2() in LineNumberExtract.Tizen.dll: method_token(0x6000001), il_offset(0x5) -I/DOTNET_LAUNCHER(27298): at LineNumberExtract.Program.ExceptionMethod1() in LineNumberExtract.Tizen.dll: method_token(0x6000002), il_offset(0x1) -I/DOTNET_LAUNCHER(27298): at LineNumberExtract.Program.OnCreate() in LineNumberExtract.Tizen.dll: method_token(0x6000004), il_offset(0x15) +I/DOTNET_LAUNCHER(12345): System.NullReferenceException: Object reference not set to an instance of an object. +I/DOTNET_LAUNCHER(12345): at TestApp.Program.TestMethod1() in TestApp.Tizen.dll: token 0x6000001+0x5 +I/DOTNET_LAUNCHER(12345): at TestApp.Program.TestMethod2() in TestApp.Tizen.dll: token 0x6000002+0x1 +I/DOTNET_LAUNCHER(12345): at TestApp.Program.OnCreate() in TestApp.Tizen.dll: token 0x6000004+0x15 sh-3.2# -sh-3.2# dotnet eExtractor /tmp/Exception.log --assembly /opt/usr/globalapps/org.tizen.example.LineNumberExtract.Tizen/ +sh-3.2# dotnet extractor convert --input /tmp/Exception.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp.Tizen/ ##### Line Number Extractor Tool (v1.0) ##### Extraction result: - at LineNumberExtract.Program.ExceptionMethod2(i) in U:\PTX\LineNumberExtract\LineNumberExtract\LineNumberExtract.Tizen\LineNumberExtract.Tizen.cs:line 14 - at LineNumberExtract.Program.ExceptionMethod1(t) in U:\PTX\LineNumberExtract\LineNumberExtract\LineNumberExtract.Tizen\LineNumberExtract.Tizen.cs:line 19 - at LineNumberExtract.Program.OnCreate() in U:\PTX\LineNumberExtract\LineNumberExtract\LineNumberExtract.Tizen\LineNumberExtract.Tizen.cs:line 58 + at TestApp.Program.TestMethod1() in U:\TestApp\TestApp\TestApp.Tizen\TestApp.Tizen.cs:line 14 + at TestApp.Program.TestMethod2() in U:\TestApp\TestApp\TestApp.Tizen\TestApp.Tizen.cs:line 19 + at TestApp.Program.OnCreate() in U:\TestApp\TestApp\TestApp.Tizen\TestApp.Tizen.cs:line 58 Output: /tmp/Exception.out diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs b/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs index d4e7db5..26c91a7 100644 --- a/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs +++ b/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs @@ -1,7 +1,4 @@ using System; -using System.IO; -using System.Text; -using System.Xml; namespace Tizen.Runtime.Tools { @@ -13,7 +10,7 @@ namespace Tizen.Runtime.Tools public void CodeBlocks() { - Console.WriteLine("Not implemented yet"); + Console.WriteLine("Not implemented yet\n"); } } } \ No newline at end of file diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/Extractor.cs b/tools/Extractor/dotnet-extractor/dotnet-extractor/Extractor.cs index b13e0ce..757b1b8 100644 --- a/tools/Extractor/dotnet-extractor/dotnet-extractor/Extractor.cs +++ b/tools/Extractor/dotnet-extractor/dotnet-extractor/Extractor.cs @@ -23,7 +23,7 @@ namespace Tizen.Runtime.Tools UsageCommand(); Environment.Exit(0); break; - case "line-number": + case "convert": command = "line"; break; case "code-block": @@ -47,7 +47,7 @@ namespace Tizen.Runtime.Tools } else if (command == "code") { - //new CodeBlock().CodeBlocks(); + new CodeBlock().CodeBlocks(); } } catch (Exception e) @@ -60,7 +60,7 @@ namespace Tizen.Runtime.Tools { string UsageMsg = "Usage: dotnet-extractor [Command]\n\n" + "Commands:\n" - + " line-number Get line number\n"; + + " convert Get the line number from the token value in the stacktrace\n"; //+ " code-block Get code block\n"; Console.WriteLine(UsageMsg); } diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs b/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs index 792792a..085d960 100644 --- a/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs +++ b/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs @@ -1,6 +1,7 @@ using Microsoft.DiaSymReader.Tools; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -12,44 +13,47 @@ namespace Tizen.Runtime.Tools { private static readonly string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - public LineNumber() - { - } - internal sealed class Args { public readonly string[] AssemblyPaths; public readonly string[] PdbPaths; public readonly string ExceptionPath; + public readonly List ExceptionString; public readonly string OutputPath; - public Args(string[] assemblyPaths, string[] pdbPaths, string exceptionPath, string outputPath) + public Args(string[] assemblyPaths, string[] pdbPaths, string exceptionPath, List exceptionString, string outputPath) { AssemblyPaths = assemblyPaths; PdbPaths = pdbPaths; ExceptionPath = exceptionPath; + ExceptionString = exceptionString; OutputPath = outputPath; } } public static void UsageLine() { - string UsageMsg = "Usage: dotnet-extractor line-number [Log] [Options]\n\n" - + "Log:\n" - + " Path to the exception log file (Filename extension: xxxxx.log)\n\n" - //+ " Input the log message directly\n\n" + string UsageMsg = "Usage: dotnet-extractor convert [Options]\n\n" + + "Interactive shell:\n" + + " Input the exception log string directly\n\n" + "Options:\n" + " -h, --help Show this help message\n" + + " -i, --input Path to the exception log file (File extension: xxxxx.log)\n" + " -a, --assembly Multiple paths with assembly directories separated by colon(':')\n" + " -p, --pdb Path to the pdb directory (Can be omitted if it is the same as the assembly directory path)\n" - + " -o, --out Path to the output file (Default: Output to console. If omitted, the xxxxx.out file is created in the same location as the log file)\n\n" + + " -o, --output Path to the output file (Default: Output to console. If omitted, the xxxxx.out file is created in the same location as the log file)\n\n" + "Example:\n" - + "1. If both assembly and pdb are in the current directory\n" - + " # dotnet extractor line-number /tmp/Exception1.log\n" - + "2. If both assembly and pdb are in the same directory specified\n" - + " # dotnet extractor line-number /tmp/Exception2.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp/:/usr/share/dotnet.tizen/\n" - + "3. If assembly and pdb are separated in each directory\n" - + " # dotnet extractor line-number /tmp/Exception3.log --assembly /usr/share/dotnet.tizen/framework/ --pdb /tmp/pdbs/\n"; + + "1. If you enter the exception log string directly\n" + + " # dotnet extractor convert\n" + + " Enter the exception log string:\n" + + " I/DOTNET_LAUNCHER(12345): at TestApp.Program.TestMethod() in TestApp.Tizen.dll: token 0x6000001+0x5\n" + + " I/DOTNET_LAUNCHER(12345): at TestApp.Program.OnCreate() in TestApp.Tizen.dll: token 0x6000002+0x1\n\n" + + "2. If both assembly and pdb are in the current directory\n" + + " # dotnet extractor convert --input /tmp/Exception1.log\n\n" + + "3. If both assembly and pdb are in the same directory specified\n" + + " # dotnet extractor convert --input /tmp/Exception2.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp.Tizen/:/usr/share/dotnet.tizen/\n\n" + + "4. If assembly and pdb are separated in each directory\n" + + " # dotnet extractor convert --input /tmp/Exception3.log --assembly /usr/share/dotnet.tizen/framework/ --pdb /tmp/pdbs/\n"; Console.WriteLine(UsageMsg); } @@ -72,6 +76,7 @@ namespace Tizen.Runtime.Tools string[] assemblyPaths = null; string[] pdbPaths = null; string exceptionPath = null; + List exceptionString = new List(); string outputPath = null; int i = 1; @@ -86,38 +91,56 @@ namespace Tizen.Runtime.Tools UsageLine(); Environment.Exit(0); break; + case "-i": + case "--input": + exceptionPath = ReadValue(); + break; + case "-a": + case "--assembly": + assemblyPaths = ReadValue().Split(":"); + break; case "-p": case "--pdb": pdbPaths = ReadValue().Split(":"); break; case "-o": - case "--out": + case "--output": outputPath = ReadValue(); break; - case "-a": - case "--assembly": - assemblyPaths = ReadValue().Split(":"); - break; default: - if (arg.Contains("-")) - { - UsageLine(); - Console.WriteLine($"Unknown option [{arg}]\n"); - Environment.Exit(0); - break; - } - exceptionPath ??= arg; - if (!File.Exists(exceptionPath)) - { - throw new FileNotFoundException("Log file not found\n"); - } + UsageLine(); + Console.WriteLine($"Unknown option [{arg}]\n"); + Environment.Exit(0); break; } } if (exceptionPath == null) { - throw new InvalidDataException("Missing exception log path\n"); + Console.WriteLine("Enter the exception log string:"); + string line; + while ((line = Console.ReadLine()) != null && line != "") + { + if (!line.Contains(":")) + { + exceptionPath = line; + exceptionString.Clear(); + break; + } + exceptionString.Add(line); + } + } + if (exceptionString.Count == 0) + { + if (exceptionPath == null) + { + throw new InvalidDataException("Missing exception log path or log string\n"); + } + else if (!File.Exists(exceptionPath)) + { + throw new FileNotFoundException("Exception log file not found\n"); + } } + assemblyPaths ??= new string[] { Directory.GetCurrentDirectory() }; pdbPaths ??= assemblyPaths; try @@ -133,6 +156,7 @@ namespace Tizen.Runtime.Tools assemblyPaths: assemblyPaths, pdbPaths: pdbPaths, exceptionPath: exceptionPath, + exceptionString: exceptionString, outputPath: outputPath); } @@ -181,12 +205,8 @@ namespace Tizen.Runtime.Tools { if (Path.GetFileNameWithoutExtension(peFile) == Path.GetFileNameWithoutExtension(pdbFile)) { - string xmlPath = Path.ChangeExtension(peFile, "xml"); - if (xmlPath.Contains("/usr/share/dotnet")) - { - xmlPath = tempDirectory + "/" + Path.GetFileName(xmlPath); - } - GetXmlFromPdb(peFile, pdbFile, xmlPath); + string xmlPath = Path.Combine(tempDirectory, Path.GetFileName(Path.ChangeExtension(peFile, "xml"))); + GenXmlFromPdb(peFile, pdbFile, xmlPath); xmlList.Add(xmlPath); break; } @@ -195,7 +215,7 @@ namespace Tizen.Runtime.Tools return xmlList; } - private static void GetXmlFromPdb(string assemblyPath, string pdbPath, string xmlPath) + private static void GenXmlFromPdb(string assemblyPath, string pdbPath, string xmlPath) { using var peStream = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read); using var pdbStream = new FileStream(pdbPath, FileMode.Open, FileAccess.Read); @@ -206,25 +226,28 @@ namespace Tizen.Runtime.Tools PdbToXmlConverter.ToXml(sw, pdbStream, peStream, options); } + private static void RemoveTempDirectory() + { + if (Directory.Exists(tempDirectory)) + { + Directory.Delete(tempDirectory, true); + } + } + /* * Extract */ private static void Extract(Args args, List xmlList) { - string logFile = args.ExceptionPath; - string outputPath = args.OutputPath; - if (xmlList.Count == 0) { + RemoveTempDirectory(); throw new FileNotFoundException("Xml file not found\n"); } - GetLineFromLog(logFile, xmlList, outputPath); + GetLineFromLog(args, xmlList); - if (Directory.Exists(tempDirectory)) - { - Directory.Delete(tempDirectory, true); - } + RemoveTempDirectory(); } internal sealed class StackTraceInfo @@ -242,58 +265,93 @@ namespace Tizen.Runtime.Tools public string EndLine; } - private static void GetLineFromLog(string logPath, List xmlList, string outputPath) + private static string GetRegex(string line, List xmlList) { - Console.WriteLine("Extraction result: "); - try + string ret = line; + string logtagStr = Regex.Match(line, "(?.*?) at").Groups["splitdata"].Value; + if (logtagStr.Length != 0) { - using StreamReader fsr = new StreamReader(new FileStream(logPath, FileMode.Open, FileAccess.Read)); - using StreamWriter fsw = new StreamWriter(new FileStream(outputPath, FileMode.Create, FileAccess.Write)); + ret = line.Replace(logtagStr, ""); + } + string typeMethodStr = Regex.Match(line, "at (?.*?)\\((.*)\\)").Groups["splitdata"].Value; + string methodStr = typeMethodStr.Split(".")[^1]; + string typenameStr = typeMethodStr.Replace("." + methodStr, ""); + string parameterStr = Regex.Match(line, methodStr + "\\((?.*?)\\)").Groups["splitdata"].Value; + string assemblyStr = Regex.Match(line, " in (?.*?)\\: ").Groups["splitdata"].Value; + string[] tokenOffsetStr = Regex.Match(line, "\\: token (?.*)?").Groups["splitdata"].Value.Split("+"); + string xmlStr = assemblyStr.Contains(".ni.dll") ? assemblyStr.Replace(".ni.dll", ".xml") : assemblyStr.Replace(".dll", ".xml"); - bool isParsed = false; - while (!fsr.EndOfStream) + if (tokenOffsetStr.Length != 2 || methodStr == "" || typenameStr == "" || assemblyStr == "") + { + return ret; + } + + StackTraceInfo stInfo = new StackTraceInfo() { Type = typenameStr, Method = methodStr, Assembly = assemblyStr, Token = tokenOffsetStr[0], Offset = tokenOffsetStr[1] }; + + foreach (var xmlPath in xmlList) + { + if (xmlPath.Contains(xmlStr)) { - string line = fsr.ReadLine(); - if (!line.Contains(" at ")) + GetLineFromXml(xmlPath, stInfo); + if (stInfo.Filepath != null && stInfo.StartLine != null) { - continue; + ret = $" at {stInfo.Type}.{stInfo.Method}({parameterStr}) in {stInfo.Filepath}:line {stInfo.StartLine}"; + break; } - string typeMethodStr = Regex.Match(line, " at (?.*?)\\(\\)").Groups["splitdata"].Value; - string methodStr = typeMethodStr.Split(".")[^1]; - string typenameStr = typeMethodStr.Replace("." + methodStr, ""); - string assemblyStr = Regex.Match(line, " in (?.*?)\\: ").Groups["splitdata"].Value; - string tokenStr = Regex.Match(line, " method_token\\((?.*?)\\)\\,").Groups["splitdata"].Value; - string offsetStr = Regex.Match(line, " il_offset\\((?.*?)\\)").Groups["splitdata"].Value; - string xmlStr = assemblyStr.Replace(".dll", ".xml"); + } + } + return ret; + } - StackTraceInfo stInfo = new StackTraceInfo() { Type = typenameStr, Method = methodStr, Assembly = assemblyStr, Token = tokenStr, Offset = offsetStr/*, Xml = xmlStr*/ }; + private static void GetLineFromLog(Args args, List xmlList) + { + Console.WriteLine("Extraction result: "); - foreach (var xmlPath in xmlList) + try + { + List result = new List(); + if (args.ExceptionPath == null) + { + // + foreach (var line in args.ExceptionString) { - if (xmlPath.Contains(xmlStr)) + if (!line.Contains("at ")) { - isParsed = true; - GetLineFromXml(xmlPath, stInfo); - if (stInfo.Filepath == null || stInfo.StartLine == null) - { - Console.WriteLine(" ===== PARSE ERROR FOR EXCEPTION LOG IN THIS LINE. PLEASE RECHECK THE EXCEPTION LOG ====="); - break; - } - string ret = $" at {stInfo.Type}.{stInfo.Method}({stInfo.Param}) in {stInfo.Filepath}:line {stInfo.StartLine}"; - fsw.WriteLine(ret); - Console.WriteLine(ret); + continue; } + string ret = GetRegex(line, xmlList); + result.Add(ret); + Console.WriteLine(ret); } } - if (!isParsed) + else { - Console.WriteLine(" There is no content matching the exception log."); - Console.WriteLine(" Please recheck the assembly and pdb directory path.\n"); + // + using StreamReader fsr = new StreamReader(new FileStream(args.ExceptionPath, FileMode.Open, FileAccess.Read)); + while (!fsr.EndOfStream) + { + string line = fsr.ReadLine(); + if (!line.Contains(" at ")) + { + continue; + } + string ret = GetRegex(line, xmlList); + result.Add(ret); + Console.WriteLine(ret); + } } - else + + string output = string.Empty; + if (args.OutputPath != null) { - Console.WriteLine($"\nOutput: {outputPath}\n"); + using StreamWriter fsw = new StreamWriter(new FileStream(args.OutputPath, FileMode.Create, FileAccess.Write)); + foreach (var ret in result) + { + fsw.WriteLine(ret); + } + output = $"\nOutput: {args.OutputPath}\n"; } + Console.WriteLine($"{output}"); } catch (Exception e) { @@ -393,25 +451,40 @@ namespace Tizen.Runtime.Tools } } + private static int HexToInt(string value) + { + // strip the leading 0x + if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + { + value = value.Substring(2); + } + return Int32.Parse(value, NumberStyles.HexNumber); + } + private static void ParseEntry(XmlNodeList xn, StackTraceInfo stInfo) { try { + XmlNode bestPointSoFar = null; + int ilOffset = HexToInt(stInfo.Offset); foreach (XmlNode node in xn) { - if (stInfo.Offset == node.Attributes["offset"].Value) + // If the attribute is not 'startLine', but 'hidden', select the best value so far + if (HexToInt(node.Attributes["offset"].Value) > ilOffset) { - if (node.Attributes.Item(1).Name == "startLine") - { - stInfo.StartLine = node.Attributes["startLine"].Value; - } - if (node.Attributes.Item(3).Name == "endLine") - { - stInfo.EndLine = node.Attributes["endLine"].Value; - } - stInfo.Document = node.Attributes["document"].Value; + break; + } + if (node.Attributes["startLine"] != null) + { + bestPointSoFar = node; } } + if (bestPointSoFar != null) + { + stInfo.StartLine = bestPointSoFar.Attributes["startLine"].Value; + stInfo.EndLine = bestPointSoFar.Attributes["endLine"].Value; + stInfo.Document = bestPointSoFar.Attributes["document"].Value; + } } catch (ArgumentException e) { -- 2.7.4