Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/actions/rust-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ runs:
- uses: Swatinem/rust-cache@v2
- name: Build
shell: bash
run: if [ "${{ inputs.toolchain }}" != stable ]; then rm -fv Cargo.lock; fi && cargo build --all-features --verbose
run: |
if [ "${{ inputs.toolchain }}" != stable ]; then
rm -fv Cargo.lock
fi
cargo build --all-features --verbose
- name: Run tests
shell: bash
run: cargo test --all-features --verbose
24 changes: 0 additions & 24 deletions .github/workflows/build-decoder.yml

This file was deleted.

90 changes: 88 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on: [pull_request]

jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -17,5 +18,90 @@ jobs:
uses: ./.github/actions/rust-build
with:
toolchain: ${{ matrix.toolchain }}


build-for-testing:
name: Build For Testing
runs-on: ubuntu-latest
env:
RUSTFLAGS: --cfg tokio_unstable
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: Build
shell: bash
run: cargo build --all-features --verbose --example simple
- name: Upload artifact for testing
uses: actions/upload-artifact@v4
with:
name: example-simple
path: ./target/debug/examples/simple
build-decoder:
name: Build Decoder
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: 1
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
with:
cache-directories: decoder
- name: Build
working-directory: decoder
shell: bash
run: cargo build --all-features --verbose
- name: Run tests
working-directory: decoder
shell: bash
run: cargo test --all-features --verbose
- name: Upload artifact for testing
uses: actions/upload-artifact@v4
with:
name: pollcatch-decoder
path: ./decoder/target/debug/pollcatch-decoder
build-async-profiler:
name: Build async-profiler
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install async-profiler Dependencies
run: sudo apt-get install -y sudo libicu-dev patchelf curl make g++ openjdk-11-jdk-headless gcovr
- name: Build async-profiler
working-directory: tests
shell: bash
run: ./build-async-profiler.sh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: libasyncProfiler
path: ./tests/async-profiler/build/lib/libasyncProfiler.so
test:
name: Integration Test
runs-on: ubuntu-latest
needs: [build, build-for-testing, build-decoder, build-async-profiler]
steps:
- uses: actions/checkout@v4
- name: Download pollcatch-decoder
uses: actions/download-artifact@v4
with:
name: pollcatch-decoder
path: ./tests
- name: Download example-simple
uses: actions/download-artifact@v4
with:
name: example-simple
path: ./tests
- name: Download libasyncProfiler
uses: actions/download-artifact@v4
with:
name: libasyncProfiler
path: ./tests
- name: Run integration test
shell: bash
working-directory: tests
run: chmod +x simple pollcatch-decoder && LD_LIBRARY_PATH=$PWD ./integration.sh
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/target
/decoder/target
/tests/async-profiler
/tests/profiles
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio = { version = "1", features = ["test-util", "full"] }
test-case = "3"
rand = "0.9"
humantime = "2"

[[example]]
name = 'simple'
Expand Down
120 changes: 110 additions & 10 deletions decoder/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::{ffi::OsString, fs::File, io::Cursor};
use std::{
ffi::OsString,
fs::File,
io::{self, Cursor, Write},
};

use clap::{Parser, Subcommand};
use jfrs::reader::{
Expand Down Expand Up @@ -78,7 +82,7 @@ fn main() -> anyhow::Result<()> {
}
}
};
print_samples(samples, stack_depth);
print_samples(&mut io::stdout(), samples, stack_depth).ok();
Ok(())
}
}
Expand All @@ -94,7 +98,7 @@ fn symbol_to_string(s: Accessor<'_>) -> Option<&str> {
None
}

