Notes on Code Coverage for .NET Applications

This post summarizes my learnings about code coverage visualization in SonarQube and GitLab.

Formats for GitLab and SonarQube

I'll detail different options for displaying code coverage for a C# (.NET) application in both tools. GitLab uses Cobertura coverage files. When such a file is uploaded as a job artifact, GitLab marks each line as covered or uncovered in a pull request (Merge Request in GitLab terms). GitLab also provides other coverage features that are not covered in this post.

SonarQube accepts several coverage formats. As of writing this post, for .NET applications it works with coverage files in these formats:

Find out more


Getting Started with ClrMD

What is ClrMD?

ClrMD or Microsoft.Diagnostics.Runtime NuGet package is a helpful tool to analyze crash dumps or debug applications at runtime.

This post discusses scenarios where this tool excels compared to other diagnostic tools. Its capabilities are comparable to WinDBG with the SOS extension or PerfView. As a NuGet package, it also provides common CLR abstractions enabling developers to build their own diagnostic tools.

In most investigations, the usual tools such as WinDBG + SOS, PerfView, Visual Studio's performance profiler, etc. provide clear insights into the application. However, in rare cases, a developer might want to write custom scripts or code for deeper analysis not available through standard commands or views. In these cases, ClrMD comes to the rescue.

Find out more


Constant Folding and Inlining

In .NET the JIT performs more-and-more optimization on the code using dynamic PGO, constant folding and inlining. While writing this post, I am using .NET 9 Runtime on x64 architecture, although many of the discussed optimizations are available in one or another form since .NET 7. The presented assembly code snippets generated by the runtime are all optimized, Tier1-OSR code using Dynamic PGO.

Constant-Folding is applied when a compiler 'sees' a constant expression and replaces it with the result of the expression. The C# compiler does it at compile time (ie. var i = 3 + 4; can be substituted with var i = 7). Some expressions involving readonly fields, environment specific parameters, architecture, CPU, etc. can only be folded at runtime. In this latter case the JIT compiler may perform the optimization.

Inlining is a technique where a method's invocation is replaced with the actual body of the method. This reduces the overhead of the method invocation, but it increases the overall code size (and memory consumption) at runtime. This optimization also allows further optimizations across the overall method at the callsite.

These two optimizations can work together to achieve an overall optimization that is only possible at runtime. In this blog post I will present one such case.

Find out more


Palindrome Number

I few years ago I explored the palindrome problem. At the time I focused on inputs of strings. That previous post has described a few solutions from a simple implementation to vectorized solutions.

In this post I will focus on the same problem but with numbers as the input. Is a number palindrome? sounds like a simple question but requires some clarifications to before answering. In this post I use the following constraints:

  • Only positive integers and 0 zero are considered valid inputs (uints).

  • A number is palindrome if the digits of it in base 10 represent a palindrome. All other non-digit characters (., ,, -, etc.) are omitted.

Improving the Vectorized Palindrome

Find out more


Ref-Readonly and In

The current version of C# (C# 13 at the time of writing) has in and ref keywords. In this post I investigate using these keywords as parameter modifiers. Specifically, the difference between ref readonly and the in parameter modifier.

Both parameter modifiers are well-suited for methods that have large struct parameters. Regular struct parameters follow copy-by-value semantics. Larger struct copies incur a performance penalty as the larger the struct is the more data has to be copied. I investigated the cost of copies in a post about inline arrays. One approach to address this problem is by passing a reference to the struct parameters.

In modifier

An author of a method could use the in parameter modifier for value type parameters. In the following example as RegularStruct is passed to a method named PassByIn, with the in modifier:

Find out more