Mono(mono)

๐Ÿ“ฆ
version 6.14.1 ยท 3 versions
, in alpine 3.24, epel 10, fedora 44, nixos 26.05, nixpkgs unstable, ubuntu resolute, ubuntu stonking

๐ŸŒˆ Mono(mono) ๐ŸŒˆ

okay bestie, welcome to the mono manual page โ€” yes, that Mono(mono) header up top, that's this one ๐Ÿ’…. Sit down, grab a boba, because we're about to go through every single thing this runtime does and I am not skipping a crumb.


๐Ÿ’– NAME ๐Ÿ’–

mono โ€” Mono's ECMA-CLI native code generator (Just-in-Time and Ahead-of-Time) ๐Ÿฆ‹


โœจ SYNOPSIS โœจ

mono [options] file [arguments...]

mono-sgen [options] file [arguments...]

๐ŸŒธ DESCRIPTION ๐ŸŒธ

mono is a runtime implementation of the ECMA Common Language Infrastructure. You can use it to run ECMA and .NET applications. fr fr, that's the whole vibe.

The runtime loads the file you specify and optionally passes the arguments along to it. That file is an ECMA assembly, and bestie, those typically have a .exe or .dll extension.

These executables can reference extra functionality through what we call assembly references. By default, those assembly references get resolved like this: mscorlib.dll is resolved from the system profile that's configured by Mono, and all the other assemblies are loaded from the Global Assembly Cache (GAC). no cap.

The runtime contains a native code generator that transforms the Common Intermediate Language into native code. iconic behavior.

That code generator can operate in two modes: Just-in-time compilation (JIT) or Ahead-of-time compilation (AOT). And the way that code can be dynamically loaded means the runtime environment and the JIT are always present โ€” even if the code was compiled ahead of time. like, always always.

The runtime provides a bunch of configuration options for running applications, for developing and debugging, and for testing and debugging the runtime itself. multitasking queen ๐Ÿ‘‘

One thing to know: the mono command uses the moving and generational SGen garbage collector, while the mono-boehm command uses the conservative Boehm garbage collector. two different girls, two different energies.


๐Ÿฆ‹ PORTABILITY ๐Ÿฆ‹

On Unix-based systems, Mono gives you a mechanism to emulate Windows-style file access. This includes providing a case-insensitive view of the file system, directory separator mapping (from \ to /) and stripping the drive letters. yes, it really said "I'll pretend".

This functionality is enabled by setting the MONO_IOMAP environment variable to one of all, drive and case.

See the description for MONO_IOMAP in the environment variables section for more details โ€” it's down there, don't worry, we'll get to it ๐Ÿ’–.


๐ŸŽ€ METHOD DESCRIPTIONS ๐ŸŽ€

A number of diagnostic command line options take as argument a method description. A method description is a textual representation that can be used to uniquely identify a method. The syntax is as follows:

[namespace]classname:methodname[(arguments)]

The values in brackets are optional โ€” like the namespace and the arguments. The arguments themselves are either empty, or a comma-separated list of arguments. Both the classname and methodname can be set to the special value '*' to match any values. and omg, Unix shell users: escape the argument to avoid the shell interpreting this โš ๏ธ.

The arguments, if present, should be a comma separated list of types โ€” either a full typename, or for built-in types it should use the low-level ILAsm type names for the built-in types, like 'void', 'char', 'bool', 'byte', 'sbyte', 'uint16', 'int16', 'uint',

Pointer types should be the name of the type, followed by a '*'. Arrays should be the typename followed by '[' one or more commas (to indicate the rank of the array), and ']'. periodt.

Generic values should use '<', one or more type names, separated by both a comma and a space, and '>'.

By-reference arguments should include a "&" after the typename.

Examples:

*:ctor(int)         // All constructors that take an int as an argument
*:Main              // Methods named Main in any class
*:Main(string[])    // Methods named Main that take a string array in any class

๐Ÿ”ฅ RUNTIME OPTIONS ๐Ÿ”ฅ

The following options are available:

  • --aot, --aot[=options] โ€” This option is used to precompile the CIL code in the specified assembly to native code. The generated code is stored in a file with the extension .so, and that file will be automatically picked up by the runtime when the assembly is executed. Ahead-of-Time compilation is most useful if you use it in combination with the -O=all,-shared flag, which enables all of the optimizations in the code generator to be performed โ€” some of those optimizations are just not practical for Just-in-Time compilation since they might be very time consuming. Unlike the .NET Framework, Ahead-of-Time compilation will not generate domain independent code: it generates the same code that the Just-in-Time compiler would produce. Since most applications use a single domain, this is fine. If you want to optimize the generated code for use in multi-domain applications, consider using the -O=shared flag. This pre-compiles the methods, but the original assembly is still required to execute, as this one contains the metadata and exception information which is not available on the generated file. When precompiling code, you might want to compile with all optimizations (-O=all). Pre-compiled code is position independent code. Precompilation is just a mechanism to reduce startup time, increase code sharing across multiple mono processes and avoid just-in-time compilation program startup costs. The original assembly must still be present, as the metadata is contained there. AOT code typically can not be moved from one computer to another (CPU-specific optimizations that are detected at runtime), so you should not try to move the pre-generated assemblies or package the pre-generated assemblies for deployment.

A few options are available as a parameter to the --aot command line option. The options are separated by commas, and more than one can be specified:

  • asmonly โ€” Instructs the AOT compiler to output assembly code instead of an object file.
  • bind-to-runtime-version โ€” If specified, forces the generated AOT files to be bound to the runtime version of the compiling Mono. This will prevent the AOT files from being consumed by a different Mono runtime.
  • data-outfile=FILE.dll.aotdata โ€” This instructs the AOT code generator to output certain data constructs into a separate file. This can reduce the executable images some five to twenty percent. Developers need to then ship the resulting aotdata as a resource and register a hook to load the data on demand by using the mono_install_load_aot_data_hook method.
  • direct-icalls โ€” When this option is specified, icalls (internal calls made from the standard library into the mono runtime code) are invoked directly instead of going through the operating system symbol lookup operation. This requires use of the static option.
  • direct-pinvoke โ€” When this option is specified, P/Invoke methods are invoked directly instead of going through the operating system symbol lookup operation. This requires use of the static option.
  • dwarfdebug โ€” Instructs the AOT compiler to emit DWARF debugging information. When used together with the nodebug option, only DWARF debugging information is emitted, but not the information that can be used at runtime.
  • full โ€” This creates binaries which can be used with the --full-aot option.
  • hybrid โ€” This creates binaries which can be used with the --hybrid-aot option.
  • llvm โ€” AOT will be performed with the LLVM backend instead of the Mono backend where possible. This will be slower to compile but most likely result in a performance improvement.
  • llvmonly โ€” AOT will be performed with the LLVM backend exclusively and the Mono backend will not be used. The only output in this mode will be the bitcode file normally specified with the llvm-outfile option. Use of llvmonly automatically enables the full and llvm options. This feature is experimental.
  • llvmopts=[options] โ€” Use this option to add more flags to the built-in set of flags passed to the LLVM optimizer. When you invoke the mono command with the --aot=llvm it displays the current list of flags that are being passed to the opt command. The list of possible flags that can be passed can be obtained by calling the bundled opt program that comes with Mono, and calling it like this:
opt --help
  • llvmllc=[options] โ€” Use this option to add more flags to the built-in set of flags passed to the LLVM static compiler (llc). The list of possible flags that can be passed can be obtained by calling the bundled llc program that comes with Mono, and calling it like this:
llc --help
  • mcpu=[native o generic] โ€” cpu=native allows AOT mode to use all instructions current CPU supports, e.g. AVX2, SSE42, etc. Default value is 'generic'.
  • mattr=[cpu feature] โ€” Allows AOT code generator to use specified CPU features where possible including System.Runtime.Intrinsics.*. E.g. mattr=+avx2,mattr=-lzcnt unlocks sse1-4.2, avx1-2 and disables lzcnt. It's useful for cross-compilation or when it's not possible to use -mcpu=native (which enables all cpu feature current cpu has).
  • llvm-outfile=[filename] โ€” Gives the path for the temporary LLVM bitcode file created during AOT.
  • dedup โ€” Each AOT module will typically contain the code for inflated methods and wrappers that are called by code in that module. In dedup mode, we identify and skip compiling all of those methods. When using this mode with fullaot, dedup-include is required or these methods will remain missing.
  • dedup-include=[filename] โ€” In dedup-include mode, we are in the pass of compilation where we compile the methods that we had previously skipped. All of them are emitted into the assembly that is passed as this option. We consolidate the many duplicate skipped copies of the same method into one.
  • info โ€” Print the architecture the AOT in this copy of Mono targets and quit.

more coming bestie, don't go anywhere ๐Ÿ’œ

  • interp โ€” ๐Ÿ’ซ generates all the required wrappers, so you can run --interpreter with literally zero code generation happening at runtime. This one only makes sense with mscorlib.dll, ok? Embedders can set:
mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP);
  • depfile=[filename] โ€” outputs a gcc -M style dependency file. ๐Ÿ“„
  • ld-flags โ€” extra flags to pass to the C linker (only if the current AOT mode is asking for the linker to be invoked, iykyk).
  • llvm-path=<PREFIX> โ€” same deal but for the llvm tools opt and llc. ๐Ÿ› ๏ธ
  • msym-dir=<PATH> โ€” tells the AOT compiler to generate offline sequence points .msym files. Those generated .msym files get stored into a subfolder of <PATH> named as the compilation AOTID. ๐Ÿ—‚๏ธ
  • mtriple=<TRIPLE> โ€” use the GNU style target triple <TRIPLE> to decide some code generation options, i.e. --mtriple=armv7-linux-gnueabi will generate code that targets ARMv7. Currently only the ARM backend supports this. In LLVM mode, this triple gets passed on to the LLVM llc compiler. ๐ŸŽฏ
  • nimt-trampolines=[number] โ€” when compiling in full aot mode, the IMT trampolines must be precreated in the AOT image. You can add additional method trampolines with this argument. Defaults to 512.
  • ngsharedvt-trampolines=[number] โ€” when compiling in full aot mode, the value type generic sharing trampolines must be precreated in the AOT image. You can add additional method trampolines with this argument. Defaults to 512.
  • nodebug โ€” instructs the AOT compiler to not output any debugging information. ๐Ÿ™ˆ no debug info, we don't know her.
  • no-direct-calls โ€” this prevents the AOT compiler from generating a direct calls to a method. Normally the AOT compiler generates direct calls for certain methods that don't require going through the PLT (for example, methods that are known to not require a hook like a static constructor) or call into simple internal calls.
  • nrgctx-trampolines=[number] โ€” when compiling in full aot mode, the generic sharing trampolines must be precreated in the AOT image. You can add additional method trampolines with this argument. Defaults to 4096.
  • nrgctx-fetch-trampolines=[number] โ€” when compiling in full aot mode, the generic sharing fetch trampolines must be precreated in the AOT image. You can add additional method trampolines with this argument. Defaults to 128.
  • ntrampolines=[number] โ€” when compiling in full aot mode, the method trampolines must be precreated in the AOT image. You can add additional method trampolines with this argument. Defaults to 4096.
  • outfile=[filename] โ€” tells the AOT compiler to save the output to the specified file. ๐Ÿ’พ
  • print-skipped-methods โ€” if the AOT compiler can't compile a method for any reason, turning this flag on will output the skipped methods to the console. ๐Ÿ‘€
  • profile=[file] โ€” specify a file to use for profile-guided optimization. See the AOT profiler sub-section. Want multiple files? Just include the profile option multiple times.
  • profile-only โ€” AOT only the methods described in the files specified with the profile option. See the AOT profiler sub-section.
  • readonly-value=namespace.typename.fieldname=type/value โ€” override the value of a static readonly field. So here's the tea: usually during JIT compilation the static constructor is ran eagerly, so the value of a static readonly field is known at compilation time and the compiler can do a bunch of optimizations based on it. During AOT, instead, the static constructor can't be ran, so this option lets you set the value of such a field and enable that same set of optimizations. type can be any of i1, i2, i4 for integers of the respective sizes (in bytes). Note that signed/unsigned numbers don't matter here, just the storage size. This option can be specified multiple times and it doesn't prevent the static constructor for the type defining the field from executing with the usual rules at runtime (hence possibly computing a different value for the field). ๐Ÿคฏ
  • save-temps,keep-temps โ€” instructs the AOT compiler to keep temporary files. ๐Ÿงบ
  • soft-debug โ€” this instructs the compiler to generate sequence point checks that let Mono's soft debugger debug applications even on systems where it's not possible to set breakpoints or to single step (certain hardware configurations like the cell phones and video gaming consoles). ๐ŸŽฎ๐Ÿ“ฑ
  • static โ€” create an ELF object file (.o) or .s file which can be statically linked into an executable when embedding the mono runtime. When this option is used, the object file needs to be registered with the embedded runtime using the mono_aot_register_module function, which takes as its argument the mono_aot_module_<ASSEMBLY NAME>_info global symbol from the object file:
extern void *mono_aot_module_hello_info;

mono_aot_register_module (mono_aot_module_hello_info);
  • stats โ€” print various stats collected during AOT compilation. ๐Ÿ“Š
  • temp-path=[path] โ€” explicitly specify the path to store temporary files created during AOT compilation.
  • threads=[number] โ€” this is an experimental option for the AOT compiler to use multiple threads when compiling the methods.
  • tool-prefix=<PREFIX> โ€” prepends <PREFIX> to the name of tools ran by the AOT compiler, i.e. 'as'/'ld'. For example, --tool=prefix=arm-linux-gnueabi- will make the AOT compiler run
  • ld-name=NAME โ€” one of the tools used for AOT builds is the linker. Its name differs between various systems and it may happen that the assumed default name of the binary isn't present. If the toolchain used doesn't have a linker with the default name (e.g. Android NDK r22 does not have the default 'ld' linker prefixed with 'tool-prefix' above, instead it has prefixed 'ld.gold' and 'ld.bfd' linkers) this option can be used to set the linker binary name. It will be prefixed with 'tool-prefix' to form the full linker executable name. ๐Ÿ”—
  • verbose โ€” prints additional information about type loading failures. ๐Ÿ—ฃ๏ธ
  • write-symbols,no-write-symbols โ€” instructs the AOT compiler to emit (or not emit) debug symbol information.
  • no-opt โ€” instructs the AOT compiler tot no call opt when compiling with LLVM. (yes bestie, that's the original wording, we're keeping it โœ‹)
  • For more information about AOT, see: http://www.mono-project.com/docs/advanced/aot/

  • --aot-path=PATH โ€” list of additional directories to search for AOT images. ๐Ÿ”
  • --apply-bindings=FILE โ€” apply the assembly bindings from the specified configuration file when running the AOT compiler. Super handy when you're compiling an auxiliary assembly that's referenced by a main assembly which provides a configuration file. Like, if app.exe uses lib.dll then in order to make the assembly bindings from app.exe.config available when compiling lib.dll ahead of time, use:
mono --apply-bindings=app.exe.config --aot lib.dll
  • --assembly-loader=MODE โ€” if mode is strict, Mono will check that the public key token, culture and version of a candidate assembly matches the requested strong name. If mode is legacy, as long as the name matches, the candidate will be allowed. strict is the behavior consistent with .NET Framework but may break some existing mono-based applications. The default is legacy. ๐Ÿ•ต๏ธโ™€๏ธ
  • --attach=[options] โ€” currently the only option supported by this command line argument is disable, which disables the attach functionality.
  • --config filename โ€” load the specified configuration file instead of the default one(s). The default files are /etc/mono/config and ~/.mono/config or the file specified in the MONO_CONFIG environment variable, if set. See the mono-config(5) man page for details on the format of this file. ๐Ÿ“œ
  • --debugger-agent=[options] โ€” this instructs the Mono runtime to start a debugging agent inside the Mono runtime and connect it to a client user interface that will control the Mono process. This option is typically used by IDEs, like the MonoDevelop or Visual Studio IDEs. The configuration is specified using one of more of the following options:
    • address=host:port โ€” use this option to specify the IP address where your debugger client is listening to. ๐Ÿ“ก
    • loglevel=LEVEL โ€” specifies the diagnostics log level for
    • logfile=filename โ€” used to specify the file where the log will be stored, it defaults to standard output.
    • server=[y/n] โ€” defaults to no, with the default option Mono will actively connect to the host/port configured with the address option. If you set it to 'y', it instructs the Mono runtime to start debugging in server mode, where Mono actively waits for the debugger front end to connect to the Mono process. Mono will print out to stdout the IP address and port where it is listening. ๐Ÿ›ฐ๏ธ
  • setpgid=[y/n] โ€” set this to yes and Mono will call setpgid(0, 0) on startup, but only if that function actually exists on your system. Why do you care? Because it stops signals aimed at a process running the debuggee from leaking through to the debuggee itself โ€” like when Ctrl-C fires off SIGINT at the sdb tool. ๐Ÿ›ก๏ธ
  • suspend=[y/n] โ€” defaults to yes, and with that default Mono will pause the VM on startup and justโ€ฆ wait, until it successfully connects to a debugger front end. Set it to 'n' and pair it with server=y, and instead the Mono runtime runs like normal while it caches metadata to hand over to the debugger front end whenever that connection happens. ๐Ÿ’คโžก๏ธโœจ
  • transport=transport_name โ€” tells the debugger which transport to talk over. It must be specified, and rn it has to be 'dt_socket'. No substitutes, bestie. ๐Ÿšš

  • --desktop โ€” tunes the VM to vibe better with desktop apps. Right now that means it tells the GC system to avoid growing the heap as much as it can, at the cost of making garbage collection a lil slower. Trade-off, but make it cozy. ๐Ÿ–ฅ๏ธ
  • --full-aot โ€” this one tells the Mono runtime: no codegen at runtime, periodt. It depends entirely on code that was already generated by running mono --aot=full beforehand. Super useful on platforms that won't let you generate dynamic code, or if you need to run assemblies that got stripped of their IL (say, via mono-cil-strip). โš ๏ธ Heads up though โ€” this will abort execution at runtime if any codepath in your program or in Mono's class libraries tries to generate code dynamically. Test your software upfront and make sure you're not leaning on any dynamic features, fr fr.
  • --full-aot-interp โ€” same as --full-aot, but with a fallback to the interpreter. ๐Ÿช‚
  • --gc=boehm, --gc=sgen โ€” picks which Garbage Collector engine Mono uses: Boehm or SGen. Right now this basically just makes sure you're running either the mono or mono-sgen commands. You can set this flag in the MONO_ENV_OPTIONS environment variable to force all your child processes onto one particular kind of GC with the Mono runtime. ๐Ÿ‘‘
  • --gc-debug=[options] โ€” the command line equivalent of the MONO_GC_DEBUG environment variable. ๐Ÿชž
  • --gc-params=[options] โ€” the command line equivalent of the MONO_GC_PARAMS environment variable. ๐Ÿชž
  • --arch=32, --arch=64 โ€” (Mac OS X only ๐ŸŽ): selects the bitness of the Mono binary being used, if it's available. If the binary you're already on matches the bitness you picked, nothing changes. If not, execution switches to a binary with the selected bitness suffix installed side by side. For example, '/bin/mono --arch=64' will switch to '/bin/mono64' iff '/bin/mono' is a 32-bit build. โœจ
  • --help, -h โ€” shows usage instructions. The classic. ๐Ÿซถ
  • --interpreter โ€” the Mono runtime will use its interpreter to execute a given assembly. The interpreter is usually slower than the JIT, but it comes in clutch on platforms where code generation at runtime isn't allowed. ๐Ÿข
  • --hybrid-aot โ€” lets the Mono runtime run assemblies that have been stripped of IL, e.g. with mono-cil-strip. For that to work, the assembly must have been AOT compiled with --aot=hybrid. This flag is similar to --full-aot, but it does not disable the JIT. Which means you can use dynamic features like System.Reflection.Emit. ๐Ÿงฌ
  • --llvm โ€” if the Mono runtime was compiled with LLVM support (not available in all configurations), Mono will use the LLVM optimization and code generation engine to JIT or AOT compile. More info here: http://www.mono-project.com/docs/advanced/mono-llvm/ ๐Ÿ”ฅ
  • --ffast-math โ€” lets Mono and LLVM apply aggressive floating point optimizations. Can break IEEE754 compliance. You've been warned, babe. ๐Ÿงฎ๐Ÿ’€
  • --nollvm โ€” when you're on a Mono that was compiled with LLVM support, this forces Mono to fall back to its JIT engine and not use the LLVM backend. ๐Ÿšช
  • --optimize=MODE, -O=MODE โ€” MODE is a comma separated list of optimizations. You can also turn optimizations off by prefixing the optimization name with a minus sign. In general Mono has been tuned to use the default set of flags โ€” so before you start using these for a deployment setting, maybe actually measure whether they're helping you. ๐Ÿ‘€ The following optimization flags are implemented in the core engine:
    • abcrem โ€” Array bound checks removal
    • all โ€” Turn on all optimizations
    • aot โ€” Usage of Ahead Of Time compiled code
    • branch โ€” Branch optimizations
    • cfold โ€” Constant folding
    • cmov โ€” Conditional moves [arch-dependency]
    • deadce โ€” Dead code elimination
    • consprop โ€” Constant propagation
    • copyprop โ€” Copy propagation
    • fcmov โ€” Fast x86 FP compares [arch-dependency]
    • float32 โ€” Perform 32-bit float arithmetic using 32-bit operations
    • gshared โ€” Enable generic code sharing.
    • inline โ€” Inline method calls
    • intrins โ€” Intrinsic method implementations
    • linears โ€” Linear scan global reg allocation
    • leaf โ€” Leaf procedures optimizations
    • loop โ€” Loop related optimizations
    • peephole โ€” Peephole postpass
    • precomp โ€” Precompile all methods before executing Main
    • sched โ€” Instruction scheduling
    • shared โ€” Emit per-domain code
    • sse2 โ€” SSE2 instructions on x86 [arch-dependency]
    • tailc โ€” Tail recursion and tail calls

