467 lines
16 KiB
Perl
467 lines
16 KiB
Perl
#!/usr/bin/env perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
use diagnostics;
|
|
|
|
use FindBin;
|
|
use lib $FindBin::Bin;
|
|
|
|
use Util;
|
|
use Mars;
|
|
use TUI;
|
|
use Text::CSV_XS;
|
|
|
|
use feature 'say';
|
|
|
|
# TODO: Less navigation if the object is selected first, then the action
|
|
# - Differentiate between local/remote object
|
|
# - List all types in the same list?
|
|
# - Select actions afterwards and apply fitting ones all in one go
|
|
# - Hide unfeasible actions
|
|
# TODO: Much can be extracted into utility functions
|
|
|
|
my $local_obsidian = '/home/christoph/Notes/Obsidian/Chriphost';
|
|
my $local_obsidian_attach = "$local_obsidian/attach";
|
|
|
|
my $local_wamr = '/home/christoph/Notes/TU/MastersThesis/05 WAMR';
|
|
my $local_newlib = '/home/christoph/Notes/TU/MastersThesis/07 NewLib';
|
|
my $local_root = '/home/christoph/Notes/TU/MastersThesis/FailNix';
|
|
my $local_scripts_dir = "$local_root/scripts";
|
|
my $local_builds_dir = "$local_root/builds";
|
|
my $local_archive_dir = "$local_root/injections";
|
|
my $local_charts_dir = "$local_root/charts";
|
|
my $local_ghidra_projects = "$local_root/ghidra/projects";
|
|
my $local_ghidra_scripts = "$local_root/ghidra/scripts";
|
|
my $local_db_conf = "$local_root/db.conf";
|
|
|
|
my $resultbrowser_port = '5000';
|
|
my $resultbrowser = 'resultbrowser.py';
|
|
|
|
my $remote_root = '/home/lab/smchurla/Documents/failnix';
|
|
my $remote_builds_dir = "$remote_root/builds";
|
|
|
|
my %handlers = (
|
|
'01. Build Experiments' => sub { do "$local_scripts_dir/build.pl"; },
|
|
|
|
'02. Deploy Experiments (Mars)' =>
|
|
sub { do "$local_scripts_dir/deploy.pl"; },
|
|
|
|
'03. Archive Experiments (Downloads from Mars)' => sub {
|
|
|
|
# Download ran experiments from mars
|
|
my @dirs = Mars::find_remote_subdirs($remote_builds_dir);
|
|
|
|
my @existing = Util::find_subdirs($local_archive_dir);
|
|
my @new_dirs;
|
|
foreach (@dirs) {
|
|
my $dir = $_ =~ s/:/-/gr;
|
|
unless ( grep { /$dir/ } @existing ) {
|
|
push @new_dirs, $_;
|
|
}
|
|
}
|
|
|
|
my @selected_dirs =
|
|
TUI::select_from_list( "Select Experiments to Download from Mars",
|
|
1, @new_dirs );
|
|
die "No experiment selected" unless @selected_dirs;
|
|
Mars::download_dir( "$remote_builds_dir/$_",
|
|
"$local_archive_dir/" . $_ =~ s/:/-/gr )
|
|
for @selected_dirs;
|
|
},
|
|
|
|
'04. Query Databases (Mars)' => sub {
|
|
|
|
# Select databases
|
|
my @db_names = Mars::db_list();
|
|
my @selected_dbs =
|
|
TUI::select_from_list( "Select Databases to Query", 1, @db_names );
|
|
die "No database selected" unless @selected_dbs;
|
|
|
|
# Select queries
|
|
my @queries =
|
|
map { s/\.pm//r } Util::find_files("$local_root/scripts/Queries");
|
|
my @selected_queries =
|
|
TUI::select_from_list( "Select Queries to Run", 1, @queries );
|
|
die "No query selected" unless @selected_queries;
|
|
|
|
# Run queries on databases
|
|
foreach my $db (@selected_dbs) {
|
|
foreach my $query (@selected_queries) {
|
|
Util::rewrite_file( $local_db_conf, "database=",
|
|
"database=$db\n" );
|
|
|
|
say "Running $query on $db...";
|
|
Util::execute_query( $db =~ s/smchurla_//r,
|
|
$query, $local_db_conf, $local_archive_dir, 0 );
|
|
}
|
|
}
|
|
},
|
|
|
|
'05. Import Experiments Into Ghidra' => sub {
|
|
|
|
my @existing = Util::find_files($local_ghidra_projects);
|
|
|
|
my $is_old = sub {
|
|
my ($name) = @_;
|
|
$name =~ s/:/-/g;
|
|
|
|
return grep { /^$name.gpr$/ } @existing;
|
|
};
|
|
|
|
# Import archived experiments into ghidra
|
|
my @dirs =
|
|
grep { !$is_old->($_) } Util::find_subdirs($local_archive_dir);
|
|
|
|
my @selected_dirs =
|
|
TUI::select_from_list( "Select Experiments to Import into Ghidra",
|
|
1, @dirs );
|
|
foreach (@selected_dirs) {
|
|
say "Creating Ghidra project for $_...";
|
|
system(
|
|
'ghidra-analyzeHeadless',
|
|
|
|
$local_ghidra_projects, $_ =~ s/:/-/gr,
|
|
'-import', "$local_archive_dir/$_/system.elf",
|
|
'-scriptPath', $local_ghidra_scripts,
|
|
'-postScript', 'DWARFLineInfoSourceMapScript',
|
|
'-postScript', 'DWARFLineInfoCommentScript',
|
|
'-postScript', 'ImportMarkersAsBookmarks',
|
|
"$local_archive_dir/$_/faults.csv"
|
|
);
|
|
}
|
|
},
|
|
|
|
'06. Import Experiments Into Obsidian' => sub {
|
|
|
|
my @experiments = Util::find_subdirs($local_archive_dir);
|
|
|
|
# Filter experiments that already have notes
|
|
my @new_experiments;
|
|
my @existing_notes = split "\n", qx{obsidian files};
|
|
foreach my $experiment (@experiments) {
|
|
push @new_experiments, $experiment
|
|
unless ( grep { /zettel\/$experiment/ } @existing_notes );
|
|
}
|
|
|
|
my @selected_experiments =
|
|
TUI::select_from_list( "Select Experiments to Import into Obsidian",
|
|
1, @new_experiments );
|
|
die "No experiment selected" unless @selected_experiments;
|
|
|
|
foreach my $experiment (@selected_experiments) {
|
|
|
|
# Create note
|
|
system(
|
|
'obsidian', 'create',
|
|
"name=$experiment", 'path=zettel',
|
|
'template=FailExperiment', 'open',
|
|
'newtab'
|
|
);
|
|
|
|
# Insert results
|
|
if ( -f "$local_archive_dir/$experiment/results.txt" ) {
|
|
open( my $fhandle, '<',
|
|
"$local_archive_dir/$experiment/results.txt" )
|
|
or return;
|
|
my $results = join "", <$fhandle>;
|
|
close($fhandle);
|
|
|
|
say "$local_archive_dir/$experiment/results.txt does not exist";
|
|
|
|
system( 'obsidian', 'append', "file=zettel/$experiment",
|
|
"content=## Results\n\n```\n$results```\n" );
|
|
}
|
|
|
|
# Insert charts
|
|
system(
|
|
'obsidian', 'append',
|
|
"file=zettel/$experiment", "content=## Charts\n\n"
|
|
);
|
|
|
|
my $attach_image = sub {
|
|
my ($name) = @_;
|
|
|
|
system( 'obsidian', 'append', "file=zettel/$experiment",
|
|
"content=\n"
|
|
);
|
|
};
|
|
$attach_image->("single_result");
|
|
$attach_image->("scatter");
|
|
}
|
|
},
|
|
|
|
'10. Open Experiment In Explorer' =>
|
|
sub { do "$local_scripts_dir/explore.pl" },
|
|
|
|
'11. 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",
|
|
)
|
|
);
|
|
},
|
|
|
|
'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;
|
|
|
|
# Read results
|
|
my %all_results;
|
|
foreach my $experiment (@selected_experiments) {
|
|
|
|
# Schema: benchmark, resulttype, faults
|
|
my $data = Text::CSV_XS::csv(
|
|
in => "$local_archive_dir/$experiment/resultsdata.csv",
|
|
headers => 'auto'
|
|
);
|
|
|
|
foreach my $row (@$data) {
|
|
$all_results{$experiment}{ $row->{benchmark} }
|
|
{ $row->{resulttype} } = $row->{faults};
|
|
}
|
|
}
|
|
|
|
my @benchs = ( 'ip', 'mem', 'regs' );
|
|
my @markers = (
|
|
'OK_MARKER', 'FAIL_MARKER',
|
|
'DETECTED_MARKER', 'TIMEOUT',
|
|
'TRAP', 'WRITE_TEXTSEGMENT',
|
|
'ACCESS_OUTERSPACE'
|
|
);
|
|
|
|
my $heading = sprintf( "%5s %20s ", "BENCH", "TYPE" );
|
|
foreach my $experiment (@selected_experiments) {
|
|
$heading .= sprintf( "%50s ", $experiment );
|
|
}
|
|
|
|
my @entries = ( $heading, "" );
|
|
foreach my $benchmark (@benchs) {
|
|
foreach my $marker (@markers) {
|
|
my $entry = sprintf( "%5s %20s ", $benchmark, $marker );
|
|
|
|
foreach my $experiment (@selected_experiments) {
|
|
if ( exists $all_results{$experiment}{$benchmark}{$marker} )
|
|
{
|
|
$entry .= sprintf(
|
|
"%50s ",
|
|
Util::format_number_sep(
|
|
$all_results{$experiment}{$benchmark}{$marker}
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
$entry .= sprintf( "%50s ", "" );
|
|
}
|
|
}
|
|
|
|
push @entries, $entry;
|
|
}
|
|
push @entries, "";
|
|
}
|
|
|
|
TUI::select_from_list(
|
|
"Comparing " . scalar(@selected_experiments) . " Experiments",
|
|
0, @entries );
|
|
},
|
|
|
|
'15. Plot Results' => sub {
|
|
|
|
# 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 );
|
|
}
|
|
},
|
|
|
|
'16. 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;
|
|
my $selected_build = $selected_builds[0];
|
|
|
|
my $build_dir = "$local_builds_dir/$selected_build";
|
|
my $build_name = $selected_build =~ s/.*-.*-.*:.*:.*?_(.*)-.*-.*/$1/r;
|
|
my $module_source = "$local_root/build-$build_name";
|
|
|
|
say "$build_name";
|
|
|
|
system(
|
|
'gdb',
|
|
'--tui',
|
|
'-q',
|
|
"$build_dir/system.elf",
|
|
'-ex',
|
|
"set substitute-path '$module_source' '$build_dir'",
|
|
'-ex',
|
|
"set substitute-path '/build/source/core' '$local_wamr/core'",
|
|
'-ex',
|
|
'break main',
|
|
'-ex',
|
|
'break fail_start_trace',
|
|
'-ex',
|
|
'break fail_stop_trace',
|
|
'-ex',
|
|
'break fail_marker_positive',
|
|
'-ex',
|
|
'break fail_marker_detected',
|
|
'-ex',
|
|
'break fail_marker_negative',
|
|
);
|
|
},
|
|
|
|
'95. Delete Builds' => sub {
|
|
|
|
# Delete old build files
|
|
my @builds = Util::find_subdirs($local_builds_dir);
|
|
my @selected_builds =
|
|
TUI::select_from_list( "Select Builds to Delete", 1, @builds );
|
|
die "No builds selected" unless @selected_builds;
|
|
system( 'rm', '-rf', "$local_builds_dir/$_" ) for @selected_builds;
|
|
},
|
|
|
|
'96. Delete Builds (Mars)' => sub {
|
|
|
|
# Delete ran experiments from mars
|
|
my @builds = Mars::find_remote_subdirs($remote_builds_dir);
|
|
my @selected_builds =
|
|
TUI::select_from_list( "Select Builds to Delete from Mars",
|
|
1, @builds );
|
|
die "No experiment selected" unless @selected_builds;
|
|
Mars::ssh_system( 'rm', '-rf', "$remote_builds_dir/$_" )
|
|
for @selected_builds;
|
|
},
|
|
|
|
'97. Delete Ghidra Projects' => sub {
|
|
|
|
# Delete ghidra projects
|
|
my @projects =
|
|
map { s/\.gpr//r } Util::find_files($local_ghidra_projects);
|
|
my @selected_projects =
|
|
TUI::select_from_list( "Select Ghidra Projects to Delete",
|
|
1, @projects );
|
|
die "No project selected" unless @selected_projects;
|
|
system( 'rm', '-rf', "$local_ghidra_projects/$_.gpr" )
|
|
for @selected_projects;
|
|
system( 'rm', '-rf', "$local_ghidra_projects/$_.rep" )
|
|
for @selected_projects;
|
|
},
|
|
|
|
'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;
|
|
system( 'rm', '-rf', "$local_archive_dir/$_" )
|
|
for @selected_experiments;
|
|
},
|
|
|
|
'99. Drop Databases (Mars)' => sub {
|
|
|
|
# Drop databases on mars
|
|
my @db_names = Mars::db_list();
|
|
my @selected_dbs =
|
|
TUI::select_from_list( "Select Databases to Drop from Mars",
|
|
1, @db_names );
|
|
die "No database selected" unless @selected_dbs;
|
|
Mars::db_drop($_) for @selected_dbs;
|
|
},
|
|
);
|
|
|
|
while (1) {
|
|
my @submenu =
|
|
TUI::select_from_list( "FailNix Menu", 0, sort keys %handlers );
|
|
die "No action selected" unless @submenu;
|
|
|
|
say @submenu;
|
|
|
|
eval { $handlers{ $submenu[0] }(); }
|
|
}
|
|
|
|
Mars::db_disconnect();
|