Use the `INDEBUG(x)` macro (and related macros) judiciously, for code that only runs in DEBUG, to avoid `#ifdef`s.
-Use the `JITDUMP(x)` macro for printing things to the JIT dump output. Note that these things will only get printed when the `verbose` variable is set, which is when `COMPlus_JitDump=*` or when `COMPlus_JitDump=XXX` and we are JITting function XXX; or when `COMPlus_NgenDump=*` or `COMPlus_NgenDump=XXX` and we are NGENing function XXX. Do not use `JITDUMP` for all output in a debug-only function that might be useful to call from the debugger. In that case, define a function that uses `printf` (which is a JIT-specific implementation of this function), which can be called from the debugger, and invoke that function like this:
+Use the `JITDUMP(x)` macro for printing things to the JIT dump output. Note that these things will only get printed when the `verbose` variable is set, which is when `COMPlus_JitDump=*` or when `COMPlus_JitDump=XXX` and we are JITting function XXX. Do not use `JITDUMP` for all output in a debug-only function that might be useful to call from the debugger. In that case, define a function that uses `printf` (which is a JIT-specific implementation of this function), which can be called from the debugger, and invoke that function like this:
```c++
DBEXEC(verbose, MyDumpFunction());
|`--targetos osx --targetarch x64` |
|`--targetos osx --targetarch arm64` |
-- Passing special jit behavior flags to the compiler is done via the `--codegenopt` switch. As an example to turn on tailcall loop optimizations and dump all code compiled use a pair of them like `--codegenopt NgenDump=* --codegenopt TailCallLoopOpt=1`.
+- Passing special jit behavior flags to the compiler is done via the `--codegenopt` switch. As an example to turn on tailcall loop optimizations and dump all code compiled use a pair of them like `--codegenopt JitDump=* --codegenopt TailCallLoopOpt=1`.
-- When using the NgenDump feature of the JIT, disable parallelism as described above or specify a single method to be compiled. Otherwise, output from multiple functions will be interleaved and inscrutable.
+- When using the JitDump feature of the JIT, disable parallelism as described above or specify a single method to be compiled. Otherwise, output from multiple functions will be interleaved and inscrutable.
- Since there are 2 jits in the process, when debugging in the JIT, if the source files match up, there is a decent chance that a native debugger will stop at unfortunate and unexpected locations. This is extremely annoying, and to combat this, we generally recommend making a point of using a runtime which doesn't exactly match that of the compiler in use. However, if that isn't feasible, it is also possible to disable symbol loading in most native debuggers. For instance, in Visual Studio, one would use the "Specify excluded modules" feature.
Emitting R2R PE file: c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll
```
-I then wanted to see some more detail from the jit. To keep the size of this example small, I'm just using the `NgenOrder=1`switch, but jit developers would more likely use `NgenDump=*` switch.
+I then wanted to see some more detail from the jit. To keep the size of this example small, I'm just using the `JitOrder=1`switch, but jit developers would more likely use `JitDump=*` switch.
```
-C:\git2\runtime>"dotnet" "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt NgenOrder=1
+C:\git2\runtime>"dotnet" "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder=1
C:\git2\runtime\.dotnet
Single method repro args:--singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1
| Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | x64 codesize|
And finally, as the last `--targetarch` and `--targetos` switch is the meaningful one, it is simple to target a different architecture for ad hoc exploration...
```
-C:\git2\runtime>"dotnet" "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt NgenOrder=1 --targetarch arm64
+C:\git2\runtime>"dotnet" "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder=1 --targetarch arm64
C:\git2\runtime\.dotnet
Single method repro args:--singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1
| Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | arm64 codesize|
Since this example uses `dotnet` as the `__TestDotNetCmd` you will need to debug the `c:\git2\runtime\.dotnet\dotnet.exe` process.
```
-devenv /debugexe C:\git2\runtime\.dotnet\dotnet.exe "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt NgenOrder=1 --targetarch arm64
+devenv /debugexe C:\git2\runtime\.dotnet\dotnet.exe "c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\crossgen2\crossgen2.dll" @"c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\\Complex1.dll.rsp" -r:c:\git2\runtime\artifacts\tests\coreclr\windows.x64.Debug\jit\Directed\Arrays\Complex1\IL-CG2\*.dll --print-repro-instructions --singlemethodtypename "Complex_Array_Test,Complex1" --singlemethodname Main --singlemethodindex 1 --codegenopt JitOrder=1 --targetarch arm64
```
This will launch the Visual Studio debugger, with a solution setup for debugging the dotnet.exe process. By default this solution will debug the native code of the process only. To debug the managed components, edit the properties on the solution and set the `Debugger Type` to `Managed (.NET Core, .NET 5+)` or `Mixed (.NET Core, .NET 5+)`.
//
if (!compIsForInlining())
{
- if (jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
+ if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- if (JitConfig.NgenDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- verboseDump = true;
- }
- unsigned ngenHashDumpVal = (unsigned)JitConfig.NgenHashDump();
- if ((ngenHashDumpVal != (DWORD)-1) && (ngenHashDumpVal == info.compMethodHash()))
- {
- verboseDump = true;
- }
+ verboseDump = true;
}
- else
+ unsigned jitHashDumpVal = (unsigned)JitConfig.JitHashDump();
+ if ((jitHashDumpVal != (DWORD)-1) && (jitHashDumpVal == info.compMethodHash()))
{
- if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- verboseDump = true;
- }
- unsigned jitHashDumpVal = (unsigned)JitConfig.JitHashDump();
- if ((jitHashDumpVal != (DWORD)-1) && (jitHashDumpVal == info.compMethodHash()))
- {
- verboseDump = true;
- }
+ verboseDump = true;
}
}
}
//
if (!altJitConfig || opts.altJit)
{
- if (jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
+ bool disEnabled = true;
+
+ // Setup assembly name list for disassembly, if not already set up.
+ if (!s_pJitDisasmIncludeAssembliesListInitialized)
{
- if ((JitConfig.NgenOrder() & 1) == 1)
+ const WCHAR* assemblyNameList = JitConfig.JitDisasmAssemblies();
+ if (assemblyNameList != nullptr)
{
- opts.dspOrder = true;
+ s_pJitDisasmIncludeAssembliesList = new (HostAllocator::getHostAllocator())
+ AssemblyNamesList2(assemblyNameList, HostAllocator::getHostAllocator());
}
+ s_pJitDisasmIncludeAssembliesListInitialized = true;
+ }
- if (JitConfig.NgenGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- opts.dspGCtbls = true;
- }
+ // If we have an assembly name list for disassembly, also check this method's assembly.
+ if (s_pJitDisasmIncludeAssembliesList != nullptr && !s_pJitDisasmIncludeAssembliesList->IsEmpty())
+ {
+ const char* assemblyName = info.compCompHnd->getAssemblyName(
+ info.compCompHnd->getModuleAssembly(info.compCompHnd->getClassModule(info.compClassHnd)));
- if (JitConfig.NgenDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ if (!s_pJitDisasmIncludeAssembliesList->IsInList(assemblyName))
{
- opts.disAsm = true;
+ disEnabled = false;
}
- if (JitConfig.NgenDisasm().contains("SPILLED", nullptr, nullptr))
+ }
+
+ if (disEnabled)
+ {
+ if ((JitConfig.JitOrder() & 1) == 1)
{
- opts.disAsmSpilled = true;
+ opts.dspOrder = true;
}
- if (JitConfig.NgenUnwindDump().contains(info.compMethodName, info.compClassName,
- &info.compMethodInfo->args))
+ if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- opts.dspUnwind = true;
+ opts.dspGCtbls = true;
}
- if (JitConfig.NgenEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- opts.dspEHTable = true;
+ opts.disAsm = true;
}
- if (JitConfig.NgenDebugDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
+ if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr))
{
- opts.dspDebugInfo = true;
+ opts.disAsmSpilled = true;
}
- }
- else
- {
- bool disEnabled = true;
- // Setup assembly name list for disassembly, if not already set up.
- if (!s_pJitDisasmIncludeAssembliesListInitialized)
+ if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- const WCHAR* assemblyNameList = JitConfig.JitDisasmAssemblies();
- if (assemblyNameList != nullptr)
- {
- s_pJitDisasmIncludeAssembliesList = new (HostAllocator::getHostAllocator())
- AssemblyNamesList2(assemblyNameList, HostAllocator::getHostAllocator());
- }
- s_pJitDisasmIncludeAssembliesListInitialized = true;
+ opts.dspUnwind = true;
}
- // If we have an assembly name list for disassembly, also check this method's assembly.
- if (s_pJitDisasmIncludeAssembliesList != nullptr && !s_pJitDisasmIncludeAssembliesList->IsEmpty())
+ if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- const char* assemblyName = info.compCompHnd->getAssemblyName(
- info.compCompHnd->getModuleAssembly(info.compCompHnd->getClassModule(info.compClassHnd)));
-
- if (!s_pJitDisasmIncludeAssembliesList->IsInList(assemblyName))
- {
- disEnabled = false;
- }
+ opts.dspEHTable = true;
}
- if (disEnabled)
+ if (JitConfig.JitDebugDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
{
- if ((JitConfig.JitOrder() & 1) == 1)
- {
- opts.dspOrder = true;
- }
-
- if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- opts.dspGCtbls = true;
- }
-
- if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- opts.disAsm = true;
- }
-
- if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr))
- {
- opts.disAsmSpilled = true;
- }
-
- if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName,
- &info.compMethodInfo->args))
- {
- opts.dspUnwind = true;
- }
-
- if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
- {
- opts.dspEHTable = true;
- }
-
- if (JitConfig.JitDebugDump().contains(info.compMethodName, info.compClassName,
- &info.compMethodInfo->args))
- {
- opts.dspDebugInfo = true;
- }
+ opts.dspDebugInfo = true;
}
}
if (opts.disAsm && JitConfig.JitDisasmWithGC())
}
#ifdef DEBUG
- if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
- {
- dumpFunction =
- JitConfig.NgenDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args);
- filename = JitConfig.NgenDumpFgFile();
- pathname = JitConfig.NgenDumpFgDir();
- }
- else
- {
- dumpFunction =
- JitConfig.JitDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args);
- filename = JitConfig.JitDumpFgFile();
- pathname = JitConfig.JitDumpFgDir();
- }
+ dumpFunction = JitConfig.JitDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args);
+ filename = JitConfig.JitDumpFgFile();
+ pathname = JitConfig.JitDumpFgDir();
prePhasePattern = JitConfig.JitDumpFgPrePhase();
postPhasePattern = JitConfig.JitDumpFgPhase();
// COMPlus_JitDumpFgDir A path to a directory into which the flowgraphs will be dumped.
// COMPlus_JitDumpFgFile The filename to use. The default is "default.[xml|dot]".
// Note that the new graphs will be appended to this file if it already exists.
-// COMPlus_NgenDumpFg Same as COMPlus_JitDumpFg, but for ngen compiles.
-// COMPlus_NgenDumpFgDir Same as COMPlus_JitDumpFgDir, but for ngen compiles.
-// COMPlus_NgenDumpFgFile Same as COMPlus_JitDumpFgFile, but for ngen compiles.
// COMPlus_JitDumpFgPhase Phase(s) after which to dump the flowgraph.
// Set to the short name of a phase to see the flowgraph after that phase.
// Leave unset to dump after COLD-BLK (determine first cold block) or set to *
CONFIG_INTEGER(JitPInvokeCheckEnabled, W("JITPInvokeCheckEnabled"), 0)
CONFIG_INTEGER(JitPInvokeEnabled, W("JITPInvokeEnabled"), 1)
-// Controls verbosity for JitPrintInlinedMethods. Ignored for JitDump/NgenDump where
+// Controls verbosity for JitPrintInlinedMethods. Ignored for JitDump where
// it's always set.
CONFIG_INTEGER(JitPrintInlinedMethodsVerbose, W("JitPrintInlinedMethodsVerboseLevel"), 0)
// Prints a tree of inlinees for a specific method (use '*' for all methods)
CONFIG_INTEGER(JitVNMapSelLimit, W("JitVNMapSelLimit"), 0) // If non-zero, assert if # of VNF_MapSelect applications
// considered reaches this
-CONFIG_INTEGER(NgenHashDump, W("NgenHashDump"), -1) // same as JitHashDump, but for ngen
-CONFIG_INTEGER(NgenOrder, W("NgenOrder"), 0)
CONFIG_INTEGER(RunAltJitCode, W("RunAltJitCode"), 1) // If non-zero, and the compilation succeeds for an AltJit, then
// use the code. If zero, then we always throw away the generated
// code and fall back to the default compiler.
// exception handling
CONFIG_METHODSET(JitStressOnly, W("JitStressOnly")) // Internal Jit stress mode: stress only the specified method(s)
CONFIG_METHODSET(JitUnwindDump, W("JitUnwindDump")) // Dump the unwind codes for the method
-///
-/// NGEN
-///
-CONFIG_METHODSET(NgenDisasm, W("NgenDisasm")) // Same as JitDisasm, but for ngen
-CONFIG_METHODSET(NgenDump, W("NgenDump")) // Same as JitDump, but for ngen
-CONFIG_METHODSET(NgenEHDump, W("NgenEHDump")) // Dump the EH table for the method, as reported to the VM
-CONFIG_METHODSET(NgenGCDump, W("NgenGCDump"))
-CONFIG_METHODSET(NgenDebugDump, W("NgenDebugDump"))
-CONFIG_METHODSET(NgenUnwindDump, W("NgenUnwindDump")) // Dump the unwind codes for the method
-///
-/// JIT
-///
+
CONFIG_METHODSET(JitDumpFg, W("JitDumpFg")) // Dumps Xml/Dot Flowgraph for specified method
CONFIG_STRING(JitDumpFgDir, W("JitDumpFgDir")) // Directory for Xml/Dot flowgraph dump(s)
CONFIG_STRING(JitDumpFgFile, W("JitDumpFgFile")) // Filename for Xml/Dot flowgraph dump(s) (default: "default")
// STRESS_TAILCALL
CONFIG_STRING(JitStressRange, W("JitStressRange")) // Internal Jit stress mode
///
-/// NGEN
-///
-CONFIG_METHODSET(NgenDumpFg, W("NgenDumpFg")) // Ngen Xml/Dot flowgraph dump support
-CONFIG_STRING(NgenDumpFgDir, W("NgenDumpFgDir")) // Ngen Xml/Dot flowgraph dump support
-CONFIG_STRING(NgenDumpFgFile, W("NgenDumpFgFile")) // Ngen Xml/Dot flowgraph dump support
-///
/// JIT Hardware Intrinsics
///
CONFIG_INTEGER(EnableIncompleteISAClass, W("EnableIncompleteISAClass"), 0) // Enable testing not-yet-implemented
# subparser for asmdiffs
asm_diff_parser = subparsers.add_parser("asmdiffs", description=asm_diff_description, parents=[core_root_parser, target_parser, superpmi_common_parser, replay_common_parser, base_diff_parser])
asm_diff_parser.add_argument("--diff_jit_dump", action="store_true", help="Generate JitDump output for diffs. Default: only generate asm, not JitDump.")
-asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump/COMPlus_NgenGCDump; requires instructions to be prefixed by offsets).")
-asm_diff_parser.add_argument("--debuginfo", action="store_true", help="Include debug info after disassembly (sets COMPlus_JitDebugDump/COMPlus_NgenDebugDump).")
+asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump; requires instructions to be prefixed by offsets).")
+asm_diff_parser.add_argument("--debuginfo", action="store_true", help="Include debug info after disassembly (sets COMPlus_JitDebugDump).")
asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed")
asm_diff_parser.add_argument("-metrics", action="append", help="Metrics option to pass to jit-analyze. Can be specified multiple times, or pass comma-separated values.")
asm_diff_parser.add_argument("-retainOnlyTopFiles", action="store_true", help="Retain only top .dasm files with largest improvements or regressions and delete remaining files.")
"COMPlus_JitDisasm": "*",
"COMPlus_JitUnwindDump": "*",
"COMPlus_JitEHDump": "*",
- "COMPlus_NgenDisasm": "*",
- "COMPlus_NgenUnwindDump": "*",
- "COMPlus_NgenEHDump": "*",
"COMPlus_JitDiffableDasm": "1",
"COMPlus_JitEnableNoWayAssert": "1",
"COMPlus_JitNoForceFallback": "1",
if self.coreclr_args.gcinfo:
asm_complus_vars.update({
- "COMPlus_JitGCDump": "*",
- "COMPlus_NgenGCDump": "*" })
+ "COMPlus_JitGCDump": "*" })
if self.coreclr_args.debuginfo:
asm_complus_vars.update({
"COMPlus_JitDebugDump": "*",
- "COMPlus_NgenDebugDump": "*",
"COMPlus_JitDisasmWithDebugInfo": "1" })
jit_dump_complus_vars = asm_complus_vars.copy()
jit_dump_complus_vars.update({
- "COMPlus_JitDump": "*",
- "COMPlus_NgenDump": "*" })
+ "COMPlus_JitDump": "*" })
asm_complus_vars_full_env = os.environ.copy()
asm_complus_vars_full_env.update(asm_complus_vars)