For example, to enable all the optimizations except dead code elimination and inlining, you can use:

-O=all,-deadce,-inline

The flags marked [arch-dependency] mean that if you use that option together with Ahead of Time compilation (the --aot flag), it'll produce pre-compiled code that depends on the current CPU โ€” so it might not be safe to move to another computer. ๐Ÿ–ฅ๏ธโžก๏ธ๐Ÿšซ๐Ÿ–ฅ๏ธ

The following optimizations are supported:

  • float32 โ€” requests that the runtime perform 32-bit floating point operations using only 32-bits. By default the Mono runtime tries to use the highest precision available for floating point operations, but while that might give you better results, the code might run slower. This option also affects the code generated by the LLVM backend. ๐ŸŽฏ
  • inline โ€” controls whether the runtime should attempt to inline (the default), or not inline method invocations. ๐Ÿซฅ
  • --response=FILE โ€” provides a response file. This tells the Mono command to read other command line options out of the specified file, as if they'd been given on the command line. Chef's kiss when your command line is absurdly long. ๐Ÿ“„โœจ
  • --runtime=VERSION โ€” Mono supports different runtime versions. Which one gets used depends on the program being run or on its configuration file (named program.exe.config). This option overrides that autodetection by forcing a different runtime version. Note that you should only use this to pick a later, compatible runtime version than the one the program was compiled against. A typical usage is running a 1.1 program on a 2.0 version:
mono --runtime=v2.0.50727 program.exe
  • --server โ€” tunes the VM to be better suited for server operations (currently, it allows a heavier threadpool initialization). ๐Ÿ–ง๐Ÿ’ช
  • --use-map-jit โ€” tells Mono to generate code using MAP_JIT on MacOS. Necessary for bundled applications. ๐ŸŽ
  • --verify-all โ€” verifies mscorlib and assemblies in the global assembly cache for valid IL, and all user code for IL verifiability. ๐Ÿ” This is different from --security's verifiable or validil, in that those options only check user code and skip mscorlib and assemblies located on the global assembly cache.
  • -V, --version โ€” prints JIT version information (system configuration, release number and branch names if available). ๐Ÿท๏ธ
  • --version=number โ€” prints the version number only. Just the number, nothing else. ๐Ÿคซ

โœจ DEVELOPMENT OPTIONS โœจ

okay bestie, these next ones are the lil helpers you use when you're building a JITed application and you need to actually see what's going on inside. think of them as your backstage pass ๐ŸŽ€

  • --debug, --debug=OPTIONS โ€” flips on debugging mode in the runtime. if your assembly was compiled with debugging info, you'll get line number info in your stack traces, which is so much nicer than staring at a wall of hex like ๐Ÿ‘๏ธ๐Ÿ‘„๐Ÿ‘๏ธ

the optional OPTIONS argument is a comma separated list of debugging options. heads up though โ€” these are off by default because they generate way bigger and slower code at runtime. no free lunch, babes.

here's what you can throw in there:

  • casts โ€” throws a detailed error when an InvalidCastException gets thrown. this one needs to be explicitly enabled since it generates more verbose code at execution time.
  • mdb-optimizations โ€” disables some JIT optimizations that are usually only disabled when you're running inside the debugger. helpful if you wanna attach to the running process with mdb ๐Ÿ’…
  • gdb โ€” generates and registers debugging information with gdb. only supported on some platforms, and only when you're using gdb 7.0 or later. sorry to the old-gdb girlies.
  • --profile[=profiler[:profiler_args]] โ€” loads a profiler module with the given arguments. more deets live in the PROFILING section. you can use this option multiple times, and each time it loads an additional profiler module. stack 'em up ๐Ÿงš
  • --trace[=expression] โ€” shows method names as they get invoked. by default, everything is traced. but you can customize it to include or exclude methods, classes, or assemblies. a trace expression is a comma separated list of targets, and each target can be prefixed with a minus sign to turn that particular target off. the words program, all and disabled are special:
    • program = the main program being executed
    • all = all the method calls
    • disabled = start up with tracing off. you can turn it on later in the program by sending the SIGUSR2 signal to the runtime. iconic.

assemblies get specified by their name. so like, to trace all calls in the System assembly, you'd do:

mono --trace=System app.exe

classes use the T: prefix. to trace all calls to the System.String class:

mono --trace=T:System.String app.exe

and individual methods use the M: prefix with the standard method notation:

mono --trace=M:System.Console:WriteLine app.exe

exceptions can be traced too! it'll print a stack trace every single time an exception of the specified type gets thrown. the exception type can be specified with or without the namespace, and if you wanna trace all exceptions, just specify all as the type name:

mono --trace=E:System.Exception app.exe

and yes, you can stack a bunch of rules at once, obviously:

mono --trace=T:System.String,T:System.Random app.exe

you can also exclude pieces โ€” this next one traces calls to System.String except for the System.String:Concat method:

mono --trace=T:System.String,-M:System.String:Concat

you can trace managed to unmanaged transitions with the wrapper qualifier:

mono --trace=wrapper app.exe

and finally, namespaces use the N: prefix:

mono --trace=N:System.Xml
  • --no-x86-stack-align โ€” don't align stack frames on the x86 architecture. by default, Mono aligns stack frames to 16 bytes on x86 so that local floating point and SIMD variables can be properly aligned. this option turns that off, which usually saves one instruction per callโ€ฆ but might result in significantly lower floating point and SIMD performance. so like, tradeoff bestie, choose wisely โš–๏ธ
  • --jitmap โ€” generates a JIT method map in a /tmp/perf-PID.map file. this file then gets used by, for example, the perf tool included in recent Linux kernels. each line in the file looks like:
HEXADDR HEXSIZE methodname
currently this option is only supported on Linux. ๐Ÿง

๐Ÿ”ง JIT MAINTAINER OPTIONS ๐Ÿ”ง

