mono-profilers(1)

πŸ’… mono-profilers β€” Mono's bundled profiler modules

okay bestie, this is mono-profilers(1) from the General Commands Manual, and it's literally the glow-up guide to Mono's bundled profiler modules. grab a strawberry drink πŸ“, we're going in.


✨ NAME ✨

mono-profilers β€” Mono's bundled profiler modules


✨ SYNOPSIS ✨

mono --profile=log[:option,...] program.exe [args]...

mono --profile=coverage[:option,...] program.exe [args]...

mono --profile=aot[:option,...] program.exe [args]...

✨ DESCRIPTION ✨

Mono ships with a few profiler modules that cover most of your typical profiling scenarios. This page describes each of them, one section at a time, no skips. πŸ’–


πŸ”₯ LOG PROFILER πŸ”₯

The log profiler is Mono's general-purpose performance profiler β€” the main character of profiling, fr fr. It can collect a whole buffet of data that tools like mprof-report(1) or the Xamarin Profiler can then analyze. πŸ‘‘

By default, the log profiler writes its output to output.mlpd. Wanna know the file format? Go read the mono/profiler/log.h file in the Mono source tree, that's where the documentation lives. πŸ“„

A default invocation of the log profiler gathers only the basic stuff: metadata load and unload events, thread start and stop events, performance counter samples, exception throws, etc. But lowkey most of you are gonna want to turn on the heavier features β€” GC allocation recording, statistical sampling, heap snapshotting (heapshots), or method entry and exit instrumentation. Scroll to the Options sub-section, it's all there. πŸ‘‡

Heads up bestie: in most realistic scenarios, the log profiler will record a vast amount of data. That means very large log files. 😭 (The zip and report options can help you deal with that, so don't panic.)

πŸŽ€ Options πŸŽ€

The log profiler supports these options, and yes, we're listing every single one:

  • help β€” Print usage instructions. πŸ’
  • output=[+|#||]file β€” Write log data to file. The optional modifiers are:
    • + β€” The program PID gets appended to the file name. So like, output=+out.mlpd outputs to out.mlpd.1234 if the PID is 1234. ✨
    • # β€” file is parsed as a file descriptor number, which is opened with fdopen(3). This is mainly useful in embedding scenarios.
    • | β€” file is treated as a program name. It will be started with popen(3) and the log data will be piped to its standard input. 🚰
  • report β€” Generate a report directly instead of writing the log data to a file. And if you use this together with the output option, the report gets written to the specified file instead of the log data. two-for-one, we love to see it.
  • zip β€” Compress the output file with gzip(1). πŸ—œοΈ
  • port=port β€” Use port to listen for command server connections. See the Command server sub-section.
  • nodefaults β€” Disables pre Mono 5.6 compatibility. Specifically, this disables exception events and performance counter sampling by default. It also makes it so that GC move events won't be enabled by default when heapshots are enabled. ⚠️ To use this option, it must be the first option given to the log profiler.
This option will be the default in a future version of the log profiler. so, foreshadowing.
  • [no]event β€” Enable or disable gathering data for event, which can be one of:
    • exception β€” Exception throw and clause (catch, finally, etc) evaluation events. Enabled by default unless nodefaults is used.
    • monitor β€” Monitor lock contention, acquisition, and release events.
    • gc β€” GC start, progress, stop, and resize events.
    • gcalloc β€” GC allocation events.
    • gcmove β€” GC move events.
    • gcroot β€” GC root report events. Generated on every collection if enabled, unless nodefaults is used, in which case, they're only generated on heapshots.
    • gchandle β€” GC handle creation and deletion events.
    • finalization β€” Object finalization events.
    • counter β€” Performance counter sample events. Enabled by default unless nodefaults is used.
    • jit β€” JIT code buffer events.
    • alloc β€” Alias for gc, gcalloc, and gcmove.
    • legacy β€” Alias for exception, monitor, gc, gcmove, gcroot, gchandle, finalization, and counter.
  • sample[-real][=freq] β€” Enable statistical sampling. The default is to sample at a frequency of 100 Hz, but freq can be used to override that. πŸ“Š
    • By default, sampling uses process time (i.e., the more work a process does, the more samples are collected). The -real variant uses wall clock time instead. Wall clock time is better for programs that are I/O-bound. ⏰
  • maxsamples=num β€” Limit the number of reusable sample events to num allocations. A value of zero means no limit. By default, the value of this setting is based on the number of CPU cores. Some tinkering with this setting may be necessary for programs with an unusually high amount of threads. 🧡
  • calls β€” Enable method entry and exit instrumentation. This is an alternative to statistical sampling when you need more precise information. But bestie, be warned: this mode is extremely heavy and can slow most programs to a crawl. πŸ’€
  • callspec=spec β€” Limit method entry and exit instrumentation to methods matching the specified call spec. This uses the same syntax as the --trace option for mono(1), so go read that page for more info. πŸ“–
  • calldepth=num β€” Limit method entry and exit event collection to a call depth of num.
  • maxframes=num β€” Limit backtraces in various events (including statistical samples) to num frames.
  • heapshot[=mode] β€” Enable heap snapshots. mode, if given, can be one of:
    • ondemand β€” Only perform a heapshot when receiving a command via the command server or when triggered through the managed library. πŸ™‹
    • numgc β€” Perform a heapshot on every num collections of the major generation.
    • numms β€” Perform a heapshot on a major generation collection if num milliseconds have passed since the last heapshot. ⏱️
    • If mode is not given, a heapshot will be performed on every collection of the major generation.
  • heapshot-on-shutdown β€” In addition to any other heapshot settings, also perform a heapshot on runtime shutdown. bye bye, one last snapshot 🫢
  • debug β€” Print detailed debugging information. Most users should not use this option. πŸ™ˆ

πŸ¦‹ Command server πŸ¦‹

The log profiler features a simple command server that currently is only used to trigger manual heapshots (typcally when using the on-demand mode, but also usable with the other modes). A random port will be used to listen for connections unless the port option is used. To trigger a heapshot, open a TCP connection to the command server and send the C string "heapshot\n". 🎯

The command server supports multiple simultaneous connections. no cap, it's popular.

🧚 Managed library 🧚

The log profiler comes with a managed library called Mono.Profiler.Log. This library lets you easily read log files in managed code (e.g., C#) as well as interact with the profiler at run-time. πŸ’œ

With the ability to easily read log files, users can write all sorts of interesting analyses that might not be provided by the standard tools (e.g., mprof-report(1)). go off, build your own tooling.

The LogProfiler class allows users to reconfigure profiler settings at run-time. For example, certain event types can be toggled on or off, the mode and frequency of heapshots and sampling can be changed, etc. Heapshots can also be triggered manually. πŸŽ›οΈ

To use this library, simply pass -r:Mono.Profiler.Log when compiling your code.

🌈 Example 🌈

Collect GC allocation and sampling data for a program, then generate a report:

mono --profile=log:alloc,sample program.exe
mprof-report output.mlpd

Perform a heapshot on every 5th collection and generate a report directly:

mono --profile=log:heapshot=5gc,report program.exe

🌸 COVERAGE PROFILER 🌸

The code coverage profiler collects information about how often code paths are executed. It does this by instrumenting JIT-compiled code at all sequence points. On program exit, the coverage profiler collects all execution count information and outputs it to an XML file. πŸ“ The main use case for the coverage profiler is unit testing: by running unit test suites with the coverage profiler, it's possible to determine whether the unit tests actually cover all the code that they should. slay, actual accountability. βœ…

By default, the coverage profiler writes its output to coverage.xml. Refer to the mono/profiler/coverage.c file in the Mono source tree for documentation on the schema.

⚠️ Please note that the coverage profiler currently does not support instrumenting AOT-compiled code. When collecting coverage data, one may wish to run Mono with the -O=-aot option to disable loading AOT-compiled code.

πŸŽ€ Options πŸŽ€

The coverage profiler supports the following options:

  • help β€” Print usage instructions. πŸ’
  • output=[+|#||]file β€” Write coverage data to file. The optional modifiers are:
    • + β€” The program PID is appended to the file name. For example, output=+cov.xml outputs to cov.xml.1234 if the PID is 1234. ✨
    • # β€” file is parsed as a file descriptor number, which is opened with fdopen(3). This is mainly useful in embedding scenarios.
    • | β€” file is treated as a program name. It will be started with popen(3) and the coverage data will be piped to its standard input. 🚰
  • covfilter-file=file β€” Supply a coverage filter file. This option can be given multiple times. See the Filter files sub-section. πŸ—‚οΈ

πŸ’– Filter files πŸ’–

Filter files can be used to pick and choose which types should be considered for coverage instrumentation. A filter file consists of a series of lines of the form:

+|-<image_name>type_name_prefix

Wait wait, the original writes it with brackets around image_name, so let's keep it exact:

+|- [image_name]type_name_prefix

Here, image_name is something like mscorlib. type_name_prefix can be something like System.Int32 for a specific type, or System.App to pick all types starting with App in the System namespace. 🎯

Lines starting with + indicate that a type should be instrumented for coverage, whereas lines starting with - indicate the opposite. Lines starting with + always override lines starting with - regardless of the order they appear in. positivity wins, iconic.

Lines not starting with either character are ignored. This can be used to write comments. For example, this is a valid file:

# Ignore coverage in network-related code, except HTTP client code.
-[MyProgram]MyProgram.Net
+[MyProgram]MyProgram.Net.Http.HttpClient

🌈 Example 🌈

Coverage data for a program can be collected like this:

mono -O=-aot --profile=coverage:output=cov.xml program.exe

cov.xml will now contain the coverage data. πŸŽ‰

πŸ’… AOT PROFILER πŸ’…

okay so this one is the glow-up profiler bestie ✨ The AOT profiler watches your program run and records which generic instantiations it actually uses, then saves all that intel to a file you pick. Then the AOT compiler can take that file and compile those generic instantiations ahead of time, which means your program boots up so much faster. it's giving pre-planned, prepared, periodt πŸ‘‘

By default, the AOT profiler writes its output to output.aotprofile. If you wanna know what's actually inside that file, go read mono/profiler/aot.h in the Mono source tree β€” that's where the file format is documented, fr fr πŸ“„


✨ Options ✨

The AOT profiler supports the following options:

  • duration=num β€” only profile num seconds of runtime, then write the data out. short and sweet, we love a limited-time event πŸŽ€
  • help β€” print usage instructions. the "bestie help me" button 🫢
  • output=[+|#||]file β€” write output data to file. and yes, those optional modifiers are real, let's break them down:
    • + β€” the program PID gets appended to the file name. so like, output=+out.aotprofile outputs to out.aotprofile.1234 if the PID is 1234. cute lil personalization 🏷️
    • # β€” file gets parsed as a file descriptor number, which is opened with fdopen(3). mainly useful in embedding scenarios, iykyk πŸ”§
    • | β€” file is treated as a program name. it gets started with popen(3) and the log data gets piped straight to its standard input. pipe it, queen 🎯
  • verbose β€” print detailed debugging information. most users should not use this option. like, it's a lot. it's giving lore dump 😭

🌸 Example 🌸

A profile can be collected and used like this:

mono --profile=aot:output=program.aotprofile program.exe
mono --aot=profile=program.aotprofile program.exe
mono program.exe

three lines, one glow-up, no cap πŸ’–


πŸ‘‘ SEE ALSO πŸ‘‘


and that's the whole vibe bestie πŸ¦‹ this was the mono-profilers(1) page (that's the source right there in the footer, and the date lives down there too β€” go peek at the bottom of your terminal, it's iconic) βœ¨πŸ’… stay profiling, stay slaying πŸŒˆπŸ’œ