fn print_samples(samples: Vec<Sample>, stack_depth: usize) {
fn print_samples<F: Write>(to: &mut F, samples: Vec<Sample>, stack_depth: usize) -> io::Result<()> {
for sample in samples {
if sample.frames.iter().any(|f| {
f.name.as_ref().is_some_and(|n| {
Expand All @@ -106,39 +110,45 @@ fn print_samples(samples: Vec<Sample>, stack_depth: usize) {
// skip samples that are of sleeps
continue;
}
println!(
writeln!(
to,
"[{:.6}] thread {} - poll of {}us",
sample.start_time.as_secs_f64(),
sample.thread_id,
sample.delta_t.as_micros()
);
)?;
for (i, frame) in sample.frames.iter().enumerate() {
if i == stack_depth {
println!(
writeln!(
to,
" - {:3} more frame(s) (pass --stack-depth={} to show)",
sample.frames.len() - stack_depth,
sample.frames.len()
);
)?;
break;
}
println!(
writeln!(
to,
" - {:3}: {}.{}",
i + 1,
frame.class_name.as_deref().unwrap_or("<unknown>"),
frame.name.as_deref().unwrap_or("<unknown>")
);
)?;
}
println!();
writeln!(to)?;
}
Ok(())
}

#[derive(Debug)]
struct Sample {
delta_t: Duration,
start_time: Duration,
thread_id: i64,
frames: Vec<StackFrame>,
}

#[derive(Debug)]
struct StackFrame {
class_name: Option<String>,
name: Option<String>,
Expand Down Expand Up @@ -536,3 +546,93 @@ where
}
Ok(samples)
}

#[cfg(test)]
mod test {
use super::{jfr_samples, print_samples, Sample, StackFrame};
use std::io;
use std::time::Duration;

#[test]
fn test_print_samples() {
let mut to = vec![];
print_samples(
&mut to,
vec![Sample {
delta_t: Duration::from_millis(1),
start_time: Duration::from_secs(1),
thread_id: 1,
frames: vec![
StackFrame {
class_name: None,
name: None,
},
StackFrame {
class_name: None,
name: Some("foo".into()),
},
StackFrame {
class_name: Some("cls".into()),
name: Some("foo".into()),
},
StackFrame {
class_name: Some("cls".into()),
name: Some("bar".into()),
},
],
}],
3,
)
.unwrap();
assert_eq!(
String::from_utf8(to).unwrap(),
r#"[1.000000] thread 1 - poll of 1000us
- 1: <unknown>.<unknown>
- 2: <unknown>.foo
- 3: cls.foo
- 1 more frame(s) (pass --stack-depth=4 to show)

"#
);
}

#[test]
fn test_jfr_samples() {
let jfr = include_bytes!("../../tests/test.jfr");
let samples = jfr_samples(&mut io::Cursor::new(jfr), Duration::from_micros(200)).unwrap();
let mut to = vec![];
print_samples(&mut to, samples, 4).unwrap();
assert_eq!(
String::from_utf8(to).unwrap(),
r#"[95.789203] thread 1880 - poll of 219us
- 1: libc.so.6.clock_nanosleep
- 2: libc.so.6.nanosleep
- 3: simple.std::thread::sleep
- 4: simple.simple::slow::short_sleep
- 55 more frame(s) (pass --stack-depth=59 to show)

[92.789145] thread 1881 - poll of 1733us
- 1: libc.so.6.clock_nanosleep
- 2: libc.so.6.nanosleep
- 3: simple.std::thread::sleep_ms
- 4: simple.simple::slow::accidentally_slow
- 55 more frame(s) (pass --stack-depth=59 to show)

[96.789218] thread 1881 - poll of 203us
- 1: libc.so.6.clock_nanosleep
- 2: libc.so.6.nanosleep
- 3: simple.std::thread::sleep
- 4: simple.simple::slow::short_sleep
- 55 more frame(s) (pass --stack-depth=59 to show)

[98.789191] thread 1881 - poll of 214us
- 1: libc.so.6.clock_nanosleep
- 2: libc.so.6.nanosleep
- 3: simple.std::thread::sleep
- 4: simple.simple::slow::short_sleep
- 55 more frame(s) (pass --stack-depth=59 to show)

"#
);
}
}
Loading