okay so these ones? not for you unless you're literally developing the runtime itself. they're not typically of interest to runtime users or developers. maintainer-only vibes, iykyk ๐Ÿ’€

  • --bisect=optimization:filename โ€” used by the automatic optimization bug bisector. it takes an optimization flag and a filename of a file containing a list of full method names, one per line. when it compiles one of the methods in that file, it'll use the optimization given, in addition to the optimizations that are otherwise enabled. note: if the optimization is enabled by default, you should disable it with -O, otherwise it'll just apply to every method whether it's in the file or not. sneaky like that ๐Ÿ™ˆ
  • --break method โ€” inserts a breakpoint before the method whose name is method (namespace.class:methodname). use Main as the method name to insert a breakpoint on the application's main method. you can use it with generics too, for example "System.Collections.Generic.Queue1:Peek"`
  • --breakonex โ€” inserts a breakpoint on exceptions. this lets you debug your application with a native debugger when an exception is thrown. very main character debugging energy ๐Ÿ’…
  • --compile name โ€” compiles a method (namespace.name:methodname). this is used for testing the compiler performance or to examine the output of the code generator.
  • --compile-all โ€” compiles all the methods in an assembly. used to test the compiler performance or to examine the output of the code generator. go big or go home ๐Ÿ”ฅ
  • --graph=TYPE METHOD โ€” generates a postscript file with a graph containing details about the specified method (namespace.name:methodname). this requires dot and ghostview to be installed (it expects Ghostview to be called "gv"). the graphs available are:
    • cfg โ€” Control Flow Graph (CFG)
    • dtree โ€” Dominator Tree
    • code โ€” CFG showing code
    • ssa โ€” CFG showing code after SSA translation
    • optcode โ€” CFG showing code after IR optimizations

some graphs will only be available if certain optimizations are turned on. ๐ŸŽ€

  • --ncompile โ€” tells the runtime the number of times that the method specified by --compile (or all the methods if --compile-all is used) should be compiled. this is used for testing the code generator performance.
  • --stats=[method] โ€” displays information about the work done by the runtime during the execution of an application. if a method (namespace.name:methodname) is specified, it'll display that information when the method is first run, in addition to the end of program execution. double the stats, double the fun โœจ
  • --wapi=hps|semdel โ€” performs maintenance of the process shared data. semdel deletes the global semaphore. hps lists the currently used handles.
  • -v, --verbose โ€” increases the verbosity level. each time it's listed, it bumps the verbosity up to include more information (including, for example, a disassembly of the native code produced, code selector info, etc.). stack -vs for maximum yapping ๐Ÿ—ฃ๏ธ

๐Ÿซถ ATTACH SUPPORT ๐Ÿซถ

bestie. BESTIE. this one is so cool. the Mono runtime lets external processes attach to a running process and load assemblies into the running program. to attach, a special protocol is implemented in the Mono.Management assembly. it's giving remote control for your runtime ๐Ÿ˜ญ๐Ÿ’–

