diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index c3652d57..553345a7 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -802,11 +802,13 @@ set(MGL_ITEST_GLES_VIEW_FILTER set(MGL_ITEST_SPLIT_ARM_ONLY_FILTER "PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer") +# The two lists are named so that SplitLogPaths.cmake.in can reach the ambient CtWireScenario +# entries, which the integration job's inproc leg runs on the transport it exports. mgl_itest_lane("DirectGLES." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:${MGL_ITEST_GLES_VIEW_FILTER}:${MGL_ITEST_SPLIT_ARM_ONLY_FILTER}" - ENV "${MGL_ITEST_GLES_ENVIRONMENT}") + TEST_LIST MGL_AMBIENT_TESTS_GLES ENV "${MGL_ITEST_GLES_ENVIRONMENT}") mgl_itest_lane("DirectVulkan." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:${MGL_ITEST_SPLIT_ARM_ONLY_FILTER}" - ENV "${MGL_ITEST_VULKAN_ENVIRONMENT}") + TEST_LIST MGL_AMBIENT_TESTS_VULKAN ENV "${MGL_ITEST_VULKAN_ENVIRONMENT}") # ScenarioIsolationScenario's two cases in ONE process: the first leaves a deleted program current # and an error unread, the second asserts ScenarioTest::SetUp cleared both. The entries above run @@ -3211,10 +3213,10 @@ if (MOBILEGL_BUILD_DISAGGREGATED) endif() # P5c ct (docs/archive/contracts/CONTRACT-P5C.md §5): the two control records, split-only source for the -# same reason as f1's - the scenario reads the SERVER sink's tallies, which a monolith build -# has no symbol for. One block per case with a LOG PATH OF ITS OWN, the F1 pattern: -# SplitLogPaths.PrivateAndDistinct fails a lane whose cases share one file, and the death -# case's child truncates whatever path it inherits. +# same reason as f1's - the scenario reads the server's control-record counters (`ar`, `od` on the +# server role's PipeStats line), which only a build with a server role produces. One block per case +# with a LOG PATH OF ITS OWN, the F1 pattern: SplitLogPaths.PrivateAndDistinct fails a lane whose +# cases share one file, and the death case's child truncates whatever path it inherits. if (MOBILEGL_BUILD_DISAGGREGATED) target_sources(MobileGLIntegrationTest PRIVATE Scenarios/CtWireScenario.cpp) # P7 wave 2 package C adds the fifth: the OQ-10 capability read. It belongs in THIS list diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in index c13fd72b..1ab8fee4 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in +++ b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in @@ -2,6 +2,9 @@ # CTest appends ENVIRONMENT here; keep all previously discovered lane settings. file(MAKE_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/split-logs") file(MAKE_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/verify-split-logs") +# CtWireScenario reads the server's control-record counters (`ar`, `od`) off the server role's +# PipeStats line, so its entries turn the counters on with one line per frame. +set(mglCtWireStatsEnvironment "MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1") # P5b t2's two lanes ride the same rule: one private log path per entry, or # SplitLogPaths.PrivateAndDistinct is red for them (ID-53). # P5e vi's client-array lane joins the same list for the same reason: its two entries are the @@ -37,37 +40,34 @@ foreach(listName IN ITEMS @MGL_SPLIT_ARM_TEST_LISTS@) # P11 A2: every split arm runs T2, so the arena is declared emulated. set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MGITEST_PERSISTENT_MAP_ARM=emulated") elseif(entry MATCHES "CtWireScenario\\.") - set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_TEST_SERVER_COUNTERS=1") + set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "${mglCtWireStatsEnvironment}") endif() endforeach() endforeach() # P7 package L tier 3: the whole-suite Magma census. A LIST OF ITS OWN, and the log path is the -# ONLY property it gets - the loop above also arms the strict-arming, pmap and server-counter +# ONLY property it gets - the loop above also arms the strict-arming, pmap and persistent-map-arm # lane markers by entry name, and a census is here to be READ, not to assert three unrelated # oracles it never opted into. The path itself is not optional: these entries are # DirectVulkan.{Split,Spawn,Tcp}. names, so split_log_paths.py's ownership gate requires exactly # one absolute private path each, and on the spawn and tcp arms two processes append to it. # -# ONE EXCEPTION, AND IT IS TELEMETRY RATHER THAN AN ORACLE: CtWireScenario reads the server's -# own control-record tallies, and without MOBILEGL_TEST_SERVER_COUNTERS=1 they are not published -# at all - the case then reds on `server control-record telemetry missing`, which says nothing -# about Magma. Measured: that is exactly what the spawn arm's ApplierReset case did on this -# tier's first run. A switch that makes a counter readable is not the same kind of thing as the -# strict-arming, pmap or persistent-map-arm markers above, which make a case ASSERT something it -# never opted into, and those stay out of the census. +# One exception, telemetry rather than an oracle: CtWireScenario's counters. Without them the case +# reds on `server control-record telemetry missing`, which says nothing about Magma, and a switch +# that makes a counter readable does not make a case assert anything it never opted into, unlike +# the markers above. foreach(listName IN ITEMS @MGL_MAGMA_FULL_TEST_LISTS@) foreach(entry IN LISTS ${listName}) set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log") if(entry MATCHES "CtWireScenario\\.") - set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_TEST_SERVER_COUNTERS=1") + set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "${mglCtWireStatsEnvironment}") endif() endforeach() endforeach() # P7 wave 3 (V1): the verify comparator on the split arm. A LIST OF ITS OWN, like tier 3's and -# for tier 3's reason - the first loop also arms the strict-arming, pmap and server-counter lane +# for tier 3's reason - the first loop also arms the strict-arming, pmap and persistent-map-arm lane # markers by entry name, and this lane is here to compare fields, not to opt three unrelated -# oracles into a whole-binary set. +# oracles into a whole-binary set. CtWireScenario's counters are the same exception as tier 3's. # # THE PATH IS WHAT MAKES THE LANE ARMED RATHER THAN MERELY GREEN. These entries carry no # MGITEST_SPLIT_LANE marker (MG_IntegrationTest/CMakeLists.txt says why: it reds 13 query-only @@ -80,8 +80,20 @@ foreach(listName IN ITEMS @MGL_VERIFY_SPLIT_TEST_LISTS@) foreach(entry IN LISTS ${listName}) set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/verify-split-logs/${entry}.log") + if(entry MATCHES "CtWireScenario\\.") + set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "${mglCtWireStatsEnvironment}") + endif() endforeach() endforeach() +# The ambient CtWireScenario entries take the counters and a log of their own as well: the +# integration job's inproc leg runs them on the inproc transport it exports (under monolith they +# skip). The log is outside split-logs/, which holds the split arms' entries. +foreach(entry IN LISTS MGL_AMBIENT_TESTS_GLES MGL_AMBIENT_TESTS_VULKAN) + if(entry MATCHES "CtWireScenario\\.") + set_tests_properties("${entry}" PROPERTIES ENVIRONMENT + "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/ct-wire-${entry}.log;${mglCtWireStatsEnvironment}") + endif() +endforeach() foreach(entry IN LISTS MGL_SPLIT_PA_STRICT_DRAW_TESTS MGL_SPLIT_PA_STRICT_DISPATCH_TESTS) set_tests_properties("${entry}" PROPERTIES ENVIRONMENT diff --git a/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp index 17547b37..6f569265 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp @@ -10,9 +10,10 @@ // // applier_reset (§5.1): this process's first validate primes the pipe tracker, and the // FreshlyPrimed edge is the record's producer (MG_Impl/Pipe/PipeFill.cpp). What the scenario -// asserts is the SERVER sink's own counters - moved by nobody else - plus the pixels, so a -// reset that never crossed is red by the tally and a client that fell through to the driver -// is red by ScenarioFixture's emit-ordinal rule. +// asserts is the server's own count of the control records it applied (`ar` and `od` on the +// server role's PipeStats line, moved by nobody else) plus the pixels, so a reset that never +// crossed is red by the count and a client that fell through to the driver is red by +// ScenarioFixture's emit-ordinal rule. // // object_death (§5.2): a texture and a framebuffer die, the deaths cross, and the slots // recycle. The picture after recycling is the behavioural half: a stale twin answering for @@ -31,8 +32,6 @@ #include "../Harness/SplitRuntimePeek.h" #include -#include // `t6`: the spawn arm cannot observe the server process's counters -#include #if !defined(_WIN32) #include @@ -52,36 +51,6 @@ namespace MGITest { namespace { - MobileGL::MG_Remote::Server::ServerVerbSink& ServerVerbs() { - return MobileGL::MG_Remote::Server::ServerSessionInstance().Applier().Verbs(); - } - - // Inproc reads its local sink. A separate-process session snapshots that - // same sink on LogFlush, after the client fences its complete command prefix. - // TCP forwards the server log; unix+shm reads the server's local role file. - struct CounterSnapshot { - bool valid = false; - MobileGL::Uint64 resets = 0, serial = 0, deaths = 0; - }; - - CounterSnapshot ServerCounters() { - if (MobileGL::MG_Config::Transport != MobileGL::MG_Config::TransportMode::Spawn) { - return {true, ServerVerbs().ApplierResets(), ServerVerbs().ExpectedApplierResetSerial(), - ServerVerbs().ObjectDeaths()}; - } - MGPipeSyncPeerLog(); - const std::string log = PipeStatsWindow::ReadWholeFile(PipeStatsWindow::ServerLibraryLogPath()); - const auto at = log.rfind("MGPipe server counters:"); - if (at == std::string::npos) return {}; - const PipeStatsWindow::Window window{true, log.substr(at, log.find('\n', at) - at)}; - const auto resets = PipeStatsWindow::CounterOrAbsent(window, "resets"); - const auto serial = PipeStatsWindow::CounterOrAbsent(window, "reset-serial"); - const auto deaths = PipeStatsWindow::CounterOrAbsent(window, "deaths"); - if (resets < 0 || serial < 0 || deaths < 0) return {}; - return {true, static_cast(resets), static_cast(serial), - static_cast(deaths)}; - } - class CtWireScenario : public ScenarioTest { protected: void SetUp() override { @@ -109,8 +78,8 @@ namespace MGITest { } // The texture's level-0 content, read back through a throwaway FBO, as four bytes. - // The FBO is bound AND deleted here, so it never leaks a framebuffer death into the - // tally a case is watching. + // The FBO is bound AND deleted here, so its death lands in whichever window is open + // and a case that counts deaths reads it outside the window it counts. void ReadTextureLevel(GLuint texture, GLubyte out[4]) { GLuint fbo = 0; glGenFramebuffers(1, &fbo); @@ -120,6 +89,47 @@ namespace MGITest { glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); } + + // The control records the server applied in one window: `ar` (applier_reset records + // accepted) and `od` (object_death records dispatched) on the server role's PipeStats + // line. The entries set MOBILEGL_PIPE_STATS=1 and MOBILEGL_PIPE_STATS_PERIOD=1, so every + // swap ends a window and writes one line: under inproc the apply thread writes it to the + // server role's log, under spawn and tcp the server process writes or forwards it there. + struct ControlRecords { + bool valid = false; + long long applierResets = -1; + long long objectDeaths = -1; + }; + + // Ends the current window. The records and the present that ends it are kWaitNone, so + // the client moves on and the server counts them when it applies them. A readback is + // kWaitReply on every arm, and its answer is posted only after its record and every + // earlier one were applied, the present and the line it writes included. The harness's + // applied-seq wait is not that boundary off inproc: there the client's progress page + // moves with frames rather than with the peer's apply. + void EndWindow() { + Gl().EndFrame(); + GLubyte fence[4]{}; + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, fence); + EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "the window's fence readback left a GL error behind"; + } + + // Ends the setup's window, so that the next one counts only what follows the mark. + PipeStatsWindow::LogMark StartWindow() { + EndWindow(); + return PipeStatsWindow::MarkLaneLog(); + } + + ControlRecords EndWindowAndReadControlRecords(const PipeStatsWindow::LogMark& mark) { + EndWindow(); + const PipeStatsWindow::Window window = PipeStatsWindow::LastFromServerLogSince(mark); + if (window.found) RecordProperty("stats_line_server", window.line); + ControlRecords records; + records.applierResets = PipeStatsWindow::CounterOrAbsent(window, "ar"); + records.objectDeaths = PipeStatsWindow::CounterOrAbsent(window, "od"); + records.valid = records.applierResets >= 0 && records.objectDeaths >= 0; + return records; + } }; TEST_F(CtWireScenario, ApplierResetCrossesAtThePrimedEdgeInSerialOrder) { @@ -127,25 +137,25 @@ namespace MGITest { // The first verb of the process primes the tracker; the edge is the producer. The // clear also gives the case its pixels, so a lane that emitted nothing is red twice // (here and in the fixture's emit-ordinal rule). + const PipeStatsWindow::LogMark mark = PipeStatsWindow::MarkLaneLog(); glClearColor(0.2f, 0.4f, 0.6f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - const auto counters = ServerCounters(); - ASSERT_TRUE(counters.valid) << "server control-record telemetry missing"; - EXPECT_GE(counters.resets, 1u) - << "the FreshlyPrimed edge emitted no applier_reset; the server's g_applier was " - "never reset for this session"; - // The serial sequence is the session's own count (§1: asserted, never dispatched - // on): every record the sink accepted carried the serial it expected, or the counter - // and the accepted tally would disagree. - EXPECT_EQ(counters.serial, counters.resets) - << "an applier_reset record was dropped or replayed on the wire"; - GLubyte pixel[4]{}; glReadPixels(1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "Ct.ApplierReset.error"; const int expected[4] = {51, 102, 153, 255}; for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 1) << "Ct.ApplierReset.pixels"; + + // No swap precedes this one in the process, so its window holds the edge wherever the + // first validate ran (this clear or the fixture's state reset). The serial needs no + // check of its own: OnApplierReset counts only a record whose serial it accepted, and + // any other latches Fatal{ProtocolCorruption, "ApplierReset.ContextSerial"}. + const ControlRecords counters = EndWindowAndReadControlRecords(mark); + ASSERT_TRUE(counters.valid) << "server control-record telemetry missing"; + EXPECT_GE(counters.applierResets, 1) + << "the FreshlyPrimed edge emitted no applier_reset; the server's g_applier was " + "never reset for this session"; } TEST_F(CtWireScenario, TextureDeathCrossesAndTheRecycledSlotAnswersTheNewObject) { @@ -159,7 +169,7 @@ namespace MGITest { // went backend-agnostic and that the INPROC arm failed here with `deaths` 0 vs 0 // (docs/archive/notes/p7/magma-two-process-first-run.md §6); the table is what closes the inproc // half, and this case is the gate on it. It is also the RED-ONCE for that table: with - // the install site short-circuited, both cases red on the two EXPECT_GT below. + // the install site short-circuited, both cases red on their `od` assertions below. const GLubyte red[4] = {255, 0, 0, 255}; const GLubyte green[4] = {0, 255, 0, 255}; @@ -176,45 +186,14 @@ namespace MGITest { ASSERT_NEAR(before[i], redExpected[i], 1) << "Ct.TextureDeath.before"; // The object dies unbound so the destructor - and the death record - fire at the - // delete, and the tally is read AFTER the EmitAndWait the delete blocked on. + // delete. The counted window holds the delete alone: the FBO ReadTextureLevel deleted + // died in the setup's window, which StartWindow ends. glBindTexture(GL_TEXTURE_2D, 0); - const auto beforeCounters = ServerCounters(); - ASSERT_TRUE(beforeCounters.valid) << "server object-death telemetry missing"; - const MobileGL::Uint64 deathsBefore = beforeCounters.deaths; + const PipeStatsWindow::LogMark mark = StartWindow(); glDeleteTextures(1, &texture); - // THE FENCE, AND WHY IT IS NO LONGER A CLEAR. - // - // object_death is a kCtxObject value-class record whose wait class is kWaitNone, so - // under run-ahead the client publishes it and moves on, and the server-side tally below - // only moves at APPLY time. The clear this case used to fence on is kWaitNone TOO - - // PipeCalls.def's Clear row - so once the wait column governed (P5e's run-ahead), it - // waited for nothing and the read below raced the apply thread. Nothing had hidden that - // for a while except the per-record barriers the converted rows still took; P12 took - // those away on the wire arm (set_texture_params, respecify and the texture sub-data - // stop owning an answer), the client now runs further ahead, and the race stopped being - // a rarity. MEASURED on this entry under llvmpipe: 2 red of 5 runs (`deaths` 1 vs 1, - // the record applied a moment later) and 3 green of 3 with MOBILEGL_IPC_RUN_AHEAD=0. A - // timing race, never a lost record: the notice is emitted and applied either way. - // - // WHAT IS A REAL BOUNDARY, MEASURED ON ALL THREE ARMS RATHER THAN ASSUMED. The - // harness's applied-seq fence (WaitForSplitAppliedForTesting) is the right shape for - // inproc - and it is NOT usable here: this case is registered on the spawn and tcp arms - // too, the client's progress page there is advanced by frames rather than by the - // peer's own apply, and the wait times out. MEASURED with that fence in place: this - // case red at 5.0 s on DirectGLES.Tcp.Ct. - // - // A READBACK IS THE ONE BOUNDARY THAT HOLDS ON EVERY ARM. ReadPixels is - // kReplySlot|kWaitReply, so the client blocks on its answer, and an answer is posted - // only after its record has been APPLIED - and the ring applies in order, so the death - // published before it is applied too. (On the spawn and tcp arms the counter below - // comes from the peer's log, which ServerCounters() syncs before it reads - and that - // sync is itself a round trip the death has already crossed.) - GLubyte fencePixel[4]{}; - glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, fencePixel); - ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "the fence readback left a GL error behind"; - const auto afterCounters = ServerCounters(); - ASSERT_TRUE(afterCounters.valid) << "server object-death telemetry missing"; - EXPECT_GT(afterCounters.deaths, deathsBefore) + const ControlRecords counters = EndWindowAndReadControlRecords(mark); + ASSERT_TRUE(counters.valid) << "server object-death telemetry missing"; + EXPECT_GE(counters.objectDeaths, 1) << "the texture's death produced no object_death record; the server's twin was " "never told to let go"; @@ -323,17 +302,15 @@ namespace MGITest { // Die unbound, framebuffer first so the attachment's own death is a separate record. glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0); - const auto beforeCounters = ServerCounters(); - ASSERT_TRUE(beforeCounters.valid) << "server object-death telemetry missing"; - const MobileGL::Uint64 deathsBefore = beforeCounters.deaths; + const PipeStatsWindow::LogMark mark = StartWindow(); // THE CLIENT'S OWN ACCOUNTING, read beside the server's (P7 wave 2 package C, // CONTRACT-P7 §5.5's PipeSlotPeek clause). The two numbers answer DIFFERENT halves of // one death and neither implies the other, which is exactly why both are read here: - // * `deaths` below says the SERVER was told - the half the missing Magma table + // * `od` below says the SERVER was told - the half the missing Magma table // broke, and the only delivery a framebuffer has (no wire delete opcode at all); // * this one says the CLIENT returned the slot. That half has been backend-neutral // since P4a (PipeFill.cpp's NotifyAndFree frees whatever the notice does), so it - // is a CONTROL rather than the subject: if it ever regressed, a green `deaths` + // is a CONTROL rather than the subject: if it ever regressed, a green `od` // would be measuring an object that never let go of its handle. // `false` means the allocator is out of reach (a pull build, or Android's hidden // symbols), and the peek is then simply not asserted - "could not look" is not @@ -341,20 +318,9 @@ namespace MGITest { unsigned fboSlotsBefore = 0; const bool slotsReadable = PeekPipeSlotLiveCount(PipeSlotKind::Framebuffer, &fboSlotsBefore); glDeleteFramebuffers(1, &fbo); - // THE FENCE, the texture case's exactly: object_death is a kCtxObject value-class - // record with wait class kWaitNone, so under run-ahead it is published WITHOUT waiting - // for its own apply (production-safe: the in-order ring applies it before any record - // that recycles the slot), and the server-side counter this assertion reads only moves - // at apply time. A clear cannot be that boundary - its own class is kWaitNone too - so - // this waits on a READBACK, which is kWaitReply on every arm. The texture case carries - // the measurements, including the boundary that is right for inproc and wrong for - // spawn/tcp. - GLubyte fencePixel[4]{}; - glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, fencePixel); - ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "the fence readback left a GL error behind"; - const auto afterCounters = ServerCounters(); - ASSERT_TRUE(afterCounters.valid) << "server object-death telemetry missing"; - EXPECT_GT(afterCounters.deaths, deathsBefore) + const ControlRecords counters = EndWindowAndReadControlRecords(mark); + ASSERT_TRUE(counters.valid) << "server object-death telemetry missing"; + EXPECT_GE(counters.objectDeaths, 1) << "the framebuffer's death produced no object_death record - and no other " "opcode can carry it. Backend " << Gl().BackendName(); diff --git a/MobileGL/MG_Remote/Server/PipeApplier.cpp b/MobileGL/MG_Remote/Server/PipeApplier.cpp index 48a7fbbf..b962055f 100644 --- a/MobileGL/MG_Remote/Server/PipeApplier.cpp +++ b/MobileGL/MG_Remote/Server/PipeApplier.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -274,10 +275,10 @@ namespace MobileGL::MG_Remote::Server { // backend->SwapEGLBuffers -> BackendObject::SwapEGLBuffers -> eglSwapBuffers, and never // Present() - so the two paths do NOT both end here. The frame count staying in step with // the swap count rests entirely on c1 emitting exactly one present record per swap; - // nothing here compares Presents() to a swap count. If that drifts, the frame fence and + // nothing here compares the present count to a swap count. If that drifts, the frame fence and // TrimBufferPool's recycle watermark stop tracking frames - which is the reason the 1:1 // was wanted, recorded here so a future swap-without-present is looked for rather than - // assumed impossible. Presents() is exposed for a lane that wants to make the comparison. + // assumed impossible. // The frame's damage rides to the native present for the length of this call. MG_Backend::SetCurrentPresentDamage(DamageOfRecord(present.DamageCount, present.Damage)); table->Present(); @@ -302,7 +303,7 @@ namespace MobileGL::MG_Remote::Server { "waits on it for its credit (CONTRACT-P5E §2.4); returning a credit for " "serial 0 would release a wait that is asking about frame N"); } - m_lastPresentSerial = present.FrameSerial != 0 ? present.FrameSerial : m_presents; + const Uint64 serial = present.FrameSerial != 0 ? present.FrameSerial : m_presents; // §2.4's other half, and the reason ServerSession::ReturnPresentCredit has had no // production caller since v1 wrote it: ONE CREDIT PER SWAP, returned after Present() // has returned rather than before it, because what the client is waiting for is the @@ -314,7 +315,7 @@ namespace MobileGL::MG_Remote::Server { // or two ago at once, and a report posted only after this batch would race it. A // status query, no submit: the swap has just submitted. if (session->EventRingHasRoom()) ReportSignaledFences(/*flush=*/false); - session->ReturnPresentCredit(m_lastPresentSerial); + session->ReturnPresentCredit(serial); } return true; } @@ -350,7 +351,8 @@ namespace MobileGL::MG_Remote::Server { // MGPipeApplierReset passes because this IS the apply thread; the GL-thread direct // call it replaced is the Fatal arm. MG_Pipe::MGPipeApplierReset(); - ++m_applierResets; + if (MG_Util::PipeStats::Enabled()) + MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::ApplierResets, 1); return true; } @@ -608,7 +610,6 @@ namespace MobileGL::MG_Remote::Server { "release) is the only token that needs no entry", static_cast(bind.ClientContextToken)); } - ++m_bindContexts; #if MOBILEGL_BUILD_DISAGGREGATED // P14 S4 (docs/Disaggregated/design/11-state-ownership.md). THE NATIVE HALF OF THE SWITCH. // @@ -658,7 +659,7 @@ namespace MobileGL::MG_Remote::Server { // to nothing. MG_Backend::DirectGLES::ReleaseTwinsForWireObjectDeath(death.Handle, static_cast(death.Kind)); - ++m_objectDeaths; + if (MG_Util::PipeStats::Enabled()) MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::ObjectDeaths, 1); return true; } diff --git a/MobileGL/MG_Remote/Server/PipeApplier.h b/MobileGL/MG_Remote/Server/PipeApplier.h index 19dac709..01838dfa 100644 --- a/MobileGL/MG_Remote/Server/PipeApplier.h +++ b/MobileGL/MG_Remote/Server/PipeApplier.h @@ -183,20 +183,6 @@ namespace MobileGL::MG_Remote::Server { Bool OnSharedImage(const MG_Pipe::MGPSharedImageOp& op, Uint64 seq, MG_Pipe::MGPSharedImageReply& reply) override; - // P5c ct's tallies, for the same reason every other row's tally exists (R-16: a probe - // may not arm against a stub). ApplierResets counts the records ACCEPTED (serial - // checked, reset run); ObjectDeaths counts every record the sink dispatched. - Uint64 ApplierResets() const { return m_applierResets; } - Uint64 ObjectDeaths() const { return m_objectDeaths; } - Uint64 BindContexts() const { return m_bindContexts; } - // The ContextSerial the NEXT applier_reset record must carry. Exposed so a case can - // assert the sequence rather than only the count. - Uint64 ExpectedApplierResetSerial() const { return m_applierResetSerial; } - - // The session's per-verb tallies; the record verbs' are RecordVerbSink's. - Uint64 Presents() const { return m_presents; } - Uint64 LastPresentSerial() const { return m_lastPresentSerial; } - private: // A backend change ends every fence and query this sink holds (they are the old // backend's natives). @@ -231,20 +217,10 @@ namespace MobileGL::MG_Remote::Server { Bool QueryGpuComplete(QueryEntry& entry, Bool wait); UnorderedMap m_queries; Uint64 m_presents = 0; - Uint64 m_lastPresentSerial = 0; - // P5c ct. m_applierResetSerial is BOTH the expected ContextSerial of the next record - // and the count of accepted resets: the two are one number because the serial is the - // session's own count of applier_reset records (CONTRACT-P5C.md §1: asserted, never - // dispatched on). m_objectDeaths counts records dispatched, released twin or not - - // the idempotent second path answering "nothing held it" is a legal record. + // m_applierResetSerial is both the expected ContextSerial of the next record and the + // count of accepted resets: the serial is the session's own count of applier_reset + // records (CONTRACT-P5C.md §1: asserted, never dispatched on). Uint64 m_applierResetSerial = 0; - Uint64 m_applierResets = 0; - Uint64 m_objectDeaths = 0; - // P14 S1. BindContexts counts the records the sink dispatched and bound on the session; - // the token itself lives on the session (ServerSession::CurrentContextToken), which is - // the object the control plane's CreateContext/DestroyContext also own - one table, one - // owner, and the sink is only the ring's door into it. - Uint64 m_bindContexts = 0; // The newest native fence (shared_image NativeFence) this session handed out, kept so a // later fence with nothing left to export still answers with a descriptor: by then this // one has signaled (the backend waited the work out), and a signaled sync_file stays so. diff --git a/MobileGL/MG_Remote/Server/ServerMain.cpp b/MobileGL/MG_Remote/Server/ServerMain.cpp index e98f1771..04ac76b2 100644 --- a/MobileGL/MG_Remote/Server/ServerMain.cpp +++ b/MobileGL/MG_Remote/Server/ServerMain.cpp @@ -765,19 +765,6 @@ namespace { const auto* envelope = Protocol::GetCtrlEnvelope(buffer.data()); if (const auto* flush = envelope->msg_as_LogFlush()) { if (flush->ack()) break; - if (const char* inspect = std::getenv("MOBILEGL_TEST_SERVER_COUNTERS"); - inspect && std::strcmp(inspect, "1") == 0) { - // SyncPeerLog has fenced the client's complete command prefix. - // Acquire the applier's publication before reading its counters; - // this serial client sends no more records until this ack. - const auto applied = session.DataLink()->Progress()->appliedSeq.load(std::memory_order_acquire); - const auto& verbs = session.Applier().Verbs(); - MGLOG_I("MGPipe server counters: applied=%llu resets=%llu reset-serial=%llu deaths=%llu", - static_cast(applied), - static_cast(verbs.ApplierResets()), - static_cast(verbs.ExpectedApplierResetSerial()), - static_cast(verbs.ObjectDeaths())); - } FlushAck ack{control.get(), logForward.get(), flush->seq()}; MobileGL::MG_Util::Debug::WithLogBarrier(&SendLogAck, &ack); continue; diff --git a/MobileGL/MG_Test/Util/PipeStatsTest.cpp b/MobileGL/MG_Test/Util/PipeStatsTest.cpp index b4c63ed9..01139fbc 100644 --- a/MobileGL/MG_Test/Util/PipeStatsTest.cpp +++ b/MobileGL/MG_Test/Util/PipeStatsTest.cpp @@ -306,6 +306,9 @@ namespace { EXPECT_NE(line.find(" wlivepk="), String::npos) << line; EXPECT_NE(line.find(" wdefpk="), String::npos) << line; EXPECT_NE(line.find(" wdefsync="), String::npos) << line; + // The server's control-record counts, which CtWireScenario reads by these names. + EXPECT_NE(line.find(" ar="), String::npos) << line; + EXPECT_NE(line.find(" od="), String::npos) << line; } // Per-frame fields carry two decimals for the same reason acc/draw does: they are small @@ -544,6 +547,8 @@ namespace { // MEASUREMENTS.md entry will quote. EXPECT_STREQ(PS::NameOf(PS::ByteClass::StageSegmentBytes), "stage-segment-bytes"); EXPECT_STREQ(PS::NameOf(PS::CallClass::WireRecords), "wire-records"); + EXPECT_STREQ(PS::NameOf(PS::CallClass::ApplierResets), "applier-resets"); + EXPECT_STREQ(PS::NameOf(PS::CallClass::ObjectDeaths), "object-deaths"); EXPECT_STREQ(PS::NameOf(PS::Gate::EsprytRenderState), "espryt-render-state"); EXPECT_STREQ(PS::NameOf(PS::Gate::EsprytTextureSyncList), "espryt-texture-sync-list"); EXPECT_STREQ(PS::NameOf(PS::Gate::EsprytUnitBindingsEpoch), "espryt-unit-bindings-epoch"); diff --git a/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp b/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp index 37c94c55..1bf5e4e3 100644 --- a/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp +++ b/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -3290,21 +3291,27 @@ TEST(ApplierResetWireRoundTrip, TheSerialSequenceIsAssertedAndTheServerResetRuns EXPECT_TRUE(applied); }; - // The reset RUNS - asserted on the applier's own state, not on the tally (R-16: a probe - // may not arm against a stub, and a tally a stub could also move is a stub's witness). - // BoundVertexElements is one of the fields MGPipeApplierReset clears (PipeApply.cpp). + // The reset runs - asserted on the applier's own state: BoundVertexElements is one of the + // fields MGPipeApplierReset clears (PipeApply.cpp). The accepted count is the sink's + // applier-resets PipeStats counter. + namespace PS = MG_Util::PipeStats; + PS::SetEnabledForTesting(true); + PS::ResetForTesting(); MGPipeApplier().BoundVertexElements = MakeHandle(77); send(0); // 0 = the first make-current (CONTRACT-P5C.md §1) EXPECT_TRUE(MGPipeHandleIsNull(MGPipeApplier().BoundVertexElements)); - EXPECT_EQ(sink.ApplierResets(), 1u); - EXPECT_EQ(sink.ExpectedApplierResetSerial(), 1u); + EXPECT_EQ(PS::TotalCalls(PS::CallClass::ApplierResets), 1u); - // The second edge carries the next serial and is accepted in order. + // Each edge carries the next serial and is accepted in order; a serial the session does not + // expect aborts (CtWireFatals below), so accepting 1 and then 2 shows the expected one moved. MGPipeApplier().BoundVertexElements = MakeHandle(78); send(1); EXPECT_TRUE(MGPipeHandleIsNull(MGPipeApplier().BoundVertexElements)); - EXPECT_EQ(sink.ApplierResets(), 2u); - EXPECT_EQ(sink.ExpectedApplierResetSerial(), 2u); + EXPECT_EQ(PS::TotalCalls(PS::CallClass::ApplierResets), 2u); + send(2); + EXPECT_EQ(PS::TotalCalls(PS::CallClass::ApplierResets), 3u); + PS::ResetForTesting(); + PS::SetEnabledForTesting(false); } TEST(ObjectDeathWireRoundTrip, TheTextureAndFramebufferTwinsReleaseByHandleAndNeverByAStaleGeneration) { @@ -3323,6 +3330,12 @@ TEST(ObjectDeathWireRoundTrip, TheTextureAndFramebufferTwinsReleaseByHandleAndNe Uint64 bits; ~RestorePush() { MG_Config::Features.PipePush = bits; } } restore{savedPush}; + // The deaths the sink dispatched are its object-deaths PipeStats counter. + namespace PS = MG_Util::PipeStats; + PS::SetEnabledForTesting(true); + struct StatsOff { + ~StatsOff() { PS::SetEnabledForTesting(false); } + } statsOff; // Texture AND Framebuffer - the two kinds R-16 names for the death-recycle path. The // others share the one dispatch and the one table walk, so two kinds pin the shape: one @@ -3352,14 +3365,14 @@ TEST(ObjectDeathWireRoundTrip, TheTextureAndFramebufferTwinsReleaseByHandleAndNe ASSERT_EQ(liveGen(one.slot), 1u) << static_cast(one.kind); // The death crosses and the twin retires: the slot's live generation goes back to 0. - const Uint64 deathsBefore = sink.ObjectDeaths(); + const Uint64 deathsBefore = PS::TotalCalls(PS::CallClass::ObjectDeaths); const MGPHandleOnly death{MakeHandle(one.slot, 1), static_cast(one.kind), 0}; EXPECT_NE(wire.Encoder().EncodeRecord(MGPWireOp::ObjectDeath, &death, sizeof(death)), kInvalidSeq); bool applied = false; EXPECT_TRUE(wire.PumpOne(&applied)); EXPECT_TRUE(applied); EXPECT_EQ(liveGen(one.slot), 0u) << static_cast(one.kind); - EXPECT_EQ(sink.ObjectDeaths(), deathsBefore + 1u); + EXPECT_EQ(PS::TotalCalls(PS::CallClass::ObjectDeaths), deathsBefore + 1u); // THE ABA HALF (LiveGenAt's semantics): the slot is recycled forward to a NEW object // at generation 2, and the dead object's handle - replayed, as a duplicated or diff --git a/MobileGL/MG_Util/Metrics/PipeStats.cpp b/MobileGL/MG_Util/Metrics/PipeStats.cpp index 2f3c1dd1..0162d54c 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.cpp +++ b/MobileGL/MG_Util/Metrics/PipeStats.cpp @@ -131,6 +131,8 @@ // therefore a LOWER BOUND on the per-draw accessor count, and it is // the bound over exactly the six gates section 2.3.1 tabulates. // texture-* Per (target, level) emission, both backends. +// applier-resets ServerVerbSink::OnApplierReset, once per record accepted. +// object-deaths ServerVerbSink::OnObjectDeath, once per record dispatched. // // Gates: the six of section 2.3.1, each counted exactly once per probe. // @@ -252,6 +254,8 @@ namespace MobileGL::MG_Util::PipeStats { "wire-host-wait-us", "wire-indirect-native-dispatches", "resource-readbacks", + "applier-resets", + "object-deaths", }; const char* const kGateNames[kGateCount] = { "espryt-render-state", "espryt-texture-sync-list", "espryt-unit-bindings-epoch", @@ -658,6 +662,9 @@ namespace MobileGL::MG_Util::PipeStats { // never ran. It is deliberately NOT rsp: rsp reaches zero when the phase SUCCEEDS, so a // positive control built on it would fail on the day the debt is paid. line += " vbs=" + std::to_string(calls[static_cast(CallClass::ServerVerbBoundaries)]); + // The control records the server applied in this window (see the CallClass enum). + line += " ar=" + std::to_string(calls[static_cast(CallClass::ApplierResets)]); + line += " od=" + std::to_string(calls[static_cast(CallClass::ObjectDeaths)]); // P6 GATE 8's SECOND NUMBER (CONTRACT-P6.md §9 item 8): records/frame AFTER chunking, i.e. // the count of records the wire encoder actually committed this window rather than the // number of times an emitter was asked for one. The gap between the two is the whole diff --git a/MobileGL/MG_Util/Metrics/PipeStats.h b/MobileGL/MG_Util/Metrics/PipeStats.h index 6b666875..604b3d85 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.h +++ b/MobileGL/MG_Util/Metrics/PipeStats.h @@ -292,6 +292,12 @@ namespace MobileGL::MG_Util::PipeStats { // GPU write - before even asking whether the draw had a client-memory array to snapshot. // Not on the summary line; read through TotalCalls by the scenario that pins it. ResourceReadbacks, + // `ar` / `od` - the two control records, counted where the server applies them + // (ServerVerbSink::OnApplierReset after its serial check, ServerVerbSink::OnObjectDeath per + // record dispatched). A framebuffer has no other wire delete, so `od` is the only sign on + // the server that its death crossed. Server-side counts: a separate client process prints 0. + ApplierResets, + ObjectDeaths, Count }; diff --git a/docs/Disaggregated/design/09-build-switches-counters.md b/docs/Disaggregated/design/09-build-switches-counters.md index f0092259..839e9377 100644 --- a/docs/Disaggregated/design/09-build-switches-counters.md +++ b/docs/Disaggregated/design/09-build-switches-counters.md @@ -82,4 +82,4 @@ CMake:`MOBILEGL_BUILD_DISAGGREGATED`(OFF)、`MOBILEGL_BUILD_DISAGGREGATED_ ## 附 B:边界计数器(`MobileGL/MG_Util/Metrics/PipeStats.h`) -关闭时每站点一次全局 load + 一条永不命中的分支。字节类(stage buffer / texture / UBO / client 数组、`pmap`、`resid`、`csob-blob`、`seg` = 写进 `SEG_STAGE` 的字节)、调用类(`draws`、`accessor-calls`、纹理上传 emit / box / rect / jobs、`csom` / `csob`、`mpr`、`trp`、`rsp`、`wrec`、`maxrec` / `ringwraps` / `ringpads` / `ringwaits`、门铃 `srv` / `srvpark` / `cli` / `clipark`)与六个 memo 门的 hit/miss。每 `MOBILEGL_PIPE_STATS_PERIOD` 帧一条 `MGPipe stats:` 汇总行,按角色写日志。读法陷阱:`accessor-calls` 是静态下界,判性能只看 CPU 时间;spawn 下 client 不推进帧窗口(`frames=0`),窗口化字段在 spawn client 上无效,run total 仍有效([`docs/archive/notes/p6/README.md`](../../archive/notes/p6/README.md) §12.1)。站点清单(哪些路径**没有**接线)写在 `PipeStats.cpp` 头部,那份清单是契约。 +关闭时每站点一次全局 load + 一条永不命中的分支。字节类(stage buffer / texture / UBO / client 数组、`pmap`、`resid`、`csob-blob`、`seg` = 写进 `SEG_STAGE` 的字节)、调用类(`draws`、`accessor-calls`、纹理上传 emit / box / rect / jobs、`csom` / `csob`、`mpr`、`trp`、`rsp`、`wrec`、server 应用的控制记录 `ar`(applier_reset)/ `od`(object_death)、`maxrec` / `ringwraps` / `ringpads` / `ringwaits`、门铃 `srv` / `srvpark` / `cli` / `clipark`)与六个 memo 门的 hit/miss。每 `MOBILEGL_PIPE_STATS_PERIOD` 帧一条 `MGPipe stats:` 汇总行,按角色写日志。读法陷阱:`accessor-calls` 是静态下界,判性能只看 CPU 时间;spawn 下 client 不推进帧窗口(`frames=0`),窗口化字段在 spawn client 上无效,run total 仍有效([`docs/archive/notes/p6/README.md`](../../archive/notes/p6/README.md) §12.1)。站点清单(哪些路径**没有**接线)写在 `PipeStats.cpp` 头部,那份清单是契约。 diff --git a/docs/ci/ci-cleanup-log.md b/docs/ci/ci-cleanup-log.md index 6011309f..b276df24 100644 --- a/docs/ci/ci-cleanup-log.md +++ b/docs/ci/ci-cleanup-log.md @@ -63,6 +63,7 @@ them wrong. | 2026-10-18 | `Negative control A2 (corrupted capability)` | Test / `integration-verify` | the same five-entry rerun with `MOBILEGL_PIPE_VERIFY_CORRUPT=IsCapabilityEnabled`, accepting any red; it proved that the comparator compares the capability field (P13 W3c moved the residual block's cross-check there) | unit `PipeInputsTest.EveryCorruptedFieldIsNamedByTheComparator` (`Unit tests on the verify runtime`): every field with storage, the capability field among them, corrupted in turn must be the field the comparator names; then, in a forked child with the knob set to `IsCapabilityEnabled`, each of Clear, DrawArrays and ReadPixels whose class fills the field must report it at entry, and one must | | 2026-10-18 | `Negative control A (corrupted snapshot)` and `Keep lane logs before controls` | Test / `integration-verify-split` | the first re-ran the ten `VerifySplit.ClearThenReadPixelsScenario` entries of both backends with the same knob and accepted any red; the second only kept the lane's logs from being overwritten by that rerun | the in-lane `VerifySplitCorrupted` and `VerifySplitReadCorrupted` entries of both backends, required by name (`Verify-split control entries`); that the ambient entries arm is the per-entry arm proof, and that they run fatal is `VerifySplitArming.PipeVerifyArmingScenario.Armed` (the ambient environment plus its marker and log), which requires `fatal=1` on the arming line | | 2026-10-18 | unit `PipeInputsTest.{PoisonOmitKnobArmsTheOmission, BadPoisonOmitKnobIsFatalNamingTheKnob}`, and the `MOBILEGL_PIPE_POISON_OMIT` knob: `Features.PipePoisonOmit`, its parser on every verb (`MGPipeValidateForVerb`'s `ParsePoisonOmissionKnob`, compiled into every build) and the server stamp's own parse of it | Test / unit (every poison lane); product code | the knob existed only for negative control B, and the two cases pinned its parser and its `Fatal{PipeVerifyBadKnob}` | the PoisonOmitted and VerifySplitPoisonOmitted entries set the harness marker `MGITEST_POISON_OMIT` to the same pair, and the re-executed child arms it through `MGPipeSetPoisonOmission`, compiled only with `-DMOBILEGL_PIPE_VERIFY=ON` (`Harness/PoisonOmissionArm.h`); `OmittingOneFieldForOneVerbLeavesExactlyThatFieldStale`, `ReadingAnOmittedFieldAbortsNamingTheVerb` and `TheMutationNoticeRefreshesTheValueButNotTheStamp` withhold the stamp test-side and still run in every poison lane; the setter's scope to its verb, not its class, is checked by `OmittingOneFieldForOneVerbLeavesExactlyThatFieldStale` in a forked child, on the verify runtime only | +| 2026-10-18 | the `serial == resets` assertion of `CtWireScenario.ApplierResetCrossesAtThePrimedEdgeInSerialOrder`, and the `MOBILEGL_TEST_SERVER_COUNTERS` knob: `ServerMain`'s `MGPipe server counters:` line on every LogFlush and `ServerVerbSink`'s test-only tallies (`ApplierResets`, `ObjectDeaths`, `ExpectedApplierResetSerial`) | Test / `integration-split`, `integration-split-strict`, `integration-verify-split`, `integration (inproc)`; product code | the knob existed only so the spawn and tcp arms could read the sink's tallies; the serial assertion could not fail, since `OnApplierReset` moves the count and the serial together and any other serial latches `Fatal{ProtocolCorruption, "ApplierReset.ContextSerial"}` | `ar` / `od` on the server role's `MGPipe stats:` line, counted in `ServerVerbSink::OnApplierReset` / `OnObjectDeath`; every CtWire entry sets `MOBILEGL_PIPE_STATS=1` with period 1 and reads them on every arm, inproc included, over a window closed by a swap and a readback; the unit cases `ApplierResetWireRoundTrip.TheSerialSequenceIsAssertedAndTheServerResetRuns` and `ObjectDeathWireRoundTrip.TheTextureAndFramebufferTwinsReleaseByHandleAndNeverByAStaleGeneration` read the same counters, and the first shows the expected serial moving by having serial 2 accepted | ## Kept after investigation diff --git a/scripts/ci/tcp_server_fixture.py b/scripts/ci/tcp_server_fixture.py index d6124ffe..3dd9a3a9 100644 --- a/scripts/ci/tcp_server_fixture.py +++ b/scripts/ci/tcp_server_fixture.py @@ -68,7 +68,7 @@ def start(args): if args.state.exists(): raise RuntimeError(f'stale fixture state; run stop first: {args.state}') env = dict(os.environ, MOBILEGL_IPC_ROLE='server', MOBILEGL_IPC_DIAL='no', - MOBILEGL_IPC_TOKEN='', MOBILEGL_IPC_LOG_FORWARD='1', MOBILEGL_TEST_SERVER_COUNTERS='1', + MOBILEGL_IPC_TOKEN='', MOBILEGL_IPC_LOG_FORWARD='1', MOBILEGL_PIPE_STATS='1', MOBILEGL_PIPE_STATS_PERIOD='1') for key in ('MOBILEGL_TRANSPORT', 'MOBILEGL_IPC_SERVER_PATH', 'MOBILEGL_IPC_RING_MB', 'MOBILEGL_IPC_STAGE_MB', 'MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_ENDPOINT', 'MOBILEGL_BACKEND_TYPE'): diff --git a/tools/trace_replay/measure_loopback_cpu.py b/tools/trace_replay/measure_loopback_cpu.py index 18470916..d54b599e 100644 --- a/tools/trace_replay/measure_loopback_cpu.py +++ b/tools/trace_replay/measure_loopback_cpu.py @@ -64,7 +64,7 @@ def run(args, case, transport, repeat): LIBGL_ALWAYS_SOFTWARE='1', EGL_PLATFORM='surfaceless', MESA_GL_VERSION_OVERRIDE='3.3', MESA_GLSL_VERSION_OVERRIDE='330') for key in ('MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_DATA', 'MOBILEGL_IPC_TOKEN', 'MOBILEGL_IPC_DIAL', - 'MOBILEGL_IPC_REQUIRE_SAME_BUILD', 'MOBILEGL_TEST_SERVER_COUNTERS'): + 'MOBILEGL_IPC_REQUIRE_SAME_BUILD'): env.pop(key, None) if transport == 'tcp': env.update(MOBILEGL_IPC_CONTROL=args.endpoint, MOBILEGL_IPC_DATA='stream', MOBILEGL_IPC_TOKEN='', diff --git a/tools/trace_replay/measure_transport_cpu.py b/tools/trace_replay/measure_transport_cpu.py index a782394e..f3881994 100644 --- a/tools/trace_replay/measure_transport_cpu.py +++ b/tools/trace_replay/measure_transport_cpu.py @@ -122,7 +122,7 @@ def run(args, transport, case, repeat): LIBGL_ALWAYS_SOFTWARE='1', EGL_PLATFORM='surfaceless', MESA_GL_VERSION_OVERRIDE='3.3', MESA_GLSL_VERSION_OVERRIDE='330') for key in ('MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_DATA', 'MOBILEGL_IPC_TOKEN', 'MOBILEGL_IPC_DIAL', - 'MOBILEGL_IPC_REQUIRE_SAME_BUILD', 'MOBILEGL_TEST_SERVER_COUNTERS'): + 'MOBILEGL_IPC_REQUIRE_SAME_BUILD'): env.pop(key, None) if Path('/usr/share/glvnd/egl_vendor.d/50_mesa.json').exists(): env['__EGL_VENDOR_LIBRARY_FILENAMES'] = '/usr/share/glvnd/egl_vendor.d/50_mesa.json' diff --git a/tools/trace_replay/paired_tcp_benchmark.py b/tools/trace_replay/paired_tcp_benchmark.py index da24780d..f5607db0 100644 --- a/tools/trace_replay/paired_tcp_benchmark.py +++ b/tools/trace_replay/paired_tcp_benchmark.py @@ -140,7 +140,7 @@ def start_server(args, device): command = [sys.executable, str(script), 'start', '--serial', args.serial, '--package', args.package, '--listen', args.listen, '--token', args.token, '--allow-idle', '--state-file', str(args.server_idle_state), '--env', - 'MOBILEGL_TEST_SERVER_COUNTERS=1;MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1;' + 'MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1;' 'MOBILEGL_LOG_FILE_PATH=/data/data/' + args.package + '/files/mgl.log'] result = subprocess.run(command, check=True, capture_output=True, text=True, timeout=30) (args.out / 'server-start.txt').write_text(result.stdout + result.stderr, encoding='utf-8')