S03-H3 CLOSED baseline

This commit is contained in:
2026-08-27 11:12:22 +02:00
commit 4b5fbb6c23
30 changed files with 10130 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# ============================================================
# MARTe Benchmark Suite - Git exclusions
# ============================================================
# Runtime execution outputs.
# Every MARTe benchmark run generates its own directory here.
# These outputs are operational evidence, not source code.
runs/
# Python bytecode/cache
__pycache__/
*.py[cod]
# Editor temporary files
*~
*.swp
*.swo
# Generic temporary files
*.tmp
# Desktop metadata
.DS_Store
Thumbs.db
+738
View File
@@ -0,0 +1,738 @@
# MARTe Benchmark Suite
## Overview
`marte-benchmark-suite` is a validation and benchmarking infrastructure
developed around the MARTe2 real-time framework.
The project is designed to build a sequence of progressively more advanced
test scenarios while preserving known-good baselines from the previous stages.
The suite does not modify MARTe2 itself.
Instead, it provides:
- MARTe configuration files;
- launchers;
- configuration patchers;
- runtime orchestration;
- output parsers;
- structural validators;
- semantic validators;
- execution evidence.
The development philosophy is incremental:
> each new stage introduces one new capability while preserving the validated
> behaviour of the previous stages.
---
# Current project status
Current validated development baseline:
```text
S03-H3
STATUS=CLOSED
BASELINE=KNOWN-GOOD
```
S03-H3 is currently the authoritative baseline on which the next stages of the
benchmark suite are intended to be built.
The next planned stages are:
```text
S04 → Repeatability / Multi-Run
S05 → Deterministic Parametric Generator
S06 → MARTe2 Core Scaling
```
These stages are not part of the current S03-H3 baseline unless explicitly
implemented and accepted later.
---
# Project purpose
The suite is intended to answer increasingly complex questions about a MARTe2
application.
Conceptually:
```text
S00
Can MARTe2 execute the basic reference application?
S01
Can execution timing information be observed?
S02
Can deterministic binary data be recorded?
S03
Can the recorded data be checked semantically?
S03-H1
Can different execution/debug profiles be supported?
S03-H3
Can those profiles be formalized and validated
against an authoritative runtime contract?
S04
Does the same valid workload remain correct
when executed repeatedly?
S05
Can deterministic test workloads be generated
from parameters?
S06
How does MARTe2 Core behave as workload complexity grows?
```
---
# High-level architecture
The benchmark suite is layered around the installed MARTe2 runtime.
```text
+------------------+
| MARTe2 |
| MARTeApp.ex |
+---------+--------+
^
|
common/marte_runner.sh
^
|
launcher/
^
|
scenario / configuration
|
v
MARTe execution
|
v
RUN DIRECTORY
|
+------------+-------------+
| |
v v
runtime logs output files
| |
+------------+-------------+
|
v
parser / validator
|
v
PASS / FAIL result
```
MARTe2 and MARTe2-components remain external dependencies.
The benchmark suite orchestrates and validates them but is intentionally kept
separate from their source trees.
---
# Repository structure
Current source structure:
```text
marte-benchmark-suite/
├── common/
│ ├── environment.sh
│ ├── marte_runner.sh
│ └── paths.sh
├── configurations/
│ └── generated/
│ ├── S00/
│ ├── S01/
│ ├── S02/
│ ├── S03/
│ ├── S03H1/
│ └── S03H3/
├── launcher/
│ ├── marte_benchmark_launcher.sh
│ ├── marte_benchmark_launcher_s03h1.sh
│ ├── marte_benchmark_launcher_s03h3.sh
│ └── marte_benchmark_menu.sh
├── patchers/
│ └── configuration/
├── scenarios/
│ ├── S01_timing_display/
│ ├── S02_binary_recording/
│ ├── S03_semantic_sentinels/
│ ├── S03H1_execution_profiles/
│ └── S03H3_execution_profiles/
└── runs/
```
`runs/` contains execution outputs and is intentionally excluded from normal
Git version control.
The configurations under `configurations/generated/` are NOT generic build
artifacts. They represent configuration baselines produced by controlled
project stages and are therefore versioned.
---
# Common runtime layer
## `common/paths.sh`
Centralizes the paths used by the benchmark infrastructure.
Its purpose is to avoid hard-coding the same MARTe and project locations in
multiple scripts.
---
## `common/environment.sh`
Defines the common execution environment used by the benchmark scripts.
Keeping environment handling in one place improves reproducibility and avoids
different scenarios silently using different runtime assumptions.
---
## `common/marte_runner.sh`
Provides the common MARTe execution mechanism shared by benchmark stages.
Conceptually:
```text
launcher
common runner
MARTeApp.ex
run directory
logs / outputs
```
Higher-level stages should reuse the authoritative runner rather than create
alternative MARTe execution paths unless a future specification explicitly
authorizes such a change.
---
# Launchers
The `launcher/` directory contains the user-facing execution layer.
The project currently includes launchers from different development stages,
including the S03-H1 and S03-H3 generations.
The current S03-H3 architecture supports execution profiles such as:
```text
MINIMAL
TEST
DIAGNOSTIC
```
The profiles allow the same benchmark infrastructure to expose different
levels of runtime/debug functionality without changing the fundamental
correctness contract.
---
# Patchers
Configuration patchers live under:
```text
patchers/configuration/
```
They transform an already-known configuration into the configuration required
by the next development stage.
The dependency chain currently visible in the repository is:
```text
S00
build_s01_from_s00.sh
S01
build_s02_from_s01.sh
S02
build_s03_from_s02.sh
S03
```
Additional controlled transformations support S03-H1 and S03-H3.
This model helps preserve provenance:
```text
new configuration
=
known parent
+
explicit transformation
```
rather than independently rewriting every configuration from scratch.
---
# Validation layer
The suite does not consider a MARTe process exit by itself sufficient proof
that a test succeeded.
Validation is layered.
Depending on the stage, the suite can inspect:
```text
configuration structure
+
runtime execution
+
generated files
+
semantic content
+
expected sentinels
```
and only then derive the corresponding result.
---
# S00 — Core reference
S00 provides the basic MARTe2 reference configuration:
```text
configurations/generated/S00/smoke_s0_core.marte
```
It acts as the root from which later benchmark configurations are progressively
derived.
---
# S01 — Timing
S01 introduces timing-related observation on top of the S00 reference
configuration.
Relevant current files include:
```text
patchers/configuration/build_s01_from_s00.sh
configurations/generated/S01/from_patcher/
scenarios/S01_timing_display/validate.sh
```
S01 established infrastructure that later stages could reuse rather than
reimplement.
---
# S02 — Binary recording
S02 extends the previous stage with binary data recording.
Relevant components include:
```text
patchers/configuration/build_s02_from_s01.sh
configurations/generated/S02/from_patcher/
smoke_s2_binary_recording_from_s01.marte
scenarios/S02_binary_recording/validate.sh
```
Binary recording provides machine-readable data that later stages can parse
and validate.
---
# S03 — Semantic validation
S03 adds semantic verification of the generated data.
Relevant components include:
```text
patchers/configuration/build_s03_from_s02.sh
configurations/generated/S03/from_patcher/
smoke_s3_semantic_sentinels_from_s02.marte
scenarios/S03_semantic_sentinels/
parse_s03_binary.py
validate.sh
```
The key conceptual change is:
```text
"data file exists"
```
is not enough.
The project also checks whether the data means what the test expected it to
mean.
---
# S03-H1 — Execution profiles
S03-H1 introduced explicit execution profiles and supporting validation
infrastructure.
Current repository elements include:
```text
launcher/marte_benchmark_launcher_s03h1.sh
patchers/configuration/
build_s03h1_from_s02.sh
scenarios/S03H1_execution_profiles/
export_s03_csv.py
validate.sh
verify_s03h1_config.py
```
The historical S03-H1 configurations currently retained are:
```text
TEST
DIAGNOSTIC
```
They remain important because S03-H3 intentionally reuses validated historical
configurations where appropriate instead of duplicating them unnecessarily.
---
# S03-H3 — Current known-good baseline
S03-H3 is the current closed benchmark stage.
Its role is to formalize the execution-profile architecture and protect the
runtime contract used by future development.
Relevant repository elements include:
```text
launcher/
marte_benchmark_launcher_s03h3.sh
marte_benchmark_menu.sh
patchers/configuration/
build_s03h3_minimal_from_s03h1_test.sh
configurations/generated/S03H3/from_patcher/
smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte
scenarios/S03H3_execution_profiles/
validate.sh
verify_s03h3_minimal_structure.py
```
S03-H3 defines three go-forward execution profiles:
```text
MINIMAL
TEST
DIAGNOSTIC
```
The MINIMAL configuration is derived from the validated S03-H1 TEST
configuration by explicitly removing unnecessary display functionality while
preserving the required execution behaviour.
TEST and DIAGNOSTIC reuse their validated historical configurations.
This avoids unnecessary duplication and reduces the number of independent
configuration variants that must be trusted.
---
# Why S03-H3 matters
Before advancing toward larger benchmark campaigns, the project needs one
well-understood baseline.
S03-H3 provides that reference point.
Conceptually:
```text
KNOWN INPUT
KNOWN CONFIGURATION
KNOWN LAUNCHER
KNOWN RUNTIME DEPENDENCIES
KNOWN VALIDATION
KNOWN-GOOD RESULT
```
Future stages should extend this baseline rather than silently redefine it.
---
# Runtime dependencies
The benchmark suite depends on an external MARTe installation.
The current runtime environment includes:
```text
MARTe2
MARTe2-components
```
Important runtime objects include MARTe executables/libraries and component
libraries required by the active configurations.
These external projects are not copied into this repository.
This repository therefore acts as:
```text
benchmark source + configuration + validation infrastructure
```
and not as a complete binary backup of MARTe.
A separate verified restore mechanism is required for complete disaster
recovery of the known-good runtime environment.
---
# Run directories
Runtime execution artifacts are written under:
```text
runs/
```
A run directory may contain logs, generated data and validation evidence.
These files can be large and are produced repeatedly.
For this reason:
```text
runs/
```
is intentionally excluded through `.gitignore`.
Git is used for source/configuration history.
Runtime evidence and full environment recovery are handled separately.
---
# Git versus full restore backup
The Git repository and the full restore point serve different purposes.
```text
Git
├── scripts
├── configurations
├── validators
├── parsers
├── documentation
└── development history
```
while a complete restore point additionally needs the external runtime
environment required to reproduce the validated MARTe execution.
Therefore:
```text
Git backup ≠ complete runtime restore point
```
Both mechanisms are complementary.
---
# Development governance
The project follows a controlled development process.
A typical new stage follows approximately:
```text
DESIGN / SPECIFICATION
independent technical review
technical arbitration
human freeze
implementation
tests
independent implementation review
technical arbitration
human acceptance
new known-good baseline
```
A development-stage implementation is therefore not automatically considered
part of the authoritative baseline simply because code exists.
---
# Planned roadmap
## S04 — Repeatability / Multi-Run
Main question:
> Does the same already-valid S03-H3 workload remain correct when executed
> repeatedly?
Conceptually:
```text
S03-H3 run #1 → result
S03-H3 run #2 → result
S03-H3 run #3 → result
...
S03-H3 run #N → result
campaign-level repeatability result
```
S04 is primarily a correctness/repeatability stage.
It is not intended to turn repeatability percentages or CycleTime observations
into performance claims.
---
## S05 — Deterministic Parametric Generator
Main question:
> Can test workloads be generated automatically from controlled parameters
> while remaining reproducible?
Conceptually:
```text
parameters
+
seed
generator
deterministic MARTe scenario
```
The same inputs should regenerate the same intended scenario.
---
## S06 — MARTe2 Core Scaling
Main question:
> How does the MARTe2 Core workload behave as controlled complexity increases?
S06 is expected to reuse the capabilities established by the preceding stages:
```text
S03-H3 → correctness baseline
S04 → repeatable execution
S05 → deterministic workload generation
S06 → controlled scaling study
```
---
# Current boundary
At the time of this README baseline:
```text
S03-H3 = CLOSED / KNOWN-GOOD
S04 = next development target
S05 = planned
S06 = planned
```
The existence of this roadmap must not be interpreted as evidence that S04,
S05 or S06 have already been implemented or validated.
---
# Repository philosophy
The repository should remain:
```text
small
reviewable
deterministic
traceable
reproducible
```
Generated runtime outputs should not be mixed with source history.
Known-good configurations, however, may intentionally be versioned even when
they were produced by controlled patchers, because they are part of the
validated benchmark baseline.
---
# License and distribution
The licensing and distribution policy for this benchmark suite must be defined
by the project owners before public redistribution.
MARTe2 and MARTe2-components retain their respective upstream licensing and
ownership terms.
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Determina la directory common indipendentemente dalla directory corrente.
MARTE_COMMON_DIR="$(
cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 &&
pwd
)"
# Carica i percorsi canonici della benchmark suite.
source "$MARTE_COMMON_DIR/paths.sh"
# Directory contenenti le librerie richieste dagli scenari S00, S01 e S02.
MARTE2_CORE_LIB_DIR="$MARTE2_ROOT/Build/$MARTE_TARGET/Core"
LINUX_TIMER_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/DataSources/LinuxTimer"
LOGGER_DATA_SOURCE_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/DataSources/LoggerDataSource"
FILE_DATA_SOURCE_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/DataSources/FileDataSource"
IOGAM_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/GAMs/IOGAM"
# Librerie che devono essere presenti.
MARTE2_CORE_LIB="$MARTE2_CORE_LIB_DIR/MARTe2.so"
LINUX_TIMER_LIB="$LINUX_TIMER_LIB_DIR/LinuxTimer.so"
LOGGER_DATA_SOURCE_LIB="$LOGGER_DATA_SOURCE_LIB_DIR/LoggerDataSource.so"
FILE_DATA_SOURCE_LIB="$FILE_DATA_SOURCE_LIB_DIR/FileDataSource.so"
IOGAM_LIB="$IOGAM_LIB_DIR/IOGAM.so"
# Verifica delle directory runtime.
for marte_runtime_dir in \
"$MARTE2_CORE_LIB_DIR" \
"$LINUX_TIMER_LIB_DIR" \
"$LOGGER_DATA_SOURCE_LIB_DIR" \
"$FILE_DATA_SOURCE_LIB_DIR" \
"$IOGAM_LIB_DIR"
do
if [ ! -d "$marte_runtime_dir" ]; then
printf "DIRECTORY RUNTIME ASSENTE: %s\n" "$marte_runtime_dir" >&2
return 2 2>/dev/null || exit 2
fi
done
# Verifica delle librerie runtime.
for marte_runtime_lib in \
"$MARTE2_CORE_LIB" \
"$LINUX_TIMER_LIB" \
"$LOGGER_DATA_SOURCE_LIB" \
"$FILE_DATA_SOURCE_LIB" \
"$IOGAM_LIB"
do
if [ ! -f "$marte_runtime_lib" ]; then
printf "LIBRERIA RUNTIME ASSENTE: %s\n" "$marte_runtime_lib" >&2
return 3 2>/dev/null || exit 3
fi
done
# Inserisce una directory allinizio di LD_LIBRARY_PATH solo se non è già presente.
marte_prepend_library_path() {
marte_directory="$1"
case ":${LD_LIBRARY_PATH-}:" in
*":${marte_directory}:"*)
;;
*)
if [ -n "${LD_LIBRARY_PATH-}" ]; then
LD_LIBRARY_PATH="${marte_directory}:${LD_LIBRARY_PATH}"
else
LD_LIBRARY_PATH="${marte_directory}"
fi
;;
esac
}
# Ordine inverso perché ogni voce viene inserita in testa.
marte_prepend_library_path "$FILE_DATA_SOURCE_LIB_DIR"
marte_prepend_library_path "$IOGAM_LIB_DIR"
marte_prepend_library_path "$LOGGER_DATA_SOURCE_LIB_DIR"
marte_prepend_library_path "$LINUX_TIMER_LIB_DIR"
marte_prepend_library_path "$MARTE2_CORE_LIB_DIR"
export MARTE2_CORE_LIB_DIR
export LINUX_TIMER_LIB_DIR
export LOGGER_DATA_SOURCE_LIB_DIR
export FILE_DATA_SOURCE_LIB_DIR
export IOGAM_LIB_DIR
export LD_LIBRARY_PATH
unset marte_runtime_dir
unset marte_runtime_lib
unset marte_directory
unset -f marte_prepend_library_path
+196
View File
@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -u
if [ "$#" -ne 4 ]; then
printf "Uso: %s <scenario-id> <configurazione> <stato> <durata-secondi>\n" \
"$0" >&2
exit 2
fi
SCENARIO_ID="$1"
CONFIG_FILE="$2"
REQUESTED_STATE="$3"
DURATION_SECONDS="$4"
COMMON_DIR="$(
cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 &&
pwd
)"
ENV_FILE="$COMMON_DIR/environment.sh"
PATHS_FILE="$COMMON_DIR/paths.sh"
if [ ! -f "$ENV_FILE" ]; then
printf "FILE AMBIENTE ASSENTE: %s\n" "$ENV_FILE" >&2
exit 3
fi
source "$ENV_FILE"
if [ ! -x "$MARTE_APP" ]; then
printf "MARTeApp ASSENTE O NON ESEGUIBILE: %s\n" "$MARTE_APP" >&2
exit 4
fi
if [ ! -f "$CONFIG_FILE" ]; then
printf "CONFIGURAZIONE ASSENTE: %s\n" "$CONFIG_FILE" >&2
exit 5
fi
case "$SCENARIO_ID" in
*[!A-Za-z0-9_-]*|"")
printf "IDENTIFICATORE SCENARIO NON VALIDO: %s\n" "$SCENARIO_ID" >&2
exit 6
;;
esac
case "$REQUESTED_STATE" in
*[!A-Za-z0-9_-]*|"")
printf "NOME STATO NON VALIDO: %s\n" "$REQUESTED_STATE" >&2
exit 7
;;
esac
case "$DURATION_SECONDS" in
*[!0-9]*|"")
printf "DURATA NON VALIDA: %s\n" "$DURATION_SECONDS" >&2
exit 8
;;
esac
if [ "$DURATION_SECONDS" -le 0 ]; then
printf "LA DURATA DEVE ESSERE MAGGIORE DI ZERO.\n" >&2
exit 8
fi
STAMP="$(date +%Y%m%d_%H%M%S)"
RUN_DIR="$RUNS_ROOT/${STAMP}_${SCENARIO_ID}"
RUN_CONFIG="$RUN_DIR/configuration.marte"
RUN_LOG="$RUN_DIR/run.log"
RUN_SUMMARY="$RUN_DIR/runner_summary.txt"
RUN_HASHES="$RUN_DIR/hashes.sha256"
RUN_COMMAND="$RUN_DIR/command.txt"
mkdir -p -- "$RUN_DIR"
cp --preserve=mode,timestamps -- "$CONFIG_FILE" "$RUN_CONFIG"
RUN_DIR_PLACEHOLDER="__MARTE_RUN_DIR__"
PLACEHOLDER_COUNT="$(
python3 - "$RUN_CONFIG" "$RUN_DIR_PLACEHOLDER" "$RUN_DIR" <<'PYRUNNER'
import sys
from pathlib import Path
config_path = Path(sys.argv[1])
placeholder = sys.argv[2]
run_dir = sys.argv[3]
text = config_path.read_text(encoding="utf-8")
count = text.count(placeholder)
if count > 0:
config_path.write_text(
text.replace(placeholder, run_dir),
encoding="utf-8"
)
remaining = config_path.read_text(
encoding="utf-8"
).count(placeholder)
if remaining != 0:
print(
f"PLACEHOLDER_RESIDUI={remaining}",
file=sys.stderr
)
sys.exit(1)
print(count)
PYRUNNER
)"
PLACEHOLDER_RC=$?
if [ "$PLACEHOLDER_RC" -ne 0 ]; then
printf "ERRORE DURANTE LA SOSTITUZIONE DEL PERCORSO RUN.\n" >&2
exit 9
fi
{
printf "%q " \
timeout \
--signal=INT \
--kill-after=2s \
"${DURATION_SECONDS}s" \
"$MARTE_APP" \
-l RealTimeLoader \
-f "$RUN_CONFIG" \
-s "$REQUESTED_STATE"
printf "\n"
} > "$RUN_COMMAND"
{
sha256sum -- "$CONFIG_FILE"
sha256sum -- "$RUN_CONFIG"
sha256sum -- "$PATHS_FILE"
sha256sum -- "$ENV_FILE"
sha256sum -- "${BASH_SOURCE[0]}"
} > "$RUN_HASHES"
printf "============================================================\n"
printf "MARTe2 COMMON RUNNER\n"
printf "============================================================\n"
printf "Scenario: %s\n" "$SCENARIO_ID"
printf "Configurazione: %s\n" "$RUN_CONFIG"
printf "Stato: %s\n" "$REQUESTED_STATE"
printf "Durata: %s secondi\n" "$DURATION_SECONDS"
printf "Run directory: %s\n" "$RUN_DIR"
printf "Placeholder: %s sostituzioni\n" "$PLACEHOLDER_COUNT"
printf "\n"
set +e
timeout \
--signal=INT \
--kill-after=2s \
"${DURATION_SECONDS}s" \
"$MARTE_APP" \
-l RealTimeLoader \
-f "$RUN_CONFIG" \
-s "$REQUESTED_STATE" \
2>&1 |
tee "$RUN_LOG"
PIPE_CODES=("${PIPESTATUS[@]}")
set -e
MARTE_EXIT_CODE="${PIPE_CODES[0]}"
TEE_EXIT_CODE="${PIPE_CODES[1]}"
{
printf "============================================================\n"
printf "RIEPILOGO COMMON RUNNER\n"
printf "============================================================\n"
printf "Data: %s\n" "$(date --iso-8601=seconds)"
printf "Scenario: %s\n" "$SCENARIO_ID"
printf "Configurazione fonte: %s\n" "$CONFIG_FILE"
printf "Configurazione usata: %s\n" "$RUN_CONFIG"
printf "Stato richiesto: %s\n" "$REQUESTED_STATE"
printf "Durata richiesta: %s secondi\n" "$DURATION_SECONDS"
printf "Placeholder sostituiti: %s\n" "$PLACEHOLDER_COUNT"
printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE"
printf "Exit code tee: %s\n" "$TEE_EXIT_CODE"
printf "Run log: %s\n" "$RUN_LOG"
printf "Hash: %s\n" "$RUN_HASHES"
printf "Comando: %s\n" "$RUN_COMMAND"
} | tee "$RUN_SUMMARY"
printf "\nRUN_DIR=%s\n" "$RUN_DIR"
if [ "$TEE_EXIT_CODE" -ne 0 ]; then
printf "ERRORE DURANTE LA SCRITTURA DEL LOG.\n" >&2
exit 30
fi
exit 0
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Percorso padre dei repository e della benchmark suite.
MARTE_ROOT="/home/dariodf/Scrivania/MARTE"
# Repository esterni usati dalla benchmark suite.
MARTE2_ROOT="$MARTE_ROOT/MARTe2"
MARTE2_COMPONENTS_ROOT="$MARTE_ROOT/MARTe2-components"
MARTE_DEBUG_ROOT="$MARTE_ROOT/marte-debug"
# Directory principale della nuova infrastruttura di test.
BENCHMARK_SUITE_ROOT="$MARTE_ROOT/marte-benchmark-suite"
# Target di compilazione attualmente utilizzato.
MARTE_TARGET="x86-linux"
# Eseguibile MARTe2.
MARTE_APP="$MARTE2_ROOT/Build/$MARTE_TARGET/App/MARTeApp.ex"
# Directory delle configurazioni.
CONFIGURATIONS_ROOT="$BENCHMARK_SUITE_ROOT/configurations"
BASELINES_ROOT="$CONFIGURATIONS_ROOT/baselines"
GENERATED_CONFIGURATIONS_ROOT="$CONFIGURATIONS_ROOT/generated"
# Configurazioni attualmente disponibili.
OFFICIAL_HELLOWORLD_CONFIG="$BASELINES_ROOT/RTApp-HelloWorld-2-solution.cfg"
S00_CONFIG="$GENERATED_CONFIGURATIONS_ROOT/S00/smoke_s0_core.marte"
S01_CONFIG="$GENERATED_CONFIGURATIONS_ROOT/S01/smoke_s1_timing.marte"
# Directory operative della suite.
SCENARIOS_ROOT="$BENCHMARK_SUITE_ROOT/scenarios"
PATCHERS_ROOT="$BENCHMARK_SUITE_ROOT/patchers"
RUNS_ROOT="$BENCHMARK_SUITE_ROOT/runs"
ANALYSIS_ROOT="$BENCHMARK_SUITE_ROOT/analysis"
LOGGING_ROOT="$BENCHMARK_SUITE_ROOT/logging"
ARCHIVE_ROOT="$BENCHMARK_SUITE_ROOT/archive"
@@ -0,0 +1,89 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer GAMDisplay}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,98 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = Display
Type = uint32
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer GAMDisplay}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,152 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = Display
Type = uint32
}
}
}
+IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
+FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s02_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer GAMDisplay IOGAM_Writer}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,220 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = Display
Type = uint32
}
}
}
+IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
SentinelScalarA = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = FileWriter
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
+FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s03_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,260 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
Time = {
DataSource = Display
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = Display
Type = uint32
}
SentinelScalarA = {
DataSource = Display
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = Display
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = Display
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
+IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
SentinelScalarA = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = FileWriter
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
+FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s03_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,260 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = Display
Type = uint32
}
Time = {
DataSource = Display
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = Display
Type = uint32
}
SentinelScalarA = {
DataSource = Display
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = Display
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = Display
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
+IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
SentinelScalarA = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = FileWriter
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Display = {
Class = GAMDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
+FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s03_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
@@ -0,0 +1,193 @@
$HelloWorldApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 10
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
+IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
SentinelScalarA = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = FileWriter
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = FileWriter
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
+FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s03_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer SentinelProducer IOGAM_Writer}
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
+1246
View File
File diff suppressed because it is too large Load Diff
+572
View File
@@ -0,0 +1,572 @@
#!/usr/bin/env bash
set -u
LAUNCHER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CANDIDATE_ROOT="$(cd "$LAUNCHER_DIR/.." && pwd)"
if [ -n "${MARTE_BENCHMARK_SUITE_ROOT:-}" ]; then
SUITE="$(cd "$MARTE_BENCHMARK_SUITE_ROOT" && pwd)"
else
SUITE="/home/dariodf/Scrivania/MARTE/marte-benchmark-suite"
fi
PATCHER="$CANDIDATE_ROOT/patchers/configuration/build_s03h1_from_s02.sh"
VALIDATOR="$CANDIDATE_ROOT/scenarios/S03H1_execution_profiles/validate.sh"
CONFIG_VERIFIER="$CANDIDATE_ROOT/scenarios/S03H1_execution_profiles/verify_s03h1_config.py"
CSV_EXPORTER="$CANDIDATE_ROOT/scenarios/S03H1_execution_profiles/export_s03_csv.py"
TEST_CONFIG="$CANDIDATE_ROOT/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
DIAGNOSTIC_CONFIG="$CANDIDATE_ROOT/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
RUNNER="$SUITE/common/marte_runner.sh"
LIVE_PATHS="$SUITE/common/paths.sh"
LIVE_ENVIRONMENT="$SUITE/common/environment.sh"
LIVE_OPERATIONAL_LAUNCHER="$SUITE/launcher/marte_benchmark_launcher.sh"
LIVE_PATCHER_S01="$SUITE/patchers/configuration/build_s01_from_s00.sh"
LIVE_PATCHER_S02="$SUITE/patchers/configuration/build_s02_from_s01.sh"
LIVE_PATCHER_S03="$SUITE/patchers/configuration/build_s03_from_s02.sh"
LIVE_VALIDATOR_S02="$SUITE/scenarios/S02_binary_recording/validate.sh"
LIVE_VALIDATOR_S03="$SUITE/scenarios/S03_semantic_sentinels/validate.sh"
LIVE_PARSER_S03="$SUITE/scenarios/S03_semantic_sentinels/parse_s03_binary.py"
LIVE_S00_CONFIG="$SUITE/configurations/generated/S00/smoke_s0_core.marte"
LIVE_S01_CONFIG="$SUITE/configurations/generated/S01/from_patcher/smoke_s1_timing_from_s00.marte"
LIVE_S02_CONFIG="$SUITE/configurations/generated/S02/from_patcher/smoke_s2_binary_recording_from_s01.marte"
LIVE_S03_CONFIG="$SUITE/configurations/generated/S03/from_patcher/smoke_s3_semantic_sentinels_from_s02.marte"
EXPECTED_TEST_CONFIG_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
EXPECTED_DIAGNOSTIC_CONFIG_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
EXPECTED_PATCHER_SHA256="5db5f71db4ab8f007b2e78230c2506bed4c62a8e502fb1c08ca04eb14b645b82"
EXPECTED_VALIDATOR_SHA256="748f403a045f9dbed64910de67e75ba5529c02ab5894417a866225518c10b5e5"
EXPECTED_CONFIG_VERIFIER_SHA256="2f1c41b186bd2e79a9fb51fab0461a7a0dce104bf0852c0ec1ade2c55a017443"
EXPECTED_CSV_EXPORTER_SHA256="b413ad113e188141891b61b930a607890b8468051e8a61cb0f0454c519a51fb7"
print_usage() {
printf "Uso:\n"
printf " %s S03H1 <durata-secondi> TEST\n" "$0"
printf " %s S03H1 <durata-secondi> DIAGNOSTIC\n" "$0"
printf "\n"
printf "La candidata non installa e non modifica la baseline S03 R3.\n"
}
fail() {
code="$1"
shift
printf "S03_H1_LAUNCHER_STATUS=FAIL\n" >&2
printf "S03_H1_LAUNCHER_ERROR=%s\n" "$*" >&2
exit "$code"
}
validate_duration() {
candidate_duration="$1"
case "$candidate_duration" in
*[!0-9]*|"")
return 1
;;
esac
[ "$candidate_duration" -gt 0 ]
}
verify_hash() {
candidate_file="$1"
candidate_expected="$2"
candidate_label="$3"
if [ ! -f "$candidate_file" ]; then
printf "HASH_CHECK=%s STATUS=MISSING FILE=%s\n" \
"$candidate_label" "$candidate_file" >&2
return 1
fi
candidate_actual="$(sha256sum -- "$candidate_file" | awk '{print $1}')"
printf "HASH_CHECK=%s EXPECTED=%s ACTUAL=%s FILE=%s\n" \
"$candidate_label" "$candidate_expected" "$candidate_actual" "$candidate_file"
[ "$candidate_actual" = "$candidate_expected" ]
}
verify_candidate_components() {
verify_hash "$PATCHER" "$EXPECTED_PATCHER_SHA256" "S03H1_PATCHER" || return 1
verify_hash "$VALIDATOR" "$EXPECTED_VALIDATOR_SHA256" "S03H1_VALIDATOR" || return 1
verify_hash "$CONFIG_VERIFIER" "$EXPECTED_CONFIG_VERIFIER_SHA256" "S03H1_CONFIG_VERIFIER" || return 1
verify_hash "$CSV_EXPORTER" "$EXPECTED_CSV_EXPORTER_SHA256" "S03H1_CSV_EXPORTER" || return 1
verify_hash "$CANDIDATE_CONFIG" "$EXPECTED_CANDIDATE_CONFIG_SHA256" "S03H1_${MODE}_CONFIG" || return 1
return 0
}
PROTECTED_PATHS=(
"$LIVE_OPERATIONAL_LAUNCHER"
"$LIVE_S00_CONFIG"
"$LIVE_S01_CONFIG"
"$LIVE_S02_CONFIG"
"$LIVE_S03_CONFIG"
"$LIVE_PATCHER_S01"
"$LIVE_PATCHER_S02"
"$LIVE_PATCHER_S03"
"$LIVE_VALIDATOR_S02"
"$LIVE_VALIDATOR_S03"
"$LIVE_PARSER_S03"
"$RUNNER"
"$LIVE_PATHS"
"$LIVE_ENVIRONMENT"
)
PROTECTED_EXPECTED_HASHES=(
"7ef0419826c85c0364aad2403db4b8340cd815e9d9ce17edfbe5da8e6fbf2683"
"49a9b48595aab88cd8fd11d1e58ace4ab11d7d0a649e9b143bdc25c8866253d5"
"1513e1ca72eba56d53079ac7328908f12ddc72772456e2d75bcbacb6465d2427"
"8ecc8ba55e46e9f6fb8f304404d4070ff4c2ea5b9c783a69113bc08b457859d1"
"0f8916735ff67a9be2b167b52f147687fe131a063f410a81bfbf3bf9ff65f1e4"
"a8cced1ea1711806863606e27c3ba52a3e72794dbe8c36c253570709ce3f7ccb"
"8ac51d09f6f7984af3522e373bab59e2553775c418f69a53ccb93d8d1b893391"
"96c778ba6a15353fd969b19960706d7c0ca810c0c6fd61a14b5f88960e4e2b7a"
"7cd29b216b5edc3c72750a3326aebe5a9fdfb3b7522f1d7a6e8b0a1a97b3d60a"
"95caf207c4d29037c4973d756ff5a7004a3360aad9656d068948e222a4ba0c98"
"91c4d66770197e1986ed91eb6a4870e7da7df5a522ffdf4329b6d9250f238307"
"9e4f1a6b0dabc244fd0726fd18bf07e86b29a9fc6a76d54fcaf61c79f13c9633"
"468d92c3eb048b7137ea52a768f459d2d1f26f83f5a5c174d3333d032c9583c6"
"b1faa567e480c326c72107b886f387f108907900804621337524f1f856f056ac"
)
verify_protected_baseline() {
candidate_index=0
while [ "$candidate_index" -lt "${#PROTECTED_PATHS[@]}" ]; do
verify_hash \
"${PROTECTED_PATHS[$candidate_index]}" \
"${PROTECTED_EXPECTED_HASHES[$candidate_index]}" \
"PROTECTED_${candidate_index}" || return 1
candidate_index=$((candidate_index + 1))
done
return 0
}
write_protected_hashes() {
candidate_destination="$1"
sha256sum -- "${PROTECTED_PATHS[@]}" >"$candidate_destination"
}
parse_contract_field() {
candidate_field="$1"
candidate_file="$2"
PARSED_FIELD_VALUE=""
PARSED_FIELD_ERROR=""
if [ ! -f "$candidate_file" ]; then
PARSED_FIELD_ERROR="NO_FILE"
return 1
fi
candidate_count="$(grep -Ec "^${candidate_field}=[A-Z][A-Z0-9_]*$" "$candidate_file" || true)"
candidate_any_count="$(grep -Ec "^${candidate_field}=" "$candidate_file" || true)"
if [ "$candidate_any_count" -eq 0 ]; then
PARSED_FIELD_ERROR="MISSING"
return 1
fi
if [ "$candidate_any_count" -ne 1 ]; then
PARSED_FIELD_ERROR="DUPLICATED"
return 1
fi
if [ "$candidate_count" -ne 1 ]; then
PARSED_FIELD_ERROR="MALFORMED"
return 1
fi
PARSED_FIELD_VALUE="$(sed -n "s/^${candidate_field}=//p" "$candidate_file")"
return 0
}
classify_s03h1_run() {
classify_validator_rc="$1"
classify_tee_rc="$2"
classify_validation_file="$3"
classify_csv_rc="$4"
classify_invariance_status="$5"
FINAL_RC=20
FINAL_RESULT="INCONCLUSIVE"
BLOCK_REASON="NONE"
PARSED_MODE_STATUS="NOT_EVALUATED"
PARSED_PROFILE_CONFIG_STATUS="NOT_EVALUATED"
PARSED_OBSERVABILITY_STATUS="NOT_EVALUATED"
PARSED_FUNCTIONAL_STATUS="NOT_EVALUATED"
PARSED_SEMANTIC_STATUS="NOT_EVALUATED"
PARSED_TIMING_STATUS="NOT_EVALUATED"
PARSED_MEASUREMENT_ELIGIBILITY="NOT_EVALUATED"
if [ "$classify_tee_rc" -ne 0 ]; then
FINAL_RC=67
FINAL_RESULT="FAIL_VALIDATION_LOG"
BLOCK_REASON="VALIDATION_LOG_NOT_WRITTEN"
return 0
fi
CONTRACT_PARSE_ERROR="NONE"
for candidate_field in \
MODE_STATUS \
PROFILE_CONFIG_STATUS \
OBSERVABILITY_STATUS \
FUNCTIONAL_STATUS \
SEMANTIC_STATUS \
TIMING_STATUS \
MEASUREMENT_ELIGIBILITY
do
if parse_contract_field "$candidate_field" "$classify_validation_file"; then
case "$candidate_field" in
MODE_STATUS) PARSED_MODE_STATUS="$PARSED_FIELD_VALUE" ;;
PROFILE_CONFIG_STATUS) PARSED_PROFILE_CONFIG_STATUS="$PARSED_FIELD_VALUE" ;;
OBSERVABILITY_STATUS) PARSED_OBSERVABILITY_STATUS="$PARSED_FIELD_VALUE" ;;
FUNCTIONAL_STATUS) PARSED_FUNCTIONAL_STATUS="$PARSED_FIELD_VALUE" ;;
SEMANTIC_STATUS) PARSED_SEMANTIC_STATUS="$PARSED_FIELD_VALUE" ;;
TIMING_STATUS) PARSED_TIMING_STATUS="$PARSED_FIELD_VALUE" ;;
MEASUREMENT_ELIGIBILITY) PARSED_MEASUREMENT_ELIGIBILITY="$PARSED_FIELD_VALUE" ;;
esac
elif [ "$CONTRACT_PARSE_ERROR" = "NONE" ]; then
CONTRACT_PARSE_ERROR="${candidate_field}_${PARSED_FIELD_ERROR}"
fi
done
if [ "$CONTRACT_PARSE_ERROR" != "NONE" ]; then
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_S03H1_CONTRACT"
BLOCK_REASON="$CONTRACT_PARSE_ERROR"
return 0
fi
if [ "$PARSED_MEASUREMENT_ELIGIBILITY" != "NO" ]; then
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_S03H1_CONTRACT"
BLOCK_REASON="ELIGIBILITY_CONTRACT_VIOLATION_${PARSED_MEASUREMENT_ELIGIBILITY}"
return 0
fi
if [ "$classify_invariance_status" != "PASS" ]; then
FINAL_RC=71
FINAL_RESULT="FAIL_PROTECTED_BASELINE_CHANGED"
BLOCK_REASON="PROTECTED_BASELINE_INVARIANCE_${classify_invariance_status}"
return 0
fi
if [ "$classify_csv_rc" -ne 0 ]; then
FINAL_RC=70
FINAL_RESULT="FAIL_DERIVED_CSV_EXPORT"
BLOCK_REASON="CSV_EXPORT_RC_${classify_csv_rc}"
return 0
fi
case "$classify_validator_rc" in
0|10|11)
;;
20|21)
FINAL_RC=20
FINAL_RESULT="S03H1_VALIDATION_INCONCLUSIVE"
BLOCK_REASON="VALIDATOR_RC_${classify_validator_rc}"
return 0
;;
*)
FINAL_RC=68
FINAL_RESULT="FAIL_VALIDATOR_EXIT"
BLOCK_REASON="UNRECOGNISED_VALIDATOR_RC_${classify_validator_rc}"
return 0
;;
esac
for candidate_status in \
"$PARSED_MODE_STATUS" \
"$PARSED_PROFILE_CONFIG_STATUS" \
"$PARSED_OBSERVABILITY_STATUS" \
"$PARSED_FUNCTIONAL_STATUS" \
"$PARSED_SEMANTIC_STATUS"
do
if [ "$candidate_status" != "PASS" ]; then
FINAL_RC=25
FINAL_RESULT="S03H1_CONTRACT_STATUS_NOT_PASS"
BLOCK_REASON="REQUIRED_STATUS_${candidate_status}"
return 0
fi
done
case "$PARSED_TIMING_STATUS" in
NOT_VALID_*)
FINAL_RC=14
FINAL_RESULT="FUNCTIONAL_AND_SEMANTIC_PASS_TIMING_NOT_VALID"
BLOCK_REASON="$PARSED_TIMING_STATUS"
;;
UNASSESSED_*)
FINAL_RC=15
FINAL_RESULT="FUNCTIONAL_AND_SEMANTIC_PASS_TIMING_UNASSESSED"
BLOCK_REASON="$PARSED_TIMING_STATUS"
;;
*)
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_TIMING_CONTRACT"
BLOCK_REASON="TIMING_STATUS_${PARSED_TIMING_STATUS}"
;;
esac
return 0
}
run_s03h1() {
duration="$1"
MODE="$2"
case "$MODE" in
TEST)
CANDIDATE_CONFIG="$TEST_CONFIG"
EXPECTED_CANDIDATE_CONFIG_SHA256="$EXPECTED_TEST_CONFIG_SHA256"
;;
DIAGNOSTIC)
CANDIDATE_CONFIG="$DIAGNOSTIC_CONFIG"
EXPECTED_CANDIDATE_CONFIG_SHA256="$EXPECTED_DIAGNOSTIC_CONFIG_SHA256"
;;
*)
printf "ERRORE: MODE non riconosciuto: %s\n" "$MODE" >&2
return 2
;;
esac
if [ ! -d "$SUITE" ]; then
printf "ERRORE: suite operativa assente: %s\n" "$SUITE" >&2
return 50
fi
if ! verify_candidate_components; then
printf "ERRORE: integrita' candidata non dimostrata.\n" >&2
return 51
fi
if ! verify_protected_baseline; then
printf "ERRORE: baseline protetta assente o con hash inatteso.\n" >&2
return 52
fi
WORK_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/s03h1_launcher.XXXXXX")" || return 53
trap 'rm -rf -- "$WORK_ROOT"' EXIT HUP INT TERM
GENERATED_CONFIG="$WORK_ROOT/generated_${MODE}.marte"
PATCHER_LOG="$WORK_ROOT/patcher.log"
PROTECTED_BEFORE="$WORK_ROOT/protected_baseline_before.sha256"
CAPTURE="$WORK_ROOT/runner_capture.log"
"$PATCHER" "$LIVE_S02_CONFIG" "$GENERATED_CONFIG" "$MODE" >"$PATCHER_LOG" 2>&1
PATCHER_RC=$?
if [ "$PATCHER_RC" -ne 0 ]; then
printf "ERRORE: patcher candidato fallito rc=%s\n" "$PATCHER_RC" >&2
cat -- "$PATCHER_LOG" >&2
return 54
fi
if ! cmp -s -- "$GENERATED_CONFIG" "$CANDIDATE_CONFIG"; then
printf "ERRORE: build candidato non coincide con config confezionata.\n" >&2
return 55
fi
write_protected_hashes "$PROTECTED_BEFORE" || return 56
if [ ! -f "$LIVE_PATHS" ]; then
printf "ERRORE: paths.sh assente: %s\n" "$LIVE_PATHS" >&2
return 57
fi
# shellcheck source=/dev/null
source "$LIVE_PATHS"
CONSTANT_GAM_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/GAMs/ConstantGAM"
CONSTANT_GAM_LIB="$CONSTANT_GAM_LIB_DIR/ConstantGAM.so"
if [ ! -f "$CONSTANT_GAM_LIB" ]; then
printf "ERRORE: ConstantGAM assente: %s\n" "$CONSTANT_GAM_LIB" >&2
return 58
fi
case ":${LD_LIBRARY_PATH-}:" in
*":${CONSTANT_GAM_LIB_DIR}:"*) ;;
*)
if [ -n "${LD_LIBRARY_PATH-}" ]; then
LD_LIBRARY_PATH="${CONSTANT_GAM_LIB_DIR}:${LD_LIBRARY_PATH}"
else
LD_LIBRARY_PATH="$CONSTANT_GAM_LIB_DIR"
fi
;;
esac
export LD_LIBRARY_PATH
printf "============================================================\n"
printf "S03-H1 EXECUTION PROFILES - RUN CANDIDATA\n"
printf "============================================================\n"
printf "MODE=%s\n" "$MODE"
printf "DURATION=%s\n" "$duration"
printf "CURRENT_OPERATIONAL_BASELINE=S03_R3_INSTALLED\n"
printf "S03_H1_INSTALLATION_AUTHORIZED=NO\n"
"$RUNNER" \
"S03H1_${MODE}_launcher" \
"$CANDIDATE_CONFIG" \
"State1" \
"$duration" \
2>&1 | tee "$CAPTURE"
RUNNER_CODES=("${PIPESTATUS[@]}")
RUNNER_RC="${RUNNER_CODES[0]}"
CAPTURE_TEE_RC="${RUNNER_CODES[1]}"
if [ "$RUNNER_RC" -ne 0 ]; then
printf "ERRORE: common runner fallito rc=%s\n" "$RUNNER_RC" >&2
return 60
fi
if [ "$CAPTURE_TEE_RC" -ne 0 ]; then
printf "ERRORE: capture common runner fallita.\n" >&2
return 61
fi
RUN_DIR="$(sed -n 's/^RUN_DIR=//p' "$CAPTURE" | tail -n 1)"
if [ -z "$RUN_DIR" ] || [ ! -d "$RUN_DIR" ]; then
printf "ERRORE: RUN_DIR non individuata o assente: %s\n" "$RUN_DIR" >&2
return 62
fi
RUN_LOG="$RUN_DIR/run.log"
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
RUN_HASHES="$RUN_DIR/hashes.sha256"
OUTPUT_FILE="$RUN_DIR/s03_output.bin"
CSV_FILE="$RUN_DIR/s03_output.csv"
MODE_FILE="$RUN_DIR/s03h1_mode.txt"
VALIDATION_LOG="$RUN_DIR/validation.txt"
CSV_REPORT="$RUN_DIR/csv_export.txt"
LAUNCHER_SUMMARY="$RUN_DIR/launcher_summary.txt"
PROTECTED_BEFORE_RUN="$RUN_DIR/protected_baseline_before.sha256"
PROTECTED_AFTER_RUN="$RUN_DIR/protected_baseline_after.sha256"
INVARIANCE_REPORT="$RUN_DIR/protected_baseline_invariance.txt"
cp -- "$PROTECTED_BEFORE" "$PROTECTED_BEFORE_RUN"
{
printf "SCENARIO=S03H1\n"
printf "MODE=%s\n" "$MODE"
printf "CANDIDATE_CONFIG_SHA256=%s\n" "$EXPECTED_CANDIDATE_CONFIG_SHA256"
printf "S03_H1_INSTALLATION_AUTHORIZED=NO\n"
} >"$MODE_FILE"
sha256sum -- \
"$MODE_FILE" \
"${BASH_SOURCE[0]}" \
"$PATCHER" \
"$VALIDATOR" \
"$CONFIG_VERIFIER" \
"$CSV_EXPORTER" \
"$LIVE_PARSER_S03" \
"$RUN_LOG" \
"$RUNNER_SUMMARY" \
"$OUTPUT_FILE" \
"$PROTECTED_BEFORE_RUN" \
"${PROTECTED_PATHS[@]}" \
>>"$RUN_HASHES"
S03_BASELINE_PARSER="$LIVE_PARSER_S03" \
timeout 30s "$VALIDATOR" "$MODE" "$RUN_DIR" 2>&1 | tee "$VALIDATION_LOG"
VALIDATION_CODES=("${PIPESTATUS[@]}")
VALIDATOR_RC="${VALIDATION_CODES[0]}"
VALIDATION_TEE_RC="${VALIDATION_CODES[1]}"
"$CSV_EXPORTER" "$OUTPUT_FILE" "$CSV_FILE" >"$CSV_REPORT" 2>&1
CSV_RC=$?
write_protected_hashes "$PROTECTED_AFTER_RUN"
if cmp -s -- "$PROTECTED_BEFORE_RUN" "$PROTECTED_AFTER_RUN"; then
PROTECTED_BASELINE_INVARIANCE="PASS"
else
PROTECTED_BASELINE_INVARIANCE="FAIL"
fi
{
printf "PROTECTED_BASELINE_INVARIANCE=%s\n" "$PROTECTED_BASELINE_INVARIANCE"
printf "CURRENT_OPERATIONAL_BASELINE=S03_R3_INSTALLED\n"
printf "S03_H1_INSTALLATION_AUTHORIZED=NO\n"
} >"$INVARIANCE_REPORT"
for evidence_file in \
"$VALIDATION_LOG" \
"$CSV_REPORT" \
"$PROTECTED_AFTER_RUN" \
"$INVARIANCE_REPORT"
do
if [ -f "$evidence_file" ]; then
sha256sum -- "$evidence_file" >>"$RUN_HASHES"
fi
done
if [ -f "$CSV_FILE" ]; then
sha256sum -- "$CSV_FILE" >>"$RUN_HASHES"
fi
classify_s03h1_run \
"$VALIDATOR_RC" \
"$VALIDATION_TEE_RC" \
"$VALIDATION_LOG" \
"$CSV_RC" \
"$PROTECTED_BASELINE_INVARIANCE"
{
printf "============================================================\n"
printf "RIEPILOGO LAUNCHER S03-H1 CANDIDATO\n"
printf "============================================================\n"
printf "SCENARIO=S03H1\n"
printf "MODE=%s\n" "$MODE"
printf "DURATION=%s\n" "$duration"
printf "RUN_DIR=%s\n" "$RUN_DIR"
printf "RUNNER_RC=%s\n" "$RUNNER_RC"
printf "VALIDATOR_RC=%s\n" "$VALIDATOR_RC"
printf "CSV_EXPORT_RC=%s\n" "$CSV_RC"
printf "CSV_STATUS=%s\n" "$( [ "$CSV_RC" -eq 0 ] && printf PASS || printf FAIL )"
printf "MODE_STATUS=%s\n" "$PARSED_MODE_STATUS"
printf "PROFILE_CONFIG_STATUS=%s\n" "$PARSED_PROFILE_CONFIG_STATUS"
printf "OBSERVABILITY_STATUS=%s\n" "$PARSED_OBSERVABILITY_STATUS"
printf "FUNCTIONAL_STATUS=%s\n" "$PARSED_FUNCTIONAL_STATUS"
printf "SEMANTIC_STATUS=%s\n" "$PARSED_SEMANTIC_STATUS"
printf "TIMING_STATUS=%s\n" "$PARSED_TIMING_STATUS"
printf "MEASUREMENT_ELIGIBILITY=NO\n"
printf "PROTECTED_BASELINE_INVARIANCE=%s\n" "$PROTECTED_BASELINE_INVARIANCE"
printf "BLOCK_REASON=%s\n" "$BLOCK_REASON"
printf "FINAL_RESULT=%s\n" "$FINAL_RESULT"
printf "FINAL_RC=%s\n" "$FINAL_RC"
printf "S03_H1_INSTALLATION_AUTHORIZED=NO\n"
printf "CURRENT_OPERATIONAL_BASELINE=S03_R3_INSTALLED\n"
printf "PERFORMANCE_MEASUREMENT_AUTHORIZED=NO\n"
printf "S04_AUTHORIZED=NO\n"
printf "MD00_AUTHORIZED=NO\n"
} | tee "$LAUNCHER_SUMMARY"
SUMMARY_CODES=("${PIPESTATUS[@]}")
if [ "${SUMMARY_CODES[1]}" -ne 0 ]; then
FINAL_RC=69
else
sha256sum -- "$LAUNCHER_SUMMARY" >>"$RUN_HASHES"
fi
printf "RUN_DIR=%s\n" "$RUN_DIR"
printf "RISULTATO_FINALE=%s\n" "$FINAL_RESULT"
printf "FINAL_RC=%s\n" "$FINAL_RC"
rm -rf -- "$WORK_ROOT"
trap - EXIT HUP INT TERM
return "$FINAL_RC"
}
if [ "${S03H1_LAUNCHER_LIB_ONLY:-0}" = "1" ]; then
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
return 0
fi
printf "ERRORE: S03H1_LAUNCHER_LIB_ONLY=1 e' ammesso solo con source.\n" >&2
exit 3
fi
case "$#" in
1)
case "$1" in
-h|--help)
print_usage
exit 0
;;
*)
print_usage >&2
exit 2
;;
esac
;;
3)
if [ "$1" != "S03H1" ] && [ "$1" != "s03h1" ]; then
print_usage >&2
exit 2
fi
if ! validate_duration "$2"; then
printf "ERRORE: durata non valida: %s\n" "$2" >&2
exit 2
fi
case "$3" in
TEST|DIAGNOSTIC) ;;
*)
printf "ERRORE: MODE non riconosciuto: %s\n" "$3" >&2
exit 2
;;
esac
run_s03h1 "$2" "$3"
exit $?
;;
*)
print_usage >&2
exit 2
;;
esac
+669
View File
@@ -0,0 +1,669 @@
#!/usr/bin/env bash
set -u
set -o pipefail
LAUNCHER_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
INSTALLED_SUITE="$(cd -- "$LAUNCHER_DIR/.." && pwd -P)"
WORK_ROOT=""
print_usage() {
printf 'Uso:\n'
printf ' %s S03H3 <durata-secondi> MINIMAL\n' "$0"
printf ' %s S03H3 <durata-secondi> TEST\n' "$0"
printf ' %s S03H3 <durata-secondi> DIAGNOSTIC\n' "$0"
}
fail() {
local code="$1"
local reason="$2"
printf 'S03_H3_LAUNCHER_STATUS=FAIL\n' >&2
printf 'BLOCK_REASON=%s\n' "$reason" >&2
exit "$code"
}
is_positive_decimal() {
local value="$1"
[[ "$value" =~ ^[0-9]+$ ]] && [[ "$value" =~ [1-9] ]]
}
verify_hash() {
local file="$1"
local expected="$2"
local label="$3"
local actual
if [ ! -f "$file" ]; then
printf 'HASH_CHECK=%s STATUS=MISSING FILE=%s\n' "$label" "$file" >&2
return 1
fi
actual="$(sha256sum -- "$file" | awk '{print $1}')" || return 1
printf 'HASH_CHECK=%s EXPECTED=%s ACTUAL=%s FILE=%s\n' \
"$label" "$expected" "$actual" "$file"
[ "$actual" = "$expected" ]
}
readonly -a HISTORICAL_RELATIVE_PATHS=(
"launcher/marte_benchmark_launcher.sh"
"configurations/generated/S00/smoke_s0_core.marte"
"configurations/generated/S01/from_patcher/smoke_s1_timing_from_s00.marte"
"configurations/generated/S02/from_patcher/smoke_s2_binary_recording_from_s01.marte"
"configurations/generated/S03/from_patcher/smoke_s3_semantic_sentinels_from_s02.marte"
"patchers/configuration/build_s01_from_s00.sh"
"patchers/configuration/build_s02_from_s01.sh"
"patchers/configuration/build_s03_from_s02.sh"
"scenarios/S02_binary_recording/validate.sh"
"scenarios/S03_semantic_sentinels/validate.sh"
"scenarios/S03_semantic_sentinels/parse_s03_binary.py"
"common/marte_runner.sh"
"common/paths.sh"
"common/environment.sh"
"launcher/marte_benchmark_launcher_s03h1.sh"
"patchers/configuration/build_s03h1_from_s02.sh"
"scenarios/S03H1_execution_profiles/validate.sh"
"scenarios/S03H1_execution_profiles/verify_s03h1_config.py"
"scenarios/S03H1_execution_profiles/export_s03_csv.py"
"configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
"configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
)
readonly -a HISTORICAL_EXPECTED_HASHES=(
"7ef0419826c85c0364aad2403db4b8340cd815e9d9ce17edfbe5da8e6fbf2683"
"49a9b48595aab88cd8fd11d1e58ace4ab11d7d0a649e9b143bdc25c8866253d5"
"1513e1ca72eba56d53079ac7328908f12ddc72772456e2d75bcbacb6465d2427"
"8ecc8ba55e46e9f6fb8f304404d4070ff4c2ea5b9c783a69113bc08b457859d1"
"0f8916735ff67a9be2b167b52f147687fe131a063f410a81bfbf3bf9ff65f1e4"
"a8cced1ea1711806863606e27c3ba52a3e72794dbe8c36c253570709ce3f7ccb"
"8ac51d09f6f7984af3522e373bab59e2553775c418f69a53ccb93d8d1b893391"
"96c778ba6a15353fd969b19960706d7c0ca810c0c6fd61a14b5f88960e4e2b7a"
"7cd29b216b5edc3c72750a3326aebe5a9fdfb3b7522f1d7a6e8b0a1a97b3d60a"
"95caf207c4d29037c4973d756ff5a7004a3360aad9656d068948e222a4ba0c98"
"91c4d66770197e1986ed91eb6a4870e7da7df5a522ffdf4329b6d9250f238307"
"9e4f1a6b0dabc244fd0726fd18bf07e86b29a9fc6a76d54fcaf61c79f13c9633"
"468d92c3eb048b7137ea52a768f459d2d1f26f83f5a5c174d3333d032c9583c6"
"b1faa567e480c326c72107b886f387f108907900804621337524f1f856f056ac"
"f420afdff1f0a96026d14820662a4257c0da4924a491fe00182f838414e06152"
"5db5f71db4ab8f007b2e78230c2506bed4c62a8e502fb1c08ca04eb14b645b82"
"748f403a045f9dbed64910de67e75ba5529c02ab5894417a866225518c10b5e5"
"2f1c41b186bd2e79a9fb51fab0461a7a0dce104bf0852c0ec1ade2c55a017443"
"b413ad113e188141891b61b930a607890b8468051e8a61cb0f0454c519a51fb7"
"d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
"cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
)
readonly -a S03H3_ADD_RELATIVE_PATHS=(
"launcher/marte_benchmark_launcher_s03h3.sh"
"patchers/configuration/build_s03h3_minimal_from_s03h1_test.sh"
"scenarios/S03H3_execution_profiles/validate.sh"
"scenarios/S03H3_execution_profiles/verify_s03h3_minimal_structure.py"
"configurations/generated/S03H3/from_patcher/smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte"
)
readonly EXPECTED_PATCHER_SHA256="b3ba3c195b33a028e7ae21ba92b90a070a5e3526b455f9e3dbcebe023af05e2e"
readonly EXPECTED_VALIDATOR_SHA256="8574636315340c785f41dca37eaac097cd3b59958e7fd1160444b9c21768f2d5"
readonly EXPECTED_MINIMAL_VERIFIER_SHA256="c8d2d4880ed9cb0e8f23427617e989f5f70e82a990a49489f2863a7b3fc2cb95"
readonly EXPECTED_MINIMAL_CONFIG_SHA256="2ee67c48d748e79ec7fdbc748bc4371eb02590d7522b41990d5cb2a3649796ec"
readonly EXPECTED_TEST_CONFIG_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
readonly EXPECTED_DIAGNOSTIC_CONFIG_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
resolve_suite() {
if [ -n "${MARTE_BENCHMARK_SUITE_ROOT:-}" ]; then
[[ "$MARTE_BENCHMARK_SUITE_ROOT" = /* ]] || \
fail 50 "TEST_OVERRIDE_NOT_ABSOLUTE"
[ -d "$MARTE_BENCHMARK_SUITE_ROOT" ] || \
fail 50 "TEST_OVERRIDE_ROOT_MISSING"
SUITE="$(cd -- "$MARTE_BENCHMARK_SUITE_ROOT" && pwd -P)"
[ -n "$SUITE" ] && [ "$SUITE" != "/" ] || \
fail 50 "TEST_OVERRIDE_ROOT_UNSAFE"
if [ "$SUITE" != "$INSTALLED_SUITE" ]; then
[ "${S03H3_TEST_ONLY_OVERRIDE:-NO}" = "YES" ] || \
fail 50 "TEST_OVERRIDE_NOT_ACKNOWLEDGED"
fi
elif [ "${S03H3_TEST_ONLY_OVERRIDE:-NO}" = "YES" ]; then
fail 50 "TEST_OVERRIDE_ROOT_NOT_PROVIDED"
else
SUITE="$INSTALLED_SUITE"
fi
readonly SUITE
}
verify_historical_baseline() {
local index=0
while [ "$index" -lt "${#HISTORICAL_RELATIVE_PATHS[@]}" ]; do
verify_hash \
"$SUITE/${HISTORICAL_RELATIVE_PATHS[$index]}" \
"${HISTORICAL_EXPECTED_HASHES[$index]}" \
"HISTORICAL_${index}" || return 1
index=$((index + 1))
done
}
verify_candidate_components() {
verify_hash "$PATCHER" "$EXPECTED_PATCHER_SHA256" "S03H3_PATCHER" || return 1
verify_hash "$VALIDATOR" "$EXPECTED_VALIDATOR_SHA256" "S03H3_VALIDATOR" || return 1
verify_hash "$MINIMAL_VERIFIER" "$EXPECTED_MINIMAL_VERIFIER_SHA256" "S03H3_MINIMAL_VERIFIER" || return 1
verify_hash "$MINIMAL_CONFIG" "$EXPECTED_MINIMAL_CONFIG_SHA256" "S03H3_MINIMAL_CONFIG" || return 1
}
build_protected_paths() {
PROTECTED_PATHS=()
local relative
for relative in "${HISTORICAL_RELATIVE_PATHS[@]}"; do
PROTECTED_PATHS+=("$SUITE/$relative")
done
for relative in "${S03H3_ADD_RELATIVE_PATHS[@]}"; do
PROTECTED_PATHS+=("$SUITE/$relative")
done
PROTECTED_PATHS+=("$SUITE/launcher/marte_benchmark_menu.sh")
}
write_protected_snapshot() {
local destination="$1"
[ "${#PROTECTED_PATHS[@]}" -eq 27 ] || return 1
sha256sum -- "${PROTECTED_PATHS[@]}" >"$destination"
}
write_protected_path_list() {
local destination="$1"
printf '%s\n' "${PROTECTED_PATHS[@]}" >"$destination"
}
parse_contract_field() {
local field="$1"
local file="$2"
local count
PARSED_FIELD_VALUE=""
PARSED_FIELD_ERROR=""
count="$(grep -Ec "^${field}=" "$file" || true)"
if [ "$count" -eq 0 ]; then
PARSED_FIELD_ERROR="MISSING"
return 1
fi
if [ "$count" -ne 1 ]; then
PARSED_FIELD_ERROR="DUPLICATED"
return 1
fi
PARSED_FIELD_VALUE="$(sed -n "s/^${field}=//p" "$file")"
[ -n "$PARSED_FIELD_VALUE" ] || {
PARSED_FIELD_ERROR="MALFORMED"
return 1
}
}
classify_s03h3_run() {
local validator_rc="$1"
local validation_tee_rc="$2"
local validation_file="$3"
local csv_rc="$4"
local invariance_status="$5"
local dependency_status="$6"
local requested_mode="$7"
local field
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_S03H3_CONTRACT"
BLOCK_REASON="NOT_EVALUATED"
PARSED_MODE_STATUS="NOT_EVALUATED"
PARSED_PROFILE_CONFIG_STATUS="NOT_EVALUATED"
PARSED_PROFILE_RUNTIME_STATUS="NOT_EVALUATED"
PARSED_OBSERVABILITY_STATUS="NOT_EVALUATED"
PARSED_FUNCTIONAL_STATUS="NOT_EVALUATED"
PARSED_SEMANTIC_STATUS="NOT_EVALUATED"
PARSED_TIMING_STATUS="NOT_EVALUATED"
PARSED_MEASUREMENT_ELIGIBILITY="NOT_EVALUATED"
PARSED_COMPLETE_SAMPLE_COUNT="NOT_EVALUATED"
if [ "$dependency_status" != "PASS" ]; then
FINAL_RC=51
FINAL_RESULT="FAIL_DEPENDENCY_INTEGRITY"
BLOCK_REASON="DEPENDENCY_INTEGRITY_NOT_PASS"
return 0
fi
if [ "$validation_tee_rc" -ne 0 ]; then
FINAL_RC=67
FINAL_RESULT="FAIL_VALIDATION_LOG"
BLOCK_REASON="VALIDATION_LOG_NOT_WRITTEN"
return 0
fi
case "$validator_rc" in
0|10|11|20|21) ;;
*)
FINAL_RC=68
FINAL_RESULT="FAIL_VALIDATOR_EXIT"
BLOCK_REASON="UNRECOGNIZED_VALIDATOR_RC_${validator_rc}"
return 0
;;
esac
if [ "$csv_rc" -ne 0 ]; then
FINAL_RC=70
FINAL_RESULT="FAIL_DERIVED_CSV_EXPORT"
BLOCK_REASON="CSV_EXPORT_RC_${csv_rc}"
return 0
fi
if [ "$invariance_status" != "PASS" ]; then
FINAL_RC=71
FINAL_RESULT="FAIL_PROTECTED_BASELINE_CHANGED"
BLOCK_REASON="PROTECTED_BASELINE_INVARIANCE_${invariance_status}"
return 0
fi
CONTRACT_PARSE_ERROR="NONE"
for field in \
MODE \
MODE_STATUS \
PROFILE_CONFIG_STATUS \
PROFILE_RUNTIME_STATUS \
OBSERVABILITY_STATUS \
OBSERVABILITY_REASON \
FUNCTIONAL_STATUS \
SEMANTIC_STATUS \
TIMING_STATUS \
MEASUREMENT_ELIGIBILITY \
COMPLETE_SAMPLE_COUNT \
VALIDATION_RESULT
do
if parse_contract_field "$field" "$validation_file"; then
case "$field" in
MODE) PARSED_MODE="$PARSED_FIELD_VALUE" ;;
MODE_STATUS) PARSED_MODE_STATUS="$PARSED_FIELD_VALUE" ;;
PROFILE_CONFIG_STATUS) PARSED_PROFILE_CONFIG_STATUS="$PARSED_FIELD_VALUE" ;;
PROFILE_RUNTIME_STATUS) PARSED_PROFILE_RUNTIME_STATUS="$PARSED_FIELD_VALUE" ;;
OBSERVABILITY_STATUS) PARSED_OBSERVABILITY_STATUS="$PARSED_FIELD_VALUE" ;;
OBSERVABILITY_REASON) PARSED_OBSERVABILITY_REASON="$PARSED_FIELD_VALUE" ;;
FUNCTIONAL_STATUS) PARSED_FUNCTIONAL_STATUS="$PARSED_FIELD_VALUE" ;;
SEMANTIC_STATUS) PARSED_SEMANTIC_STATUS="$PARSED_FIELD_VALUE" ;;
TIMING_STATUS) PARSED_TIMING_STATUS="$PARSED_FIELD_VALUE" ;;
MEASUREMENT_ELIGIBILITY) PARSED_MEASUREMENT_ELIGIBILITY="$PARSED_FIELD_VALUE" ;;
COMPLETE_SAMPLE_COUNT) PARSED_COMPLETE_SAMPLE_COUNT="$PARSED_FIELD_VALUE" ;;
VALIDATION_RESULT) PARSED_VALIDATION_RESULT="$PARSED_FIELD_VALUE" ;;
esac
elif [ "$CONTRACT_PARSE_ERROR" = "NONE" ]; then
CONTRACT_PARSE_ERROR="${field}_${PARSED_FIELD_ERROR}"
fi
done
if [ "$CONTRACT_PARSE_ERROR" = "NONE" ]; then
[ "$PARSED_MODE" = "$requested_mode" ] || CONTRACT_PARSE_ERROR="MODE_MISMATCH"
for field in \
"$PARSED_MODE_STATUS" \
"$PARSED_PROFILE_CONFIG_STATUS" \
"$PARSED_PROFILE_RUNTIME_STATUS" \
"$PARSED_OBSERVABILITY_STATUS" \
"$PARSED_FUNCTIONAL_STATUS" \
"$PARSED_SEMANTIC_STATUS"
do
case "$field" in
PASS|FAIL|NOT_DEMONSTRATED) ;;
*) CONTRACT_PARSE_ERROR="STATUS_VOCABULARY_INVALID"; break ;;
esac
done
fi
if [ "$CONTRACT_PARSE_ERROR" = "NONE" ]; then
[[ "$PARSED_OBSERVABILITY_REASON" =~ ^[A-Z][A-Z0-9_]*$ ]] || \
CONTRACT_PARSE_ERROR="OBSERVABILITY_REASON_MALFORMED"
[[ "$PARSED_TIMING_STATUS" =~ ^(NOT_VALID_|UNASSESSED_)[A-Z0-9_]+$ ]] || \
CONTRACT_PARSE_ERROR="TIMING_STATUS_UNRECOGNIZED"
[ "$PARSED_MEASUREMENT_ELIGIBILITY" = "NO" ] || \
CONTRACT_PARSE_ERROR="ELIGIBILITY_CONTRACT_VIOLATION"
[[ "$PARSED_COMPLETE_SAMPLE_COUNT" =~ ^[0-9]+$ ]] || \
CONTRACT_PARSE_ERROR="COMPLETE_SAMPLE_COUNT_MALFORMED"
[[ "$PARSED_VALIDATION_RESULT" =~ ^[A-Z][A-Z0-9_]*$ ]] || \
CONTRACT_PARSE_ERROR="VALIDATION_RESULT_MALFORMED"
if [ "$PARSED_OBSERVABILITY_STATUS" = "PASS" ]; then
[ "$PARSED_OBSERVABILITY_REASON" = "NONE" ] || \
CONTRACT_PARSE_ERROR="OBSERVABILITY_REASON_CONFLICT"
else
[ "$PARSED_OBSERVABILITY_REASON" != "NONE" ] || \
CONTRACT_PARSE_ERROR="OBSERVABILITY_REASON_MISSING"
fi
case "$validator_rc:$PARSED_VALIDATION_RESULT" in
0:PASS_CLEAN_SHUTDOWN|\
10:PASS_KNOWN_SHUTDOWN_ANOMALY|\
11:PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY|\
20:FUNCTIONAL_OR_SEMANTIC_NOT_DEMONSTRATED|\
21:FAIL_REQUIRED_INPUT)
;;
*)
CONTRACT_PARSE_ERROR="VALIDATION_RESULT_RC_CONFLICT"
;;
esac
fi
if [ "$requested_mode" = "MINIMAL" ]; then
for field in \
CANONICAL_STRUCTURAL_COMPLETENESS_STATUS \
MINIMAL_RUNTIME_CONFIG_STATUS
do
if parse_contract_field "$field" "$validation_file"; then
[ "$PARSED_FIELD_VALUE" = "PASS" ] || \
CONTRACT_PARSE_ERROR="${field}_NOT_PASS"
else
CONTRACT_PARSE_ERROR="${field}_${PARSED_FIELD_ERROR}"
fi
done
fi
if [ "$CONTRACT_PARSE_ERROR" != "NONE" ]; then
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_S03H3_CONTRACT"
BLOCK_REASON="$CONTRACT_PARSE_ERROR"
return 0
fi
case "$validator_rc" in
11|20|21)
FINAL_RC=20
FINAL_RESULT="S03H3_VALIDATION_INCONCLUSIVE"
BLOCK_REASON="VALIDATOR_RC_${validator_rc}"
return 0
;;
esac
for field in \
"$PARSED_MODE_STATUS" \
"$PARSED_PROFILE_CONFIG_STATUS" \
"$PARSED_PROFILE_RUNTIME_STATUS" \
"$PARSED_OBSERVABILITY_STATUS" \
"$PARSED_FUNCTIONAL_STATUS" \
"$PARSED_SEMANTIC_STATUS"
do
if [ "$field" != "PASS" ]; then
FINAL_RC=25
FINAL_RESULT="S03H3_CONTRACT_STATUS_NOT_PASS"
BLOCK_REASON="MANDATORY_STATUS_NOT_PASS"
return 0
fi
done
case "$PARSED_TIMING_STATUS" in
NOT_VALID_*)
FINAL_RC=14
FINAL_RESULT="FUNCTIONAL_AND_SEMANTIC_PASS_TIMING_NOT_VALID"
BLOCK_REASON="$PARSED_TIMING_STATUS"
;;
UNASSESSED_*)
FINAL_RC=15
FINAL_RESULT="FUNCTIONAL_AND_SEMANTIC_PASS_TIMING_UNASSESSED"
BLOCK_REASON="$PARSED_TIMING_STATUS"
;;
*)
FINAL_RC=22
FINAL_RESULT="INCONCLUSIVE_S03H3_CONTRACT"
BLOCK_REASON="TIMING_STATUS_UNRECOGNIZED"
;;
esac
}
run_s03h3() {
local duration="$1"
MODE="$2"
local expected_selected_hash
local patcher_log
local protected_before
local capture
local run_dir
local validation_log
local csv_report
local launcher_summary
local protected_after
local invariance_report
local protected_path_list
resolve_suite
[ -d "$SUITE" ] || return 50
PATCHER="$SUITE/patchers/configuration/build_s03h3_minimal_from_s03h1_test.sh"
VALIDATOR="$SUITE/scenarios/S03H3_execution_profiles/validate.sh"
MINIMAL_VERIFIER="$SUITE/scenarios/S03H3_execution_profiles/verify_s03h3_minimal_structure.py"
MINIMAL_CONFIG="$SUITE/configurations/generated/S03H3/from_patcher/smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte"
TEST_CONFIG="$SUITE/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
DIAGNOSTIC_CONFIG="$SUITE/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
RUNNER="$SUITE/common/marte_runner.sh"
LIVE_PATHS="$SUITE/common/paths.sh"
CSV_EXPORTER="$SUITE/scenarios/S03H1_execution_profiles/export_s03_csv.py"
PARSER="$SUITE/scenarios/S03_semantic_sentinels/parse_s03_binary.py"
case "$MODE" in
MINIMAL)
SELECTED_CONFIG="$MINIMAL_CONFIG"
expected_selected_hash="$EXPECTED_MINIMAL_CONFIG_SHA256"
;;
TEST)
SELECTED_CONFIG="$TEST_CONFIG"
expected_selected_hash="$EXPECTED_TEST_CONFIG_SHA256"
;;
DIAGNOSTIC)
SELECTED_CONFIG="$DIAGNOSTIC_CONFIG"
expected_selected_hash="$EXPECTED_DIAGNOSTIC_CONFIG_SHA256"
;;
*) return 2 ;;
esac
verify_historical_baseline || return 52
verify_candidate_components || return 51
verify_hash "$SELECTED_CONFIG" "$expected_selected_hash" "SELECTED_${MODE}_CONFIG" || return 51
build_protected_paths
local protected_path
for protected_path in "${PROTECTED_PATHS[@]}"; do
[ -f "$protected_path" ] || return 51
done
WORK_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/s03h3_launcher.XXXXXX")" || return 53
trap 'rm -rf -- "$WORK_ROOT"' EXIT HUP INT TERM
patcher_log="$WORK_ROOT/patcher.log"
protected_before="$WORK_ROOT/protected_before.sha256"
capture="$WORK_ROOT/runner_capture.log"
"$PATCHER" >"$patcher_log" 2>&1
PATCHER_RC=$?
[ "$PATCHER_RC" -eq 0 ] || return 54
verify_hash "$MINIMAL_CONFIG" "$EXPECTED_MINIMAL_CONFIG_SHA256" "MINIMAL_REGENERATION" || return 55
python3 "$MINIMAL_VERIFIER" CANONICAL "$TEST_CONFIG" "$MINIMAL_CONFIG" >/dev/null 2>&1 || return 55
write_protected_snapshot "$protected_before" || return 56
[ -f "$LIVE_PATHS" ] || return 57
# shellcheck source=/dev/null
source "$LIVE_PATHS" || return 57
CONSTANT_GAM_LIB_DIR="$MARTE2_COMPONENTS_ROOT/Build/$MARTE_TARGET/Components/GAMs/ConstantGAM"
CONSTANT_GAM_LIB="$CONSTANT_GAM_LIB_DIR/ConstantGAM.so"
[ -f "$CONSTANT_GAM_LIB" ] || return 58
case ":${LD_LIBRARY_PATH-}:" in
*":${CONSTANT_GAM_LIB_DIR}:"*) ;;
*) LD_LIBRARY_PATH="${CONSTANT_GAM_LIB_DIR}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" ;;
esac
export LD_LIBRARY_PATH
printf '============================================================\n'
printf 'S03-H3 EXECUTION PROFILES\n'
printf '============================================================\n'
printf 'MODE=%s\n' "$MODE"
printf 'DURATION=%s\n' "$duration"
printf 'DEPENDENCY_INTEGRITY_STATUS=PASS\n'
"$RUNNER" "S03H3_${MODE}_launcher" "$SELECTED_CONFIG" "State1" "$duration" \
2>&1 | tee "$capture"
RUNNER_CODES=("${PIPESTATUS[@]}")
RUNNER_RC="${RUNNER_CODES[0]}"
CAPTURE_TEE_RC="${RUNNER_CODES[1]}"
[ "$RUNNER_RC" -eq 0 ] || return 60
[ "$CAPTURE_TEE_RC" -eq 0 ] || return 61
run_dir="$(sed -n 's/^RUN_DIR=//p' "$capture" | tail -n 1)"
case "$run_dir" in
"$SUITE"/runs/*_S03H3_${MODE}_launcher) ;;
*) return 62 ;;
esac
[ -d "$run_dir" ] || return 62
RUN_LOG="$run_dir/run.log"
RUN_CONFIG="$run_dir/configuration.marte"
RUNNER_SUMMARY="$run_dir/runner_summary.txt"
RUN_HASHES="$run_dir/hashes.sha256"
OUTPUT_FILE="$run_dir/s03_output.bin"
CSV_FILE="$run_dir/s03_output.csv"
MODE_FILE="$run_dir/s03h3_mode.txt"
validation_log="$run_dir/validation.txt"
csv_report="$run_dir/csv_export.txt"
launcher_summary="$run_dir/launcher_summary.txt"
PROTECTED_BEFORE_RUN="$run_dir/protected_baseline_before.sha256"
protected_after="$run_dir/protected_baseline_after.sha256"
invariance_report="$run_dir/protected_baseline_invariance.txt"
protected_path_list="$run_dir/protected_successor_paths.txt"
for protected_path in "$RUN_LOG" "$RUN_CONFIG" "$RUNNER_SUMMARY" "$RUN_HASHES" "$OUTPUT_FILE"; do
[ -f "$protected_path" ] || return 63
done
cp -- "$protected_before" "$PROTECTED_BEFORE_RUN" || return 64
write_protected_path_list "$protected_path_list" || return 64
{
printf 'SCENARIO=S03H3\n'
printf 'MODE=%s\n' "$MODE"
printf 'SELECTED_CONFIG_SHA256=%s\n' "$expected_selected_hash"
} >"$MODE_FILE" || return 64
sha256sum -- \
"$MODE_FILE" \
"${BASH_SOURCE[0]}" \
"$PATCHER" \
"$VALIDATOR" \
"$MINIMAL_VERIFIER" \
"$CSV_EXPORTER" \
"$PARSER" \
"$RUN_LOG" \
"$RUNNER_SUMMARY" \
"$OUTPUT_FILE" \
"$PROTECTED_BEFORE_RUN" \
"$protected_path_list" \
"${PROTECTED_PATHS[@]}" \
>>"$RUN_HASHES" || return 64
timeout 30s "$VALIDATOR" "$MODE" "$run_dir" 2>&1 | tee "$validation_log"
VALIDATION_CODES=("${PIPESTATUS[@]}")
VALIDATOR_RC="${VALIDATION_CODES[0]}"
VALIDATION_TEE_RC="${VALIDATION_CODES[1]}"
"$CSV_EXPORTER" "$OUTPUT_FILE" "$CSV_FILE" >"$csv_report" 2>&1
CSV_EXPORT_RC=$?
if [ "$CSV_EXPORT_RC" -eq 0 ]; then
CSV_EXPORT_STATUS="PASS"
else
CSV_EXPORT_STATUS="FAIL"
fi
if write_protected_snapshot "$protected_after" && \
cmp -s -- "$PROTECTED_BEFORE_RUN" "$protected_after"; then
PROTECTED_BASELINE_INVARIANCE="PASS"
else
PROTECTED_BASELINE_INVARIANCE="FAIL"
fi
{
printf 'PROTECTED_BASELINE_INVARIANCE=%s\n' "$PROTECTED_BASELINE_INVARIANCE"
printf 'PROTECTED_SUCCESSOR_BASELINE_COUNT=27\n'
} >"$invariance_report"
for protected_path in "$validation_log" "$csv_report" "$protected_after" "$invariance_report"; do
[ -f "$protected_path" ] && sha256sum -- "$protected_path" >>"$RUN_HASHES"
done
[ -f "$CSV_FILE" ] && sha256sum -- "$CSV_FILE" >>"$RUN_HASHES"
classify_s03h3_run \
"$VALIDATOR_RC" \
"$VALIDATION_TEE_RC" \
"$validation_log" \
"$CSV_EXPORT_RC" \
"$PROTECTED_BASELINE_INVARIANCE" \
"PASS" \
"$MODE"
{
printf '============================================================\n'
printf 'RIEPILOGO LAUNCHER S03-H3\n'
printf '============================================================\n'
printf 'SCENARIO=S03H3\n'
printf 'MODE=%s\n' "$MODE"
printf 'DURATION=%s\n' "$duration"
printf 'RUN_DIR=%s\n' "$run_dir"
printf 'RUNNER_RC=%s\n' "$RUNNER_RC"
printf 'VALIDATOR_RC=%s\n' "$VALIDATOR_RC"
printf 'CSV_EXPORT_RC=%s\n' "$CSV_EXPORT_RC"
printf 'DEPENDENCY_INTEGRITY_STATUS=PASS\n'
printf 'PROTECTED_BASELINE_INVARIANCE=%s\n' "$PROTECTED_BASELINE_INVARIANCE"
printf 'CSV_EXPORT_STATUS=%s\n' "$CSV_EXPORT_STATUS"
printf 'MODE_STATUS=%s\n' "$PARSED_MODE_STATUS"
printf 'PROFILE_CONFIG_STATUS=%s\n' "$PARSED_PROFILE_CONFIG_STATUS"
printf 'PROFILE_RUNTIME_STATUS=%s\n' "$PARSED_PROFILE_RUNTIME_STATUS"
printf 'OBSERVABILITY_STATUS=%s\n' "$PARSED_OBSERVABILITY_STATUS"
printf 'FUNCTIONAL_STATUS=%s\n' "$PARSED_FUNCTIONAL_STATUS"
printf 'SEMANTIC_STATUS=%s\n' "$PARSED_SEMANTIC_STATUS"
printf 'TIMING_STATUS=%s\n' "$PARSED_TIMING_STATUS"
printf 'MEASUREMENT_ELIGIBILITY=NO\n'
printf 'BLOCK_REASON=%s\n' "$BLOCK_REASON"
printf 'FINAL_RESULT=%s\n' "$FINAL_RESULT"
printf 'FINAL_RC=%s\n' "$FINAL_RC"
printf 'PERFORMANCE_MEASUREMENT_AUTHORIZED=NO\n'
printf 'S04_AUTHORIZED=NO\n'
printf 'MD00_AUTHORIZED=NO\n'
} | tee "$launcher_summary"
SUMMARY_CODES=("${PIPESTATUS[@]}")
if [ "${SUMMARY_CODES[1]}" -ne 0 ]; then
FINAL_RC=69
FINAL_RESULT="FAIL_LAUNCHER_SUMMARY"
BLOCK_REASON="LAUNCHER_SUMMARY_NOT_WRITTEN"
else
sha256sum -- "$launcher_summary" >>"$RUN_HASHES"
fi
printf 'RUN_DIR=%s\n' "$run_dir"
printf 'FINAL_RESULT=%s\n' "$FINAL_RESULT"
printf 'FINAL_RC=%s\n' "$FINAL_RC"
rm -rf -- "$WORK_ROOT"
WORK_ROOT=""
trap - EXIT HUP INT TERM
return "$FINAL_RC"
}
if [ "${S03H3_LAUNCHER_LIB_ONLY:-0}" = "1" ]; then
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
return 0
fi
printf 'S03H3_LAUNCHER_LIB_ONLY=1 richiede source.\n' >&2
exit 3
fi
case "$#" in
1)
case "$1" in
-h|--help)
print_usage
exit 0
;;
*)
print_usage >&2
exit 2
;;
esac
;;
3)
[ "$1" = "S03H3" ] || {
print_usage >&2
exit 2
}
is_positive_decimal "$2" || {
printf 'Durata non valida: %s\n' "$2" >&2
exit 2
}
case "$3" in
MINIMAL|TEST|DIAGNOSTIC) ;;
*)
printf 'Profilo non riconosciuto: %s\n' "$3" >&2
exit 2
;;
esac
run_s03h3 "$2" "$3"
exit $?
;;
*)
print_usage >&2
exit 2
;;
esac
+512
View File
@@ -0,0 +1,512 @@
#!/usr/bin/env bash
set -u
set -o pipefail
readonly DEFAULT_DURATION_SECONDS="8"
readonly TEST_OVERRIDE_ACK_VALUE="YES"
CAPTURE=""
STATUS_DIR=""
LAUNCH_ACTIVE="NO"
INTERRUPT_PENDING="NO"
SELECTED_SCENARIO=""
DISPLAY_SCENARIO=""
SELECTED_PROFILE="N/A"
SELECTED_DURATION=""
LAST_LAUNCHER_RC="NOT_AVAILABLE"
LAST_RUN_DIR="NOT_AVAILABLE"
LAST_RUN_DIR_STATUS="NOT_AVAILABLE"
cleanup_temporary_files() {
if [ -n "$CAPTURE" ]; then
rm -f -- "$CAPTURE"
CAPTURE=""
fi
if [ -n "$STATUS_DIR" ]; then
rm -f -- \
"$STATUS_DIR/launcher_rc" \
"$STATUS_DIR/tee_rc"
rmdir -- "$STATUS_DIR" 2>/dev/null || true
STATUS_DIR=""
fi
}
fatal_wrapper_error() {
local exit_code="$1"
local status="$2"
local message="$3"
printf 'ERRORE S03-H3: %s\n' "$message" >&2
if [ "$exit_code" -eq 70 ]; then
printf 'S03_H3_STARTUP_STATUS=%s\n' "$status" >&2
else
printf 'S03_H3_MENU_LIFECYCLE_STATUS=%s\n' "$status" >&2
fi
exit "$exit_code"
}
handle_int() {
if [ "$LAUNCH_ACTIVE" = "YES" ]; then
INTERRUPT_PENDING="YES"
return 0
fi
printf '\nInterruzione richiesta: nessun nuovo benchmark verra avviato.\n' >&2
printf 'LAUNCHER_EXECUTED=NO\n' >&2
exit 130
}
handle_hup() {
printf '\nS03-H3 terminato da SIGHUP.\n' >&2
exit 129
}
handle_term() {
printf '\nS03-H3 terminato da SIGTERM.\n' >&2
exit 143
}
trap cleanup_temporary_files EXIT
trap handle_hup HUP
trap handle_int INT
trap handle_term TERM
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
if [ -z "$SCRIPT_DIR" ]; then
fatal_wrapper_error 70 "FAIL_SUITE_ROOT" \
"impossibile determinare la directory del menu"
fi
INSTALLED_SUITE="$(cd -- "$SCRIPT_DIR/.." && pwd -P)"
if [ -z "$INSTALLED_SUITE" ]; then
fatal_wrapper_error 70 "FAIL_SUITE_ROOT" \
"impossibile determinare la radice della suite"
fi
readonly -a COMPLETE_OVERRIDE_PATHS=(
"launcher/marte_benchmark_launcher.sh"
"configurations/generated/S00/smoke_s0_core.marte"
"configurations/generated/S01/from_patcher/smoke_s1_timing_from_s00.marte"
"configurations/generated/S02/from_patcher/smoke_s2_binary_recording_from_s01.marte"
"configurations/generated/S03/from_patcher/smoke_s3_semantic_sentinels_from_s02.marte"
"patchers/configuration/build_s01_from_s00.sh"
"patchers/configuration/build_s02_from_s01.sh"
"patchers/configuration/build_s03_from_s02.sh"
"scenarios/S02_binary_recording/validate.sh"
"scenarios/S03_semantic_sentinels/validate.sh"
"scenarios/S03_semantic_sentinels/parse_s03_binary.py"
"common/marte_runner.sh"
"common/paths.sh"
"common/environment.sh"
"launcher/marte_benchmark_launcher_s03h1.sh"
"patchers/configuration/build_s03h1_from_s02.sh"
"scenarios/S03H1_execution_profiles/validate.sh"
"scenarios/S03H1_execution_profiles/verify_s03h1_config.py"
"scenarios/S03H1_execution_profiles/export_s03_csv.py"
"configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
"configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
"launcher/marte_benchmark_launcher_s03h3.sh"
"patchers/configuration/build_s03h3_minimal_from_s03h1_test.sh"
"scenarios/S03H3_execution_profiles/validate.sh"
"scenarios/S03H3_execution_profiles/verify_s03h3_minimal_structure.py"
"configurations/generated/S03H3/from_patcher/smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte"
"launcher/marte_benchmark_menu.sh"
)
verify_complete_override_root() {
local relative_path
local missing_count=0
for relative_path in "${COMPLETE_OVERRIDE_PATHS[@]}"; do
if [ ! -f "$SUITE/$relative_path" ]; then
printf 'ERRORE S03-H3: fixture incompleta, manca %s\n' \
"$relative_path" >&2
missing_count=$((missing_count + 1))
fi
done
[ "$missing_count" -eq 0 ]
}
OVERRIDE_ACTIVE="NO"
if [ -n "${MARTE_BENCHMARK_SUITE_ROOT:-}" ]; then
if [ "${S03H3_TEST_ONLY_OVERRIDE:-NO}" != "$TEST_OVERRIDE_ACK_VALUE" ]; then
fatal_wrapper_error 70 "FAIL_TEST_OVERRIDE_NOT_ACKNOWLEDGED" \
"MARTE_BENCHMARK_SUITE_ROOT richiede S03H3_TEST_ONLY_OVERRIDE=YES"
fi
if [[ "$MARTE_BENCHMARK_SUITE_ROOT" != /* ]]; then
fatal_wrapper_error 70 "FAIL_TEST_OVERRIDE_NOT_ABSOLUTE" \
"la radice di override deve essere un percorso assoluto"
fi
if [ ! -d "$MARTE_BENCHMARK_SUITE_ROOT" ]; then
fatal_wrapper_error 70 "FAIL_TEST_OVERRIDE_ROOT" \
"la radice di override non esiste"
fi
SUITE="$(cd -- "$MARTE_BENCHMARK_SUITE_ROOT" && pwd -P)"
if [ -z "$SUITE" ] || [ "$SUITE" = "/" ]; then
fatal_wrapper_error 70 "FAIL_TEST_OVERRIDE_ROOT" \
"la radice di override non e sicura"
fi
OVERRIDE_ACTIVE="YES"
if ! verify_complete_override_root; then
fatal_wrapper_error 70 "FAIL_INCOMPLETE_TEST_SUITE" \
"l'override e ammesso soltanto per una suite completa di 27 elementi"
fi
elif [ "${S03H3_TEST_ONLY_OVERRIDE:-NO}" = "$TEST_OVERRIDE_ACK_VALUE" ]; then
fatal_wrapper_error 70 "FAIL_TEST_OVERRIDE_ROOT_MISSING" \
"S03H3_TEST_ONLY_OVERRIDE=YES richiede MARTE_BENCHMARK_SUITE_ROOT"
else
SUITE="$INSTALLED_SUITE"
fi
readonly SUITE
readonly OVERRIDE_ACTIVE
STANDARD_LAUNCHER="$SUITE/launcher/marte_benchmark_launcher.sh"
S03H3_LAUNCHER="$SUITE/launcher/marte_benchmark_launcher_s03h3.sh"
readonly STANDARD_LAUNCHER
readonly S03H3_LAUNCHER
if [ ! -x "$STANDARD_LAUNCHER" ]; then
fatal_wrapper_error 70 "FAIL_REQUIRED_LAUNCHER" \
"launcher standard assente o non eseguibile: $STANDARD_LAUNCHER"
fi
if [ ! -x "$S03H3_LAUNCHER" ]; then
fatal_wrapper_error 70 "FAIL_REQUIRED_LAUNCHER" \
"launcher S03-H3 assente o non eseguibile: $S03H3_LAUNCHER"
fi
export MARTE_BENCHMARK_SUITE_ROOT="$SUITE"
is_positive_decimal() {
local value="$1"
[[ "$value" =~ ^[0-9]+$ ]] && [[ "$value" =~ [1-9] ]]
}
normalize_decimal() {
local value="$1"
while [ "${#value}" -gt 1 ] && [ "${value:0:1}" = "0" ]; do
value="${value#0}"
done
printf '%s' "$value"
}
print_main_menu() {
printf '\n============================================================\n'
printf 'MARTe2 Benchmark Suite — menu S03-H3\n'
printf '============================================================\n'
printf '1) S01\n'
printf '2) S02\n'
printf '3) S03\n'
printf '4) S03-H3\n'
printf '0) Exit\n'
printf 'Selection: '
}
read_duration() {
local value
printf 'Simulation duration [seconds] (default: %s): ' \
"$DEFAULT_DURATION_SECONDS"
if ! IFS= read -r value; then
return 1
fi
if [ -z "$value" ]; then
SELECTED_DURATION="$DEFAULT_DURATION_SECONDS"
return 0
fi
if ! is_positive_decimal "$value"; then
printf 'Durata non valida: usare solo un intero decimale positivo.\n' >&2
printf 'LAUNCHER_EXECUTED=NO\n'
return 2
fi
SELECTED_DURATION="$(normalize_decimal "$value")"
return 0
}
read_s03h3_profile() {
local value
printf '1) MINIMAL\n'
printf '2) TEST\n'
printf '3) DIAGNOSTIC\n'
printf 'Profile: '
if ! IFS= read -r value; then
return 1
fi
case "$value" in
1)
SELECTED_PROFILE="MINIMAL"
;;
2)
SELECTED_PROFILE="TEST"
;;
3)
SELECTED_PROFILE="DIAGNOSTIC"
;;
*)
printf 'Profilo non valido. Nessun launcher eseguito.\n' >&2
printf 'LAUNCHER_EXECUTED=NO\n'
return 2
;;
esac
return 0
}
confirm_execution() {
local answer
printf '\nScenario: %s\n' "$DISPLAY_SCENARIO"
printf 'Profile: %s\n' "$SELECTED_PROFILE"
printf 'Duration: %s s\n\n' "$SELECTED_DURATION"
printf 'Start execution? [y/N]: '
if ! IFS= read -r answer; then
return 1
fi
case "$answer" in
y|Y)
return 0
;;
*)
printf 'Esecuzione annullata.\n'
printf 'LAUNCHER_EXECUTED=NO\n'
return 2
;;
esac
}
delegate_launcher() {
case "$SELECTED_SCENARIO" in
S01)
"$STANDARD_LAUNCHER" S01 "$SELECTED_DURATION"
;;
S02)
"$STANDARD_LAUNCHER" S02 "$SELECTED_DURATION"
;;
S03)
"$STANDARD_LAUNCHER" S03 "$SELECTED_DURATION"
;;
S03H3)
"$S03H3_LAUNCHER" S03H3 "$SELECTED_DURATION" "$SELECTED_PROFILE"
;;
*)
return 125
;;
esac
}
read_recorded_status() {
local status_file="$1"
local fallback="$2"
local value=""
if [ -s "$status_file" ]; then
IFS= read -r value <"$status_file" || value=""
fi
if [[ "$value" =~ ^[0-9]+$ ]]; then
printf '%s' "$value"
else
printf '%s' "$fallback"
fi
}
observe_run_dir() {
local observed=""
local observed_resolved=""
local observed_parent=""
local observed_name=""
observed="$(sed -n 's/^RUN_DIR=//p' "$CAPTURE" | tail -n 1)"
if [ -z "$observed" ]; then
LAST_RUN_DIR="NOT_AVAILABLE"
LAST_RUN_DIR_STATUS="NOT_AVAILABLE"
elif [[ "$observed" != /* ]]; then
LAST_RUN_DIR="$observed"
LAST_RUN_DIR_STATUS="REPORTED_BUT_INVALID"
elif [ ! -d "$observed" ]; then
LAST_RUN_DIR="$observed"
LAST_RUN_DIR_STATUS="REPORTED_BUT_NOT_FOUND"
elif [ "$SELECTED_SCENARIO" = "S03H3" ]; then
observed_resolved="$(cd -- "$observed" && pwd -P)" || observed_resolved=""
observed_parent="$(dirname -- "$observed_resolved")"
observed_name="$(basename -- "$observed_resolved")"
if [ "$observed_parent" = "$SUITE/runs" ] &&
[[ "$observed_name" = *_S03H3_${SELECTED_PROFILE}_launcher ]]; then
LAST_RUN_DIR="$observed_resolved"
LAST_RUN_DIR_STATUS="FOUND"
else
LAST_RUN_DIR="$observed"
LAST_RUN_DIR_STATUS="REPORTED_BUT_INVALID"
fi
else
LAST_RUN_DIR="$observed"
LAST_RUN_DIR_STATUS="FOUND"
fi
}
print_execution_summary() {
printf '\n============================================================\n'
printf 'Riepilogo esecuzione S03-H3\n'
printf '============================================================\n'
printf 'Scenario: %s\n' "$DISPLAY_SCENARIO"
printf 'Profile: %s\n' "$SELECTED_PROFILE"
printf 'Duration: %s s\n' "$SELECTED_DURATION"
printf 'Launcher RC: %s\n' "$LAST_LAUNCHER_RC"
printf 'RUN_DIR: %s\n' "$LAST_RUN_DIR"
printf 'RUN_DIR status: %s\n' "$LAST_RUN_DIR_STATUS"
printf 'LAST_LAUNCHER_RC=%s\n' "$LAST_LAUNCHER_RC"
printf 'RUN_DIR_STATUS=%s\n' "$LAST_RUN_DIR_STATUS"
printf 'RUN_DIR=%s\n' "$LAST_RUN_DIR"
printf 'MENU_RECLASSIFIES_LAUNCHER_RESULT=NO\n'
}
invoke_selected_launcher() {
local -a pipeline_status
local launcher_rc
local tee_rc
cleanup_temporary_files
CAPTURE="$(mktemp "${TMPDIR:-/tmp}/marte_benchmark_menu.capture.XXXXXX")" ||
fatal_wrapper_error 71 "FAIL_CAPTURE_CREATION" \
"impossibile creare il file temporaneo di cattura"
STATUS_DIR="$(mktemp -d "${TMPDIR:-/tmp}/marte_benchmark_menu.status.XXXXXX")" || {
cleanup_temporary_files
fatal_wrapper_error 71 "FAIL_CAPTURE_CREATION" \
"impossibile creare lo stato temporaneo della cattura"
}
INTERRUPT_PENDING="NO"
LAUNCH_ACTIVE="YES"
(
delegate_launcher
launcher_rc=$?
printf '%s\n' "$launcher_rc" >"$STATUS_DIR/launcher_rc"
exit "$launcher_rc"
) 2>&1 | (
tee "$CAPTURE"
tee_rc=$?
printf '%s\n' "$tee_rc" >"$STATUS_DIR/tee_rc"
exit "$tee_rc"
)
pipeline_status=("${PIPESTATUS[@]}")
LAUNCH_ACTIVE="NO"
launcher_rc="$(read_recorded_status \
"$STATUS_DIR/launcher_rc" "${pipeline_status[0]:-125}")"
tee_rc="$(read_recorded_status \
"$STATUS_DIR/tee_rc" "${pipeline_status[1]:-125}")"
LAST_LAUNCHER_RC="$launcher_rc"
observe_run_dir
print_execution_summary
if [ "$INTERRUPT_PENDING" = "YES" ]; then
printf 'S03_H3_MENU_LIFECYCLE_STATUS=INTERRUPTED\n' >&2
printf 'LAUNCHER_RC_RECLASSIFIED=NO\n' >&2
cleanup_temporary_files
exit 130
fi
if [ "$tee_rc" -ne 0 ]; then
printf 'CAPTURE_TEE_STATUS=FAIL\n' >&2
printf 'CAPTURE_TEE_RC=%s\n' "$tee_rc" >&2
printf 'S03_H3_MENU_LIFECYCLE_STATUS=FAIL_CAPTURE_TEE\n' >&2
cleanup_temporary_files
exit 72
fi
printf 'CAPTURE_TEE_STATUS=PASS\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=CONTINUE\n'
cleanup_temporary_files
}
printf 'S03_H3_STARTUP_STATUS=PASS\n'
printf 'S03_H3_SUITE_ROOT=%s\n' "$SUITE"
printf 'S03_H3_TEST_OVERRIDE_ACTIVE=%s\n' "$OVERRIDE_ACTIVE"
while true; do
print_main_menu
if ! IFS= read -r menu_choice; then
printf '\nEOF: uscita pulita dal menu.\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=EXIT_EOF\n'
exit 0
fi
SELECTED_PROFILE="N/A"
case "$menu_choice" in
0)
printf 'Uscita dal menu S03-H3.\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=EXIT_USER\n'
exit 0
;;
1)
SELECTED_SCENARIO="S01"
DISPLAY_SCENARIO="S01"
;;
2)
SELECTED_SCENARIO="S02"
DISPLAY_SCENARIO="S02"
;;
3)
SELECTED_SCENARIO="S03"
DISPLAY_SCENARIO="S03"
;;
4)
SELECTED_SCENARIO="S03H3"
DISPLAY_SCENARIO="S03-H3"
;;
*)
printf 'Selezione non valida. Nessun launcher eseguito.\n' >&2
printf 'LAUNCHER_EXECUTED=NO\n'
continue
;;
esac
read_duration
duration_status=$?
if [ "$duration_status" -eq 1 ]; then
printf '\nEOF prima dell esecuzione: azione annullata.\n'
printf 'LAUNCHER_EXECUTED=NO\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=EXIT_EOF\n'
exit 0
elif [ "$duration_status" -ne 0 ]; then
continue
fi
if [ "$SELECTED_SCENARIO" = "S03H3" ]; then
read_s03h3_profile
profile_status=$?
if [ "$profile_status" -eq 1 ]; then
printf '\nEOF prima dell esecuzione: azione annullata.\n'
printf 'LAUNCHER_EXECUTED=NO\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=EXIT_EOF\n'
exit 0
elif [ "$profile_status" -ne 0 ]; then
continue
fi
fi
confirm_execution
confirmation_status=$?
if [ "$confirmation_status" -eq 1 ]; then
printf '\nEOF prima della conferma: azione annullata.\n'
printf 'LAUNCHER_EXECUTED=NO\n'
printf 'S03_H3_MENU_LIFECYCLE_STATUS=EXIT_EOF\n'
exit 0
elif [ "$confirmation_status" -ne 0 ]; then
continue
fi
printf 'LAUNCHER_EXECUTED=YES\n'
invoke_selected_launcher
done
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -eu
SUITE="/home/dariodf/Scrivania/MARTE/marte-benchmark-suite"
DEFAULT_SOURCE="$SUITE/configurations/generated/S00/smoke_s0_core.marte"
DEFAULT_OUTPUT="$SUITE/configurations/generated/S01/from_patcher/smoke_s1_timing_from_s00.marte"
SOURCE="${1:-$DEFAULT_SOURCE}"
OUTPUT="${2:-$DEFAULT_OUTPUT}"
EXPECTED_SOURCE_SHA256="49a9b48595aab88cd8fd11d1e58ace4ab11d7d0a649e9b143bdc25c8866253d5"
EXPECTED_OUTPUT_SHA256="1513e1ca72eba56d53079ac7328908f12ddc72772456e2d75bcbacb6465d2427"
fail() {
code="$1"
shift
printf "ERRORE: %s\n" "$*" >&2
exit "$code"
}
if [ ! -f "$SOURCE" ]; then
fail 2 "configurazione sorgente non trovata: $SOURCE"
fi
SOURCE_SHA256="$(sha256sum "$SOURCE" | awk '{print $1}')"
if [ "$SOURCE_SHA256" != "$EXPECTED_SOURCE_SHA256" ]; then
fail 3 "hash della configurazione sorgente non riconosciuto: $SOURCE_SHA256"
fi
if grep -q 'State1_Thread1_CycleTime' "$SOURCE"; then
fail 4 "la configurazione sorgente contiene già il segnale S01"
fi
OUTPUT_DIR="$(dirname "$OUTPUT")"
mkdir -p "$OUTPUT_DIR"
if [ -e "$OUTPUT" ]; then
if [ ! -f "$OUTPUT" ]; then
fail 5 "il percorso di output esiste ma non è un file regolare: $OUTPUT"
fi
OUTPUT_SHA256="$(sha256sum "$OUTPUT" | awk '{print $1}')"
if [ "$OUTPUT_SHA256" = "$EXPECTED_OUTPUT_SHA256" ]; then
printf "RISULTATO GIÀ PRESENTE E CORRETTO\n"
printf "Output: %s\n" "$OUTPUT"
printf "SHA-256: %s\n" "$OUTPUT_SHA256"
exit 0
fi
fail 6 "il file di output esiste ma ha un contenuto inatteso: $OUTPUT"
fi
TMP="${OUTPUT}.tmp.$$"
cleanup() {
if [ -f "$TMP" ]; then
rm -f "$TMP"
fi
}
trap cleanup EXIT HUP INT TERM
set +e
awk '
BEGIN {
inGamDisplay = 0
inInputSignals = 0
inOutputSignals = 0
inputInsertions = 0
outputInsertions = 0
}
{
if ($0 ~ /^ \+GAMDisplay[[:space:]]*=[[:space:]]*\{[[:space:]]*$/) {
inGamDisplay = 1
}
if (inGamDisplay &&
$0 ~ /^ InputSignals[[:space:]]*=[[:space:]]*\{[[:space:]]*$/) {
inInputSignals = 1
}
if (inGamDisplay &&
$0 ~ /^ OutputSignals[[:space:]]*=[[:space:]]*\{[[:space:]]*$/) {
inOutputSignals = 1
}
if (inInputSignals &&
$0 ~ /^ \}[[:space:]]*$/) {
print " State1_Thread1_CycleTime = {"
print " Alias = State1.Thread1_CycleTime"
print " DataSource = Timings"
print " Type = uint32"
print " }"
inputInsertions++
inInputSignals = 0
}
if (inOutputSignals &&
$0 ~ /^ \}[[:space:]]*$/) {
print " State1_Thread1_CycleTime = {"
print " DataSource = Display"
print " Type = uint32"
print " }"
print " } "
outputInsertions++
inOutputSignals = 0
next
}
print
if (inGamDisplay &&
$0 ~ /^ \}[[:space:]]*$/) {
inGamDisplay = 0
}
}
END {
if (inputInsertions != 1 || outputInsertions != 1) {
exit 42
}
}
' "$SOURCE" > "$TMP"
AWK_RC=$?
set -e
if [ "$AWK_RC" -ne 0 ]; then
fail 7 "trasformazione non applicata in modo univoco; codice awk: $AWK_RC"
fi
GENERATED_SHA256="$(sha256sum "$TMP" | awk '{print $1}')"
if [ "$GENERATED_SHA256" != "$EXPECTED_OUTPUT_SHA256" ]; then
fail 8 "output generato differente dalla S01 manuale: $GENERATED_SHA256"
fi
mv "$TMP" "$OUTPUT"
trap - EXIT HUP INT TERM
printf "PATCH S00 -> S01 COMPLETATA\n"
printf "Sorgente: %s\n" "$SOURCE"
printf "Output: %s\n" "$OUTPUT"
printf "SHA-256 sorgente: %s\n" "$SOURCE_SHA256"
printf "SHA-256 output: %s\n" "$GENERATED_SHA256"
+224
View File
@@ -0,0 +1,224 @@
#!/usr/bin/env bash
set -u
SUITE="/home/dariodf/Scrivania/MARTE/marte-benchmark-suite"
SOURCE_CONFIG="$SUITE/configurations/generated/S01/from_patcher/smoke_s1_timing_from_s00.marte"
OUTPUT_DIR="$SUITE/configurations/generated/S02/from_patcher"
OUTPUT_CONFIG="$OUTPUT_DIR/smoke_s2_binary_recording_from_s01.marte"
EXPECTED_SOURCE_SHA256="1513e1ca72eba56d53079ac7328908f12ddc72772456e2d75bcbacb6465d2427"
if [ ! -f "$SOURCE_CONFIG" ]; then
printf "SOURCE_STATUS=NOT_FOUND\n"
printf "SOURCE_CONFIG=%s\n" "$SOURCE_CONFIG"
exit 20
fi
ACTUAL_SOURCE_SHA256="$(sha256sum -- "$SOURCE_CONFIG" | awk '{print $1}')"
printf "SOURCE_CONFIG=%s\n" "$SOURCE_CONFIG"
printf "EXPECTED_SOURCE_SHA256=%s\n" "$EXPECTED_SOURCE_SHA256"
printf "ACTUAL_SOURCE_SHA256=%s\n" "$ACTUAL_SOURCE_SHA256"
if [ "$ACTUAL_SOURCE_SHA256" != "$EXPECTED_SOURCE_SHA256" ]; then
printf "SOURCE_HASH_STATUS=MISMATCH\n"
exit 21
fi
printf "SOURCE_HASH_STATUS=MATCH\n"
mkdir -p -- "$OUTPUT_DIR"
TEMP_CONFIG="$(mktemp "$OUTPUT_DIR/.s02_candidate_XXXXXX.marte")"
if [ -z "$TEMP_CONFIG" ]; then
printf "TEMPORARY_FILE_STATUS=FAIL\n"
exit 22
fi
trap 'rm -f -- "$TEMP_CONFIG"' EXIT
python3 - "$SOURCE_CONFIG" "$TEMP_CONFIG" <<'PY'
import re
import sys
from pathlib import Path
source = Path(sys.argv[1])
destination = Path(sys.argv[2])
text = source.read_text(encoding="utf-8")
for forbidden in (
"+IOGAM_Writer =",
"+FileWriter =",
"__MARTE_RUN_DIR__",
):
if forbidden in text:
print("SOURCE_ALREADY_CONTAINS_S02_CONTENT=" + forbidden)
sys.exit(30)
writer_gam = """ +IOGAM_Writer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = FileWriter
Type = uint32
}
Time = {
DataSource = FileWriter
Type = uint32
}
State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
}
}
"""
file_writer = """ +FileWriter = {
Class = FileDataSource::FileWriter
NumberOfBuffers = 100
CPUMask = 0x1
StackSize = 10000000
Filename = "__MARTE_RUN_DIR__/s02_output.bin"
Overwrite = "yes"
FileFormat = "binary"
StoreOnTrigger = 0
RefreshContent = 0
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
State1_Thread1_CycleTime = {
Type = uint32
}
}
}
"""
patched, functions_count = re.subn(
r"(?m)^ }\n \+Data = \{\n",
writer_gam + " }\n +Data = {\n",
text,
count=1,
)
patched, data_count = re.subn(
r"(?m)^ }\n \+States = \{\n",
file_writer + " }\n +States = {\n",
patched,
count=1,
)
patched, thread_count = re.subn(
r"Functions = \{GAMTimer GAMDisplay\}",
"Functions = {GAMTimer GAMDisplay IOGAM_Writer}",
patched,
count=1,
)
print("FUNCTIONS_TRANSFORM_COUNT=" + str(functions_count))
print("DATA_TRANSFORM_COUNT=" + str(data_count))
print("THREAD_TRANSFORM_COUNT=" + str(thread_count))
if (functions_count, data_count, thread_count) != (1, 1, 1):
print("TRANSFORM_STATUS=FAIL")
sys.exit(31)
checks = {
"IOGAM_WRITER_COUNT": patched.count("+IOGAM_Writer ="),
"FILE_WRITER_COUNT": patched.count("+FileWriter ="),
"FILE_WRITER_CLASS_COUNT": patched.count("Class = FileDataSource::FileWriter"),
"PLAIN_FILE_WRITER_CLASS_COUNT": patched.count("Class = FileWriter"),
"FILEWRITER_OUTPUT_COUNT": patched.count("DataSource = FileWriter"),
"RUN_DIR_PLACEHOLDER_COUNT": patched.count("__MARTE_RUN_DIR__"),
"BINARY_FORMAT_COUNT": patched.count('FileFormat = "binary"'),
"OVERWRITE_YES_COUNT": patched.count('Overwrite = "yes"'),
"CONTINUOUS_MODE_COUNT": patched.count("StoreOnTrigger = 0"),
}
for name, value in checks.items():
print(name + "=" + str(value))
expected = {
"IOGAM_WRITER_COUNT": 1,
"FILE_WRITER_COUNT": 1,
"FILE_WRITER_CLASS_COUNT": 1,
"PLAIN_FILE_WRITER_CLASS_COUNT": 0,
"FILEWRITER_OUTPUT_COUNT": 3,
"RUN_DIR_PLACEHOLDER_COUNT": 1,
"BINARY_FORMAT_COUNT": 1,
"OVERWRITE_YES_COUNT": 1,
"CONTINUOUS_MODE_COUNT": 1,
}
if checks != expected:
print("STRUCTURAL_CHECK_STATUS=FAIL")
sys.exit(32)
required_thread = "Functions = {GAMTimer GAMDisplay IOGAM_Writer}"
if patched.count(required_thread) != 1:
print("THREAD_FUNCTION_LIST_STATUS=FAIL")
sys.exit(33)
destination.write_text(patched, encoding="utf-8")
print("STRUCTURAL_CHECK_STATUS=PASS")
print("TRANSFORM_STATUS=PASS")
PY
PYTHON_RC=$?
if [ "$PYTHON_RC" -ne 0 ]; then
printf "PYTHON_TRANSFORM_EXIT_CODE=%s\n" "$PYTHON_RC"
exit 23
fi
if [ -f "$OUTPUT_CONFIG" ]; then
if cmp -s -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
printf "OUTPUT_STATUS=ALREADY_VALID\n"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" \
"$(sha256sum -- "$OUTPUT_CONFIG" | awk '{print $1}')"
exit 0
fi
printf "OUTPUT_STATUS=EXISTS_BUT_DIFFERS\n"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
exit 24
fi
chmod 0644 "$TEMP_CONFIG"
if ! mv -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
printf "OUTPUT_STATUS=PUBLISH_FAILED\n"
exit 25
fi
trap - EXIT
printf "OUTPUT_STATUS=CREATED\n"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" \
"$(sha256sum -- "$OUTPUT_CONFIG" | awk '{print $1}')"
+317
View File
@@ -0,0 +1,317 @@
#!/usr/bin/env bash
set -u
SCRIPT_DIR="$(
cd "$(dirname "${BASH_SOURCE[0]}")" &&
pwd
)"
SUITE="$(cd "$SCRIPT_DIR/../.." && pwd)"
DEFAULT_SOURCE="$SUITE/configurations/generated/S02/from_patcher/smoke_s2_binary_recording_from_s01.marte"
DEFAULT_OUTPUT="$SUITE/configurations/generated/S03/from_patcher/smoke_s3_semantic_sentinels_from_s02.marte"
SOURCE_CONFIG="${1:-$DEFAULT_SOURCE}"
OUTPUT_CONFIG="${2:-$DEFAULT_OUTPUT}"
EXPECTED_SOURCE_SHA256="8ecc8ba55e46e9f6fb8f304404d4070ff4c2ea5b9c783a69113bc08b457859d1"
EXPECTED_OUTPUT_SHA256="0f8916735ff67a9be2b167b52f147687fe131a063f410a81bfbf3bf9ff65f1e4"
fail() {
code="$1"
shift
printf "S03_BUILD_STATUS=FAIL\n" >&2
printf "S03_BUILD_ERROR=%s\n" "$*" >&2
exit "$code"
}
if [ ! -f "$SOURCE_CONFIG" ]; then
fail 20 "SOURCE_NOT_FOUND:$SOURCE_CONFIG"
fi
SOURCE_SHA256="$(sha256sum -- "$SOURCE_CONFIG" | awk '{print $1}')"
printf "SOURCE_CONFIG=%s\n" "$SOURCE_CONFIG"
printf "EXPECTED_SOURCE_SHA256=%s\n" "$EXPECTED_SOURCE_SHA256"
printf "ACTUAL_SOURCE_SHA256=%s\n" "$SOURCE_SHA256"
if [ "$SOURCE_SHA256" != "$EXPECTED_SOURCE_SHA256" ]; then
fail 21 "SOURCE_HASH_MISMATCH:$SOURCE_SHA256"
fi
OUTPUT_DIR="$(dirname "$OUTPUT_CONFIG")"
if ! mkdir -p -- "$OUTPUT_DIR"; then
fail 22 "OUTPUT_DIRECTORY_CREATE_FAILED:$OUTPUT_DIR"
fi
if [ -e "$OUTPUT_CONFIG" ] && [ ! -f "$OUTPUT_CONFIG" ]; then
fail 23 "OUTPUT_EXISTS_NOT_REGULAR_FILE:$OUTPUT_CONFIG"
fi
TEMP_CONFIG="$(mktemp "$OUTPUT_DIR/.s03_candidate_XXXXXX.marte")" ||
fail 24 "TEMPORARY_FILE_CREATE_FAILED:$OUTPUT_DIR"
cleanup() {
rm -f -- "$TEMP_CONFIG"
}
trap cleanup EXIT HUP INT TERM
PYTHONHASHSEED=0 python3 - "$SOURCE_CONFIG" "$TEMP_CONFIG" <<'PY_S03_PATCHER'
import sys
from pathlib import Path
source = Path(sys.argv[1])
destination = Path(sys.argv[2])
text = source.read_text(encoding="utf-8")
def require_count(haystack, needle, expected, label):
observed = haystack.count(needle)
print(f"{label}_COUNT={observed}")
if observed != expected:
raise RuntimeError(f"{label}_COUNT_EXPECTED_{expected}_OBSERVED_{observed}")
def replace_once(haystack, old, new, label):
require_count(haystack, old, 1, label)
return haystack.replace(old, new, 1)
for forbidden in (
"+SentinelProducer =",
"SentinelScalarA",
"SentinelScalarB",
"SentinelVector",
"s03_output.bin",
):
if forbidden in text:
raise RuntimeError("SOURCE_ALREADY_CONTAINS_S03_CONTENT:" + forbidden)
constant_gam = """ +SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
"""
sentinel_signals = """ SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
"""
filewriter_signals = """ SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
"""
text = replace_once(
text,
" +GAMDisplay = {\n",
constant_gam + " +GAMDisplay = {\n",
"SENTINEL_PRODUCER_ANCHOR",
)
writer_start = text.index(" +IOGAM_Writer = {\n")
writer_end = text.index(" }\n +Data = {\n", writer_start)
writer = text[writer_start:writer_end]
writer = replace_once(
writer,
""" State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
""",
""" State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
""" + sentinel_signals + """ }
OutputSignals = {
""",
"WRITER_INPUT_ANCHOR",
)
writer_output_signals = sentinel_signals.replace("DataSource = DDB1", "DataSource = FileWriter")
writer = replace_once(
writer,
""" State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
}
}
""",
""" State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
""" + writer_output_signals + """ }
}
""",
"WRITER_OUTPUT_ANCHOR",
)
text = text[:writer_start] + writer + text[writer_end:]
filewriter_start = text.index(" +FileWriter = {\n")
filewriter_end = text.index(" }\n +States = {\n", filewriter_start)
filewriter = text[filewriter_start:filewriter_end]
filewriter = replace_once(
filewriter,
' Filename = "__MARTE_RUN_DIR__/s02_output.bin"\n',
' Filename = "__MARTE_RUN_DIR__/s03_output.bin"\n',
"OUTPUT_FILENAME_ANCHOR",
)
filewriter = replace_once(
filewriter,
""" State1_Thread1_CycleTime = {
Type = uint32
}
}
}
""",
""" State1_Thread1_CycleTime = {
Type = uint32
}
""" + filewriter_signals + """ }
}
""",
"FILEWRITER_SIGNAL_ANCHOR",
)
text = text[:filewriter_start] + filewriter + text[filewriter_end:]
text = replace_once(
text,
"Functions = {GAMTimer GAMDisplay IOGAM_Writer}",
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}",
"THREAD_FUNCTION_LIST_ANCHOR",
)
# Canonicalise the historical trailing blank line so the generated candidate
# is byte-stable across copies and archive extraction tools.
text = text.rstrip("\n") + "\n"
checks = {
"SENTINEL_PRODUCER": text.count("+SentinelProducer ="),
"CONSTANT_GAM_CLASS": text.count("Class = ConstantGAM"),
"SENTINEL_SCALAR_A_DECLARATIONS": text.count("SentinelScalarA ="),
"SENTINEL_SCALAR_B_DECLARATIONS": text.count("SentinelScalarB ="),
"SENTINEL_VECTOR_DECLARATIONS": text.count("SentinelVector ="),
"VECTOR_DIMENSION_DECLARATIONS": text.count("NumberOfDimensions = 1"),
"VECTOR_CARDINALITY_DECLARATIONS": text.count("NumberOfElements = 3"),
"S03_OUTPUT_FILENAME": text.count("__MARTE_RUN_DIR__/s03_output.bin"),
"S02_OUTPUT_FILENAME": text.count("s02_output.bin"),
"RUN_DIR_PLACEHOLDER": text.count("__MARTE_RUN_DIR__"),
"FILEWRITER_CLASS": text.count("Class = FileDataSource::FileWriter"),
"THREAD_FUNCTION_LIST": text.count(
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
),
}
expected = {
"SENTINEL_PRODUCER": 1,
"CONSTANT_GAM_CLASS": 1,
"SENTINEL_SCALAR_A_DECLARATIONS": 4,
"SENTINEL_SCALAR_B_DECLARATIONS": 4,
"SENTINEL_VECTOR_DECLARATIONS": 4,
"VECTOR_DIMENSION_DECLARATIONS": 4,
"VECTOR_CARDINALITY_DECLARATIONS": 4,
"S03_OUTPUT_FILENAME": 1,
"S02_OUTPUT_FILENAME": 0,
"RUN_DIR_PLACEHOLDER": 1,
"FILEWRITER_CLASS": 1,
"THREAD_FUNCTION_LIST": 1,
}
for name in sorted(checks):
print(f"{name}_COUNT={checks[name]}")
if checks != expected:
raise RuntimeError(f"STRUCTURAL_CHECK_MISMATCH:{checks!r}")
destination.write_text(text, encoding="utf-8")
print("TRANSFORM_STATUS=PASS")
print("STRUCTURAL_CHECK_STATUS=PASS")
PY_S03_PATCHER
PYTHON_RC=$?
if [ "$PYTHON_RC" -ne 0 ]; then
fail 25 "TRANSFORM_FAILED_RC_$PYTHON_RC"
fi
GENERATED_SHA256="$(sha256sum -- "$TEMP_CONFIG" | awk '{print $1}')"
printf "GENERATED_SHA256=%s\n" "$GENERATED_SHA256"
if [ "$GENERATED_SHA256" != "$EXPECTED_OUTPUT_SHA256" ]; then
fail 26 "OUTPUT_HASH_MISMATCH:$GENERATED_SHA256"
fi
if [ -f "$OUTPUT_CONFIG" ]; then
if cmp -s -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
printf "S03_BUILD_STATUS=ALREADY_VALID\n"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
exit 0
fi
fail 27 "OUTPUT_EXISTS_BUT_DIFFERS:$OUTPUT_CONFIG"
fi
chmod 0644 "$TEMP_CONFIG" || fail 28 "OUTPUT_CHMOD_FAILED:$TEMP_CONFIG"
if ! mv -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
fail 29 "ATOMIC_PUBLISH_FAILED:$OUTPUT_CONFIG"
fi
trap - EXIT HUP INT TERM
printf "S03_BUILD_STATUS=CREATED\n"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
+458
View File
@@ -0,0 +1,458 @@
#!/usr/bin/env bash
set -u
SCRIPT_DIR="$(
cd "$(dirname "${BASH_SOURCE[0]}")" &&
pwd
)"
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
DEFAULT_SOURCE="$PACKAGE_ROOT/baseline/smoke_s2_binary_recording_from_s01.marte"
DEFAULT_OUTPUT_ROOT="$PACKAGE_ROOT/configurations/generated/S03H1/from_patcher"
EXPECTED_SOURCE_SHA256="8ecc8ba55e46e9f6fb8f304404d4070ff4c2ea5b9c783a69113bc08b457859d1"
EXPECTED_TEST_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
EXPECTED_DIAGNOSTIC_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
fail() {
code="$1"
shift
printf "S03_H1_BUILD_STATUS=FAIL\n" >&2
printf "S03_H1_BUILD_ERROR=%s\n" "$*" >&2
exit "$code"
}
usage() {
printf "Uso:\n" >&2
printf " %s TEST|DIAGNOSTIC\n" "$0" >&2
printf " %s <source-S02> <output-config> TEST|DIAGNOSTIC\n" "$0" >&2
}
case "$#" in
1)
SOURCE_CONFIG="$DEFAULT_SOURCE"
MODE="$1"
case "$MODE" in
TEST)
OUTPUT_CONFIG="$DEFAULT_OUTPUT_ROOT/smoke_s03h1_execution_profiles_test_from_s02.marte"
;;
DIAGNOSTIC)
OUTPUT_CONFIG="$DEFAULT_OUTPUT_ROOT/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
;;
*)
usage
fail 20 "UNKNOWN_MODE:$MODE"
;;
esac
;;
3)
SOURCE_CONFIG="$1"
OUTPUT_CONFIG="$2"
MODE="$3"
;;
*)
usage
fail 20 "INVALID_ARGUMENT_COUNT:$#"
;;
esac
case "$MODE" in
TEST)
EXPECTED_OUTPUT_SHA256="$EXPECTED_TEST_SHA256"
DISPLAY_CLASS="GAMDataSource"
;;
DIAGNOSTIC)
EXPECTED_OUTPUT_SHA256="$EXPECTED_DIAGNOSTIC_SHA256"
DISPLAY_CLASS="LoggerDataSource"
;;
*)
usage
fail 20 "UNKNOWN_MODE:$MODE"
;;
esac
if [ ! -f "$SOURCE_CONFIG" ]; then
fail 21 "SOURCE_NOT_FOUND:$SOURCE_CONFIG"
fi
SOURCE_SHA256="$(sha256sum -- "$SOURCE_CONFIG" | awk '{print $1}')"
printf "MODE=%s\n" "$MODE"
printf "SOURCE_CONFIG=%s\n" "$SOURCE_CONFIG"
printf "EXPECTED_SOURCE_SHA256=%s\n" "$EXPECTED_SOURCE_SHA256"
printf "ACTUAL_SOURCE_SHA256=%s\n" "$SOURCE_SHA256"
if [ "$SOURCE_SHA256" != "$EXPECTED_SOURCE_SHA256" ]; then
fail 22 "SOURCE_HASH_MISMATCH:$SOURCE_SHA256"
fi
OUTPUT_DIR="$(dirname "$OUTPUT_CONFIG")"
if ! mkdir -p -- "$OUTPUT_DIR"; then
fail 23 "OUTPUT_DIRECTORY_CREATE_FAILED:$OUTPUT_DIR"
fi
if [ -e "$OUTPUT_CONFIG" ] && [ ! -f "$OUTPUT_CONFIG" ]; then
fail 24 "OUTPUT_EXISTS_NOT_REGULAR_FILE:$OUTPUT_CONFIG"
fi
TEMP_CONFIG="$(mktemp "$OUTPUT_DIR/.s03h1_candidate_XXXXXX.marte")" ||
fail 25 "TEMPORARY_FILE_CREATE_FAILED:$OUTPUT_DIR"
cleanup() {
rm -f -- "$TEMP_CONFIG"
}
trap cleanup EXIT HUP INT TERM
PYTHONHASHSEED=0 python3 - \
"$SOURCE_CONFIG" \
"$TEMP_CONFIG" \
"$MODE" \
"$DISPLAY_CLASS" <<'PY_S03H1_PATCHER'
import sys
from pathlib import Path
source = Path(sys.argv[1])
destination = Path(sys.argv[2])
mode = sys.argv[3]
display_class = sys.argv[4]
text = source.read_text(encoding="utf-8")
def require_count(haystack, needle, expected, label):
observed = haystack.count(needle)
print(f"{label}_COUNT={observed}")
if observed != expected:
raise RuntimeError(
f"{label}_COUNT_EXPECTED_{expected}_OBSERVED_{observed}"
)
def replace_once(haystack, old, new, label):
require_count(haystack, old, 1, label)
return haystack.replace(old, new, 1)
for forbidden in (
"+SentinelProducer =",
"SentinelScalarA",
"SentinelScalarB",
"SentinelVector",
"s03_output.bin",
):
if forbidden in text:
raise RuntimeError("SOURCE_ALREADY_CONTAINS_S03_CONTENT:" + forbidden)
if mode not in {"TEST", "DIAGNOSTIC"}:
raise RuntimeError("UNKNOWN_MODE:" + mode)
if display_class not in {"GAMDataSource", "LoggerDataSource"}:
raise RuntimeError("UNKNOWN_DISPLAY_CLASS:" + display_class)
constant_gam = """ +SentinelProducer = {
Class = ConstantGAM
OutputSignals = {
SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 324508639
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
Default = 610839776
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
Default = {16909060 286397204 555885348}
}
}
}
"""
gam_display = f""" +GAMDisplay = {{
Class = IOGAM
InputSignals = {{
Counter = {{
DataSource = DDB1
Type = uint32
}}
Time = {{
DataSource = DDB1
Type = uint32
}}
State1_Thread1_CycleTime = {{
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}}
SentinelScalarA = {{
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}}
SentinelScalarB = {{
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}}
SentinelVector = {{
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}}
}}
OutputSignals = {{
Counter = {{
DataSource = Display
Type = uint32
}}
Time = {{
DataSource = Display
Type = uint32
}}
State1_Thread1_CycleTime = {{
DataSource = Display
Type = uint32
}}
SentinelScalarA = {{
DataSource = Display
Type = uint32
NumberOfElements = 1
}}
SentinelScalarB = {{
DataSource = Display
Type = uint32
NumberOfElements = 1
}}
SentinelVector = {{
DataSource = Display
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}}
}}
}}
"""
sentinel_signals = """ SentinelScalarA = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
DataSource = DDB1
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
DataSource = DDB1
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
"""
filewriter_signals = """ SentinelScalarA = {
Type = uint32
NumberOfElements = 1
}
SentinelScalarB = {
Type = uint32
NumberOfElements = 1
}
SentinelVector = {
Type = uint32
NumberOfDimensions = 1
NumberOfElements = 3
}
"""
require_count(text, " +GAMDisplay = {\n", 1, "GAMDISPLAY_START_ANCHOR")
require_count(text, " +IOGAM_Writer = {\n", 1, "IOGAM_WRITER_START_ANCHOR")
display_start = text.index(" +GAMDisplay = {\n")
writer_start = text.index(" +IOGAM_Writer = {\n", display_start)
text = text[:display_start] + constant_gam + gam_display + text[writer_start:]
writer_start = text.index(" +IOGAM_Writer = {\n")
writer_end = text.index(" }\n +Data = {\n", writer_start)
writer = text[writer_start:writer_end]
writer = replace_once(
writer,
""" State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
}
OutputSignals = {
""",
""" State1_Thread1_CycleTime = {
Alias = State1.Thread1_CycleTime
DataSource = Timings
Type = uint32
}
""" + sentinel_signals + """ }
OutputSignals = {
""",
"WRITER_INPUT_ANCHOR",
)
writer_output_signals = sentinel_signals.replace(
"DataSource = DDB1", "DataSource = FileWriter"
)
writer = replace_once(
writer,
""" State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
}
}
""",
""" State1_Thread1_CycleTime = {
DataSource = FileWriter
Type = uint32
}
""" + writer_output_signals + """ }
}
""",
"WRITER_OUTPUT_ANCHOR",
)
text = text[:writer_start] + writer + text[writer_end:]
text = replace_once(
text,
""" +Display = {
Class = LoggerDataSource
}
""",
f""" +Display = {{
Class = {display_class}
}}
""",
"DISPLAY_DATASOURCE_ANCHOR",
)
filewriter_start = text.index(" +FileWriter = {\n")
filewriter_end = text.index(" }\n +States = {\n", filewriter_start)
filewriter = text[filewriter_start:filewriter_end]
filewriter = replace_once(
filewriter,
' Filename = "__MARTE_RUN_DIR__/s02_output.bin"\n',
' Filename = "__MARTE_RUN_DIR__/s03_output.bin"\n',
"OUTPUT_FILENAME_ANCHOR",
)
filewriter = replace_once(
filewriter,
""" State1_Thread1_CycleTime = {
Type = uint32
}
}
}
""",
""" State1_Thread1_CycleTime = {
Type = uint32
}
""" + filewriter_signals + """ }
}
""",
"FILEWRITER_SIGNAL_ANCHOR",
)
text = text[:filewriter_start] + filewriter + text[filewriter_end:]
text = replace_once(
text,
"Functions = {GAMTimer GAMDisplay IOGAM_Writer}",
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}",
"THREAD_FUNCTION_LIST_ANCHOR",
)
text = text.rstrip("\n") + "\n"
checks = {
"SENTINEL_PRODUCER": text.count("+SentinelProducer ="),
"CONSTANT_GAM_CLASS": text.count("Class = ConstantGAM"),
"GAMDISPLAY_OBJECT": text.count("+GAMDisplay ="),
"SENTINEL_SCALAR_A_DECLARATIONS": text.count("SentinelScalarA ="),
"SENTINEL_SCALAR_B_DECLARATIONS": text.count("SentinelScalarB ="),
"SENTINEL_VECTOR_DECLARATIONS": text.count("SentinelVector ="),
"VECTOR_DIMENSION_DECLARATIONS": text.count("NumberOfDimensions = 1"),
"VECTOR_CARDINALITY_DECLARATIONS": text.count("NumberOfElements = 3"),
"S03_OUTPUT_FILENAME": text.count("__MARTE_RUN_DIR__/s03_output.bin"),
"S02_OUTPUT_FILENAME": text.count("s02_output.bin"),
"RUN_DIR_PLACEHOLDER": text.count("__MARTE_RUN_DIR__"),
"FILEWRITER_CLASS": text.count("Class = FileDataSource::FileWriter"),
"THREAD_FUNCTION_LIST": text.count(
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
),
"DISPLAY_DATASOURCE_BLOCK": text.count(
f" +Display = {{\n Class = {display_class}\n }}\n"
),
}
expected = {
"SENTINEL_PRODUCER": 1,
"CONSTANT_GAM_CLASS": 1,
"GAMDISPLAY_OBJECT": 1,
"SENTINEL_SCALAR_A_DECLARATIONS": 6,
"SENTINEL_SCALAR_B_DECLARATIONS": 6,
"SENTINEL_VECTOR_DECLARATIONS": 6,
"VECTOR_DIMENSION_DECLARATIONS": 6,
"VECTOR_CARDINALITY_DECLARATIONS": 6,
"S03_OUTPUT_FILENAME": 1,
"S02_OUTPUT_FILENAME": 0,
"RUN_DIR_PLACEHOLDER": 1,
"FILEWRITER_CLASS": 1,
"THREAD_FUNCTION_LIST": 1,
"DISPLAY_DATASOURCE_BLOCK": 1,
}
for name in sorted(checks):
print(f"{name}_COUNT={checks[name]}")
if checks != expected:
raise RuntimeError(f"STRUCTURAL_CHECK_MISMATCH:{checks!r}")
destination.write_text(text, encoding="utf-8")
print("TRANSFORM_STATUS=PASS")
print("STRUCTURAL_CHECK_STATUS=PASS")
PY_S03H1_PATCHER
PYTHON_RC=$?
if [ "$PYTHON_RC" -ne 0 ]; then
fail 26 "TRANSFORM_FAILED_RC_$PYTHON_RC"
fi
GENERATED_SHA256="$(sha256sum -- "$TEMP_CONFIG" | awk '{print $1}')"
printf "GENERATED_SHA256=%s\n" "$GENERATED_SHA256"
printf "EXPECTED_OUTPUT_SHA256=%s\n" "$EXPECTED_OUTPUT_SHA256"
if [ "$GENERATED_SHA256" != "$EXPECTED_OUTPUT_SHA256" ]; then
fail 27 "OUTPUT_HASH_MISMATCH:$GENERATED_SHA256"
fi
if [ -f "$OUTPUT_CONFIG" ]; then
if cmp -s -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
printf "S03_H1_BUILD_STATUS=ALREADY_VALID\n"
printf "MODE=%s\n" "$MODE"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
exit 0
fi
fail 28 "OUTPUT_EXISTS_BUT_DIFFERS:$OUTPUT_CONFIG"
fi
chmod 0644 "$TEMP_CONFIG" || fail 29 "OUTPUT_CHMOD_FAILED:$TEMP_CONFIG"
if ! mv -- "$TEMP_CONFIG" "$OUTPUT_CONFIG"; then
fail 30 "ATOMIC_PUBLISH_FAILED:$OUTPUT_CONFIG"
fi
trap - EXIT HUP INT TERM
printf "S03_H1_BUILD_STATUS=CREATED\n"
printf "MODE=%s\n" "$MODE"
printf "OUTPUT_CONFIG=%s\n" "$OUTPUT_CONFIG"
printf "OUTPUT_SHA256=%s\n" "$GENERATED_SHA256"
@@ -0,0 +1,151 @@
#!/usr/bin/env bash
set -u
set -o pipefail
PATCHER_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
SUITE="$(cd -- "$PATCHER_DIR/../.." && pwd -P)"
PARENT="$SUITE/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
OUTPUT_DIR="$SUITE/configurations/generated/S03H3/from_patcher"
OUTPUT="$OUTPUT_DIR/smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte"
VERIFIER="$SUITE/scenarios/S03H3_execution_profiles/verify_s03h3_minimal_structure.py"
EXPECTED_PARENT_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
TEMP_OUTPUT=""
cleanup() {
if [ -n "$TEMP_OUTPUT" ]; then
rm -f -- "$TEMP_OUTPUT"
fi
}
trap cleanup EXIT HUP INT TERM
fail() {
local code="$1"
local reason="$2"
printf 'PATCHER_STATUS=FAIL\n' >&2
printf 'PATCHER_ERROR=%s\n' "$reason" >&2
printf 'SOURCE_MODIFIED_IN_PLACE=NO\n' >&2
exit "$code"
}
if [ "$#" -ne 0 ]; then
printf 'Uso: %s\n' "$0" >&2
exit 2
fi
if [ ! -f "$PARENT" ]; then
fail 10 "CANONICAL_TEST_PARENT_MISSING"
fi
if [ ! -f "$VERIFIER" ]; then
fail 11 "MINIMAL_VERIFIER_MISSING"
fi
PARENT_SHA256_BEFORE="$(sha256sum -- "$PARENT" | awk '{print $1}')" || \
fail 12 "PARENT_HASH_UNAVAILABLE"
if [ "$PARENT_SHA256_BEFORE" != "$EXPECTED_PARENT_SHA256" ]; then
fail 13 "CANONICAL_TEST_PARENT_SHA256_MISMATCH"
fi
if ! mkdir -p -- "$OUTPUT_DIR"; then
fail 14 "OUTPUT_DIRECTORY_CREATION_FAILED"
fi
TEMP_OUTPUT="$(mktemp "$OUTPUT_DIR/.s03h3_minimal.XXXXXX")" || \
fail 15 "TEMPORARY_OUTPUT_CREATION_FAILED"
if ! python3 - "$PARENT" "$TEMP_OUTPUT" <<'PY'
import sys
from pathlib import Path
parent = Path(sys.argv[1])
target = Path(sys.argv[2])
text = parent.read_bytes().decode("utf-8")
gamdisplay_start = " +GAMDisplay = {\n"
writer_start = " +IOGAM_Writer = {\n"
display_block = (
" +Display = {\n"
" Class = GAMDataSource\n"
" }\n"
)
old_functions = (
" Functions = "
"{GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
)
new_functions = (
" Functions = "
"{GAMTimer SentinelProducer IOGAM_Writer}"
)
checks = {
"GAMDISPLAY_ANCHOR": text.count(gamdisplay_start),
"WRITER_ANCHOR": text.count(writer_start),
"DISPLAY_BLOCK": text.count(display_block),
"FUNCTIONS_ANCHOR": text.count(old_functions),
"MINIMAL_FUNCTIONS_PREEXISTING": text.count(new_functions),
}
if checks != {
"GAMDISPLAY_ANCHOR": 1,
"WRITER_ANCHOR": 1,
"DISPLAY_BLOCK": 1,
"FUNCTIONS_ANCHOR": 1,
"MINIMAL_FUNCTIONS_PREEXISTING": 0,
}:
for name, value in checks.items():
print(f"{name}_COUNT={value}", file=sys.stderr)
raise SystemExit(20)
start = text.index(gamdisplay_start)
end = text.index(writer_start, start)
if end <= start:
raise SystemExit(21)
result = text[:start] + text[end:]
result = result.replace(display_block, "", 1)
result = result.replace(old_functions, new_functions, 1)
target.write_bytes(result.encode("utf-8"))
PY
then
fail 16 "STRUCTURAL_TRANSFORMATION_FAILED"
fi
PARENT_SHA256_AFTER="$(sha256sum -- "$PARENT" | awk '{print $1}')" || \
fail 17 "PARENT_POST_HASH_UNAVAILABLE"
if [ "$PARENT_SHA256_AFTER" != "$PARENT_SHA256_BEFORE" ]; then
fail 18 "SOURCE_MODIFIED_IN_PLACE"
fi
if ! python3 "$VERIFIER" CANONICAL "$PARENT" "$TEMP_OUTPUT"; then
fail 19 "CANONICAL_STRUCTURAL_COMPLETENESS_FAILED"
fi
if [ -e "$OUTPUT" ]; then
if [ ! -f "$OUTPUT" ]; then
fail 20 "OUTPUT_EXISTS_NOT_REGULAR_FILE"
fi
if ! cmp -s -- "$TEMP_OUTPUT" "$OUTPUT"; then
fail 21 "EXISTING_OUTPUT_DIFFERS_FROM_DETERMINISTIC_RESULT"
fi
if ! python3 "$VERIFIER" CANONICAL "$PARENT" "$OUTPUT" >/dev/null; then
fail 22 "EXISTING_OUTPUT_COMPLETENESS_FAILED"
fi
printf 'PATCHER_RESULT=ALREADY_VALID\n'
else
chmod 0644 -- "$TEMP_OUTPUT" || fail 23 "OUTPUT_MODE_SET_FAILED"
mv -- "$TEMP_OUTPUT" "$OUTPUT" || fail 24 "OUTPUT_PUBLICATION_FAILED"
TEMP_OUTPUT=""
if ! python3 "$VERIFIER" CANONICAL "$PARENT" "$OUTPUT" >/dev/null; then
fail 25 "PUBLISHED_OUTPUT_COMPLETENESS_FAILED"
fi
printf 'PATCHER_RESULT=CREATED\n'
fi
OUTPUT_SHA256="$(sha256sum -- "$OUTPUT" | awk '{print $1}')" || \
fail 26 "OUTPUT_HASH_UNAVAILABLE"
printf 'PATCHER_STATUS=PASS\n'
printf 'PATCHER_DETERMINISM=PASS\n'
printf 'PATCHER_PARENT_HASH_MATCH=PASS\n'
printf 'CANONICAL_STRUCTURAL_COMPLETENESS_STATUS=PASS\n'
printf 'SOURCE_MODIFIED_IN_PLACE=NO\n'
printf 'MINIMAL_CONFIG=%s\n' "$OUTPUT"
printf 'MINIMAL_CONFIG_SHA256=%s\n' "$OUTPUT_SHA256"
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -u
if [ "$#" -ne 1 ]; then
printf "Uso: %s <run.log>\n" "$0" >&2
exit 2
fi
RUN_LOG="$1"
if [ ! -f "$RUN_LOG" ]; then
printf "LOG ASSENTE: %s\n" "$RUN_LOG" >&2
exit 3
fi
START_LINES="$(
grep -Ec \
'^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' \
"$RUN_LOG" || true
)"
COUNTER_SAMPLES="$(
grep -Ec \
'^\[Information - LoggerBroker\.cpp:[0-9]+\]: Counter \[0:0\]:[0-9]+[[:space:]]*$' \
"$RUN_LOG" || true
)"
CYCLE_TIME_SAMPLES="$(
grep -Ec \
'^\[Information - LoggerBroker\.cpp:[0-9]+\]: State1_Thread1_CycleTime \[0:0\]:[0-9]+[[:space:]]*$' \
"$RUN_LOG" || true
)"
SIGINT_LINES="$(
grep -Ec \
'^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' \
"$RUN_LOG" || true
)"
STOP_OK_LINES="$(
grep -Ec \
'^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' \
"$RUN_LOG" || true
)"
FATAL_ERROR_LINES="$(
grep -Ec '^\[FatalError - ' "$RUN_LOG" || true
)"
ERROR_LINES="$(
grep -Ec '^\[Error - ' "$RUN_LOG" || true
)"
NO_ERROR_LINES="$(
grep -Ec '^\[NoError - ' "$RUN_LOG" || true
)"
DOUBLE_STOP_LINES="$(
grep -Fc \
'Could not stop the RealTimeApplication. Was it ever started?' \
"$RUN_LOG" || true
)"
LAST_COUNTER="$(
grep -E \
'^\[Information - LoggerBroker\.cpp:[0-9]+\]: Counter \[0:0\]:[0-9]+[[:space:]]*$' \
"$RUN_LOG" |
tail -n 1 |
awk -F: '{gsub(/[[:space:]]/, "", $NF); print $NF}'
)"
printf "============================================================\n"
printf "VALIDAZIONE PRECISA S01\n"
printf "============================================================\n"
printf "Log: %s\n" "$RUN_LOG"
printf "Avvii di State1: %s\n" "$START_LINES"
printf "Campioni Counter reali: %s\n" "$COUNTER_SAMPLES"
printf "Ultimo Counter: %s\n" "${LAST_COUNTER:-non disponibile}"
printf "Campioni CycleTime reali: %s\n" "$CYCLE_TIME_SAMPLES"
printf "SIGINT ricevuti: %s\n" "$SIGINT_LINES"
printf "Stop riusciti: %s\n" "$STOP_OK_LINES"
printf "Righe FatalError reali: %s\n" "$FATAL_ERROR_LINES"
printf "Righe Error reali: %s\n" "$ERROR_LINES"
printf "Righe NoError: %s\n" "$NO_ERROR_LINES"
printf "Anomalie doppio arresto: %s\n" "$DOUBLE_STOP_LINES"
FUNCTIONAL_OK=0
if [ "$START_LINES" -ge 1 ] &&
[ "$COUNTER_SAMPLES" -ge 50 ] &&
[ "$CYCLE_TIME_SAMPLES" -ge 50 ]; then
FUNCTIONAL_OK=1
fi
printf "\n============================================================\n"
printf "CLASSIFICAZIONE\n"
printf "============================================================\n"
if [ "$FUNCTIONAL_OK" -eq 1 ]; then
printf "[VERIFICATO] Funzionamento S01 e produzione dei segnali.\n"
else
printf "[NON DIMOSTRATO] Funzionamento S01 non sufficientemente provato.\n"
exit 20
fi
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ]; then
printf "[VERIFICATO] Shutdown privo di Error e FatalError.\n"
exit 0
fi
printf "[DA VERIFICARE] Funzionalità riuscita, ma shutdown con anomalia.\n"
exit 10
+722
View File
@@ -0,0 +1,722 @@
#!/usr/bin/env bash
set -u
if [ "$#" -ne 1 ]; then
printf "Uso: %s <directory-run|run.log>\n" "$0" >&2
exit 2
fi
INPUT_PATH="$1"
if [ -d "$INPUT_PATH" ]; then
RUN_DIR="$(cd "$INPUT_PATH" >/dev/null 2>&1 && pwd)"
RUN_LOG="$RUN_DIR/run.log"
elif [ -f "$INPUT_PATH" ]; then
RUN_LOG="$(cd "$(dirname "$INPUT_PATH")" >/dev/null 2>&1 && pwd)/$(basename "$INPUT_PATH")"
RUN_DIR="$(dirname "$RUN_LOG")"
else
printf "[NON DIMOSTRATO] Percorso di validazione non trovato: %s\n" "$INPUT_PATH" >&2
exit 21
fi
RUN_CONFIG="$RUN_DIR/configuration.marte"
OUTPUT_FILE="$RUN_DIR/s02_output.bin"
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
for required_file in "$RUN_LOG" "$RUN_CONFIG" "$OUTPUT_FILE"; do
if [ ! -f "$required_file" ]; then
printf "[NON DIMOSTRATO] File richiesto assente: %s\n" "$required_file" >&2
exit 21
fi
done
count_lines() {
pattern="$1"
file="$2"
grep -Ec "$pattern" "$file" 2>/dev/null || true
}
START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")"
APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")"
FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")"
SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")"
STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")"
TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")"
STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")"
FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")"
OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")"
ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")"
INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")"
NO_ERROR_LINES="$(count_lines '^\[NoError ' "$RUN_LOG")"
WARNING_LINES="$(count_lines '^\[Warning ' "$RUN_LOG")"
PRIORITY_CLIP_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Requested a thread priority that is higher than ' "$RUN_LOG")"
PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")"
UNCLASSIFIED_WARNING_LINES=$((WARNING_LINES - PRIORITY_CLIP_LINES - PRIORITY_FAIL_LINES))
if [ "$UNCLASSIFIED_WARNING_LINES" -lt 0 ]; then
UNCLASSIFIED_WARNING_LINES=0
fi
DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")"
ASYNC_EVENTSEM_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")"
PTHREAD_MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_EVENTSEM_CLOSE_LINES))
UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - PTHREAD_MUTEX_DESTROY_LINES))
if [ "$UNCLASSIFIED_FATAL_LINES" -lt 0 ]; then
UNCLASSIFIED_FATAL_LINES=0
fi
if [ "$UNCLASSIFIED_OS_ERROR_LINES" -lt 0 ]; then
UNCLASSIFIED_OS_ERROR_LINES=0
fi
PLACEHOLDER_COUNT="$(grep -o '__MARTE_RUN_DIR__' "$RUN_CONFIG" 2>/dev/null | wc -l)"
NAMESPACED_CLASS_COUNT="$(count_lines '^[[:space:]]*Class = FileDataSource::FileWriter[[:space:]]*$' "$RUN_CONFIG")"
PLAIN_CLASS_COUNT="$(count_lines '^[[:space:]]*Class = FileWriter[[:space:]]*$' "$RUN_CONFIG")"
EXPECTED_FILENAME="$RUN_DIR/s02_output.bin"
FILENAME_MATCH_LINES="$(
python3 - "$RUN_CONFIG" "$EXPECTED_FILENAME" <<'PYCONFIG'
import sys
from pathlib import Path
config_path = Path(sys.argv[1])
expected = f'Filename = "{sys.argv[2]}"'
count = sum(
1
for line in config_path.read_text(encoding="utf-8").splitlines()
if line.strip() == expected
)
print(count)
PYCONFIG
)"
MARTE_EXIT_CODE="NOT_AVAILABLE"
TEE_EXIT_CODE="NOT_AVAILABLE"
if [ -f "$RUNNER_SUMMARY" ]; then
value="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
if [ -n "$value" ]; then
MARTE_EXIT_CODE="$value"
fi
value="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
if [ -n "$value" ]; then
TEE_EXIT_CODE="$value"
fi
fi
case "$MARTE_EXIT_CODE" in
124)
MARTE_EXIT_CLASS="TIMEOUT_EXIT_124"
;;
137)
MARTE_EXIT_CLASS="EXIT_137_SIGKILL"
;;
0)
MARTE_EXIT_CLASS="EXIT_0"
;;
NOT_AVAILABLE)
MARTE_EXIT_CLASS="NOT_AVAILABLE"
;;
*)
MARTE_EXIT_CLASS="OTHER_EXIT_CODE"
;;
esac
CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s02_validator_config_XXXXXX.log")"
BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s02_validator_binary_XXXXXX.log")"
trap 'rm -f -- "$CONFIG_REPORT" "$BINARY_REPORT"' EXIT
python3 - "$RUN_CONFIG" >"$CONFIG_REPORT" 2>&1 <<'PYCONFIGOBS'
import re
import sys
from decimal import Decimal, InvalidOperation
from pathlib import Path
class Node:
def __init__(self, name):
self.name = name.lstrip("+$")
self.properties = {}
def sanitise(text):
"""Remove comments while preserving strings, braces and line boundaries."""
output = []
index = 0
quote = None
block_comment = False
while index < len(text):
char = text[index]
next_char = text[index + 1] if index + 1 < len(text) else ""
if block_comment:
if char == "*" and next_char == "/":
block_comment = False
output.extend(" ")
index += 2
else:
output.append("\n" if char == "\n" else " ")
index += 1
continue
if quote:
output.append(char)
if char == "\\" and index + 1 < len(text):
output.append(text[index + 1])
index += 2
continue
if char == quote:
quote = None
index += 1
continue
if char in {'"', "'"}:
quote = char
output.append(char)
index += 1
continue
if char == "/" and next_char == "*":
block_comment = True
output.extend(" ")
index += 2
continue
if char == "/" and next_char == "/":
while index < len(text) and text[index] != "\n":
output.append(" ")
index += 1
continue
if char == "#":
while index < len(text) and text[index] != "\n":
output.append(" ")
index += 1
continue
output.append(char)
index += 1
return "".join(output)
def braces_outside_strings(line):
result = []
quote = None
index = 0
while index < len(line):
char = line[index]
if quote:
if char == "\\" and index + 1 < len(line):
index += 2
continue
if char == quote:
quote = None
elif char in {'"', "'"}:
quote = char
elif char in "{}":
result.append(char)
index += 1
return result
text = Path(sys.argv[1]).read_text(encoding="utf-8")
clean = sanitise(text)
nodes = []
stack = []
object_open = re.compile(r"^\s*([+$]?[A-Za-z_][A-Za-z0-9_.:-]*)\s*=\s*\{")
assignment = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$")
for line in clean.splitlines():
match = object_open.match(line)
if match:
node = Node(match.group(1))
nodes.append(node)
stack.append(node)
elif stack:
match = assignment.match(line)
if match and not match.group(2).startswith("{"):
stack[-1].properties[match.group(1)] = match.group(2).strip()
opens_and_closes = braces_outside_strings(line)
opening_already_consumed = bool(match and object_open.match(line))
if opening_already_consumed:
removed = False
adjusted = []
for brace in opens_and_closes:
if brace == "{" and not removed:
removed = True
continue
adjusted.append(brace)
opens_and_closes = adjusted
for brace in opens_and_closes:
if brace == "{":
stack.append(Node("<anonymous>"))
elif stack:
stack.pop()
def report_objects(prefix, classes, property_name):
matches = [node for node in nodes if node.properties.get("Class") in classes]
names = ",".join(node.name for node in matches) or "NOT_AVAILABLE_OBJECT_NOT_FOUND"
affinities = []
for node in matches:
value = node.properties.get(property_name, "NOT_EXPLICITLY_CONFIGURED")
affinities.append(f"{node.name}={value}")
affinity = ",".join(affinities) or "NOT_AVAILABLE_OBJECT_NOT_FOUND"
print(f"{prefix}_OBJECT_COUNT={len(matches)}")
print(f"{prefix}_OBJECT_NAMES={names}")
print(f"{prefix}_AFFINITY={affinity}")
report_objects("REALTIME_THREAD", {"RealTimeThread"}, "CPUs")
report_objects("FILE_WRITER", {"FileDataSource::FileWriter", "FileWriter"}, "CPUMask")
report_objects("LINUX_TIMER", {"LinuxTimer"}, "CPUMask")
frequency_values = []
for node in nodes:
raw = node.properties.get("Frequency")
if raw is None:
continue
raw = raw.strip().strip('"').strip("'")
try:
value = Decimal(raw)
except InvalidOperation:
continue
if value > 0:
frequency_values.append(value)
unique_frequencies = sorted(set(frequency_values))
if len(unique_frequencies) == 1:
frequency = unique_frequencies[0]
period = Decimal(1_000_000) / frequency
period_text = str(int(period)) if period == period.to_integral_value() else format(period, ".6f").rstrip("0").rstrip(".")
frequency_text = format(frequency, "f").rstrip("0").rstrip(".") if "." in format(frequency, "f") else format(frequency, "f")
print(f"DT_NOMINAL_PERIOD_US={period_text}")
print(f"DT_NOMINAL_SOURCE=CONFIG_FREQUENCY_{frequency_text}_HZ")
elif not unique_frequencies:
print("DT_NOMINAL_PERIOD_US=NOT_AVAILABLE")
print("DT_NOMINAL_SOURCE=NOT_AVAILABLE_NO_POSITIVE_FREQUENCY")
else:
print("DT_NOMINAL_PERIOD_US=NOT_AVAILABLE")
print("DT_NOMINAL_SOURCE=NOT_AVAILABLE_MULTIPLE_FREQUENCIES")
print("CONFIG_OBSERVABILITY_STATUS=PASS")
PYCONFIGOBS
CONFIG_RC=$?
config_value() {
key="$1"
sed -n "s/^${key}=//p" "$CONFIG_REPORT" | tail -n 1
}
CONFIG_OBSERVABILITY_STATUS="$(config_value CONFIG_OBSERVABILITY_STATUS)"
REALTIME_THREAD_OBJECT_COUNT="$(config_value REALTIME_THREAD_OBJECT_COUNT)"
REALTIME_THREAD_OBJECT_NAMES="$(config_value REALTIME_THREAD_OBJECT_NAMES)"
REALTIME_THREAD_AFFINITY="$(config_value REALTIME_THREAD_AFFINITY)"
FILE_WRITER_OBJECT_COUNT="$(config_value FILE_WRITER_OBJECT_COUNT)"
FILE_WRITER_OBJECT_NAMES="$(config_value FILE_WRITER_OBJECT_NAMES)"
FILE_WRITER_AFFINITY="$(config_value FILE_WRITER_AFFINITY)"
LINUX_TIMER_OBJECT_COUNT="$(config_value LINUX_TIMER_OBJECT_COUNT)"
LINUX_TIMER_OBJECT_NAMES="$(config_value LINUX_TIMER_OBJECT_NAMES)"
LINUX_TIMER_AFFINITY="$(config_value LINUX_TIMER_AFFINITY)"
DT_NOMINAL_PERIOD_US="$(config_value DT_NOMINAL_PERIOD_US)"
DT_NOMINAL_SOURCE="$(config_value DT_NOMINAL_SOURCE)"
python3 - "$OUTPUT_FILE" "${DT_NOMINAL_PERIOD_US:-NOT_AVAILABLE}" >"$BINARY_REPORT" 2>&1 <<'PY'
from collections import Counter
from decimal import Decimal, InvalidOperation
import struct
import sys
from pathlib import Path
path = Path(sys.argv[1])
nominal_period_raw = sys.argv[2]
data = path.read_bytes()
expected_names = [
"Counter",
"Time",
"State1_Thread1_CycleTime",
]
expected_type_hex = "0408"
expected_signals = len(expected_names)
signal_header_bytes = 2 + 32 + 4
print(f"FILE_SIZE_BYTES={len(data)}")
if len(data) < 4:
print("BINARY_STATUS=FAIL_HEADER_TOO_SMALL")
sys.exit(31)
number_of_signals = struct.unpack_from("<I", data, 0)[0]
print(f"NUMBER_OF_SIGNALS={number_of_signals}")
if number_of_signals != expected_signals:
print("BINARY_STATUS=FAIL_SIGNAL_COUNT")
sys.exit(32)
offset = 4
names = []
type_hex_values = []
elements = []
for index in range(number_of_signals):
if offset + signal_header_bytes > len(data):
print(f"TRUNCATED_SIGNAL_HEADER_INDEX={index}")
print("BINARY_STATUS=FAIL_TRUNCATED_SIGNAL_HEADER")
sys.exit(33)
type_bytes = data[offset:offset + 2]
offset += 2
raw_name = data[offset:offset + 32]
offset += 32
number_of_elements = struct.unpack_from("<I", data, offset)[0]
offset += 4
name = raw_name.split(b"\x00", 1)[0].decode("ascii", errors="replace")
names.append(name)
type_hex_values.append(type_bytes.hex())
elements.append(number_of_elements)
print(f"SIGNAL_{index}_TYPE_HEX={type_bytes.hex()}")
print(f"SIGNAL_{index}_NAME={name}")
print(f"SIGNAL_{index}_NUMBER_OF_ELEMENTS={number_of_elements}")
print("SIGNAL_NAMES=" + ",".join(names))
if names != expected_names:
print("BINARY_STATUS=FAIL_SIGNAL_NAMES")
sys.exit(34)
if any(value != expected_type_hex for value in type_hex_values):
print("BINARY_STATUS=FAIL_SIGNAL_TYPES")
sys.exit(35)
if any(value != 1 for value in elements):
print("BINARY_STATUS=FAIL_NUMBER_OF_ELEMENTS")
sys.exit(36)
header_bytes = offset
sample_bytes = expected_signals * 4
payload_bytes = len(data) - header_bytes
remainder_bytes = payload_bytes % sample_bytes
sample_count = payload_bytes // sample_bytes
print(f"HEADER_BYTES={header_bytes}")
print(f"SAMPLE_BYTES={sample_bytes}")
print(f"PAYLOAD_BYTES={payload_bytes}")
print(f"PAYLOAD_REMAINDER_BYTES={remainder_bytes}")
print(f"COMPLETE_SAMPLE_COUNT={sample_count}")
if remainder_bytes != 0:
print("BINARY_STATUS=FAIL_PAYLOAD_REMAINDER")
sys.exit(37)
records = [
struct.unpack_from("<III", data, header_bytes + index * sample_bytes)
for index in range(sample_count)
]
counters = [record[0] for record in records]
times = [record[1] for record in records]
cycle_times = [record[2] for record in records]
dt_values = [current - previous for previous, current in zip(times, times[1:])]
dt_histogram = Counter(dt_values)
distinct_dt_values = sorted(dt_histogram)
leading_zero_cycle_time_samples = 0
for value in cycle_times:
if value != 0:
break
leading_zero_cycle_time_samples += 1
print(f"DT_INTERVAL_COUNT={len(dt_values)}")
print("DT_DISTINCT_VALUES=" + (",".join(str(value) for value in distinct_dt_values) or "NOT_AVAILABLE"))
print(
"DT_HISTOGRAM="
+ (",".join(f"{value}:{dt_histogram[value]}" for value in distinct_dt_values) or "NOT_AVAILABLE")
)
print(f"DT_MIN={min(dt_values) if dt_values else 'NOT_AVAILABLE'}")
print(f"DT_MAX={max(dt_values) if dt_values else 'NOT_AVAILABLE'}")
print(f"LEADING_ZERO_CYCLE_TIME_SAMPLES={leading_zero_cycle_time_samples}")
try:
nominal_period = Decimal(nominal_period_raw)
except InvalidOperation:
nominal_period = None
if nominal_period is not None and dt_values:
anomaly_indices = [
index for index, value in enumerate(dt_values)
if Decimal(value) != nominal_period
]
print(f"DT_ANOMALY_COUNT={len(anomaly_indices)}")
print("DT_ANOMALY_INDICES=" + (",".join(str(index) for index in anomaly_indices) or "NONE"))
if anomaly_indices:
first_index = anomaly_indices[0]
print(f"DT_FIRST_ANOMALY_BETWEEN_RECORDS={first_index}_AND_{first_index + 1}")
print(f"DT_FIRST_ANOMALY_COUNTER_PAIR={counters[first_index]}_AND_{counters[first_index + 1]}")
print(f"DT_FIRST_ANOMALY_VALUE={dt_values[first_index]}")
else:
print("DT_FIRST_ANOMALY_BETWEEN_RECORDS=NONE")
print("DT_FIRST_ANOMALY_COUNTER_PAIR=NONE")
print("DT_FIRST_ANOMALY_VALUE=NONE")
else:
print("DT_ANOMALY_COUNT=NOT_AVAILABLE")
print("DT_ANOMALY_INDICES=NOT_AVAILABLE")
print("DT_FIRST_ANOMALY_BETWEEN_RECORDS=NOT_AVAILABLE")
print("DT_FIRST_ANOMALY_COUNTER_PAIR=NOT_AVAILABLE")
print("DT_FIRST_ANOMALY_VALUE=NOT_AVAILABLE")
if sample_count < 50:
print("BINARY_STATUS=FAIL_INSUFFICIENT_SAMPLES")
sys.exit(38)
counter_gap_count = sum(
1 for previous, current in zip(counters, counters[1:])
if current != previous + 1
)
time_decrease_count = sum(
1 for previous, current in zip(times, times[1:])
if current < previous
)
nonzero_cycle_times = [value for value in cycle_times if value > 0]
print(f"COUNTER_FIRST={counters[0]}")
print(f"COUNTER_LAST={counters[-1]}")
print(f"COUNTER_GAP_COUNT={counter_gap_count}")
print(f"TIME_FIRST={times[0]}")
print(f"TIME_LAST={times[-1]}")
print(f"TIME_DECREASE_COUNT={time_decrease_count}")
print(f"NONZERO_CYCLE_TIME_SAMPLES={len(nonzero_cycle_times)}")
if nonzero_cycle_times:
print(f"CYCLE_TIME_MIN={min(nonzero_cycle_times)}")
print(f"CYCLE_TIME_MAX={max(nonzero_cycle_times)}")
print(f"CYCLE_TIME_MEAN={sum(nonzero_cycle_times) / len(nonzero_cycle_times):.3f}")
else:
print("CYCLE_TIME_MIN=NOT_AVAILABLE")
print("CYCLE_TIME_MAX=NOT_AVAILABLE")
print("CYCLE_TIME_MEAN=NOT_AVAILABLE")
if counters[0] != 1 or counter_gap_count != 0:
print("BINARY_STATUS=FAIL_COUNTER_SEQUENCE")
sys.exit(39)
if time_decrease_count != 0:
print("BINARY_STATUS=FAIL_TIME_MONOTONICITY")
sys.exit(40)
if len(nonzero_cycle_times) == 0:
print("BINARY_STATUS=FAIL_NO_CYCLE_TIME")
sys.exit(41)
print("BINARY_STATUS=PASS")
PY
BINARY_RC=$?
binary_value() {
key="$1"
sed -n "s/^${key}=//p" "$BINARY_REPORT" | tail -n 1
}
BINARY_STATUS="$(binary_value BINARY_STATUS)"
FILE_SIZE_BYTES="$(binary_value FILE_SIZE_BYTES)"
NUMBER_OF_SIGNALS="$(binary_value NUMBER_OF_SIGNALS)"
SIGNAL_NAMES="$(binary_value SIGNAL_NAMES)"
HEADER_BYTES="$(binary_value HEADER_BYTES)"
SAMPLE_BYTES="$(binary_value SAMPLE_BYTES)"
PAYLOAD_BYTES="$(binary_value PAYLOAD_BYTES)"
PAYLOAD_REMAINDER_BYTES="$(binary_value PAYLOAD_REMAINDER_BYTES)"
COMPLETE_SAMPLE_COUNT="$(binary_value COMPLETE_SAMPLE_COUNT)"
COUNTER_FIRST="$(binary_value COUNTER_FIRST)"
COUNTER_LAST="$(binary_value COUNTER_LAST)"
COUNTER_GAP_COUNT="$(binary_value COUNTER_GAP_COUNT)"
TIME_FIRST="$(binary_value TIME_FIRST)"
TIME_LAST="$(binary_value TIME_LAST)"
TIME_DECREASE_COUNT="$(binary_value TIME_DECREASE_COUNT)"
NONZERO_CYCLE_TIME_SAMPLES="$(binary_value NONZERO_CYCLE_TIME_SAMPLES)"
CYCLE_TIME_MIN="$(binary_value CYCLE_TIME_MIN)"
CYCLE_TIME_MAX="$(binary_value CYCLE_TIME_MAX)"
CYCLE_TIME_MEAN="$(binary_value CYCLE_TIME_MEAN)"
DT_INTERVAL_COUNT="$(binary_value DT_INTERVAL_COUNT)"
DT_DISTINCT_VALUES="$(binary_value DT_DISTINCT_VALUES)"
DT_HISTOGRAM="$(binary_value DT_HISTOGRAM)"
DT_MIN="$(binary_value DT_MIN)"
DT_MAX="$(binary_value DT_MAX)"
DT_ANOMALY_COUNT="$(binary_value DT_ANOMALY_COUNT)"
DT_ANOMALY_INDICES="$(binary_value DT_ANOMALY_INDICES)"
DT_FIRST_ANOMALY_BETWEEN_RECORDS="$(binary_value DT_FIRST_ANOMALY_BETWEEN_RECORDS)"
DT_FIRST_ANOMALY_COUNTER_PAIR="$(binary_value DT_FIRST_ANOMALY_COUNTER_PAIR)"
DT_FIRST_ANOMALY_VALUE="$(binary_value DT_FIRST_ANOMALY_VALUE)"
LEADING_ZERO_CYCLE_TIME_SAMPLES="$(binary_value LEADING_ZERO_CYCLE_TIME_SAMPLES)"
affinity_display() {
object_count="$1"
affinity_mapping="$2"
if [ "$object_count" = "1" ]; then
printf "%s" "${affinity_mapping#*=}"
else
printf "%s" "$affinity_mapping"
fi
}
REALTIME_THREAD_AFFINITY_DISPLAY="$(affinity_display "${REALTIME_THREAD_OBJECT_COUNT:-0}" "${REALTIME_THREAD_AFFINITY:-NOT_AVAILABLE}")"
FILE_WRITER_AFFINITY_DISPLAY="$(affinity_display "${FILE_WRITER_OBJECT_COUNT:-0}" "${FILE_WRITER_AFFINITY:-NOT_AVAILABLE}")"
LINUX_TIMER_AFFINITY_DISPLAY="$(affinity_display "${LINUX_TIMER_OBJECT_COUNT:-0}" "${LINUX_TIMER_AFFINITY:-NOT_AVAILABLE}")"
printf "============================================================\n"
printf "VALIDAZIONE PRECISA S02\n"
printf "============================================================\n"
printf "Run directory: %s\n" "$RUN_DIR"
printf "Log: %s\n" "$RUN_LOG"
printf "Configurazione: %s\n" "$RUN_CONFIG"
printf "File binario: %s\n" "$OUTPUT_FILE"
printf "Avvii State1: %s\n" "$START_LINES"
printf "Application starting: %s\n" "$APPLICATION_START_LINES"
printf "Aperture FileWriter: %s\n" "$FILE_OPEN_LINES"
printf "Placeholder residui: %s\n" "$PLACEHOLDER_COUNT"
printf "Classe qualificata FileWriter: %s\n" "$NAMESPACED_CLASS_COUNT"
printf "Classe semplice FileWriter: %s\n" "$PLAIN_CLASS_COUNT"
printf "Filename corretti: %s\n" "$FILENAME_MATCH_LINES"
printf "Dimensione file: %s byte\n" "${FILE_SIZE_BYTES:-NOT_AVAILABLE}"
printf "Segnali binari: %s\n" "${NUMBER_OF_SIGNALS:-NOT_AVAILABLE}"
printf "Nomi segnali: %s\n" "${SIGNAL_NAMES:-NOT_AVAILABLE}"
printf "Header: %s byte\n" "${HEADER_BYTES:-NOT_AVAILABLE}"
printf "Record: %s byte\n" "${SAMPLE_BYTES:-NOT_AVAILABLE}"
printf "Payload: %s byte\n" "${PAYLOAD_BYTES:-NOT_AVAILABLE}"
printf "Residuo payload: %s byte\n" "${PAYLOAD_REMAINDER_BYTES:-NOT_AVAILABLE}"
printf "Campioni completi: %s\n" "${COMPLETE_SAMPLE_COUNT:-NOT_AVAILABLE}"
printf "Counter iniziale: %s\n" "${COUNTER_FIRST:-NOT_AVAILABLE}"
printf "Counter finale: %s\n" "${COUNTER_LAST:-NOT_AVAILABLE}"
printf "Salti Counter: %s\n" "${COUNTER_GAP_COUNT:-NOT_AVAILABLE}"
printf "Time iniziale: %s\n" "${TIME_FIRST:-NOT_AVAILABLE}"
printf "Time finale: %s\n" "${TIME_LAST:-NOT_AVAILABLE}"
printf "Regressioni Time: %s\n" "${TIME_DECREASE_COUNT:-NOT_AVAILABLE}"
printf "CycleTime non nulli: %s\n" "${NONZERO_CYCLE_TIME_SAMPLES:-NOT_AVAILABLE}"
printf "CycleTime minimo: %s\n" "${CYCLE_TIME_MIN:-NOT_AVAILABLE}"
printf "CycleTime massimo: %s\n" "${CYCLE_TIME_MAX:-NOT_AVAILABLE}"
printf "CycleTime medio: %s\n" "${CYCLE_TIME_MEAN:-NOT_AVAILABLE}"
printf "Stato parser binario: %s\n" "${BINARY_STATUS:-NOT_AVAILABLE}"
printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE"
printf "Exit code tee: %s\n" "$TEE_EXIT_CODE"
printf "SIGINT ricevuti: %s\n" "$SIGINT_LINES"
printf "Stop riusciti: %s\n" "$STOP_OK_LINES"
printf "Application terminated: %s\n" "$TERMINATED_LINES"
printf "Errori startup: %s\n" "$STARTUP_ERROR_LINES"
printf "FatalError reali: %s\n" "$FATAL_ERROR_LINES"
printf "OSError reali: %s\n" "$OS_ERROR_LINES"
printf "Error reali: %s\n" "$ERROR_LINES"
printf "InitialisationError reali: %s\n" "$INITIALISATION_ERROR_LINES"
printf "NoError: %s\n" "$NO_ERROR_LINES"
printf "Doppio arresto: %s\n" "$DOUBLE_STOP_LINES"
printf "Close EventSem asincrono: %s\n" "$ASYNC_EVENTSEM_CLOSE_LINES"
printf "pthread_mutex_destroy: %s\n" "$PTHREAD_MUTEX_DESTROY_LINES"
printf "FatalError non classificati: %s\n" "$UNCLASSIFIED_FATAL_LINES"
printf "OSError non classificati: %s\n" "$UNCLASSIFIED_OS_ERROR_LINES"
printf "\n--- INTERVALLI TEMPORALI (osservativo) ---\n"
printf "Periodo nominale: %s us\n" "${DT_NOMINAL_PERIOD_US:-NOT_AVAILABLE}"
printf "Fonte periodo nominale: %s\n" "${DT_NOMINAL_SOURCE:-NOT_AVAILABLE}"
printf "Intervalli dt: %s\n" "${DT_INTERVAL_COUNT:-NOT_AVAILABLE}"
printf "Valori dt distinti: %s\n" "${DT_DISTINCT_VALUES:-NOT_AVAILABLE}"
printf "Istogramma dt: %s\n" "${DT_HISTOGRAM:-NOT_AVAILABLE}"
printf "dt minimo: %s\n" "${DT_MIN:-NOT_AVAILABLE}"
printf "dt massimo: %s\n" "${DT_MAX:-NOT_AVAILABLE}"
printf "Intervalli dt anomali: %s\n" "${DT_ANOMALY_COUNT:-NOT_AVAILABLE}"
printf "Indici dt anomali: %s\n" "${DT_ANOMALY_INDICES:-NOT_AVAILABLE}"
printf "Primo dt anomalo tra record: %s\n" "${DT_FIRST_ANOMALY_BETWEEN_RECORDS:-NOT_AVAILABLE}"
printf "Primo dt anomalo Counter: %s\n" "${DT_FIRST_ANOMALY_COUNTER_PAIR:-NOT_AVAILABLE}"
printf "Primo dt anomalo valore: %s\n" "${DT_FIRST_ANOMALY_VALUE:-NOT_AVAILABLE}"
printf "CycleTime nulli iniziali: %s\n" "${LEADING_ZERO_CYCLE_TIME_SAMPLES:-NOT_AVAILABLE}"
printf "\n--- AFFINITA' DICHIARATE IN CONFIGURAZIONE ---\n"
REALTIME_THREAD_LABEL="RealTimeThread (${REALTIME_THREAD_OBJECT_NAMES:-NOT_AVAILABLE}) CPUs:"
FILE_WRITER_LABEL="FileWriter (${FILE_WRITER_OBJECT_NAMES:-NOT_AVAILABLE}) CPUMask:"
LINUX_TIMER_LABEL="LinuxTimer (${LINUX_TIMER_OBJECT_NAMES:-NOT_AVAILABLE}) CPUMask:"
printf "%-40s %s (rc=%s)\n" "Stato parser configurazione:" "${CONFIG_OBSERVABILITY_STATUS:-NOT_AVAILABLE}" "$CONFIG_RC"
printf "%-40s %s\n" "$REALTIME_THREAD_LABEL" "${REALTIME_THREAD_AFFINITY_DISPLAY:-NOT_AVAILABLE}"
printf "%-40s %s\n" "$FILE_WRITER_LABEL" "${FILE_WRITER_AFFINITY_DISPLAY:-NOT_AVAILABLE}"
printf "%-40s %s\n" "$LINUX_TIMER_LABEL" "${LINUX_TIMER_AFFINITY_DISPLAY:-NOT_AVAILABLE}"
printf "\n--- PRIORITA' E TERMINAZIONE ---\n"
printf "Warning totali: %s\n" "$WARNING_LINES"
printf "Warning clipping priorita': %s\n" "$PRIORITY_CLIP_LINES"
printf "Warning priorita' non applic.: %s\n" "$PRIORITY_FAIL_LINES"
printf "Warning non classificati: %s\n" "$UNCLASSIFIED_WARNING_LINES"
printf "Classe exit code: %s\n" "$MARTE_EXIT_CLASS"
FUNCTIONAL_OK=0
if [ "$START_LINES" -ge 1 ] &&
[ "$APPLICATION_START_LINES" -ge 1 ] &&
[ "$FILE_OPEN_LINES" -ge 1 ] &&
[ "$STARTUP_ERROR_LINES" -eq 0 ] &&
[ "$PLACEHOLDER_COUNT" -eq 0 ] &&
[ "$NAMESPACED_CLASS_COUNT" -eq 1 ] &&
[ "$PLAIN_CLASS_COUNT" -eq 0 ] &&
[ "$FILENAME_MATCH_LINES" -eq 1 ] &&
[ "$BINARY_RC" -eq 0 ]; then
FUNCTIONAL_OK=1
fi
if [ "$FUNCTIONAL_OK" -eq 1 ]; then
FUNCTIONAL_STATUS="PASS"
else
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
fi
if [ "$MARTE_EXIT_CLASS" = "EXIT_137_SIGKILL" ]; then
TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL"
elif [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then
TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED"
else
TIMING_STATUS="UNASSESSED_DT_SEMANTICS"
fi
printf "\nFUNCTIONAL_STATUS=%s\n" "$FUNCTIONAL_STATUS"
printf "TIMING_STATUS=%s\n" "$TIMING_STATUS"
case "$FUNCTIONAL_STATUS:$TIMING_STATUS" in
PASS:NOT_VALID_*)
printf "FUNCTIONAL_PASS_TIMING_NOT_VALID\n"
;;
esac
printf "\n============================================================\n"
printf "CLASSIFICAZIONE\n"
printf "============================================================\n"
if [ "$FUNCTIONAL_OK" -ne 1 ]; then
printf "[NON DIMOSTRATO] Funzionamento S02 e registrazione binaria non sufficientemente provati.\n"
printf "[DA VERIFICARE] Dettaglio parser binario:\n"
cat -- "$BINARY_REPORT"
exit 20
fi
printf "[VERIFICATO] Funzionamento S02 e registrazione binaria dimostrati.\n"
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
[ "$OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "[VERIFICATO] Shutdown privo di Error, OSError e FatalError.\n"
exit 0
fi
KNOWN_SHUTDOWN_TOTAL=$((DOUBLE_STOP_LINES + ASYNC_EVENTSEM_CLOSE_LINES + PTHREAD_MUTEX_DESTROY_LINES))
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "[DA VERIFICARE] Funzionalità riuscita con sole anomalie di shutdown già classificate.\n"
exit 10
fi
printf "[DA VERIFICARE] Funzionalità riuscita, ma sono presenti anomalie di shutdown non classificate.\n"
exit 11
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""Export the approved S03 binary record layout to a derived CSV artifact."""
import argparse
import csv
import hashlib
import os
import struct
import sys
import tempfile
from pathlib import Path
UINT32_TYPE_HEX = "0408"
SIGNAL_HEADER_BYTES = 38
EXPECTED_SIGNALS = (
("Counter", 1),
("Time", 1),
("State1_Thread1_CycleTime", 1),
("SentinelScalarA", 1),
("SentinelScalarB", 1),
("SentinelVector", 3),
)
EXPECTED_HEADER_BYTES = 4 + len(EXPECTED_SIGNALS) * SIGNAL_HEADER_BYTES
EXPECTED_RECORD_BYTES = 32
CSV_COLUMNS = (
"Counter",
"Time",
"CycleTime",
"SentinelScalarA",
"SentinelScalarB",
"SentinelVector0",
"SentinelVector1",
"SentinelVector2",
)
class ExportError(Exception):
"""A fail-closed binary or publication error."""
def decode_name(raw):
try:
return raw.split(b"\x00", 1)[0].decode("ascii")
except UnicodeDecodeError as exc:
raise ExportError("NON_ASCII_SIGNAL_NAME") from exc
def read_records(binary_path):
try:
data = binary_path.read_bytes()
except OSError as exc:
raise ExportError(f"BINARY_READ_FAILED:{exc}") from exc
if len(data) < EXPECTED_HEADER_BYTES:
raise ExportError(
f"HEADER_TOO_SMALL:EXPECTED_{EXPECTED_HEADER_BYTES}:OBSERVED_{len(data)}"
)
signal_count = struct.unpack_from("<I", data, 0)[0]
if signal_count != len(EXPECTED_SIGNALS):
raise ExportError(
f"SIGNAL_COUNT_MISMATCH:EXPECTED_{len(EXPECTED_SIGNALS)}:OBSERVED_{signal_count}"
)
offset = 4
observed = []
for _ in range(signal_count):
type_hex = data[offset : offset + 2].hex()
offset += 2
name = decode_name(data[offset : offset + 32])
offset += 32
elements = struct.unpack_from("<I", data, offset)[0]
offset += 4
observed.append((name, type_hex, elements))
expected = tuple(
(name, UINT32_TYPE_HEX, elements) for name, elements in EXPECTED_SIGNALS
)
if tuple(observed) != expected:
raise ExportError(
"HEADER_CONTRACT_MISMATCH:"
f"EXPECTED_{expected!r}:OBSERVED_{tuple(observed)!r}"
)
if offset != EXPECTED_HEADER_BYTES:
raise ExportError(
f"HEADER_SIZE_MISMATCH:EXPECTED_{EXPECTED_HEADER_BYTES}:OBSERVED_{offset}"
)
payload = data[offset:]
remainder = len(payload) % EXPECTED_RECORD_BYTES
if remainder != 0:
raise ExportError(
f"PAYLOAD_ALIGNMENT_ERROR:REMAINDER_{remainder}:RECORD_{EXPECTED_RECORD_BYTES}"
)
if not payload:
raise ExportError("NO_COMPLETE_RECORDS")
records = [
struct.unpack_from("<8I", payload, record_offset)
for record_offset in range(0, len(payload), EXPECTED_RECORD_BYTES)
]
return data, records
def write_csv_atomic(output_path, records):
output_path.parent.mkdir(parents=True, exist_ok=True)
if output_path.exists() and not output_path.is_file():
raise ExportError(f"OUTPUT_NOT_REGULAR_FILE:{output_path}")
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{output_path.name}.",
suffix=".tmp",
dir=output_path.parent,
text=True,
)
temporary_path = Path(temporary_name)
try:
with os.fdopen(descriptor, "w", encoding="utf-8", newline="") as stream:
writer = csv.writer(stream, lineterminator="\n")
writer.writerow(CSV_COLUMNS)
writer.writerows(records)
stream.flush()
os.fsync(stream.fileno())
if output_path.exists():
if temporary_path.read_bytes() == output_path.read_bytes():
temporary_path.unlink()
return "ALREADY_VALID"
raise ExportError(f"OUTPUT_EXISTS_BUT_DIFFERS:{output_path}")
os.chmod(temporary_path, 0o644)
os.replace(temporary_path, output_path)
return "CREATED"
finally:
if temporary_path.exists():
temporary_path.unlink()
def sha256_bytes(data):
return hashlib.sha256(data).hexdigest()
def main():
parser = argparse.ArgumentParser(
description=(
"Create a human-readable CSV from the S03 FileWriter binary. "
"The binary remains the primary evidence."
)
)
parser.add_argument("binary", type=Path)
parser.add_argument("csv_output", type=Path, nargs="?")
args = parser.parse_args()
output_path = args.csv_output
if output_path is None:
output_path = args.binary.with_suffix(".csv")
try:
binary_data, records = read_records(args.binary)
publication = write_csv_atomic(output_path, records)
csv_data = output_path.read_bytes()
except ExportError as exc:
print("CSV_EXPORT_STATUS=FAIL")
print(f"CSV_EXPORT_ERROR={exc}")
return 40
except OSError as exc:
print("CSV_EXPORT_STATUS=FAIL")
print(f"CSV_EXPORT_ERROR=OS_ERROR:{exc}")
return 41
print("PRIMARY_EVIDENCE=s03_output.bin")
print("DERIVED_ARTIFACT=s03_output.csv")
print(f"BINARY_PATH={args.binary}")
print(f"BINARY_SHA256={sha256_bytes(binary_data)}")
print(f"CSV_PATH={output_path}")
print(f"CSV_SHA256={sha256_bytes(csv_data)}")
print(f"CSV_RECORD_COUNT={len(records)}")
print(f"CSV_PUBLICATION={publication}")
print("CSV_EXPORT_STATUS=PASS")
return 0
if __name__ == "__main__":
sys.exit(main())
+414
View File
@@ -0,0 +1,414 @@
#!/usr/bin/env bash
set -u
MINIMUM_SAMPLES="50"
EXPECTED_BASELINE_PARSER_SHA256="91c4d66770197e1986ed91eb6a4870e7da7df5a522ffdf4329b6d9250f238307"
EXPECTED_TEST_CONFIG_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
EXPECTED_DIAGNOSTIC_CONFIG_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PARSER="${S03_BASELINE_PARSER:-$PACKAGE_ROOT/baseline/parse_s03_binary.py}"
CONFIG_VERIFIER="$SCRIPT_DIR/verify_s03h1_config.py"
MODE="NOT_PROVIDED"
MODE_STATUS="NOT_DEMONSTRATED"
PROFILE_CONFIG_STATUS="NOT_DEMONSTRATED"
OBSERVABILITY_STATUS="NOT_DEMONSTRATED"
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
SEMANTIC_STATUS="NOT_DEMONSTRATED"
TIMING_STATUS="NOT_EVALUATED"
MEASUREMENT_ELIGIBILITY="NO"
emit_contract() {
printf "MODE=%s\n" "$MODE"
printf "MODE_STATUS=%s\n" "$MODE_STATUS"
printf "PROFILE_CONFIG_STATUS=%s\n" "$PROFILE_CONFIG_STATUS"
printf "OBSERVABILITY_STATUS=%s\n" "$OBSERVABILITY_STATUS"
printf "FUNCTIONAL_STATUS=%s\n" "$FUNCTIONAL_STATUS"
printf "SEMANTIC_STATUS=%s\n" "$SEMANTIC_STATUS"
printf "TIMING_STATUS=%s\n" "$TIMING_STATUS"
printf "MEASUREMENT_ELIGIBILITY=%s\n" "$MEASUREMENT_ELIGIBILITY"
}
fail_early() {
code="$1"
shift
printf "[NON DIMOSTRATO] %s\n" "$*" >&2
emit_contract
printf "VALIDATION_RESULT=FAIL_REQUIRED_INPUT\n"
exit "$code"
}
if [ "$#" -ne 2 ]; then
printf "Uso: %s TEST|DIAGNOSTIC <directory-run|run.log>\n" "$0" >&2
emit_contract
exit 2
fi
MODE="$1"
case "$MODE" in
TEST)
EXPECTED_PROFILE_CONFIG_SHA256="$EXPECTED_TEST_CONFIG_SHA256"
;;
DIAGNOSTIC)
EXPECTED_PROFILE_CONFIG_SHA256="$EXPECTED_DIAGNOSTIC_CONFIG_SHA256"
;;
*)
MODE_STATUS="FAIL"
PROFILE_CONFIG_STATUS="FAIL"
OBSERVABILITY_STATUS="FAIL"
printf "UNKNOWN_MODE=%s\n" "$MODE" >&2
emit_contract
printf "VALIDATION_RESULT=FAIL_UNKNOWN_MODE\n"
exit 2
;;
esac
INPUT_PATH="$2"
if [ -d "$INPUT_PATH" ]; then
RUN_DIR="$(cd "$INPUT_PATH" && pwd)"
RUN_LOG="$RUN_DIR/run.log"
elif [ -f "$INPUT_PATH" ]; then
RUN_LOG="$(cd "$(dirname "$INPUT_PATH")" && pwd)/$(basename "$INPUT_PATH")"
RUN_DIR="$(dirname "$RUN_LOG")"
else
fail_early 21 "Percorso di validazione non trovato: $INPUT_PATH"
fi
RUN_CONFIG="$RUN_DIR/configuration.marte"
OUTPUT_FILE="$RUN_DIR/s03_output.bin"
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
RUN_HASHES="$RUN_DIR/hashes.sha256"
MODE_FILE="$RUN_DIR/s03h1_mode.txt"
if [ ! -f "$PARSER" ]; then
fail_early 21 "Parser baseline S03 assente: $PARSER"
fi
PARSER_SHA256="$(sha256sum -- "$PARSER" | awk '{print $1}')"
if [ "$PARSER_SHA256" != "$EXPECTED_BASELINE_PARSER_SHA256" ]; then
fail_early 21 "Parser baseline S03 con hash inatteso: $PARSER_SHA256"
fi
if [ ! -x "$CONFIG_VERIFIER" ]; then
fail_early 21 "Verificatore configurazione assente o non eseguibile: $CONFIG_VERIFIER"
fi
for required_file in \
"$RUN_LOG" \
"$RUN_CONFIG" \
"$OUTPUT_FILE" \
"$RUNNER_SUMMARY" \
"$RUN_HASHES" \
"$MODE_FILE"
do
if [ ! -f "$required_file" ]; then
fail_early 21 "File richiesto assente: $required_file"
fi
done
if [ ! -s "$RUN_HASHES" ]; then
fail_early 21 "Manifest hash assente o vuoto: $RUN_HASHES"
fi
count_lines() {
pattern="$1"
file="$2"
grep -Ec "$pattern" "$file" || true
}
report_value() {
key="$1"
file="$2"
sed -n "s/^${key}=//p" "$file" | tail -n 1
}
MODE_DECLARATION_COUNT="$(count_lines "^MODE=${MODE}$" "$MODE_FILE")"
SCENARIO_DECLARATION_COUNT="$(count_lines '^SCENARIO=S03H1$' "$MODE_FILE")"
CONFIG_HASH_DECLARATION_COUNT="$(
count_lines "^CANDIDATE_CONFIG_SHA256=${EXPECTED_PROFILE_CONFIG_SHA256}$" "$MODE_FILE"
)"
INSTALLATION_GATE_DECLARATION_COUNT="$(
count_lines '^S03_H1_INSTALLATION_AUTHORIZED=NO$' "$MODE_FILE"
)"
if [ "$MODE_DECLARATION_COUNT" -eq 1 ] &&
[ "$SCENARIO_DECLARATION_COUNT" -eq 1 ] &&
[ "$CONFIG_HASH_DECLARATION_COUNT" -eq 1 ] &&
[ "$INSTALLATION_GATE_DECLARATION_COUNT" -eq 1 ]; then
MODE_STATUS="PASS"
else
MODE_STATUS="FAIL"
fi
START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")"
APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")"
FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")"
SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")"
STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")"
TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")"
STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")"
FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")"
OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")"
ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")"
INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")"
WARNING_LINES="$(count_lines '^\[Warning ' "$RUN_LOG")"
PRIORITY_CLIP_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Requested a thread priority that is higher than ' "$RUN_LOG")"
PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")"
LINUX_TIMER_DEFAULT_WARNING_LINES="$(count_lines '^\[Warning - LinuxTimer\.cpp:[0-9]+\]: (ExecutionMode|CPUMask|StackSize) not specified using: ' "$RUN_LOG")"
CONFIG_DB_NODE_WARNING_LINES="$(count_lines '^\[Warning - MARTeApp\.cpp:[0-9]+\]: \[ConfigurationDatabaseNode\] - instances: [0-9]+[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_WARNING_LINES=$((
WARNING_LINES -
PRIORITY_CLIP_LINES -
PRIORITY_FAIL_LINES -
LINUX_TIMER_DEFAULT_WARNING_LINES -
CONFIG_DB_NODE_WARNING_LINES
))
if [ "$UNCLASSIFIED_WARNING_LINES" -lt 0 ]; then
UNCLASSIFIED_WARNING_LINES=0
fi
DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")"
ASYNC_EVENTSEM_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")"
PTHREAD_MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_EVENTSEM_CLOSE_LINES))
UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - PTHREAD_MUTEX_DESTROY_LINES))
if [ "$UNCLASSIFIED_FATAL_LINES" -lt 0 ]; then
UNCLASSIFIED_FATAL_LINES=0
fi
if [ "$UNCLASSIFIED_OS_ERROR_LINES" -lt 0 ]; then
UNCLASSIFIED_OS_ERROR_LINES=0
fi
LOGGER_SIGNAL_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: (Counter|Time|State1_Thread1_CycleTime|SentinelScalarA|SentinelScalarB|SentinelVector) \[[0-9]+:[0-9]+\]:' "$RUN_LOG")"
LOGGER_COUNTER_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Counter \[0:0\]:' "$RUN_LOG")"
LOGGER_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Time \[0:0\]:' "$RUN_LOG")"
LOGGER_CYCLE_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: State1_Thread1_CycleTime \[0:0\]:' "$RUN_LOG")"
LOGGER_SCALAR_A_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarA \[0:0\]:' "$RUN_LOG")"
LOGGER_SCALAR_B_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarB \[0:0\]:' "$RUN_LOG")"
LOGGER_VECTOR_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelVector \[0:2\]:' "$RUN_LOG")"
case "$MODE" in
TEST)
if [ "$LOGGER_SIGNAL_LINES" -eq 0 ] &&
[ "$LOGGER_COUNTER_LINES" -eq 0 ] &&
[ "$LOGGER_TIME_LINES" -eq 0 ] &&
[ "$LOGGER_CYCLE_TIME_LINES" -eq 0 ] &&
[ "$LOGGER_SCALAR_A_LINES" -eq 0 ] &&
[ "$LOGGER_SCALAR_B_LINES" -eq 0 ] &&
[ "$LOGGER_VECTOR_LINES" -eq 0 ]; then
OBSERVABILITY_STATUS="PASS"
else
OBSERVABILITY_STATUS="FAIL"
fi
;;
DIAGNOSTIC)
if [ "$LOGGER_COUNTER_LINES" -ge 1 ] &&
[ "$LOGGER_TIME_LINES" -ge 1 ] &&
[ "$LOGGER_CYCLE_TIME_LINES" -ge 1 ] &&
[ "$LOGGER_SCALAR_A_LINES" -ge 1 ] &&
[ "$LOGGER_SCALAR_B_LINES" -ge 1 ] &&
[ "$LOGGER_VECTOR_LINES" -ge 1 ]; then
OBSERVABILITY_STATUS="PASS"
else
OBSERVABILITY_STATUS="FAIL"
fi
;;
esac
CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_config_XXXXXX.log")" || exit 22
BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_binary_XXXXXX.log")" || exit 22
HASH_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_hashes_XXXXXX.log")" || exit 22
trap 'rm -f -- "$CONFIG_REPORT" "$BINARY_REPORT" "$HASH_REPORT"' EXIT HUP INT TERM
EXPECTED_FILENAME="$RUN_DIR/s03_output.bin"
PYTHONHASHSEED=0 "$CONFIG_VERIFIER" \
"$MODE" \
"$RUN_CONFIG" \
"$EXPECTED_FILENAME" \
>"$CONFIG_REPORT" 2>&1
CONFIG_RC=$?
PROFILE_CONFIG_STATUS="$(
report_value CONFIG_PROFILE_VERIFICATION_STATUS "$CONFIG_REPORT"
)"
if [ -z "$PROFILE_CONFIG_STATUS" ]; then
PROFILE_CONFIG_STATUS="FAIL"
fi
PYTHONHASHSEED=0 python3 "$PARSER" "$OUTPUT_FILE" --min-samples "$MINIMUM_SAMPLES" \
>"$BINARY_REPORT" 2>&1
PARSER_RC=$?
sha256sum -c --strict "$RUN_HASHES" >"$HASH_REPORT" 2>&1
HASH_RC=$?
FUNCTIONAL_BINARY_STATUS="$(report_value FUNCTIONAL_BINARY_STATUS "$BINARY_REPORT")"
SEMANTIC_BINARY_STATUS="$(report_value SEMANTIC_BINARY_STATUS "$BINARY_REPORT")"
BINARY_STATUS="$(report_value BINARY_STATUS "$BINARY_REPORT")"
FIRST_ERROR_CODE="$(report_value FIRST_ERROR_CODE "$BINARY_REPORT")"
PROFILE_ERROR_CODE="$(report_value PROFILE_ERROR_CODE "$CONFIG_REPORT")"
COMPLETE_SAMPLE_COUNT="$(report_value COMPLETE_SAMPLE_COUNT "$BINARY_REPORT")"
HEADER_BYTES="$(report_value OBSERVED_HEADER_BYTES "$BINARY_REPORT")"
RECORD_BYTES="$(report_value OBSERVED_RECORD_BYTES_FROM_HEADER "$BINARY_REPORT")"
PAYLOAD_BYTES="$(report_value PAYLOAD_BYTES "$BINARY_REPORT")"
PAYLOAD_REMAINDER_BYTES="$(report_value PAYLOAD_REMAINDER_BYTES "$BINARY_REPORT")"
MARTE_EXIT_CODE="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
TEE_EXIT_CODE="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
if [ -z "$MARTE_EXIT_CODE" ]; then
MARTE_EXIT_CODE="NOT_AVAILABLE"
fi
if [ -z "$TEE_EXIT_CODE" ]; then
TEE_EXIT_CODE="NOT_AVAILABLE"
fi
case "$MARTE_EXIT_CODE" in
137)
TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL"
;;
*)
if [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then
TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED"
else
TIMING_STATUS="UNASSESSED_DT_SEMANTICS"
fi
;;
esac
FUNCTIONAL_OK=0
if [ "$START_LINES" -ge 1 ] &&
[ "$APPLICATION_START_LINES" -ge 1 ] &&
[ "$FILE_OPEN_LINES" -ge 1 ] &&
[ "$STARTUP_ERROR_LINES" -eq 0 ] &&
[ "$MODE_STATUS" = "PASS" ] &&
[ "$CONFIG_RC" -eq 0 ] &&
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
[ "$OBSERVABILITY_STATUS" = "PASS" ] &&
[ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] &&
[ "$HASH_RC" -eq 0 ] &&
[ "$TEE_EXIT_CODE" = "0" ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
FUNCTIONAL_OK=1
fi
if [ "$FUNCTIONAL_OK" -eq 1 ]; then
FUNCTIONAL_STATUS="PASS"
fi
if [ "$MODE_STATUS" = "PASS" ] &&
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
[ "$SEMANTIC_BINARY_STATUS" = "PASS" ]; then
SEMANTIC_STATUS="PASS"
elif [ "$SEMANTIC_BINARY_STATUS" = "FAIL" ]; then
SEMANTIC_STATUS="FAIL"
else
SEMANTIC_STATUS="NOT_DEMONSTRATED"
fi
printf "============================================================\n"
printf "VALIDAZIONE S03-H1 - EXECUTION PROFILES\n"
printf "============================================================\n"
printf "Mode: %s\n" "$MODE"
printf "Run directory: %s\n" "$RUN_DIR"
printf "Configurazione: %s\n" "$RUN_CONFIG"
printf "File binario: %s\n" "$OUTPUT_FILE"
printf "Manifest hash: %s\n" "$RUN_HASHES"
printf "Parser baseline SHA-256: %s\n" "$PARSER_SHA256"
printf "Mode file: %s\n" "$MODE_FILE"
printf "Mode declaration count: %s\n" "$MODE_DECLARATION_COUNT"
printf "Scenario declaration count: %s\n" "$SCENARIO_DECLARATION_COUNT"
printf "Config hash declaration count: %s\n" "$CONFIG_HASH_DECLARATION_COUNT"
printf "Installation gate count: %s\n" "$INSTALLATION_GATE_DECLARATION_COUNT"
printf "Configurazione profilo: %s (rc=%s)\n" "$PROFILE_CONFIG_STATUS" "$CONFIG_RC"
printf "Errore profilo: %s\n" "${PROFILE_ERROR_CODE:-NOT_AVAILABLE}"
printf "Osservabilita' profilo: %s\n" "$OBSERVABILITY_STATUS"
printf "Logger signal lines: %s\n" "$LOGGER_SIGNAL_LINES"
printf "Logger Counter lines: %s\n" "$LOGGER_COUNTER_LINES"
printf "Logger Time lines: %s\n" "$LOGGER_TIME_LINES"
printf "Logger CycleTime lines: %s\n" "$LOGGER_CYCLE_TIME_LINES"
printf "Logger ScalarA lines: %s\n" "$LOGGER_SCALAR_A_LINES"
printf "Logger ScalarB lines: %s\n" "$LOGGER_SCALAR_B_LINES"
printf "Logger Vector[3] lines: %s\n" "$LOGGER_VECTOR_LINES"
printf "Verifica manifest hash: %s (rc=%s)\n" "$( [ "$HASH_RC" -eq 0 ] && printf PASS || printf FAIL )" "$HASH_RC"
printf "Parser binario: %s (rc=%s)\n" "${BINARY_STATUS:-NOT_AVAILABLE}" "$PARSER_RC"
printf "Primo errore parser: %s\n" "${FIRST_ERROR_CODE:-NOT_AVAILABLE}"
printf "Header osservato: %s byte\n" "${HEADER_BYTES:-NOT_AVAILABLE}"
printf "Record da header: %s byte\n" "${RECORD_BYTES:-NOT_AVAILABLE}"
printf "Payload: %s byte\n" "${PAYLOAD_BYTES:-NOT_AVAILABLE}"
printf "Residuo payload: %s byte\n" "${PAYLOAD_REMAINDER_BYTES:-NOT_AVAILABLE}"
printf "Campioni completi: %s\n" "${COMPLETE_SAMPLE_COUNT:-NOT_AVAILABLE}"
printf "Avvii State1: %s\n" "$START_LINES"
printf "Application starting: %s\n" "$APPLICATION_START_LINES"
printf "Aperture FileWriter: %s\n" "$FILE_OPEN_LINES"
printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE"
printf "Exit code tee: %s\n" "$TEE_EXIT_CODE"
printf "Warning priorita' non applic.: %s\n" "$PRIORITY_FAIL_LINES"
printf "Warning non classificati: %s\n" "$UNCLASSIFIED_WARNING_LINES"
printf "FatalError non classificati: %s\n" "$UNCLASSIFIED_FATAL_LINES"
printf "OSError non classificati: %s\n" "$UNCLASSIFIED_OS_ERROR_LINES"
printf "Error non classificati: %s\n" "$ERROR_LINES"
printf "InitialisationError: %s\n" "$INITIALISATION_ERROR_LINES"
printf "UNCLASSIFIED_WARNING_LINES=%s\n" "$UNCLASSIFIED_WARNING_LINES"
printf "LOGGER_SIGNAL_LINES=%s\n" "$LOGGER_SIGNAL_LINES"
printf "LOGGER_COUNTER_LINES=%s\n" "$LOGGER_COUNTER_LINES"
printf "LOGGER_TIME_LINES=%s\n" "$LOGGER_TIME_LINES"
printf "LOGGER_CYCLE_TIME_LINES=%s\n" "$LOGGER_CYCLE_TIME_LINES"
printf "LOGGER_SCALAR_A_LINES=%s\n" "$LOGGER_SCALAR_A_LINES"
printf "LOGGER_SCALAR_B_LINES=%s\n" "$LOGGER_SCALAR_B_LINES"
printf "LOGGER_VECTOR_LINES=%s\n" "$LOGGER_VECTOR_LINES"
printf "\n--- DETTAGLIO CONFIGURAZIONE PROFILE-AWARE ---\n"
cat -- "$CONFIG_REPORT"
printf "\n--- DETTAGLIO PARSER BINARIO BASELINE ---\n"
cat -- "$BINARY_REPORT"
printf "\n--- VERIFICA HASH ---\n"
cat -- "$HASH_REPORT"
printf "\n--- CONTRATTO S03-H1 ---\n"
emit_contract
if [ "$FUNCTIONAL_STATUS" != "PASS" ]; then
printf "VALIDATION_RESULT=FUNCTIONAL_NOT_DEMONSTRATED\n"
exit 20
fi
if [ "$SEMANTIC_STATUS" != "PASS" ]; then
printf "VALIDATION_RESULT=SEMANTIC_VALIDATION_FAILED\n"
exit 20
fi
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
[ "$OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "VALIDATION_RESULT=PASS_CLEAN_SHUTDOWN\n"
exit 0
fi
KNOWN_SHUTDOWN_TOTAL=$((
DOUBLE_STOP_LINES +
ASYNC_EVENTSEM_CLOSE_LINES +
PTHREAD_MUTEX_DESTROY_LINES
))
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "VALIDATION_RESULT=PASS_KNOWN_SHUTDOWN_ANOMALY\n"
exit 10
fi
printf "VALIDATION_RESULT=PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY\n"
exit 11
+267
View File
@@ -0,0 +1,267 @@
#!/usr/bin/env python3
"""Fail-closed, profile-aware verifier for S03-H1 runtime configurations."""
import argparse
import hashlib
import sys
from pathlib import Path
EXPECTED_PROFILE_HASHES = {
"TEST": "d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e",
"DIAGNOSTIC": "cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3",
}
EXPECTED_DISPLAY_CLASSES = {
"TEST": "GAMDataSource",
"DIAGNOSTIC": "LoggerDataSource",
}
EXPECTED_GAMDISPLAY_BLOCK_SHA256 = (
"13b14b504dac43df5febf230cd069c5093d951726382b1d5dee8edf43d6814d0"
)
EXPECTED_IOGAM_WRITER_BLOCK_SHA256 = (
"b3d54ad2fae08e6640bfa0be39ead9841906b4573a3b407e5ecf21340b5c5717"
)
EXPECTED_FILEWRITER_BLOCK_SHA256 = (
"0bb949b9d72fa758bdfd8b2039336bc1fda071ecf038304402032de63f4fc455"
)
PLACEHOLDER_LINE = ' Filename = "__MARTE_RUN_DIR__/s03_output.bin"'
def digest(text):
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def extract_block(text, start_marker, end_marker):
if text.count(start_marker) != 1:
raise ValueError(f"START_MARKER_COUNT:{start_marker!r}:{text.count(start_marker)}")
start = text.index(start_marker)
try:
end = text.index(end_marker, start)
except ValueError as exc:
raise ValueError(f"END_MARKER_MISSING:{end_marker!r}") from exc
return text[start:end]
class Verification:
def __init__(self):
self.checks = {}
self.first_error = "NONE"
def check(self, name, condition, error_code):
self.checks[name] = "PASS" if condition else "FAIL"
if not condition and self.first_error == "NONE":
self.first_error = error_code
def emit(self):
for name in sorted(self.checks):
print(f"{name}={self.checks[name]}")
print(f"PROFILE_ERROR_CODE={self.first_error}")
def verify(mode, config_path, expected_filename):
report = Verification()
try:
text = config_path.read_text(encoding="utf-8")
except OSError as exc:
print("CONFIG_READ_STATUS=FAIL")
print(f"CONFIG_READ_ERROR={exc}")
print("PROFILE_ERROR_CODE=CONFIG_READ_FAILED")
print("CONFIG_PROFILE_VERIFICATION_STATUS=FAIL")
return 1
expected_line = f' Filename = "{expected_filename}"'
filename_count = sum(1 for line in text.splitlines() if line == expected_line)
placeholder_count = text.count("__MARTE_RUN_DIR__")
canonical = text
if filename_count == 1:
canonical = text.replace(expected_line, PLACEHOLDER_LINE, 1)
canonical_hash = digest(canonical)
expected_profile_hash = EXPECTED_PROFILE_HASHES[mode]
expected_display_class = EXPECTED_DISPLAY_CLASSES[mode]
print(f"VERIFIED_MODE={mode}")
print(f"EXPECTED_FILENAME={expected_filename}")
print(f"EXPECTED_FILENAME_COUNT={filename_count}")
print(f"PLACEHOLDER_COUNT={placeholder_count}")
print(f"CANONICAL_CONFIG_SHA256={canonical_hash}")
print(f"EXPECTED_PROFILE_CONFIG_SHA256={expected_profile_hash}")
report.check(
"RUNTIME_FILENAME_STATUS",
filename_count == 1 and placeholder_count == 0,
"RUNTIME_FILENAME_MISMATCH",
)
report.check(
"GAMDISPLAY_OBJECT_STATUS",
text.count("+GAMDisplay =") == 1,
"GAMDISPLAY_OBJECT_COUNT_MISMATCH",
)
report.check(
"THREAD_ORDER_STATUS",
text.count(
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
)
== 1,
"THREAD_ORDER_MISMATCH",
)
try:
gamdisplay = extract_block(
canonical,
" +GAMDisplay = {\n",
" +IOGAM_Writer = {\n",
)
except ValueError as exc:
print(f"GAMDISPLAY_EXTRACTION_ERROR={exc}")
gamdisplay = ""
display_checks = {
"GAMDISPLAY_CLASS_COUNT": gamdisplay.count("Class = IOGAM") == 1,
"GAMDISPLAY_COUNTER_COUNT": gamdisplay.count(
" Counter = {\n"
)
== 2,
"GAMDISPLAY_TIME_COUNT": gamdisplay.count(" Time = {\n") == 2,
"GAMDISPLAY_CYCLETIME_COUNT": gamdisplay.count(
"State1_Thread1_CycleTime ="
)
== 2,
"GAMDISPLAY_SCALAR_A_COUNT": gamdisplay.count("SentinelScalarA =") == 2,
"GAMDISPLAY_SCALAR_B_COUNT": gamdisplay.count("SentinelScalarB =") == 2,
"GAMDISPLAY_VECTOR_COUNT": gamdisplay.count("SentinelVector =") == 2,
"GAMDISPLAY_VECTOR_DIMENSION_COUNT": gamdisplay.count(
"NumberOfDimensions = 1"
)
== 2,
"GAMDISPLAY_VECTOR_ELEMENTS_COUNT": gamdisplay.count(
"NumberOfElements = 3"
)
== 2,
"GAMDISPLAY_OUTPUT_DATASOURCE_COUNT": gamdisplay.count(
"DataSource = Display"
)
== 6,
}
for name, passed in display_checks.items():
report.check(name, passed, f"{name}_MISMATCH")
gamdisplay_hash = digest(gamdisplay)
print(f"GAMDISPLAY_BLOCK_SHA256={gamdisplay_hash}")
print(
"EXPECTED_GAMDISPLAY_BLOCK_SHA256="
f"{EXPECTED_GAMDISPLAY_BLOCK_SHA256}"
)
report.check(
"GAMDISPLAY_BLOCK_STATUS",
gamdisplay_hash == EXPECTED_GAMDISPLAY_BLOCK_SHA256,
"GAMDISPLAY_BLOCK_HASH_MISMATCH",
)
try:
writer = extract_block(
canonical,
" +IOGAM_Writer = {\n",
" }\n +Data = {\n",
)
except ValueError as exc:
print(f"IOGAM_WRITER_EXTRACTION_ERROR={exc}")
writer = ""
writer_hash = digest(writer)
print(f"IOGAM_WRITER_BLOCK_SHA256={writer_hash}")
print(
"EXPECTED_IOGAM_WRITER_BLOCK_SHA256="
f"{EXPECTED_IOGAM_WRITER_BLOCK_SHA256}"
)
report.check(
"IOGAM_WRITER_INVARIANT_STATUS",
writer_hash == EXPECTED_IOGAM_WRITER_BLOCK_SHA256,
"IOGAM_WRITER_BLOCK_HASH_MISMATCH",
)
try:
filewriter = extract_block(
canonical,
" +FileWriter = {\n",
" }\n +States = {\n",
)
except ValueError as exc:
print(f"FILEWRITER_EXTRACTION_ERROR={exc}")
filewriter = ""
filewriter_hash = digest(filewriter)
print(f"FILEWRITER_BLOCK_SHA256={filewriter_hash}")
print(
"EXPECTED_FILEWRITER_BLOCK_SHA256="
f"{EXPECTED_FILEWRITER_BLOCK_SHA256}"
)
report.check(
"FILEWRITER_INVARIANT_STATUS",
filewriter_hash == EXPECTED_FILEWRITER_BLOCK_SHA256,
"FILEWRITER_BLOCK_HASH_MISMATCH",
)
structural_expectations = {
"CONSTANT_GAM_COUNT": (text.count("Class = ConstantGAM"), 1),
"SENTINEL_SCALAR_A_DECLARATION_COUNT": (
text.count("SentinelScalarA ="),
6,
),
"SENTINEL_SCALAR_B_DECLARATION_COUNT": (
text.count("SentinelScalarB ="),
6,
),
"SENTINEL_VECTOR_DECLARATION_COUNT": (
text.count("SentinelVector ="),
6,
),
"VECTOR_DIMENSION_COUNT": (text.count("NumberOfDimensions = 1"), 6),
"VECTOR_CARDINALITY_COUNT": (text.count("NumberOfElements = 3"), 6),
"FILEWRITER_CLASS_COUNT": (
text.count("Class = FileDataSource::FileWriter"),
1,
),
"PLAIN_FILEWRITER_CLASS_COUNT": (text.count("Class = FileWriter"), 0),
}
for name, (observed, expected) in structural_expectations.items():
print(f"{name}={observed}")
print(f"EXPECTED_{name}={expected}")
report.check(
f"{name}_STATUS",
observed == expected,
f"{name}_MISMATCH",
)
expected_display_block = (
" +Display = {\n"
f" Class = {expected_display_class}\n"
" }\n"
)
report.check(
"DISPLAY_DATASOURCE_CLASS_STATUS",
text.count(expected_display_block) == 1,
"DISPLAY_DATASOURCE_CLASS_MISMATCH",
)
report.check(
"PROFILE_CANONICAL_HASH_STATUS",
canonical_hash == expected_profile_hash,
"PROFILE_CANONICAL_HASH_MISMATCH",
)
report.emit()
passed = all(value == "PASS" for value in report.checks.values())
print(f"CONFIG_PROFILE_VERIFICATION_STATUS={'PASS' if passed else 'FAIL'}")
return 0 if passed else 1
def main():
parser = argparse.ArgumentParser()
parser.add_argument("mode", choices=sorted(EXPECTED_PROFILE_HASHES))
parser.add_argument("configuration", type=Path)
parser.add_argument("expected_filename")
args = parser.parse_args()
return verify(args.mode, args.configuration, args.expected_filename)
if __name__ == "__main__":
sys.exit(main())
+508
View File
@@ -0,0 +1,508 @@
#!/usr/bin/env bash
set -u
set -o pipefail
readonly MINIMUM_SAMPLES="50"
readonly EXPECTED_PARSER_SHA256="91c4d66770197e1986ed91eb6a4870e7da7df5a522ffdf4329b6d9250f238307"
readonly EXPECTED_H1_VERIFIER_SHA256="2f1c41b186bd2e79a9fb51fab0461a7a0dce104bf0852c0ec1ade2c55a017443"
readonly EXPECTED_TEST_CONFIG_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
readonly EXPECTED_DIAGNOSTIC_CONFIG_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
SUITE="$(cd -- "$SCRIPT_DIR/../.." && pwd -P)"
PARSER="$SUITE/scenarios/S03_semantic_sentinels/parse_s03_binary.py"
H1_VERIFIER="$SUITE/scenarios/S03H1_execution_profiles/verify_s03h1_config.py"
MINIMAL_VERIFIER="$SCRIPT_DIR/verify_s03h3_minimal_structure.py"
TEST_CONFIG="$SUITE/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_test_from_s02.marte"
DIAGNOSTIC_CONFIG="$SUITE/configurations/generated/S03H1/from_patcher/smoke_s03h1_execution_profiles_diagnostic_from_s02.marte"
MINIMAL_CONFIG="$SUITE/configurations/generated/S03H3/from_patcher/smoke_s03h3_execution_profiles_minimal_from_s03h1_test.marte"
MODE="NOT_PROVIDED"
MODE_STATUS="NOT_DEMONSTRATED"
PROFILE_CONFIG_STATUS="NOT_DEMONSTRATED"
PROFILE_RUNTIME_STATUS="NOT_DEMONSTRATED"
OBSERVABILITY_STATUS="NOT_DEMONSTRATED"
OBSERVABILITY_REASON="NOT_EVALUATED"
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
SEMANTIC_STATUS="NOT_DEMONSTRATED"
TIMING_STATUS="NOT_EVALUATED"
MEASUREMENT_ELIGIBILITY="NO"
COMPLETE_SAMPLE_COUNT="NOT_DEMONSTRATED"
CONFIG_REPORT=""
RUNTIME_CONFIG_REPORT=""
BINARY_REPORT=""
HASH_REPORT=""
cleanup() {
rm -f -- \
"${CONFIG_REPORT:-}" \
"${RUNTIME_CONFIG_REPORT:-}" \
"${BINARY_REPORT:-}" \
"${HASH_REPORT:-}" 2>/dev/null || true
}
trap cleanup EXIT HUP INT TERM
emit_contract() {
printf 'MODE=%s\n' "$MODE"
printf 'MODE_STATUS=%s\n' "$MODE_STATUS"
printf 'PROFILE_CONFIG_STATUS=%s\n' "$PROFILE_CONFIG_STATUS"
printf 'PROFILE_RUNTIME_STATUS=%s\n' "$PROFILE_RUNTIME_STATUS"
printf 'OBSERVABILITY_STATUS=%s\n' "$OBSERVABILITY_STATUS"
printf 'OBSERVABILITY_REASON=%s\n' "$OBSERVABILITY_REASON"
printf 'FUNCTIONAL_STATUS=%s\n' "$FUNCTIONAL_STATUS"
printf 'SEMANTIC_STATUS=%s\n' "$SEMANTIC_STATUS"
printf 'TIMING_STATUS=%s\n' "$TIMING_STATUS"
printf 'MEASUREMENT_ELIGIBILITY=%s\n' "$MEASUREMENT_ELIGIBILITY"
printf 'COMPLETE_SAMPLE_COUNT=%s\n' "$COMPLETE_SAMPLE_COUNT"
}
fail_early() {
local code="$1"
local reason="$2"
printf '[NON DIMOSTRATO] %s\n' "$reason" >&2
OBSERVABILITY_REASON="$reason"
emit_contract
printf 'VALIDATION_RESULT=FAIL_REQUIRED_INPUT\n'
exit "$code"
}
count_lines() {
local pattern="$1"
local file="$2"
grep -Ec "$pattern" "$file" || true
}
report_value_exact() {
local key="$1"
local file="$2"
local count
count="$(grep -Ec "^${key}=" "$file" || true)"
if [ "$count" -ne 1 ]; then
return 1
fi
sed -n "s/^${key}=//p" "$file"
}
verify_identity() {
local file="$1"
local expected="$2"
[ -f "$file" ] || return 1
[ "$(sha256sum -- "$file" | awk '{print $1}')" = "$expected" ]
}
if [ "$#" -ne 2 ]; then
printf 'Uso: %s MINIMAL|TEST|DIAGNOSTIC <directory-run|run.log>\n' "$0" >&2
emit_contract
printf 'VALIDATION_RESULT=FAIL_INVALID_USAGE\n'
exit 2
fi
MODE="$1"
case "$MODE" in
MINIMAL)
SELECTED_CONFIG="$MINIMAL_CONFIG"
;;
TEST)
SELECTED_CONFIG="$TEST_CONFIG"
EXPECTED_SELECTED_CONFIG_SHA256="$EXPECTED_TEST_CONFIG_SHA256"
;;
DIAGNOSTIC)
SELECTED_CONFIG="$DIAGNOSTIC_CONFIG"
EXPECTED_SELECTED_CONFIG_SHA256="$EXPECTED_DIAGNOSTIC_CONFIG_SHA256"
;;
*)
MODE_STATUS="FAIL"
PROFILE_CONFIG_STATUS="FAIL"
PROFILE_RUNTIME_STATUS="FAIL"
OBSERVABILITY_STATUS="FAIL"
OBSERVABILITY_REASON="UNKNOWN_MODE"
emit_contract
printf 'VALIDATION_RESULT=FAIL_UNKNOWN_MODE\n'
exit 2
;;
esac
INPUT_PATH="$2"
if [ -d "$INPUT_PATH" ]; then
RUN_DIR="$(cd -- "$INPUT_PATH" && pwd -P)"
RUN_LOG="$RUN_DIR/run.log"
elif [ -f "$INPUT_PATH" ]; then
RUN_LOG="$(cd -- "$(dirname -- "$INPUT_PATH")" && pwd -P)/$(basename -- "$INPUT_PATH")"
RUN_DIR="$(dirname -- "$RUN_LOG")"
else
fail_early 21 "INPUT_PATH_NOT_FOUND"
fi
RUN_CONFIG="$RUN_DIR/configuration.marte"
OUTPUT_FILE="$RUN_DIR/s03_output.bin"
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
RUN_HASHES="$RUN_DIR/hashes.sha256"
MODE_FILE="$RUN_DIR/s03h3_mode.txt"
verify_identity "$PARSER" "$EXPECTED_PARSER_SHA256" || \
fail_early 21 "PARSER_IDENTITY_NOT_DEMONSTRATED"
verify_identity "$H1_VERIFIER" "$EXPECTED_H1_VERIFIER_SHA256" || \
fail_early 21 "H1_VERIFIER_IDENTITY_NOT_DEMONSTRATED"
[ -x "$MINIMAL_VERIFIER" ] || fail_early 21 "MINIMAL_VERIFIER_NOT_EXECUTABLE"
for required_file in \
"$RUN_LOG" \
"$RUN_CONFIG" \
"$OUTPUT_FILE" \
"$RUNNER_SUMMARY" \
"$RUN_HASHES" \
"$MODE_FILE" \
"$SELECTED_CONFIG"
do
[ -f "$required_file" ] || fail_early 21 "REQUIRED_FILE_MISSING"
done
[ -s "$RUN_LOG" ] || fail_early 21 "RUN_LOG_EMPTY"
[ -s "$RUN_HASHES" ] || fail_early 21 "RUN_HASH_MANIFEST_EMPTY"
SELECTED_CONFIG_SHA256="$(sha256sum -- "$SELECTED_CONFIG" | awk '{print $1}')"
if [ "$MODE" = "MINIMAL" ]; then
EXPECTED_SELECTED_CONFIG_SHA256="$SELECTED_CONFIG_SHA256"
fi
CANONICAL_CONFIG_IDENTITY_STATUS="PASS"
if [ "$SELECTED_CONFIG_SHA256" != "$EXPECTED_SELECTED_CONFIG_SHA256" ]; then
CANONICAL_CONFIG_IDENTITY_STATUS="FAIL"
fi
MODE_FILE_CONTENT_STATUS="PASS"
MODE_DECLARATION_COUNT=0
SCENARIO_DECLARATION_COUNT=0
CONFIG_HASH_DECLARATION_COUNT=0
while IFS= read -r metadata_line || [ -n "$metadata_line" ]; do
case "$metadata_line" in
'')
;;
SCENARIO=S03H3)
SCENARIO_DECLARATION_COUNT=$((SCENARIO_DECLARATION_COUNT + 1))
;;
MODE=MINIMAL|MODE=TEST|MODE=DIAGNOSTIC)
MODE_DECLARATION_COUNT=$((MODE_DECLARATION_COUNT + 1))
[ "${metadata_line#MODE=}" = "$MODE" ] || MODE_FILE_CONTENT_STATUS="FAIL"
;;
SELECTED_CONFIG_SHA256=*)
CONFIG_HASH_DECLARATION_COUNT=$((CONFIG_HASH_DECLARATION_COUNT + 1))
declared_config_sha256="${metadata_line#SELECTED_CONFIG_SHA256=}"
if [[ ! "$declared_config_sha256" =~ ^[0-9a-f]{64}$ ]] ||
[ "$declared_config_sha256" != "$EXPECTED_SELECTED_CONFIG_SHA256" ] ||
[ "$declared_config_sha256" != "$SELECTED_CONFIG_SHA256" ]; then
MODE_FILE_CONTENT_STATUS="FAIL"
fi
;;
*)
MODE_FILE_CONTENT_STATUS="FAIL"
;;
esac
done <"$MODE_FILE"
if [ "$MODE_FILE_CONTENT_STATUS" = "PASS" ] &&
[ "$CANONICAL_CONFIG_IDENTITY_STATUS" = "PASS" ] &&
[ "$MODE_DECLARATION_COUNT" -eq 1 ] &&
[ "$SCENARIO_DECLARATION_COUNT" -eq 1 ] &&
[ "$CONFIG_HASH_DECLARATION_COUNT" -eq 1 ]; then
MODE_STATUS="PASS"
else
MODE_STATUS="FAIL"
fi
CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h3_config.XXXXXX")" || \
fail_early 21 "CONFIG_REPORT_CREATION_FAILED"
RUNTIME_CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h3_runtime.XXXXXX")" || \
fail_early 21 "RUNTIME_REPORT_CREATION_FAILED"
BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h3_binary.XXXXXX")" || \
fail_early 21 "BINARY_REPORT_CREATION_FAILED"
HASH_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h3_hashes.XXXXXX")" || \
fail_early 21 "HASH_REPORT_CREATION_FAILED"
EXPECTED_FILENAME="$RUN_DIR/s03_output.bin"
CONFIG_RC=1
RUNTIME_CONFIG_RC=1
CANONICAL_STRUCTURAL_COMPLETENESS_STATUS="NOT_APPLICABLE"
MINIMAL_RUNTIME_CONFIG_STATUS="NOT_APPLICABLE"
if [ "$MODE" = "MINIMAL" ]; then
PYTHONHASHSEED=0 python3 "$MINIMAL_VERIFIER" \
CANONICAL "$TEST_CONFIG" "$MINIMAL_CONFIG" \
>"$CONFIG_REPORT" 2>&1
CONFIG_RC=$?
PYTHONHASHSEED=0 python3 "$MINIMAL_VERIFIER" \
RUNTIME "$MINIMAL_CONFIG" "$RUN_CONFIG" "$RUN_DIR" \
>"$RUNTIME_CONFIG_REPORT" 2>&1
RUNTIME_CONFIG_RC=$?
CANONICAL_STRUCTURAL_COMPLETENESS_STATUS="$(
report_value_exact CANONICAL_STRUCTURAL_COMPLETENESS_STATUS "$CONFIG_REPORT" || printf FAIL
)"
MINIMAL_RUNTIME_CONFIG_STATUS="$(
report_value_exact MINIMAL_RUNTIME_CONFIG_STATUS "$RUNTIME_CONFIG_REPORT" || printf FAIL
)"
if [ "$CONFIG_RC" -eq 0 ] &&
[ "$RUNTIME_CONFIG_RC" -eq 0 ] &&
[ "$CANONICAL_STRUCTURAL_COMPLETENESS_STATUS" = "PASS" ] &&
[ "$MINIMAL_RUNTIME_CONFIG_STATUS" = "PASS" ]; then
PROFILE_CONFIG_STATUS="PASS"
else
PROFILE_CONFIG_STATUS="FAIL"
fi
else
PYTHONHASHSEED=0 python3 "$H1_VERIFIER" \
"$MODE" "$RUN_CONFIG" "$EXPECTED_FILENAME" \
>"$CONFIG_REPORT" 2>&1
CONFIG_RC=$?
RUNTIME_CONFIG_RC=0
PROFILE_CONFIG_STATUS="$(
report_value_exact CONFIG_PROFILE_VERIFICATION_STATUS "$CONFIG_REPORT" || printf FAIL
)"
fi
GAMTIMER_RUNTIME_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function GAMTimer \[idx: 0\][[:space:]]*$' "$RUN_LOG")"
SENTINEL_RUNTIME_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function SentinelProducer \[idx: 1\][[:space:]]*$' "$RUN_LOG")"
GAMDISPLAY_IDX2_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function GAMDisplay \[idx: 2\][[:space:]]*$' "$RUN_LOG")"
IOGAM_IDX2_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function IOGAM_Writer \[idx: 2\][[:space:]]*$' "$RUN_LOG")"
IOGAM_IDX3_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function IOGAM_Writer \[idx: 3\][[:space:]]*$' "$RUN_LOG")"
FUNCTION_RESOLUTION_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving for function .* \[idx: [0-9]+\][[:space:]]*$' "$RUN_LOG")"
GAMTIMER_OBJECT_RESOLUTION_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving GAMTimer[[:space:]]*$' "$RUN_LOG")"
SENTINEL_OBJECT_RESOLUTION_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving SentinelProducer[[:space:]]*$' "$RUN_LOG")"
GAMDISPLAY_OBJECT_RESOLUTION_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving GAMDisplay[[:space:]]*$' "$RUN_LOG")"
IOGAM_OBJECT_RESOLUTION_LINES="$(count_lines '^\[Information - RealTimeApplicationConfigurationBuilder\.cpp:[0-9]+\]: Resolving IOGAM_Writer[[:space:]]*$' "$RUN_LOG")"
case "$MODE" in
MINIMAL)
if [ "$GAMTIMER_RUNTIME_LINES" -eq 1 ] &&
[ "$SENTINEL_RUNTIME_LINES" -eq 1 ] &&
[ "$GAMDISPLAY_IDX2_LINES" -eq 0 ] &&
[ "$IOGAM_IDX2_LINES" -eq 1 ] &&
[ "$IOGAM_IDX3_LINES" -eq 0 ] &&
[ "$GAMTIMER_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$SENTINEL_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$GAMDISPLAY_OBJECT_RESOLUTION_LINES" -eq 0 ] &&
[ "$IOGAM_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$FUNCTION_RESOLUTION_LINES" -eq 3 ]; then
PROFILE_RUNTIME_STATUS="PASS"
else
PROFILE_RUNTIME_STATUS="FAIL"
fi
;;
TEST|DIAGNOSTIC)
if [ "$GAMTIMER_RUNTIME_LINES" -eq 1 ] &&
[ "$SENTINEL_RUNTIME_LINES" -eq 1 ] &&
[ "$GAMDISPLAY_IDX2_LINES" -eq 1 ] &&
[ "$IOGAM_IDX2_LINES" -eq 0 ] &&
[ "$IOGAM_IDX3_LINES" -eq 1 ] &&
[ "$GAMTIMER_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$SENTINEL_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$GAMDISPLAY_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$IOGAM_OBJECT_RESOLUTION_LINES" -eq 1 ] &&
[ "$FUNCTION_RESOLUTION_LINES" -eq 4 ]; then
PROFILE_RUNTIME_STATUS="PASS"
else
PROFILE_RUNTIME_STATUS="FAIL"
fi
;;
esac
PYTHONHASHSEED=0 python3 "$PARSER" "$OUTPUT_FILE" --min-samples "$MINIMUM_SAMPLES" \
>"$BINARY_REPORT" 2>&1
PARSER_RC=$?
sha256sum -c --strict "$RUN_HASHES" >"$HASH_REPORT" 2>&1
HASH_RC=$?
FUNCTIONAL_BINARY_STATUS="$(report_value_exact FUNCTIONAL_BINARY_STATUS "$BINARY_REPORT" || printf FAIL)"
SEMANTIC_BINARY_STATUS="$(report_value_exact SEMANTIC_BINARY_STATUS "$BINARY_REPORT" || printf FAIL)"
BINARY_STATUS="$(report_value_exact BINARY_STATUS "$BINARY_REPORT" || printf FAIL)"
PARSER_PAYLOAD_ALIGNMENT_STATUS="$(report_value_exact PAYLOAD_ALIGNMENT_STATUS "$BINARY_REPORT" || printf FAIL)"
COMPLETE_SAMPLE_COUNT="$(report_value_exact COMPLETE_SAMPLE_COUNT "$BINARY_REPORT" || printf NOT_DEMONSTRATED)"
COMPLETE_SAMPLE_COUNT_STATUS="FAIL"
case "$COMPLETE_SAMPLE_COUNT" in
''|*[!0-9]*) ;;
*)
if [ "$PARSER_RC" -eq 0 ] &&
[ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] &&
[ "$SEMANTIC_BINARY_STATUS" = "PASS" ] &&
[ "$BINARY_STATUS" = "PASS" ] &&
[ "$PARSER_PAYLOAD_ALIGNMENT_STATUS" = "PASS" ]; then
COMPLETE_SAMPLE_COUNT_STATUS="PASS"
fi
;;
esac
LOGGER_SIGNAL_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: (Counter|Time|State1_Thread1_CycleTime|SentinelScalarA|SentinelScalarB|SentinelVector) \[[0-9]+:[0-9]+\]:' "$RUN_LOG")"
LOGGER_COUNTER_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Counter \[0:0\]:' "$RUN_LOG")"
LOGGER_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Time \[0:0\]:' "$RUN_LOG")"
LOGGER_CYCLE_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: State1_Thread1_CycleTime \[0:0\]:' "$RUN_LOG")"
LOGGER_SCALAR_A_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarA \[0:0\]:' "$RUN_LOG")"
LOGGER_SCALAR_B_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarB \[0:0\]:' "$RUN_LOG")"
LOGGER_VECTOR_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelVector \[0:2\]:' "$RUN_LOG")"
if [ "$COMPLETE_SAMPLE_COUNT_STATUS" != "PASS" ]; then
OBSERVABILITY_STATUS="NOT_DEMONSTRATED"
OBSERVABILITY_REASON="COMPLETE_SAMPLE_COUNT_NOT_AUTHORITATIVE"
elif [ "$MODE" = "MINIMAL" ] || [ "$MODE" = "TEST" ]; then
if [ "$LOGGER_SIGNAL_LINES" -eq 0 ] &&
[ "$LOGGER_COUNTER_LINES" -eq 0 ] &&
[ "$LOGGER_TIME_LINES" -eq 0 ] &&
[ "$LOGGER_CYCLE_TIME_LINES" -eq 0 ] &&
[ "$LOGGER_SCALAR_A_LINES" -eq 0 ] &&
[ "$LOGGER_SCALAR_B_LINES" -eq 0 ] &&
[ "$LOGGER_VECTOR_LINES" -eq 0 ]; then
OBSERVABILITY_STATUS="PASS"
OBSERVABILITY_REASON="NONE"
else
OBSERVABILITY_STATUS="FAIL"
OBSERVABILITY_REASON="UNEXPECTED_LOGGER_SIGNAL_LINES"
fi
else
if [ "$LOGGER_COUNTER_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_TIME_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_CYCLE_TIME_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_SCALAR_A_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_SCALAR_B_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_VECTOR_LINES" -eq "$COMPLETE_SAMPLE_COUNT" ] &&
[ "$LOGGER_SIGNAL_LINES" -eq $((6 * COMPLETE_SAMPLE_COUNT)) ]; then
OBSERVABILITY_STATUS="PASS"
OBSERVABILITY_REASON="NONE"
elif [ "$LOGGER_COUNTER_LINES" -eq "$LOGGER_TIME_LINES" ] &&
[ "$LOGGER_COUNTER_LINES" -eq "$LOGGER_CYCLE_TIME_LINES" ] &&
[ "$LOGGER_COUNTER_LINES" -eq "$LOGGER_SCALAR_A_LINES" ] &&
[ "$LOGGER_COUNTER_LINES" -eq "$LOGGER_SCALAR_B_LINES" ] &&
[ "$LOGGER_COUNTER_LINES" -eq "$LOGGER_VECTOR_LINES" ]; then
OBSERVABILITY_STATUS="NOT_DEMONSTRATED"
OBSERVABILITY_REASON="SHUTDOWN_SKEW"
else
OBSERVABILITY_STATUS="FAIL"
OBSERVABILITY_REASON="LOGGER_BINARY_COUNT_MISMATCH"
fi
fi
START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")"
APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")"
FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")"
SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")"
STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")"
TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")"
STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")"
FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")"
OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")"
ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")"
INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")"
PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")"
DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")"
ASYNC_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")"
MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_CLOSE_LINES))
UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - MUTEX_DESTROY_LINES))
[ "$UNCLASSIFIED_FATAL_LINES" -ge 0 ] || UNCLASSIFIED_FATAL_LINES=0
[ "$UNCLASSIFIED_OS_ERROR_LINES" -ge 0 ] || UNCLASSIFIED_OS_ERROR_LINES=0
MARTE_EXIT_CODE="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
TEE_EXIT_CODE="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
[ -n "$MARTE_EXIT_CODE" ] || MARTE_EXIT_CODE="NOT_AVAILABLE"
[ -n "$TEE_EXIT_CODE" ] || TEE_EXIT_CODE="NOT_AVAILABLE"
case "$MARTE_EXIT_CODE" in
137) TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL" ;;
*)
if [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then
TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED"
else
TIMING_STATUS="UNASSESSED_DT_SEMANTICS"
fi
;;
esac
if [ "$MODE_STATUS" = "PASS" ] &&
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
[ "$PROFILE_RUNTIME_STATUS" = "PASS" ] &&
[ "$OBSERVABILITY_STATUS" = "PASS" ] &&
[ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] &&
[ "$START_LINES" -eq 1 ] &&
[ "$APPLICATION_START_LINES" -eq 1 ] &&
[ "$FILE_OPEN_LINES" -eq 1 ] &&
[ "$STARTUP_ERROR_LINES" -eq 0 ] &&
[ "$HASH_RC" -eq 0 ] &&
[ "$TEE_EXIT_CODE" = "0" ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
FUNCTIONAL_STATUS="PASS"
else
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
fi
if [ "$MODE_STATUS" = "PASS" ] &&
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
[ "$SEMANTIC_BINARY_STATUS" = "PASS" ]; then
SEMANTIC_STATUS="PASS"
elif [ "$SEMANTIC_BINARY_STATUS" = "FAIL" ]; then
SEMANTIC_STATUS="FAIL"
else
SEMANTIC_STATUS="NOT_DEMONSTRATED"
fi
printf '============================================================\n'
printf 'VALIDAZIONE S03-H3 - MINIMAL / TEST / DIAGNOSTIC\n'
printf '============================================================\n'
printf 'Run directory: %s\n' "$RUN_DIR"
printf 'CANONICAL_STRUCTURAL_COMPLETENESS_STATUS=%s\n' "$CANONICAL_STRUCTURAL_COMPLETENESS_STATUS"
printf 'MINIMAL_RUNTIME_CONFIG_STATUS=%s\n' "$MINIMAL_RUNTIME_CONFIG_STATUS"
printf 'FUNCTION_RESOLUTION_LINES=%s\n' "$FUNCTION_RESOLUTION_LINES"
printf 'LOGGER_SIGNAL_LINES=%s\n' "$LOGGER_SIGNAL_LINES"
printf 'LOGGER_COUNTER_LINES=%s\n' "$LOGGER_COUNTER_LINES"
printf 'LOGGER_TIME_LINES=%s\n' "$LOGGER_TIME_LINES"
printf 'LOGGER_CYCLE_TIME_LINES=%s\n' "$LOGGER_CYCLE_TIME_LINES"
printf 'LOGGER_SCALAR_A_LINES=%s\n' "$LOGGER_SCALAR_A_LINES"
printf 'LOGGER_SCALAR_B_LINES=%s\n' "$LOGGER_SCALAR_B_LINES"
printf 'LOGGER_VECTOR_LINES=%s\n' "$LOGGER_VECTOR_LINES"
printf 'PARSER_RC=%s\n' "$PARSER_RC"
printf 'HASH_VERIFICATION_STATUS=%s\n' "$( [ "$HASH_RC" -eq 0 ] && printf PASS || printf FAIL )"
printf '\n--- CONFIGURATION VERIFIER DETAIL ---\n'
if [ "$MODE" = "MINIMAL" ]; then
sed 's/^/MINIMAL_CANONICAL_VERIFIER_/' "$CONFIG_REPORT"
sed 's/^/MINIMAL_RUNTIME_VERIFIER_/' "$RUNTIME_CONFIG_REPORT"
else
cat -- "$CONFIG_REPORT"
fi
printf '\n--- BINARY PARSER DETAIL ---\n'
sed 's/^/BINARY_PARSER_/' "$BINARY_REPORT"
printf '\n--- HASH DETAIL ---\n'
cat -- "$HASH_REPORT"
printf '\n--- CONTRATTO S03-H3 ---\n'
emit_contract
if [ "$FUNCTIONAL_STATUS" != "PASS" ] || [ "$SEMANTIC_STATUS" != "PASS" ]; then
printf 'VALIDATION_RESULT=FUNCTIONAL_OR_SEMANTIC_NOT_DEMONSTRATED\n'
exit 20
fi
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
[ "$OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf 'VALIDATION_RESULT=PASS_CLEAN_SHUTDOWN\n'
exit 0
fi
KNOWN_SHUTDOWN_TOTAL=$((DOUBLE_STOP_LINES + ASYNC_CLOSE_LINES + MUTEX_DESTROY_LINES))
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf 'VALIDATION_RESULT=PASS_KNOWN_SHUTDOWN_ANOMALY\n'
exit 10
fi
printf 'VALIDATION_RESULT=PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY\n'
exit 11
@@ -0,0 +1,331 @@
#!/usr/bin/env python3
"""Fail-closed structural verifier for the S03-H3 MINIMAL profile."""
from __future__ import annotations
import argparse
import hashlib
import os
import re
import sys
from pathlib import Path
EXPECTED_TEST_SHA256 = (
"d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
)
PLACEHOLDER = "__MARTE_RUN_DIR__"
TEST_FUNCTIONS = (
" Functions = "
"{GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
)
MINIMAL_FUNCTIONS = (
" Functions = "
"{GAMTimer SentinelProducer IOGAM_Writer}"
)
def sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def read_utf8(path: Path) -> tuple[bytes, str]:
data = path.read_bytes()
return data, data.decode("utf-8")
def object_matches(text: str, name: str) -> list[re.Match[str]]:
pattern = re.compile(
rf"(?m)^[ \t]*\+{re.escape(name)}[ \t]*=[ \t]*\{{[ \t]*(?:\r?\n|$)"
)
return list(pattern.finditer(text))
def object_count(text: str, name: str) -> int:
return len(object_matches(text, name))
def block_span(text: str, name: str) -> tuple[int, int]:
matches = object_matches(text, name)
if len(matches) != 1:
raise ValueError(f"{name.upper()}_ANCHOR_COUNT_{len(matches)}")
match = matches[0]
opening = text.find("{", match.start(), match.end())
if opening < 0:
raise ValueError(f"{name.upper()}_OPENING_BRACE_MISSING")
depth = 0
quoted = False
escaped = False
closing = -1
for index in range(opening, len(text)):
char = text[index]
if quoted:
if escaped:
escaped = False
elif char == "\\":
escaped = True
elif char == '"':
quoted = False
continue
if char == '"':
quoted = True
elif char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0:
closing = index + 1
break
if depth < 0:
raise ValueError(f"{name.upper()}_UNBALANCED_BRACE")
if closing < 0:
raise ValueError(f"{name.upper()}_CLOSING_BRACE_MISSING")
if text.startswith("\r\n", closing):
closing += 2
elif text.startswith("\n", closing):
closing += 1
return match.start(), closing
def derive_expected_minimal(canonical: str) -> str:
if canonical.count(TEST_FUNCTIONS) != 1:
raise ValueError(
f"FUNCTIONS_ANCHOR_COUNT_{canonical.count(TEST_FUNCTIONS)}"
)
if canonical.count(MINIMAL_FUNCTIONS) != 0:
raise ValueError("SOURCE_ALREADY_PARTIALLY_TRANSFORMED")
spans = [
block_span(canonical, "GAMDisplay"),
block_span(canonical, "Display"),
]
if spans[0][0] < spans[1][1] and spans[1][0] < spans[0][1]:
raise ValueError("AUTHORIZED_BLOCK_SPANS_OVERLAP")
transformed = canonical
for start, end in sorted(spans, reverse=True):
transformed = transformed[:start] + transformed[end:]
transformed = transformed.replace(TEST_FUNCTIONS, MINIMAL_FUNCTIONS, 1)
return transformed
class Report:
def __init__(self) -> None:
self.values: list[tuple[str, str]] = []
self.failed = False
self.first_error = "NONE"
def value(self, name: str, value: object) -> None:
self.values.append((name, str(value)))
def status(self, name: str, passed: bool, error: str) -> None:
self.value(name, "PASS" if passed else "FAIL")
if not passed:
self.failed = True
if self.first_error == "NONE":
self.first_error = error
def emit(self) -> int:
for name, value in self.values:
print(f"{name}={value}")
print(f"MINIMAL_VERIFIER_ERROR={self.first_error}")
return 1 if self.failed else 0
def emit_canonical_failure(error: str) -> int:
report = Report()
report.value("VERIFICATION_MODE", "CANONICAL")
report.value("GAMDisplay_OBJECT_COUNT", 0)
report.value("Display_DATASOURCE_COUNT", 0)
report.value("GAMDisplay_THREAD_TOKEN_COUNT", 0)
report.value("GAMTimer_OBJECT_COUNT", 0)
report.value("SentinelProducer_OBJECT_COUNT", 0)
report.value("IOGAM_Writer_OBJECT_COUNT", 0)
report.value("FileWriter_OBJECT_COUNT", 0)
report.status("THREAD_FUNCTIONS_STATUS", False, error)
report.status("BINARY_PATH_STRUCTURE_STATUS", False, error)
report.status("CANONICAL_STRUCTURAL_COMPLETENESS_STATUS", False, error)
report.status("PROFILE_CONFIG_STATUS", False, error)
return report.emit()
def verify_canonical(canonical_path: Path, minimal_path: Path) -> int:
try:
canonical_bytes, canonical = read_utf8(canonical_path)
minimal_bytes, minimal = read_utf8(minimal_path)
except (OSError, UnicodeError) as exc:
return emit_canonical_failure(f"INPUT_READ_FAILED_{type(exc).__name__}")
canonical_hash = sha256_bytes(canonical_bytes)
try:
expected_minimal = derive_expected_minimal(canonical)
except ValueError as exc:
return emit_canonical_failure(str(exc))
expected_bytes = expected_minimal.encode("utf-8")
complete = minimal_bytes == expected_bytes
report = Report()
report.value("VERIFICATION_MODE", "CANONICAL")
report.value("CANONICAL_TEST_SHA256", canonical_hash)
report.value("EXPECTED_CANONICAL_TEST_SHA256", EXPECTED_TEST_SHA256)
report.value("MINIMAL_CONFIG_SHA256", sha256_bytes(minimal_bytes))
report.value("EXPECTED_MINIMAL_CONFIG_SHA256", sha256_bytes(expected_bytes))
counts = {
"GAMDisplay_OBJECT_COUNT": object_count(minimal, "GAMDisplay"),
"Display_DATASOURCE_COUNT": object_count(minimal, "Display"),
"GAMDisplay_THREAD_TOKEN_COUNT": len(
re.findall(r"\bGAMDisplay\b", minimal)
),
"GAMTimer_OBJECT_COUNT": object_count(minimal, "GAMTimer"),
"SentinelProducer_OBJECT_COUNT": object_count(
minimal, "SentinelProducer"
),
"IOGAM_Writer_OBJECT_COUNT": object_count(minimal, "IOGAM_Writer"),
"FileWriter_OBJECT_COUNT": object_count(minimal, "FileWriter"),
}
for name, value in counts.items():
report.value(name, value)
expected_counts = {
"GAMDisplay_OBJECT_COUNT": 0,
"Display_DATASOURCE_COUNT": 0,
"GAMDisplay_THREAD_TOKEN_COUNT": 0,
"GAMTimer_OBJECT_COUNT": 1,
"SentinelProducer_OBJECT_COUNT": 1,
"IOGAM_Writer_OBJECT_COUNT": 1,
"FileWriter_OBJECT_COUNT": 1,
}
counts_pass = counts == expected_counts
thread_pass = minimal.count(MINIMAL_FUNCTIONS) == 1
binary_pass = (
counts["IOGAM_Writer_OBJECT_COUNT"] == 1
and counts["FileWriter_OBJECT_COUNT"] == 1
and minimal.count('Filename = "__MARTE_RUN_DIR__/s03_output.bin"') == 1
and minimal.count('FileFormat = "binary"') == 1
and minimal.count("DataSource = FileWriter") == 6
)
parent_pass = canonical_hash == EXPECTED_TEST_SHA256
report.value("UNAUTHORIZED_ADDITION_COUNT", 0 if complete else 1)
report.status(
"CANONICAL_TEST_IDENTITY_STATUS",
parent_pass,
"CANONICAL_TEST_SHA256_MISMATCH",
)
report.status(
"AUTHORIZED_ABSENCE_AND_PRESENCE_STATUS",
counts_pass,
"OBJECT_OR_TOKEN_COUNT_MISMATCH",
)
report.status(
"THREAD_FUNCTIONS_STATUS", thread_pass, "THREAD_FUNCTIONS_MISMATCH"
)
report.status(
"BINARY_PATH_STRUCTURE_STATUS",
binary_pass,
"BINARY_PATH_STRUCTURE_MISMATCH",
)
report.status(
"CANONICAL_STRUCTURAL_COMPLETENESS_STATUS",
parent_pass and complete,
"UNAUTHORIZED_STRUCTURAL_DIFFERENCE",
)
report.status(
"PROFILE_CONFIG_STATUS",
parent_pass and complete and counts_pass and thread_pass and binary_pass,
"MINIMAL_PROFILE_CONFIG_NOT_PROVEN",
)
return report.emit()
def extract_runtime_filename(text: str) -> list[str]:
return re.findall(r'(?m)^[ \t]*Filename[ \t]*=[ \t]*"([^"]+)"[ \t]*$', text)
def verify_runtime(packaged_path: Path, runtime_path: Path, run_dir: Path) -> int:
report = Report()
report.value("VERIFICATION_MODE", "RUNTIME")
try:
packaged_bytes, packaged = read_utf8(packaged_path)
runtime_bytes, runtime = read_utf8(runtime_path)
except (OSError, UnicodeError) as exc:
report.status(
"MINIMAL_RUNTIME_CONFIG_STATUS",
False,
f"INPUT_READ_FAILED_{type(exc).__name__}",
)
return report.emit()
supplied = str(run_dir)
absolute = run_dir.is_absolute()
exists = run_dir.is_dir()
placeholder_count = packaged.count(PLACEHOLDER)
unresolved_count = runtime.count(PLACEHOLDER)
expected_runtime = packaged.replace(PLACEHOLDER, supplied)
exact = runtime_bytes == expected_runtime.encode("utf-8")
filenames = extract_runtime_filename(runtime)
expected_filename = os.path.join(supplied, "s03_output.bin")
filename_pass = filenames.count(expected_filename) == 1 and len(filenames) == 1
report.value("PACKAGED_MINIMAL_SHA256", sha256_bytes(packaged_bytes))
report.value("RUNTIME_CONFIG_SHA256", sha256_bytes(runtime_bytes))
report.value("PACKAGED_PLACEHOLDER_COUNT", placeholder_count)
report.value("RUNTIME_UNRESOLVED_PLACEHOLDER_COUNT", unresolved_count)
report.value("RUNTIME_FILENAME", filenames[0] if len(filenames) == 1 else "AMBIGUOUS")
report.value("EXPECTED_RUNTIME_FILENAME", expected_filename)
report.status("RUN_DIR_ABSOLUTE_STATUS", absolute, "RUN_DIR_NOT_ABSOLUTE")
report.status("RUN_DIR_EXISTS_STATUS", exists, "RUN_DIR_NOT_FOUND")
report.status(
"PACKAGED_PLACEHOLDER_STATUS",
placeholder_count == 1,
"PACKAGED_PLACEHOLDER_COUNT_MISMATCH",
)
report.status(
"RUNTIME_PLACEHOLDER_RESOLUTION_STATUS",
unresolved_count == 0,
"RUNTIME_PLACEHOLDER_UNRESOLVED",
)
report.status(
"RUNTIME_FILENAME_STATUS", filename_pass, "RUNTIME_FILENAME_OUTSIDE_RUN_DIR"
)
report.status(
"MINIMAL_RUNTIME_CONFIG_STATUS",
absolute
and exists
and placeholder_count == 1
and unresolved_count == 0
and filename_pass
and exact,
"RUNTIME_DIFF_EXCEEDS_AUTHORIZED_SUBSTITUTION",
)
return report.emit()
def main() -> int:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="mode", required=True)
canonical = subparsers.add_parser("CANONICAL")
canonical.add_argument("canonical_test", type=Path)
canonical.add_argument("packaged_minimal", type=Path)
runtime = subparsers.add_parser("RUNTIME")
runtime.add_argument("packaged_minimal", type=Path)
runtime.add_argument("runtime_configuration", type=Path)
runtime.add_argument("run_dir", type=Path)
args = parser.parse_args()
if args.mode == "CANONICAL":
return verify_canonical(args.canonical_test, args.packaged_minimal)
return verify_runtime(
args.packaged_minimal, args.runtime_configuration, args.run_dir
)
if __name__ == "__main__":
sys.exit(main())
+589
View File
@@ -0,0 +1,589 @@
#!/usr/bin/env python3
"""Fail-closed parser for the MARTe2 S03 FileWriter binary contract."""
import argparse
import struct
import sys
from pathlib import Path
UINT32_TYPE_HEX = "0408"
UINT32_BYTES = 4
SIGNAL_HEADER_BYTES = 2 + 32 + 4
SENTINEL_SCALAR_A = 324508639 # 0x13579BDF
SENTINEL_SCALAR_B = 610839776 # 0x2468ACE0
SENTINEL_VECTOR = (16909060, 286397204, 555885348)
EXPECTED_SIGNALS = (
("Counter", UINT32_TYPE_HEX, 1),
("Time", UINT32_TYPE_HEX, 1),
("State1_Thread1_CycleTime", UINT32_TYPE_HEX, 1),
("SentinelScalarA", UINT32_TYPE_HEX, 1),
("SentinelScalarB", UINT32_TYPE_HEX, 1),
("SentinelVector", UINT32_TYPE_HEX, 3),
)
EXPECTED_OFFSETS = (0, 4, 8, 12, 16, 20)
EXPECTED_RECORD_BYTES = 32
EXPECTED_HEADER_BYTES = 4 + len(EXPECTED_SIGNALS) * SIGNAL_HEADER_BYTES
STATUS_ORDER = (
"HEADER_STATUS",
"RECORD_LAYOUT_STATUS",
"PAYLOAD_ALIGNMENT_STATUS",
"TRUNCATION_STATUS",
"SIGNAL_COUNT_STATUS",
"SIGNAL_NAMES_STATUS",
"SIGNAL_TYPES_STATUS",
"SIGNAL_ELEMENTS_STATUS",
"SIGNAL_ORDER_STATUS",
"COUNTER_STATUS",
"TIME_STATUS",
"CYCLE_TIME_STATUS",
"SENTINEL_SCALAR_A_STATUS",
"SENTINEL_SCALAR_B_STATUS",
"SENTINEL_VECTOR_STATUS",
"SENTINEL_SEQUENCE_STATUS",
"FUNCTIONAL_BINARY_STATUS",
"SEMANTIC_BINARY_STATUS",
"BINARY_STATUS",
)
def safe_value(value):
return str(value).replace("\r", "\\r").replace("\n", "\\n")
class Report:
def __init__(self):
self.status = {name: "NOT_EVALUATED" for name in STATUS_ORDER}
self.details = {}
self.first_error = {
"FIRST_ERROR_CODE": "NONE",
"EXPECTED": "NOT_APPLICABLE",
"OBSERVED": "NOT_APPLICABLE",
"RECORD_INDEX": "NOT_APPLICABLE",
"SIGNAL_NAME": "NOT_APPLICABLE",
"ELEMENT_INDEX": "NOT_APPLICABLE",
"BYTE_OFFSET": "NOT_APPLICABLE",
}
def set_error(
self,
code,
expected="NOT_APPLICABLE",
observed="NOT_APPLICABLE",
record_index="NOT_APPLICABLE",
signal_name="NOT_APPLICABLE",
element_index="NOT_APPLICABLE",
byte_offset="NOT_APPLICABLE",
):
if self.first_error["FIRST_ERROR_CODE"] != "NONE":
return
self.first_error.update(
{
"FIRST_ERROR_CODE": safe_value(code),
"EXPECTED": safe_value(expected),
"OBSERVED": safe_value(observed),
"RECORD_INDEX": safe_value(record_index),
"SIGNAL_NAME": safe_value(signal_name),
"ELEMENT_INDEX": safe_value(element_index),
"BYTE_OFFSET": safe_value(byte_offset),
}
)
def emit(self):
for key in STATUS_ORDER:
print(f"{key}={self.status[key]}")
for key in sorted(self.details):
print(f"{key}={safe_value(self.details[key])}")
for key in (
"FIRST_ERROR_CODE",
"EXPECTED",
"OBSERVED",
"RECORD_INDEX",
"SIGNAL_NAME",
"ELEMENT_INDEX",
"BYTE_OFFSET",
):
print(f"{key}={self.first_error[key]}")
def padded_name(raw):
try:
return raw.split(b"\x00", 1)[0].decode("ascii")
except UnicodeDecodeError:
return raw.split(b"\x00", 1)[0].decode("ascii", errors="replace")
def finish_early(report, exit_code):
report.status["FUNCTIONAL_BINARY_STATUS"] = "FAIL"
report.status["SEMANTIC_BINARY_STATUS"] = "NOT_DEMONSTRATED"
report.status["BINARY_STATUS"] = "FAIL"
report.emit()
return exit_code
def parse_binary(path, min_samples):
report = Report()
try:
data = path.read_bytes()
except OSError as exc:
report.status["HEADER_STATUS"] = "FAIL"
report.status["TRUNCATION_STATUS"] = "FAIL"
report.details["FILE_SIZE_BYTES"] = "NOT_AVAILABLE"
report.set_error("FILE_READ_ERROR", observed=exc)
return report, finish_early(report, 31), True
report.details["FILE_SIZE_BYTES"] = len(data)
report.details["EXPECTED_HEADER_BYTES"] = EXPECTED_HEADER_BYTES
report.details["EXPECTED_RECORD_BYTES"] = EXPECTED_RECORD_BYTES
report.details["EXPECTED_SIGNAL_COUNT"] = len(EXPECTED_SIGNALS)
if len(data) < 4:
report.status["HEADER_STATUS"] = "FAIL"
report.status["TRUNCATION_STATUS"] = "FAIL"
report.status["SIGNAL_COUNT_STATUS"] = "NOT_EVALUATED"
report.set_error(
"HEADER_TOO_SMALL",
expected="AT_LEAST_4_BYTES",
observed=f"{len(data)}_BYTES",
byte_offset=len(data),
)
return report, 31, False
signal_count = struct.unpack_from("<I", data, 0)[0]
report.details["OBSERVED_SIGNAL_COUNT"] = signal_count
if signal_count > 4096:
report.status["HEADER_STATUS"] = "FAIL"
report.status["TRUNCATION_STATUS"] = "FAIL"
report.status["SIGNAL_COUNT_STATUS"] = "FAIL"
report.set_error(
"SIGNAL_COUNT_UNREASONABLE",
expected=len(EXPECTED_SIGNALS),
observed=signal_count,
byte_offset=0,
)
return report, 32, False
if signal_count == len(EXPECTED_SIGNALS):
report.status["SIGNAL_COUNT_STATUS"] = "PASS"
else:
report.status["SIGNAL_COUNT_STATUS"] = "FAIL"
report.set_error(
"SIGNAL_COUNT_MISMATCH",
expected=len(EXPECTED_SIGNALS),
observed=signal_count,
byte_offset=0,
)
offset = 4
observed = []
for index in range(signal_count):
if offset + SIGNAL_HEADER_BYTES > len(data):
report.status["HEADER_STATUS"] = "FAIL"
report.status["TRUNCATION_STATUS"] = "FAIL"
report.set_error(
"TRUNCATED_SIGNAL_HEADER",
expected=SIGNAL_HEADER_BYTES,
observed=max(0, len(data) - offset),
signal_name=f"SIGNAL_{index}",
byte_offset=offset,
)
report.details["OBSERVED_HEADER_BYTES"] = offset
return report, 33, False
type_offset = offset
type_hex = data[offset : offset + 2].hex()
offset += 2
name_offset = offset
name = padded_name(data[offset : offset + 32])
offset += 32
elements_offset = offset
elements = struct.unpack_from("<I", data, offset)[0]
offset += 4
observed.append(
{
"name": name,
"type_hex": type_hex,
"elements": elements,
"type_offset": type_offset,
"name_offset": name_offset,
"elements_offset": elements_offset,
}
)
report.details[f"SIGNAL_{index}_NAME"] = name
report.details[f"SIGNAL_{index}_TYPE_HEX"] = type_hex
report.details[f"SIGNAL_{index}_NUMBER_OF_ELEMENTS"] = elements
report.status["HEADER_STATUS"] = "PASS"
report.details["OBSERVED_HEADER_BYTES"] = offset
header_bytes = offset
observed_names = [item["name"] for item in observed]
expected_names = [item[0] for item in EXPECTED_SIGNALS]
report.details["OBSERVED_SIGNAL_NAMES"] = ",".join(observed_names)
report.details["EXPECTED_SIGNAL_NAMES"] = ",".join(expected_names)
duplicates = sorted({name for name in observed_names if observed_names.count(name) > 1})
if duplicates:
report.status["SIGNAL_NAMES_STATUS"] = "FAIL"
report.status["SIGNAL_ORDER_STATUS"] = "FAIL"
report.set_error(
"DUPLICATE_SIGNAL_NAME",
expected="UNIQUE_NAMES",
observed=",".join(duplicates),
)
elif sorted(observed_names) == sorted(expected_names):
report.status["SIGNAL_NAMES_STATUS"] = "PASS"
if observed_names == expected_names:
report.status["SIGNAL_ORDER_STATUS"] = "PASS"
else:
report.status["SIGNAL_ORDER_STATUS"] = "FAIL"
first_index = next(
index
for index, pair in enumerate(zip(observed_names, expected_names))
if pair[0] != pair[1]
)
report.set_error(
"SIGNAL_ORDER_MISMATCH",
expected=expected_names[first_index],
observed=observed_names[first_index],
signal_name=observed_names[first_index],
byte_offset=observed[first_index]["name_offset"],
)
else:
report.status["SIGNAL_NAMES_STATUS"] = "FAIL"
report.status["SIGNAL_ORDER_STATUS"] = "FAIL"
mismatch_index = 0
for mismatch_index in range(min(len(observed_names), len(expected_names))):
if observed_names[mismatch_index] != expected_names[mismatch_index]:
break
observed_name = (
observed_names[mismatch_index]
if mismatch_index < len(observed_names)
else "MISSING"
)
expected_name = (
expected_names[mismatch_index]
if mismatch_index < len(expected_names)
else "NO_ADDITIONAL_SIGNAL"
)
byte_offset = (
observed[mismatch_index]["name_offset"]
if mismatch_index < len(observed)
else header_bytes
)
report.set_error(
"SIGNAL_NAME_MISMATCH",
expected=expected_name,
observed=observed_name,
signal_name=observed_name,
byte_offset=byte_offset,
)
types_ok = len(observed) == len(EXPECTED_SIGNALS)
elements_ok = len(observed) == len(EXPECTED_SIGNALS)
observed_offsets = []
running_offset = 0
for index, item in enumerate(observed):
observed_offsets.append(running_offset)
width = UINT32_BYTES if item["type_hex"] == UINT32_TYPE_HEX else UINT32_BYTES
running_offset += width * item["elements"]
report.details[f"SIGNAL_{index}_OBSERVED_OFFSET"] = observed_offsets[-1]
if index < len(EXPECTED_OFFSETS):
report.details[f"SIGNAL_{index}_EXPECTED_OFFSET"] = EXPECTED_OFFSETS[index]
if index >= len(EXPECTED_SIGNALS):
types_ok = False
elements_ok = False
continue
expected_name, expected_type, expected_elements = EXPECTED_SIGNALS[index]
if item["type_hex"] != expected_type:
types_ok = False
report.set_error(
"SIGNAL_TYPE_MISMATCH",
expected=expected_type,
observed=item["type_hex"],
signal_name=expected_name,
byte_offset=item["type_offset"],
)
if item["elements"] != expected_elements:
elements_ok = False
report.set_error(
"SIGNAL_ELEMENTS_MISMATCH",
expected=expected_elements,
observed=item["elements"],
signal_name=expected_name,
byte_offset=item["elements_offset"],
)
report.status["SIGNAL_TYPES_STATUS"] = "PASS" if types_ok else "FAIL"
report.status["SIGNAL_ELEMENTS_STATUS"] = "PASS" if elements_ok else "FAIL"
report.details["OBSERVED_RECORD_BYTES_FROM_HEADER"] = running_offset
layout_ok = all(
report.status[name] == "PASS"
for name in (
"SIGNAL_COUNT_STATUS",
"SIGNAL_NAMES_STATUS",
"SIGNAL_TYPES_STATUS",
"SIGNAL_ELEMENTS_STATUS",
"SIGNAL_ORDER_STATUS",
)
) and observed_offsets == list(EXPECTED_OFFSETS)
report.status["RECORD_LAYOUT_STATUS"] = "PASS" if layout_ok else "FAIL"
if not layout_ok and report.first_error["FIRST_ERROR_CODE"] == "NONE":
report.set_error(
"RECORD_LAYOUT_MISMATCH",
expected=EXPECTED_RECORD_BYTES,
observed=running_offset,
)
payload_bytes = len(data) - header_bytes
remainder_bytes = payload_bytes % EXPECTED_RECORD_BYTES
sample_count = payload_bytes // EXPECTED_RECORD_BYTES
report.details["PAYLOAD_BYTES"] = payload_bytes
report.details["PAYLOAD_REMAINDER_BYTES"] = remainder_bytes
report.details["COMPLETE_SAMPLE_COUNT"] = sample_count
report.details["MINIMUM_SAMPLE_COUNT"] = min_samples
if remainder_bytes == 0:
report.status["PAYLOAD_ALIGNMENT_STATUS"] = "PASS"
report.status["TRUNCATION_STATUS"] = "PASS"
else:
report.status["PAYLOAD_ALIGNMENT_STATUS"] = "FAIL"
report.status["TRUNCATION_STATUS"] = "FAIL"
report.set_error(
"PAYLOAD_SIZE_NOT_MULTIPLE_OF_RECORD",
expected=f"MULTIPLE_OF_{EXPECTED_RECORD_BYTES}",
observed=f"REMAINDER_{remainder_bytes}",
byte_offset=header_bytes + sample_count * EXPECTED_RECORD_BYTES,
)
if sample_count < min_samples:
report.set_error(
"INSUFFICIENT_SAMPLES",
expected=f"AT_LEAST_{min_samples}",
observed=sample_count,
)
records = []
if remainder_bytes == 0:
for record_index in range(sample_count):
start = header_bytes + record_index * EXPECTED_RECORD_BYTES
records.append(struct.unpack_from("<8I", data, start))
counters = [record[0] for record in records]
times = [record[1] for record in records]
cycle_times = [record[2] for record in records]
counter_error = None
if counters:
if counters[0] != 1:
counter_error = (0, 1, counters[0])
else:
for index in range(1, len(counters)):
if counters[index] != counters[index - 1] + 1:
counter_error = (index, counters[index - 1] + 1, counters[index])
break
else:
counter_error = ("NOT_APPLICABLE", "AT_LEAST_ONE_RECORD", "NO_RECORDS")
if counter_error is None:
report.status["COUNTER_STATUS"] = "PASS"
else:
report.status["COUNTER_STATUS"] = "FAIL"
record_index, expected_counter, observed_counter = counter_error
byte_offset = (
header_bytes + int(record_index) * EXPECTED_RECORD_BYTES
if isinstance(record_index, int)
else header_bytes
)
report.set_error(
"COUNTER_SEQUENCE_MISMATCH",
expected=expected_counter,
observed=observed_counter,
record_index=record_index,
signal_name="Counter",
element_index=0,
byte_offset=byte_offset,
)
time_error = None
for index in range(1, len(times)):
if times[index] < times[index - 1]:
time_error = (index, f">={times[index - 1]}", times[index])
break
if records and time_error is None:
report.status["TIME_STATUS"] = "PASS"
else:
report.status["TIME_STATUS"] = "FAIL"
if time_error is not None:
record_index, expected_time, observed_time = time_error
report.set_error(
"TIME_REGRESSION",
expected=expected_time,
observed=observed_time,
record_index=record_index,
signal_name="Time",
element_index=0,
byte_offset=header_bytes + record_index * EXPECTED_RECORD_BYTES + 4,
)
nonzero_cycle_times = [value for value in cycle_times if value > 0]
if records and nonzero_cycle_times:
report.status["CYCLE_TIME_STATUS"] = "PASS"
else:
report.status["CYCLE_TIME_STATUS"] = "FAIL"
report.set_error(
"NO_NONZERO_CYCLE_TIME",
expected="AT_LEAST_ONE_NONZERO_SAMPLE",
observed=len(nonzero_cycle_times),
signal_name="State1_Thread1_CycleTime",
byte_offset=header_bytes + 8,
)
sentinel_failures = {
"A": None,
"B": None,
"VECTOR": None,
}
for record_index, record in enumerate(records):
if sentinel_failures["A"] is None and record[3] != SENTINEL_SCALAR_A:
sentinel_failures["A"] = (record_index, 0, SENTINEL_SCALAR_A, record[3], 12)
if sentinel_failures["B"] is None and record[4] != SENTINEL_SCALAR_B:
sentinel_failures["B"] = (record_index, 0, SENTINEL_SCALAR_B, record[4], 16)
for element_index, expected_value in enumerate(SENTINEL_VECTOR):
observed_value = record[5 + element_index]
if sentinel_failures["VECTOR"] is None and observed_value != expected_value:
sentinel_failures["VECTOR"] = (
record_index,
element_index,
expected_value,
observed_value,
20 + element_index * UINT32_BYTES,
)
for key, status_name, signal_name in (
("A", "SENTINEL_SCALAR_A_STATUS", "SentinelScalarA"),
("B", "SENTINEL_SCALAR_B_STATUS", "SentinelScalarB"),
("VECTOR", "SENTINEL_VECTOR_STATUS", "SentinelVector"),
):
failure = sentinel_failures[key]
if records and failure is None:
report.status[status_name] = "PASS"
else:
report.status[status_name] = "FAIL"
if failure is not None:
record_index, element_index, expected_value, observed_value, field_offset = failure
code = {
"A": "SENTINEL_SCALAR_A_MISMATCH",
"B": "SENTINEL_SCALAR_B_MISMATCH",
"VECTOR": "SENTINEL_VECTOR_MISMATCH",
}[key]
report.set_error(
code,
expected=expected_value,
observed=observed_value,
record_index=record_index,
signal_name=signal_name,
element_index=element_index,
byte_offset=header_bytes
+ record_index * EXPECTED_RECORD_BYTES
+ field_offset,
)
sentinel_values_ok = all(
report.status[name] == "PASS"
for name in (
"SENTINEL_SCALAR_A_STATUS",
"SENTINEL_SCALAR_B_STATUS",
"SENTINEL_VECTOR_STATUS",
)
)
report.status["SENTINEL_SEQUENCE_STATUS"] = (
"PASS" if sentinel_values_ok and records else "FAIL"
)
if counters:
report.details["COUNTER_FIRST"] = counters[0]
report.details["COUNTER_LAST"] = counters[-1]
if times:
report.details["TIME_FIRST"] = times[0]
report.details["TIME_LAST"] = times[-1]
report.details["NONZERO_CYCLE_TIME_SAMPLES"] = len(nonzero_cycle_times)
if nonzero_cycle_times:
report.details["CYCLE_TIME_MIN"] = min(nonzero_cycle_times)
report.details["CYCLE_TIME_MAX"] = max(nonzero_cycle_times)
base_header_ok = len(observed) >= 3 and all(
(
observed[index]["name"],
observed[index]["type_hex"],
observed[index]["elements"],
)
== EXPECTED_SIGNALS[index]
for index in range(3)
)
functional_ok = (
report.status["HEADER_STATUS"] == "PASS"
and base_header_ok
and report.status["PAYLOAD_ALIGNMENT_STATUS"] == "PASS"
and sample_count >= min_samples
and report.status["COUNTER_STATUS"] == "PASS"
and report.status["TIME_STATUS"] == "PASS"
and report.status["CYCLE_TIME_STATUS"] == "PASS"
)
semantic_ok = (
layout_ok
and report.status["PAYLOAD_ALIGNMENT_STATUS"] == "PASS"
and sample_count >= min_samples
and sentinel_values_ok
and report.status["SENTINEL_SEQUENCE_STATUS"] == "PASS"
)
report.status["FUNCTIONAL_BINARY_STATUS"] = "PASS" if functional_ok else "FAIL"
report.status["SEMANTIC_BINARY_STATUS"] = "PASS" if semantic_ok else "FAIL"
report.status["BINARY_STATUS"] = (
"PASS" if functional_ok and semantic_ok else "FAIL"
)
return report, 0 if report.status["BINARY_STATUS"] == "PASS" else 40, False
def main():
parser = argparse.ArgumentParser(
description="Validate the S03 MARTe2 FileWriter binary contract."
)
parser.add_argument("binary", type=Path)
parser.add_argument("--min-samples", type=int, default=50)
args = parser.parse_args()
if args.min_samples < 1:
parser.error("--min-samples must be at least 1")
report, exit_code, already_emitted = parse_binary(args.binary, args.min_samples)
if not already_emitted:
if report.status["FUNCTIONAL_BINARY_STATUS"] == "NOT_EVALUATED":
report.status["FUNCTIONAL_BINARY_STATUS"] = "FAIL"
if report.status["SEMANTIC_BINARY_STATUS"] == "NOT_EVALUATED":
report.status["SEMANTIC_BINARY_STATUS"] = "NOT_DEMONSTRATED"
if report.status["BINARY_STATUS"] == "NOT_EVALUATED":
report.status["BINARY_STATUS"] = "FAIL"
report.emit()
return exit_code
if __name__ == "__main__":
sys.exit(main())
+347
View File
@@ -0,0 +1,347 @@
#!/usr/bin/env bash
set -u
EXPECTED_S03_CONFIG_SHA256="0f8916735ff67a9be2b167b52f147687fe131a063f410a81bfbf3bf9ff65f1e4"
MINIMUM_SAMPLES="50"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARSER="$SCRIPT_DIR/parse_s03_binary.py"
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
SEMANTIC_STATUS="NOT_DEMONSTRATED"
TIMING_STATUS="NOT_EVALUATED"
MEASUREMENT_ELIGIBILITY="NO"
emit_contract() {
printf "FUNCTIONAL_STATUS=%s\n" "$FUNCTIONAL_STATUS"
printf "SEMANTIC_STATUS=%s\n" "$SEMANTIC_STATUS"
printf "TIMING_STATUS=%s\n" "$TIMING_STATUS"
printf "MEASUREMENT_ELIGIBILITY=%s\n" "$MEASUREMENT_ELIGIBILITY"
}
fail_early() {
code="$1"
shift
printf "[NON DIMOSTRATO] %s\n" "$*" >&2
emit_contract
printf "VALIDATION_RESULT=FAIL_REQUIRED_INPUT\n"
exit "$code"
}
if [ "$#" -ne 1 ]; then
printf "Uso: %s <directory-run|run.log>\n" "$0" >&2
emit_contract
exit 2
fi
INPUT_PATH="$1"
if [ -d "$INPUT_PATH" ]; then
RUN_DIR="$(cd "$INPUT_PATH" && pwd)"
RUN_LOG="$RUN_DIR/run.log"
elif [ -f "$INPUT_PATH" ]; then
RUN_LOG="$(cd "$(dirname "$INPUT_PATH")" && pwd)/$(basename "$INPUT_PATH")"
RUN_DIR="$(dirname "$RUN_LOG")"
else
fail_early 21 "Percorso di validazione non trovato: $INPUT_PATH"
fi
RUN_CONFIG="$RUN_DIR/configuration.marte"
OUTPUT_FILE="$RUN_DIR/s03_output.bin"
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
RUN_HASHES="$RUN_DIR/hashes.sha256"
if [ ! -x "$PARSER" ]; then
fail_early 21 "Parser S03 assente o non eseguibile: $PARSER"
fi
for required_file in \
"$RUN_LOG" \
"$RUN_CONFIG" \
"$OUTPUT_FILE" \
"$RUNNER_SUMMARY" \
"$RUN_HASHES"
do
if [ ! -f "$required_file" ]; then
fail_early 21 "File richiesto assente: $required_file"
fi
done
if [ ! -s "$RUN_HASHES" ]; then
fail_early 21 "Manifest hash assente o vuoto: $RUN_HASHES"
fi
count_lines() {
pattern="$1"
file="$2"
grep -Ec "$pattern" "$file" || true
}
START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")"
APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")"
FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")"
SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")"
STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")"
TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")"
STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")"
FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")"
OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")"
ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")"
INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")"
WARNING_LINES="$(count_lines '^\[Warning ' "$RUN_LOG")"
PRIORITY_CLIP_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Requested a thread priority that is higher than ' "$RUN_LOG")"
PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")"
LINUX_TIMER_DEFAULT_WARNING_LINES="$(count_lines '^\[Warning - LinuxTimer\.cpp:[0-9]+\]: (ExecutionMode|CPUMask|StackSize) not specified using: ' "$RUN_LOG")"
CONFIG_DB_NODE_WARNING_LINES="$(count_lines '^\[Warning - MARTeApp\.cpp:[0-9]+\]: \[ConfigurationDatabaseNode\] - instances: [0-9]+[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_WARNING_LINES=$((
WARNING_LINES -
PRIORITY_CLIP_LINES -
PRIORITY_FAIL_LINES -
LINUX_TIMER_DEFAULT_WARNING_LINES -
CONFIG_DB_NODE_WARNING_LINES
))
if [ "$UNCLASSIFIED_WARNING_LINES" -lt 0 ]; then
UNCLASSIFIED_WARNING_LINES=0
fi
DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")"
ASYNC_EVENTSEM_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")"
PTHREAD_MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")"
UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_EVENTSEM_CLOSE_LINES))
UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - PTHREAD_MUTEX_DESTROY_LINES))
if [ "$UNCLASSIFIED_FATAL_LINES" -lt 0 ]; then
UNCLASSIFIED_FATAL_LINES=0
fi
if [ "$UNCLASSIFIED_OS_ERROR_LINES" -lt 0 ]; then
UNCLASSIFIED_OS_ERROR_LINES=0
fi
CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_config_XXXXXX.log")" || exit 22
BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_binary_XXXXXX.log")" || exit 22
HASH_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03_validator_hashes_XXXXXX.log")" || exit 22
trap 'rm -f -- "$CONFIG_REPORT" "$BINARY_REPORT" "$HASH_REPORT"' EXIT HUP INT TERM
EXPECTED_FILENAME="$RUN_DIR/s03_output.bin"
PYTHONHASHSEED=0 python3 - \
"$RUN_CONFIG" \
"$EXPECTED_FILENAME" \
"$EXPECTED_S03_CONFIG_SHA256" \
>"$CONFIG_REPORT" 2>&1 <<'PY_S03_CONFIG'
import hashlib
import sys
from pathlib import Path
config_path = Path(sys.argv[1])
expected_filename = sys.argv[2]
expected_hash = sys.argv[3]
text = config_path.read_text(encoding="utf-8")
expected_line = f' Filename = "{expected_filename}"'
placeholder_line = ' Filename = "__MARTE_RUN_DIR__/s03_output.bin"'
filename_count = sum(1 for line in text.splitlines() if line == expected_line)
placeholder_count = text.count("__MARTE_RUN_DIR__")
canonical = text.replace(expected_line, placeholder_line, 1)
canonical_hash = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
checks = {
"EXPECTED_FILENAME_COUNT": filename_count,
"PLACEHOLDER_COUNT": placeholder_count,
"FILEWRITER_CLASS_COUNT": text.count("Class = FileDataSource::FileWriter"),
"PLAIN_FILEWRITER_CLASS_COUNT": text.count("Class = FileWriter"),
"CONSTANT_GAM_COUNT": text.count("Class = ConstantGAM"),
"SENTINEL_SCALAR_A_DECLARATION_COUNT": text.count("SentinelScalarA ="),
"SENTINEL_SCALAR_B_DECLARATION_COUNT": text.count("SentinelScalarB ="),
"SENTINEL_VECTOR_DECLARATION_COUNT": text.count("SentinelVector ="),
"VECTOR_DIMENSION_COUNT": text.count("NumberOfDimensions = 1"),
"VECTOR_CARDINALITY_COUNT": text.count("NumberOfElements = 3"),
"THREAD_ORDER_COUNT": text.count(
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
),
}
expected_checks = {
"EXPECTED_FILENAME_COUNT": 1,
"PLACEHOLDER_COUNT": 0,
"FILEWRITER_CLASS_COUNT": 1,
"PLAIN_FILEWRITER_CLASS_COUNT": 0,
"CONSTANT_GAM_COUNT": 1,
"SENTINEL_SCALAR_A_DECLARATION_COUNT": 4,
"SENTINEL_SCALAR_B_DECLARATION_COUNT": 4,
"SENTINEL_VECTOR_DECLARATION_COUNT": 4,
"VECTOR_DIMENSION_COUNT": 4,
"VECTOR_CARDINALITY_COUNT": 4,
"THREAD_ORDER_COUNT": 1,
}
for key in sorted(checks):
print(f"{key}={checks[key]}")
print(f"CANONICAL_CONFIG_SHA256={canonical_hash}")
print(f"EXPECTED_CONFIG_SHA256={expected_hash}")
if checks == expected_checks and canonical_hash == expected_hash:
print("CONFIG_STATUS=PASS")
sys.exit(0)
print("CONFIG_STATUS=FAIL")
sys.exit(1)
PY_S03_CONFIG
CONFIG_RC=$?
PYTHONHASHSEED=0 "$PARSER" "$OUTPUT_FILE" --min-samples "$MINIMUM_SAMPLES" \
>"$BINARY_REPORT" 2>&1
PARSER_RC=$?
sha256sum -c --strict "$RUN_HASHES" >"$HASH_REPORT" 2>&1
HASH_RC=$?
report_value() {
key="$1"
file="$2"
sed -n "s/^${key}=//p" "$file" | tail -n 1
}
CONFIG_STATUS="$(report_value CONFIG_STATUS "$CONFIG_REPORT")"
FUNCTIONAL_BINARY_STATUS="$(report_value FUNCTIONAL_BINARY_STATUS "$BINARY_REPORT")"
SEMANTIC_BINARY_STATUS="$(report_value SEMANTIC_BINARY_STATUS "$BINARY_REPORT")"
BINARY_STATUS="$(report_value BINARY_STATUS "$BINARY_REPORT")"
FIRST_ERROR_CODE="$(report_value FIRST_ERROR_CODE "$BINARY_REPORT")"
COMPLETE_SAMPLE_COUNT="$(report_value COMPLETE_SAMPLE_COUNT "$BINARY_REPORT")"
HEADER_BYTES="$(report_value OBSERVED_HEADER_BYTES "$BINARY_REPORT")"
RECORD_BYTES="$(report_value OBSERVED_RECORD_BYTES_FROM_HEADER "$BINARY_REPORT")"
PAYLOAD_BYTES="$(report_value PAYLOAD_BYTES "$BINARY_REPORT")"
PAYLOAD_REMAINDER_BYTES="$(report_value PAYLOAD_REMAINDER_BYTES "$BINARY_REPORT")"
MARTE_EXIT_CODE="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
TEE_EXIT_CODE="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
if [ -z "$MARTE_EXIT_CODE" ]; then
MARTE_EXIT_CODE="NOT_AVAILABLE"
fi
if [ -z "$TEE_EXIT_CODE" ]; then
TEE_EXIT_CODE="NOT_AVAILABLE"
fi
case "$MARTE_EXIT_CODE" in
137)
TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL"
;;
*)
if [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then
TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED"
else
TIMING_STATUS="UNASSESSED_DT_SEMANTICS"
fi
;;
esac
FUNCTIONAL_OK=0
if [ "$START_LINES" -ge 1 ] &&
[ "$APPLICATION_START_LINES" -ge 1 ] &&
[ "$FILE_OPEN_LINES" -ge 1 ] &&
[ "$STARTUP_ERROR_LINES" -eq 0 ] &&
[ "$CONFIG_RC" -eq 0 ] &&
[ "$CONFIG_STATUS" = "PASS" ] &&
[ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] &&
[ "$HASH_RC" -eq 0 ] &&
[ "$TEE_EXIT_CODE" = "0" ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
FUNCTIONAL_OK=1
fi
if [ "$FUNCTIONAL_OK" -eq 1 ]; then
FUNCTIONAL_STATUS="PASS"
fi
if [ "$CONFIG_STATUS" = "PASS" ] && [ "$SEMANTIC_BINARY_STATUS" = "PASS" ]; then
SEMANTIC_STATUS="PASS"
elif [ "$SEMANTIC_BINARY_STATUS" = "FAIL" ]; then
SEMANTIC_STATUS="FAIL"
else
SEMANTIC_STATUS="NOT_DEMONSTRATED"
fi
printf "============================================================\n"
printf "VALIDAZIONE S03 - SENTINELLE SEMANTICHE\n"
printf "============================================================\n"
printf "Run directory: %s\n" "$RUN_DIR"
printf "Configurazione: %s\n" "$RUN_CONFIG"
printf "File binario: %s\n" "$OUTPUT_FILE"
printf "Manifest hash: %s\n" "$RUN_HASHES"
printf "Configurazione canonica: %s (rc=%s)\n" "${CONFIG_STATUS:-NOT_AVAILABLE}" "$CONFIG_RC"
printf "Verifica manifest hash: %s (rc=%s)\n" "$( [ "$HASH_RC" -eq 0 ] && printf PASS || printf FAIL )" "$HASH_RC"
printf "Parser binario: %s (rc=%s)\n" "${BINARY_STATUS:-NOT_AVAILABLE}" "$PARSER_RC"
printf "Primo errore parser: %s\n" "${FIRST_ERROR_CODE:-NOT_AVAILABLE}"
printf "Header osservato: %s byte\n" "${HEADER_BYTES:-NOT_AVAILABLE}"
printf "Record da header: %s byte\n" "${RECORD_BYTES:-NOT_AVAILABLE}"
printf "Payload: %s byte\n" "${PAYLOAD_BYTES:-NOT_AVAILABLE}"
printf "Residuo payload: %s byte\n" "${PAYLOAD_REMAINDER_BYTES:-NOT_AVAILABLE}"
printf "Campioni completi: %s\n" "${COMPLETE_SAMPLE_COUNT:-NOT_AVAILABLE}"
printf "Avvii State1: %s\n" "$START_LINES"
printf "Application starting: %s\n" "$APPLICATION_START_LINES"
printf "Aperture FileWriter: %s\n" "$FILE_OPEN_LINES"
printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE"
printf "Exit code tee: %s\n" "$TEE_EXIT_CODE"
printf "Warning priorita' non applic.: %s\n" "$PRIORITY_FAIL_LINES"
printf "Warning non classificati: %s\n" "$UNCLASSIFIED_WARNING_LINES"
printf "FatalError non classificati: %s\n" "$UNCLASSIFIED_FATAL_LINES"
printf "OSError non classificati: %s\n" "$UNCLASSIFIED_OS_ERROR_LINES"
printf "Error non classificati: %s\n" "$ERROR_LINES"
printf "InitialisationError: %s\n" "$INITIALISATION_ERROR_LINES"
printf "\n--- DETTAGLIO CONFIGURAZIONE ---\n"
cat -- "$CONFIG_REPORT"
printf "\n--- DETTAGLIO PARSER BINARIO ---\n"
cat -- "$BINARY_REPORT"
printf "\n--- VERIFICA HASH ---\n"
cat -- "$HASH_REPORT"
printf "\n--- CONTRATTO S03 ---\n"
emit_contract
if [ "$FUNCTIONAL_STATUS" != "PASS" ]; then
printf "VALIDATION_RESULT=FUNCTIONAL_NOT_DEMONSTRATED\n"
exit 20
fi
if [ "$SEMANTIC_STATUS" != "PASS" ]; then
printf "VALIDATION_RESULT=SEMANTIC_VALIDATION_FAILED\n"
exit 20
fi
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
[ "$OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "VALIDATION_RESULT=PASS_CLEAN_SHUTDOWN\n"
exit 0
fi
KNOWN_SHUTDOWN_TOTAL=$((
DOUBLE_STOP_LINES +
ASYNC_EVENTSEM_CLOSE_LINES +
PTHREAD_MUTEX_DESTROY_LINES
))
if [ "$SIGINT_LINES" -ge 1 ] &&
[ "$STOP_OK_LINES" -ge 1 ] &&
[ "$TERMINATED_LINES" -ge 1 ] &&
[ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] &&
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
[ "$ERROR_LINES" -eq 0 ] &&
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
printf "VALIDATION_RESULT=PASS_KNOWN_SHUTDOWN_ANOMALY\n"
exit 10
fi
printf "VALIDATION_RESULT=PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY\n"
exit 11