StarletBootstrapProbe: --broadway-h1a's report and budget, from the final review

Two findings of the review of 2df9dfc909 (StarOS H-1A, D2):
- The report reads the bench window too (hw0c_report(): HW-M4-0D's
  area, and H-1A's record if it was never read), but a refused access
  there did not fail the run, as one during the run does. It does now:
  the failure on stderr, and the run fails.
- --cycles' bound of 2^34 was raised whenever "--broadway-h1a" appeared
  among the arguments, even as the value of --ipc-script or --stage-spk
  (a file of that name), the run then not in the client's mode.
  ClientRequested reads the options with their grammar: the word after
  --ipc-script, --stage-spk or --cycles is a value, never an option.

StarletProbeBroadwayTest: ClientRequested.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Yaya48andClaude Opus 5.5 committed 2026-11-04 21:59:34 +01:00
1 parent 2df9dfc909
commit 9ad22f00bd
3 files changed
+42 -9

No files matched your search

@@ -112,6 +112,22 @@ TEST(StarletProbeBroadway, TheClientsBudgetIsBoundedAndOnlyItsOwn)
EXPECT_FALSE(ParseCycles("17179869184"));
}
TEST(StarletProbeBroadway, OnlyTheOptionItselfRaisesTheBudget)
{
const auto requested = [](std::vector<const char*> argv) {
return ClientRequested(static_cast<int>(argv.size()), argv.data());
};
EXPECT_TRUE(requested({"probe", "image.elf", "--broadway-h1a"}));
EXPECT_TRUE(requested({"probe", "image.elf", "--jit", "--cycles", "4096", "--broadway-h1a"}));
EXPECT_TRUE(requested({"probe", "image.elf", "--stage-spk", "p.bin", "--broadway-h1a"}));
EXPECT_FALSE(requested({"probe", "image.elf"}));
// The image's own name, or another option's value, is no option.
EXPECT_FALSE(requested({"probe", "--broadway-h1a"}));
EXPECT_FALSE(requested({"probe", "image.elf", "--ipc-script", "--broadway-h1a"}));
EXPECT_FALSE(requested({"probe", "image.elf", "--stage-spk", "--broadway-h1a", "--jit"}));
EXPECT_FALSE(requested({"probe", "image.elf", "--cycles", "--broadway-h1a"}));
}
TEST(StarletProbeBroadway, TheStarletIsDoneAsTheLoadersMonitorSaysIt)
{
// StarOS boot/contract/staros_boot.h: RUNNING 2, IDLE 3, FATAL 4, PANIC 5, FAULT 6; EXI PPC 1.
@@ -48,6 +48,22 @@ constexpr bool StarletDone(bool trace_terminal, u32 sbr1_state, u32 sbr1_exi_own
return trace_terminal || (sbr1_state >= SBR1_STATE_IDLE && sbr1_exi_owner == SBR1_EXI_PPC);
}
// Whether --broadway-h1a is among the probe's options (argv[2] on), read with their grammar: the
// word after --ipc-script, --stage-spk or --cycles is that option's value, never an option. It
// raises --cycles' bound, which is read where it stands, before the other options are known.
inline bool ClientRequested(int argc, const char* const* argv)
{
for (int index = 2; index < argc; ++index)
{
const std::string_view option = argv[index];
if (option == "--ipc-script" || option == "--stage-spk" || option == "--cycles")
++index;
else if (option == "--broadway-h1a")
return true;
}
return false;
}
// Whether a whole word at `address` lies in the bench window.
constexpr bool InBenchWindow(u64 address)
{
@@ -236,11 +236,15 @@ bool Run(const std::string& path, bool jit, bool mmu_faults, SBR1Mode sbr1,
std::cerr << "Starlet panic state " << memory.Read32(info.panic_state) << " code "
<< memory.Read32(info.panic_code) << '\n';
};
// The client's capture on stdout, after the trace, and its counts on stderr.
// The client's capture on stdout, after the trace, and its counts on stderr. The report reads the
// window too: false if it reached outside (the access refused, as during the run).
const auto report_client = [&](std::string_view status) {
std::cout << client->Report(status);
std::cerr << "--broadway-h1a: " << client->Steps() << " steps, timebase 0x" << std::hex
<< client->Timebase() << std::dec << ", done " << client->Done() << '\n';
if (client->Failed())
std::cerr << "--broadway-h1a: " << client->Failure() << '\n';
return !client->Failed();
};
ProbeBus script_bus(mem1, system.GetWiiIPC());
// The model's own line raises the IPC cause: the probe has no Starlet object (ProbeScript.h).
@@ -324,7 +328,7 @@ bool Run(const std::string& path, bool jit, bool mmu_faults, SBR1Mode sbr1,
<< " instructions=" << core.GetExecutedInstructions() << '\n';
if (length == TRACE_CAPACITY)
std::cerr << "--broadway-h1a: the trace is full: its last lines are in the SBR1 ring\n";
report_client(CLIENT_STATUS_DONE);
const bool reported = report_client(CLIENT_STATUS_DONE);
const bool startup_ok = trace.find("REV:BOOT:ENTRY\n") != std::string::npos &&
trace.find("REV:BOOT:BSS_OK\n") != std::string::npos &&
trace.find("REV:BOOT:DATA_OK\n") != std::string::npos &&
@@ -332,7 +336,7 @@ bool Run(const std::string& path, bool jit, bool mmu_faults, SBR1Mode sbr1,
trace.find("REV:BOOT:RUST\n") != std::string::npos;
const bool idle =
state == SBR1_STATE_IDLE || trace.find("REV:BOOT:IDLE\n") != std::string::npos;
return startup_ok && idle && trace.find("REV:PANIC") == std::string::npos &&
return reported && startup_ok && idle && trace.find("REV:PANIC") == std::string::npos &&
trace.find("REV:FATAL") == std::string::npos;
}
if (memory.Read32(info.trace) != TRACE_MAGIC)
@@ -426,12 +430,9 @@ int main(int argc, char** argv)
std::optional<u64> cycles;
bool broadway_h1a = false;
// --cycles' bound: MAX_CYCLES, or MAX_CLIENT_CYCLES when --broadway-h1a is among the options
// (ProbeBudget.h), known before they are read so that --cycles is read where it stands.
const auto is_client = [](const char* argument) {
return std::string_view(argument) == "--broadway-h1a";
};
const u64 max_cycles =
std::any_of(argv + 2, argv + argc, is_client) ? MAX_CLIENT_CYCLES : MAX_CYCLES;
// (ProbeBudget.h, ClientRequested), known before they are read so that --cycles is read where
// it stands.
const u64 max_cycles = ClientRequested(argc, argv) ? MAX_CLIENT_CYCLES : MAX_CYCLES;
std::optional<std::string> package_path;
std::optional<std::string> script_path;
for (int index = 2; index < argc; ++index)