update
This commit is contained in:
parent
65623a630c
commit
f2bbbdccdd
58
README.md
Normal file
58
README.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# FRET
|
||||||
|
## Structure
|
||||||
|
* LibAFL-based fuzzer under `LibAFL/fuzzers/FRET`
|
||||||
|
* FreeRTOS demos under `FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC`
|
||||||
|
* QEMU instrumentation under `qemu-libafl-bridge`
|
||||||
|
## HowTo
|
||||||
|
### Development environment
|
||||||
|
`nix develop` or `nix-shell`
|
||||||
|
### Build FRET
|
||||||
|
```sh
|
||||||
|
cd LibAFL/fuzzers/FRET
|
||||||
|
# First time and after changes to QEMU
|
||||||
|
sh -c "unset CUSTOM_QEMU_NO_BUILD CUSTOM_QEMU_NO_CONFIGURE && cargo build"
|
||||||
|
# Afterwards, simply use
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
### Build additional tools
|
||||||
|
```sh
|
||||||
|
# Trace analysis tool
|
||||||
|
cd state2gantt && cargo build && cd -
|
||||||
|
# Benchmark evaluation tool
|
||||||
|
cd LibAFL/fuzzers/FRET/benchmark/number_cruncher && cargo build && cd -
|
||||||
|
```
|
||||||
|
### Build FreeRTOS Demos
|
||||||
|
```sh
|
||||||
|
cd LibAFL/fuzzers/FRET/benchmark
|
||||||
|
sh build_all_demos.sh
|
||||||
|
```
|
||||||
|
### Example usage
|
||||||
|
* Build the demos and additional tools first
|
||||||
|
```sh
|
||||||
|
cd LibAFL/fuzzers/FRET
|
||||||
|
# Help for arguments
|
||||||
|
cargo run -- --help
|
||||||
|
# Example
|
||||||
|
export DUMP=$(mktemp -d)
|
||||||
|
dd if=/dev/random of=$DUMP/input bs=8K count=1
|
||||||
|
# fuzz for 10 seconds
|
||||||
|
cargo run -- -k benchmark/build/waters_seq_full.elf -c benchmark/target_symbols.csv -n $DUMP/output -tag fuzz -t 10 --seed 123456
|
||||||
|
# Produce a trace for the worst case found
|
||||||
|
cargo run -- -k benchmark/build/waters_seq_full.elf -c benchmark/target_symbols.csv -n $DUMP/show -tr showmap -i $DUMP/output.case
|
||||||
|
# plot the result
|
||||||
|
../../../state2gantt/driver.sh $DUMP/show.trace.ron
|
||||||
|
# view the gantt chart
|
||||||
|
open $DUMP/show_job.html
|
||||||
|
```
|
||||||
|
### Perform canned benchmarks
|
||||||
|
* Build the demos and additional tools first
|
||||||
|
* Select a benchmark set in `LibAFL/fuzzers/FRET/benchmark/Snakefile`
|
||||||
|
```sh
|
||||||
|
# $BENCHDIR
|
||||||
|
cd LibAFL/fuzzers/FRET/benchmark
|
||||||
|
# e.g.
|
||||||
|
snakemake -c 128 set48 set64 set128
|
||||||
|
# plot the resutls
|
||||||
|
sh plot_all_benchmarks.sh
|
||||||
|
sh plot_all_traces.sh
|
||||||
|
```
|
@ -19,6 +19,7 @@
|
|||||||
sphinx_rtd_theme
|
sphinx_rtd_theme
|
||||||
# other python packages
|
# other python packages
|
||||||
];
|
];
|
||||||
|
R-with-my-packages = pkgs.rWrapper.override{ packages = with pkgs.rPackages; [ ggplot2 readr dplyr plotly mosaic DBI tikzDevice colorspace heatmaply RColorBrewer RSQLite languageserver ]; };
|
||||||
in
|
in
|
||||||
with pkgs;
|
with pkgs;
|
||||||
rec {
|
rec {
|
||||||
@ -47,13 +48,15 @@
|
|||||||
# generate bindings from RTOS to Rust
|
# generate bindings from RTOS to Rust
|
||||||
rust-bindgen
|
rust-bindgen
|
||||||
# compare libafl edges
|
# compare libafl edges
|
||||||
packages.edge_compare
|
#packages.edge_compare
|
||||||
# Debugging
|
# Debugging
|
||||||
ddd
|
ddd
|
||||||
# visualization
|
# visualization
|
||||||
graphviz
|
graphviz
|
||||||
#rstudioWrapper # prefer host packages for R
|
#rstudioWrapper # prefer host packages for R
|
||||||
#R
|
#R
|
||||||
|
R-with-my-packages
|
||||||
|
pandoc
|
||||||
# dependencies for mosaic
|
# dependencies for mosaic
|
||||||
freetype
|
freetype
|
||||||
fontconfig
|
fontconfig
|
||||||
@ -75,6 +78,10 @@
|
|||||||
# export CROSS_CC=arm-none-eabi-gcc
|
# export CROSS_CC=arm-none-eabi-gcc
|
||||||
export LIBCLANG_PATH=${llvmPackages_19.libclang.lib}/lib
|
export LIBCLANG_PATH=${llvmPackages_19.libclang.lib}/lib
|
||||||
export BENCHDIR=bench_default
|
export BENCHDIR=bench_default
|
||||||
|
|
||||||
|
export PICO_SDK_PATH=$(pwd)/pico-sdk
|
||||||
|
export FREERTOS_KERNEL_PATH=$(pwd)/FreeRTOS-Kernel
|
||||||
|
mkdir -p $TMPDIR
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
947
graph2viz/Cargo.lock
generated
947
graph2viz/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
3
input_serde/.gitignore
vendored
Normal file
3
input_serde/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target
|
||||||
|
*.case
|
||||||
|
*.edit
|
@ -51,7 +51,7 @@ fn unfold_input(input : &MultipartInput<BytesInput>) -> HashMap<String,Either<Ve
|
|||||||
res.insert(name.to_string(),Left(part.bytes().to_vec()));
|
res.insert(name.to_string(),Left(part.bytes().to_vec()));
|
||||||
} else {
|
} else {
|
||||||
// let times = unsafe{std::mem::transmute::<&[u8], &[u32]>(&part.bytes()[0..4*(part.bytes().len()/4)])}.to_vec();
|
// let times = unsafe{std::mem::transmute::<&[u8], &[u32]>(&part.bytes()[0..4*(part.bytes().len()/4)])}.to_vec();
|
||||||
println!("name {} len {}", name, part.bytes().len());
|
eprintln!("name {} len {}", name, part.bytes().len());
|
||||||
let mut times = part.bytes().chunks(4).filter(|x| x.len()==4).map(|x| u32::from_le_bytes(x.try_into().unwrap())).collect::<Vec<_>>();
|
let mut times = part.bytes().chunks(4).filter(|x| x.len()==4).map(|x| u32::from_le_bytes(x.try_into().unwrap())).collect::<Vec<_>>();
|
||||||
times.sort_unstable();
|
times.sort_unstable();
|
||||||
res.insert(name.to_string(),Right(times));
|
res.insert(name.to_string(),Right(times));
|
||||||
|
947
state2gantt/Cargo.lock
generated
947
state2gantt/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
fret = { path = "../LibAFL/fuzzers/FRET" }
|
fret = { path = "../LibAFL/fuzzers/FRET" }
|
||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
# hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible
|
||||||
# petgraph = { version="0.6.0", features = ["serde-1"] }
|
# petgraph = { version="0.6.0", features = ["serde-1"] }
|
||||||
ron = "0.7" # write serialized data - including hashmaps
|
ron = "0.7" # write serialized data - including hashmaps
|
||||||
rand = "0.5"
|
rand = "0.5"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
ROOTDIR=~/code/work/FRETv3
|
||||||
if [ -z "$1" ]; then exit 1; fi
|
if [ -z "$1" ]; then exit 1; fi
|
||||||
OFILE_A="$(dirname "$1")/$(basename -s .trace.ron "$1")_job.csv"
|
OFILE_A="$(dirname "$1")/$(basename -s .trace.ron "$1")_job.csv"
|
||||||
OFILE_B="$(dirname "$1")/$(basename -s .trace.ron "$1")_instance.csv"
|
OFILE_B="$(dirname "$1")/$(basename -s .trace.ron "$1")_instance.csv"
|
||||||
@ -7,6 +8,7 @@ if [ -n "$2" ]; then
|
|||||||
EXTRA="-t $2"
|
EXTRA="-t $2"
|
||||||
fi
|
fi
|
||||||
rm -f "$OFILE_A" "$OFILE_B"
|
rm -f "$OFILE_A" "$OFILE_B"
|
||||||
~/code/FRET/state2gantt/target/debug/state2gantt -i $1 -a "$OFILE_A" -r "$OFILE_B" -p "$OFILE_C" $EXTRA
|
echo $ROOTDIR/state2gantt/target/debug/state2gantt -i $1 -a "$OFILE_A" -r "$OFILE_B" -p "$OFILE_C" $EXTRA
|
||||||
# ~/code/FRET/state2gantt/plot.r "$OFILE_A" html
|
$ROOTDIR/state2gantt/target/debug/state2gantt -i $1 -a "$OFILE_A" -r "$OFILE_B" -p "$OFILE_C" $EXTRA
|
||||||
~/code/FRET/state2gantt/plot_response.r "$OFILE_A" "$OFILE_B" html
|
echo $ROOTDIR/state2gantt/plot_response.r "$OFILE_A" "$OFILE_B" html
|
||||||
|
$ROOTDIR/state2gantt/plot_response.r "$OFILE_A" "$OFILE_B" html
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env Rscript
|
#!/usr/bin/env Rscript
|
||||||
# Load necessary libraries
|
# Load necessary libraries
|
||||||
|
#install.packages(c(ggplot2,readr,dplyr,plotly))
|
||||||
library(ggplot2)
|
library(ggplot2)
|
||||||
library(readr)
|
library(readr)
|
||||||
library(dplyr)
|
library(dplyr)
|
||||||
@ -58,6 +59,8 @@ create_gantt_chart <- function(csv_file_a, csv_file_b, MIN_WIDTH, output_format
|
|||||||
"<br>",
|
"<br>",
|
||||||
"State:", df$state,
|
"State:", df$state,
|
||||||
"<br>",
|
"<br>",
|
||||||
|
"ABB:", df$abb,
|
||||||
|
"<br>",
|
||||||
"End:", df$end
|
"End:", df$end
|
||||||
)
|
)
|
||||||
df_b$label <- paste(
|
df_b$label <- paste(
|
||||||
@ -84,14 +87,12 @@ create_gantt_chart <- function(csv_file_a, csv_file_b, MIN_WIDTH, output_format
|
|||||||
aes(x = start, y = name),
|
aes(x = start, y = name),
|
||||||
color = "red", size = 1)
|
color = "red", size = 1)
|
||||||
|
|
||||||
|
|
||||||
# Convert the ggplot object to a plotly object for interactivity
|
|
||||||
p_interactive <- ggplotly(p)
|
|
||||||
|
|
||||||
# Handle output format
|
# Handle output format
|
||||||
if (!is.null(output_format)) {
|
if (!is.null(output_format)) {
|
||||||
output_file <- sub("\\.csv$", paste0(".", output_format), csv_file_a)
|
output_file <- sub("\\.csv$", paste0(".", output_format), csv_file_a)
|
||||||
if (output_format == "html") {
|
if (output_format == "html") {
|
||||||
|
# Convert the ggplot object to a plotly object for interactivity
|
||||||
|
p_interactive <- ggplotly(p)
|
||||||
htmlwidgets::saveWidget(p_interactive, output_file)
|
htmlwidgets::saveWidget(p_interactive, output_file)
|
||||||
} else if (output_format == "png") {
|
} else if (output_format == "png") {
|
||||||
ggsave(output_file, plot = p, device = "png")
|
ggsave(output_file, plot = p, device = "png")
|
||||||
@ -99,6 +100,8 @@ create_gantt_chart <- function(csv_file_a, csv_file_b, MIN_WIDTH, output_format
|
|||||||
stop("Invalid output format. Use 'html' or 'png'.")
|
stop("Invalid output format. Use 'html' or 'png'.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
# Convert the ggplot object to a plotly object for interactivity
|
||||||
|
p_interactive <- ggplotly(p)
|
||||||
# Print the interactive Gantt chart
|
# Print the interactive Gantt chart
|
||||||
print(p_interactive)
|
print(p_interactive)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use hashbrown::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env,fs};
|
use std::{env,fs};
|
||||||
use fret::systemstate::{ExecInterval, JobInstance, ReducedFreeRTOSSystemState};
|
use fret::systemstate::{ExecInterval, RTOSJob, target_os::SystemTraceData, target_os::freertos::FreeRTOSTraceMetadata, target_os::SystemState, target_os::TaskControlBlock};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -29,7 +29,7 @@ struct Config {
|
|||||||
task: Option<String>,
|
task: Option<String>,
|
||||||
|
|
||||||
/// Translate times to microseconds
|
/// Translate times to microseconds
|
||||||
#[arg(short, long, default_value = "false")]
|
#[arg(short, long)]
|
||||||
micros: bool,
|
micros: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,23 +52,29 @@ fn main() {
|
|||||||
.append(false)
|
.append(false)
|
||||||
.open(x).expect("Could not create file"));
|
.open(x).expect("Could not create file"));
|
||||||
|
|
||||||
let mut level_per_task : HashMap<&String, u32> = HashMap::new();
|
let mut level_per_task : HashMap<String, u32> = HashMap::new();
|
||||||
|
|
||||||
|
|
||||||
let mut trace : (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>, Vec<JobInstance>, HashMap<String, HashMap<u32, (usize, usize, u64)>>) = ron::from_str(&String::from_utf8_lossy(&raw_input)).expect("Can not parse HashMap");
|
// Store priority per task
|
||||||
for s in &trace.0 {
|
let trace : FreeRTOSTraceMetadata = ron::from_str(&String::from_utf8_lossy(&raw_input)).expect("Can not parse HashMap");
|
||||||
|
// task_name -> (abb_addr -> (interval_count, exec_count, exec_time, woet))
|
||||||
|
let mut abb_profile : HashMap<String, HashMap<u32, (usize, usize, u64, u64)>> = trace.select_abb_profile(conf.task.clone());
|
||||||
|
for s in trace.intervals() {
|
||||||
if s.level == 0 {
|
if s.level == 0 {
|
||||||
level_per_task.insert(&trace.1[&s.start_state].current_task.task_name,trace.1[&s.start_state].current_task.priority);
|
let t = trace.states_map()[&s.start_state].current_task();
|
||||||
|
level_per_task.insert(t.task_name().clone(),t.base_priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let limits = conf.task.as_ref().map(|task| trace.2.iter().filter_map(move |x| if &x.name == task {Some(x)} else {None}).max_by_key(|x| x.response-x.release)).flatten().map(|x| x.release..x.response);
|
// Range of longest selected job
|
||||||
|
let limits = conf.task.as_ref().map(|task| trace.worst_jobs_per_task_by_response_time().get(task).map(|x| x.release..x.response)).flatten();
|
||||||
if let Some(limits) = &limits {
|
if let Some(limits) = &limits {
|
||||||
println!("Limits: {} - {}",limits.start,limits.end);
|
println!("Limits: {} - {}",limits.start,limits.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
activation_file.as_mut().map(|x| writeln!(x,"start,end,prio,name,state_id,state").expect("Could not write to file"));
|
let mut intervals = trace.intervals().clone();
|
||||||
for s in trace.0.iter_mut() {
|
activation_file.as_mut().map(|x| writeln!(x,"start,end,prio,name,state_id,state,abb").expect("Could not write to file"));
|
||||||
|
for s in intervals.iter_mut() {
|
||||||
if let Some(l) = &limits {
|
if let Some(l) = &limits {
|
||||||
if s.start_tick > l.end || s.end_tick < l.start {
|
if s.start_tick > l.end || s.end_tick < l.start {
|
||||||
continue;
|
continue;
|
||||||
@ -76,16 +82,17 @@ fn main() {
|
|||||||
s.start_tick = s.start_tick.max(l.start);
|
s.start_tick = s.start_tick.max(l.start);
|
||||||
s.end_tick = s.end_tick.min(l.end);
|
s.end_tick = s.end_tick.min(l.end);
|
||||||
}
|
}
|
||||||
let start_tick = if conf.micros {s.start_tick / fret::time::clock::QEMU_ISNS_PER_USEC as u64} else {s.start_tick};
|
let start_tick = if conf.micros {s.start_tick as f32 / fret::time::clock::QEMU_ISNS_PER_USEC} else {s.start_tick as f32};
|
||||||
let end_tick = if conf.micros {s.end_tick / fret::time::clock::QEMU_ISNS_PER_USEC as u64} else {s.end_tick};
|
let end_tick = if conf.micros {s.end_tick as f32 / fret::time::clock::QEMU_ISNS_PER_USEC} else {s.end_tick as f32};
|
||||||
let state = &trace.1[&s.start_state];
|
let state = &trace.states_map()[&s.start_state];
|
||||||
if s.level == 0 {
|
if s.level == 0 {
|
||||||
activation_file.as_mut().map(|x| writeln!(x,"{},{},{},{},{:X},{}",start_tick,end_tick,trace.1[&s.start_state].current_task.priority,trace.1[&s.start_state].current_task.task_name, state.get_hash()>>48, state).expect("Could not write to file"));
|
activation_file.as_mut().map(|x| writeln!(x,"{},{},{},{},{:X},{},{}",start_tick,end_tick,trace.states_map()[&s.start_state].current_task().priority,trace.states_map()[&s.start_state].current_task().task_name, state.get_hash()>>48, state, s.abb.as_ref().map(|x| x.get_start()).unwrap_or(u32::MAX) ).expect("Could not write to file"));
|
||||||
} else {
|
} else {
|
||||||
activation_file.as_mut().map(|x| writeln!(x,"{},{},-{},{},{:X},{}",start_tick,end_tick,s.level,s.start_capture.1, state.get_hash()>>48, state).expect("Could not write to file"));
|
activation_file.as_mut().map(|x| writeln!(x,"{},{},-{},{},{:X},{},{}",start_tick,end_tick,s.level,s.start_capture.1, state.get_hash()>>48, state, s.abb.as_ref().map(|x| x.get_start()).unwrap_or(u32::MAX)).expect("Could not write to file"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut jobs = trace.jobs().clone();
|
||||||
/* Write all job instances from release to response */
|
/* Write all job instances from release to response */
|
||||||
let instance_file = instance_path.map(|x| std::fs::OpenOptions::new()
|
let instance_file = instance_path.map(|x| std::fs::OpenOptions::new()
|
||||||
.read(false)
|
.read(false)
|
||||||
@ -96,7 +103,7 @@ fn main() {
|
|||||||
|
|
||||||
if let Some(mut file) = instance_file {
|
if let Some(mut file) = instance_file {
|
||||||
writeln!(file,"start,end,prio,name").expect("Could not write to file");
|
writeln!(file,"start,end,prio,name").expect("Could not write to file");
|
||||||
for s in trace.2.iter_mut() {
|
for s in jobs.iter_mut() {
|
||||||
if limits.as_ref().map(|x| !x.contains(&s.release) && !x.contains(&s.response) ).unwrap_or(false) {
|
if limits.as_ref().map(|x| !x.contains(&s.release) && !x.contains(&s.response) ).unwrap_or(false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -121,13 +128,13 @@ fn main() {
|
|||||||
|
|
||||||
if let Some(mut file) = abb_file {
|
if let Some(mut file) = abb_file {
|
||||||
conf.micros = true;
|
conf.micros = true;
|
||||||
if trace.3.is_empty() {
|
if abb_profile.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writeln!(file,"name,addr,active,finish,micros").expect("Could not write to file");
|
writeln!(file,"name,addr,active,finish,micros,woet").expect("Could not write to file");
|
||||||
for (name, rest) in trace.3.iter_mut().sorted_by_key(|x| x.0) {
|
for (name, rest) in abb_profile.iter_mut().sorted_by_key(|x| x.0) {
|
||||||
rest.iter().sorted_by_key(|x| x.0).for_each(|(addr, (active, finish, time))| {
|
rest.iter().sorted_by_key(|x| x.0).for_each(|(addr, (active, finish, time, woet))| {
|
||||||
writeln!(file,"{},{},{},{},{:.1}",name,addr,active,finish,if conf.micros {*time as f64 / fret::time::clock::QEMU_ISNS_PER_USEC as f64} else {*time as f64}).expect("Could not write to file");
|
writeln!(file,"{},{},{},{},{},{}",name,addr,active,finish,if conf.micros {*time as f64 / fret::time::clock::QEMU_ISNS_PER_USEC as f64} else {*time as f64}, if conf.micros {*woet as f64 / fret::time::clock::QEMU_ISNS_PER_USEC as f64} else {*woet as f64}).expect("Could not write to file");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user