with this support, you can load assemblies that have an entry point (they're created with -target:exe or -target:winexe) to be loaded and executed in the Mono process.

the code gets loaded into the root domain, and it starts execution on the special runtime attach thread. the attached program should create its own threads and return after invocation. no hogging the thread, periodt โœ‹

this support allows for, example, debugging applications by having the csharp shell attach to running processes. so you can justโ€ฆ vibe check a live process. iconic. ๐ŸŒˆ

๐ŸŽ€ PROFILING ๐ŸŽ€

okay so bestie, this is the good part. ๐Ÿ’…

The Mono runtime includes a profiler API that dynamically loaded profiler modules and embedders can use to collect performance-related data about an application. Profiler modules are loaded by passing the --profile command line argument to the Mono runtime.

Mono ships with a few profiler modules, of which the log profiler is the most feature-rich. It is also the default profiler if the profiler argument is not given, or if default is given. It is possible to write your own profiler modules; see the Custom profilers sub-section. ๐Ÿซถ

โœจ Log profiler โœจ

The log profiler can be used to collect a lot of information about a program running in the Mono runtime. This data can be used (both while the process is running and later) to do analyses of the program behavior, determine resource usage, performance issues or even look for particular execution patterns. it's giving detective energy fr ๐Ÿ”

This is accomplished by logging the events provided by the Mono runtime through the profiler API and periodically writing them to a file which can later be inspected with the mprof-report(1) tool.

More information about how to use the log profiler is available on the mono-profilers(1) page, under the LOG PROFILER section, as well as the mprof-report(1) page.

๐Ÿฆ‹ Coverage profiler ๐Ÿฆ‹

The code coverage profiler can instrument a program to help determine which classes, methods, code paths, etc are actually executed. This is most useful when running a test suite to determine whether the tests actually cover the code they're expected to. literally "did you even run that line bestie" ๐Ÿ’€

More information about how to use the coverage profiler is available on the mono-profilers(1) page, under the COVERAGE PROFILER section.

๐ŸŒธ AOT profiler ๐ŸŒธ

The AOT profiler can help improve startup performance by logging which generic instantiations are used by a program, which the AOT compiler can then use to compile those instantiations ahead of time so that they won't have to be JIT compiled at startup. speedrunning startup, iconic behavior ๐Ÿ‘‘

More information about how to use the AOT profiler is available on the mono-profilers(1) page, under the AOT PROFILER section.

๐Ÿ’œ Custom profilers ๐Ÿ’œ

Custom profiler modules can be loaded in exactly the same way as the standard modules that ship with Mono. They can also access the same profiler API to gather all kinds of information about the code being executed.

For example, to use a third-party profiler called custom, you would load it like this:

mono --profile=custom program.exe

You could also pass arguments to it:

mono --profile=custom:arg1,arg2=arg3 program.exe

In the above example, Mono will load the profiler from the shared library called libmono-profiler-custom.so (name varies based on platform, e.g., libmono-profiler-custom.dylib on OS X). This profiler module must be on your dynamic linker library path (LD_LIBRARY_PATH on most systems, DYLD_LIBRARY_PATH on OS X).

For a sample of how to write your own custom profiler, look at the samples/profiler/sample.c file in the Mono source tree. go off, write ur own ๐Ÿงš


๐Ÿ’€ DEBUGGING AIDS ๐Ÿ’€

To debug managed applications, you can use the mdb command, a command line debugger.

It is possible to obtain a stack trace of all the active threads in Mono by sending the QUIT signal to Mono, you can do this from the command line, like this:

kill -QUIT pid

Where pid is the Process ID of the Mono process you want to examine. The process will continue running afterwards, but its state is not guaranteed.

โš ๏ธ Important: this is a last-resort mechanism for debugging applications and should not be used to monitor or probe a production application. The integrity of the runtime after sending this signal is not guaranteed and the application might crash or terminate at any given point afterwards. like. genuinely. do not do this on prod, bestie, I am begging ๐Ÿ˜ญ

The --debug=casts option can be used to get more detailed information for Invalid Cast operations, it will provide information about the types involved.

You can use the MONO_LOG_LEVEL and MONO_LOG_MASK environment variables to get verbose debugging output about the execution of your application within Mono.

The MONO_LOG_LEVEL environment variable if set, the logging level is changed to the set value. Possible values are "error", "critical", "warning", "message", "info", "debug". The default value is "error". Messages with a logging level greater then or equal to the log level will be printed to stdout/stderr.

Use "info" to track the dynamic loading of assemblies.

Use the MONO_LOG_MASK environment variable to limit the extent of the messages you get: If set, the log mask is changed to the set value. Possible values are "asm" (assembly loader), "type", "dll" (native library loader), "gc" (garbage collector), "cfg" (config file loader), "aot" (precompiler), "security" (e.g. Moonlight CoreCLR support), "threadpool" (thread pool generic), "io-selector" (async socket operations), "io-layer" (I/O layer - processes, files, sockets, events, semaphores, mutexes and handles), "io-layer-process", "io-layer-file", "io-layer-socket", "io-layer-event", "io-layer-semaphore", "io-layer-mutex", "io-layer-handle" and "all". The default value is "all". Changing the mask value allows you to display only messages for a certain component. You can use multiple masks by comma separating them. For example to see config file messages and assembly loader messages set you mask to "asm,cfg".

The following is a common use to track down problems with P/Invoke:

$ MONO_LOG_LEVEL="debug" MONO_LOG_MASK="dll" mono glue.exe

๐ŸŒˆ DEBUGGING WITH LLDB ๐ŸŒˆ

If you are using LLDB, you can use the mono.py script to print some internal data structures with it. To use this, add this to your $HOME/.lldbinit file:

command script import $PREFIX/lib/mono/lldb/mono.py

Where $PREFIX is the prefix value that you used when you configured Mono (typically /usr).

Once this is done, then you can inspect some Mono Runtime data structures, for example:

(lldb) p method

(MonoMethod *) $0 = 0x05026ac0 [mscorlib]System.OutOfMemoryException:.ctor()

peek inside the runtime bestie, we love a lil look behind the curtain ๐Ÿ‘€โœจ


๐Ÿ“ SERIALIZATION ๐Ÿ“

Mono's XML serialization engine by default will use a reflection-based approach to serialize which might be slow for continuous processing (web service applications). The serialization engine will determine when a class must use a hand-tuned serializer based on a few parameters and if needed it will produce a customized C# serializer for your types at runtime. This customized serializer then gets dynamically loaded into your application.

You can control this with the MONO_XMLSERIALIZER_THS environment variable.

The possible values are no to disable the use of a C# customized serializer, or an integer that is the minimum number of uses before the runtime will produce a custom serializer (0 will produce a custom serializer on the first access, 50 will produce a serializer on the 50th use). Mono will fallback to an interpreted serializer if the serializer generation somehow fails. This behavior can be disabled by setting the option nofallback (for example: MONO_XMLSERIALIZER_THS=0,nofallback).


๐Ÿ’– ENVIRONMENT VARIABLES ๐Ÿ’–

GC_DONT_GC

Turns off the garbage collection in Mono. This should be only used for debugging purposes. (yes, only for debugging, she's not a lifestyle, she's a phase ๐Ÿšซ๐Ÿ—‘๏ธ)

HTTP_PROXY

(Also http_proxy) If set, web requests using the Mono Class Library will be automatically proxied through the given URL. Not supported on Windows, Mac OS, iOS or Android. See also NO_PROXY.

LLVM_COUNT

When Mono is compiled with LLVM support, this instructs the runtime to stop using LLVM after the specified number of methods are JITed. This is a tool used in diagnostics to help isolate problems in the code generation backend. For example LLVM_COUNT=10 would only compile 10 methods with LLVM and then switch to the Mono JIT engine. LLVM_COUNT=0 would disable the LLVM engine altogether.

MONO_ASPNET_INHIBIT_SETTINGSMAP

Mono contains a feature which allows modifying settings in the .config files shipped with Mono by using config section mappers. The mappers and the mapping rules are defined in the $prefix/etc/mono/2.0/settings.map file and, optionally, in the settings.map file found in the top-level directory of your ASP.NET application. Both files are read by System.Web on application startup, if they are found at the above locations. If you don't want the mapping to be performed you can set this variable in your environment before starting the application and no action will be taken.

MONO_ASPNET_WEBCONFIG_CACHESIZE

Mono has a cache of ConfigSection objects for speeding up WebConfigurationManager queries. Its default size is 100 items, and when more items are needed, cache evictions start happening. If evictions are too frequent this could impose unnecessary overhead, which could be avoided by using this environment variable to set up a higher cache size (or to lower memory requirements by decreasing it).

MONO_CAIRO_DEBUG_DISPOSE

If set, causes Mono.Cairo to collect stack traces when objects are allocated, so that the finalization/Dispose warnings include information about the instance's origin.

MONO_CFG_DIR

If set, this variable overrides the default system configuration directory ($PREFIX/etc). It's used to locate machine.config file.

MONO_COM

Sets the style of COM interop. If the value of this variable is "MS" Mono will use string marshalling routines from the liboleaut32 for the BSTR type library, any other values will use the mono-builtin BSTR string marshalling.

MONO_CONFIG

If set, this variable overrides the default runtime configuration file ($PREFIX/etc/mono/config). The --config command line options overrides the environment variable.

MONO_CPU_ARCH

Override the automatic cpu detection mechanism. Currently used only on arm. The format of the value is as follows:

"armvV [thumb[2]]"

where V is the architecture number 4, 5, 6, 7 and the options can be currently be "thumb" or "thumb2". Example:

MONO_CPU_ARCH="armv4 thumb" mono ...

MONO_ARM_FORCE_SOFT_FLOAT

When Mono is built with a soft float fallback on ARM and this variable is set to "1", Mono will always emit soft float code, even if a VFP unit is detected.

MONO_DARWIN_USE_KQUEUE_FSW

Fall back on the kqueue FileSystemWatcher implementation in Darwin. The default is the FSEvent implementation.

MONO_DARWIN_WATCHER_MAXFDS

This is a debugging aid used to force limits on the kqueue FileSystemWatcher implementation in Darwin. There is no limit by default.

MONO_DISABLE_MANAGED_COLLATION

okay so if you set this one to yes, the runtime switches to unmanaged collation โ€” which, plot twist, actually means no culture-sensitive collation at all. it quietly turns off the managed collation stuff that gets called through the members of the System.Globalization.CompareInfo class. collation is on by default, so only touch this if you know why you're touching it ๐Ÿซถ


MONO_DISABLE_SHARED_AREA

Unix only, bestie. if it's set, you're disabling the use of shared memory for exposing performance counters. what that means practically: you won't be able to externally read perf counters from this process, and you also won't be able to read the counters of external processes. it's giving โœจisolationโœจ, but at a cost ๐Ÿ’€


MONO_DNS

set this and Mono uses a fully managed DNS resolver instead of the regular libc functions. it genuinely pops off when you've got a bunch of queries running in parallel โ€” like, way better. ๐Ÿ”ฅ

heads up though: /etc/nsswitch.conf gets ignored entirely. no cap.

MONO_EGD_SOCKET

for platforms that don't have any other way of getting random bytes, you can set this to the name of a file system socket where an egd or prngd daemon is listening. entropy where you can find it, iykyk ๐ŸŽ€


MONO_ENABLE_AIO

if it's set, Mono will attempt to use native asynchronous I/O services. if it's not set, you get the default select/poll implementation instead. rn epoll and kqueue are the ones supported ๐Ÿ‘‘


MONO_THREADS_SUSPEND

this one picks the mechanism Mono uses to suspend threads. you can set it to "preemptive", "coop", or "hybrid".

so like, why do threads even need suspending? the debugger might need it, or some .NET threading APIs, and most commonly the SGen garbage collector needs to stop all the threads during a critical phase of collection.

  • Preemptive mode โ€” the mode Mono has used historically, all the way back to the Boehm days. the GC just runs whenever and suspends execution of all threads as needed to do a collection.
  • Cooperative mode โ€” requires all threads to cooperate and stop at a safe point. this makes for a GC that's easier to debug, and it improves runtime stability, because threads aren't suspended while they're accessing critical resources. the catch: if Mono is embedded in another app, cooperative suspend means the embedder code has to follow coding guidelines to cooperate with the GC. cooperative suspend in embedded Mono is currently experimental.
  • Hybrid mode โ€” a combo of the two, and it keeps better compatibility with embedded scenarios: threads running managed code, or code that makes up the Mono runtime, get suspended cooperatively, while threads running embedder code get suspended preemptively. hybrid suspend is the default on some desktop platforms.

alternatively, coop and hybrid can be turned on at compile time with the --enable-cooperative-suspend or --enable-hybrid-suspend flags when you call configure. but the MONO_THREADS_SUSPEND environment variable takes priority over whatever the compiled default is ๐Ÿ’…


MONO_ENABLE_COOP_SUSPEND

this one is obsolete ๐Ÿ’€ but it's still here for backward compatibility. use MONO_THREADS_SUSPEND set to "coop" instead. and note: if configure flags were given to enable cooperative or hybrid suspend, this variable is just ignored.


MONO_ENV_OPTIONS

this is the fun one โ€” it lets you pass command line arguments to a Mono process through the environment. super handy if you want, say, to force all of your Mono processes to use LLVM or SGEN without editing a single launch script. obsessed โœจ


MONO_SDB_ENV_OPTIONS

used to pass extra options to the debugger agent inside the runtime, exactly as they'd be passed with --debugger-agent= ๐Ÿ›


MONO_EVENTLOG_TYPE

sets which type of event log provider to use (this is for System.Diagnostics.EventLog). possible values are:

  • local[:path] โ€” persists event logs and entries to the local file system. you can specify the directory to persist event logs, event sources and entries in as part of the value. if the path isn't explicitly set, it defaults to /var/lib/mono/eventlog on unix and %APPDATA%no\ventlog on Windows.
  • win32 โ€” uses the native win32 API to write events and registers event logs and event sources in the registry. this is only available on Windows. on Unix, the directory permission for individual event log and event source directories is set to 777 (with the +t bit), which lets everyone read and write event log entries while only allowing entries to be deleted by the user(s) that created them.
  • null โ€” silently discards any events. ๐Ÿซฅ

the default is "null" on Unix (and versions of Windows before NT), and "win32" on Windows NT (and higher).


MONO_EXTERNAL_ENCODINGS

if set, this holds a colon-separated list of text encodings to try when turning externally-generated text โ€” think command-line arguments or filenames โ€” into Unicode. the encoding names come from the list iconv provides, plus the special case "default_locale", which means the current locale's default encoding.

  • when reading externally-generated text strings, UTF-8 is tried first, and then this list gets tried in order, with the first successful conversion ending the search.
  • when writing external text โ€” new filenames, arguments to new processes, that kind of thing โ€” the first item in this list is used, or UTF-8 if the environment variable isn't set.
okay so there's a real problem with using MONO_EXTERNAL_ENCODINGS to process your files, and it's this: even though you might be able to get the right file name, it's not necessarily possible to actually open the file. ๐Ÿ˜ญ in general, if you're having encoding problems with your filenames, use the "convmv" program instead.

MONO_GC_PARAMS

when you're running Mono with the SGen garbage collector, this variable controls a bunch of the collector's parameters. its value is a comma separated list of words. here's the whole menu, bestie:

  • max-heap-size=size โ€” sets the maximum size of the heap. the size is in bytes and must be a power of two. the suffixes `k', `m' and `g' can be used for kilo-, mega- and gigabytes. the limit is the sum of the nursery, major heap and large object heap. once you hit the limit, the app starts getting OutOfMemoryExceptions when it tries to allocate. note that not the full extent of memory set in max-heap-size could be available to satisfy a single allocation, because of internal fragmentation. by default the heap limit is disabled and the GC will try to use all available memory.
  • nursery-size=size โ€” sets the size of the nursery. also in bytes, also must be a power of two, and yes, the `k', `m' and `g' suffixes work here too. the nursery is the first generation (of two). a bigger nursery will usually speed up your program but will obviously use more memory. default nursery size is 4 MB.
  • major=collector โ€” picks which major collector to use. options are `marksweep' for the Mark&Sweep collector, `marksweep-conc' for concurrent Mark&Sweep, and `marksweep-conc-par' for parallel and concurrent Mark&Sweep. the concurrent Mark&Sweep collector is the default.
  • mode=balanced|throughput|pause[:max-pause] โ€” says what the GC's target should be. `throughput' mode aims to cut down time spent in the GC and improve application speed, `pause' mode aims to keep pause times minimal and takes the max-pause argument, which specifies the maximum acceptable pause time in milliseconds, and `balanced' mode is the general purpose optimal one. ๐Ÿ’…
  • soft-heap-limit=size โ€” once the heap size gets bigger than this, ignore whatever the default major collection trigger metric says and only allow four nursery size's of major heap growth between major collections.
  • evacuation-threshold=threshold โ€” sets the evacuation threshold in percent. only available on the Mark&Sweep major collectors. the value must be an integer in the range 0 to 100. the default is 66. if the sweep phase of the collection finds that the occupancy of a specific heap block type is less than this percentage, it'll do a copying collection for that block type in the next major collection, which restores occupancy to close to 100 percent. a value of 0 turns evacuation off.
  • (no-)lazy-sweep โ€” enables or disables lazy sweep for the Mark&Sweep collector. if enabled, the sweeping of individual major heap blocks happens piecemeal whenever the need comes up, typically during nursery collections. lazy sweeping is enabled by default.
  • (no-)concurrent-sweep โ€” enables or disables concurrent sweep for the Mark&Sweep collector. if enabled, the iteration over all major blocks to figure out which ones can be freed and which ones have to be kept and swept happens concurrently with the running program. concurrent sweeping is enabled by default.
  • stack-mark=mark-mode โ€” tells the runtime how it should scan your application threads, bestie. Options are precise and conservative. precise marking means the collector actually knows which values on the stack are references and which are just... random little numbers living their lives. conservative marking is the paranoid girlie: it treats every value as a potential reference and leaves them all untouched. Precise marking cuts down on floating garbage and can speed up nursery collection and allocation rate โœจ โ€” but the trade-off is a significant extra memory cost per compiled method ๐Ÿ˜ญ. Which one is right for you? Honestly? Experimentation. The manpage said it, not me.
  • save-target-ratio=ratio โ€” sets the target save ratio for the major collector. Here's the vibe: the collector lets a certain amount of memory get promoted out of the nursery (via minor collections) before it goes "okay enough" and triggers a major collection. That amount is based on how much memory it expects to free, and it's expressed as a ratio of the size of the heap after a major collection. Valid values are between 0.1 and 2.0. Default is 0.5. Smaller values keep the major heap smaller but trigger more major collections. Bigger values use more memory and mean less frequent major collections. > This option is EXPERIMENTAL, so it might just ghost us in later versions of mono ๐Ÿ’€
  • default-allowance-ratio=ratio โ€” the default allocation allowance for when the calculated size is too small. The allocation allowance is how much memory the collector lets be promoted before triggering a major collection, and it's a ratio of the nursery size. Valid values are between 1.0 and 10.0. Default is 4.0. Smaller values โ†’ smaller heaps, more frequent major collections. Bigger values โ†’ heap grows faster but eats more memory once it hits a stable size. > Also EXPERIMENTAL, so it might disappear in later versions of mono. Same energy, different option ๐Ÿ™ˆ
  • minor=minor-collector โ€” picks which minor collector you're running with. Options: simple, which promotes all objects from the nursery straight into the old generation; simple-par, which has the same promotion behavior as simple but with multiple workers (parallel slay ๐Ÿ’…); and split, which lets objects hang out in the nursery a bit longer before promoting.
  • alloc-ratio=ratio โ€” the ratio of memory from the nursery that gets used by the alloc space. This one only works with the split minor collector, no exceptions. Valid values are integers between 1 and 100. Default is 60.
  • promotion-age=age โ€” how old an object has to be inside the nursery before it gets promoted to the old generation. Again, this only works with the split minor collector. Valid values are integers between 1 and 14. Default is 2.
  • (no-)cementing โ€” enables or disables cementing. On some benchmarks where pinned objects are referred to from the major heap, this can dramatically shorten nursery collection times. Dramatically!! ๐Ÿ”ฅ
  • allow-synchronous-major โ€” this forbids the major collector from performing synchronous major collections. The major collector might want to do a synchronous collection because of excessive fragmentation, and disabling that can trigger an OutOfMemory error in situations that otherwise wouldn't happen. So like... know what you're doing, bestie ๐Ÿซถ

๐Ÿ’– MONO_GC_DEBUG ๐Ÿ’–

When you're running Mono with the SGen garbage collector, this environment variable turns on the collector's various debugging features. Its value is a comma-separated list of words. > Do not use these options in production. I'm so serious. Not even a little bit.

  • number โ€” sets the debug level to the specified number.
  • print-allowance โ€” after each major collection, prints memory consumption before and after the collection, plus the allowance for the minor collector โ€” i.e. how much the heap is allowed to grow from minor collections before the next major collection gets triggered.
  • print-pinning โ€” gathers statistics on the classes whose objects are pinned in the nursery, and for which global remset entries are added. Prints those stats when shutting down.
  • collect-before-allocs
  • check-remset-consistency โ€” performs a remset consistency check at various opportunities, and also clears the nursery at collection time instead of the default, which is when buffers are allocated (clear-at-gc). The consistency check makes sure there are no major-to-minor references that aren't on the remembered sets.
  • mod-union-consistency-check โ€” checks that the mod-union cardtable is consistent before each finishing major collection pause. Only applicable to concurrent major collectors.
  • check-mark-bits โ€” checks that the mark bits in the major heap are consistent at the end of each major collection. Consistent mark bits mean that if an object is marked, all the objects it had references to must also be marked.
  • check-nursery-untag โ€” after garbage collections, checks whether all vtable pointers are no longer tagged.
  • xdomain-checks โ€” performs a check to make sure no references are left to an unloaded AppDomain.
  • clear-at-tlab-creation โ€” clears the nursery incrementally when the thread local allocation buffers (TLAB) are created. The default setting clears the whole nursery at GC time.
  • debug-clear-at-tlab-creation โ€” clears the nursery incrementally when the thread local allocation buffers (TLAB) are created, but at GC time fills it with the byte 0xff, which should result in a crash more quickly if clear-at-tlab-creation doesn't work properly. Chaos as a debugging tool, iconic honestly ๐Ÿ’€
  • clear-at-gc โ€” clears the nursery at GC time instead of doing it when the thread local allocation buffer (TLAB) is created. The default is to clear the nursery at TLAB creation time.
  • disable-minor โ€” don't do minor collections. If the nursery fills up, a major collection gets triggered instead โ€” unless that's disabled too.
  • disable-major โ€” don't do major collections.
  • conservative-stack-mark โ€” forces the GC to scan the stack conservatively, even when precise scanning is available.
  • no-managed-allocator โ€” disables the managed allocator.
  • managed-allocator โ€” enables the managed allocator.
  • check-scan-starts โ€” if set, does a plausibility check on the scan_starts before and after each collection.
  • verify-nursery-at-minor-gc โ€” if set, does a complete object walk of the nursery at the start of each minor collection.
  • dump-nursery-at-minor-gc โ€” if set, dumps the contents of the nursery at the start of each minor collection. Requires verify-nursery-at-minor-gc to be set.
  • heap-dump=file โ€” dumps the heap contents to the specified file. To visualize the information, use the mono-heapviz tool.
  • binary-protocol=file โ€” outputs the debugging output to the specified file. For this to work, Mono needs to be compiled with the BINARY_PROTOCOL define on sgen-gc.c. You can then use this command to explore the output:
sgen-grep-binprot 0x1234 0x5678 < file
  • nursery-canaries โ€” if set, objects allocated in the nursery are suffixed with a canary (guard) word, which is checked on each minor collection. Can be used to detect/debug heap corruption issues. This disables the usage of the managed allocator, because allocation from full aot code is inconsistent with this option. If the application is guaranteed not to use aot code, the managed allocator can be enabled back with the managed-allocator option.
  • do-not-finalize(=classes) โ€” if enabled, finalizers will not be run. Everything else is unaffected: finalizable objects will still be put into the finalization queue where they survive until they're scheduled to finalize. Once they're not in the queue anymore, they'll be collected regularly. If you give a list of comma-separated class names, only objects from those classes will not be finalized.
  • log-finalizers โ€” logs verbosely around the finalization process to help with debugging.

๐ŸŒธ MONO_GAC_PREFIX ๐ŸŒธ

Provides a prefix the runtime uses to look for Global Assembly Caches. Directories are separated by the platform path separator (colons on unix ๐Ÿซถ). MONO_GAC_PREFIX should point to the top directory of a prefixed install โ€” or to the directory provided in the gacutil /gacdir command. Example:

/home/username/.mono:/usr/local/mono/

๐Ÿ’œ MONO_IOMAP ๐Ÿ’œ

(deprecated) Enabled some filename rewriting support to assist badly-written applications that hard-code Windows paths. > It no longer works as of Mono 6.0. Rip to that era ๐Ÿ’€


โœจ MONO_LLVM โœจ

When Mono is using the LLVM code generation backend, you can use this environment variable to pass code generation options to the LLVM compiler.

  • MONO_MANAGED_WATCHER โ€” okay so if you set this to "disabled", then System.IO.FileSystemWatcher will use a file watcher implementation that silently ignores all the watching requests ๐Ÿ’€ like it's just not gonna tell you anything. If you set it to literally any other value, System.IO.FileSystemWatcher uses the default managed implementation (which is slow bestie, be warned). And if you don't set it at all? mono tries inotify, FAM, Gamin, kevent on Unix systems and native API calls on Windows, falling back to the managed implementation if it errors out. ๐Ÿฆ‹
  • MONO_MESSAGING_PROVIDER โ€” Mono supports a plugin model for its implementation of System.Messaging, which makes it possible to support a whole variety of messaging implementations (e.g. AMQP, ActiveMQ). To specify which messaging implementation gets used, you set this environment variable to the full class name for the provider. Like, for example, to use the RabbitMQ based AMQP implementation you'd set it to:
Mono.Messaging.RabbitMQ.RabbitMQMessagingProvider,Mono.Messaging.RabbitMQ
  • MONO_NO_SMP โ€” if you set this, it causes the mono process to be bound to a single processor. Lowkey useful when you're debugging or working around race conditions. ๐Ÿ”ฅ
  • MONO_NO_TLS โ€” disables inlining of thread local accesses. Try setting this if you get a segfault early on in the execution of mono. ๐Ÿ˜ญ
  • MONO_NO_TLS... no wait, same one, moving on. ๐Ÿ™ˆ
  • MONO_PATH โ€” provides a search path to the runtime telling it where to look for library files. This is a tool that's convenient for debugging applications, but should not be used by deployed applications because it breaks the assembly loader in subtle ways. โš ๏ธ Directories are separated by the platform path separator (colons on unix). Example:
/home/username/lib:/usr/local/mono/lib

Relative paths are resolved based on the launch-time current directory. Alternative solutions to MONO_PATH include: installing libraries into the Global Assembly Cache (see gacutil(1)) or having the dependent libraries side-by-side with the main executable. For a complete description of recommended practices for application deployment, see

http://www.mono-project.com/docs/getting-started/application-deployment/
  • MONO_SHARED_DIR โ€” if you set it, this is the directory where the ".wapi" handle state is stored. It's the directory where the Windows I/O Emulation layer stores its shared state data (files, events, mutexes, pipes). By default Mono will store the ".wapi" directory in the user's home directory. ๐Ÿ 
  • MONO_SHARED_HOSTNAME โ€” uses the string value of this variable as a replacement for the host name when creating file names in the ".wapi" directory. This helps if the host name of your machine is likely to be changed while a mono application is running, or if you have a .wapi directory shared among several different computers. Mono typically uses the hostname to create the files that are used to share state across multiple Mono processes. This is done to support home directories that might be shared over the network. ๐ŸŒ
  • MONO_STRICT_IO_EMULATION โ€” if set, extra checks are made during IO operations. Currently, this includes only advisory locks around file writes. ๐Ÿ”’
  • MONO_TLS_PROVIDER โ€” this environment variable controls which TLS/SSL provider Mono will use. The options are usually determined by the operating system where Mono was compiled and the configuration options that were used for it. The choices are:
    • default โ€” Uses the default TLS stack that the Mono runtime was configured with. Usually this is configured to use Apple's SSL stack on Apple platforms, and Boring SSL on other platforms.
    • apple โ€” Forces the use of the Apple SSL stack, only works on Apple platforms. ๐ŸŽ
    • btls โ€” Forces the use of the BoringSSL stack. See https://opensource.google.com/projects/boringssl for more information about this stack.
    • legacy โ€” This is the old Mono stack, which only supports SSL and TLS up to version 1.0. It is deprecated and will be removed in the future. ๐Ÿ’€
  • MONO_TLS_SESSION_CACHE_TIMEOUT โ€” the time, in seconds, that the SSL/TLS session cache will keep its entry to avoid a new negotiation between the client and a server. Negotiations are very CPU intensive so an application-specific custom value may prove useful for small embedded systems. The default is 180 seconds. โณ
  • MONO_THREADS_PER_CPU โ€” the minimum number of threads in the general threadpool will be MONO_THREADS_PER_CPU * number of CPUs. The default value for this variable is 1. ๐Ÿงต
  • MONO_XMLSERIALIZER_THS โ€” controls the threshold for the XmlSerializer to produce a custom serializer for a given class instead of using the Reflection-based interpreter. The possible values are no to disable the use of a custom serializer, or a number to indicate when the XmlSerializer should start serializing. The default value is 50, which means that a custom serializer will be produced on the 50th use. โœจ
  • MONO_X509_REVOCATION_MODE โ€” sets the revocation mode used when validating a X509 certificate chain (https, ftps, smtps...). The default is 'nocheck', which performs no revocation check at all. The other possible values are 'offline', which performs CRL check (not implemented yet) and 'online' which uses OCSP and CRL to verify the revocation status (not implemented yet). ๐ŸŽ€
  • NO_PROXY โ€” (Also no_proxy) If both HTTP_PROXY and NO_PROXY are set, NO_PROXY will be treated as a comma-separated list of "bypass" domains which will not be sent through the proxy. Domains in NO_PROXY may contain wildcards, as in "*.mono-project.com" or "build????.local". Not supported on Windows, Mac OS, iOS or Android. ๐Ÿšซ

๐Ÿ› ENVIRONMENT VARIABLES FOR DEBUGGING ๐Ÿ›

  • MONO_ASPNET_NODELETE โ€” if set to any value, temporary source files generated by ASP.NET support classes will not be removed. They will be kept in the user's temporary directory. ๐Ÿ—‚๏ธ
  • MONO_DEBUG โ€” if set, enables some features of the runtime useful for debugging. This variable should contain a comma separated list of debugging options. Currently, the following options are supported:
    • align-small-structs โ€” Enables small structs alignment to 4/8 bytes.
    • arm-use-fallback-tls โ€” When this option is set on ARM, a fallback thread local store will be used instead of the default fast thread local storage primitives.
    • break-on-unverified โ€” If this variable is set, when the Mono VM runs into a verification problem, instead of throwing an exception it will break into the debugger. This is useful when debugging verifier problems.
    • casts โ€” This option can be used to get more detailed information from InvalidCast exceptions, it will provide information about the types involved. ๐Ÿ”
    • check-pinvoke-callconv โ€” This option causes the runtime to check for calling convention mismatches when using pinvoke, i.e. mixing cdecl/stdcall. It only works on windows. If a mismatch is detected, an ExecutionEngineException is thrown.
    • collect-pagefault-stats โ€” Collects information about pagefaults. This is used internally to track the number of page faults produced to load metadata. To display this information you must use this option with --stats command line option.
    • debug-domain-unload โ€” When this option is set, the runtime will invalidate the domain memory pool instead of destroying it.
    • disable_omit_fp โ€” Disables a compiler optimization where the frame pointer is omitted from the stack. This optimization can interact badly with debuggers. ๐Ÿ™ˆ
    • dont-free-domains โ€” This is an Optimization for multi-AppDomain applications (most commonly ASP.NET applications). Due to internal limitations Mono, Mono by default does not use typed allocations on multi-appDomain applications as they could leak memory when a domain is unloaded. Although this is a fine default, for applications that use more than on AppDomain heavily (for example, ASP.NET applications) it is worth trading off the small leaks for the increased performance (additionally, since ASP.NET applications are not likely going to unload the application domains on production systems, it is worth using this feature). ๐Ÿ”ฅ
    • dyn-runtime-invoke โ€” Instructs the runtime to try to use a generic runtime-invoke wrapper instead of creating one invoke wrapper.
    • explicit-null-checks โ€” Makes the JIT generate an explicit NULL check on variable dereferences instead of depending on the operating system to raise a SIGSEGV or another form of trap event when an invalid memory location is accessed.
    • gdb โ€” Equivalent to setting the MONO_XDEBUG variable, this emits symbols into a shared library as the code is JITed that can be loaded into GDB to inspect symbols. ๐Ÿซถ
    • gen-seq-points โ€” Automatically generates sequence points where the IL stack is empty. These are places where the debugger can set a breakpoint.
    • llvm-disable-implicit-null-checks โ€” Makes the LLVM backend use explicit NULL checks on variable dereferences instead of depending on operating system support for signals or traps when an invalid memory location is accessed. Unconditionally enabled by explicit-null-checks.
    • no-compact-seq-points โ€” Unless the option is used, the runtime generates sequence points data that maps native offsets to IL offsets. Sequence point data is used to display IL offset in stacktraces. Stacktraces with IL offsets can be symbolicated using mono-symbolicate tool.
    • handle-sigint โ€” Captures the interrupt signal (Control-C) and displays a stack trace when pressed. Useful to find out where the program is executing at a given point. This only displays the stack trace of a single thread. ๐Ÿ˜ญ
  • init-stacks โ€” tells the runtime to prefill the stack with some known values (0x2a on x86-64) at the very start of a method, which helps you debug the JIT engine. โœจ
  • keep-delegates โ€” this one leaks delegate trampolines that aren't referenced anymore, on purpose, so you get way more info about delegate misuse. Basically the vibe is: a delegate instance can get created, passed off to unmanaged code, and then nobody keeps a reference in managed code, so the GC just eats the code ๐Ÿ’€. With this option you can actually track down where the problem is coming from.
  • no-gdb-backtrace โ€” disables the GDB backtrace that the runtime spits out after a SIGSEGV or SIGABRT in unmanaged code.
  • partial-sharing โ€” when this is set, the runtime can share generated code between generic types, which effectively reduces the amount of code generated. lowkey a slay for code size ๐ŸŽ€
  • reverse-pinvoke-exceptions โ€” this makes mono abort with a descriptive message when, during stack unwinding after an exception, it reaches a native stack frame. When does that happen? When a managed delegate is passed to native code and that managed delegate throws. Normally mono tries to unwind the stack to the first (managed) exception handler, skipping any native stack frames along the way. And that leads to undefined behaviour (since mono doesn't know how to process native frames), leaks, and possibly crashes too. so yeah. careful bestie ๐Ÿซถ
  • single-imm-size โ€” guarantees that each time managed code is compiled, the same instructions and registers get used, regardless of the size of the values being used.
  • soft-breakpoints โ€” lets you use single-steps and breakpoints on hardware where we can't do it with signals.
  • suspend-on-native-crash โ€” suspends the program when a native crash happens (SIGSEGV, SIGILL, ...). super useful for debugging crashes that don't show up under gdb, since a live process holds way more information than a core file. ๐Ÿ‘‘
  • suspend-on-sigsegv โ€” same as suspend-on-native-crash.
  • suspend-on-exception โ€” suspends the program when an exception occurs.
  • suspend-on-unhandled โ€” suspends the program when an unhandled exception occurs.
  • thread-dump-dir=DIR โ€” use DIR as the storage spot for thread dumps created by SIGQUIT.
  • weak-memory-model โ€” don't enforce the CLR memory model on platforms with weak memory models. Heads up though: this can introduce random crashes in some rare cases, for multithreaded environments. It can be used for a performance boost on applications that are single threaded.
  • verbose-gdb โ€” makes gdb output on native crashes more verbose.

MONO_LOG_LEVEL</p> ๐ŸŒธ

The logging level. Possible values are error, critical, warning, message, info and debug. See the DEBUGGING section for more details.

MONO_LOG_MASK</p> ๐Ÿฆ‹

Controls the domain of the Mono runtime that logging will apply to. If set, the log mask is changed to the set value. Possible values are "asm" (assembly loader), "type", "dll" (native library loader), "gc" (garbage collector), "cfg" (config file loader), "aot" (precompiler), "security" (e.g. Moonlight CoreCLR support) and "all". The default value is "all". Changing the mask value lets you display only messages for a certain component. You can use multiple masks by comma separating them โ€” for example, to see config file messages and assembly loader messages, set your mask to "asm,cfg". ๐Ÿ’…

MONO_LOG_DEST</p> โœจ

Controls where trace log messages are written. If it's not set, the messages go to stdout. If it is set, the string either specifies a path to a file that will have messages appended to it, or the string "syslog", in which case the messages get written to the system log. Under Windows, this is simulated by writing to a file called "mono.log".

MONO_LOG_HEADER controls whether trace log messages not directed to syslog have the id, timestamp, and pid as the prefix to the log message. To enable a header, this environment variable just needs to be non-null.

MONO_TRACE</p> ๐Ÿ”ฅ

Used for runtime tracing of method calls. The format of the comma separated trace options is:

[-]M:method name
[-]N:namespace
[-]T:class name
[-]all
[-]program
disabled       Trace output off upon start.

You can toggle trace output on/off by sending a SIGUSR2 signal to the program.

MONO_TRACE_LISTENER</p> ๐Ÿ’–

If set, enables the System.Diagnostics.DefaultTraceListener, which will print the output of the System.Diagnostics Trace and Debug classes. It can be set to a filename, and to Console.Out or Console.Error to display output to standard output or standard error, respectively. If it's set to Console.Out or Console.Error you can append an optional prefix that will be used when writing messages like this: Console.Error:MyProgramName. See the System.Diagnostics.DefaultTraceListener documentation for more information.

MONO_WCF_TRACE</p> ๐ŸŽ€

This eases WCF diagnostics functionality by simply outputting all log messages from the WCF engine to "stdout", "stderr" or any file passed to this environment variable. The log format is the same as usual diagnostic output.

MONO_XEXCEPTIONS</p> ๐Ÿ’€

This throws an exception when an X11 error is encountered; by default a message is displayed but execution continues.

MONO_XMLSERIALIZER_DEBUG</p> ๐Ÿ“

Set this value to 1 to prevent the serializer from removing the temporary files that are created for fast serialization; this might be useful when debugging.

MONO_XSYNC</p> ๐ŸŒˆ

This is used in the System.Windows.Forms implementation when running with the X11 backend. It's used to debug problems in Windows.Forms, because it forces all of the commands sent to the X11 server to be done synchronously. The default mode of operation is asynchronous, which makes it hard to isolate the root of certain problems.

MONO_XDEBUG</p> ๐Ÿงš

When the MONO_XDEBUG env var is set, debugging info for JITted code is emitted into a shared library, loadable into gdb. This enables, for example, seeing managed frame names on gdb backtraces.

MONO_VERBOSE_METHOD</p> โญ

Enables the maximum JIT verbosity for the specified method. This is very helpful for diagnosing miscompilation problems of a specific method. This can be a semicolon-separated list of method names to match. If the name is simple, this applies to any method with that name; otherwise you can use a mono method description (see the section METHOD DESCRIPTIONS).

MONO_JIT_DUMP_METHOD</p> ๐Ÿ‘‘

Enables sending of the JITs intermediate representation for a specified method to the IdealGraphVisualizer tool.

MONO_VERBOSE_HWCAP</p> ๐Ÿ”ฅ

If set, makes the JIT output information about detected CPU features (such as SSE, CMOV, FCMOV, etc) to stdout.

MONO_CONSERVATIVE_HWCAP</p> ๐Ÿฆ‹

If set, the JIT will not perform any hardware capability detection. This may be useful to pinpoint the cause of JIT issues. This is the default when Mono is built as an AOT cross compiler, so that the generated code will run on most hardware.

MONO_PROFILE</p> ๐Ÿ’œ

Equivalent to --profile argument.


VALGRIND ๐Ÿงš

If you want to use Valgrind, you will find the file mono.supp useful โ€” it contains the suppressions for the GC which trigger incorrect warnings. Use it like this:

valgrind --suppressions=mono.supp mono ...

DTRACE โœจ

On some platforms, Mono can expose a set of DTrace probes (also known as user-land statically defined, USDT Probes).

They are defined in the file mono.d.

  • ves-init-begin, ves-init-end โ€” begin and end of runtime initialization.
  • method-compile-begin, method-compile-end โ€” begin and end of method compilation. The probe arguments are class name, method name and signature, and in case of method-compile-end success or failure of compilation.
  • gc-begin, gc-end โ€” begin and end of Garbage Collection.

To verify the availability of the probes, run:

dtrace -P mono'$target' -l -c mono

PERMISSIONS ๐Ÿ‘‘

Mono's Ping implementation for detecting network reachability can create the ICMP packets itself, without requiring the system ping command to do the work. If you want to enable this on Linux for non-root users, you need to give the Mono binary special permissions.

As root, run this command:

# setcap cap_net_raw=+ep /usr/bin/mono

๐Ÿ“ FILES ๐Ÿ“

okay bestie, here's the map of where Mono keeps all its stuff on your machine ๐Ÿ’–

On Unix systems, assemblies get loaded from the installation's lib directory. So if you set prefix to /usr, bestie, your assemblies are gonna be chillin' in /usr/lib. On Windows, assemblies load from whatever directory mono and mint are living in ๐Ÿ 

  • ~/.mono/aot-cache โ€” this is the directory where the ahead-of-time compiler demand creation assemblies hang out โœจ
  • /etc/mono/config, ~/.mono/config โ€” the Mono runtime configuration file. Wanna know more? Go read the mono-config(5) manual page, it's giving lore ๐Ÿ“–
  • ~/.config/.mono/certs, /usr/share/.mono/certs โ€” this holds the Mono certificate stores for users and for the machine ๐ŸŽ€ If you wanna manage your cert stores, hit up the certmgr(1) manual page. And if you're wondering how to import the Mozilla root certificates into the Mono certificate store, that's a mozroots(1) situation ๐Ÿฆ‹
  • ~/.mono/assemblies/ASSEMBLY/ASSEMBLY.config โ€” files in this directory let a user customize the configuration for a given system assembly. The format is exactly the one described over in the mono-config(5) page ๐Ÿ’…
  • ~/.config/.mono/keypairs, /usr/share/.mono/keypairs โ€” this contains Mono cryptographic keypairs for users and for the machine ๐Ÿ” You can access them by using a CspParameters object with the DSACryptoServiceProvider and RSACryptoServiceProvider classes. no cap.
  • ~/.config/.isolatedstorage, ~/.local/share/.isolatedstorage, /usr/share/.isolatedstorage โ€” this holds Mono isolated storage for non-roaming users, roaming users, and the local machine ๐ŸŒธ You can access isolated storage using the classes from the System.IO.IsolatedStorage namespace ๐Ÿซถ
  • <assembly>.config โ€” configuration information for individual assemblies gets loaded by the runtime from side-by-side files with the .config files ๐Ÿ“ For more info, see http://www.mono-project.com/Config
  • Web.config, web.config โ€” ASP.NET applications get configured through these files, and the config is done on a per-directory basis ๐Ÿ“ For more on this whole subject, see the http://www.mono-project.com/Config_system.web page

๐Ÿ’Œ MAILING LISTS ๐Ÿ’Œ

All the mailing lists are listed over at http://www.mono-project.com/community/help/mailing-lists/ โ€” go join the group chat bestie, iykyk ๐Ÿ˜ญ๐Ÿ’œ


๐ŸŒ WEB SITE ๐ŸŒ

http://www.mono-project.com โ€” that's the site, iconic, periodt ๐Ÿ‘‘


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

certmgr(1), cert-sync(1), csharp(1), gacutil(1), mcs(1), monodis(1), mono-config(5), mono-profilers(1), mprof-report(1), pdb2mdb(1), xsp(1), mod_mono(8)

For more information on AOT, check out http://www.mono-project.com/docs/advanced/aot/ ๐Ÿ”ฅ

And for all the ASP.NET-related documentation, see the xsp(1) manual page ๐ŸŒˆ


and that's a wrap on the whole page, bestie!! ๐ŸŽ€โœจ this entire manual page signs off as Mono(mono) โ€” that's its lil footer, source and version and date and all โ€” so consider yourself fully briefed, hydrated, and slaying ๐Ÿ’…๐Ÿ’–๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from