|
| 1 | +# Overview |
| 2 | + |
| 3 | +This directory (`src/coreclr/src/ToolBox/superpmi` in the GitHub |
| 4 | +https://github.com/dotnet/runtime repository) contains the SuperPMI |
| 5 | +tool used for testing the .NET just-in-time (JIT) compiler. |
| 6 | + |
| 7 | +## Purpose |
| 8 | + |
| 9 | +SuperPMI has two primary uses: |
| 10 | +1. Verification that a JIT code change doesn't cause any asserts. |
| 11 | +2. Finding test code where two JIT compilers generate different code, or |
| 12 | +verifying that the two compilers generate the same code. |
| 13 | + |
| 14 | +Case #1 is useful for doing quick regression checking when making a source |
| 15 | +code change to the JIT compiler. The process is: (a) make a JIT source code |
| 16 | +change, (b) run that newly built JIT through a SuperPMI run to verify no |
| 17 | +asserts have been introduced. |
| 18 | + |
| 19 | +Case #2 is useful for generating assembly language diffs, to help analyze the |
| 20 | +impact of a JIT code change. |
| 21 | + |
| 22 | +## SuperPMI architecture |
| 23 | + |
| 24 | +SuperPMI works in two phases: collection and playback. |
| 25 | + |
| 26 | +In the collection phase, the system is configured to collect SuperPMI data. |
| 27 | +Then, run any set of .NET managed programs. When these managed programs invoke the JIT |
| 28 | +compiler, SuperPMI gathers and captures all information passed between the |
| 29 | +JIT and its .NET host. This data is post-processed to remove essentially |
| 30 | +duplicate function information, and is collected into one or just a few |
| 31 | +files. |
| 32 | + |
| 33 | +In the playback phase, SuperPMI loads the JIT directly, and causes it to |
| 34 | +compile all the functions that were previously compiled in the collection |
| 35 | +phase, but using the collected data to provide answers to various questions |
| 36 | +that the JIT needs to ask. The .NET execution engine (EE) is not invoked at all. |
| 37 | +When doing playback for assertion checking, only a single JIT is loaded and |
| 38 | +used for compilation. When doing playback to check for assembly diffs, both |
| 39 | +a "baseline" and a "diff" compiler are loaded. Each JIT is asked to compile |
| 40 | +each recorded function. The generated results are compared with a built-in |
| 41 | +SuperPMI "near differ", which depends on an external disassembler component |
| 42 | +called `coredistools`. Typically, scripting has SuperPMI generate a list of |
| 43 | +functions with differences, then the script re-invokes each JIT to generate |
| 44 | +disassembly output (using `COMPlus_JitDisasm`) for each differing function. |
| 45 | +These are then compared either visually, or with the jitutils tool |
| 46 | +`jit-analyze`, or both. |
| 47 | + |
| 48 | + |
| 49 | +# Tools |
| 50 | + |
| 51 | +There are two native executable tools: `superpmi` and `mcs`. To do the collection, |
| 52 | +there is the `superpmi-shim-collector` binary (`.dll` or `.so` or `.dylib`). |
| 53 | + |
| 54 | +To harness collection, there is a .NET Core C# program that is built as |
| 55 | +part of the coreclr tests build called superpmicollect.exe |
| 56 | +(source: src/coreclr/tests/src/JIT/superpmi in https://github.com/dotnet/runtime repository). |
| 57 | +This tool also functions as a SuperPMI collection and playback unit test. |
| 58 | + |
| 59 | +The superpmicollect tool is also being moved to the jitutils repository |
| 60 | +(https://github.com/dotnet/jitutils). |
| 61 | + |
| 62 | +Each tool will show a help screen if passed `-?`. |
| 63 | + |
| 64 | +Finally, there is a Python script that harnesses SuperPMI collection, |
| 65 | +playback, and other functions, such as download of existing SuperPMI |
| 66 | +collections from well-known locations. This tool is `superpmi.py`, |
| 67 | +found in `src/corclr/scripts` in the dotnet/runtime repository. See |
| 68 | +[superpmi.md](../../../scripts/superpmi.md) for more details. |
| 69 | + |
| 70 | + |
| 71 | +# Collection |
| 72 | + |
| 73 | +To manually do a collection (not using the `superpmi.py` script or |
| 74 | +`superpmicollect.exe` tool), follow the following steps. |
| 75 | + |
| 76 | +## Overall collection process |
| 77 | + |
| 78 | +First, build the `dotnet/runtime` repo, which builds the `superpmi`, `mcs`, |
| 79 | +and `superpmi-shim-collector` programs, along with the rest of coreclr, |
| 80 | +and places them in the same native code directory as the JIT and the rest |
| 81 | +of coreclr, e.g., `f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\superpmi.exe` |
| 82 | +for a `dotnet/runtime` repo rooted at the `f:\gh\runtime` directory, and |
| 83 | +built on Windows for the x64 Checked architecture / build flavor combination. |
| 84 | + |
| 85 | +The SuperPMI collection process requires a lot of disk space. How much will |
| 86 | +depend on the number, size, and complexity of the functions the JIT needs to |
| 87 | +compile. Make sure that the disk where the collected data will be collected |
| 88 | +is sufficiently large. It is also best if the data is written on an SSD, to |
| 89 | +speed up the disk operations. |
| 90 | + |
| 91 | +These are the general steps that are followed when doing a SuperPMI collection: |
| 92 | + |
| 93 | +1. Collect .MC files. Set up for collection, then cause the JIT to be invoked |
| 94 | +by either running a scenario, running tests, crossgen compiling assemblies, |
| 95 | +or using PMI to force the JIT to compile functions in an assembly. |
| 96 | +During collection, the data for each JIT compiled function is stored |
| 97 | +in a uniquely named file with a ".MC" filename extension (for "method context"). |
| 98 | +2. Merge .MC files to .MCH file. We want all the generated data to be merged to a |
| 99 | +single file (or some smaller set of files), to both be more manageable, and |
| 100 | +to collect the data for multiple compiled functions into one place for easier |
| 101 | +replay. (MCH stands for "method context hive".) |
| 102 | +3. Remove duplicates in the .MCH file. Many compiled functions are essentially |
| 103 | +equivalent, such as trivial class constructors, or if some functions are |
| 104 | +compiled multiple times in different scenarios or tests. We filter out the |
| 105 | +duplicates, which makes playback much faster, and the resultant MCH file much |
| 106 | +smaller. |
| 107 | +4. Create a "clean" .MCH with no SuperPMI failures. The original collected MCH |
| 108 | +file might not replay cleanly. This is generally due to existing, un-investigated |
| 109 | +SuperPMI bugs or limitations. We don't want to see these during normal playback, |
| 110 | +so filter out the failing replays at this point so the "baseline" replay is clean. |
| 111 | +5. Create a table of contents (TOC) file. This creates an index for the generated |
| 112 | +MCH file that greatly speeds up certain operations. |
| 113 | +6. Test final .MCH file is clean. This is purely to check that the resultant |
| 114 | +MCH file is in a good state for future use. |
| 115 | + |
| 116 | + |
| 117 | +## Collect .MC files |
| 118 | + |
| 119 | +Set the following environment variables: |
| 120 | + |
| 121 | +``` |
| 122 | +SuperPMIShimLogPath=<full path to an existing, empty temporary directory> |
| 123 | +SuperPMIShimPath=<full path to clrjit.dll, the "standalone" JIT> |
| 124 | +COMPlus_AltJit=* |
| 125 | +COMPlus_AltJitNgen=* |
| 126 | +COMPlus_AltJitName=superpmi-shim-collector.dll |
| 127 | +``` |
| 128 | + |
| 129 | +for example, on Windows: |
| 130 | + |
| 131 | +``` |
| 132 | +mkdir f:\spmi\temp |
| 133 | +set SuperPMIShimLogPath=f:\spmi\temp |
| 134 | +set SuperPMIShimPath=f:\gh\runtime\artifacts\tests\coreclr\Windows_NT.x64.Checked\Tests\Core_Root\clrjit.dll |
| 135 | +set COMPlus_AltJit=* |
| 136 | +set COMPlus_AltJitNgen=* |
| 137 | +set COMPlus_AltJitName=superpmi-shim-collector.dll |
| 138 | +``` |
| 139 | + |
| 140 | +(On Linux, use `libclrjit.so` and `libsuperpmi-shim-collector.so`. |
| 141 | +On Mac, use `libclrjit.dylib` and `libsuperpmi-shim-collector.dylib`.) |
| 142 | + |
| 143 | +Note that the `superpmi-shim-collector.dll` must live in the same directory as the `coreclr.dll` |
| 144 | +(or libcoreclr.so on Linux) that will be invoked when running .NET Core. This will normally be |
| 145 | +a `Core_Root` directory, since you must create such a directory to be able to run |
| 146 | +.NET Core applications (such as by using the `corerun` tool). |
| 147 | + |
| 148 | +If you want to collect using an official .NET Core build, you will need to build a matching set |
| 149 | +of superpmi binaries, and copy `superpmi-shim-collector.dll` to the correct directory. This |
| 150 | +option has not been tested (as far as I know). |
| 151 | + |
| 152 | +Then, cause the JIT to compile some code. Do one or more of: |
| 153 | +1. Run a managed scenario. |
| 154 | +2. Run managed code tests. |
| 155 | +3. Crossgen some assemblies. |
| 156 | +4. Run PMI over some assemblies (see https://github.com/dotnet/jitutils for details on PMI) |
| 157 | + |
| 158 | +When done running programs, un-set these environment variables. |
| 159 | + |
| 160 | +Now, you will have a large number of .MC files in the specified temporary |
| 161 | +directory (specified by `SuperPMIShimLogPath`). |
| 162 | + |
| 163 | + |
| 164 | +## Merge .MC files to .MCH file |
| 165 | + |
| 166 | +Merge the generated .MC files using the `mcs` tool: |
| 167 | + |
| 168 | +``` |
| 169 | +mcs -merge base.mch *.mc -recursive |
| 170 | +``` |
| 171 | + |
| 172 | +This assumes the current directory is the root directory where the .MC files |
| 173 | +were placed, namely the directory specified as `SuperPMIShimLogPath` above. |
| 174 | +You can also specify a directory prefix to the file system regular expression. |
| 175 | +The `-recursive` flag is only necessary if .MC files also exist in subdirectories |
| 176 | +of this, and you want those also added to the resultant, collected `base.mch` |
| 177 | +file. So, for the example above, you might use: |
| 178 | + |
| 179 | +``` |
| 180 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\mcs.exe -merge f:\spmi\base.mch f:\spmi\temp\*.mc -recursive |
| 181 | +``` |
| 182 | + |
| 183 | +Note that `mcs -merge` is literally just a file concatenation of many files into |
| 184 | +one, so this step will double the required disk space. |
| 185 | + |
| 186 | +After this step, you can remove all the individual .MC files unless you want |
| 187 | +to keep them to debug the SuperPMI collection process itself. |
| 188 | + |
| 189 | + |
| 190 | +## Remove duplicates in the .MCH file |
| 191 | + |
| 192 | +One benefit of SuperPMI is the ability to remove duplicated compilations, so |
| 193 | +on replay only unique functions are compiled. Use the following to create a |
| 194 | +"unique" set of functions: |
| 195 | + |
| 196 | +``` |
| 197 | +mcs -removeDup -thin base.mch unique.mch |
| 198 | +``` |
| 199 | + |
| 200 | +Note that `-thin` is not required. However, it will delete all the compilation |
| 201 | +results collected during the collection phase, which makes the resulting MCH |
| 202 | +file smaller. Those compilation results are not required for playback for |
| 203 | +the ways in which we normally use SuperPMI. |
| 204 | + |
| 205 | +For the continuing example, you might use: |
| 206 | + |
| 207 | +``` |
| 208 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\mcs.exe -removeDup -thin f:\spmi\base.mch f:\spmi\unique.mch |
| 209 | +``` |
| 210 | + |
| 211 | +After this step, you can remove the base.mch file (unless you want to debug |
| 212 | +the SuperPMI collection process itself). |
| 213 | + |
| 214 | + |
| 215 | +## Create a "clean" .MCH with no SuperPMI failures |
| 216 | + |
| 217 | +As stated above, due to various bugs or otherwise uninvestigated issues, a SuperPMI |
| 218 | +replay of the unique.mch file might contain errors. We don't want that, so we filter |
| 219 | +out those errors in a "baseline" run, as follows. |
| 220 | + |
| 221 | +``` |
| 222 | +superpmi -p -f basefail.mcl unique.mch clrjit.dll |
| 223 | +mcs.exe -strip basefail.mcl unique.mch final.mch |
| 224 | +``` |
| 225 | + |
| 226 | +Or, continuing the example above, giving full paths, we have: |
| 227 | + |
| 228 | +``` |
| 229 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\superpmi.exe -p -f f:\spmi\basefail.mcl f:\spmi\unique.mch f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\clrjit.dll |
| 230 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\mcs.exe -strip f:\spmi\basefail.mcl f:\spmi\unique.mch f:\spmi\final.mch |
| 231 | +``` |
| 232 | + |
| 233 | + |
| 234 | +## Create a table of contents (TOC) file |
| 235 | + |
| 236 | +This is |
| 237 | + |
| 238 | +``` |
| 239 | +mcs -toc final.mch |
| 240 | +``` |
| 241 | + |
| 242 | +or, using the full paths from above: |
| 243 | + |
| 244 | +``` |
| 245 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\mcs.exe -toc f:\spmi\final.mch |
| 246 | +``` |
| 247 | + |
| 248 | + |
| 249 | +## Test final .MCH file is clean |
| 250 | + |
| 251 | +This is done using a replay just like for the "create clean .MCH file" step: |
| 252 | + |
| 253 | +``` |
| 254 | +superpmi -p -f finalfail.mcl final.mch clrjit.dll |
| 255 | +``` |
| 256 | + |
| 257 | +Or, continuing the example above, giving full paths, we have: |
| 258 | + |
| 259 | +``` |
| 260 | +f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\superpmi.exe -p -f f:\spmi\finalfail.mcl f:\spmi\final.mch f:\gh\runtime\artifacts\bin\coreclr\Windows_NT.x64.Checked\clrjit.dll |
| 261 | +``` |
| 262 | + |
| 263 | +In this case, if `finalfail.mcl` is not empty, there was a failure in the final "check" replay. |
| 264 | +This will lead to the same failure in all future replays, if the resultant final.mch file is |
| 265 | +published for general use. This is annoying, but not necessarily a fatal problem. |
| 266 | +It's possible to cycle the process, stripping this failure from the final MCH, |
| 267 | +testing for "clean-ness" again, and repeating. It should not be necessary, however. |
| 268 | + |
| 269 | + |
| 270 | +## Publishing the SuperPMI collection |
| 271 | + |
| 272 | +The files that should be published for consumption either by yourself or by a group |
| 273 | +are `final.mch` and `final.mch.mct` (the TOC). All other files described above are |
| 274 | +intermediate files in the collection process, and are not needed afterwards. |
| 275 | + |
| 276 | + |
| 277 | +# Playback |
| 278 | + |
| 279 | +Once you have a merged, de-duplicated MCH collection, you can play it back |
| 280 | +using: |
| 281 | + |
| 282 | + superpmi -p final.mch clrjit.dll |
| 283 | + |
| 284 | +The `-p` switch says to utilize all the processors on your machine, |
| 285 | +and replay in parallel. You can omit this if you wish to replay |
| 286 | +without using parallelism (this is usually done when replaying |
| 287 | +a single compilation, whereas `-p` is usually used when replaying |
| 288 | +a full MCH file set of compilations). |
0 commit comments