PRAT is the research artifact accompanying the paper "Guided Feature Identification and Removal for Resource-constrained Firmware" (ACM TOSEM, 2021). It identifies and removes feature-specific code from C/C++/Rust projects by differential dynamic coverage analysis: it builds a project with and without each feature, runs a test suite against both, and attributes to the feature the lines that execute only when it is enabled.
📚 If you use PRAT in academic work, please cite the paper.
The paper does not publish the exact source revisions or its original artifact. The Docker corpus therefore pins exact commits from later releases and checks whether the implementation behaves consistently on every paper codebase. It is a compatibility evaluation, not a claim that modern-version line counts exactly reproduce the 2021 run.
PRAT implements Algorithm 1 of the paper. For a program P with features F:
-
Identify features by parsing build configurations (Make, CMake, autoconf, Cargo)
-
Assemble the test suite
T = U u S— the project's own tests plus tests generated by KLEE. Normal analysis may run withUalone;--paper-algorithmrequires KLEE. -
Build the baseline
B_allwith every feature enabled and collect the executed line setL_allby runningTagainst it -
For each feature
f, buildB_fwith all features exceptf, collectL_f, and computeD_f = L_all \ L_fthe lines that execute when
fis on and do not whenfis off. This isn+1builds fornfeatures, not2n. -
Present
D_fas a feature graph — a DAG whose roots are features, intermediate nodes are source files, and leaves are contiguous runs of lines — so an analyst can see how much code each feature carries and where, before deciding -
Remove the selected features' lines, rebuild, and re-run
T, checking for crashes and for divergence from pre-removal behaviour
The mapping is a set difference over executed lines, not a textual diff. A line never
executed in either build is never removed: the paper trades completeness for soundness, so
incomplete test coverage shrinks D_f rather than pulling in unrelated code. How much was left
in place for that reason is reported on every run.
- Python 3.9+
- Docker (for reproducible demos)
- Build tools: gcc, make, cmake
- Coverage tools: gcov or llvm-cov
git clone <repository-url>
cd PRAT
pip install -e ".[dev]"Target projects (Mosquitto, FFmpeg) are not vendored in the repo. Fetch them with:
./scripts/fetch-targets.sh # All targets
./scripts/fetch-targets.sh mosquitto # Just Mosquitto
./scripts/fetch-targets.sh ffmpeg # Just FFmpegfrom prat.workflow import run_complete_workflow
result = run_complete_workflow(
project_path="App/mosquitto",
feature="TLS",
run_tests=False
)
print(f"Removable lines: {result.extraction_result.total_removable_lines}")# Analyze a feature
prat App/mosquitto TLS
# List available features
prat App/mosquitto --list
# Dry run (preview operations)
prat App/mosquitto TLS --dry-run
# With test suite
prat App/mosquitto TLS --tests --verbose
# Enforce Algorithm 1 end to end, including KLEE, union removal, and verification
prat App/mosquitto --paper-algorithmThe KLEE stage currently supports C/C++ LLVM bitcode. Cargo/Rust projects support dynamic differential mapping and fail explicitly if symbolic or full-paper mode is requested.
# Build and run a demo
python3 src/demo-runner.py --build mosquitto-tls
python3 src/demo-runner.py --run mosquitto-tls --output demo_output
# Run all demos with comparison report
python3 src/demo-runner.py --run-all --output demo_outputsrc/prat/
├── workflow.py # End-to-end orchestration with checkpoints
├── batch.py # Multi-feature batch analysis (paper Algorithm 1)
├── discovery.py # Automatic feature flag discovery
├── compilation.py # Multi-build-system compilation (Make, CMake, Autotools, Cargo)
├── coverage.py # gcov/llvm-cov/cargo-llvm-cov coverage generation
├── gcov.py # gcov parsing: executed / never-executed / non-executable line sets
├── mapping.py # D_f = L_all \ L_f (paper Algorithm 1, lines 9-11)
├── diff.py # Side-by-side code comparison reports
├── extraction.py # Packages D_f for reporting and removal
├── removal.py # Automated feature code removal (line- and file-level)
├── verification.py # Post-removal correctness verification
├── symbolic.py # KLEE symbolic test generation (paper Table 3 parameters)
├── variants.py # Cumulative variant chain for the correctness evaluation
├── fuzzing.py # Boofuzz MQTT harness (optional: pip install 'prat[fuzz]')
├── feature_graph.py # Three-tier feature-graph DAG + interactive D3.js view
├── reporting.py # HTML report + DOT graph generation
├── docker_runner.py # Docker container orchestration
└── adapters/ # Project-specific build adapters
├── base.py # Abstract adapter interface
├── mosquitto.py # Mosquitto MQTT broker (Make)
├── ffmpeg.py # FFmpeg multimedia framework (Autotools)
├── uamqp.py # azure-uamqp-c (CMake)
├── opendds.py # OpenDDS (MPC/ACE-TAO)
├── aom.py # libaom AV1 codec (CMake)
├── cmake.py # Generic CMake projects
└── rust.py # Cargo/Rust projects
| Build System | Feature Flag Format | Example |
|---|---|---|
| Make | WITH_FEATURE=yes/no |
WITH_TLS=yes |
| CMake | -DCONFIG_FEATURE=1/0 |
-DCONFIG_TLS=1 |
| Autotools | --enable/--disable-feature |
--disable-decoder=dca |
| Cargo | --features feature |
--features qlog |
Feature discovery runs every analyzer whose build system is present and merges the results,
because a project can expose different features through different build systems — Mosquitto ships
both CMakeLists.txt and config.mk, and declares BRIDGE, PERSISTENCE, WEBSOCKETS, SYS_TREE and
MEMORY_TRACKING only in the latter.
PRAT generates:
- Coverage directories:
coverage_files_WITH_{FEATURE}_{yes|no}/, pluscoverage_files_all_features/for a batch run's baseline report.html— removable lines per source fileFDG.dot— the feature graph as Graphviz (feature → file → line-range)feature_graph.html— interactive three-tier graph (feature → files → line sets); a single-feature run yields a one-root graph, a batch run the whole DAG. Self-contained: D3 is vendored and inlined, so the page opens offlinecomparison_reports/— the paper's side-by-side reports: per-line execution state in both builds withD_fmarked, and original-vs-debloated source after a removalreport.jsonandworkflow_checkpoint.json— machine-readable results
Eight demos cover all seven paper evaluation codebases (Mosquitto has two). Each clones and verifies an exact target commit, maps one feature, removes the exact mapped lines, rebuilds, replays the tests, and records the source commit, tool versions, execution evidence, and run identity.
Expected values come from paper_expected_results.json, where every figure is traceable to
paper/results/code_removal.csv (the paper's per-feature data) or to Table 4 (whole-program
totals). Four demos analyze a feature the paper publishes no line count for; those are
measured and reported as OBSERVED rather than scored, because there is nothing published to
compare them against. See REPRODUCIBILITY.md for the methodology and for
why each substitution was necessary.
No results are committed. The mapping algorithm was corrected after the last snapshot was taken, so every number must be regenerated on the reviewer's machine. See
results/README.mdandARTIFACT.md.
# Disk-safe compatibility corpus: map → remove → verify each source-pinned target
make compatibility-check
# Or one demo at a time, removing its (large) image afterward:
python3 src/demo-runner.py --build mosquitto-tls
python3 src/demo-runner.py --run mosquitto-tls --cleanup --output results/docker
# Compare one completed target with the corresponding published number:
python3 scripts/validate_paper_results.py results/docker/ \
--target mosquitto-tls --strictDisk note: the C/C++ targets (ffmpeg, aom, opendds) produce multi-GB images. Always run with
--cleanup(ormake compatibility-check, which removes each image after its run) so the Docker VM disk is not exhausted.
See docker/README.md for detailed Docker instructions and REPRODUCIBILITY.md for results.
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Type checking
mypy src/prat/
# Lint
ruff check src/- Artifact guide — for reviewers: what to run first, how long it takes, what success looks like
- Reproducibility report — methodology, provenance of every paper number, and limitations
- Paper Alignment — each paper claim mapped to the code and the tests that pin it
- API Reference
- Usage Examples
- Troubleshooting
- Docker Demos
If you use PRAT or build on this work, please cite the paper:
@article{williams2021guided,
author = {Williams, Ryan and Ren, Tongwei and De Carli, Lorenzo and Lu, Long and Smith, Gillian},
title = {Guided Feature Identification and Removal for Resource-constrained Firmware},
journal = {ACM Transactions on Software Engineering and Methodology},
volume = {31},
number = {2},
pages = {1--25},
year = {2021},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
issn = {1049-331X},
doi = {10.1145/3487568},
url = {https://doi.org/10.1145/3487568}
}Ryan Williams, Tongwei Ren, Lorenzo De Carli, Long Lu, and Gillian Smith. 2021. Guided Feature Identification and Removal for Resource-constrained Firmware. ACM Transactions on Software Engineering and Methodology 31, 2: 1–25. https://doi.org/10.1145/3487568
A machine-readable CITATION.cff is also provided (GitHub renders a
"Cite this repository" button from it).
- Ryan Williams — author and maintainer
- Tongwei Ren, Lorenzo De Carli, Long Lu, Gillian Smith — paper co-authors
PRAT follows Semantic Versioning. The current version is declared in
pyproject.toml; release history is in CHANGELOG.md.
PRAT is released under the MIT License.
