Profiling Rust code on Windows using CodeXL
Tags: rust, profiling, tutorial, windows
Published: 2017-01-31 11:08:26

---
title: Profiling Rust code on Windows using CodeXL
published: true
description: A tutorial for those Rustaceans on Windows that feel left behind
cover_image: http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/cover.png?ver=3
tags: rust, profiling, tutorial, windows
---

I like the [Rust language](https://www.rust-lang.org). However, developing with Rust on Windows currently has a lot of rough edges. One thing I was struggling with was a working solution for profiling Rust code on Windows. 
In the end, the best tool for the job I found was CodeXL (formerly known as AMD CodeXL). Here is a step-by-step tutorial on how it is done.
The tutorial was tested on Windows 10 with Rust 1.14.0.

#Note on Debugging and Targets
To be able to debug the code I use the `x86_64-pc-windows-gnu` target (as opposed to  `x86_64-pc-windows-msvc` which is the default for Windows). 
However, the profiling experience is better with the MSVC target. So maybe the best approach is to switch between the targets based on your current needs using rustup:

    > rustup default stable-x86_64-pc-windows-msvc
    > rustup default stable-x86_64-pc-windows-gnu

An aside on debugging on Windows. If you want to setup Rust debug environment on Windows, I recommend [Sherry Ummen's tutorial](https://sherryummen.in/2016/09/02/debugging-rust-on-windows-using-visual-studio-code/) for
setting up Rust + Visual Studio Code (requires the GNU target).
I've heard good rumours about Rust support in Sublime and Atom, but have yet to test it myself.
~~AFAIK the debugging support in the [Visual Studio 2015 extension](https://marketplace.visualstudio.com/items?itemName=vosen.VisualRust) is fairly limited and also requires the GNU target.~~

**UPDATE:** In theory, you should be able to use standard debugger and profiler of Visual Studio (with the C++ toolset) to analyze code compiled with the MSVC target. I tried attaching Visual Studio debugger to a running Rust process (compiled with debug information) and it worked nicely! I was however unable to make the profiler work with the exact same program. I'll write another tutorial if I figure it out.


#Profiling

First thing we need to do is to compile the program optimized (otherwise profiling makes no sense), but with debugging information:

 `
> rustc -g -O -o rna.exe ..\src\main.rs
 `

If you compile through Cargo, you should be able to set the necessary options by modifying the  `[profile.release] ` section of  `Cargo.toml ` file. See the [docs](http://doc.crates.io/manifest.html) for more details.

Now open [CodeXL](https://github.com/GPUOpen-Tools/CodeXL). I have tested with version 1.9 and 2.2, but I believe other versions would work as well.

First you switch to profile mode ( `Profile -> Switch to Profile Mode `):

![Switch to profile mode](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/switch_to_profile.png)

My app was running pretty long, so I chose the easy path, started the app from command line and used  `Profile -> Attach to process `. 

![Attach to process](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/attach_to_process.png)

It should be possible to have CodeXL start the app for you, but I didn't bother.

Then you end the profiling by clicking the stop button.

![Stop profiling](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/stop_profiling.png)

And you get the output:

![Profile output](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/results-inlined.png)

As you can note, there are only two functions in the profiling output. That's not because my app has no functions, but because Rust inlined all the rest.

This is the moment, when the GNU target bites you. If you compiled to GNU target, double-clicking on a function shows the actual time spent in individual processors instructions, but I found no simple way to match those instructions with the Rust code (and the inlined functions).

![Function detail - GNU target](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/results-function-detail.png)


On the other hand, if you use the MSVC target, you see the actual source code! 

![Function detail - MSVC target](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/results-function-detail-msvc.png)

However, even with MSVC, all samples attributed to an inlined function are associated with the line where the function is called, so you still see very little detail. 
To take the example above: is the  `next_cut() ` function really consuming so many samples or is the  `match ` statement responsible?

So I did a little trick, that gives me more information, but may add noise to the timings. I forced the compiler to not inline anything:

 `
> rustc -g -O -C inline-threshold=0 -o rna.exe ..\src\main.rs
 `

Which gives a more helpful result:

![Results - without inlining](http://popelka.ms.mff.cuni.cz/~cerny/rust-profile-img/results-no-inline.png)


I can now be quite confident that most of the time is spent in the actual body of  `main ` and  `find_next_cut ` and not in the inlined functions.

I also tried the [Very Sleepy profiler](https://github.com/VerySleepy/verysleepy), which seemed to sort of work, 
but I could not get it to display any debug information (especially the function names).

Hope this helps you!