Add line number to extractor tool accepted/tizen/unified/20210225.130459 submit/tizen/20210225.050444
authorj-h.choi <j-h.choi@samsung.com>
Wed, 10 Feb 2021 02:06:54 +0000 (11:06 +0900)
committer조웅석/Common Platform Lab(SR)/Principal Engineer/삼성전자 <ws77.cho@samsung.com>
Wed, 24 Feb 2021 03:00:46 +0000 (12:00 +0900)
Change-Id: I5da17e91d8d5bc59a5e59e5385ae413ef93ac178

12 files changed:
tools/Extractor/README.md [new file with mode: 0644]
tools/Extractor/build.sh [new file with mode: 0755]
tools/Extractor/dotnet-extractor/dotnet-extractor.sln [new file with mode: 0644]
tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs [new file with mode: 0644]
tools/Extractor/dotnet-extractor/dotnet-extractor/Extractor.cs [new file with mode: 0644]
tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs [new file with mode: 0644]
tools/Extractor/dotnet-extractor/dotnet-extractor/Microsoft.DiaSymReader.Converter.Xml.dll [new file with mode: 0644]
tools/Extractor/dotnet-extractor/dotnet-extractor/dotnet-extractor.csproj [new file with mode: 0644]
tools/Performance/README [moved from tools/README with 100% similarity]
tools/Performance/memorystamp.sh [moved from tools/memorystamp.sh with 100% similarity]
tools/Performance/performance_test.sh [moved from tools/performance_test.sh with 100% similarity]
tools/Performance/timestamp.sh [moved from tools/timestamp.sh with 100% similarity]

