frontend UI improvements + compile metrics script fixes

This commit is contained in:
JalleliAchraf committed 2025-12-16 17:43:33 +01:00
1 parent eab675795a
commit 0b6bd68a24
4 files changed
+152 -53

No files matched your search

+8 -11
View File
@@ -3,18 +3,15 @@ set -euo pipefail
BUILD_DIR="${BUILD_DIR:-target}"
CSV_FILE="${CSV_FILE:-compile_metrics.csv}"
DEFAULT_PKG="server"
if [ "$#" -lt 1 ]; then
echo "Usage: $0 cargo build [--release]"
exit 1
if [ "$#" -eq 0 ]; then
set -- cargo build
fi
if [ -d "$BUILD_DIR" ]; then
find "$BUILD_DIR" -type f -name '*.ll' -delete
fi
if [ "$1" = "cargo" ] && [ "$2" = "build" ]; then
BUILD_CMD=(cargo rustc)
if [ "${1:-}" = "cargo" ] && [ "${2:-}" = "build" ]; then
cargo clean
BUILD_CMD=(cargo rustc -p "$DEFAULT_PKG")
shift 2
BUILD_CMD+=("$@")
BUILD_CMD+=(-- --emit=llvm-ir -C debuginfo=0)
@@ -57,7 +54,6 @@ else
binary_size_bytes=0
fi
finished_timestamp_ms=$(($(date +%s%N) / 1000000))
finished_datetime=$(date -Iseconds)
@@ -67,4 +63,5 @@ fi
echo "${finished_datetime},${finished_timestamp_ms},${compile_ms},${llvm_ir_lines},${binary_size_bytes}" \
>> "$CSV_FILE"
echo "Metrics recorded in $CSV_FILE"
echo "Metrics recorded in $CSV_FILE"
+42
View File
@@ -25,6 +25,8 @@ export function getUnitIcon(unit: Unit): IconName {
return "lu:Gauge";
case "mHz":
return "lu:AudioWaveform";
case "Hz":
return "lu:AudioWaveform";
case "W":
return "lu:Zap";
case "V":
@@ -37,6 +39,16 @@ export function getUnitIcon(unit: Unit): IconName {
return "lu:Waves";
case "%":
return "lu:ChartNoAxesColumn";
case "µs":
return "lu:RefreshCcw";
case "s":
return "lu:Clock";
case "MiB":
return "lu:MemoryStick";
case "Mbit/s":
return "lu:Network";
case "/s":
return "lu:Gauge";
default:
return "lu:ChartNoAxesColumn";
}
@@ -64,6 +76,8 @@ export function renderUnitSymbol(unit: Unit | undefined): string {
return "m/min";
case "mHz":
return "mHz";
case "Hz":
return "Hz";
case "W":
return "W";
case "V":
@@ -76,6 +90,16 @@ export function renderUnitSymbol(unit: Unit | undefined): string {
return "l/min";
case "%":
return "%";
case "µs":
return "µs";
case "s":
return "s";
case "MiB":
return "MiB";
case "Mbit/s":
return "Mbit/s";
case "/s":
return "/s";
default:
return "";
}
@@ -125,6 +149,8 @@ export function renderUnitSymbolLong(unit: Unit): string {
return "meters/minute";
case "mHz":
return "millihertz";
case "Hz":
return "hertz";
case "W":
return "watts";
case "V":
@@ -137,6 +163,16 @@ export function renderUnitSymbolLong(unit: Unit): string {
return "liters/minute";
case "%":
return "percent";
case "µs":
return "microseconds";
case "s":
return "seconds";
case "MiB":
return "mebibytes";
case "Mbit/s":
return "megabits/second";
case "/s":
return "per second";
default:
return "";
}
@@ -153,12 +189,18 @@ export const units = [
"bar",
"m/min",
"mHz",
"Hz",
"W",
"V",
"A",
"kWh",
"l/min",
"%",
"µs",
"s",
"MiB",
"Mbit/s",
"/s",
] as const;
export type Unit = (typeof units)[number];
+80 -27
View File
@@ -1,17 +1,54 @@
import React from "react";
import { Page } from "@/components/Page";
import { useRuntimeMetrics } from "./useRuntimeMetrics";
import { SectionTitle } from "@/components/SectionTitle";
import {
renderValueToReactNode,
renderUnitSymbol,
getUnitIcon,
type Unit,
} from "@/control/units";
import { Icon } from "@/components/Icon";
type MetricCardProps = {
title: string;
value: string;
value: React.ReactNode;
unit?: Unit;
};
function MetricCard({ title, value }: MetricCardProps) {
function renderMetricWithUnit(
value: number | null | undefined,
unit: Unit,
digits = 1,
) {
if (value == null) {
return "—";
}
return (
<>
{renderValueToReactNode(value, unit, (v) => v.toFixed(digits))}
<span className="text-muted-foreground ml-1 text-xs">
{renderUnitSymbol(unit)}
</span>
</>
);
}
function MetricCard({ title, value, unit }: MetricCardProps) {
const iconName = unit ? getUnitIcon(unit) : "lu:StickyNote";
return (
<div className="flex flex-col gap-4 rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
<div className="flex items-center justify-between">
<h2 className="text-sm font-medium text-slate-700">{title}</h2>
<div className="flex items-center gap-2">
{iconName && (
<span className="inline-flex h-7 w-7 items-center justify-center rounded-full bg-slate-100 text-slate-500">
<Icon name={iconName} className="h-4 w-4" />
</span>
)}
<h2 className="text-sm font-medium text-slate-700">{title}</h2>
</div>
<span className="text-base font-semibold text-slate-900">{value}</span>
</div>
</div>
@@ -39,55 +76,71 @@ export function MetricsControlPage() {
return (
<Page>
<div className="space-y-6">
<p className="text-sm text-slate-600">
Runtime metrics are refreshed immediately once and then every 5
seconds.
</p>
<SectionTitle title="Backend Metrics" />
<div className="grid gap-6 md:grid-cols-2">
{/* Loop jitter: max, in µs */}
<MetricCard
title="Loop jitter (max, µs)"
value={jitterMaxUs != null ? `${jitterMaxUs.toFixed(1)} µs` : "—"}
title="Max Loop Jitter"
unit={"µs"}
value={renderMetricWithUnit(jitterMaxUs, "µs")}
/>
{/* CPU usage */}
<MetricCard
title="CPU usage / time"
value={
cpuTimeSec != null && cpuPercent != null
? `${cpuPercent.toFixed(1)} % • ${cpuTimeSec.toFixed(1)} s`
: cpuTimeSec != null
? `${cpuTimeSec.toFixed(1)} s`
: "—"
}
title="CPU Usage"
unit={"%"}
value={renderMetricWithUnit(cpuPercent, "%")}
/>
{/* CPU time */}
<MetricCard
title="Memory usage"
value={rssMb != null ? `${rssMb.toFixed(1)} MiB` : "—"}
title="CPU Time"
unit={"s"}
value={renderMetricWithUnit(cpuTimeSec, "s")}
/>
{/* Memory usage */}
<MetricCard
title="Page faults (total)"
title="Memory Usage"
unit={"MiB"}
value={renderMetricWithUnit(rssMb, "MiB")}
/>
{/* Total page faults (composite, no single unit) */}
<MetricCard
title="Total Page Faults"
value={
minorFaults != null || majorFaults != null
? `${minorFaults ?? "–"} minor / ${majorFaults ?? "–"} major`
? `${minorFaults ?? "–"} minor | ${majorFaults ?? "–"} major`
: "—"
}
/>
{/* EtherCAT throughput (RX | TX) */}
<MetricCard
title="EtherCAT throughput (RX / TX)"
title="EtherCAT Throughput (RX | TX)"
unit={"Mbit/s"}
value={
rxMbit != null && txMbit != null
? `${rxMbit.toFixed(2)} / ${txMbit.toFixed(2)} Mbit/s`
: "—"
rxMbit != null && txMbit != null ? (
<span className="flex items-center gap-1">
{renderMetricWithUnit(rxMbit, "Mbit/s")}
<span className="text-muted-foreground mx-1 text-xs">|</span>
{renderMetricWithUnit(txMbit, "Mbit/s")}
</span>
) : (
"—"
)
}
/>
{/* Preemptions per second */}
<MetricCard
title="Preemptions"
unit={"/s"}
value={
preemptionRate != null ? `${preemptionRate.toFixed(1)} /s` : "—"
preemptionRate != null
? renderMetricWithUnit(preemptionRate, "/s")
: "—"
}
/>
</div>
+22 -15
View File
@@ -8,6 +8,7 @@ import {
} from "@/components/graph";
import { useRuntimeMetrics } from "./useRuntimeMetrics";
import { useRuntimeMetricSeries } from "./runtimeSeries";
import { getUnitIcon, type Unit } from "@/control/units";
export function MetricsGraphsPage() {
// Start / keep the global poller alive
@@ -18,9 +19,15 @@ export function MetricsGraphsPage() {
const syncHook = useGraphSync("runtime-metrics");
const jitterUnit: Unit = "µs";
const cpuUnit: Unit = "%";
const memUnit: Unit = "MiB";
const ioUnit: Unit = "Mbit/s";
const preemptUnit: Unit = "/s";
const jitterConfig: GraphConfig = {
title: "Loop jitter (avg, µs)",
icon: "lu:Activity",
title: "Average Loop Jitter",
icon: getUnitIcon(jitterUnit),
colors: {
primary: "#f97316",
grid: "#e2e8f0",
@@ -30,8 +37,8 @@ export function MetricsGraphsPage() {
};
const cpuConfig: GraphConfig = {
title: "CPU usage (%)",
icon: "lu:Cpu",
title: "CPU Usage",
icon: getUnitIcon(cpuUnit),
colors: {
primary: "#3b82f6",
grid: "#e2e8f0",
@@ -41,8 +48,8 @@ export function MetricsGraphsPage() {
};
const memConfig: GraphConfig = {
title: "Memory usage (MiB)",
icon: "lu:MemoryStick",
title: "Memory Usage",
icon: getUnitIcon(memUnit),
colors: {
primary: "#10b981",
grid: "#e2e8f0",
@@ -52,8 +59,8 @@ export function MetricsGraphsPage() {
};
const ioConfig: GraphConfig = {
title: "EtherCAT throughput (Mbit/s)",
icon: "lu:Network",
title: "EtherCAT Throughput",
icon: getUnitIcon(ioUnit),
colors: {
primary: "#10b981",
grid: "#e2e8f0",
@@ -64,8 +71,8 @@ export function MetricsGraphsPage() {
};
const preemptConfig: GraphConfig = {
title: "Preemptions per second (1/s)",
icon: "lu:Timer",
title: "Preemptions Per Second",
icon: getUnitIcon(preemptUnit),
colors: {
primary: "#6366f1",
grid: "#e2e8f0",
@@ -84,7 +91,7 @@ export function MetricsGraphsPage() {
newData: series.jitter,
color: "#f97316",
}}
unit={undefined}
unit={jitterUnit}
renderValue={(v) => v.toFixed(1)}
config={jitterConfig}
graphId="runtime_jitter"
@@ -98,7 +105,7 @@ export function MetricsGraphsPage() {
newData: series.cpu,
color: "#3b82f6",
}}
unit="%"
unit={cpuUnit}
renderValue={(v) => v.toFixed(1)}
config={cpuConfig}
graphId="runtime_cpu"
@@ -112,7 +119,7 @@ export function MetricsGraphsPage() {
newData: series.mem,
color: "#10b981",
}}
unit={undefined}
unit={memUnit}
renderValue={(v) => v.toFixed(1)}
config={memConfig}
graphId="runtime_memory"
@@ -134,7 +141,7 @@ export function MetricsGraphsPage() {
color: "#ef4444",
},
]}
unit={undefined}
unit={ioUnit}
renderValue={(v) => v.toFixed(2)}
config={ioConfig}
graphId="runtime_io"
@@ -148,7 +155,7 @@ export function MetricsGraphsPage() {
newData: series.preempt,
color: "#6366f1",
}}
unit={undefined}
unit={preemptUnit}
renderValue={(v) => v.toFixed(1)}
config={preemptConfig}
graphId="runtime_preemptions"