diff --git a/Source/UnitTests/Core/IOS/Starlet/ProbeBroadwayTest.cpp b/Source/UnitTests/Core/IOS/Starlet/ProbeBroadwayTest.cpp index 17780ecb2b..dbba653c80 100644 --- a/Source/UnitTests/Core/IOS/Starlet/ProbeBroadwayTest.cpp +++ b/Source/UnitTests/Core/IOS/Starlet/ProbeBroadwayTest.cpp @@ -112,6 +112,22 @@ TEST(StarletProbeBroadway, TheClientsBudgetIsBoundedAndOnlyItsOwn) EXPECT_FALSE(ParseCycles("17179869184")); } +TEST(StarletProbeBroadway, OnlyTheOptionItselfRaisesTheBudget) +{ + const auto requested = [](std::vector argv) { + return ClientRequested(static_cast(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. diff --git a/Tools/StarletBootstrapProbe/ProbeBroadway.h b/Tools/StarletBootstrapProbe/ProbeBroadway.h index 4ef54970ca..eed5be05d2 100644 --- a/Tools/StarletBootstrapProbe/ProbeBroadway.h +++ b/Tools/StarletBootstrapProbe/ProbeBroadway.h @@ -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) { diff --git a/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp b/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp index 317ab046f5..85af8d2be2 100644 --- a/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp +++ b/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp @@ -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 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 package_path; std::optional script_path; for (int index = 2; index < argc; ++index)