diff --git a/tools/Extractor/README.md b/tools/Extractor/README.md
new file mode 100644 (file)
index 0000000..360656d
--- /dev/null
@@ -0,0 +1,74 @@
+# Line Number Extractor tool
+
+### Usage
+
+```
+dotnet-extractor line-number [Log] [Options]
+```
+
+#### Log
+* Filepath
+   Path to the exception log file (Filename extension: xxxxx.log)
+
+#### Options
+* -h, --help  
+    Show this help message
+
+* -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]  
+    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 both assembly and pdb are in the current directory
+```
+ # dotnet extractor line-number /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/
+```
+
+* If assembly and pdb are separated in each directory
+```
+ # dotnet extractor /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)
+  ------- Format -------
+ I/DOTNET_LAUNCHER(00000):    at {typeName}.{methodName}() in {moduleName}: method_token{methodToken}, il_offset{ilOffset}
+```
+
+----
+
+#### 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)
+sh-3.2# 
+sh-3.2# dotnet eExtractor /tmp/Exception.log --assembly /opt/usr/globalapps/org.tizen.example.LineNumberExtract.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
+
+Output: /tmp/Exception.out
+
+sh-3.2#
+```
diff --git a/tools/Extractor/build.sh b/tools/Extractor/build.sh
new file mode 100755 (executable)
index 0000000..52f490a
--- /dev/null
@@ -0,0 +1,78 @@
+#! /bin/bash
+
+SCRIPT_DIR=$(dirname $(readlink -f $0))
+
+CONFIGURATION=Release
+SLN_NAME=dotnet-extractor/dotnet-extractor
+TARGET_FRAMEWORK=netcoreapp3.1
+SLN_FILE=$SCRIPT_DIR/$SLN_NAME.sln
+OUTDIR=$SCRIPT_DIR/$SLN_NAME/bin/$CONFIGURATION/$TARGET_FRAMEWORK
+TARGET_ASSEMBLY_DIR=/home/owner/share/.dotnet/tools
+
+usage() {
+  echo "Usage: $0 [command]"
+  echo "Commands:"
+  echo " -b, --build     Build a ExtractLine module"
+  echo " -i, --install   Install assemblies to the target device"
+  echo " -c, --clean     Clean all artifacts"
+}
+
+remove_intermediates() {
+  find $1 -type d \( -name bin -o -name obj \) -print0 | xargs -0 -I {} rm -fr "{}"
+}
+
+clean() {
+  remove_intermediates $SCRIPT_DIR/$SLN_NAME
+  rm -f msbuild.log
+}
+
+build() {
+  clean
+  dotnet build -c $CONFIGURATION $SLN_FILE
+}
+
+install() {
+  DEVICE_ID=$1
+
+  RUNTIME_ASSEMBLIES="$OUTDIR/*.dll"
+
+  device_cnt=$(sdb devices | grep -v "List" | wc -l)
+
+  if [ $device_cnt -eq 0 ]; then
+    echo "No connected devices"
+    exit 1
+  fi
+
+  if [ $device_cnt -gt 1 ] && [ -z "$DEVICE_ID" ]; then
+    echo "Multiple devices are connected. Specify the device. (ex: ./build.sh --install [device-id])"
+    sdb devices
+    exit 1
+  fi
+
+  SDB_OPTIONS=""
+  if [ -n "$DEVICE_ID" ]; then
+    SDB_OPTIONS="-s $DEVICE_ID"
+  fi
+
+  sdb $SDB_OPTIONS root on
+  sdb $SDB_OPTIONS shell mount -o remount,rw /
+  sdb $SDB_OPTIONS push $RUNTIME_ASSEMBLIES $TARGET_ASSEMBLY_DIR
+
+  nifile_cnt=$(sdb $SDB_OPTIONS shell find $TARGET_ASSEMBLY_DIR -name 'Microsoft.DiaSymReader*.ni.dll' | wc -l)
+  if [ $nifile_cnt -gt 0 ]; then
+    sdb $SDB_OPTIONS shell "rm -f $TARGET_ASSEMBLY_DIR/Microsoft.DiaSymReader*.ni.dll"
+  fi
+
+  #sdb $SDB_OPTIONS shell dotnettool --ni-dll $TARGET_ASSEMBLY_DIR/Microsoft.DiaSymReader*.dll
+  sdb $SDB_OPTIONS shell chsmack -a '_' $TARGET_ASSEMBLY_DIR/*.dll
+  sdb $SDB_OPTIONS shell chown owner:users $TARGET_ASSEMBLY_DIR/*.dll
+  sdb $SDB_OPTIONS shell chmod 644 $TARGET_ASSEMBLY_DIR/*.dll
+}
+
+cmd=$1; [ $# -gt 0 ] && shift;
+case "$cmd" in
+  build|--build|-b) build ;;
+  install|--install|-i) install $@ ;;
+  clean|--clean|-c) clean ;;
+  *) usage ;;
+esac
diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor.sln b/tools/Extractor/dotnet-extractor/dotnet-extractor.sln
new file mode 100644 (file)
index 0000000..b54341f
--- /dev/null
@@ -0,0 +1,25 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio Version 16\r
+VisualStudioVersion = 16.0.29613.14\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-extractor", "dotnet-extractor\dotnet-extractor.csproj", "{452A02D6-B803-4F73-BF0F-62A53D6FF367}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Any CPU = Debug|Any CPU\r
+               Release|Any CPU = Release|Any CPU\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {452A02D6-B803-4F73-BF0F-62A53D6FF367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r
+               {452A02D6-B803-4F73-BF0F-62A53D6FF367}.Debug|Any CPU.Build.0 = Debug|Any CPU\r
+               {452A02D6-B803-4F73-BF0F-62A53D6FF367}.Release|Any CPU.ActiveCfg = Release|Any CPU\r
+               {452A02D6-B803-4F73-BF0F-62A53D6FF367}.Release|Any CPU.Build.0 = Release|Any CPU\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+       GlobalSection(ExtensibilityGlobals) = postSolution\r
+               SolutionGuid = {B0A7D6D5-EC91-4FB0-8A1B-DB9B39D1073B}\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs b/tools/Extractor/dotnet-extractor/dotnet-extractor/CodeBlock.cs
new file mode 100644 (file)
index 0000000..d4e7db5
--- /dev/null
@@ -0,0 +1,19 @@
+using System;\r
+using System.IO;\r
+using System.Text;\r
+using System.Xml;\r
+\r
+namespace Tizen.Runtime.Tools\r
+{\r
+    public class CodeBlock\r
+    {\r
+        public CodeBlock()\r
+        {\r
+        }\r
+\r
+        public void CodeBlocks()\r
+        {\r
+            Console.WriteLine("Not implemented yet");\r
+        }\r
+    }\r
+}
\ 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
new file mode 100644 (file)
index 0000000..b13e0ce
--- /dev/null
@@ -0,0 +1,68 @@
+using System;\r
+using System.IO;\r
+\r
+namespace Tizen.Runtime.Tools\r
+{\r
+    class Extractor\r
+    {\r
+        private static readonly string version = "1.0";\r
+\r
+        public static void Main(string[] args)\r
+        {\r
+            Console.WriteLine($"\n### Dotnet Extractor Tool (v{version}) ###\n");\r
+\r
+            try\r
+            {\r
+                string command = null;\r
+                if (0 < args.Length)\r
+                {\r
+                    switch (args[0])\r
+                    {\r
+                        case "-h":\r
+                        case "--help":\r
+                            UsageCommand();\r
+                            Environment.Exit(0);\r
+                            break;\r
+                        case "line-number":\r
+                            command = "line";\r
+                            break;\r
+                        case "code-block":\r
+                            command = "code";\r
+                            break;\r
+                        default:\r
+                            UsageCommand();\r
+                            Console.WriteLine($"Unknown option [{args[0]}]\n");\r
+                            Environment.Exit(0);\r
+                            break;\r
+                    }\r
+                }\r
+                if (command == null)\r
+                {\r
+                    UsageCommand();\r
+                    throw new InvalidDataException("Required command was not provided\n");\r
+                }\r
+                else if (command == "line")\r
+                {\r
+                    new LineNumber().LineNumbers(args);\r
+                }\r
+                else if (command == "code")\r
+                {\r
+                    //new CodeBlock().CodeBlocks();\r
+                }\r
+            }\r
+            catch (Exception e)\r
+            {\r
+                Console.Error.WriteLine(e.Message);\r
+            }\r
+        }\r
+\r
+        public static void UsageCommand()\r
+        {\r
+            string UsageMsg = "Usage: dotnet-extractor [Command]\n\n"\r
+                              + "Commands:\n"\r
+                              + " line-number           Get line number\n";\r
+                              //+ " code-block            Get code block\n";\r
+            Console.WriteLine(UsageMsg);\r
+        }\r
+    }\r
+}\r
diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs b/tools/Extractor/dotnet-extractor/dotnet-extractor/LineNumber.cs
new file mode 100644 (file)
index 0000000..792792a
--- /dev/null
@@ -0,0 +1,422 @@
+using Microsoft.DiaSymReader.Tools;\r
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+using System.Xml;\r
+\r
+namespace Tizen.Runtime.Tools\r
+{\r
+    public class LineNumber\r
+    {\r
+        private static readonly string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());\r
+\r
+        public LineNumber()\r
+        {\r
+        }\r
+\r
+        internal sealed class Args\r
+        {\r
+            public readonly string[] AssemblyPaths;\r
+            public readonly string[] PdbPaths;\r
+            public readonly string ExceptionPath;\r
+            public readonly string OutputPath;\r
+\r
+            public Args(string[] assemblyPaths, string[] pdbPaths, string exceptionPath, string outputPath)\r
+            {\r
+                AssemblyPaths = assemblyPaths;\r
+                PdbPaths = pdbPaths;\r
+                ExceptionPath = exceptionPath;\r
+                OutputPath = outputPath;\r
+            }\r
+        }\r
+\r
+        public static void UsageLine()\r
+        {\r
+            string UsageMsg = "Usage: dotnet-extractor line-number [Log] [Options]\n\n"\r
+                              + "Log:\n"\r
+                              + "<Filepath>                             Path to the exception log file (Filename extension: xxxxx.log)\n\n"\r
+                              //+ "<String>                               Input the log message directly\n\n"\r
+                              + "Options:\n"\r
+                              + " -h, --help                            Show this help message\n"\r
+                              + " -a, --assembly <Path1:Path2:...>      Multiple paths with assembly directories separated by colon(':')\n"\r
+                              + " -p, --pdb <Pdb path>                  Path to the pdb directory (Can be omitted if it is the same as the assembly directory path)\n"\r
+                              + " -o, --out <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)\n\n"\r
+                              + "Example:\n"\r
+                              + "1. If both assembly and pdb are in the current directory\n"\r
+                              + " # dotnet extractor line-number /tmp/Exception1.log\n"\r
+                              + "2. If both assembly and pdb are in the same directory specified\n"\r
+                              + " # dotnet extractor line-number /tmp/Exception2.log --assembly /opt/usr/globalapps/org.tizen.example.TestApp/:/usr/share/dotnet.tizen/\n"\r
+                              + "3. If assembly and pdb are separated in each directory\n"\r
+                              + " # dotnet extractor line-number /tmp/Exception3.log --assembly /usr/share/dotnet.tizen/framework/ --pdb /tmp/pdbs/\n";\r
+            Console.WriteLine(UsageMsg);\r
+        }\r
+\r
+        public void LineNumbers(string[] args)\r
+        {\r
+            try\r
+            {\r
+                Args parsedArgs = ParseArgs(args);\r
+                Extract(parsedArgs, Convert(parsedArgs));\r
+            }\r
+            catch (Exception e)\r
+            {\r
+                UsageLine();\r
+                Console.Error.WriteLine(e.Message);\r
+            }\r
+        }\r
+\r
+        internal static Args ParseArgs(string[] args)\r
+        {\r
+            string[] assemblyPaths = null;\r
+            string[] pdbPaths = null;\r
+            string exceptionPath = null;\r
+            string outputPath = null;\r
+\r
+            int i = 1;\r
+            while (i < args.Length)\r
+            {\r
+                var arg = args[i++];\r
+                string ReadValue() => (i < args.Length) ? args[i++] : throw new InvalidDataException(string.Format("Missing value for option '{0}'", arg));\r
+                switch (arg)\r
+                {\r
+                    case "-h":\r
+                    case "--help":\r
+                        UsageLine();\r
+                        Environment.Exit(0);\r
+                        break;\r
+                    case "-p":\r
+                    case "--pdb":\r
+                        pdbPaths = ReadValue().Split(":");\r
+                        break;\r
+                    case "-o":\r
+                    case "--out":\r
+                        outputPath = ReadValue();\r
+                        break;\r
+                    case "-a":\r
+                    case "--assembly":\r
+                        assemblyPaths = ReadValue().Split(":");\r
+                        break;\r
+                    default:\r
+                        if (arg.Contains("-"))\r
+                        {\r
+                            UsageLine();\r
+                            Console.WriteLine($"Unknown option [{arg}]\n");\r
+                            Environment.Exit(0);\r
+                            break;\r
+                        }\r
+                        exceptionPath ??= arg;\r
+                        if (!File.Exists(exceptionPath))\r
+                        {\r
+                            throw new FileNotFoundException("Log file not found\n");\r
+                        }\r
+                        break;\r
+                }\r
+            }\r
+            if (exceptionPath == null)\r
+            {\r
+                throw new InvalidDataException("Missing exception log path\n");\r
+            }\r
+            assemblyPaths ??= new string[] { Directory.GetCurrentDirectory() };\r
+            pdbPaths ??= assemblyPaths;\r
+            try\r
+            {\r
+                outputPath ??= Path.ChangeExtension(exceptionPath, "out");\r
+            }\r
+            catch (Exception e)\r
+            {\r
+                throw new InvalidDataException(e.Message);\r
+            }\r
+\r
+            return new Args(\r
+                assemblyPaths: assemblyPaths,\r
+                pdbPaths: pdbPaths,\r
+                exceptionPath: exceptionPath,\r
+                outputPath: outputPath);\r
+        }\r
+\r
+        private static List<string> GrabFiles(string[] paths, string searchPattern)\r
+        {\r
+            List<string> files = new List<string>();\r
+            foreach (var assemDir in paths)\r
+            {\r
+                if (Directory.Exists(assemDir))\r
+                {\r
+                    foreach (var peFile in Directory.GetFiles(assemDir, searchPattern, SearchOption.AllDirectories))\r
+                    {\r
+                        files.Add(peFile);\r
+                    }\r
+                }\r
+            }\r
+            return files;\r
+        }\r
+\r
+        /*\r
+        * Convert\r
+        */\r
+        private static List<string> Convert(Args args)\r
+        {\r
+            List<string> peFiles = GrabFiles(args.AssemblyPaths, "*.dll");\r
+            if (peFiles.Count == 0)\r
+            {\r
+                throw new FileNotFoundException("Assembly file not found\n");\r
+            }\r
+\r
+            List<string> pdbFiles = GrabFiles(args.PdbPaths, "*.pdb");\r
+            if (pdbFiles.Count == 0)\r
+            {\r
+                throw new FileNotFoundException("PDB file not found\n");\r
+            }\r
+\r
+            Console.Write("Converting pdb to xml...");\r
+            Console.SetCursorPosition(0, Console.CursorTop);\r
+\r
+            Directory.CreateDirectory(tempDirectory);\r
+\r
+            List<string> xmlList = new List<string>();\r
+            foreach (var pdbFile in pdbFiles)\r
+            {\r
+                foreach (var peFile in peFiles)\r
+                {\r
+                    if (Path.GetFileNameWithoutExtension(peFile) == Path.GetFileNameWithoutExtension(pdbFile))\r
+                    {\r
+                        string xmlPath = Path.ChangeExtension(peFile, "xml");\r
+                        if (xmlPath.Contains("/usr/share/dotnet"))\r
+                        {\r
+                            xmlPath = tempDirectory + "/" + Path.GetFileName(xmlPath);\r
+                        }\r
+                        GetXmlFromPdb(peFile, pdbFile, xmlPath);\r
+                        xmlList.Add(xmlPath);\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+            return xmlList;\r
+        }\r
+\r
+        private static void GetXmlFromPdb(string assemblyPath, string pdbPath, string xmlPath)\r
+        {\r
+            using var peStream = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read);\r
+            using var pdbStream = new FileStream(pdbPath, FileMode.Open, FileAccess.Read);\r
+            using var dstFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.ReadWrite);\r
+            using var sw = new StreamWriter(dstFileStream, Encoding.UTF8);\r
+            PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeModuleDebugInfo | PdbToXmlOptions.IncludeTokens;\r
+\r
+            PdbToXmlConverter.ToXml(sw, pdbStream, peStream, options);\r
+        }\r
+\r
+        /*\r
+         * Extract\r
+         */\r
+        private static void Extract(Args args, List<string> xmlList)\r
+        {\r
+            string logFile = args.ExceptionPath;\r
+            string outputPath = args.OutputPath;\r
+\r
+            if (xmlList.Count == 0)\r
+            {\r
+                throw new FileNotFoundException("Xml file not found\n");\r
+            }\r
+\r
+            GetLineFromLog(logFile, xmlList, outputPath);\r
+\r
+            if (Directory.Exists(tempDirectory))\r
+            {\r
+                Directory.Delete(tempDirectory, true);\r
+            }\r
+        }\r
+\r
+        internal sealed class StackTraceInfo\r
+        {\r
+            public string Type;\r
+            public string Method;\r
+            public string Param;\r
+            public string Assembly;\r
+            public string Token;\r
+            public string Offset;\r
+            public string Document;\r
+            public string Filepath;\r
+            public string Filename;\r
+            public string StartLine;\r
+            public string EndLine;\r
+        }\r
+\r
+        private static void GetLineFromLog(string logPath, List<string> xmlList, string outputPath)\r
+        {\r
+            Console.WriteLine("Extraction result:      ");\r
+            try\r
+            {\r
+                using StreamReader fsr = new StreamReader(new FileStream(logPath, FileMode.Open, FileAccess.Read));\r
+                using StreamWriter fsw = new StreamWriter(new FileStream(outputPath, FileMode.Create, FileAccess.Write));\r
+\r
+                bool isParsed = false;\r
+                while (!fsr.EndOfStream)\r
+                {\r
+                    string line = fsr.ReadLine();\r
+                    if (!line.Contains(" at "))\r
+                    {\r
+                        continue;\r
+                    }\r
+                    string typeMethodStr = Regex.Match(line, " at (?<splitdata>.*?)\\(\\)").Groups["splitdata"].Value;\r
+                    string methodStr = typeMethodStr.Split(".")[^1];\r
+                    string typenameStr = typeMethodStr.Replace("." + methodStr, "");\r
+                    string assemblyStr = Regex.Match(line, " in (?<splitdata>.*?)\\: ").Groups["splitdata"].Value;\r
+                    string tokenStr = Regex.Match(line, " method_token\\((?<splitdata>.*?)\\)\\,").Groups["splitdata"].Value;\r
+                    string offsetStr = Regex.Match(line, " il_offset\\((?<splitdata>.*?)\\)").Groups["splitdata"].Value;\r
+                    string xmlStr = assemblyStr.Replace(".dll", ".xml");\r
+\r
+                    StackTraceInfo stInfo = new StackTraceInfo() { Type = typenameStr, Method = methodStr, Assembly = assemblyStr, Token = tokenStr, Offset = offsetStr/*, Xml = xmlStr*/ };\r
+\r
+                    foreach (var xmlPath in xmlList)\r
+                    {\r
+                        if (xmlPath.Contains(xmlStr))\r
+                        {\r
+                            isParsed = true;\r
+                            GetLineFromXml(xmlPath, stInfo);\r
+                            if (stInfo.Filepath == null || stInfo.StartLine == null)\r
+                            {\r
+                                Console.WriteLine("    ===== PARSE ERROR FOR EXCEPTION LOG IN THIS LINE. PLEASE RECHECK THE EXCEPTION LOG =====");\r
+                                break;\r
+                            }\r
+                            string ret = $" at {stInfo.Type}.{stInfo.Method}({stInfo.Param}) in {stInfo.Filepath}:line {stInfo.StartLine}";\r
+                            fsw.WriteLine(ret);\r
+                            Console.WriteLine(ret);\r
+                        }\r
+                    }\r
+                }\r
+                if (!isParsed)\r
+                {\r
+                    Console.WriteLine(" There is no content matching the exception log.");\r
+                    Console.WriteLine(" Please recheck the assembly and pdb directory path.\n");\r
+                }\r
+                else\r
+                {\r
+                    Console.WriteLine($"\nOutput: {outputPath}\n");\r
+                }\r
+            }\r
+            catch (Exception e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+\r
+        private static void GetLineFromXml(string xmlPath, StackTraceInfo stInfo)\r
+        {\r
+            try\r
+            {\r
+                XmlDocument xmlDoc = new XmlDocument();\r
+                xmlDoc.Load(xmlPath);\r
+                XmlElement xRoot = xmlDoc.DocumentElement;\r
+                XmlNodeList xnList = xRoot.ChildNodes;\r
+                int xnCount = xnList.Count;\r
+                if (xnCount > 0)\r
+                {\r
+                    for (int i = xnCount - 1; i >= 0; i--)\r
+                    {\r
+                        XmlNode node = xnList[i];\r
+                        if (node.Name == "files")\r
+                        {\r
+                            ParseFile(node.ChildNodes, stInfo);\r
+                        }\r
+                        else if (node.Name == "methods")\r
+                        {\r
+                            ParseMethod(node.ChildNodes, stInfo);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+            catch (ArgumentException e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+\r
+        private static void ParseFile(XmlNodeList xn, StackTraceInfo stInfo)\r
+        {\r
+            try\r
+            {\r
+                foreach (XmlNode node in xn)\r
+                {\r
+                    if (stInfo.Document == node.Attributes["id"].Value)\r
+                    {\r
+                        stInfo.Filepath = node.Attributes["name"].Value;\r
+                        stInfo.Filename = Path.GetFileName(node.Attributes["name"].Value);\r
+                    }\r
+                }\r
+            }\r
+            catch (ArgumentException e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+\r
+        private static void ParseMethod(XmlNodeList xn, StackTraceInfo stInfo)\r
+        {\r
+            try\r
+            {\r
+                foreach (XmlNode node in xn)\r
+                {\r
+                    if (stInfo.Type == node.Attributes["containingType"].Value &&\r
+                        stInfo.Method == node.Attributes["name"].Value &&\r
+                        stInfo.Token == node.Attributes["token"].Value)\r
+                    {\r
+                        if (node.Attributes.Item(2).Name == "parameterNames")\r
+                        {\r
+                            stInfo.Param = node.Attributes["parameterNames"].Value;\r
+                        }\r
+                        ParseSequence(node.ChildNodes, stInfo);\r
+                    }\r
+                }\r
+            }\r
+            catch (ArgumentException e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+\r
+        private static void ParseSequence(XmlNodeList xn, StackTraceInfo stInfo)\r
+        {\r
+            try\r
+            {\r
+                foreach (XmlNode node in xn)\r
+                {\r
+                    if (node.Name == "sequencePoints")\r
+                    {\r
+                        ParseEntry(node.ChildNodes, stInfo);\r
+                    }\r
+                }\r
+            }\r
+            catch (ArgumentException e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+\r
+        private static void ParseEntry(XmlNodeList xn, StackTraceInfo stInfo)\r
+        {\r
+            try\r
+            {\r
+                foreach (XmlNode node in xn)\r
+                {\r
+                    if (stInfo.Offset == node.Attributes["offset"].Value)\r
+                    {\r
+                        if (node.Attributes.Item(1).Name == "startLine")\r
+                        {\r
+                            stInfo.StartLine = node.Attributes["startLine"].Value;\r
+                        }\r
+                        if (node.Attributes.Item(3).Name == "endLine")\r
+                        {\r
+                            stInfo.EndLine = node.Attributes["endLine"].Value;\r
+                        }\r
+                        stInfo.Document = node.Attributes["document"].Value;\r
+                    }\r
+                }\r
+            }\r
+            catch (ArgumentException e)\r
+            {\r
+                Console.WriteLine(e);\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/Microsoft.DiaSymReader.Converter.Xml.dll b/tools/Extractor/dotnet-extractor/dotnet-extractor/Microsoft.DiaSymReader.Converter.Xml.dll
new file mode 100644 (file)
index 0000000..0d204d2
Binary files /dev/null and b/tools/Extractor/dotnet-extractor/dotnet-extractor/Microsoft.DiaSymReader.Converter.Xml.dll differ
diff --git a/tools/Extractor/dotnet-extractor/dotnet-extractor/dotnet-extractor.csproj b/tools/Extractor/dotnet-extractor/dotnet-extractor/dotnet-extractor.csproj
new file mode 100644 (file)
index 0000000..6b388ca
--- /dev/null
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+  <PropertyGroup>\r
+    <OutputType>Exe</OutputType>\r
+    <TargetFramework>netcoreapp3.1</TargetFramework>\r
+    <RootNamespace>dotnet_extractor</RootNamespace>\r
+  </PropertyGroup>\r
+\r
+  <ItemGroup>\r
+    <PackageReference Include="Microsoft.DiaSymReader" Version="1.3.0" />\r
+    <PackageReference Include="Microsoft.DiaSymReader.PortablePdb" Version="1.5.0" />\r
+  </ItemGroup>\r
+\r
+  <ItemGroup>\r
+    <Reference Include="Microsoft.DiaSymReader.Converter.Xml">\r
+      <HintPath>Microsoft.DiaSymReader.Converter.Xml.dll</HintPath>\r
+    </Reference>\r
+  </ItemGroup>\r
+\r
+</Project>\r
similarity index 100%
rename from tools/README
rename to tools/Performance/README