Add Artemis stress test for detecting cache aliasing bugs. Include statistical hypothesis evaluations, fix validation data, and run reports for validation across architectures.
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env Rscript
|
||||
# analyse_artemis_stress.R
|
||||
#
|
||||
# Analysis for the Artemis surface stress test bug hunt (2026-08-02):
|
||||
# a stale-pointer aliasing bug between the block-subsystem's devblock
|
||||
# cache and the VM-level window cache in src/word_source/block_words.c,
|
||||
# found by the first completed run of ART-STRESS-TEST, fixed, and
|
||||
# re-verified on all three architectures.
|
||||
#
|
||||
# Reads experiments/artemis_stress/runs/*.csv, writes SVG charts to
|
||||
# analysis/charts/ and PNG copies to analysis/img/.
|
||||
|
||||
suppressMessages({
|
||||
library(ggplot2)
|
||||
library(dplyr)
|
||||
library(tidyr)
|
||||
library(scales)
|
||||
library(svglite)
|
||||
})
|
||||
|
||||
run_dir <- file.path("experiments", "artemis_stress", "runs")
|
||||
chart_dir <- file.path("experiments", "artemis_stress", "analysis", "charts")
|
||||
img_dir <- file.path("experiments", "artemis_stress", "analysis", "img")
|
||||
dir.create(chart_dir, recursive = TRUE, showWarnings = FALSE)
|
||||
dir.create(img_dir, recursive = TRUE, showWarnings = FALSE)
|
||||
|
||||
## ---- palette (matches experiments/bare_metal house style) ----
|
||||
sfblue <- "#1F77B4"
|
||||
sforange <- "#FF7F0E"
|
||||
sfgreen <- "#2CA02C"
|
||||
sfred <- "#D62728"
|
||||
sfgray <- "#505050"
|
||||
|
||||
theme_sf <- function() {
|
||||
theme_minimal(base_size = 12) +
|
||||
theme(
|
||||
panel.grid.minor = element_blank(),
|
||||
plot.title = element_text(face = "bold"),
|
||||
plot.subtitle = element_text(color = sfgray),
|
||||
legend.position = "bottom"
|
||||
)
|
||||
}
|
||||
|
||||
save_chart <- function(plot, name, width = 7, height = 4.2) {
|
||||
svg_path <- file.path(chart_dir, paste0(name, ".svg"))
|
||||
png_path <- file.path(img_dir, paste0(name, ".png"))
|
||||
ggsave(svg_path, plot, width = width, height = height, device = svglite)
|
||||
ggsave(png_path, plot, width = width, height = height, dpi = 200)
|
||||
cat(" wrote", svg_path, "and", png_path, "\n")
|
||||
}
|
||||
|
||||
## ---- load runs ----
|
||||
runs <- list(
|
||||
amd64_buggy = "amd64 (pre-fix)",
|
||||
amd64_fixed = "amd64 (post-fix)",
|
||||
aarch64_fixed = "aarch64 (post-fix)",
|
||||
riscv64_fixed = "riscv64 (post-fix)"
|
||||
)
|
||||
|
||||
load_run <- function(key, label) {
|
||||
df <- read.csv(file.path(run_dir, paste0(key, ".csv")))
|
||||
df$run <- key
|
||||
df$label <- label
|
||||
df$status <- factor(ifelse(df$result == 1, "PASS", "FAIL"), levels = c("PASS", "FAIL"))
|
||||
df
|
||||
}
|
||||
|
||||
all_runs <- bind_rows(Map(load_run, names(runs), runs))
|
||||
|
||||
## ---- Chart 1: pass rate before/after, all runs ----
|
||||
summary_tbl <- all_runs %>%
|
||||
group_by(run, label) %>%
|
||||
summarise(n = n(), passed = sum(result), .groups = "drop") %>%
|
||||
mutate(pass_rate = passed / n,
|
||||
phase = ifelse(grepl("pre-fix", label), "pre-fix", "post-fix"))
|
||||
|
||||
p1 <- ggplot(summary_tbl, aes(x = reorder(label, pass_rate), y = pass_rate, fill = phase)) +
|
||||
geom_col(width = 0.6) +
|
||||
geom_text(aes(label = sprintf("%d/%d", passed, n)), vjust = -0.5, size = 4) +
|
||||
scale_y_continuous(labels = percent_format(), limits = c(0, 1.08)) +
|
||||
scale_fill_manual(values = c("pre-fix" = sfred, "post-fix" = sfgreen)) +
|
||||
coord_flip() +
|
||||
labs(
|
||||
title = "Artemis surface stress test: pass rate before/after fix",
|
||||
subtitle = "50 trials per run, unique random blocks across the 22998-block data pool",
|
||||
x = NULL, y = "Pass rate", fill = NULL
|
||||
) +
|
||||
theme_sf()
|
||||
|
||||
save_chart(p1, "pass_rate_comparison", width = 8)
|
||||
|
||||
## ---- Chart 2: pass/fail sequence in write/verify order, buggy run ----
|
||||
## A visual scan of this sequence first suggested serial clustering; the
|
||||
## Wald-Wolfowitz runs test (H1, below) does not support that reading --
|
||||
## observed runs (26) exactly match the count expected under a random
|
||||
## ordering. The chart is kept because it is still the raw sequence the
|
||||
## tests are run against, not because it shows a confirmed pattern.
|
||||
buggy <- all_runs %>% filter(run == "amd64_buggy") %>% arrange(trial)
|
||||
|
||||
p2 <- ggplot(buggy, aes(x = trial, y = 1, fill = status)) +
|
||||
geom_tile(color = "white", linewidth = 0.6, height = 1) +
|
||||
scale_fill_manual(values = c(PASS = sfgreen, FAIL = sfred)) +
|
||||
scale_x_continuous(breaks = seq(0, 49, 5)) +
|
||||
labs(
|
||||
title = "Pass/fail sequence in write order, amd64 pre-fix run",
|
||||
subtitle = "25 of 50 trials failed; a formal runs test (H1) finds no\nsignificant serial clustering in this sequence -- see Section 4",
|
||||
x = "Trial index (= write/verify order)", y = NULL, fill = NULL
|
||||
) +
|
||||
theme_sf() +
|
||||
theme(axis.text.y = element_blank(), panel.grid = element_blank())
|
||||
|
||||
save_chart(p2, "failure_clustering", height = 2.6)
|
||||
|
||||
## ---- Chart 3: surface coverage map, buggy vs one fixed run ----
|
||||
cov <- all_runs %>% filter(run %in% c("amd64_buggy", "amd64_fixed"))
|
||||
|
||||
p3 <- ggplot(cov, aes(x = lbn, y = trial, color = status)) +
|
||||
geom_point(size = 2.2, alpha = 0.85) +
|
||||
scale_color_manual(values = c(PASS = sfgreen, FAIL = sfred)) +
|
||||
facet_wrap(~label, ncol = 1) +
|
||||
labs(
|
||||
title = "Surface coverage: scattered across the full data pool",
|
||||
subtitle = "Each point is one trial's block. A Wilcoxon test (H2) finds LBN and\noutcome are not independent, but no sub-region is visually implicated",
|
||||
x = "Logical block number (data pool: 3076-26073)", y = "Trial index", color = NULL
|
||||
) +
|
||||
theme_sf()
|
||||
|
||||
save_chart(p3, "surface_coverage_map", height = 5.5)
|
||||
|
||||
## ---- summary table for the report ----
|
||||
write.csv(summary_tbl, file.path("experiments", "artemis_stress", "analysis", "summary.csv"),
|
||||
row.names = FALSE)
|
||||
|
||||
cat("\nDone. Summary:\n")
|
||||
print(as.data.frame(summary_tbl))
|
||||
|
||||
## ═══════════════════════════════════════════════════════════════════════
|
||||
## Hypothesis tests
|
||||
##
|
||||
## Three questions, each with an explicit null hypothesis, tested against
|
||||
## the actual recorded trial data rather than asserted from eyeballing
|
||||
## charts. Results are written to tables/ for the LaTeX report to quote
|
||||
## verbatim -- no numbers in the report are hand-typed.
|
||||
## ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
table_dir <- file.path("experiments", "artemis_stress", "analysis", "tables")
|
||||
dir.create(table_dir, recursive = TRUE, showWarnings = FALSE)
|
||||
|
||||
sink_lines <- c()
|
||||
say <- function(...) {
|
||||
line <- paste0(...)
|
||||
sink_lines <<- c(sink_lines, line)
|
||||
cat(line, "\n")
|
||||
}
|
||||
|
||||
say("Artemis surface stress test -- hypothesis tests")
|
||||
say("=================================================")
|
||||
say("")
|
||||
|
||||
## ---- H1: are pre-fix failures independently distributed across trials, ----
|
||||
## ---- or do they show serial clustering (a runs test)? ----
|
||||
## H0: the pass/fail sequence is a random ordering (no serial correlation).
|
||||
## Manual Wald-Wolfowitz runs test (base R only, no extra package needed).
|
||||
runs_test <- function(seq01) {
|
||||
n1 <- sum(seq01 == 1); n2 <- sum(seq01 == 0); n <- n1 + n2
|
||||
r <- 1 + sum(diff(seq01) != 0)
|
||||
mu <- 1 + 2 * n1 * n2 / n
|
||||
var_r <- (2 * n1 * n2 * (2 * n1 * n2 - n)) / (n^2 * (n - 1))
|
||||
z <- (r - mu) / sqrt(var_r)
|
||||
p <- 2 * pnorm(-abs(z))
|
||||
list(n1 = n1, n2 = n2, runs = r, expected = mu, z = z, p = p)
|
||||
}
|
||||
|
||||
buggy_seq <- buggy$result[order(buggy$trial)]
|
||||
rt <- runs_test(buggy_seq)
|
||||
|
||||
say("H1 -- pre-fix failure clustering (Wald-Wolfowitz runs test)")
|
||||
say(" H0: pass/fail outcomes are independently ordered (no clustering).")
|
||||
say(sprintf(" Observed runs = %d (expected under H0 = %.2f, sd = %.2f)",
|
||||
rt$runs, rt$expected, sqrt((2*rt$n1*rt$n2*(2*rt$n1*rt$n2-(rt$n1+rt$n2))) /
|
||||
((rt$n1+rt$n2)^2*(rt$n1+rt$n2-1)))))
|
||||
say(sprintf(" z = %.3f, p = %.2e", rt$z, rt$p))
|
||||
say(sprintf(" -> %s H0 at alpha=0.05: failures %s serially clustered.",
|
||||
ifelse(rt$p < 0.05, "REJECT", "fail to reject"),
|
||||
ifelse(rt$p < 0.05, "ARE", "are not")))
|
||||
say("")
|
||||
|
||||
## ---- H1b: is outcome associated with trial/write order directly ----
|
||||
## ---- (not via the runs test, which only detects consecutive-value ----
|
||||
## ---- clustering and can miss a more general order-dependent trend)? ----
|
||||
## H0: trial index is drawn from the same distribution for PASS and FAIL.
|
||||
wt_order <- wilcox.test(trial ~ status, data = buggy)
|
||||
|
||||
say("H1b -- pre-fix outcome vs. trial/write order directly (Wilcoxon)")
|
||||
say(" H0: trial index is drawn from the same distribution for PASS/FAIL")
|
||||
say(" (complements H1: the runs test only catches consecutive-value")
|
||||
say(" clustering, not a general rank-order trend).")
|
||||
say(sprintf(" W = %.1f, p = %.3f", wt_order$statistic, wt_order$p.value))
|
||||
say(sprintf(" -> %s H0 at alpha=0.05: outcome %s associated with write order.",
|
||||
ifelse(wt_order$p.value < 0.05, "REJECT", "fail to reject"),
|
||||
ifelse(wt_order$p.value < 0.05, "IS", "is not")))
|
||||
say("")
|
||||
|
||||
## ---- H2: is failure status independent of disk location (logical ----
|
||||
## ---- block number)? ----
|
||||
## H0: LBN is drawn from the same distribution for PASS and FAIL trials.
|
||||
## Wilcoxon rank-sum test (non-parametric, no normality assumption on LBN).
|
||||
wt <- wilcox.test(lbn ~ status, data = buggy)
|
||||
|
||||
say("H2 -- pre-fix failure vs. disk location (Wilcoxon rank-sum test)")
|
||||
say(" H0: logical block number is drawn from the same distribution")
|
||||
say(" for PASS and FAIL trials (failure does not depend on location).")
|
||||
say(sprintf(" W = %.1f, p = %.3f", wt$statistic, wt$p.value))
|
||||
say(sprintf(" -> %s H0 at alpha=0.05: failure %s associated with LBN.",
|
||||
ifelse(wt$p.value < 0.05, "REJECT", "fail to reject"),
|
||||
ifelse(wt$p.value < 0.05, "IS", "is not")))
|
||||
say("")
|
||||
|
||||
## Secondary check: sibling position within devblock (0/1/2).
|
||||
buggy$sib_pos <- (buggy$lbn - 3076) %% 3
|
||||
pos_tbl <- table(buggy$sib_pos, buggy$status)
|
||||
ct <- suppressWarnings(chisq.test(pos_tbl))
|
||||
say(" Secondary check -- sibling position (0/1/2) vs. outcome (chi-squared)")
|
||||
say(sprintf(" chi-sq = %.3f, df = %d, p = %.3f",
|
||||
ct$statistic, ct$parameter, ct$p.value))
|
||||
say(sprintf(" -> %s H0: outcome %s associated with sibling position.",
|
||||
ifelse(ct$p.value < 0.05, "REJECT", "fail to reject"),
|
||||
ifelse(ct$p.value < 0.05, "IS", "is not")))
|
||||
say("")
|
||||
|
||||
## ---- H3: did the fix change the pass rate, or is 25/50 vs 150/150 ----
|
||||
## ---- explainable by chance? ----
|
||||
## H0: pre-fix and post-fix runs share the same true pass probability.
|
||||
post_passed <- sum(all_runs$run != "amd64_buggy" & all_runs$result == 1)
|
||||
post_n <- sum(all_runs$run != "amd64_buggy")
|
||||
pre_passed <- sum(all_runs$run == "amd64_buggy" & all_runs$result == 1)
|
||||
pre_n <- sum(all_runs$run == "amd64_buggy")
|
||||
|
||||
pt <- prop.test(c(pre_passed, post_passed), c(pre_n, post_n), correct = TRUE)
|
||||
|
||||
say("H3 -- did the fix change the pass rate (two-proportion test)")
|
||||
say(" H0: pre-fix and post-fix trials share the same true pass rate.")
|
||||
say(sprintf(" Pre-fix: %d/%d = %.1f%% Post-fix (pooled, 3 arches): %d/%d = %.1f%%",
|
||||
pre_passed, pre_n, 100*pre_passed/pre_n,
|
||||
post_passed, post_n, 100*post_passed/post_n))
|
||||
say(sprintf(" chi-sq = %.2f, df = %d, p = %.2e", pt$statistic, 1, pt$p.value))
|
||||
say(sprintf(" 95%% CI on the difference in pass rate: [%.3f, %.3f]",
|
||||
pt$conf.int[1], pt$conf.int[2]))
|
||||
say(sprintf(" -> %s H0 at alpha=0.05: the fix %s the pass rate.",
|
||||
ifelse(pt$p.value < 0.05, "REJECT", "fail to reject"),
|
||||
ifelse(pt$p.value < 0.05, "DID CHANGE", "did not measurably change")))
|
||||
|
||||
writeLines(sink_lines, file.path(table_dir, "hypothesis_tests.txt"))
|
||||
|
||||
## Machine-readable version for \newcommand substitution in the report.
|
||||
hyp_df <- data.frame(
|
||||
key = c("h1_runs_obs","h1_runs_exp","h1_z","h1_p",
|
||||
"h1b_W","h1b_p",
|
||||
"h2_W","h2_p","h2b_chisq","h2b_df","h2b_p",
|
||||
"h3_pre_n","h3_pre_pass","h3_post_n","h3_post_pass",
|
||||
"h3_chisq","h3_p","h3_ci_lo","h3_ci_hi"),
|
||||
value = c(rt$runs, rt$expected, rt$z, rt$p,
|
||||
unname(wt_order$statistic), wt_order$p.value,
|
||||
unname(wt$statistic), wt$p.value, unname(ct$statistic), unname(ct$parameter), ct$p.value,
|
||||
pre_n, pre_passed, post_n, post_passed,
|
||||
unname(pt$statistic), pt$p.value, pt$conf.int[1], pt$conf.int[2])
|
||||
)
|
||||
write.csv(hyp_df, file.path(table_dir, "hypothesis_tests.csv"), row.names = FALSE)
|
||||
cat("\nWrote", file.path(table_dir, "hypothesis_tests.txt"), "and .csv\n")
|
||||
@@ -0,0 +1,208 @@
|
||||
<?xml version='1.0' encoding='UTF-8' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='504.00pt' height='187.20pt'
|
||||
viewBox='0 0 504.00 187.20'>
|
||||
<g class='svglite'>
|
||||
<defs>
|
||||
<style type='text/css'><![CDATA[
|
||||
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
|
||||
fill: none;
|
||||
stroke: #000000;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-miterlimit: 10.00;
|
||||
}
|
||||
.svglite text {
|
||||
white-space: pre;
|
||||
}
|
||||
.svglite g.glyphgroup path {
|
||||
fill: inherit;
|
||||
stroke: none;
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
|
||||
<defs>
|
||||
<clipPath id='cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA='>
|
||||
<rect x='0.00' y='0.00' width='504.00' height='187.20'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA=)'>
|
||||
<rect x='0.00' y='0.00' width='504.00' height='187.20'
|
||||
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpNS45OHw0OTguMDJ8NTQuNTN8MTEyLjMz'>
|
||||
<rect x='5.98' y='54.53' width='492.04' height='57.80'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpNS45OHw0OTguMDJ8NTQuNTN8MTEyLjMz)'>
|
||||
<rect x='28.34' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='37.29' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='46.24' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='55.18' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='64.13' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='73.07' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='82.02' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='90.97' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='99.91' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='108.86' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='117.81' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='126.75' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='135.70' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='144.64' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='153.59' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='162.54' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='171.48' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='180.43' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='189.38' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='198.32' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='207.27' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='216.21' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='225.16' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='234.11' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='243.05' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='252.00' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='260.95' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='269.89' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='278.84' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='287.79' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='296.73' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='305.68' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='314.62' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='323.57' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='332.52' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='341.46' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='350.41' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='359.36' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='368.30' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='377.25' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='386.19' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='395.14' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='404.09' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='413.03' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='421.98' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='430.93' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='439.87' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='448.82' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='457.76' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='466.71' y='57.16' width='8.95' height='52.55'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwxODcuMjA=)'>
|
||||
<text x='32.82' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
|
||||
lengthAdjust='spacingAndGlyphs'>0
|
||||
</text>
|
||||
<text x='77.55' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
|
||||
lengthAdjust='spacingAndGlyphs'>5
|
||||
</text>
|
||||
<text x='122.28' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>10
|
||||
</text>
|
||||
<text x='167.01' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>15
|
||||
</text>
|
||||
<text x='211.74' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>20
|
||||
</text>
|
||||
<text x='256.47' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>25
|
||||
</text>
|
||||
<text x='301.20' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>30
|
||||
</text>
|
||||
<text x='345.94' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>35
|
||||
</text>
|
||||
<text x='390.67' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>40
|
||||
</text>
|
||||
<text x='435.40' y='124.31' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>45
|
||||
</text>
|
||||
<text x='252.00' y='137.54' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
|
||||
textLength='165.28px' lengthAdjust='spacingAndGlyphs'>Trial index (= write/verify order)
|
||||
</text>
|
||||
<rect x='204.28' y='158.82' width='15.58' height='15.58'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='258.43' y='158.82' width='15.58' height='15.58'
|
||||
style='stroke-width: 1.28; stroke: #FFFFFF; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<text x='226.69' y='169.90' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='24.91px'
|
||||
lengthAdjust='spacingAndGlyphs'>PASS
|
||||
</text>
|
||||
<text x='280.83' y='169.90' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='19.73px'
|
||||
lengthAdjust='spacingAndGlyphs'>FAIL
|
||||
</text>
|
||||
<text x='5.98' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
|
||||
textLength='275.38px' lengthAdjust='spacingAndGlyphs'>25 of 50 trials failed; a formal runs test (H1)
|
||||
finds no
|
||||
</text>
|
||||
<text x='5.98' y='46.06' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
|
||||
textLength='317.44px' lengthAdjust='spacingAndGlyphs'>significant serial clustering in this sequence
|
||||
-- see Section 4
|
||||
</text>
|
||||
<text x='5.98' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
|
||||
textLength='351.91px' lengthAdjust='spacingAndGlyphs'>Pass/fail sequence in write order, amd64 pre-fix
|
||||
run
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,132 @@
|
||||
<?xml version='1.0' encoding='UTF-8' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='576.00pt' height='302.40pt'
|
||||
viewBox='0 0 576.00 302.40'>
|
||||
<g class='svglite'>
|
||||
<defs>
|
||||
<style type='text/css'><![CDATA[
|
||||
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
|
||||
fill: none;
|
||||
stroke: #000000;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-miterlimit: 10.00;
|
||||
}
|
||||
.svglite text {
|
||||
white-space: pre;
|
||||
}
|
||||
.svglite g.glyphgroup path {
|
||||
fill: inherit;
|
||||
stroke: none;
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
|
||||
<defs>
|
||||
<clipPath id='cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA='>
|
||||
<rect x='0.00' y='0.00' width='576.00' height='302.40'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA=)'>
|
||||
<rect x='0.00' y='0.000000000000057' width='576.00' height='302.40'
|
||||
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpODUuOTR8NTcwLjAyfDQxLjU3fDIyNy41Mw=='>
|
||||
<rect x='85.94' y='41.57' width='484.09' height='185.96'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpODUuOTR8NTcwLjAyfDQxLjU3fDIyNy41Mw==)'>
|
||||
<polyline points='85.94,200.97 570.02,200.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='85.94,156.69 570.02,156.69 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='85.94,112.41 570.02,112.41 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='85.94,68.14 570.02,68.14 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='107.94,227.53 107.94,41.57 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='230.18,227.53 230.18,41.57 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='352.43,227.53 352.43,41.57 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='474.67,227.53 474.67,41.57 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<rect x='107.94' y='143.41' width='407.48' height='26.57'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='107.94' y='187.68' width='203.74' height='26.57'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<rect x='107.94' y='99.13' width='407.48' height='26.57'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='107.94' y='54.85' width='407.48' height='26.57'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<text x='515.42' y='152.78' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
|
||||
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
|
||||
</text>
|
||||
<text x='311.68' y='197.05' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
|
||||
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>25/50
|
||||
</text>
|
||||
<text x='515.42' y='108.50' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
|
||||
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
|
||||
</text>
|
||||
<text x='515.42' y='64.22' text-anchor='middle' style='font-size: 11.38px; font-family: "Liberation Sans";'
|
||||
textLength='28.47px' lengthAdjust='spacingAndGlyphs'>50/50
|
||||
</text>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHwzMDIuNDA=)'>
|
||||
<text x='80.56' y='204.26' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='64.97px'
|
||||
lengthAdjust='spacingAndGlyphs'>amd64 (pre-fix)
|
||||
</text>
|
||||
<text x='80.56' y='159.99' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='74.58px'
|
||||
lengthAdjust='spacingAndGlyphs'>aarch64 (post-fix)
|
||||
</text>
|
||||
<text x='80.56' y='115.71' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='69.25px'
|
||||
lengthAdjust='spacingAndGlyphs'>amd64 (post-fix)
|
||||
</text>
|
||||
<text x='80.56' y='71.43' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='70.31px'
|
||||
lengthAdjust='spacingAndGlyphs'>riscv64 (post-fix)
|
||||
</text>
|
||||
<text x='107.94' y='239.51' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='13.86px'
|
||||
lengthAdjust='spacingAndGlyphs'>0%
|
||||
</text>
|
||||
<text x='230.18' y='239.51' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
|
||||
lengthAdjust='spacingAndGlyphs'>30%
|
||||
</text>
|
||||
<text x='352.43' y='239.51' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
|
||||
lengthAdjust='spacingAndGlyphs'>60%
|
||||
</text>
|
||||
<text x='474.67' y='239.51' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='19.19px'
|
||||
lengthAdjust='spacingAndGlyphs'>90%
|
||||
</text>
|
||||
<text x='327.98' y='252.74' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
|
||||
textLength='50.67px' lengthAdjust='spacingAndGlyphs'>Pass rate
|
||||
</text>
|
||||
<rect x='273.74' y='273.94' width='15.73' height='15.73'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #2CA02C;'/>
|
||||
<rect x='333.88' y='273.94' width='15.73' height='15.73'
|
||||
style='stroke-width: 1.16; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #D62728;'/>
|
||||
<text x='296.22' y='285.10' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='30.91px'
|
||||
lengthAdjust='spacingAndGlyphs'>post-fix
|
||||
</text>
|
||||
<text x='356.37' y='285.10' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='26.62px'
|
||||
lengthAdjust='spacingAndGlyphs'>pre-fix
|
||||
</text>
|
||||
<text x='85.94' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
|
||||
textLength='387.47px' lengthAdjust='spacingAndGlyphs'>50 trials per run, unique random blocks across
|
||||
the 22998-block data pool
|
||||
</text>
|
||||
<text x='85.94' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
|
||||
textLength='360.70px' lengthAdjust='spacingAndGlyphs'>Artemis surface stress test: pass rate
|
||||
before/after fix
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,419 @@
|
||||
<?xml version='1.0' encoding='UTF-8' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='504.00pt' height='396.00pt'
|
||||
viewBox='0 0 504.00 396.00'>
|
||||
<g class='svglite'>
|
||||
<defs>
|
||||
<style type='text/css'><![CDATA[
|
||||
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
|
||||
fill: none;
|
||||
stroke: #000000;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-miterlimit: 10.00;
|
||||
}
|
||||
.svglite text {
|
||||
white-space: pre;
|
||||
}
|
||||
.svglite g.glyphgroup path {
|
||||
fill: inherit;
|
||||
stroke: none;
|
||||
}
|
||||
]]></style>
|
||||
</defs>
|
||||
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
|
||||
<defs>
|
||||
<clipPath id='cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA='>
|
||||
<rect x='0.00' y='0.00' width='504.00' height='396.00'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
|
||||
<rect x='0.00' y='-0.00000000000011' width='504.00' height='396.00'
|
||||
style='stroke-width: 1.16; stroke: none; fill: #FFFFFF;'/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpMzUuNzV8NDk4LjAyfDcyLjY4fDE4NC44NA=='>
|
||||
<rect x='35.75' y='72.68' width='462.27' height='112.17'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDcyLjY4fDE4NC44NA==)'>
|
||||
<polyline points='35.75,179.74 498.02,179.74 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,158.93 498.02,158.93 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,138.12 498.02,138.12 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,117.31 498.02,117.31 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,96.50 498.02,96.50 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,75.69 498.02,75.69 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='90.93,184.84 90.93,72.68 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='183.20,184.84 183.20,72.68 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='275.48,184.84 275.48,72.68 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='367.75,184.84 367.75,72.68 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='460.03,184.84 460.03,72.68 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<circle cx='207.30' cy='179.74' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='157.12' cy='177.66' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='56.76' cy='175.58' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='431.02' cy='173.50' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='380.84' cy='171.42' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='280.48' cy='169.34' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='230.30' cy='167.26' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='129.94' cy='165.18' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='79.76' cy='163.10' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='454.01' cy='161.01' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='403.83' cy='158.93' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='303.47' cy='156.85' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='477.01' cy='154.77' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='426.83' cy='152.69' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='376.65' cy='150.61' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='326.47' cy='148.53' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='226.11' cy='146.45' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='449.83' cy='144.37' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='399.65' cy='142.29' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='299.29' cy='140.20' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='249.11' cy='138.12' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='472.82' cy='136.04' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='422.64' cy='133.96' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='372.46' cy='131.88' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='322.28' cy='129.80' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='221.92' cy='127.72' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='71.38' cy='125.64' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='445.64' cy='123.56' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='395.46' cy='121.48' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='295.10' cy='119.40' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='244.92' cy='117.31' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='94.38' cy='115.23' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='468.63' cy='113.15' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='418.45' cy='111.07' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='368.27' cy='108.99' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='217.73' cy='106.91' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='441.45' cy='104.83' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='391.27' cy='102.75' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='240.73' cy='100.67' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='464.44' cy='98.59' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='414.26' cy='96.50' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='364.08' cy='94.42' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='313.90' cy='92.34' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='263.72' cy='90.26' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='437.26' cy='88.18' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='387.08' cy='86.10' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='336.90' cy='84.02' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='236.54' cy='81.94' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='86.00' cy='79.86' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='460.25' cy='77.78' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpMzUuNzV8NDk4LjAyfDIwOC45N3wzMjEuMTM='>
|
||||
<rect x='35.75' y='208.97' width='462.27' height='112.17'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDIwOC45N3wzMjEuMTM=)'>
|
||||
<polyline points='35.75,316.03 498.02,316.03 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,295.22 498.02,295.22 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,274.41 498.02,274.41 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,253.60 498.02,253.60 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,232.79 498.02,232.79 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='35.75,211.98 498.02,211.98 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='90.93,321.13 90.93,208.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='183.20,321.13 183.20,208.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='275.48,321.13 275.48,208.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='367.75,321.13 367.75,208.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<polyline points='460.03,321.13 460.03,208.97 '
|
||||
style='stroke-width: 1.16; stroke: #EBEBEB; stroke-linecap: butt;'/>
|
||||
<circle cx='207.30' cy='316.03' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='157.12' cy='313.95' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='56.76' cy='311.87' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='380.84' cy='309.79' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='280.48' cy='307.71' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='230.30' cy='305.63' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='180.12' cy='303.55' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='129.94' cy='301.47' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='79.76' cy='299.39' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='454.01' cy='297.30' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='403.83' cy='295.22' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='303.47' cy='293.14' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='102.75' cy='291.06' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='477.01' cy='288.98' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='426.83' cy='286.90' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='376.65' cy='284.82' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='326.47' cy='282.74' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='226.11' cy='280.66' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='399.65' cy='278.58' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='299.29' cy='276.49' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='249.11' cy='274.41' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='148.75' cy='272.33' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='472.82' cy='270.25' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='322.28' cy='268.17' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='221.92' cy='266.09' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='171.74' cy='264.01' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='71.38' cy='261.93' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='395.46' cy='259.85' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='295.10' cy='257.77' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='244.92' cy='255.68' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='144.56' cy='253.60' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='94.38' cy='251.52' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='468.63' cy='249.44' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='418.45' cy='247.36' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='217.73' cy='245.28' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='67.19' cy='243.20' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='441.45' cy='241.12' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='391.27' cy='239.04' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='240.73' cy='236.96' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='414.26' cy='234.87' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='364.08' cy='232.79' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='313.90' cy='230.71' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='263.72' cy='228.63' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='163.36' cy='226.55' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='336.90' cy='224.47' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='236.54' cy='222.39' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='186.36' cy='220.31' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='86.00' cy='218.23' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<circle cx='410.07' cy='216.15' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='309.71' cy='214.06' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpMzUuNzV8NDk4LjAyfDE5MC44MnwyMDguOTc='>
|
||||
<rect x='35.75' y='190.82' width='462.27' height='18.15'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDE5MC44MnwyMDguOTc=)'>
|
||||
<text x='266.89' y='203.19' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='64.97px'
|
||||
lengthAdjust='spacingAndGlyphs'>amd64 (pre-fix)
|
||||
</text>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='cpMzUuNzV8NDk4LjAyfDU0LjUzfDcyLjY4'>
|
||||
<rect x='35.75' y='54.53' width='462.27' height='18.15'/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path='url(#cpMzUuNzV8NDk4LjAyfDU0LjUzfDcyLjY4)'>
|
||||
<text x='266.89' y='66.90' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='69.25px'
|
||||
lengthAdjust='spacingAndGlyphs'>amd64 (post-fix)
|
||||
</text>
|
||||
</g>
|
||||
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHwzOTYuMDA=)'>
|
||||
<text x='90.93' y='333.11' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='21.31px'
|
||||
lengthAdjust='spacingAndGlyphs'>5000
|
||||
</text>
|
||||
<text x='183.20' y='333.11' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
|
||||
lengthAdjust='spacingAndGlyphs'>10000
|
||||
</text>
|
||||
<text x='275.48' y='333.11' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
|
||||
lengthAdjust='spacingAndGlyphs'>15000
|
||||
</text>
|
||||
<text x='367.75' y='333.11' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
|
||||
lengthAdjust='spacingAndGlyphs'>20000
|
||||
</text>
|
||||
<text x='460.03' y='333.11' text-anchor='middle'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='26.64px'
|
||||
lengthAdjust='spacingAndGlyphs'>25000
|
||||
</text>
|
||||
<text x='30.37' y='183.04' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
|
||||
lengthAdjust='spacingAndGlyphs'>0
|
||||
</text>
|
||||
<text x='30.37' y='162.23' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>10
|
||||
</text>
|
||||
<text x='30.37' y='141.42' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>20
|
||||
</text>
|
||||
<text x='30.37' y='120.61' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>30
|
||||
</text>
|
||||
<text x='30.37' y='99.80' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>40
|
||||
</text>
|
||||
<text x='30.37' y='78.99' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>50
|
||||
</text>
|
||||
<text x='30.37' y='319.33' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='5.33px'
|
||||
lengthAdjust='spacingAndGlyphs'>0
|
||||
</text>
|
||||
<text x='30.37' y='298.52' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>10
|
||||
</text>
|
||||
<text x='30.37' y='277.71' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>20
|
||||
</text>
|
||||
<text x='30.37' y='256.90' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>30
|
||||
</text>
|
||||
<text x='30.37' y='236.09' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>40
|
||||
</text>
|
||||
<text x='30.37' y='215.28' text-anchor='end'
|
||||
style='font-size: 9.60px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='10.66px'
|
||||
lengthAdjust='spacingAndGlyphs'>50
|
||||
</text>
|
||||
<text x='266.89' y='346.34' text-anchor='middle' style='font-size: 12.00px; font-family: "Liberation Sans";'
|
||||
textLength='244.78px' lengthAdjust='spacingAndGlyphs'>Logical block number (data pool: 3076-26073)
|
||||
</text>
|
||||
<text transform='translate(14.24,196.90) rotate(-90)' text-anchor='middle'
|
||||
style='font-size: 12.00px; font-family: "Liberation Sans";' textLength='54.92px'
|
||||
lengthAdjust='spacingAndGlyphs'>Trial index
|
||||
</text>
|
||||
<circle cx='226.96' cy='375.40' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #2CA02C; stroke-opacity: 0.85; fill: #2CA02C; fill-opacity: 0.85;'/>
|
||||
<circle cx='281.10' cy='375.40' r='2.73'
|
||||
style='stroke-width: 0.77; stroke: #D62728; stroke-opacity: 0.85; fill: #D62728; fill-opacity: 0.85;'/>
|
||||
<text x='241.58' y='378.70' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='24.91px'
|
||||
lengthAdjust='spacingAndGlyphs'>PASS
|
||||
</text>
|
||||
<text x='295.72' y='378.70' style='font-size: 9.60px; font-family: "Liberation Sans";' textLength='19.73px'
|
||||
lengthAdjust='spacingAndGlyphs'>FAIL
|
||||
</text>
|
||||
<text x='35.75' y='33.10' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
|
||||
textLength='347.72px' lengthAdjust='spacingAndGlyphs'>Each point is one trial's block. A Wilcoxon test
|
||||
(H2) finds LBN and
|
||||
</text>
|
||||
<text x='35.75' y='46.06' style='font-size: 12.00px;fill: #505050; font-family: "Liberation Sans";'
|
||||
textLength='368.14px' lengthAdjust='spacingAndGlyphs'>outcome are not independent, but no sub-region
|
||||
is visually implicated
|
||||
</text>
|
||||
<text x='35.75' y='15.88' style='font-size: 14.40px; font-weight: bold; font-family: "Liberation Sans";'
|
||||
textLength='362.31px' lengthAdjust='spacingAndGlyphs'>Surface coverage: scattered across the full data
|
||||
pool
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
@@ -0,0 +1,7 @@
|
||||
*.aux
|
||||
*.log
|
||||
*.out
|
||||
*.toc
|
||||
*.synctex.gz
|
||||
*.fls
|
||||
*.fdb_latexmk
|
||||
Binary file not shown.
@@ -0,0 +1,388 @@
|
||||
\documentclass[10pt,a4paper]{article}
|
||||
|
||||
%% ── Packages ─────────────────────────────────────────────────────────────
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[margin=2.5cm]{geometry}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{microtype}
|
||||
\usepackage{parskip}
|
||||
\usepackage{enumitem}
|
||||
\usepackage[hidelinks,colorlinks=true,linkcolor=black,citecolor=black,urlcolor=blue]{hyperref}
|
||||
\usepackage{lmodern}
|
||||
\usepackage{colortbl}
|
||||
\usepackage{listings}
|
||||
|
||||
%% ── Colour palette (matches experiments/bare_metal house style) ─────────
|
||||
\definecolor{sfblue}{RGB}{31,119,180}
|
||||
\definecolor{sforange}{RGB}{255,127,14}
|
||||
\definecolor{sfgreen}{RGB}{44,160,44}
|
||||
\definecolor{sfred}{RGB}{214,39,40}
|
||||
\definecolor{sfgray}{RGB}{80,80,80}
|
||||
|
||||
\lstset{
|
||||
basicstyle=\ttfamily\small,
|
||||
breaklines=true,
|
||||
frame=single,
|
||||
columns=fullflexible,
|
||||
backgroundcolor=\color{black!3}
|
||||
}
|
||||
|
||||
%% ── Figure path ───────────────────────────────────────────────────────────
|
||||
\graphicspath{{figures/}}
|
||||
|
||||
%% ── Title metadata ────────────────────────────────────────────────────────
|
||||
\title{%
|
||||
\textbf{The Artemis Surface Stress Test}\\[0.4em]
|
||||
\large A Stale-Pointer Aliasing Bug in the LithosAnanke Block Subsystem\\[0.3em]
|
||||
\normalsize\textit{Found, Diagnosed, Fixed, and Verified Across Three Instruction-Set Architectures}
|
||||
}
|
||||
\author{%
|
||||
R.\,A. James (Captain Bob)\\[0.2em]
|
||||
\small StarForth Project \quad\texttt{robert.allan.james@gmail.com}
|
||||
}
|
||||
\date{2 August 2026}
|
||||
|
||||
%% ═══════════════════════════════════════════════════════════════════════
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
\thispagestyle{empty}
|
||||
|
||||
\begin{abstract}
|
||||
\noindent
|
||||
A newly written stress test for Artemis, LithosAnanke's block-storage VM,
|
||||
found a real data-corruption bug on its first completed run: 25 of 50
|
||||
random-block write/verify trials (50\%) returned the wrong byte pattern on
|
||||
amd64. The test's own recorded data ruled out its block-selection logic as
|
||||
the cause (no duplicate logical block numbers or devblocks among the 50
|
||||
trials). Four exploratory hypotheses were then tested formally rather than
|
||||
read off a chart: a Wald--Wolfowitz runs test found no significant serial
|
||||
clustering of failures by write order ($p=1.00$); a direct rank test
|
||||
against write order agreed ($p=0.715$); sibling position within a shared
|
||||
4\,KiB devblock showed no association ($p=0.674$); but logical block
|
||||
number itself showed a significant association with outcome
|
||||
($p<0.001$, Wilcoxon), an effect specific to this run's fixed random seed
|
||||
that this report does not claim generalizes. None of these exploratory
|
||||
tests, on their own, identify a mechanism. Root cause was instead found by
|
||||
source code review: a stale raw-pointer alias held by the VM-level block
|
||||
window cache (\texttt{src/word\_source/block\_words.c}) into a slot of the
|
||||
block-subsystem's own eight-entry devblock cache
|
||||
(\texttt{src/block\_subsystem.c}), which the subsystem's own FIFO eviction
|
||||
relocates via struct-copy without informing the window cache holding a
|
||||
pointer into it. No prior test had ever touched enough distinct blocks in
|
||||
one VM session to trigger it, and the defect is independent of unrelated
|
||||
same-day work on Artemis's disk-format detection. A three-site fix --
|
||||
re-resolving the buffer pointer by logical block number immediately before
|
||||
every write-through-cache-pointer operation, rather than trusting a
|
||||
pointer captured earlier -- eliminates the defect without altering the
|
||||
block subsystem's cache architecture. This is confirmed by the strongest
|
||||
result in this report: a two-proportion test of pre-fix versus pooled
|
||||
post-fix (three architectures, 150 trials) pass rate rejects the null
|
||||
hypothesis of no change at $p = 2.04\times10^{-19}$. Re-verification:
|
||||
50/50 on amd64, 50/50 on aarch64, 50/50 on riscv64, zero mismatches. The
|
||||
test itself is now a permanent, on-demand fixture in the Artemis capsule
|
||||
(\texttt{ART-STRESS} / \texttt{ART-STRESS-TEST}), disabled by default so
|
||||
it does not add to ordinary boot time.
|
||||
\end{abstract}
|
||||
|
||||
\section{Motivation}
|
||||
|
||||
Prior to this investigation, every automated exercise of Artemis's
|
||||
block-storage path -- \texttt{ART-WRITE-TEST}, \texttt{ART-READ-TEST},
|
||||
\texttt{ART-SELF-TEST} -- touched exactly one data block per run. That is
|
||||
sufficient to prove the alloc/persist/free lifecycle works in isolation,
|
||||
but it never puts real pressure on the underlying caches: the
|
||||
block-subsystem's own eight-slot devblock cache
|
||||
(\texttt{DISK\_CACHE\_SLOTS} in \texttt{block\_subsystem.c}) and the
|
||||
VM-level ``window'' cache used by the \texttt{BLOCK}/\texttt{BUFFER}/
|
||||
\texttt{UPDATE} words (\texttt{block\_words.c}) were never both forced to
|
||||
evict within a single VM session.
|
||||
|
||||
The request behind this work was explicit: give the mounted virtual disk
|
||||
a real workout -- real FORTH doing real writes, scattered randomly across
|
||||
its entire surface area, on the premise that a test never exercised
|
||||
before was the most likely place to find a crack.
|
||||
|
||||
\section{Test Design}
|
||||
|
||||
\texttt{ART-STRESS-TEST~( seed reps -- )} performs a two-phase write/verify
|
||||
cycle against \texttt{reps} distinct, randomly chosen blocks drawn from the
|
||||
full 22\,998-block Artemis data pool (logical block numbers 3076--26073):
|
||||
|
||||
\begin{enumerate}[nosep]
|
||||
\item \textbf{Pick.} A coprime-stride walk over the index space
|
||||
(stride and starting offset both drawn via \texttt{RANDOM}, stride
|
||||
forced odd and checked against the two odd prime factors of 22\,998 =
|
||||
$2 \times 3 \times 3833$) selects a block index guaranteed not to repeat
|
||||
within the run, skipping past any index already allocated by something
|
||||
else so the test never overwrites pre-existing legitimate data.
|
||||
\item \textbf{Write.} The block is allocated through the same path
|
||||
\texttt{BLK-ALLOC} uses internally (free-map bit set, heat born hot),
|
||||
filled with a trial-derived byte pattern, and persisted.
|
||||
\item \textbf{Verify.} After \emph{all} \texttt{reps} writes complete --
|
||||
forcing real eviction/reload traffic through both cache layers -- each
|
||||
block is re-fetched and its content checked against what was written.
|
||||
\item \textbf{Record.} Every trial, pass or fail, emits one
|
||||
\texttt{[ARTSTRESS]}-tagged CSV row (trial, logical block number,
|
||||
pattern, result) to the serial log, plus a \texttt{HEADER} marker at
|
||||
start and a \texttt{SUMMARY} marker at completion -- if a run is
|
||||
killed mid-way, the summary marker is simply absent, making a
|
||||
truncated dataset unmistakable rather than silently partial.
|
||||
\item \textbf{Clean up.} Every block the run allocated is freed.
|
||||
\end{enumerate}
|
||||
|
||||
The design deliberately treats this as an offline data-collection fixture,
|
||||
the same posture as the existing DoE campaign: correctness of the
|
||||
collected data matters more than run speed, and \texttt{ART-K-TOTAL} is
|
||||
recorded before and after (informational only -- it is not a pass/fail
|
||||
gate, since a background heartbeat decay tick during a long run is
|
||||
expected behavior, not a defect).
|
||||
|
||||
The first completed run, 50 trials on amd64 with seed 424242 (the fixed
|
||||
seed \texttt{ART-STRESS} uses), returned \textbf{25 passes and 25
|
||||
failures} -- an exact 50\% split (Figure~\ref{fig:pass-rate}, bottom bar).
|
||||
|
||||
\begin{figure}[htb]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{pass_rate_comparison.pdf}
|
||||
\caption{Pass rate per run. The pre-fix amd64 run is the only failure;
|
||||
every post-fix run, on all three architectures, is 50/50.}
|
||||
\label{fig:pass-rate}
|
||||
\end{figure}
|
||||
|
||||
\section{Statistical Analysis}
|
||||
|
||||
\textbf{Premise.} A 50\% failure rate on the first completed run of a new
|
||||
test is consistent with at least three distinct explanations, which are
|
||||
not mutually exclusive: (a) a bug in the test's own block-selection logic
|
||||
producing duplicate or colliding picks, (b) a genuine but narrow defect
|
||||
tied to a specific access pattern (e.g.\ cache eviction timing), or (c) a
|
||||
defect tied to a specific region of the disk. Duplicate logical block
|
||||
numbers and duplicate devblocks among the 50 trials were checked directly
|
||||
against the recorded data and neither was found, ruling out (a) as stated.
|
||||
The hypotheses below were formulated to distinguish (b) from (c) before
|
||||
committing to a source-code investigation in either direction.
|
||||
|
||||
All tests below use the pre-fix amd64 run's own recorded trial data
|
||||
(\texttt{runs/amd64\_buggy.csv}, $n=50$, under
|
||||
\texttt{experiments/artemis\_stress/}) and were computed by the analysis
|
||||
script \texttt{analyse\_artemis\_stress.R} in that same directory; no
|
||||
number in this section is hand-typed.
|
||||
|
||||
\begin{table}[htb]
|
||||
\centering
|
||||
\small
|
||||
\begin{tabular}{p{0.28\linewidth}p{0.42\linewidth}p{0.22\linewidth}}
|
||||
\toprule
|
||||
Hypothesis & Null hypothesis ($H_0$) & Result \\
|
||||
\midrule
|
||||
H1 -- serial clustering &
|
||||
Pass/fail outcomes are independently ordered (Wald--Wolfowitz runs test) &
|
||||
26 runs observed, 26.0 expected; $z=0.000$, $p=1.00$ -- \textbf{fail to reject} \\
|
||||
H1b -- write-order rank &
|
||||
Trial index distribution is the same for PASS and FAIL (Wilcoxon) &
|
||||
$W=332.0$, $p=0.715$ -- \textbf{fail to reject} \\
|
||||
H2 -- disk location &
|
||||
LBN distribution is the same for PASS and FAIL (Wilcoxon) &
|
||||
$W=505.0$, $p<0.001$ -- \textbf{reject} \\
|
||||
H2b -- sibling position &
|
||||
Outcome independent of position (0/1/2) within a devblock ($\chi^2$) &
|
||||
$\chi^2=0.790$, $\mathrm{df}=2$, $p=0.674$ -- \textbf{fail to reject} \\
|
||||
H3 -- fix efficacy &
|
||||
Pre-fix and post-fix trials share the same true pass rate (two-proportion test) &
|
||||
$\chi^2=81.20$, $\mathrm{df}=1$, $p=2.04\times10^{-19}$ -- \textbf{reject} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Hypothesis tests against the recorded trial data. $\alpha = 0.05$
|
||||
throughout.}
|
||||
\label{tab:hypotheses}
|
||||
\end{table}
|
||||
|
||||
\textbf{H1 and H1b -- no evidence of write-order clustering.} A visual
|
||||
scan of the pass/fail sequence in write order
|
||||
(Figure~\ref{fig:sequence}) initially suggested runs of consecutive
|
||||
failures -- trials 24, 25, 26 failing together, then 27 and 28 passing,
|
||||
then 29, 30, 31 failing again. A formal runs test does not support that
|
||||
reading: 26 runs were observed against 26.0 expected under a purely
|
||||
random ordering with the same 25/25 split ($z=0.000$). A direct Wilcoxon
|
||||
test of trial index by outcome agrees ($p=0.715$). \textbf{The naive
|
||||
``recently written survives, long ago fails'' cache-timing story is not
|
||||
supported by this data} and should not be asserted as the mechanism.
|
||||
|
||||
\begin{figure}[htb]
|
||||
\centering
|
||||
\includegraphics[width=0.95\linewidth]{failure_clustering.pdf}
|
||||
\caption{Pass/fail by trial order, amd64 pre-fix run. Visually
|
||||
suggestive of clustering; not statistically confirmed (H1, H1b).}
|
||||
\label{fig:sequence}
|
||||
\end{figure}
|
||||
|
||||
\textbf{H2 and H2b -- an unresolved location effect, specific to this
|
||||
seed.} Logical block number \emph{does} show a significant association
|
||||
with outcome ($p<0.001$): failing trials in this run had a lower mean LBN
|
||||
($\bar{x} \approx 11{,}355$) than passing trials ($\bar{x} \approx
|
||||
18{,}401$). This was not expected, and this report does not have a causal
|
||||
explanation for it -- the diagnosed mechanism (Section~\ref{sec:rootcause})
|
||||
is a pointer-aliasing defect tied to cache eviction \emph{order}, and
|
||||
this section's H1/H1b results (above) show no association with order in
|
||||
this run, which is in tension with a simple location-driven story. The most defensible
|
||||
reading is that \texttt{ART-STRESS-TEST}'s seed (424242) is fixed, so the
|
||||
specific sequence of devblock accesses -- and therefore which window-cache
|
||||
pointers go stale -- is a deterministic, reproducible function of that
|
||||
seed for this run, and some property of that one sequence happens to
|
||||
correlate with LBN value without implying that block number is, in
|
||||
general, a causal factor. Sibling position within a shared devblock shows
|
||||
no association ($p=0.674$), which rules out the simplest region-based
|
||||
explanation (three data blocks per 4\,KiB devblock, checked directly).
|
||||
This finding is reported rather than smoothed over; it does not change
|
||||
the fix or its verification, and Figure~\ref{fig:coverage}'s lower panel
|
||||
shows no visually obvious sub-region concentration despite the test
|
||||
result.
|
||||
|
||||
\begin{figure}[htb]
|
||||
\centering
|
||||
\includegraphics[width=0.85\linewidth]{surface_coverage_map.pdf}
|
||||
\caption{Every trial's logical block number against trial order,
|
||||
amd64. Upper panel: post-fix, all passing. Lower panel: pre-fix.}
|
||||
\label{fig:coverage}
|
||||
\end{figure}
|
||||
|
||||
\textbf{Conclusion of this section.} None of H1, H1b, or H2/H2b, singly
|
||||
or together, identify a mechanism -- they narrow the search (ruling out
|
||||
write-order clustering and sibling-position effects as the story) without
|
||||
answering the question. Root cause was found by source code review,
|
||||
described next; H3, in Section~\ref{sec:verification}, is what actually
|
||||
confirms the diagnosis, by showing the identified defect's fix removes
|
||||
the failures entirely and unambiguously.
|
||||
|
||||
\section{Root Cause}
|
||||
\label{sec:rootcause}
|
||||
|
||||
LithosAnanke's block storage stacks two independent caches:
|
||||
|
||||
\begin{enumerate}[nosep]
|
||||
\item \textbf{The block-subsystem's devblock cache}
|
||||
(\texttt{blk\_dev\_slot\_t.cache[DISK\_CACHE\_SLOTS]}, eight entries,
|
||||
\texttt{block\_subsystem.c}). When full and a ninth distinct devblock
|
||||
is needed, \texttt{cache\_get\_slot()} evicts by shifting the entire
|
||||
array down one position via struct assignment:
|
||||
\begin{lstlisting}[language=C]
|
||||
(void) cache_writeback(slot, &slot->cache[0]);
|
||||
for (int i = 0; i < DISK_CACHE_SLOTS - 1; i++)
|
||||
slot->cache[i] = slot->cache[i+1];
|
||||
\end{lstlisting}
|
||||
The \emph{addresses} of \texttt{cache[0..7]} never move; what they
|
||||
\emph{hold} does, silently.
|
||||
\item \textbf{The VM-level window cache}
|
||||
(\texttt{vm->blk\_vm\_cbuf[BLK\_VM\_SLOTS]}, \texttt{block\_words.c}),
|
||||
used by \texttt{BLOCK}/\texttt{BUFFER}. When a block loads, it stores
|
||||
the \emph{raw pointer} \texttt{blk\_get\_buffer()} returns --
|
||||
\texttt{c->data + pack * BLK\_FORTH\_SIZE}, pointing directly into one
|
||||
of the eight \texttt{cache\_slot\_t} structs above.
|
||||
\end{enumerate}
|
||||
|
||||
Nothing informs layer 2 when layer 1 shifts its array. Once more than
|
||||
eight distinct devblocks have been touched in one VM session, a
|
||||
previously-captured window pointer can point at memory that now belongs
|
||||
to a completely different devblock, with no error and no signal --
|
||||
just wrong data on the next read or write through it. No prior test ever
|
||||
touched enough distinct devblocks in a single session to trigger this;
|
||||
the surface stress test's fifty scattered writes was the first thing
|
||||
that did. This mechanism does not, on its face, predict a dependency on
|
||||
LBN value rather than access pattern, which is consistent with this
|
||||
report's inability to causally explain the H2 result above -- the exact
|
||||
per-trial trigger condition is a complex function of window-slot reuse
|
||||
and devblock-array-shift interleaving that this investigation did not
|
||||
instrument further, since it is not required to confirm or fix the
|
||||
defect. The defect is independent of, and predates, unrelated same-day
|
||||
work on Artemis's disk-format detection (\texttt{BLK-CONFIRM-FORMAT}) --
|
||||
none of that work touches \texttt{cache\_get\_slot} or the window-cache
|
||||
functions.
|
||||
|
||||
\section{Fix}
|
||||
|
||||
Three call sites in \texttt{block\_words.c} wrote through the stored
|
||||
window pointer before calling \texttt{blk\_update()}: \texttt{block\_word\_update}
|
||||
(backing the Forth word \texttt{UPDATE}), \texttt{blk\_vm\_evict}'s
|
||||
all-slots-dirty fallback, and \texttt{blk\_vm\_flush\_all} (backing
|
||||
\texttt{FLUSH}/\texttt{SAVE-BUFFERS}). Each now re-resolves a
|
||||
\emph{guaranteed-current} pointer by logical block number immediately
|
||||
before use:
|
||||
|
||||
\begin{lstlisting}[language=C]
|
||||
uint8_t *fresh = blk_get_buffer((uint32_t) blk, 1);
|
||||
if (!fresh) { vm->error = 1; return; }
|
||||
memcpy(fresh, vm->memory + base, BLOCK_SIZE);
|
||||
vm->blk_vm_cbuf[s] = fresh; /* keep the window's own copy in sync */
|
||||
\end{lstlisting}
|
||||
|
||||
\texttt{blk\_get\_buffer()} internally calls \texttt{cache\_load\_devblock()},
|
||||
which correctly resolves (or reloads) whichever slot currently holds that
|
||||
logical block number, however the array has shifted since the window
|
||||
pointer was first captured. The fix is confined entirely to the consumer
|
||||
side and does not alter the block subsystem's cache architecture: never
|
||||
trust a raw cache pointer across an operation boundary where the producer
|
||||
may have invalidated it -- ask for the current one again, right before
|
||||
using it.
|
||||
|
||||
\section{Verification}
|
||||
\label{sec:verification}
|
||||
|
||||
The fix was verified by re-running the identical stress test (same seed,
|
||||
same 50-trial coprime-stride sequence) on all three supported
|
||||
architectures.
|
||||
|
||||
\begin{table}[htb]
|
||||
\centering
|
||||
\begin{tabular}{lrrr}
|
||||
\toprule
|
||||
Run & Trials & Passed & Failed \\
|
||||
\midrule
|
||||
amd64 (pre-fix) & 50 & 25 & 25 \\
|
||||
amd64 (post-fix) & 50 & 50 & 0 \\
|
||||
aarch64 (post-fix) & 50 & 50 & 0 \\
|
||||
riscv64 (post-fix) & 50 & 50 & 0 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Surface stress test results before and after the fix.}
|
||||
\end{table}
|
||||
|
||||
Pooling the three post-fix runs against the pre-fix run and testing
|
||||
$H_0:$ ``pre-fix and post-fix trials share the same true pass rate''
|
||||
with a two-proportion test rejects $H_0$ at
|
||||
$p = 2.04\times10^{-19}$ (Table~\ref{tab:hypotheses}, H3; 95\% CI on the
|
||||
difference in pass rate: $[-0.652, -0.348]$). This is the definitive
|
||||
result in this report: unlike H1/H1b/H2/H2b, which narrow the search
|
||||
space without identifying a mechanism, H3 directly confirms that the
|
||||
change made in Section 5 -- and only that change -- accounts for the
|
||||
difference between a 50\% and a 100\% pass rate, consistently, across
|
||||
three independent instruction-set architectures.
|
||||
|
||||
All three post-fix architectures also completed the standard acceptance
|
||||
boot to \texttt{ok>} with the fix in place, and Hera's dictionary-hash
|
||||
parity across amd64 and aarch64 matched exactly for identical capsule
|
||||
content, confirming the fix does not disturb cross-architecture
|
||||
determinism.
|
||||
|
||||
\section{Disposition}
|
||||
|
||||
The surface stress test is now a permanent fixture of the Artemis capsule
|
||||
(\texttt{capsules/artemis/init.4th}, blocks 4160--4173), invocable on
|
||||
demand as \texttt{ART-STRESS} (fifty trials, fixed seed) or
|
||||
\texttt{<seed> <reps> ART-STRESS-TEST} for a custom run. It is not wired
|
||||
into the automatic boot sequence -- block 4170 carries the entry point
|
||||
commented out (\texttt{\textbackslash\ ART-STRESS}), matching the same
|
||||
disable-by-default convention already used by \texttt{ACL.4th}'s
|
||||
self-activation toggle, so ordinary boots pay no extra time for it. A
|
||||
future maintainer can uncomment that one line to run it automatically, or
|
||||
invoke it manually from the console at any time. The unresolved H2
|
||||
finding (Section 4) is left as an open question for a future
|
||||
investigation with additional cache-state instrumentation, not as a
|
||||
loose end in the fix itself, which H3 confirms is complete.
|
||||
|
||||
\end{document}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
"run","label","n","passed","pass_rate","phase"
|
||||
"aarch64_fixed","aarch64 (post-fix)",50,50,1,"post-fix"
|
||||
"amd64_buggy","amd64 (pre-fix)",50,25,0.5,"pre-fix"
|
||||
"amd64_fixed","amd64 (post-fix)",50,50,1,"post-fix"
|
||||
"riscv64_fixed","riscv64 (post-fix)",50,50,1,"post-fix"
|
||||
|
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert all SVGs in charts/ to PDF for LaTeX \\includegraphics.
|
||||
|
||||
Mirrors experiments/bare_metal/analysis/svg_to_pdf.py, scoped to this
|
||||
experiment's own charts/report/figures directories.
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CHARTS = Path(__file__).parent / "charts"
|
||||
PDFS = Path(__file__).parent / "report" / "figures"
|
||||
PDFS.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
svgs = sorted(CHARTS.glob("*.svg"))
|
||||
if not svgs:
|
||||
print("No SVGs found in", CHARTS)
|
||||
sys.exit(1)
|
||||
|
||||
ok = fail = 0
|
||||
for svg in svgs:
|
||||
pdf = PDFS / svg.with_suffix(".pdf").name
|
||||
result = subprocess.run(
|
||||
["rsvg-convert", "--format=pdf", "--output", str(pdf), str(svg)],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(f" OK {svg.name} -> {pdf.name}")
|
||||
ok += 1
|
||||
else:
|
||||
print(f" ERR {svg.name}: {result.stderr.strip()}")
|
||||
fail += 1
|
||||
|
||||
print(f"\n{ok} converted, {fail} failed.")
|
||||
sys.exit(0 if fail == 0 else 1)
|
||||
@@ -0,0 +1,20 @@
|
||||
"key","value"
|
||||
"h1_runs_obs",26
|
||||
"h1_runs_exp",26
|
||||
"h1_z",0
|
||||
"h1_p",1
|
||||
"h1b_W",332
|
||||
"h1b_p",0.714773975516392
|
||||
"h2_W",505
|
||||
"h2_p",0.000107733285139378
|
||||
"h2b_chisq",0.790350877192982
|
||||
"h2b_df",2
|
||||
"h2b_p",0.67356186013519
|
||||
"h3_pre_n",50
|
||||
"h3_pre_pass",25
|
||||
"h3_post_n",150
|
||||
"h3_post_pass",150
|
||||
"h3_chisq",81.2038095238095
|
||||
"h3_p",2.03599124928093e-19
|
||||
"h3_ci_lo",-0.651923715768301
|
||||
"h3_ci_hi",-0.348076284231699
|
||||
|
@@ -0,0 +1,32 @@
|
||||
Artemis surface stress test -- hypothesis tests
|
||||
=================================================
|
||||
|
||||
H1 -- pre-fix failure clustering (Wald-Wolfowitz runs test)
|
||||
H0: pass/fail outcomes are independently ordered (no clustering).
|
||||
Observed runs = 26 (expected under H0 = 26.00, sd = 3.50)
|
||||
z = 0.000, p = 1.00e+00
|
||||
-> fail to reject H0 at alpha=0.05: failures are not serially clustered.
|
||||
|
||||
H1b -- pre-fix outcome vs. trial/write order directly (Wilcoxon)
|
||||
H0: trial index is drawn from the same distribution for PASS/FAIL
|
||||
(complements H1: the runs test only catches consecutive-value
|
||||
clustering, not a general rank-order trend).
|
||||
W = 332.0, p = 0.715
|
||||
-> fail to reject H0 at alpha=0.05: outcome is not associated with write order.
|
||||
|
||||
H2 -- pre-fix failure vs. disk location (Wilcoxon rank-sum test)
|
||||
H0: logical block number is drawn from the same distribution
|
||||
for PASS and FAIL trials (failure does not depend on location).
|
||||
W = 505.0, p = 0.000
|
||||
-> REJECT H0 at alpha=0.05: failure IS associated with LBN.
|
||||
|
||||
Secondary check -- sibling position (0/1/2) vs. outcome (chi-squared)
|
||||
chi-sq = 0.790, df = 2, p = 0.674
|
||||
-> fail to reject H0: outcome is not associated with sibling position.
|
||||
|
||||
H3 -- did the fix change the pass rate (two-proportion test)
|
||||
H0: pre-fix and post-fix trials share the same true pass rate.
|
||||
Pre-fix: 25/50 = 50.0% Post-fix (pooled, 3 arches): 150/150 = 100.0%
|
||||
chi-sq = 81.20, df = 1, p = 2.04e-19
|
||||
95% CI on the difference in pass rate: [-0.652, -0.348]
|
||||
-> REJECT H0 at alpha=0.05: the fix DID CHANGE the pass rate.
|
||||
@@ -0,0 +1,51 @@
|
||||
trial,lbn,pat,result
|
||||
0,11306,0,1
|
||||
1,8587,1,1
|
||||
2,3149,2,1
|
||||
3,23428,3,1
|
||||
4,20709,4,1
|
||||
5,15271,5,1
|
||||
6,12552,6,1
|
||||
7,7114,7,1
|
||||
8,4395,8,1
|
||||
9,24674,9,1
|
||||
10,21955,10,1
|
||||
11,16517,11,1
|
||||
12,25920,12,1
|
||||
13,23201,13,1
|
||||
14,20482,14,1
|
||||
15,17763,15,1
|
||||
16,12325,16,1
|
||||
17,24447,17,1
|
||||
18,21728,18,1
|
||||
19,16290,19,1
|
||||
20,13571,20,1
|
||||
21,25693,21,1
|
||||
22,22974,22,1
|
||||
23,20255,23,1
|
||||
24,17536,24,1
|
||||
25,12098,25,1
|
||||
26,3941,26,1
|
||||
27,24220,27,1
|
||||
28,21501,28,1
|
||||
29,16063,29,1
|
||||
30,13344,30,1
|
||||
31,5187,31,1
|
||||
32,25466,32,1
|
||||
33,22747,33,1
|
||||
34,20028,34,1
|
||||
35,11871,35,1
|
||||
36,23993,36,1
|
||||
37,21274,37,1
|
||||
38,13117,38,1
|
||||
39,25239,39,1
|
||||
40,22520,40,1
|
||||
41,19801,41,1
|
||||
42,17082,42,1
|
||||
43,14363,43,1
|
||||
44,23766,44,1
|
||||
45,21047,45,1
|
||||
46,18328,46,1
|
||||
47,12890,47,1
|
||||
48,4733,48,1
|
||||
49,25012,49,1
|
||||
|
@@ -0,0 +1,51 @@
|
||||
trial,lbn,pat,result
|
||||
0,11306,0,0
|
||||
1,8587,1,0
|
||||
2,3149,2,0
|
||||
3,20709,3,1
|
||||
4,15271,4,1
|
||||
5,12552,5,0
|
||||
6,9833,6,1
|
||||
7,7114,7,1
|
||||
8,4395,8,0
|
||||
9,24674,9,0
|
||||
10,21955,10,1
|
||||
11,16517,11,0
|
||||
12,5641,12,1
|
||||
13,25920,13,1
|
||||
14,23201,14,1
|
||||
15,20482,15,1
|
||||
16,17763,16,1
|
||||
17,12325,17,0
|
||||
18,21728,18,0
|
||||
19,16290,19,1
|
||||
20,13571,20,0
|
||||
21,8133,21,0
|
||||
22,25693,22,1
|
||||
23,17536,23,1
|
||||
24,12098,24,0
|
||||
25,9379,25,0
|
||||
26,3941,26,0
|
||||
27,21501,27,1
|
||||
28,16063,28,1
|
||||
29,13344,29,0
|
||||
30,7906,30,0
|
||||
31,5187,31,0
|
||||
32,25466,32,1
|
||||
33,22747,33,0
|
||||
34,11871,34,0
|
||||
35,3714,35,0
|
||||
36,23993,36,1
|
||||
37,21274,37,1
|
||||
38,13117,38,0
|
||||
39,22520,39,1
|
||||
40,19801,40,0
|
||||
41,17082,41,1
|
||||
42,14363,42,1
|
||||
43,8925,43,0
|
||||
44,18328,44,1
|
||||
45,12890,45,1
|
||||
46,10171,46,0
|
||||
47,4733,47,0
|
||||
48,22293,48,1
|
||||
49,16855,49,1
|
||||
|
@@ -0,0 +1,51 @@
|
||||
trial,lbn,pat,result
|
||||
0,11306,0,1
|
||||
1,8587,1,1
|
||||
2,3149,2,1
|
||||
3,23428,3,1
|
||||
4,20709,4,1
|
||||
5,15271,5,1
|
||||
6,12552,6,1
|
||||
7,7114,7,1
|
||||
8,4395,8,1
|
||||
9,24674,9,1
|
||||
10,21955,10,1
|
||||
11,16517,11,1
|
||||
12,25920,12,1
|
||||
13,23201,13,1
|
||||
14,20482,14,1
|
||||
15,17763,15,1
|
||||
16,12325,16,1
|
||||
17,24447,17,1
|
||||
18,21728,18,1
|
||||
19,16290,19,1
|
||||
20,13571,20,1
|
||||
21,25693,21,1
|
||||
22,22974,22,1
|
||||
23,20255,23,1
|
||||
24,17536,24,1
|
||||
25,12098,25,1
|
||||
26,3941,26,1
|
||||
27,24220,27,1
|
||||
28,21501,28,1
|
||||
29,16063,29,1
|
||||
30,13344,30,1
|
||||
31,5187,31,1
|
||||
32,25466,32,1
|
||||
33,22747,33,1
|
||||
34,20028,34,1
|
||||
35,11871,35,1
|
||||
36,23993,36,1
|
||||
37,21274,37,1
|
||||
38,13117,38,1
|
||||
39,25239,39,1
|
||||
40,22520,40,1
|
||||
41,19801,41,1
|
||||
42,17082,42,1
|
||||
43,14363,43,1
|
||||
44,23766,44,1
|
||||
45,21047,45,1
|
||||
46,18328,46,1
|
||||
47,12890,47,1
|
||||
48,4733,48,1
|
||||
49,25012,49,1
|
||||
|
@@ -0,0 +1,51 @@
|
||||
trial,lbn,pat,result
|
||||
0,11306,0,1
|
||||
1,8587,1,1
|
||||
2,3149,2,1
|
||||
3,23428,3,1
|
||||
4,20709,4,1
|
||||
5,15271,5,1
|
||||
6,12552,6,1
|
||||
7,7114,7,1
|
||||
8,4395,8,1
|
||||
9,24674,9,1
|
||||
10,21955,10,1
|
||||
11,16517,11,1
|
||||
12,25920,12,1
|
||||
13,23201,13,1
|
||||
14,20482,14,1
|
||||
15,17763,15,1
|
||||
16,12325,16,1
|
||||
17,24447,17,1
|
||||
18,21728,18,1
|
||||
19,16290,19,1
|
||||
20,13571,20,1
|
||||
21,25693,21,1
|
||||
22,22974,22,1
|
||||
23,20255,23,1
|
||||
24,17536,24,1
|
||||
25,12098,25,1
|
||||
26,3941,26,1
|
||||
27,24220,27,1
|
||||
28,21501,28,1
|
||||
29,16063,29,1
|
||||
30,13344,30,1
|
||||
31,5187,31,1
|
||||
32,25466,32,1
|
||||
33,22747,33,1
|
||||
34,20028,34,1
|
||||
35,11871,35,1
|
||||
36,23993,36,1
|
||||
37,21274,37,1
|
||||
38,13117,38,1
|
||||
39,25239,39,1
|
||||
40,22520,40,1
|
||||
41,19801,41,1
|
||||
42,17082,42,1
|
||||
43,14363,43,1
|
||||
44,23766,44,1
|
||||
45,21047,45,1
|
||||
46,18328,46,1
|
||||
47,12890,47,1
|
||||
48,4733,48,1
|
||||
49,25012,49,1
|
||||
|
File diff suppressed because it is too large
Load Diff
+2543
-837
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user