Skip to content

BLARRCHER/profiling

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

broken-app - fixing, UB verification, and optimization

Origin. Assignment from the Yandex Practicum Rust course (debugging & profiling module): a deliberately broken app with 8 seeded defects. Developed locally, published as a snapshot. All measurements in REPORT.md are reproducible via scripts/ (Docker: Valgrind, TSan, perf).

broken-app was seeded with 8 defects (memory/UB/logic/asymptotics/ data race) and deliberately slow functions. The task is to fix them, prove the absence of UB/leaks/races with tooling, and speed up the hot paths, checking behavior against the reference-app baseline (which was left unchanged).

All the details and measurements collected into files:

Detailed analysis with measurements - REPORT.md. Map of artifacts - broken-app/artifacts/README.md.


Final results

  • All 8 defects fixed, all unsafe removed; cargo build and clippy - no warnings.

  • Correctness confirmed by 5 tools (real logs in broken-app/artifacts/):

    Check Before After
    cargo test crash on OOB + assert failures 14 passed
    Miri OOB, leak, use-after-free, race clean
    Valgrind --leak-check=full definitely lost: 5,008 bytes definitely lost: 0
    AddressSanitizer heap-use-after-free clean
    ThreadSanitizer data race @ concurrency.rs:15 clean
  • Regression tests added - one per bug (broken-app/tests/regression.rs).

  • Two optimizations (algorithmic + micro), measured with criterion on identical inputs:

    Bench Before After Speedup
    slow_fib/n32 13.38 ms 54 ns ≈ 249,000×
    slow_dedup/n10000 87.8 ms 411 µs ≈ 214×
    slow_dedup/n5000 21.67 ms 280 µs ≈ 77×

    Callgrind profile of the same scenario: 2.27B -> 14.4M instructions (≈ 157×).

Summary of fixes and optimizations

Function Before After
sum_even 0..=len + get_unchecked (OOB, UB) safe iterator
leak_buffer Box::into_raw without from_raw (leak) iter().filter().count()
use_after_free read after drop (UAF) no raw pointers
normalize stripped only spaces (not tabs) split_whitespace()
average_positive divided by all elements mean over > 0 only
slow_dedup O(n²·log n), a sort on every insert O(n) via HashSet + a single sort (algorithm + with_capacity = micro)
slow_fib O(φⁿ) recursion iterative O(n) (algorithm)
race_increment static mut from threads (race, UB) AtomicU64

Structure

src/lib.rs           # sum_even, leak_buffer, normalize, average_positive, use_after_free
src/algo.rs          # slow_dedup, slow_fib (optimized)
src/concurrency.rs   # race_increment (AtomicU64)
src/bin/demo.rs      # example run; `demo bench [N]` - heavy scenario for profiling
tests/integration.rs # tests checked against reference-app
tests/regression.rs  # regressions for each of the 8 bugs (bug1..bug8)
benches/baseline.rs  # criterion benches of the hot paths (harness=false)
scripts/             # mem_check / asan_check / tsan_check / profile / compare (Docker)
artifacts/           # "before/after" logs, criterion report, PNG profiles
REPORT.md            # detailed report

Running

1. Natively on Windows (Rust 1.91 stable + nightly)

cargo test                       # all tests (14)
cargo run --release --bin demo   # example output
cargo run --release --bin demo bench 30   # heavy run of the hot paths

# UB detection (catches OOB, use-after-free, leaks AND data races):
rustup component add --toolchain nightly miri   # once
cargo +nightly miri test

# Benchmarks (criterion prints change% relative to the previous run itself):
cargo bench --bench baseline

2. Linux tools via Docker

valgrind, perf, and ThreadSanitizer don't work on Windows, so a container is used. All scripts are parameterized: <crate_dir> <target_dir> <out_file> [filter].

# Start the container (volume - this directory), install the tools (once):
docker run -d --name rustlab -v "$(pwd)":/work rustlang/rust:nightly sleep infinity
docker exec rustlab bash -c "apt-get update && apt-get install -y valgrind && rustup component add rust-src"

# Memory/race checks and profiling (write straight into artifacts/):
docker exec rustlab bash /work/scripts/mem_check.sh  /work /tmp/tl   /work/artifacts/after/valgrind_after.txt
docker exec rustlab bash /work/scripts/asan_check.sh /work /tmp/asan /work/artifacts/after/asan_after.txt
docker exec rustlab bash /work/scripts/tsan_check.sh /work /tmp/tsan /work/artifacts/after/tsan_after.txt bug8_race_increment
docker exec rustlab bash /work/scripts/profile.sh    /work /tmp/prof /work/artifacts/after/callgrind_after 8

docker rm -f rustlab             # stop the container when done

On Windows, call docker from Git Bash with MSYS_NO_PATHCONV=1 so that Unix paths (/work) are not converted.

3. "Before/after" benchmarks

./scripts/compare.sh before   # on the slow version of algo.rs
# (apply the optimizations)
./scripts/compare.sh after    # criterion shows the speedup

Ready-made HTML report with charts: broken-app/artifacts/criterion/report/index.html.


Known coverage limitations

  • Performance regressions are guarded by benchmarks (unit tests pin correctness, not speed); a partial guard is slow_fib(90/93) in the regressions (recursion can't handle that depth).
  • A leak/UAF/OOB is not always failed by plain cargo test - they are caught by Miri/Valgrind/ASan (runs attached; there's no CI in the project, the sanitizers are run manually via scripts).

The full list with rationale is in REPORT.md.

About

Rust debugging & performance lab: 8 seeded defects fixed and verified with Miri, Valgrind, ASan, TSan; fibonacci ~249,000× faster, dedup ~214×. Course assignment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors