mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MGPipe] the Fatal texts no longer claim P5 does not chunk
R-10's "P5 does not chunk (R-10)" wording predates the stage chunk budget: buffer content ranges are cut at MGPipeStageChunkBytes() (1e7c372e) and a texture level at whole-width slabs (9469d48e), so the claim is false for the two rows that can outgrow SEG_STAGE - and these messages are what a bring-up reads. - PipeWireEncoder::StageAllocate's two Fatal{RingOverrun, "SEG_STAGE"} sites now say what reaching them means: a blob is staged whole, a row that cuts its content at the stage chunk budget never arrives, so this is a record type with no cut or a piece still larger than the arena. - EncodeRecord's MaxRecordBytes arm says the bound is about the record's own bytes - the stage chunk budget cuts blobs, not a header, payload or inline tail - and keeps the integrator decision it names. - Comments in PipeWireCodec.h, EmitTables.cpp's maxrecop line, Config.h's STAGE_MB knob, PipeStats.h's MaxRecordBytes gauge, SplitRuntimePeek.h and WireLedgerChecks.h said P5 does no chunking; they now say what is cut and what is not. - PipeWireCodecTest.ARecordLargerThanHalfTheRingIsFatalRingOverrun asserts the new diagnostic text instead of "does not chunk (R-10)".
This commit is contained in:
+3
-1
@@ -476,7 +476,9 @@ namespace MobileGL::MG_Config {
|
||||
// codec publish a max-record-bytes counter rather than assume that is enough.
|
||||
Uint32 RingMb = 8;
|
||||
// MOBILEGL_IPC_STAGE_MB: SEG_STAGE size. Every blob and every var-tail's bytes live
|
||||
// here (R-10: no chunking in P5, so nothing may exceed it).
|
||||
// here ONE BLOB AT A TIME: a row whose content can outgrow the segment cuts it at the
|
||||
// stage chunk budget (MGPipeStageChunkBytes, a quarter of this), and a record type with
|
||||
// no cut is Fatal{RingOverrun, "SEG_STAGE"} rather than allowed to exceed it.
|
||||
Uint32 StageMb = 32;
|
||||
// MOBILEGL_IPC_SPIN_US: spin before parking on a doorbell, either direction.
|
||||
Uint32 SpinUs = 50;
|
||||
|
||||
@@ -87,8 +87,9 @@ namespace MGITest {
|
||||
// ring and 0 wraps in a process whose ring never filled are the same number and
|
||||
// completely different facts.
|
||||
//
|
||||
// maxRecordBytes / maxRecordBytesCap: R-10 says P5 does no chunking and must prove it
|
||||
// needs none. The cap is RingProducer::MaxRecordBytes() == MOBILEGL_IPC_RING_MB / 2,
|
||||
// maxRecordBytes / maxRecordBytesCap: R-10's bound on a record's OWN bytes - the
|
||||
// content rows cut their blobs at the stage chunk budget, nothing cuts the record. The
|
||||
// cap is RingProducer::MaxRecordBytes() == MOBILEGL_IPC_RING_MB / 2,
|
||||
// read from the ring this process actually got rather than recomputed from the
|
||||
// environment.
|
||||
//
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
// THE TWO ASSERTIONS THE WIRE PRODUCER'S LEDGER MAKES POSSIBLE, in one place so the scenarios
|
||||
// that carry them cannot drift apart on what the numbers mean.
|
||||
//
|
||||
// R-10 (ExpectMaxRecordBytesUnderCap). P5 does no chunking and must instead PROVE it never
|
||||
// needs any: no record may exceed RingProducer::MaxRecordBytes() == MOBILEGL_IPC_RING_MB / 2.
|
||||
// R-10 (ExpectMaxRecordBytesUnderCap). No record may exceed
|
||||
// RingProducer::MaxRecordBytes() == MOBILEGL_IPC_RING_MB / 2: the content rows cut their BLOBS
|
||||
// at the stage chunk budget, so what is left uncut is the record itself, and it has to be shown
|
||||
// to stay under the bound.
|
||||
// Half of that proof is already a Fatal - PipeWireCodec.cpp aborts Fatal{RingOverrun} on a
|
||||
// record ABOVE the cap - and it is the loud half. The quiet half is the one this assertion
|
||||
// covers: the phase has to publish the MAXIMUM ACTUALLY SEEN on a real workload, so that a
|
||||
@@ -91,10 +93,11 @@ namespace MGITest::WireLedger {
|
||||
<< where << ": R-10 - the largest record this session wrote is " << state.maxRecordBytes
|
||||
<< " bytes and RingProducer::MaxRecordBytes() is " << state.maxRecordBytesCap
|
||||
<< " (half of a " << (state.maxRecordBytesCap * 2)
|
||||
<< " byte SEG_CMD, i.e. MOBILEGL_IPC_RING_MB). P5 does NOT chunk: a record at or "
|
||||
"above the cap is Fatal{RingOverrun} at the encoder, and a maximum that has "
|
||||
"climbed to it is the proof obligation failing. Report it to the integrator, who "
|
||||
"decides between early chunking (P8) and a bigger default ring";
|
||||
<< " byte SEG_CMD, i.e. MOBILEGL_IPC_RING_MB). The content rows cut their blobs at "
|
||||
"the stage chunk budget, so nothing cuts this record: one at or above the cap is "
|
||||
"Fatal{RingOverrun} at the encoder, and a maximum that has climbed to it is the "
|
||||
"proof obligation failing. Report it to the integrator, who decides between a "
|
||||
"cut for that row and a bigger default ring";
|
||||
::testing::Test::RecordProperty("max_record_bytes",
|
||||
static_cast<int>(state.maxRecordBytes));
|
||||
::testing::Test::RecordProperty("max_record_bytes_cap",
|
||||
|
||||
@@ -1058,7 +1058,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// AND THE ROW, WHENEVER THE MAXIMUM MOVES. The summary line can carry the
|
||||
// number but not the name - MG_Util is below MG_Remote and has no WireOpName -
|
||||
// and the name is the actionable half: R-10 makes the integrator choose between
|
||||
// early chunking and a bigger ring, and that is a decision about a record
|
||||
// a cut for that row and a bigger ring, and that is a decision about a record
|
||||
// FAMILY. ClientSession::Stop prints the same pair at teardown, but a trace
|
||||
// replay never reaches it (measured: the OpenRA lane's library log ends mid-run
|
||||
// with no teardown line at all), so a stats-enabled run would otherwise publish
|
||||
@@ -1067,7 +1067,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
if (encoder.MaxRecordBytesSeen() > g_publishedMaxRecordBytes) {
|
||||
g_publishedMaxRecordBytes = encoder.MaxRecordBytesSeen();
|
||||
MGLOG_I("MGPipe: wire ledger: new maximum record - maxrec=%llu "
|
||||
"maxrecop=%s cap=%llu (R-10's proof obligation; P5 does not chunk)",
|
||||
"maxrecop=%s cap=%llu (R-10's proof obligation; blobs are cut, a record is not)",
|
||||
static_cast<unsigned long long>(g_publishedMaxRecordBytes),
|
||||
encoder.MaxRecordOpName(),
|
||||
static_cast<unsigned long long>(encoder.MaxRecordBytesCap()));
|
||||
|
||||
@@ -856,8 +856,10 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
const Uint64 need = Align8(size);
|
||||
if (need > m_stageCapacity) {
|
||||
MGLOG_F("MGPipe: Fatal{RingOverrun, \"SEG_STAGE\"} a %llu byte blob cannot fit a "
|
||||
"%llu byte staging segment at any occupancy; P5 does not chunk (R-10) - "
|
||||
"raise MOBILEGL_IPC_STAGE_MB or report the record to the integrator",
|
||||
"%llu byte staging segment at any occupancy; a blob is staged whole, so a "
|
||||
"row that cuts its content at MGPipeStageChunkBytes() never reaches this - "
|
||||
"raise MOBILEGL_IPC_STAGE_MB or report the record type with no cut to the "
|
||||
"integrator",
|
||||
static_cast<unsigned long long>(size),
|
||||
static_cast<unsigned long long>(m_stageCapacity));
|
||||
std::abort();
|
||||
@@ -914,9 +916,9 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
ReclaimStagedBytes();
|
||||
}
|
||||
MGLOG_F("MGPipe: Fatal{RingOverrun, \"SEG_STAGE\"} a %llu byte blob does not fit a %llu "
|
||||
"byte staging segment with %llu bytes still in flight (retiredSeq=%llu); P5 "
|
||||
"does not chunk (R-10) - raise MOBILEGL_IPC_STAGE_MB or report the record to "
|
||||
"the integrator",
|
||||
"byte staging segment with %llu bytes still in flight (retiredSeq=%llu); a "
|
||||
"blob is staged whole and only the rows that cut at the stage chunk budget "
|
||||
"stay small - raise MOBILEGL_IPC_STAGE_MB or report that row to the integrator",
|
||||
static_cast<unsigned long long>(size),
|
||||
static_cast<unsigned long long>(m_stageCapacity),
|
||||
static_cast<unsigned long long>(m_stageHead - m_stageTail),
|
||||
@@ -1016,12 +1018,12 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
|
||||
const Uint64 total = layout.TotalBytes;
|
||||
if (total > m_cmd->MaxRecordBytes()) {
|
||||
// R-10: P5 does no chunking and must instead PROVE it never needs any. This is
|
||||
// where the proof fails loudly if it was wrong.
|
||||
// R-10's record bound. The content rows cut their BLOBS at the stage chunk budget
|
||||
// (MGPipeStageChunkBytes); a record's own bytes have no such budget and die here.
|
||||
MGLOG_F("MGPipe: Fatal{RingOverrun, \"%s\"} a %llu byte record exceeds "
|
||||
"RingProducer::MaxRecordBytes() == %llu (half of a %llu byte SEG_CMD); P5 "
|
||||
"does not chunk (R-10) - report it to the integrator, who decides between "
|
||||
"early chunking and a bigger default ring",
|
||||
"RingProducer::MaxRecordBytes() == %llu (half of a %llu byte SEG_CMD); the stage chunk "
|
||||
"budget cuts blobs, not a record's own bytes - report the row to the "
|
||||
"integrator",
|
||||
WireOpName(op), static_cast<unsigned long long>(total),
|
||||
static_cast<unsigned long long>(m_cmd->MaxRecordBytes()),
|
||||
static_cast<unsigned long long>(m_cmd->Capacity()));
|
||||
|
||||
@@ -237,9 +237,9 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// reply-slot id (R-3), or kInvalidSeq if the ring refused it.
|
||||
//
|
||||
// A record larger than RingProducer::MaxRecordBytes() is Fatal{RingOverrun}, NOT a
|
||||
// wait: R-10 says P5 does no chunking and must instead PROVE it never needs any, so
|
||||
// this is where the proof fails loudly if it is wrong. MaxRecordBytesSeen() is the
|
||||
// counter that feeds that proof into MEASUREMENTS.
|
||||
// wait: R-10's content rows cut their blobs at the stage chunk budget, so this bound is
|
||||
// about a record's own bytes and this is where one that grew past it fails loudly.
|
||||
// MaxRecordBytesSeen() is the counter that feeds that bound into MEASUREMENTS.
|
||||
Uint64 EncodeRecord(MG_Pipe::MGPWireOp op, const void* payload, Uint64 payloadBytes,
|
||||
const void* varTail = nullptr, Uint64 varTailBytes = 0);
|
||||
|
||||
|
||||
@@ -1774,7 +1774,7 @@ TEST_F(PipeWireCodecTest, TheDecoderWritesNoRingControlFieldOfItsOwn) {
|
||||
}
|
||||
|
||||
TEST_F(PipeWireCodecTest, MaxRecordBytesSeenStaysFarBelowHalfTheRing) {
|
||||
// R-10's proof obligation. P5 does no chunking and must instead show it never needed any.
|
||||
// R-10's proof obligation, over a record's own bytes: the content rows cut their blobs.
|
||||
//
|
||||
// THE CAP IS ASKED FOR AT RUNTIME AND NEVER DERIVED FROM MOBILEGL_IPC_RING_MB, and this
|
||||
// phase is why: the number moved twice in one afternoon. s1 first found that the control
|
||||
@@ -2329,7 +2329,7 @@ TEST_F(PipeWireCodecTest, ANonZeroSizeWithNoSegmentIsFatal) {
|
||||
|
||||
TEST_F(PipeWireCodecTest, ARecordLargerThanHalfTheRingIsFatalRingOverrun) {
|
||||
// R-10's PROOF OBLIGATION, FAILING ON PURPOSE - the red-once for everything the phase
|
||||
// publishes as `maxrec=`. P5 does no chunking: a record above
|
||||
// publishes as `maxrec=`. Nothing cuts a record's own bytes, so a record above
|
||||
// RingProducer::MaxRecordBytes() == Capacity()/2 must abort by name at the ENCODER, on the
|
||||
// producing side, rather than becoming a nullptr from Reserve that some caller reads as
|
||||
// "the ring is full, wait" - which on an EMPTY ring would be a wait that never ends.
|
||||
@@ -2351,9 +2351,10 @@ TEST_F(PipeWireCodecTest, ARecordLargerThanHalfTheRingIsFatalRingOverrun) {
|
||||
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
|
||||
EXPECT_NE(r.Log.find("Fatal{RingOverrun,"), std::string::npos) << r.Log;
|
||||
EXPECT_NE(r.Log.find("exceeds RingProducer::MaxRecordBytes()"), std::string::npos) << r.Log;
|
||||
// The diagnostic has to name R-10 and the decision it forces, because the person reading it
|
||||
// has to choose between early chunking and a bigger ring and neither is a local fix.
|
||||
EXPECT_NE(r.Log.find("does not chunk (R-10)"), std::string::npos) << r.Log;
|
||||
// The diagnostic has to name the decision it forces, because the person reading it
|
||||
// has to choose between a cut for this row and a bigger ring and neither is a local fix.
|
||||
EXPECT_NE(r.Log.find("budget cuts blobs, not a record's own bytes"), std::string::npos)
|
||||
<< r.Log;
|
||||
}
|
||||
|
||||
TEST_F(PipeWireCodecTest, ARunThatLeavesItsSegmentIsFatal) {
|
||||
|
||||
@@ -224,8 +224,9 @@ namespace MobileGL::MG_Util::PipeStats {
|
||||
enum class Gauge : Uint32 {
|
||||
// R-10's PROOF OBLIGATION. The largest single record the wire encoder has written, in
|
||||
// bytes, and the cap it must stay under - RingProducer::MaxRecordBytes() ==
|
||||
// MOBILEGL_IPC_RING_MB / 2. P5 does no chunking and has to prove it needs none; before
|
||||
// this pair existed the only consumers of PipeWireEncoder::MaxRecordBytesSeen() were
|
||||
// MOBILEGL_IPC_RING_MB / 2. The content rows cut their blobs at the stage chunk
|
||||
// budget, so what this measures is a record's own bytes. Until this pair existed the
|
||||
// only consumers of PipeWireEncoder::MaxRecordBytesSeen() were
|
||||
// codec unit tests, so BRIEF 8 item 3 had no measurement from any real workload
|
||||
// (joint-v1.md 5, "Maximum record bytes: NO MEASUREMENT").
|
||||
MaxRecordBytes = 0,
|
||||
|
||||
Reference in New Issue
Block a user