723 lines
28 KiB
Bash
Executable File
723 lines
28 KiB
Bash
Executable File
#!/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
|