summaryrefslogtreecommitdiff
path: root/src/ToolBox/superpmi/mcs
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/ToolBox/superpmi/mcs
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/ToolBox/superpmi/mcs')
-rw-r--r--src/ToolBox/superpmi/mcs/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/mcs/CMakeLists.txt76
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.cpp581
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.h76
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.h9
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.cpp68
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.h19
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.cpp99
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.cpp36
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.cpp64
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.cpp32
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.cpp625
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.h24
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.cpp37
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.cpp470
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.h29
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.cpp145
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.cpp96
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.h22
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.cpp150
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.h18
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.h17
34 files changed, 3194 insertions, 0 deletions
diff --git a/src/ToolBox/superpmi/mcs/.gitmirror b/src/ToolBox/superpmi/mcs/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/mcs/CMakeLists.txt b/src/ToolBox/superpmi/mcs/CMakeLists.txt
new file mode 100644
index 0000000000..7e6ff70471
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/CMakeLists.txt
@@ -0,0 +1,76 @@
+project(mcs)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+set(MCS_SOURCES
+ commandline.cpp
+ mcs.cpp
+ verbasmdump.cpp
+ verbconcat.cpp
+ verbdump.cpp
+ verbdumpmap.cpp
+ verbdumptoc.cpp
+ verbfracture.cpp
+ verbildump.cpp
+ verbinteg.cpp
+ verbmerge.cpp
+ verbremovedup.cpp
+ verbsmarty.cpp
+ verbstat.cpp
+ verbstrip.cpp
+ verbtoc.cpp
+ ../superpmi-shared/asmdumper.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextiterator.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ MCS_SOURCES
+)
+
+add_executable(mcs
+ ${MCS_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(mcs
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(mcs
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/mcs.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS mcs DESTINATION .)
diff --git a/src/ToolBox/superpmi/mcs/commandline.cpp b/src/ToolBox/superpmi/mcs/commandline.cpp
new file mode 100644
index 0000000000..ea6a4a3e72
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.cpp
@@ -0,0 +1,581 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.cpp - tiny very specific command line parser
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "commandline.h"
+#include "logging.h"
+#include "mclist.h"
+
+void CommandLine::DumpHelp(const char* program)
+{
+ printf("MCS is a utility for examining and manipulating SuperPMI MC files.\n");
+ printf("\n");
+ printf("Usage: %s [options] {verb} {verb options}", program);
+ printf("\n");
+ printf("Options:\n");
+ printf("\n");
+ printf(" -v[erbosity] messagetypes\n");
+ printf(" Controls which types of messages MCS logs. Specify a string of\n");
+ printf(" characters representing message categories to enable, where:\n");
+ printf(" e - errors (internal fatal errors that are non-recoverable)\n");
+ printf(" w - warnings (internal conditions that are unusual, but not serious)\n");
+ printf(" m - missing (failures due to missing JIT-EE interface details)\n");
+ printf(" n - information (notifications/summaries, e.g. 'Loaded 42, Saved 23')\n");
+ printf(" v - verbose (status messages, e.g. 'Jit startup took 151.12ms')\n");
+ printf(" d - debug (lots of detailed output)\n");
+ printf(" a - all (enable all message types; overrides other enable message types)\n");
+ printf(" q - quiet (disable all output; overrides all others)\n");
+ printf(" e.g. '-v ew' only writes error and warning messages to the console.\n");
+ printf(" 'q' takes precedence over any other message type specified.\n");
+ printf(" Default set of messages enabled is 'ewmnv'.\n");
+ printf("\n");
+ printf(" -writeLogFile logfile\n");
+ printf(" Write log messages to the specified file.\n");
+ printf("\n");
+ printf("Verbs:\n");
+ printf("\n");
+ printf(" -ASMDump {optional range} inputfile outputfile\n");
+ printf(" Dump out the asm file for each input methodContext.\n");
+ printf(" inputfile is read and output is written to outputfile.\n");
+ printf(" e.g. -ASMDump a.mc a.asm\n");
+ printf("\n");
+ printf(" -concat file1 file2\n");
+ printf(" Concatenate two files without regard to internal formatting.\n");
+ printf(" file2 is appended to file1.\n");
+ printf(" e.g. -concat a.mch b.mch\n");
+ printf("\n");
+ printf(" -copy range file1 file2\n");
+ printf(" Copy methodContext numbers in range from file1 to file2.\n");
+ printf(" file1 is read and file2 is written\n");
+ printf(" e.g. -copy a.mch b.mch\n");
+ printf("\n");
+ printf(" -dump {optional range} inputfile\n");
+ printf(" Dump details for each methodContext\n");
+ printf(" e.g. -dump a.mc\n");
+ printf("\n");
+ printf(" -dumpMap inputfile\n");
+ printf(" Dump a map from MC index to function name to the console, in CSV format\n");
+ printf(" e.g. -dumpMap a.mc\n");
+ printf("\n");
+ printf(" -dumpToc inputfile\n");
+ printf(" Dump a TOC file\n");
+ printf(" e.g. -dumpToc a.mct\n");
+ printf("\n");
+ printf(" -fracture range inputfile outputfile\n");
+ printf(" Break the input file into chunks sized by range.\n");
+ printf(" If '-thin' is also passed, CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. '-fracture 3 a.mch b-' leads to b-0.mch, b-1.mch, etc., with 3 mc's in each file.\n");
+ printf("\n");
+ printf(" -ildump {optional range} inputfile\n");
+ printf(" Dump raw IL for each methodContext\n");
+ printf(" e.g. -ildump a.mc\n");
+ printf("\n");
+ printf(" -integ inputfile\n");
+ printf(" Check the integrity of each methodContext\n");
+ printf(" e.g. -integ a.mc\n");
+ printf("\n");
+ printf(" -merge outputfile pattern\n");
+ printf(" Merge all the input files matching the pattern.\n");
+ printf(" e.g. -merge a.mch *.mc\n");
+ printf(" e.g. -merge a.mch c:\\foo\\bar\\*.mc\n");
+ printf(" e.g. -merge a.mch relpath\\*.mc\n");
+ printf(" e.g. -merge a.mch .\n");
+ printf(" e.g. -merge a.mch onedir\n");
+ printf("\n");
+ printf(" -merge outputfile pattern -recursive\n");
+ printf(" Merge all the input files matching the pattern, in the specified and all child directories.\n");
+ printf(" e.g. -merge a.mch *.mc -recursive\n");
+ printf("\n");
+ printf(" -removeDup inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" e.g. -removeDup a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -legacy inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" Comparisons are performed using the legacy method and may take much longer\n");
+ printf(" e.g. -removeDup -legacy a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -thin inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. -removeDup -thin a.mc b.mc\n");
+ printf(" e.g. -removeDup -legacy -thin a.mc b.mc\n");
+ printf("\n");
+ printf(" -smarty range inputfile outputfile\n");
+ printf(" Write smarty Test IDs of the range from inputfile to outputfile.\n");
+ printf(" e.g. -smarty 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -stat {optional range} inputfile outputfile\n");
+ printf(" Report various statistics per method context.\n");
+ printf(" inputfile is read and statistics are written into outputfile\n");
+ printf(" e.g. -stat a.mc a.csv\n");
+ printf("\n");
+ printf(" -strip range inputfile outputfile\n");
+ printf(" Copy method contexts from one file to another, skipping ranged items.\n");
+ printf(" inputfile is read and records not in range are written to outputfile.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -toc inputfile\n");
+ printf(" Create a Table of Contents file for inputfile to allow better random access\n");
+ printf(" to the mch file.\n");
+ printf(" e.g. '-toc a.mch' creates a.mch.mct\n");
+ printf("\n");
+ printf("Range descriptions are either a single number, or a text file with .mcl extension\n");
+ printf("containing a sorted list of line delimited numbers.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf(" e.g. -strip list.mcl a.mc b.mc\n");
+ printf("\n");
+ printf("Note: Inputs are case insensitive.\n");
+}
+
+//Assumption: All inputs are initialized to default or real value. we'll just set the stuff in what we see on the command line.
+//Assumption: Single byte names are passed in.. mb stuff doesnt cause an obvious problem... but it might have issues...
+//Assumption: Values larger than 2^31 aren't expressible from the commandline.... (atoi) Unless you pass in negatives.. :-|
+bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
+{
+ size_t argLen = 0;
+ size_t tempLen = 0;
+
+ bool foundVerb = false;
+ bool foundFile1 = false;
+ bool foundFile2 = false;
+
+ if (argc == 1) //Print help when no args are passed
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ for (int i = 1; i<argc; i++)
+ {
+ bool isASwitch = (argv[i][0] == '-');
+#ifndef FEATURE_PAL
+ if (argv[i][0] == '/') // Also accept "/" on Windows
+ {
+ isASwitch = true;
+ }
+#endif // !FEATURE_PAL
+
+ //Process a switch
+ if (isASwitch)
+ {
+ argLen = strlen(argv[i]);
+
+ if (argLen >1)
+ argLen--; //adjust for leading switch
+ else
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if ((_strnicmp(&argv[i][1], "help", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "?", argLen) == 0))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+ else if ((_strnicmp(&argv[i][1], "ASMDump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionASMDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "concat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionConcat = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "copy", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionCopy = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "fracture", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionFracture = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumpmap", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpMap = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumptoc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpToc = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "ildump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionILDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "merge", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionMerge = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "recursive", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->recursive = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "toc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionTOC = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "input", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processInput:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, name of file missing.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (foundFile1 == false)
+ {
+ o->nameOfFile1 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile1, tempLen + 1, argv[i]);
+ foundFile1 = true;
+ }
+ else if (foundFile2 == false)
+ {
+ o->nameOfFile2 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile2, tempLen + 1, argv[i]);
+ foundFile2 = true;
+ }
+ else
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, too many files given.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ else if ((_strnicmp(&argv[i][1], "integ", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionInteg = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "mcl", argLen) == 0))
+ {
+ if (i + 1 >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processMCL:
+ i++;
+ processMCL2:
+
+ bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
+ if (!isValidList)
+ i--;
+ }
+ else if ((_strnicmp(&argv[i][1], "removeDup", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionRemoveDup = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "stat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStat = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "strip", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStrip = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "thin", argLen) == 0))
+ {
+ o->stripCR = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "legacy", argLen) == 0))
+ {
+ o->legacyCompare = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "smarty", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->actionSmarty = true;
+ foundVerb = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::SetLogLevel(Logger::ParseLogLevelString(argv[i]));
+ }
+ else if ((_strnicmp(&argv[i][1], "writeLogFile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::OpenLogFile(argv[i]);
+ }
+ else
+ {
+ LogError("CommandLine::Parse() - Unknown verb '%s'", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ }
+ //Process an input filename
+ else
+ {
+ char *lastdot = strrchr(argv[i], '.');
+ if (lastdot != nullptr)
+ {
+ if (_stricmp(lastdot, ".mcl") == 0)
+ goto processMCL2;
+ }
+ goto processInput;
+ }
+ }
+
+ if (o->recursive)
+ {
+ if (!o->actionMerge)
+ {
+ LogError("CommandLine::Parse() '-recursive' requires -merge.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+
+ if (o->actionASMDump)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -ASMDump needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionConcat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-concat' needs two input files (second will be used as output).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionMerge)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-merge' needs an output file (the first) and a file pattern (the second).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionCopy)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-copy' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -copy requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dump' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionFracture)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-fracture' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -fracture requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 1)
+ {
+ LogWarning("CommandLine::Parse() -fracture found multiple ranges, we'll use the first one.");
+ }
+ return true;
+ }
+ if (o->actionDumpMap)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpMap' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDumpToc)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpToc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionILDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-ildump' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionTOC)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-toc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionInteg)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-integ' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionRemoveDup)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -removeDup needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-stat' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStrip)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -strip needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -strip requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionSmarty)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-smarty' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+
+ DumpHelp(argv[0]);
+ return false;
+}
diff --git a/src/ToolBox/superpmi/mcs/commandline.h b/src/ToolBox/superpmi/mcs/commandline.h
new file mode 100644
index 0000000000..a04969696b
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.h - tiny very specific command line parser
+//----------------------------------------------------------
+#ifndef _CommandLine
+#define _CommandLine
+
+class CommandLine
+{
+public:
+
+ class Options
+ {
+ public:
+ Options() :
+ actionASMDump(false),
+ actionConcat(false),
+ actionCopy(false),
+ actionDump(false),
+ actionDumpMap(false),
+ actionDumpToc(false),
+ actionFracture(false),
+ actionILDump(false),
+ actionInteg(false),
+ actionMerge(false),
+ actionRemoveDup(false),
+ actionSmarty(false),
+ actionStat(false),
+ actionStrip(false),
+ actionTOC(false),
+ legacyCompare(false),
+ recursive(false),
+ stripCR(false),
+ nameOfFile1(nullptr),
+ nameOfFile2(nullptr),
+ nameOfFile3(nullptr),
+ indexCount(-1),
+ indexes(nullptr)
+ {
+ }
+
+ bool actionASMDump;
+ bool actionConcat;
+ bool actionCopy;
+ bool actionDump;
+ bool actionDumpMap;
+ bool actionDumpToc;
+ bool actionFracture;
+ bool actionILDump;
+ bool actionInteg;
+ bool actionMerge;
+ bool actionRemoveDup;
+ bool actionSmarty;
+ bool actionStat;
+ bool actionStrip;
+ bool actionTOC;
+ bool legacyCompare;
+ bool recursive;
+ bool stripCR;
+ char* nameOfFile1;
+ char* nameOfFile2;
+ char* nameOfFile3;
+ int indexCount;
+ int* indexes;
+ };
+
+ static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
+
+private:
+ static void DumpHelp(const char* program);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/mcs.cpp b/src/ToolBox/superpmi/mcs/mcs.cpp
new file mode 100644
index 0000000000..d2debdd90f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "mcs.h"
+#include "commandline.h"
+#include "verbasmdump.h"
+#include "verbinteg.h"
+#include "verbdump.h"
+#include "verbfracture.h"
+#include "verbdumpmap.h"
+#include "verbdumptoc.h"
+#include "verbildump.h"
+#include "verbtoc.h"
+#include "verbremovedup.h"
+#include "verbstat.h"
+#include "verbconcat.h"
+#include "verbmerge.h"
+#include "verbstrip.h"
+#include "verbsmarty.h"
+#include "logging.h"
+
+int __cdecl main(int argc, char* argv[])
+{
+#ifdef FEATURE_PAL
+ if (0 != PAL_Initialize(argc, argv))
+ {
+ fprintf(stderr, "Error: Fail to PAL_Initialize\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+
+ CommandLine::Options o;
+ if(!CommandLine::Parse(argc, argv, &o))
+ {
+ return -1;
+ }
+
+ //execute the chosen command.
+ int exitCode = 0;
+ if (o.actionASMDump)
+ {
+ exitCode = verbASMDump::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionConcat)
+ {
+ exitCode = verbConcat::DoWork(o.nameOfFile1, o.nameOfFile2);
+ }
+ if (o.actionMerge)
+ {
+ exitCode = verbMerge::DoWork(o.nameOfFile1, o.nameOfFile2, o.recursive);
+ }
+ if (o.actionCopy)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, false, o.stripCR);
+ }
+ if (o.actionDump)
+ {
+ exitCode = verbDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionFracture)
+ {
+ exitCode = verbFracture::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, o.stripCR);
+ }
+ if (o.actionDumpMap)
+ {
+ exitCode = verbDumpMap::DoWork(o.nameOfFile1);
+ }
+ if (o.actionDumpToc)
+ {
+ exitCode = verbDumpToc::DoWork(o.nameOfFile1);
+ }
+ if (o.actionILDump)
+ {
+ exitCode = verbILDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionInteg)
+ {
+ exitCode = verbInteg::DoWork(o.nameOfFile1);
+ }
+ if (o.actionRemoveDup)
+ {
+ exitCode = verbRemoveDup::DoWork(o.nameOfFile1, o.nameOfFile2, o.stripCR, o.legacyCompare);
+ }
+ if (o.actionStat)
+ {
+ exitCode = verbStat::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionStrip)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, true, o.stripCR);
+ }
+ if (o.actionTOC)
+ {
+ exitCode = verbTOC::DoWork(o.nameOfFile1);
+ }
+ if (o.actionSmarty)
+ {
+ exitCode = verbSmarty::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+
+ Logger::Shutdown();
+ return exitCode;
+}
diff --git a/src/ToolBox/superpmi/mcs/mcs.h b/src/ToolBox/superpmi/mcs/mcs.h
new file mode 100644
index 0000000000..3eb19502db
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.h
@@ -0,0 +1,9 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _MCS
+#define _MCS
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.cpp b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
new file mode 100644
index 0000000000..3f018b3a45
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbasmdump.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "asmdumper.h"
+#include "errorhandling.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbASMDump::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Loading from '%s' and writing ASM output into '%s-MC#.asm'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ char buff[500];
+ sprintf_s(buff, 500, "%s-%d.asm", nameOfOutput, mci.MethodContextNumber());
+
+ HANDLE hFileOut = CreateFileA(buff, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", buff, GetLastError());
+ return -1;
+ }
+
+ if (mc->cr->IsEmpty())
+ {
+ const size_t bufflen = 4096;
+ DWORD bytesWritten;
+ char buff[bufflen];
+ ZeroMemory(buff, bufflen * sizeof(char));
+ int buff_offset = sprintf_s(buff, bufflen, ";;Method context has no compile result");
+ WriteFile(hFileOut, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+ }
+ else
+ {
+ ASMDumper::DumpToFile(hFileOut, mc, mc->cr);
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ savedCount++;
+ }
+
+ LogInfo("Asm'd %d", savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.h b/src/ToolBox/superpmi/mcs/verbasmdump.h
new file mode 100644
index 0000000000..693366ef91
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbASMDump.h - verb that
+//----------------------------------------------------------
+#ifndef _verbASMDump
+#define _verbASMDump
+
+class verbASMDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
+
+
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.cpp b/src/ToolBox/superpmi/mcs/verbconcat.cpp
new file mode 100644
index 0000000000..36620f11b2
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.cpp
@@ -0,0 +1,99 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbconcat.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbConcat::DoWork(const char *nameOfFile1, const char *nameOfFile2)
+{
+ SimpleTimer st1;
+
+ LogVerbose("Concatenating '%s'+'%s' into %s", nameOfFile1, nameOfFile2, nameOfFile1);
+
+ LARGE_INTEGER DataTemp1;
+ LARGE_INTEGER DataTemp2;
+
+ HANDLE hFileIn1 = CreateFileA(nameOfFile1, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn1 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn1, &DataTemp1)==0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LONG highDWORD = 0;
+ DWORD dwPtr = SetFilePointer(hFileIn1, 0, &highDWORD, FILE_END);
+ if (dwPtr == INVALID_SET_FILE_POINTER)
+ {
+ LogError("Failed to SetFilePointer on input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+
+ HANDLE hFileIn2 = CreateFileA(nameOfFile2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn2 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 2 '%s'. GetLastError()=%u", nameOfFile2, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn2, &DataTemp2)==0)
+ {
+ LogError("2nd GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+
+ st1.Start();
+ for(LONGLONG offset = 0; offset < DataTemp2.QuadPart; offset += BUFFER_SIZE)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn2, buffer, BUFFER_SIZE, &bytesRead, nullptr);
+ if(res == 0)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", nameOfFile2, offset, GetLastError());
+ return -1;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileIn1, buffer, bytesRead, &bytesWritten, nullptr);
+ if(res2 == 0)
+ {
+ LogError("Failed to write '%s' at offset %lld. GetLastError()=%u", nameOfFile1, offset, GetLastError());
+ return -1;
+ }
+ if(bytesRead!=bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ return -1;
+ }
+ }
+ st1.Stop();
+
+ delete[] buffer;
+
+ if(CloseHandle(hFileIn1)==0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ if(CloseHandle(hFileIn2)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.\n",
+ DataTemp2.QuadPart/(1000*1000),
+ (((double)DataTemp2.QuadPart)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.h b/src/ToolBox/superpmi/mcs/verbconcat.h
new file mode 100644
index 0000000000..26e55857bd
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbConcat.h - verb that concatenates two files
+//----------------------------------------------------------
+#ifndef _verbConcat
+#define _verbConcat
+
+class verbConcat
+{
+public:
+ static int DoWork(const char *nameOfFile1, const char *nameOfFile2);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdump.cpp b/src/ToolBox/superpmi/mcs/verbdump.cpp
new file mode 100644
index 0000000000..290cbdbac5
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.cpp
@@ -0,0 +1,36 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbdump.h"
+#include "logging.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("Dumping '%s' to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ mc->dumpToConsole(mci.MethodContextNumber());
+ dumpedCount++;
+ }
+
+ LogVerbose("Dumped %d methodContexts", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdump.h b/src/ToolBox/superpmi/mcs/verbdump.h
new file mode 100644
index 0000000000..32f4b0d661
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDump.h - verb that Dumps a MC file
+//----------------------------------------------------------
+#ifndef _verbDump
+#define _verbDump
+
+class verbDump
+{
+public:
+ static int DoWork(const char *nameofInput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
new file mode 100644
index 0000000000..3fc8268f5f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbdumpmap.h"
+#include "verbildump.h"
+
+// Dump the CSV format header for all the columns we're going to dump.
+void DumpMapHeader()
+{
+ printf("index,");
+ // printf("process name,");
+ printf("method name,");
+ printf("full signature\n");
+}
+
+void DumpMap(int index, MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("%d,", index);
+ // printf("\"%s\",", mc->cr->repProcessName());
+ printf("%s:%s,", className, methodName);
+
+ // Also, dump the full method signature
+ printf("\"");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\"\n");
+}
+
+int verbDumpMap::DoWork(const char *nameOfInput)
+{
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ DumpMapHeader();
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpMap(mci.MethodContextNumber(), mc);
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.h b/src/ToolBox/superpmi/mcs/verbdumpmap.h
new file mode 100644
index 0000000000..129b8d8440
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpMap.h - verb that dumps a map of index to function name for an MC file
+//----------------------------------------------------------
+#ifndef _verbDumpMap
+#define _verbDumpMap
+
+class verbDumpMap
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.cpp b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
new file mode 100644
index 0000000000..2837f0bf3e
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbdumptoc.h"
+#include "methodcontext.h"
+#include "tocfile.h"
+#include "runtimedetails.h"
+
+int verbDumpToc::DoWork(const char *nameOfInput)
+{
+ TOCFile tf;
+
+ tf.LoadToc(nameOfInput, false);
+
+ for (size_t i = 0; i < tf.GetTocCount(); i++)
+ {
+ const TOCElement* te = tf.GetElementPtr(i);
+ printf("%4u: %016llX ", te->Number, te->Offset);
+
+ for (int j = 0; j < sizeof(te->Hash); j++)
+ {
+ printf("%02x ", te->Hash[j]);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.h b/src/ToolBox/superpmi/mcs/verbdumptoc.h
new file mode 100644
index 0000000000..c2ea880b52
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpToc.h - verb that dumps a MCH Table of Context file
+//----------------------------------------------------------
+#ifndef _verbDumpToc
+#define _verbDumpToc
+
+class verbDumpToc
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.cpp b/src/ToolBox/superpmi/mcs/verbfracture.cpp
new file mode 100644
index 0000000000..bcc6d04e1d
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbfracture.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+int verbFracture::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ int rangeSize = indexes[0];
+
+ LogVerbose("Reading from '%s' copying %d MethodContexts files into each output file of '%s'", nameOfInput, rangeSize, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int fileCount = 0;
+ char fileName[512];
+
+ HANDLE hFileOut = INVALID_HANDLE_VALUE;
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ if ((hFileOut == INVALID_HANDLE_VALUE) || (((mci.MethodContextNumber() - 1) % rangeSize) == 0))
+ {
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("1st CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ hFileOut = INVALID_HANDLE_VALUE;
+ }
+ sprintf_s(fileName, 512, "%s-%0*d.mch", nameOfOutput, 5, fileCount++);
+ hFileOut = CreateFileA(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+ }
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ }
+
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ }
+
+ LogInfo("Output fileCount %d", fileCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.h b/src/ToolBox/superpmi/mcs/verbfracture.h
new file mode 100644
index 0000000000..feddbe1908
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbFracture.h - verb that copies N items into each child file.
+//----------------------------------------------------------
+#ifndef _verbFracture
+#define _verbFracture
+
+class verbFracture
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbildump.cpp b/src/ToolBox/superpmi/mcs/verbildump.cpp
new file mode 100644
index 0000000000..23b68da003
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.cpp
@@ -0,0 +1,625 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbildump.h"
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle)
+{
+ switch(prim)
+ {
+ case CORINFO_TYPE_VOID: printf("void"); return;
+ case CORINFO_TYPE_BOOL: printf("bool"); return;
+ case CORINFO_TYPE_CHAR: printf("char"); return;
+ case CORINFO_TYPE_BYTE: printf("int8"); return;
+ case CORINFO_TYPE_UBYTE: printf("unsigned int8"); return;
+ case CORINFO_TYPE_SHORT: printf("int16"); return;
+ case CORINFO_TYPE_USHORT: printf("unsigned int16"); return;
+ case CORINFO_TYPE_INT: printf("int32"); return;
+ case CORINFO_TYPE_UINT: printf("unsigned int32"); return;
+ case CORINFO_TYPE_LONG: printf("int64"); return;
+ case CORINFO_TYPE_ULONG: printf("unsigned int64"); return;
+ case CORINFO_TYPE_NATIVEINT: printf("native int"); return;
+ case CORINFO_TYPE_NATIVEUINT: printf("native unsigned int"); return;
+ case CORINFO_TYPE_FLOAT: printf("float32"); return;
+ case CORINFO_TYPE_DOUBLE: printf("float64"); return;
+// case CORINFO_TYPE_STRING: printf("string"); return;
+ case CORINFO_TYPE_PTR: printf("ptr"); return;
+ case CORINFO_TYPE_BYREF: printf("byref"); return;
+ case CORINFO_TYPE_VALUECLASS: printf("valueclass %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_CLASS: printf("class %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_REFANY: printf("refany"); return;
+ case CORINFO_TYPE_VAR: printf("var"); return;
+ default:
+ LogWarning("unknown type in PrimToString(0x%x)",prim);
+ __debugbreak();
+ return;
+ }
+}
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig)
+{
+ CORINFO_ARG_LIST_HANDLE currentItem = pSig->args;
+ DWORD exceptionCode;
+
+ for(int i=0; i < (int)pSig->numArgs; i++)
+ {
+ DWORDLONG dl;
+ CorInfoTypeWithMod type = mc->repGetArgType(pSig, currentItem, (CORINFO_CLASS_HANDLE *)&dl, &exceptionCode);
+ CorInfoType cit = strip(type);
+ if (cit == CORINFO_TYPE_CLASS)
+ dl = (DWORDLONG)mc->repGetArgClass(pSig, currentItem, &exceptionCode);
+ if ((type & CORINFO_TYPE_MOD_PINNED) == CORINFO_TYPE_MOD_PINNED)
+ printf("pinned ");
+ DumpPrimToConsoleBare(mc, cit, dl);
+ currentItem = mc->repGetArgNext(currentItem);
+ if (i + 1 < (int)pSig->numArgs)
+ printf(", ");
+ }
+}
+
+
+void DumpILToConsoleBare(unsigned char *ilCode, int len)
+{
+ int i,j,k;
+ for(i=0;i<len;i++)
+ {
+ printf("IL_%04x: ",i);
+ switch(ilCode[i])
+ {
+ case 0x00:printf("nop");continue;
+ case 0x01:printf("break");continue;
+ case 0x02:printf("ldarg.0");continue;
+ case 0x03:printf("ldarg.1");continue;
+ case 0x04:printf("ldarg.2");continue;
+ case 0x05:printf("ldarg.3");continue;
+ case 0x06:printf("ldloc.0");continue;
+ case 0x07:printf("ldloc.1");continue;
+ case 0x08:printf("ldloc.2");continue;
+ case 0x09:printf("ldloc.3");continue;
+ case 0x0a:printf("stloc.0");continue;
+ case 0x0b:printf("stloc.1");continue;
+ case 0x0c:printf("stloc.2");continue;
+ case 0x0d:printf("stloc.3");continue;
+ case 0x0e: //ldarg.s X
+ printf("ldarg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x0f: //ldarga.s X
+ printf("ldarga.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x10: //starg.s X
+ printf("starg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x11: //ldloc.s X
+ printf("ldloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x12: //ldloca.s X
+ printf("ldloca.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13: //stloc.s X
+ printf("stloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x14:printf("ldnull");continue;
+ case 0x15:printf("ldc.i4.m1");continue;
+ case 0x16:printf("ldc.i4.0");continue;
+ case 0x17:printf("ldc.i4.1");continue;
+ case 0x18:printf("ldc.i4.2");continue;
+ case 0x19:printf("ldc.i4.3");continue;
+ case 0x1a:printf("ldc.i4.4");continue;
+ case 0x1b:printf("ldc.i4.5");continue;
+ case 0x1c:printf("ldc.i4.6");continue;
+ case 0x1d:printf("ldc.i4.7");continue;
+ case 0x1e:printf("ldc.i4.8");continue;
+ case 0x1f: //ldc.i4.s X
+ printf("ldc.i4.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x20: //ldc.i4 XXXX
+ printf("ldc.i4 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x21: //ldc.i8 XXXXXXXX
+ printf("ldc.i8 0x%02x%02x%02x%02x%02x%02x%02x%02x", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x22: //ldc.r4 XXXX
+ printf("ldc.r4 float32(0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x23: //ldc.r8 XXXXXXXX
+ printf("ldc.r8 float64(0x%02x%02x%02x%02x%02x%02x%02x%02x)", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x25:printf("dup");continue;
+ case 0x26:printf("pop");continue;
+ case 0x27: //JMP <T>
+ printf("jmp <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x28: //call <T>
+ printf("call <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x29: //calli <T>
+ printf("calli <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x2a:printf("ret");continue;
+ case 0x2b: //br.s X
+ printf("br.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2c: //brfalse.s X
+ printf("brfalse.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2d: //brtrue.s X
+ printf("brtrue.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2e: //beq.s X
+ printf("beq.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2f: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x30: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x31: //ble.s X
+ printf("ble.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x32: //blt.s X
+ printf("blt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x33: //bne.un.s X
+ printf("bne.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x34: //bge.un.s X
+ printf("bge.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x35: //bgt.un.s X
+ printf("bgt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x36: //ble.un.s X
+ printf("ble.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x37: //blt.un.s X
+ printf("blt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x38: //br XXXX
+ printf("br IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x39: //brfalse XXXX
+ printf("brfalse IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3a: //brtrue XXXX
+ printf("brtrue IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3b: //beq XXXX
+ printf("beq IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3c: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3d: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3e: //ble XXXX
+ printf("ble IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3f: //blt XXXX
+ printf("blt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x40: //bne.un XXXX
+ printf("bne.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x41: //bge.un XXXX
+ printf("bge.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x42: //bgt.un XXXX
+ printf("bgt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x43: //ble.un XXXX
+ printf("ble.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x44: //blt.un XXXX
+ printf("blt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x45: //switch NNNN NNNN*XXXX
+ printf("switch (0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ k = (ilCode[i+4]<<24)|(ilCode[i+3]<<16)|(ilCode[i+2]<<8)|(ilCode[i+1]<<0);
+ i+=4;
+ for(j=0;j<k;j++)
+ {
+ printf(" <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ }
+ continue;
+ case 0x46:printf("ldind.i1");continue;
+ case 0x47:printf("ldind.u1");continue;
+ case 0x48:printf("ldind.i2");continue;
+ case 0x49:printf("ldind.u2");continue;
+ case 0x4a:printf("ldind.i4");continue;
+ case 0x4b:printf("ldind.u4");continue;
+ case 0x4c:printf("ldind.i8");continue;
+ case 0x4d:printf("ldind.u8");continue;
+ case 0x4e:printf("ldind.r4");continue;
+ case 0x4f:printf("ldind.r8");continue;
+ case 0x50:printf("ldind.ref");continue;
+ case 0x51:printf("stind.ref");continue;
+ case 0x52:printf("stind.i1");continue;
+ case 0x53:printf("stind.i2");continue;
+ case 0x54:printf("stind.i4");continue;
+ case 0x55:printf("stind.i8");continue;
+ case 0x56:printf("stind.r4");continue;
+ case 0x57:printf("stind.r8");continue;
+ case 0x58:printf("add");continue;
+ case 0x59:printf("sub");continue;
+ case 0x5a:printf("mul");continue;
+ case 0x5b:printf("div");continue;
+ case 0x5c:printf("div.un");continue;
+ case 0x5d:printf("rem");continue;
+ case 0x5e:printf("rem.un");continue;
+ case 0x5f:printf("and");continue;
+ case 0x60:printf("or");continue;
+ case 0x61:printf("xor");continue;
+ case 0x62:printf("shl");continue;
+ case 0x63:printf("shr");continue;
+ case 0x64:printf("shr.un");continue;
+ case 0x65:printf("neg");continue;
+ case 0x66:printf("not");continue;
+ case 0x67:printf("conv.i1");continue;
+ case 0x68:printf("conv.i2");continue;
+ case 0x69:printf("conv.i4");continue;
+ case 0x6a:printf("conv.i8");continue;
+ case 0x6b:printf("conv.r4");continue;
+ case 0x6c:printf("conv.r8");continue;
+ case 0x6d:printf("conv.u4");continue;
+ case 0x6e:printf("conv.u8");continue;
+ case 0x6f: //callvirt <T>
+ printf("callvirt <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x70: //cpobj <T>
+ printf("cpobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x71: //ldobj <T>
+ printf("ldobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x72: //ldstr <T>
+ printf("ldstr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x73: //newobj <T>
+ printf("newobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x74: //castclass <T>
+ printf("castclass <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x75: //isinst <T>
+ printf("isinst <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x76:printf("conv.r.un");continue;
+ case 0x79: //unbox <T>
+ printf("unbox <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7a:printf("throw");continue;
+ case 0x7b: //ldfld <T>
+ printf("ldfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7c: //ldflda <T>
+ printf("ldflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7d: //stfld <T>
+ printf("stfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7e: //ldsfld <T>
+ printf("ldsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7f: //ldsflda <T>
+ printf("ldsflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x80: //stsfld <T>
+ printf("stsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x81: //stobj <T>
+ printf("stobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x82:printf("conv.ovf.i1.un");continue;
+ case 0x83:printf("conv.ovf.i2.un");continue;
+ case 0x84:printf("conv.ovf.i4.un");continue;
+ case 0x85:printf("conv.ovf.i8.un");continue;
+ case 0x86:printf("conv.ovf.u1.un");continue;
+ case 0x87:printf("conv.ovf.u2.un");continue;
+ case 0x88:printf("conv.ovf.u4.un");continue;
+ case 0x89:printf("conv.ovf.u8.un");continue;
+ case 0x8a:printf("conv.ovf.i.un");continue;
+ case 0x8b:printf("conv.ovf.u.un");continue;
+ case 0x8c: //box <T>
+ printf("box <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8d: //newarr <T>
+ printf("newarr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8e:printf("ldlen");continue;
+ case 0x8f:printf("ldelema <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x90:printf("ldelem.i1");continue;
+ case 0x91:printf("ldelem.u1");continue;
+ case 0x92:printf("ldelem.i2");continue;
+ case 0x93:printf("ldelem.u2");continue;
+ case 0x94:printf("ldelem.i4");continue;
+ case 0x95:printf("ldelem.u4");continue;
+ case 0x96:printf("ldelem.i8");continue;
+ case 0x97:printf("ldelem.i");continue;
+ case 0x98:printf("ldelem.r4");continue;
+ case 0x99:printf("ldelem.r8");continue;
+ case 0x9a:printf("ldelem.ref");continue;
+ case 0x9b:printf("stelem.i");continue;
+ case 0x9c:printf("stelem.i1");continue;
+ case 0x9d:printf("stelem.i2");continue;
+ case 0x9e:printf("stelem.i4");continue;
+ case 0x9f:printf("stelem.i8");continue;
+ case 0xa0:printf("stelem.r4");continue;
+ case 0xa1:printf("stelem.r8");continue;
+ case 0xa2:printf("stelem.ref");continue;
+ case 0xa3:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa4:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa5:printf("unbox.any <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xb3:printf("conv.ovf.i1");continue;
+ case 0xb4:printf("conv.ovf.u1");continue;
+ case 0xb5:printf("conv.ovf.i2");continue;
+ case 0xb6:printf("conv.ovf.u2");continue;
+ case 0xb7:printf("conv.ovf.i4");continue;
+ case 0xb8:printf("conv.ovf.u4");continue;
+ case 0xb9:printf("conv.ovf.i8");continue;
+ case 0xba:printf("conv.ovf.u8");continue;
+ case 0xc2: //refanyval <T>
+ printf("refanyval <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xc3:printf("ckfinite");continue;
+ case 0xc6: //mkrefany <T>
+ printf("mkrefany <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd0: //ldtoken <T>
+ printf("ldtoken <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd1:printf("conv.u2");continue;
+ case 0xd2:printf("conv.u1");continue;
+ case 0xd3:printf("conv.i");continue;
+ case 0xd4:printf("conv.ovf.i");continue;
+ case 0xd5:printf("conv.ovf.u");continue;
+ case 0xd6:printf("add.ovf");continue;
+ case 0xd7:printf("add.ovf.un");continue;
+ case 0xd8:printf("mul.ovf");continue;
+ case 0xd9:printf("mul.ovf.un");continue;
+ case 0xda:printf("sub.ovf");continue;
+ case 0xdb:printf("sub.ovf.un");continue;
+ case 0xdc:printf("endfinally");continue;
+ case 0xdd: //leave XXXX
+ printf("leave 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xde: //leave.s X
+ printf("leave 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0xdf:printf("stind.i");continue;
+ case 0xe0:printf("conv.u");continue;
+ case 0xfe:
+ i++;
+ switch(ilCode[i])
+ {
+ case 0x00:printf("arglist");continue;
+ case 0x01:printf("ceq");continue;
+ case 0x02:printf("cgt");continue;
+ case 0x03:printf("cgt.un");continue;
+ case 0x04:printf("clt");continue;
+ case 0x05:printf("clt.un");continue;
+ case 0x06: //ldftn <T>
+ printf("ldftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x07: //ldvirtftn <T>
+ printf("ldvirtftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x09: //ldarg XX
+ printf("ldarg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0a: //ldarga XX
+ printf("ldarga 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0b: //starg XX
+ printf("starg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0c: //ldloc XX
+ printf("ldloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0d: //ldloca XX
+ printf("ldloca 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0e: //stloc XX
+ printf("stloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0f:printf("localloc");continue;
+ case 0x11:printf("endfilter");continue;
+ case 0x12: //unaligned X
+ printf("unaligned. 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13:printf("volatile.");continue;
+ case 0x14:printf("tail.");continue;
+ case 0x15: //initobj <T>
+ printf("initobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x16://incomplete?
+ printf("constrained. <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x17:printf("cpblk");continue;
+ case 0x18:printf("initblk");continue;
+ case 0x19:printf("no.");continue; //incomplete?
+ case 0x1a:printf("rethrow");continue;
+ case 0x1c: //sizeof <T>
+ printf("sizeof <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x1d:printf("refanytype");continue;
+ default:
+ LogError("unknown ilCode 0xfe%2x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ default:
+ LogError("unknown ilCode 0x%02x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ printf("\n");
+ }
+}
+char * DumpAttributeToConsoleBare(DWORD attribute)
+{
+ const char *s_static = "static";
+ const char *s_dontInline = "$dontInline ";
+ const char *s_constructor = "$constructor";
+ const char *s_cfnw = "$noSecurityWrap";
+
+#define ifPrint(s,t) else if((s&attribute)==s) {printf(t); printf(" ");}
+
+ if(0);
+ ifPrint(CORINFO_FLG_STATIC, s_static)
+ ifPrint(CORINFO_FLG_DONT_INLINE, s_dontInline)
+ ifPrint(CORINFO_FLG_CONSTRUCTOR, s_constructor)
+ ifPrint(CORINFO_FLG_NOSECURITYWRAP, s_cfnw)
+ else
+ {
+ LogError("unknown attribute %x", attribute);
+ __debugbreak();
+ }
+ return nullptr;
+
+#undef ifPrint
+}
+
+void DumpIL(MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("// ProcessName - '%s'\n", mc->cr->repProcessName());
+ printf(".assembly extern mscorlib{}\n");
+ printf(".assembly %s{}\n", moduleName);
+ printf(".class %s\n", className);
+ printf("{\n");
+ printf(" .method ");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\n");
+ printf(" {\n");
+ printf(" .maxstack %u\n", cmi.maxStack);
+ printf(" .locals%s(", (((cmi.options&CORINFO_OPT_INIT_LOCALS)==CORINFO_OPT_INIT_LOCALS)?" init ":" "));
+ DumpSigToConsoleBare(mc, &cmi.locals);
+ printf(")\n");
+ DumpILToConsoleBare(cmi.ILCode, cmi.ILCodeSize);
+ printf(" }\n");
+ printf("}\n");
+}
+
+int verbILDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("// Reading from '%s' dumping raw IL for MC Indexes to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpIL(mc);
+ dumpedCount++;
+ }
+
+ LogInfo("// Dumped %d", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbildump.h b/src/ToolBox/superpmi/mcs/verbildump.h
new file mode 100644
index 0000000000..e947fcf40c
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbILDump.h - verb that attempts to dump the raw IL for a MC
+//----------------------------------------------------------
+#ifndef _verbILDump
+#define _verbILDump
+
+#include "methodcontext.h"
+
+class verbILDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, int indexCount, const int *indexes);
+};
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle);
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig);
+char * DumpAttributeToConsoleBare(DWORD attribute);
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.cpp b/src/ToolBox/superpmi/mcs/verbinteg.cpp
new file mode 100644
index 0000000000..9b1057769a
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.cpp
@@ -0,0 +1,37 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbinteg.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+int verbInteg::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Checking the integrity of '%s'", nameOfInput);
+
+ SimpleTimer st2;
+ st2.Start();
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ // Nothing to do except load the current one.
+ }
+
+ st2.Stop();
+ LogInfo("Checked the integrity of %d methodContexts at %d per second",
+ mci.MethodContextNumber(), (int)((double)mci.MethodContextNumber() / st2.GetSeconds()));
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.h b/src/ToolBox/superpmi/mcs/verbinteg.h
new file mode 100644
index 0000000000..d45ea15307
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbInteg.h - verb that checks the integrity of a MC file
+//----------------------------------------------------------
+#ifndef _verbInteg
+#define _verbInteg
+
+class verbInteg
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.cpp b/src/ToolBox/superpmi/mcs/verbmerge.cpp
new file mode 100644
index 0000000000..c4acfd8769
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.cpp
@@ -0,0 +1,470 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbmerge.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+// Do reads/writes in large 256MB chunks.
+#define BUFFER_SIZE 0x10000000
+
+// MergePathStrings: take two file system path components, compose them together, and return the merged pathname string.
+// The caller must delete the returned string with delete[].
+//
+// static
+char* verbMerge::MergePathStrings(const char* dir, const char* file)
+{
+ size_t dirlen = strlen(dir);
+ size_t filelen = strlen(file);
+ size_t newlen = dirlen + 1 /* slash */ + filelen + 1 /* null */;
+ char* newpath = new char[newlen];
+ strcpy(newpath, dir);
+ strcat(newpath, DIRECTORY_SEPARATOR_STR_A);
+ strcat(newpath, file);
+ return newpath;
+}
+
+// AppendFile: append the file named by 'fileName' to the output file referred to by 'hFileOut'. The 'hFileOut'
+// handle is assumed to be open, and the file position is assumed to be at the correct spot for writing, to append.
+//
+// 'buffer' is memory that can be used to do reading/buffering.
+//
+// static
+int verbMerge::AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize)
+{
+ int result = 0; // default to zero == success
+
+ LogInfo("Appending file '%s'", fileName);
+
+ HANDLE hFileIn = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileIn == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+
+ LARGE_INTEGER fileSize;
+ if (GetFileSizeEx(hFileIn, &fileSize) == 0)
+ {
+ LogError("GetFileSizeEx on '%s' failed. GetLastError()=%u", fileName, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+
+ for (LONGLONG offset = 0; offset < fileSize.QuadPart; offset += bufferSize)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn, buffer, (DWORD)bufferSize, &bytesRead, nullptr);
+ if (!res)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", fileName, offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileOut, buffer, bytesRead, &bytesWritten, nullptr);
+ if (!res2)
+ {
+ LogError("Failed to write output file at offset %lld. GetLastError()=%u", offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ if (bytesRead != bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ result = -1;
+ goto CLEAN_UP;
+ }
+ }
+
+CLEAN_UP:
+
+ if (CloseHandle(hFileIn) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ return result;
+}
+
+// Return true if this is a directory
+//
+// static
+bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ {
+ // It's a directory. See if we want to exclude it because of other reasons, such as:
+ // 1. reparse points: avoid the possibility of loops
+ // 2. system directories
+ // 3. hidden directories
+ // 4. "." or ".."
+
+#ifndef FEATURE_PAL // FILE_ATTRIBUTE_REPARSE_POINT is not defined in the PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+ return false;
+#endif // !FEATURE_PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0)
+ return false;
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)
+ return false;
+
+ if (strcmp(findData->cFileName, ".") == 0)
+ return false;
+ if (strcmp(findData->cFileName, "..") == 0)
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Return true if this is a file.
+//
+// static
+bool verbMerge::DirectoryFilterFile(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ // This is not a directory, so it must be a file.
+ return true;
+ }
+
+ return false;
+}
+
+// static
+int __cdecl verbMerge::WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2)
+{
+ const WIN32_FIND_DATAA* file1 = (WIN32_FIND_DATAA*)p1;
+ const WIN32_FIND_DATAA* file2 = (WIN32_FIND_DATAA*)p2;
+ return strcmp(file1->cFileName, file2->cFileName);
+}
+
+// Enumerate a directory for the files specified by "searchPattern". For each element in the directory,
+// pass it to the filter function. If the filter returns true, we keep it, otherwise we ignore it. Return
+// an array of information for the files that we kept, sorted by filename.
+//
+// Returns 0 on success, non-zero on failure.
+// If success, fileArray and elemCount are set.
+//
+// static
+int verbMerge::FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount)
+{
+ // First, build up a list, then create an array and sort it after we know how many elements there are.
+ struct findDataList
+ {
+ findDataList(WIN32_FIND_DATAA* newFindData, findDataList* newNext)
+ : findData(*newFindData)
+ , next(newNext)
+ {
+ }
+
+ static void DeleteList(findDataList* root)
+ {
+ for (findDataList* loop = root; loop != nullptr; )
+ {
+ findDataList* tmp = loop;
+ loop = loop->next;
+ delete tmp;
+ }
+ }
+
+ WIN32_FIND_DATAA findData;
+ findDataList* next;
+ };
+
+ WIN32_FIND_DATAA* retArray = nullptr;
+ findDataList* first = nullptr;
+
+ int result = 0; // default to zero == success
+ int elemCount = 0;
+
+ // NOTE: this function only works on Windows 7 and later.
+ WIN32_FIND_DATAA findData;
+ HANDLE hSearch;
+#ifdef FEATURE_PAL
+ // PAL doesn't have FindFirstFileEx(). So just use FindFirstFile(). The only reason we use
+ // the Ex version is potentially better performance (don't populate short name; use large fetch),
+ // not functionality.
+ hSearch = FindFirstFileA(searchPattern, &findData);
+#else // !FEATURE_PAL
+ hSearch = FindFirstFileExA(searchPattern,
+ FindExInfoBasic, // We don't care about the short names
+ &findData,
+ FindExSearchNameMatch, // standard name matching
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+#endif // !FEATURE_PAL
+
+ if (hSearch == INVALID_HANDLE_VALUE)
+ {
+ DWORD lastErr = GetLastError();
+ if (lastErr == ERROR_FILE_NOT_FOUND)
+ {
+ // This is ok; there was just nothing matching the pattern.
+ }
+ else
+ {
+ LogError("Failed to find pattern '%s'. GetLastError()=%u", searchPattern, GetLastError());
+ }
+ goto CLEAN_UP;
+ }
+
+ while (true)
+ {
+ // Do something with findData...
+
+ if (filter(&findData))
+ {
+ // Prepend it to the list.
+ first = new findDataList(&findData, first);
+ ++elemCount;
+ }
+
+ BOOL ok = FindNextFileA(hSearch, &findData);
+ if (!ok)
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES)
+ {
+ LogError("Failed to find next file. GetLastError()=%u", GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ break;
+ }
+ }
+
+ // Now sort the list. Create an array to put everything in.
+
+ int i;
+
+ retArray = new WIN32_FIND_DATAA[elemCount];
+ i = 0;
+ for (findDataList* tmp = first; tmp != nullptr; tmp = tmp->next)
+ {
+ retArray[i++] = tmp->findData;
+ }
+
+ qsort(retArray, elemCount, sizeof(retArray[0]), WIN32_FIND_DATAA_qsort_helper);
+
+CLEAN_UP:
+
+ findDataList::DeleteList(first);
+
+ if ((hSearch != INVALID_HANDLE_VALUE) && !FindClose(hSearch))
+ {
+ LogError("Failed to close search handle. GetLastError()=%u", GetLastError());
+ delete[] retArray;
+ return -1;
+ }
+
+ *ppFileArray = retArray;
+ *pElemCount = elemCount;
+ return result;
+}
+
+// Append all files in the given directory matching the file pattern.
+//
+// static
+int verbMerge::AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size)
+{
+ int result = 0; // default to zero == success
+ LONGLONG totalSize = 0;
+
+ char* searchPattern = MergePathStrings(dir, file);
+
+ WIN32_FIND_DATAA* fileArray = nullptr;
+ int elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterFile, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+
+ // Is it zero length? If so, skip it.
+ if ((findData.nFileSizeLow == 0) && (findData.nFileSizeHigh == 0))
+ {
+ LogInfo("Skipping zero-length file '%s'", fileFullPath);
+ }
+ else
+ {
+ result = AppendFile(hFileOut, fileFullPath, buffer, bufferSize);
+ if (result != 0)
+ {
+ // Error was already logged.
+ delete[] fileFullPath;
+ goto CLEAN_UP;
+ }
+ }
+
+ delete[] fileFullPath;
+ totalSize += ((LONGLONG)findData.nFileSizeHigh << 32) + (LONGLONG)findData.nFileSizeLow;
+ }
+
+ // If we need to recurse, then search the directory again for directories, and recursively search each one.
+ if (recursive)
+ {
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ searchPattern = MergePathStrings(dir, "*");
+ fileArray = nullptr;
+ elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterDirectories, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ LONGLONG dirSize = 0;
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+ result = AppendAllInDir(hFileOut, fileFullPath, file, buffer, bufferSize, recursive, &dirSize);
+ delete[] fileFullPath;
+ if (result != 0)
+ {
+ // Error was already logged.
+ goto CLEAN_UP;
+ }
+
+ totalSize += dirSize;
+ }
+ }
+
+CLEAN_UP:
+
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ if (result == 0)
+ {
+ *size = totalSize;
+ }
+
+ return result;
+}
+
+// Merge a set of .MC files into an output .MCH file. The .MC files to merge are given as a pattern, one of:
+// 1. *.mc -- simple pattern. Assumes current directory.
+// 2. foo\bar\*.mc -- simple pattern with relative directory.
+// 3. c:\foo\bar\baz\*.mc -- simple pattern with full path.
+// If no pattern is given, then the last component of the path is expected to be a directory name, and the pattern is assumed to be "*" (that is, all files).
+//
+// If "recursive" is true, then the pattern is searched for in the specified directory (or implicit current directory) and
+// all sub-directories, recursively.
+//
+// static
+int verbMerge::DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive)
+{
+ int result = 0; // default to zero == success
+ SimpleTimer st1;
+
+ LogInfo("Merging files matching '%s' into '%s'", pattern, nameOfOutputFile);
+
+ HANDLE hFileOut = CreateFileA(nameOfOutputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", nameOfOutputFile, GetLastError());
+ return -1;
+ }
+
+ // Create a buffer we can use for all the copies.
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+ char* dir = nullptr;
+ const char* file = nullptr;
+
+ dir = _strdup(pattern);
+ char* lastSlash = strrchr(dir, DIRECTORY_SEPARATOR_CHAR_A);
+ if (lastSlash == NULL)
+ {
+ // The user may have passed a relative path without a slash, or the current directory.
+ // If there is a wildcard, we use it as the file pattern. If there isn't, we assume it's a relative directory name
+ // and use it as a directory, with "*" as the file pattern.
+ const char* wildcard = strchr(dir, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+ }
+ else
+ {
+ file = dir;
+ dir = _strdup(".");
+ }
+ }
+ else
+ {
+ const char* wildcard = strchr(lastSlash, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+
+ // Minor canonicalization: if there is a trailing last slash, strip it (probably should do this in a loop...)
+ if (*(lastSlash + 1) == '\0')
+ {
+ *lastSlash = '\0';
+ }
+ }
+ else
+ {
+ // ok, we found a wildcard after the last slash, so assume there is a pattern. Strip it at the last slash.
+ *lastSlash = '\0';
+ file = lastSlash + 1;
+ }
+ }
+
+ LONGLONG totalSize = 0;
+ LONGLONG dirSize = 0;
+
+ st1.Start();
+
+ result = AppendAllInDir(hFileOut, dir, file, buffer, BUFFER_SIZE, recursive, &dirSize);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+ totalSize += dirSize;
+
+ st1.Stop();
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.",
+ totalSize/(1000*1000),
+ (((double)totalSize)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+CLEAN_UP:
+
+ free((void*)dir);
+ delete[] buffer;
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ if (result != 0)
+ {
+ // There was a failure. Delete the output file, to avoid leaving some half-created file.
+ BOOL ok = DeleteFileA(nameOfOutputFile);
+ if (!ok)
+ {
+ LogError("Failed to delete file after MCS /merge failed. GetLastError()=%u", GetLastError());
+ }
+ }
+
+ return result;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.h b/src/ToolBox/superpmi/mcs/verbmerge.h
new file mode 100644
index 0000000000..1d612426f3
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbMerge.h - verb that merges multiple .MC into one .MCH file
+//----------------------------------------------------------
+#ifndef _verbMerge
+#define _verbMerge
+
+class verbMerge
+{
+public:
+ static int DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive);
+
+private:
+ typedef bool (*DirectoryFilterFunction_t)(WIN32_FIND_DATAA*);
+ static bool DirectoryFilterDirectories(WIN32_FIND_DATAA* findData);
+ static bool DirectoryFilterFile(WIN32_FIND_DATAA* findData);
+ static int __cdecl WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2);
+ static int FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount);
+
+ static char* MergePathStrings(const char* dir, const char* file);
+
+ static int AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize);
+ static int AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.cpp b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
new file mode 100644
index 0000000000..eca5dc1b09
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
@@ -0,0 +1,145 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbremovedup.h"
+#include "simpletimer.h"
+#include "lightweightmap.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//We use a hash to limit the number of comparisons we need to do.
+//The first level key to our hash map is ILCodeSize and the second
+//level map key is just an index and the value is an existing MC Hash.
+
+LightWeightMap<int, DenseLightWeightMap<char *> *> *inFile = nullptr;
+
+bool unique(MethodContext *mc)
+{
+ if (inFile == nullptr)
+ inFile = new LightWeightMap<int, DenseLightWeightMap<char *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ char *md5Buff = new char[MD5_HASH_BUFFER_SIZE];
+ mc->dumpMethodMD5HashToBuffer(md5Buff, MD5_HASH_BUFFER_SIZE);
+
+ if (inFile->GetIndex(newInfo.ILCodeSize) == -1)
+ inFile->Add(newInfo.ILCodeSize, new DenseLightWeightMap<char *>());
+
+ DenseLightWeightMap<char *> *ourRank = inFile->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ char *md5Buff2 = ourRank->Get(i);
+
+ if (strncmp(md5Buff, md5Buff2, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ delete[] md5Buff;
+ return false;
+ }
+ }
+
+ ourRank->Append(md5Buff);
+ return true;
+}
+
+LightWeightMap<int, DenseLightWeightMap<MethodContext *> *> *inFileLegacy = nullptr;
+
+bool uniqueLegacy(MethodContext *mc)
+{
+ if (inFileLegacy == nullptr)
+ inFileLegacy = new LightWeightMap<int, DenseLightWeightMap<MethodContext *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ if (inFileLegacy->GetIndex(newInfo.ILCodeSize) == -1)
+ inFileLegacy->Add(newInfo.ILCodeSize, new DenseLightWeightMap<MethodContext *>());
+
+ DenseLightWeightMap<MethodContext *> *ourRank = inFileLegacy->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ MethodContext *scratch = ourRank->Get(i);
+ if (mc->Equal(scratch))
+ {
+ return false;
+ }
+ }
+
+ // We store the MethodContext in our map.
+ ourRank->Append(mc);
+ return true;
+}
+
+int verbRemoveDup::DoWork(const char *nameOfInput, const char *nameOfOutput, bool stripCR, bool legacyCompare)
+{
+ LogVerbose("Removing duplicates from '%s', writing to '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.CurrentTakeOwnership();
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ if (legacyCompare)
+ {
+ if (uniqueLegacy(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+
+ // In this case, for the legacy comparer, it has placed the 'mc' in the 'inFileLegacy' table, so we can't delete it.
+ }
+ else
+ {
+ delete mc; // we no longer need this
+ }
+ }
+ else
+ {
+ if (unique(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ delete mc; // we no longer need this
+ }
+ }
+
+ // We're leaking 'inFile' or 'inFileLegacy', but the process is going away, so it shouldn't matter.
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.h b/src/ToolBox/superpmi/mcs/verbremovedup.h
new file mode 100644
index 0000000000..ab0cb98692
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbRemoveDup.h - verb that attempts to remove dups
+//----------------------------------------------------------
+#ifndef _verbRemoveDup
+#define _verbRemoveDup
+
+class verbRemoveDup
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, bool stripCR, bool legacyCompare);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.cpp b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
new file mode 100644
index 0000000000..9b881cdcbf
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
@@ -0,0 +1,96 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbsmarty.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//
+// Constructs a new verbSmarty.
+//
+// Arguments:
+// hFile - A handle to the output file that we are writing the Smarty Test IDs
+//
+// Assumptions:
+// hFile refers to an open and writeable file handle.
+//
+verbSmarty::verbSmarty(HANDLE hFile)
+{
+ m_hFile=hFile;
+}
+
+//
+// Dumps the Smarty TestID to file
+//
+// Arguments:
+// testID - Smarty Test ID
+//
+void verbSmarty::DumpTestInfo(int testID)
+{
+ #define bufflen 4096
+ DWORD bytesWritten;
+
+ char buff[bufflen];
+ int buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "%i\r\n", testID);
+ WriteFile(m_hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+}
+
+
+int verbSmarty::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Reading from '%s' reading Smarty ID for the Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ verbSmarty *verbList = new verbSmarty(hFileOut);
+
+ //TODO-Cleanup: look to use toc for this
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ int testID = mc->repGetTestID();
+ if (testID != -1)
+ {
+ //write to the file
+ verbList->DumpTestInfo(testID);
+ }
+ else
+ {
+ LogError("Smarty ID not found for '%s'", mc->cr->repProcessName());
+ }
+ }
+
+ delete verbList;
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.h b/src/ToolBox/superpmi/mcs/verbsmarty.h
new file mode 100644
index 0000000000..994695da79
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbSmarty.h - verb that outputs Smarty test ID for mc
+//----------------------------------------------------------
+#ifndef _verbSmarty
+#define _verbSmarty
+
+class verbSmarty
+{
+public:
+ verbSmarty(HANDLE hFile);
+ void DumpTestInfo(int testID);
+ static int DoWork(const char *nameOfInput, const char *nameofOutput, int indexCount, const int *indexes);
+
+private:
+ HANDLE m_hFile;
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstat.cpp b/src/ToolBox/superpmi/mcs/verbstat.cpp
new file mode 100644
index 0000000000..473f452f96
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbstat.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbStat::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Stat'ing from '%s' and writing output into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ #define bufflen 50000
+ DWORD bytesWritten;
+ char buff[bufflen];
+ int offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ offset += sprintf_s(buff, bufflen, "Title,MC#,");
+ offset += MethodContext::dumpStatTitleToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ if ((mc->cr->ProcessName != nullptr) && (mc->cr->ProcessName->GetCount() > 0))
+ {
+ const char *procname = mc->cr->repProcessName();
+ strcpy_s(&buff[offset], bufflen, procname);
+ offset += (int)strlen(procname);
+ }
+ buff[offset++] = ',';
+ offset += sprintf_s(&buff[offset], bufflen - offset, "%d,", mci.MethodContextNumber());
+ offset += mc->dumpStatToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+ savedCount++;
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Stat'd %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstat.h b/src/ToolBox/superpmi/mcs/verbstat.h
new file mode 100644
index 0000000000..53a3a78d6f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbStat.h - verb that
+//----------------------------------------------------------
+#ifndef _verbStat
+#define _verbStat
+
+class verbStat
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.cpp b/src/ToolBox/superpmi/mcs/verbstrip.cpp
new file mode 100644
index 0000000000..8783b1a767
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbstrip.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "errorhandling.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+
+// verbStrip::DoWork handles both "-copy" and "-strip". These both copy from input file to output file,
+// but treat the passed-in indexes in opposite ways.
+int verbStrip::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR)
+{
+ if (strip)
+ return DoWorkTheOldWay(nameOfInput, nameOfOutput, indexCount, indexes, stripCR);
+ SimpleTimer *st1 = new SimpleTimer();
+
+ LogVerbose("Reading from '%s' removing Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ int loadedCount = 0;
+ MethodContext *mc = nullptr;
+ int savedCount = 0;
+ int index = 0;
+
+ // The method context reader handles skipping any unrequested method contexts
+ // Used in conjunction with an MCI file, it does a lot less work...
+ MethodContextReader *reader = new MethodContextReader(nameOfInput, indexes, indexCount);
+ if (!reader->isValid())
+ {
+ return -1;
+ }
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ if(indexCount == -1)
+ strip = true; //Copy command with no indexes listed should copy all the inputs...
+ while(true)
+ {
+ MethodContextBuffer mcb = reader->GetNextMethodContext();
+ if (mcb.Error())
+ {
+ return -1;
+ }
+ else if (mcb.allDone())
+ {
+ break;
+ }
+
+ loadedCount++;
+ if((loadedCount%500==0)&&(loadedCount>0))
+ {
+ st1->Stop();
+ LogVerbose("%2.1f%% - Loaded %d at %d per second", reader->PercentComplete(), loadedCount, (int)((double)500 / st1->GetSeconds()));
+ st1->Start();
+ }
+
+ if (!MethodContext::Initialize(loadedCount, mcb.buff, mcb.size, &mc))
+ return -1;
+
+ if(stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ delete mc;
+ }
+ if(CloseHandle(hFileOut)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ LogInfo("Loaded %d, Saved %d", loadedCount, savedCount);
+
+ return 0;
+}
+
+
+// This is only used for "-strip".
+int verbStrip::DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ LogVerbose("Reading from '%s' removing MC Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+ bool write;
+ int index = 0; // Can't use MethodContextIterator indexing, since we want the opposite of that.
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ write = true; // assume we'll write it
+ if (index < indexCount)
+ {
+ if (indexes[index] == mci.MethodContextNumber())
+ {
+ index++;
+ write = false;
+ }
+ }
+
+ if (write)
+ {
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ }
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (index < indexCount)
+ LogWarning("Didn't use all of index count input %d < %d (i.e. didn't see MC #%d)", index, indexCount, indexes[index]);
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.h b/src/ToolBox/superpmi/mcs/verbstrip.h
new file mode 100644
index 0000000000..9db77736a8
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbStrip.h - verb that removes a list of mc's from an MCH file
+//----------------------------------------------------------
+#ifndef _verbStrip
+#define _verbStrip
+
+class verbStrip
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR);
+ static int DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.cpp b/src/ToolBox/superpmi/mcs/verbtoc.cpp
new file mode 100644
index 0000000000..a99fbf0183
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbtoc.h"
+#include "methodcontext.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+#include "simpletimer.h"
+
+class TOCElementNode
+{
+public:
+ TOCElementNode *Next;
+ TOCElement tocElement;
+
+ TOCElementNode(int number, __int64 offset)
+ : Next(nullptr)
+ , tocElement(number, offset)
+ {
+ }
+};
+
+int verbTOC::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Indexing from '%s' into '%s.mct'", nameOfInput, nameOfInput);
+
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ TOCElementNode *head = nullptr;
+ TOCElementNode *curElem = nullptr;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ TOCElementNode *nxt = new TOCElementNode(mci.MethodContextNumber(), mci.CurrentPos());
+ mc->dumpMethodMD5HashToBuffer(nxt->tocElement.Hash, MD5_HASH_BUFFER_SIZE);
+
+ if (curElem != nullptr)
+ {
+ curElem->Next = nxt;
+ }
+ else
+ {
+ head = nxt;
+ }
+ curElem = nxt;
+ savedCount++;
+ }
+
+ size_t maxLen = strlen(nameOfInput) + 5;
+ char *nameOfOutput = (char*)_alloca(maxLen);
+ strcpy_s(nameOfOutput, maxLen, nameOfInput);
+ strcat_s(nameOfOutput, maxLen, ".mct");
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ DWORD written;
+ // Write out the signature "INDX" and then the element count
+ LARGE_INTEGER token;
+ token.u.LowPart = *(const int*)"INDX"; // cuz Type Safety is for languages that have good IO facilities
+ token.u.HighPart = savedCount;
+ if (!WriteFile(hFileOut, &token, sizeof(token), &written, nullptr) || written != sizeof(token))
+ {
+ LogError("Failed to write index header. GetLastError()=%u", GetLastError());
+ }
+
+ // Now just dump sizeof(TOCElement) byte chunks into the file.
+ // I could probably do this more efficiently, but I don't think it matters
+ DWORD chunkSize = sizeof(TOCElement);
+ for (curElem = head; curElem != nullptr; curElem = curElem->Next)
+ {
+ if (!WriteFile(hFileOut, &curElem->tocElement, chunkSize, &written, nullptr) || written != chunkSize)
+ {
+ LogError("Failed to write index element '%d'. GetLastError()=%u", curElem->tocElement.Number, GetLastError());
+ return -1;
+ }
+ }
+ // Now write out a final "INDX" to flag the end of the file...
+ if (!WriteFile(hFileOut, &token.u.LowPart, sizeof(token.u.LowPart), &written, nullptr) || (written != sizeof(token.u.LowPart)))
+ {
+ LogError("Failed to write index terminal. GetLastError()=%u", GetLastError());
+ }
+
+ LogInfo("Loaded %d, added %d to Table of Contents", mci.MethodContextNumber(), savedCount);
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.h b/src/ToolBox/superpmi/mcs/verbtoc.h
new file mode 100644
index 0000000000..7eea371191
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbTOC.h - verb that creates a Table of Context for a MCH file
+//----------------------------------------------------------
+#ifndef _verbTOC
+#define _verbTOC
+
+class verbTOC
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif