Compare commits

...

33 Commits

Author SHA1 Message Date
9ca8e88ec0 don't save client logs 2026-05-06 20:36:10 +02:00
be2c4b7bf2 log experiment stages to separate files 2026-05-06 20:30:41 +02:00
644603fb8b don't move runner log 2026-05-06 20:08:39 +02:00
0dccd1df46 update fail binaries again again 2026-05-06 19:52:25 +02:00
9db4cb8bb1 remove fail binaries 2026-05-06 19:39:47 +02:00
10fe608a90 separate client/server logs + move logs to experiment dir 2026-05-04 13:32:34 +02:00
2de6e20675 update fail binaries again - set static port (--server-port doesn't work) 2026-05-04 13:31:18 +02:00
bc98e2af5c add updated fail binaries 2026-05-04 11:38:05 +02:00
3819e84acb remove fail binaries 2026-05-04 11:35:42 +02:00
6ce8987ebd add --server-port option to generic-experiment-client 2026-04-29 21:02:10 +02:00
6859402483 update FAIL database queries for new version 2026-04-29 20:16:24 +02:00
32b1f3fa31 move old FAIL binaries so nix doesn't autopatchelf them 2026-04-29 20:10:02 +02:00
a6f33f1960 update FAIL version to current 2026-04-29 19:58:18 +02:00
1bf4886c64 choose experiments instead of builds for radare/objdump/wasm-objdump actions 2026-04-28 00:53:46 +02:00
e33fed9b8d add binaryninja action to menu 2026-04-28 00:40:31 +02:00
c87409dd5c add radare/objdump/wasm-objdump actions to menu 2026-04-28 00:17:51 +02:00
5f6537b7ea reenable --catch-outerspace and --catch-write-textsegment 2026-04-27 19:09:19 +02:00
5bfe1a366b disable --catch-outerspace and --catch-write-textsegment 2026-04-27 18:49:51 +02:00
f2559c8445 disable --catch-write-textsegment 2026-04-27 15:57:17 +02:00
4d0693dd30 disable --catch-outerspace 2026-04-27 15:49:18 +02:00
bba9ced348 add sankey chart 2026-04-24 15:27:41 +02:00
a8aa0bbb07 add matrix multiplication test program 2026-04-24 15:27:20 +02:00
1257534a64 enable --full-trace and --check-bounds 2026-04-24 15:27:11 +02:00
5aab319424 import native symbols in lib.h
lib.h gets included into the host module and the wasm module. For the
host module the attributes will be ignored.
2026-04-24 10:45:55 +02:00
348aac20ae add lazysql menu action 2026-04-24 00:25:06 +02:00
5da8fffed0 add tableplus to shell environment 2026-04-24 00:03:25 +02:00
2b554a22ad read entire marker notes file instead of first line 2026-04-23 22:44:50 +02:00
c76b0b8a8e allow annotating markers in explorer 2026-04-23 21:53:07 +02:00
5f42323ccb display corresponding section for markers in explorer 2026-04-23 18:42:31 +02:00
551335bf53 store additional files with builds (flake/startup/linkerscript/...) 2026-04-23 17:56:46 +02:00
7bd3d205c9 scatterplot: addresses in hex 2026-04-23 17:55:57 +02:00
59e36f6a7f update fail-db.png 2026-04-22 23:59:53 +02:00
693378d059 fix fail trace sources import 2026-04-22 21:53:52 +02:00
22 changed files with 774 additions and 332 deletions

80
charts/combined_sankey.r Normal file
View File

@ -0,0 +1,80 @@
library(ggplot2)
library(ggalluvial)
# Usage: Rscript combined_comparion.r exp_abspath1 exp_abspath2 ...
args <- commandArgs(trailingOnly = TRUE)
argc <- length(args)
if (argc != 2) {
print("Expecting two input files")
stop()
}
for (experiment in args) {
datafile <- paste(experiment, "/faults.csv", sep = "")
if (!file.exists(datafile)) {
print(paste("Input file", datafile, "is missing"))
stop()
}
}
resulttype_labels <- c(
OK_MARKER = "OK",
FAIL_MARKER = "FAIL",
DETECTED_MARKER = "DETECTED",
TIMEOUT = "TIMEOUT",
TRAP = "TRAP",
ACCESS_OUTERSPACE = "OUTERSPC",
WRITE_TEXTSEGMENT = "WRITETXT"
)
# Read data
datafile1 <- paste(args[1], "/faults.csv", sep = "")
data1 <- readr::read_csv(datafile1)
data1$fault_address <- strtoi(data1$fault_address)
data1$resulttype <- resulttype_labels[data1$resulttype]
# tibble::glimpse(data1)
datafile2 <- paste(args[2], "/faults.csv", sep = "")
data2 <- readr::read_csv(datafile2)
data2$fault_address <- strtoi(data2$fault_address)
data2$resulttype <- resulttype_labels[data2$resulttype]
# tibble::glimpse(data2)
# https://corybrunson.github.io/ggalluvial/
joined <- merge(
data1[, c("fault_address", "resulttype", "faults")],
data2[, c("fault_address", "resulttype", "faults")],
by = "fault_address",
suffixes = c("_bench1", "_bench2")
)
streams <- aggregate(
faults_bench2 ~ resulttype_bench1 + resulttype_bench2,
data = joined,
sum
)
names(streams) <- c("bench1", "bench2", "faults")
plot <- ggplot(
data = streams,
aes(axis1 = bench1, axis2 = bench2, y = faults)
) +
scale_x_discrete(
# TODO: Name the benchmarks
# limits = c(args[1], args[2])
limits = c("Bench A", "Bench B")
) +
labs(x = "Benchmark", y = "Faults") +
geom_alluvium(aes(fill = bench1)) +
geom_stratum() +
geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
theme_minimal() +
theme(legend.position = "none")
# TODO: Name the file according to the benchmarks
ggsave(
paste(args[2], "/../sankey.svg", sep = ""),
plot = plot,
)

View File

@ -20,4 +20,7 @@ plot <- ggplot(data, aes(x = benchmark, y = faults, fill = resulttype)) +
labs(x = "Benchmark", y = "Faults", fill = "Result Type") +
theme_minimal()
ggsave(paste(experiment, "/single_result.svg", sep = ""), plot = plot)
ggsave(
paste(experiment, "/single_result.svg", sep = ""),
plot = plot,
)

View File

@ -19,8 +19,14 @@ tibble::glimpse(data)
plot <- ggplot(data, aes(x = fault_address, y = faults)) +
geom_point(aes(color = resulttype)) +
scale_x_continuous(
labels = function(x) sprintf("0x%X", as.integer(x))
) +
scale_y_log10() +
labs(x = "Address", y = "Faults", color = "Type") +
theme_minimal()
ggsave(paste(experiment, "/scatter.svg", sep = ""), plot = plot)
ggsave(
paste(experiment, "/scatter.svg", sep = ""),
plot = plot,
)

BIN
fail-db.png (Stored with Git LFS)

Binary file not shown.

BIN
fail/bin/fail-x86-tracing (Stored with Git LFS)

Binary file not shown.

BIN
fail/bin/generic-experiment-client (Stored with Git LFS)

Binary file not shown.

BIN
fail/bin/generic-experiment-server (Stored with Git LFS)

Binary file not shown.

BIN
fail/bin/import-trace (Stored with Git LFS)

Binary file not shown.

BIN
fail/bin/prune-trace (Stored with Git LFS)

Binary file not shown.

6
flake.lock generated
View File

@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1773110118,
"narHash": "sha256-mPAG8phMbCReKSiKAijjjd3v7uVcJOQ75gSjGJjt/Rk=",
"lastModified": 1776329215,
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e607cb5360ff1234862ac9f8839522becb853bb9",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
"type": "github"
},
"original": {

View File

@ -321,10 +321,17 @@ rec {
src = pkgs.fetchFromGitea {
domain = "gitea.local.chriphost.de";
owner = "christoph";
# domain = "git.cs.tu-dortmund.de";
# owner = "christoph.urlacher";
repo = "wamr";
# With mmap_space in .text.wamr_aot
rev = "cda2009deb85511089b04b0ac736ad4da2d07e58";
hash = "sha256-CN6xTiwzF4Jbrpf21TF5c/C03Xb3urwkibRuIXjoU/w=";
# Without mmap_space in .text.wamr_aot
# rev = "4e7aed33fe53bf3ee4a3f2fe582c74816f850759";
# hash = "sha256-/4BKwoFDRfkA+DmbWagxdtkCDAED5rxbz5e4xvjvVWU=";
};
nativeBuildInputs = with pkgs; [cmake];
@ -408,7 +415,10 @@ rec {
grub2
xorriso
mariadb.client
dbeaver-bin
# dbeaver-bin
# beekeeper-studio
# tableplus
lazysql
iwasm
wamrc
fail-bin
@ -443,17 +453,28 @@ rec {
buildInputs = with pkgs; [
# FAIL runtime dependencies
python # bochs-experiment-runner.py, resultbrowser.py
alsa-lib # libasound.so.2
boost_pkgs.boost174 # libboost_coroutine.so.1.74.0, libboost_regex.so.1.74.0, libboost_thread.so.1.74.0
capstone_4 # libcapstone.so.4
# For old VSS FAIL
# alsa-lib # libasound.so.2
# boost_pkgs.boost174 # libboost_coroutine.so.1.74.0, libboost_regex.so.1.74.0, libboost_thread.so.1.74.0
# capstone_4 # libcapstone.so.4
# libdwarf_pkgs.libdwarf # libdwarf.so.1
# elfutils # libelf.so.1
# mariadb # libmariadb.so.3
# libpcl # libpcl.so.1
# protobuf_21 # libprotobuf.so.32
# SDL # libSDL-1.2.so.0
# libx11 # libX11.so.6
# libxrandr # libXrandr.so.2
# libz # libz.so.1
# For current FAIL
boost183
capstone # libcapstone.so.5
libdwarf_pkgs.libdwarf # libdwarf.so.1
elfutils # libelf.so.1
mariadb # libmariadb.so.3
libpcl # libpcl.so.1
protobuf_21 # libprotobuf.so.32
SDL # libSDL-1.2.so.0
libx11 # libX11.so.6
libxrandr # libXrandr.so.2
libz # libz.so.1
];

View File

@ -12,12 +12,12 @@ benchmark, resulttype, SUM(t.time2 - t.time1 + 1) AS faults,
CONCAT('0x', HEX(p.injection_instr_absolute)) AS fault_address
FROM variant v
JOIN trace t ON v.id = t.variant_id
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_physical_address = t.data_physical_address
JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id
JOIN fsppilot p ON r.pilot_id = p.id
WHERE v.variant = '$experiment'
GROUP BY benchmark, resulttype, p.injection_instr_absolute
ORDER BY benchmark, resulttype, SUM(t.time2 - t.time1 + 1) DESC;"
ORDER BY benchmark, resulttype, SUM(t.time2 - t.time1 + 1) DESC;";
}
sub args { return "--batch --raw"; }

View File

@ -11,7 +11,7 @@ sub query {
benchmark, resulttype, sum(t.time2 - t.time1 + 1) AS faults
FROM variant v
JOIN trace t ON v.id = t.variant_id
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_physical_address = t.data_physical_address
JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id
JOIN fsppilot p ON r.pilot_id = p.id
WHERE v.variant = '$experiment'

View File

@ -11,7 +11,7 @@ sub query {
benchmark, resulttype, sum(t.time2 - t.time1 + 1) AS faults
FROM variant v
JOIN trace t ON v.id = t.variant_id
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address
JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_physical_address = t.data_physical_address
JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id
JOIN fsppilot p ON r.pilot_id = p.id
WHERE v.variant = '$experiment'

View File

@ -8,6 +8,9 @@ use DateTime;
use feature 'say';
my $local_root = '/home/christoph/Notes/TU/MastersThesis/FailNix';
my $local_archive_dir = "$local_root/injections";
my $ntfy_url = 'https://ntfy.vps.chriphost.de';
my $ntfy_token = 'tk_rx8fd6hojuz4ekcb72j7juugkbmga'; # May be public
my $ntfy_topic = 'fail-alerts';
@ -139,4 +142,127 @@ sub execute_query {
return $result;
}
sub format_number_sep {
my ($number) = @_;
1 while $number =~ s/^(-?\d+)(\d{3})/$1.$2/;
return $number;
}
sub elf_read_sections {
my ($elffile) = @_;
my $readelf_out = qx{readelf -S $elffile};
my @lines = split "\n", $readelf_out;
my @sections;
foreach my $line (@lines) {
# [ 1] .text PROGBITS 00100000 001000 0000f0 00 AX 0 0 4
next
unless $line =~
/^\s*\[\s*\d+\]\s+(\..+?)\s+([A-Z]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+.*$/;
push @sections, {
name => $1,
type => $2,
address => $3, # Memory location
offset => $4, # File location
size => $5,
};
}
return @sections;
}
sub get_section_name {
my ( $address, @sections ) = @_;
my $name;
my $last_address = 0;
foreach my $section (@sections) {
my $cur_address = hex( $section->{address} );
if ( hex($address) >= $cur_address && $cur_address > $last_address ) {
$name = $section->{name};
$last_address = $cur_address;
}
}
return $name;
}
sub read_experiment_info {
my ($exp) = @_;
return unless ( -f "$local_archive_dir/$exp/0.info" );
open( my $fhandle, '<', "$local_archive_dir/$exp/0.info" )
or die "Failed to open 0.info: $!";
my $info = <$fhandle>;
chomp $info if defined $info;
close($fhandle);
return defined $info ? $info : "";
}
sub read_marker_info {
my ( $experiment, $benchmark, $address ) = @_;
return ""
unless (
-f "$local_archive_dir/$experiment/markers/$benchmark-$address.info" );
open( my $fhandle, '<',
"$local_archive_dir/$experiment/markers/$benchmark-$address.info" )
or die "Failed to open $benchmark-$address.info: $!";
local $/;
my $info = <$fhandle>;
chomp $info;
close($fhandle);
return $info;
}
sub overwrite_marker_info {
my ( $experiment, $benchmark, $address, $info ) = @_;
system( 'mkdir', '-p', "$local_archive_dir/$experiment/markers" );
open( my $fhandle, '>',
"$local_archive_dir/$experiment/markers/$benchmark-$address.info" )
or die "Failed to open $benchmark-$address.info: $!";
print $fhandle $info;
close($fhandle);
}
sub delete_marker_info {
my ( $experiment, $benchmark, $address ) = @_;
system( 'rm',
"$local_archive_dir/$experiment/markers/$benchmark-$address.info" );
}
sub select_experiment {
my ($multi) = @_;
my @experiments = find_subdirs($local_archive_dir);
my @exp_with_notes;
foreach my $exp (@experiments) {
my $info = read_experiment_info($exp);
push @exp_with_notes,
( defined $info && length($info) > 0 )
? sprintf( "%-50s (%s)", $exp, $info )
: $exp;
}
my @selected_experiments =
TUI::select_from_list( "Select Experiment", $multi, @exp_with_notes );
die "No experiment selected" unless @selected_experiments;
map { s/(.*?)\s+\(.+\)$/$1/ } @selected_experiments;
return $multi == 1 ? @selected_experiments : $selected_experiments[0];
}
1;

View File

@ -21,12 +21,7 @@ my $local_root = '/home/christoph/Notes/TU/MastersThesis/FailNix';
my $local_archive_dir = "$local_root/injections";
# Select experiment to open
my @experiments = Util::find_subdirs($local_archive_dir);
my @selected_experiments =
TUI::select_from_list( "Select Archived Experiment to Open", 0,
@experiments );
die "No experiment selected" unless @selected_experiments;
my $selected_experiment = $selected_experiments[0];
my $selected_experiment = Util::select_experiment(0);
my $cui = TUI::init_cui();
@ -75,7 +70,25 @@ my %index_of_markers;
my $selected_threshold = 0;
# TODO: Display the segment the addresses belong to
my @sections;
sub marker_label {
my ($marker) = @_;
return sprintf(
"%4s %8s %5s %17s %12sx %-40s",
$marker->{benchmark},
$marker->{fault_address},
Util::get_section_name( $marker->{fault_address}, @sections ),
$marker->{resulttype},
Util::format_number_sep( $marker->{faults} ),
Util::read_marker_info(
$selected_experiment, $marker->{benchmark},
$marker->{fault_address}
),
);
}
sub load_faults_csv {
@marker_values = ();
%marker_labels = ();
@ -86,11 +99,8 @@ sub load_faults_csv {
headers => 'auto'
);
sub format_number_sep {
my ($number) = @_;
1 while $number =~ s/^(-?\d+)(\d{3})/$1.$2/;
return $number;
}
@sections = Util::elf_read_sections(
"$local_archive_dir/$selected_experiment/system.elf");
# Result:
# [ { benchmark => "ip", resulttype => "OK_MARKER",
@ -99,13 +109,7 @@ sub load_faults_csv {
@marker_values = @$data;
@filtered_marker_values = @marker_values;
%marker_labels =
map {
$_ => sprintf(
"%5s %10s %20s %15sx",
$_->{benchmark}, $_->{fault_address},
$_->{resulttype}, format_number_sep( $_->{faults} ),
);
} @marker_values;
map { $_ => marker_label($_); } @marker_values;
}
sub filter_marker_values {
@ -361,7 +365,7 @@ $win->add(
'info', 'Label',
-y => -1,
-text =>
"Space = toggle, F = filter markers, S = open source, A = open assembly, R = resize, Q = quit",
"Space = toggle, F = filter markers, S = open source, A = open assembly, N = edit notes, R = resize, Q = quit",
);
sub set_geometry {
@ -379,8 +383,8 @@ sub layout_resize {
my $w = $win->width();
my $h = $win->height();
my $w1 = int( $w * 0.2 );
my $w2 = int( $w * 0.4 );
my $w1 = int( $w * 0.3 );
my $w2 = int( $w * 0.3 );
my $w3 = int( $w - $w1 - $w2 );
set_geometry( $left, 0, 1, $w1, $h - 2 );
@ -466,6 +470,7 @@ sub markers_filter_popup {
-title => "By Count Threshold",
-border => 1,
-text => $selected_threshold,
-pos => length($selected_threshold),
-regexp => '/^\d*$/',
);
@ -534,6 +539,79 @@ sub markers_filter_popup {
$focus->();
}
# =============================================================================
# Notes Popup
# =============================================================================
sub edit_notes_popup {
my $w = 60;
my $h = 24;
my $marker = $markers_panel->get_active_value();
my $marker_text =
Util::read_marker_info( $selected_experiment, $marker->{benchmark},
$marker->{fault_address} );
my $popup = $cui->add(
'popup', 'Window',
-x => int( $cui->width() / 2 - $w / 2 ),
-y => int( $cui->height() / 2 - $h / 2 ),
-width => $w,
-height => $h,
-border => 1,
-title => "Edit Notes",
);
my $notes = $popup->add(
'marker_notes',
'TextEditor',
-height => $h - 3,
-border => 1,
-text => $marker_text,
-pos => length($marker_text),
);
$popup->add(
'popup_info', 'Label',
-y => -1,
-text => "^S/^C = save, ^Q = quit",
);
$popup->set_binding(
sub {
my $text = $notes->get();
if ( length($text) > 0 ) {
Util::overwrite_marker_info( $selected_experiment,
$marker->{benchmark}, $marker->{fault_address}, $text, );
}
else {
Util::delete_marker_info( $selected_experiment,
$marker->{benchmark}, $marker->{fault_address} );
}
$marker_labels{$marker} = marker_label($marker);
$cui->delete('popup');
$cui->layout();
$markers_panel->draw();
},
"\cS",
"\cC",
);
$popup->set_binding(
sub {
$cui->delete('popup');
$cui->layout();
},
"\cQ"
);
$cui->layout();
$notes->focus();
}
# =============================================================================
# Bindings
# =============================================================================
@ -545,6 +623,13 @@ $win->set_binding(
'f',
);
$win->set_binding(
sub {
edit_notes_popup();
},
'n',
);
$win->set_binding(
sub {
layout_resize();

View File

@ -41,6 +41,10 @@ my $resultbrowser = 'resultbrowser.py';
my $remote_root = '/home/lab/smchurla/Documents/failnix';
my $remote_builds_dir = "$remote_root/builds";
my $db_host = "127.0.0.1";
my $db_port = "3306";
my $db_user = "smchurla";
my %handlers = (
'01. Build Experiments' => sub { do "$local_scripts_dir/build.pl"; },
@ -68,6 +72,9 @@ my %handlers = (
Mars::download_dir( "$remote_builds_dir/$_",
"$local_archive_dir/" . $_ =~ s/:/-/gr )
for @selected_dirs;
system( 'touch', "$local_archive_dir/" . $_ =~ s/:/-/gr . "/0.info" )
for @selected_dirs;
},
'04. Query Databases (Mars)' => sub {
@ -174,25 +181,18 @@ my %handlers = (
}
# Insert charts
my $attach_image = sub {
my ($name) = @_;
return unless ( -f "$local_archive_dir/$experiment/$name.svg" );
system(
'cp', '-i',
"$local_archive_dir/$experiment/$name.svg",
"$local_obsidian_attach/${experiment}_$name.svg"
);
system( 'obsidian', 'append', "file=zettel/$experiment",
"content=![[${experiment}_$name.svg]]\n" );
};
system(
'obsidian', 'append',
"file=zettel/$experiment", "content=## Charts\n\n"
);
my $attach_image = sub {
my ($name) = @_;
system( 'obsidian', 'append', "file=zettel/$experiment",
"content=![$name](file://$local_archive_dir/$experiment/$name.svg)\n"
);
};
$attach_image->("single_result");
$attach_image->("scatter");
}
@ -201,70 +201,9 @@ my %handlers = (
'10. Open Experiment In Explorer' =>
sub { do "$local_scripts_dir/explore.pl" },
'11. Open Experiment in Ghidra' => sub {
'11. Compare Experiment Results' => sub {
my @projects =
map { s/\.gpr//r } Util::find_files($local_ghidra_projects);
my @selected_projects =
TUI::select_from_list( "Select Project to Open in Ghidra",
0, @projects );
die "No project selected" unless @selected_projects;
my $project = $selected_projects[0];
system(
join " ",
(
"_JAVA_AWT_WM_NONREPARENTING=1", "ghidra",
"$local_ghidra_projects/$project.gpr",
)
);
},
'12. Open Experiment in Binsider' => sub {
my @experiments = Util::find_subdirs($local_archive_dir);
my @selected_experiments =
TUI::select_from_list( "Select Experiments to Open in Binsider",
0, @experiments );
die "No experiment selected" unless @selected_experiments;
my $selected_experiment = $selected_experiments[0];
system( 'binsider',
"$local_archive_dir/$selected_experiment/system.elf" );
},
'13. Open Experiment in ResultBrowser' => sub {
my @db_names = Mars::db_list();
my @selected_dbs =
TUI::select_from_list( "Select Database for ResultBrowser",
0, @db_names );
die "No database selected" unless @selected_dbs;
my $selected_db = $selected_dbs[0];
Util::rewrite_file( $local_db_conf, "database=",
"database=$selected_db\n" );
system( $resultbrowser, '-c', $local_db_conf, '--host=0.0.0.0',
"--port=$resultbrowser_port" );
},
'14. Compare Experiment Results' => sub {
# TODO: To util function (select_experiments,
# could also include the file-existance filter)
my @experiments = Util::find_subdirs($local_archive_dir);
# Only include experiments with resultsdata.csv
my @viable_experiments;
foreach my $experiment (@experiments) {
push @viable_experiments, $experiment
if ( -f "$local_archive_dir/$experiment/resultsdata.csv" );
}
# Select experiments
my @selected_experiments =
TUI::select_from_list( "Select Experiments to Compare",
1, @viable_experiments );
die "No experiments selected" unless @selected_experiments;
my @selected_experiments = Util::select_experiment(1);
# Read results
my %all_results;
@ -282,13 +221,6 @@ my %handlers = (
}
}
# TODO: To util function
sub format_number_sep {
my ($number) = @_;
1 while $number =~ s/^(-?\d+)(\d{3})/$1.$2/;
return $number;
}
my @benchs = ( 'ip', 'mem', 'regs' );
my @markers = (
'OK_MARKER', 'FAIL_MARKER',
@ -297,12 +229,15 @@ my %handlers = (
'ACCESS_OUTERSPACE'
);
my $heading = sprintf( "%5s %20s ", "BENCH", "TYPE" );
my $heading = sprintf( "%5s %20s ", "BENCH", "TYPE" );
my $subheading = sprintf( "%5s %20s ", "", "" );
foreach my $experiment (@selected_experiments) {
$heading .= sprintf( "%50s ", $experiment );
$subheading .=
sprintf( "%50s ", Util::read_experiment_info($experiment) );
}
my @entries = ( $heading, "" );
my @entries = ( $heading, $subheading, "" );
foreach my $benchmark (@benchs) {
foreach my $marker (@markers) {
my $entry = sprintf( "%5s %20s ", $benchmark, $marker );
@ -312,7 +247,7 @@ my %handlers = (
{
$entry .= sprintf(
"%50s ",
format_number_sep(
Util::format_number_sep(
$all_results{$experiment}{$benchmark}{$marker}
)
);
@ -332,48 +267,129 @@ my %handlers = (
0, @entries );
},
'15. Plot Results' => sub {
'12. Open Experiment in BinaryNinja' => sub {
my @selected_experiments = Util::select_experiment(1);
my @paths =
map { "$local_archive_dir/$_/system.elf" } @selected_experiments;
# Generate R ggplot2 charts
my @experiments = Util::find_subdirs($local_archive_dir);
my @selected_experiments =
TUI::select_from_list( "Select Experiments to Plot", 1,
@experiments );
die "No experiment selected" unless @selected_experiments;
my @charts = map { s/\.r//r } Util::find_files($local_charts_dir);
my @selected_charts =
TUI::select_from_list( "Select Plots to Generate", 1, @charts );
die "No plot selected" unless @selected_charts;
my @single_charts = grep { /single/ } @selected_charts;
foreach my $experiment (@selected_experiments) {
foreach my $chart (@single_charts) {
say " - Generating plot $chart for $experiment...";
system(
'Rscript',
"$local_charts_dir/$chart.r",
"$local_archive_dir/$experiment"
);
}
}
my @combined_charts = grep { /combined/ } @selected_charts;
my $print_experiments = join " ", @selected_experiments;
my @path_experiments =
map { "$local_archive_dir/$_" } @selected_experiments;
foreach my $chart (@combined_charts) {
say " - Generating plot $chart for ($print_experiments)...";
system( 'Rscript', "$local_charts_dir/$chart.r",
@path_experiments );
}
system( 'binaryninja', @paths );
},
'16. Run Build in GDB' => sub {
'13. Open Experiment in Binsider' => sub {
my $selected_experiment = Util::select_experiment(0);
system( 'binsider',
"$local_archive_dir/$selected_experiment/system.elf" );
},
'14. Open Experiment in Ghidra' => sub {
my @projects =
map { s/\.gpr//r } Util::find_files($local_ghidra_projects);
my @selected_projects =
TUI::select_from_list( "Select Project to Open in Ghidra",
0, @projects );
die "No project selected" unless @selected_projects;
my $project = $selected_projects[0];
system(
join " ",
(
"_JAVA_AWT_WM_NONREPARENTING=1", "ghidra",
"$local_ghidra_projects/$project.gpr",
)
);
},
'15. Run Objdump on Experiment' => sub {
my $selected_experiment = Util::select_experiment(0);
system(
"objdump $local_archive_dir/$selected_experiment/system.elf -D -M intel -S | bat --color never"
);
},
'16. Run Wasm-Objdump on Experiment' => sub {
my $selected_experiment = Util::select_experiment(0);
system(
"wasm-objdump -d $local_archive_dir/$selected_experiment/wasm_module.wasm | bat --color never"
);
},
'17. Run Radare2 on Experiment' => sub {
my $selected_experiment = Util::select_experiment(0);
say "Radare help:";
say "s <address> - Seek to address";
say "pd <n> - Disassemble n instructions";
say "pdf - Disassemble current function";
say "pdf @ <function> - Disassemble function";
say "pdr - Disassemble recursively";
say "V - Switch view";
say "p - Switch print mode";
say "P - Switch layout";
system(
'radare2', '-AA',
'-c', '"-s dbg.os_main"',
'-e', 'scr.color=3',
'-e', 'scr.scrollbar=0',
'-e', 'scr.responsive=true',
'-e', 'scr.interactive=true',
'-e', 'scr.utf8=true',
'-e', 'scr.utf8.curvy=true',
'-e', 'asm.syntax=intel',
'-e', 'asm.lines=false',
'-e', 'asm.xrefs=true',
'-e', 'asm.flags=true',
'-e', 'asm.comments=true',
'-e', 'asm.functions=true',
'-e', 'asm.var=true',
'-e', 'asm.cmt.right=true',
'-e', 'asm.dwarf=true',
'-e', 'asm.pseudo=false',
'-e', 'asm.describe=false',
'-e', 'bin.relocs.apply=true',
"$local_archive_dir/$selected_experiment/system.elf",
);
},
'18. Open Database in ResultBrowser (Mars)' => sub {
my @db_names = Mars::db_list();
my @selected_dbs =
TUI::select_from_list( "Select Database for ResultBrowser",
0, @db_names );
die "No database selected" unless @selected_dbs;
my $selected_db = $selected_dbs[0];
Util::rewrite_file( $local_db_conf, "database=",
"database=$selected_db\n" );
system( $resultbrowser, '-c', $local_db_conf, '--host=0.0.0.0',
"--port=$resultbrowser_port" );
},
'19. Open Database in LazySQL (Mars)' => sub {
my $experiment =
Util::select_experiment(0) =~ s/T(\d\d)-(\d\d)-(\d\d)/T$1:$2:$3/r;
my $ssh = Mars::ssh_connect();
my $db_password = Mars::read_db_password_file();
system( 'lazysql', '-read-only',
"mariadb://$db_user:$db_password\@$db_host:$db_port/${db_user}_$experiment"
);
},
'20. Open TablePlus (Mars)' => sub {
system('tableplus');
},
'21. Run Build in GDB' => sub {
my @builds = grep { /linux/ } Util::find_subdirs($local_builds_dir);
my @selected_builds =
TUI::select_from_list( "Select Build to Run in GDB", 0, @builds );
die "No experiment selected" unless @selected_builds;
die "No build selected" unless @selected_builds;
my $selected_build = $selected_builds[0];
my $build_dir = "$local_builds_dir/$selected_build";
@ -406,6 +422,39 @@ my %handlers = (
);
},
'30. Plot Results' => sub {
# Generate R ggplot2 charts
my @selected_experiments = Util::select_experiment(1);
my @charts = map { s/\.r//r } Util::find_files($local_charts_dir);
my @selected_charts =
TUI::select_from_list( "Select Plots to Generate", 1, @charts );
die "No plot selected" unless @selected_charts;
my @single_charts = grep { /single/ } @selected_charts;
foreach my $experiment (@selected_experiments) {
foreach my $chart (@single_charts) {
say " - Generating plot $chart for $experiment...";
system(
'Rscript',
"$local_charts_dir/$chart.r",
"$local_archive_dir/$experiment"
);
}
}
my @combined_charts = grep { /combined/ } @selected_charts;
my $print_experiments = join " ", @selected_experiments;
my @path_experiments =
map { "$local_archive_dir/$_" } @selected_experiments;
foreach my $chart (@combined_charts) {
say " - Generating plot $chart for ($print_experiments)...";
system( 'Rscript', "$local_charts_dir/$chart.r",
@path_experiments );
}
},
'95. Delete Builds' => sub {
# Delete old build files
@ -446,11 +495,8 @@ my %handlers = (
'98. Delete Archived Experiments' => sub {
# Delete archived experiments
my @experiments = Util::find_subdirs($local_archive_dir);
my @selected_experiments =
TUI::select_from_list( "Select Archived Experiments to Delete",
1, @experiments );
die "No experiment selected" unless @selected_experiments;
my @selected_experiments = Util::select_experiment(1);
system( 'rm', '-rf', "$local_archive_dir/$_" )
for @selected_experiments;
},

View File

@ -166,7 +166,7 @@ build module="__help" target="fail" mode="aot":
just clean {{ module }}
just create-build-dir {{ module }}
just copy-c-module {{ module }}
just copy-auxiliary {{ module }}
if [ "{{ mode }}" = "aot" ]; then
just build-wasm-module {{ module }}

View File

@ -35,108 +35,101 @@ my $result_browser = "$fail_bin/resultbrowser.py";
sub trace {
my ($experiment) = @_;
Util::notify("Tracing $experiment...");
# Util::notify("Tracing $experiment...");
system(
join " ",
(
"$bochs_runner",
"-V $fail_share/vgabios.bin",
"-b $fail_share/BIOS-bochs-latest",
"-1",
"-f $fail_trace",
"-e $remote_builds_dir/$experiment/system.elf",
"-i $remote_builds_dir/$experiment/system.iso",
"--",
"-Wf,--start-symbol=fail_start_trace",
"-Wf,--save-symbol=fail_start_trace",
"-Wf,--end-symbol=fail_stop_trace",
"-Wf,--state-file=$remote_builds_dir/$experiment/state",
"-Wf,--trace-file=$remote_builds_dir/$experiment/trace.pb",
"-Wf,--elf-file=$remote_builds_dir/$experiment/system.elf"
)
my $trace_command = join " ", (
"$bochs_runner",
"-V $fail_share/vgabios.bin",
"-b $fail_share/BIOS-bochs-latest",
"-1",
"-f $fail_trace",
"-e $remote_builds_dir/$experiment/system.elf",
"-i $remote_builds_dir/$experiment/system.iso",
"--",
"-Wf,--start-symbol=fail_start_trace",
"-Wf,--save-symbol=fail_start_trace",
"-Wf,--end-symbol=fail_stop_trace",
"-Wf,--state-file=$remote_builds_dir/$experiment/state",
"-Wf,--trace-file=$remote_builds_dir/$experiment/trace.pb",
"-Wf,--elf-file=$remote_builds_dir/$experiment/system.elf",
# "-Wf,--full-trace",
# "-Wf,--check-bounds",
">$remote_builds_dir/$experiment/1_trace.log"
);
say "Trace command: $trace_command";
system($trace_command);
}
sub import_trace {
my ($experiment) = @_;
Util::notify("Importing $experiment trace...");
# Benchmark: mem
system(
join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i MemoryImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b mem"
)
);
# Benchmark: regs
system(
join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i RegisterImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b regs",
"--flags" # Inject flags register
)
);
# Util::notify("Importing $experiment trace...");
# Benchmark: ip
system(
join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i RegisterImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b ip",
"--no-gp", # Don't inject general purpose registers
"--ip" # Inject instruction pointer
)
my $import_ip_command = join " ", (
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i RegisterImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b ip",
"--no-gp", # Don't inject general purpose registers
"--ip", # Inject instruction pointer
">$remote_builds_dir/$experiment/2_import_ip.log"
);
say "Import IP command: $import_ip_command";
system($import_ip_command);
# Benchmark: mem
my $import_mem_command = join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i MemoryImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b mem",
">$remote_builds_dir/$experiment/2_import_mem.log"
);
say "Import MEM command: $import_mem_command";
system($import_mem_command);
# Benchmark: regs
my $import_regs_command = join " ", (
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i RegisterImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"-b regs",
"--flags", # Inject flags register
">$remote_builds_dir/$experiment/2_import_regs.log"
);
say "Import REGS command: $import_regs_command";
system($import_regs_command);
# Import fulltrace for VisualFAIL
system(
join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i FullTraceImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
)
);
# system(
# join " ",
# (
# "$fail_import",
# "--database-option-file $remote_db_conf",
# "-t $remote_builds_dir/$experiment/trace.pb",
# "-i FullTraceImporter",
# "-e $remote_builds_dir/$experiment/system.elf",
# "-v $experiment",
# )
# );
# Import objdump disassembly + source files
system(
join " ",
(
"$fail_import",
"--database-option-file $remote_db_conf",
"-t $remote_builds_dir/$experiment/trace.pb",
"-i ElfImporter",
"-e $remote_builds_dir/$experiment/system.elf",
"-v $experiment",
"--objdump objdump",
"--sources",
)
);
# I think those are redundant with the import above?
# system(
# join " ",
# (
@ -144,10 +137,12 @@ sub import_trace {
# "--database-option-file $remote_db_conf",
# "-t $remote_builds_dir/$experiment/trace.pb",
# "-i ElfImporter",
# "--objdump objdump",
# "-e $remote_builds_dir/$experiment/system.elf",
# "-v $experiment",
# "-b ip"
# "-b ip",
# "--objdump objdump",
#
# # "--sources",
# )
# );
# system(
@ -157,10 +152,12 @@ sub import_trace {
# "--database-option-file $remote_db_conf",
# "-t $remote_builds_dir/$experiment/trace.pb",
# "-i ElfImporter",
# "--objdump objdump",
# "-e $remote_builds_dir/$experiment/system.elf",
# "-v $experiment",
# "-b mem"
# "-b mem",
# "--objdump objdump",
#
# # "--sources",
# )
# );
# system(
@ -170,22 +167,24 @@ sub import_trace {
# "--database-option-file $remote_db_conf",
# "-t $remote_builds_dir/$experiment/trace.pb",
# "-i ElfImporter",
# "--objdump objdump",
# "-e $remote_builds_dir/$experiment/system.elf",
# "-v $experiment",
# "-b regs"
# "-b regs",
# "--objdump objdump",
#
# # "--sources",
# )
# );
system(
join " ",
(
"$fail_prune",
"--database-option-file $remote_db_conf",
"-v $experiment",
"-b %%", "--overwrite"
)
);
my $prune_command = join " ",
(
"$fail_prune", "--database-option-file $remote_db_conf",
"-v $experiment", "-b %%",
"--overwrite", ">$remote_builds_dir/$experiment/2_prune.log"
);
say "Prune command: $prune_command";
system($prune_command);
}
sub inject {
@ -195,6 +194,43 @@ sub inject {
Util::notify("Injecting $experiment using $count cores...");
my $server_command = join " ", (
"$fail_server",
# "--port $fail_server_port",
"--database-option-file $remote_db_conf", "-v $experiment",
"-b %", "--inject-single-bit",
"--inject-registers",
">$remote_builds_dir/$experiment/3_server.log"
);
say "Server command: $server_command";
my $client_command = join " ", (
"nice $bochs_runner",
"-V $fail_share/vgabios.bin",
"-b $fail_share/BIOS-bochs-latest",
"-f $fail_inject",
"-e $remote_builds_dir/$experiment/system.elf",
"-i $remote_builds_dir/$experiment/system.iso",
"-j $count",
"--",
# "-Wf,--server-port=$fail_server_port",
"-Wf,--state-dir=$remote_builds_dir/$experiment/state",
"-Wf,--trap",
"-Wf,--catch-outerspace",
"-Wf,--catch-write-textsegment",
"-Wf,--timeout=500000",
"-Wf,--ok-marker=fail_marker_positive",
"-Wf,--fail-marker=fail_marker_negative",
"-Wf,--detected-marker=fail_marker_detected",
">/dev/null"
# ">$remote_builds_dir/$experiment/4_client.log"
);
say "Client command: $client_command";
say "Forking...";
my $pid = fork();
die "fork failed: $!" unless defined $pid;
@ -202,52 +238,16 @@ sub inject {
if ( $pid == 0 ) {
# child -> server
say "Running server in child process...";
exec(
join " ",
(
"$fail_server",
"--port $fail_server_port",
"--database-option-file $remote_db_conf",
"-v $experiment",
"-b %",
"--inject-single-bit",
"--inject-registers"
)
) == 0 or die "exec server failed: $!";
exec($server_command) == 0 or die "exec server failed: $!";
}
# parent -> client
say "Waiting for server...";
sleep(10);
say "Running client with $count cores in parent process";
system(
join " ",
(
"nice $bochs_runner",
"-V $fail_share/vgabios.bin",
"-b $fail_share/BIOS-bochs-latest",
"-f $fail_inject",
"-e $remote_builds_dir/$experiment/system.elf",
"-i $remote_builds_dir/$experiment/system.iso",
"-j $count",
"--",
"-Wf,--server-port=$fail_server_port",
"-Wf,--state-dir=$remote_builds_dir/$experiment/state",
"-Wf,--trap",
"-Wf,--catch-outerspace",
"-Wf,--catch-write-textsegment",
"-Wf,--timeout=500000",
"-Wf,--ok-marker=fail_marker_positive",
"-Wf,--fail-marker=fail_marker_negative",
"-Wf,--detected-marker=fail_marker_detected",
">/dev/null"
)
) == 0 or die "client failed: $?";
system($client_command) == 0 or die "client failed: $?";
say "Killing server with pid $pid...";
kill 'TERM', $pid;
@ -281,3 +281,5 @@ for my $experiment (@experiments) {
inject($experiment);
results($experiment);
}
Util::notify("Finished all experiments");

View File

@ -15,7 +15,6 @@ WASI_CFLAGS := "\
-Wl,--initial-memory=65536 \
-Wl,--export=__heap_base \
-Wl,--export=__data_end \
-Wl,--allow-undefined \
"
CROSS_CFLAGS_NOWASM := "\
-O0 \
@ -53,8 +52,7 @@ CROSS_WAMRCFLAGS := "\
--target=i386 \
--cpu=generic \
--opt-level=0 \
--enable-indirect-mode \
--disable-llvm-intrinsics \
--xip \
"
LINUX_WAMRCFLAGS := "\
--target=i386 \
@ -131,7 +129,14 @@ build-c-module module target="fail":
fi
[private]
copy-c-module module:
copy-auxiliary module:
cp flake.nix {{ BUILD_DIR }}-{{ module }}/flake.nix
cp scripts/runner.pl {{ BUILD_DIR }}-{{ module }}/runner.pl
cp scripts/wasm.just {{ BUILD_DIR }}-{{ module }}/wasm.just
cp targets/lib.h {{ BUILD_DIR }}-{{ module }}/lib.h
cp targets/linker.ld {{ BUILD_DIR }}-{{ module }}/linker.ld
cp targets/startup.s {{ BUILD_DIR }}-{{ module }}/startup.s
cp targets/syscalls.c {{ BUILD_DIR }}-{{ module }}/syscalls.c
cp targets/wasm-module/{{ module }}.cpp {{ BUILD_DIR }}-{{ module }}/wasm-module.cpp
# =================================================================================================================== #

View File

@ -60,12 +60,17 @@ extern "C" {
#endif
// Those functions are defined in the host program
void NOINLINE fail_start_trace(void); // Mark start of injection
void NOINLINE fail_stop_trace(void); // Mark end of injection
void NOINLINE fail_marker_positive(void); // Everything ok
void NOINLINE fail_marker_detected(void); // Everything ok
void NOINLINE fail_marker_negative(void); // Invalid code
void NOINLINE print(const char *msg);
void NOINLINE IMPORT("fail_start_trace")
fail_start_trace(void); // Mark start of injection
void NOINLINE IMPORT("fail_stop_trace")
fail_stop_trace(void); // Mark end of injection
void NOINLINE IMPORT("fail_marker_positive")
fail_marker_positive(void); // Everything ok
void NOINLINE IMPORT("fail_marker_detected")
fail_marker_detected(void); // Everything ok
void NOINLINE IMPORT("fail_marker_negative")
fail_marker_negative(void); // Invalid code
void NOINLINE IMPORT("print") print(const char *msg);
#ifdef __cplusplus
}

View File

@ -0,0 +1,63 @@
#include "../lib.h"
#define REPLICA_COUNT 1
#define MAT_SIZE 3
static uint32_t X[MAT_SIZE * MAT_SIZE] = {
0, 1, 2, //
3, 4, 5, //
6, 7, 8 //
};
static uint32_t Y[MAT_SIZE * MAT_SIZE] = {
8, 7, 6, //
5, 4, 3, //
2, 1, 0 //
};
static uint32_t Calculated[MAT_SIZE * MAT_SIZE] = {0};
static INLINE uint32_t *idx(uint32_t *matrix, uint8_t x, uint8_t y) {
return &matrix[y * MAT_SIZE + x];
}
template <const unsigned int N> static INLINE void matrix(void) {
for (uint8_t y = 0; y < MAT_SIZE; ++y) {
for (uint8_t x = 0; x < MAT_SIZE; ++x) {
for (uint8_t k = 0; k < MAT_SIZE; ++k) {
*idx(Calculated, x, y) += (*idx(X, k, y)) * (*idx(Y, x, k));
}
}
}
}
static int cmp(uint32_t *A, uint32_t *B) {
for (uint8_t i = 0; i < MAT_SIZE * MAT_SIZE; ++i) {
if (A[i] != B[i]) {
return 0;
}
}
return 1;
}
extern "C" EXPORT("wasm_module") int wasm_module(void) {
fail_start_trace();
matrix<0>();
fail_stop_trace();
uint32_t Expected[MAT_SIZE * MAT_SIZE] = {
9, 6, 3, //
54, 42, 30, //
99, 78, 57 //
};
if (cmp(Calculated, Expected)) {
HOST_PRINT("result correct.\n");
fail_marker_positive();
return 0;
} else {
HOST_PRINT("result incorrect.\n");
fail_marker_negative();
return 1;
}
}