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
|
||||
|
Reference in New Issue
Block a user