Skip to content
Permalink
Browse files

Add a Rust implementation of `coz.h`

After seeing a recording of Emery's recent talk at Strange Loop I was
super impressed with `coz` and immediately wanted to try it out with
Rust code! I was curious to see if `coz` could help us out with
profiling either Cargo, Rust's build system, or perhaps the compiler
itself, `rustc`. In any case I ended up creating
https://github.com/alexcrichton/coz-rs as a repository to house an
implementation of `coz.h` for Rust so `coz` could be used to easily work
with Rust executables.

I toyed around a bit and Emery [mentioned] that this would be good
support to have upstream once it's working, and I think it's working
now! I haven't been able to get a ton of useful results myself yet, but
I'm still figuring out the best way to ask `coz` questions and the best
way to instrument programs myself.

Nonetheless I think that the Rust support here is correct in the sense
that it's a faithful translation of `coz.h`, and now it's just a problem
with me learning how best to instrument programs.

This commit adds a new `rust` directory with a crate (Rust parlance for
"library") which provides macros that allow using `coz` instrumentation
similar as you would in C, except in idiomatic Rust style. I've
published the crate on Rust's package registry, crates.io, as
[`coz`][crate], and I don't mind keeping it there or transferring it to
y'all, whichever you'd prefer! I also don't mind being on the hook for
reviewing any Rust-related changes, or if y'all need to jettison the
Rust support in the future I don't mind that either and can give it a
home!

[mentioned]: alexcrichton/coz-rs#2
[crate]: https://crates.io/crates/coz
  • Loading branch information...
alexcrichton committed Sep 23, 2019
1 parent 72c4afe commit 2f12e6e4f3dbf194a9455a710d902973d58d7c90
Showing with 450 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +10 −0 .travis.yml
  3. +18 −0 rust/Cargo.toml
  4. +108 −0 rust/README.md
  5. +21 −0 rust/examples/toy.rs
  6. +276 −0 rust/src/lib.rs
  7. +15 −0 rust/tests/smoke.rs
  8. BIN rust/toy.png
@@ -29,3 +29,5 @@ benchmarks/string_match/string_match_datafiles
benchmarks/word_count/word_count_datafiles

viewer/node_modules
rust/Cargo.lock
rust/target
@@ -27,3 +27,13 @@ install:
# Run the make check target (add tests later)
script:
- make check USE_SYSTEM_COZ=1

# Test the Rust support as well
matrix:
include:
- language: rust
rust: stable
before_install: true
install: true
script: cargo test --manifest-path rust/Cargo.toml

@@ -0,0 +1,18 @@
[package]
name = "coz"
version = "0.1.2"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
edition = "2018"
repository = "https://github.com/plasma-umass/coz"
license-file = "../LICENSE.md"
readme = "README.md"
description = """
Rust support for the `coz` Causal Profiler: https://github.com/plasma-umass/coz
"""

[dependencies]
libc = "0.2"
once_cell = "1.1"

[profile.release]
debug = 1
@@ -0,0 +1,108 @@
# coz-rs

Rust support for the [`coz` Causal Profiler](https://github.com/plasma-umass/coz)

[![Documentation](https://docs.rs/coz/badge.svg)](https://docs.rs/coz)

## Usage

First, follow the instructions in [`coz`] to install the `coz` command.

[`coz`]: https://github.com/plasma-umass/coz/#installation

Next, `coz` is a profiler that, for the best results, typically requires
source-level modifications of your code. To do this first add this to your
`Cargo.toml`

```toml
[dependencies]
coz = "0.1"
```

Then you'll want to either at throughput or latency tracepoints. More
information on this [can be found
upstream](https://github.com/plasma-umass/coz/#profiling-modes). If you have
something you'd wish whould execute more often, you can add:

```rust
fn main() {
loop {
// ...
// For example you wish this `loop` executed more iterations
coz::progress!(); // equivalent of `COZ_PROGRESS`
}
}
```

Note that `coz::progress!("name")` is the equivalent of `COZ_PROGRESS_NAMED` as
well.

If you'd like to profile the latency of an operation you can instead use:

```rust
// Boy I wish this function executed more quickly...
fn foo() {
coz::begin!("foo");
// ...
coz::end!("foo");
}
```

After you've instrumented your code, you need to also ensure that you're
compiling with DWARF debug information. To do this you'll want to configure
`Cargo.toml` again:

```toml
[profile.release]
debug = 1
```

Next up you'll build your application with `cargo build --release`, and then
finally you can run it with `coz run --- ./target/release/$your_binary`.

## Caveats

Known caveats so far to generate a report that collects information are:

* Rust programs by default segfault when run with `coz` with an issue related to
[plasma-umass/coz#110](https://github.com/plasma-umass/coz/issues/110). Rust
programs set up a `sigaltstack` to run segfault handlers to print "you ran out
of stack", but this alternate stack is too small to run the `SIGPROF` handler
that `coz` installs. To handle this this crate provides a `coz::thread_init()`
function which will increase the `sigaltstack` size that Rust installs by
default to something large enough to run `coz`. If you see segfaults, or
corrupt reports, you may wish to manually call `coz::thread_init()` instead of
waiting for this crate to automatically call it for you.

* Debug information looks to be critical to get a report from `coz`. Make sure
that your program is compiled with at least line-table information (`debug =
1`) to ensure you get the best experience using `coz`.

* Currently `coz` only works on Linux, and while this crate should compile on
all platforms it only actually does something on Linux.

## Examples

You can find an example toy program at `examples/toy.rs` in this repository, and
we can execute it with `coz`:

```
$ cargo build --release
$ coz run --- ./target/release/examples/toy
...
[profiler.cpp:75] Starting profiler thread
$
```

That should generate `profile.coz` in the current directory, which if you plot
that should look something like this:

![plot of `toy.rs`](toy.png)

Note that I'm still learning myself the best sorts of applications to run on as
well as the best sorts of questions to ask `coz` and where to put
latency/throughput points. If you've got ideas or good examples, please feel
free to add them here!
@@ -0,0 +1,21 @@
const A: usize = 2_000_000_000;
const B: usize = (A as f64 * 1.2) as usize;

fn main() {
coz::thread_init();

let a = std::thread::spawn(move || {
coz::thread_init();
for _ in 0..A {
coz::progress!("a");
}
});
let b = std::thread::spawn(move || {
coz::thread_init();
for _ in 0..B {
coz::progress!("b");
}
});
a.join().unwrap();
b.join().unwrap();
}

0 comments on commit 2f12e6e

Please sign in to comment.
You can’t perform that action at this time.