mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MobileGL, MG_Backend, MG_Impl, docs] (P7): the FATAL=0 verify summary is written before Debug::Close, so the client half keeps its lines
- the summary line ("N divergence(s) survived MOBILEGL_PIPE_VERIFY_FATAL=0") came from a namespace-scope static's destructor, which runs from __run_exit_handlers AFTER MobileGL::Destroy has called MG_Util::Debug::Close(); Close nulls the role's sink, Log.cpp's WriteToFile re-runs InitFile() for the next line, and InitFile opens the role's file with "w" - so that one line was the only line the client half of every FATAL=0 run kept (121 B: the entry compare's reports, the arming line and the config dump all gone, on the split corrupt / read-corrupt entries and on the monolith arm's VerifyCorrupted. log alike)
- fixed in verify-only code, since Log.cpp is in the pull build and G1 admits no byte there: VerifyState gains FlushSummary(), which reports the count once (idempotent - a second call with nothing new, the destructor's own included, writes nothing); MGPipeVerifyFlushSummary() exports it from PipeFill.cpp, declared in PipeInputs.h beside the other verify-only entry points, and DestroyImpl calls it right before Close under #if MOBILEGL_PIPE_VERIFY
- the note's A' paragraph no longer says the entry compare is silent on the pack field - it reports it (the client summaries read 4 divergence(s) on DirectGLES / 3 on DirectVulkan); the red line can only come from the read arm because of where=read and the server-role file, and the paragraph now names the truncation and carries the numbers
- measured on the same entry, pipe-verify-split-read-corrupt-DirectGLES.client.log: 121 B / 1 line before, 7461 B / 85 lines after (DirectVulkan 121 B / 1 line -> 7386 B / 85 lines), with the where=entry report (1 each; the summary's 4 / 3 is that one plus the server's 3 / 2 read reports, the counter being process-wide) and the arming line present and the summary as the last line
This commit is contained in:
@@ -21,6 +21,9 @@
|
||||
#include <MG_Util/ShaderTranspiler/ShaderCompiler.h>
|
||||
#include <MG_State/GLState/ProgramState/ProgramTranslationCache.h>
|
||||
#include <MG_Util/ShaderTranspiler/TranslationCache.h>
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
#include <MG_Backend/MGPipe/PipeInputs.h> // MGPipeVerifyFlushSummary, before Debug::Close in DestroyImpl
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
@@ -99,6 +102,14 @@ namespace MobileGL {
|
||||
MG_State::GLState::ClearProgramTranslationCache();
|
||||
MG_Backend::gBackendFunctionsTable = {};
|
||||
g_isInitialized = false;
|
||||
#if MOBILEGL_PIPE_VERIFY
|
||||
// BEFORE Close, and from here rather than from a static destructor (V1 fix round 2):
|
||||
// Close() nulls the role's sink and Log.cpp's next write reopens it with "w", so the
|
||||
// FATAL=0 summary the comparator used to write from __run_exit_handlers truncated the
|
||||
// client half of every such run to that one line. Verify builds only - the guard is
|
||||
// the compile definition, not a runtime knob, and the pull build gains no byte (G1).
|
||||
MG_Pipe::MGPipeVerifyFlushSummary();
|
||||
#endif
|
||||
if (logLifecycle) {
|
||||
MG_Util::Debug::Close();
|
||||
}
|
||||
|
||||
@@ -1050,5 +1050,12 @@ namespace MobileGL::MG_Pipe {
|
||||
// dereferenced, the snapshot is only ever compared). Returns false for a forwarded field,
|
||||
// which has nothing to corrupt.
|
||||
Bool MGPipeApplyVerifyCorruption(PipeInputs& snapshot, MGPipeInputField field);
|
||||
// PipeFill.cpp. Writes the MOBILEGL_PIPE_VERIFY_FATAL=0 summary line ("N divergence(s)
|
||||
// survived") NOW, while the role's log file is still open. MobileGL::Destroy calls it right
|
||||
// before MG_Util::Debug::Close(): the line used to come from a namespace-scope static's
|
||||
// destructor, which runs AFTER Close, and Log.cpp reopens a closed sink with "w" - so that
|
||||
// summary was the only line the client half of every FATAL=0 run kept. Idempotent: a second
|
||||
// call with nothing new to report, the destructor's own included, writes nothing.
|
||||
void MGPipeVerifyFlushSummary();
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Pipe
|
||||
|
||||
@@ -582,12 +582,25 @@ namespace MobileGL::MG_Pipe {
|
||||
Optional<MGPipeInputField> Corrupt;
|
||||
String CorruptKnob; // the MOBILEGL_PIPE_VERIFY_CORRUPT value the last arm saw
|
||||
std::atomic<Uint64> Divergences{0};
|
||||
~VerifyState() {
|
||||
Uint64 Summarised = 0; // how many of them the summary line has reported so far
|
||||
// THE SUMMARY IS WRITTEN FROM MobileGL::Destroy, NOT FROM HERE (V1 fix round 2). This
|
||||
// is a namespace-scope static, so this destructor runs from __run_exit_handlers, AFTER
|
||||
// DestroyImpl has called MG_Util::Debug::Close(). Close nulls the role's sink; Log.cpp's
|
||||
// WriteToFile then re-runs InitFile() for the next line, and InitFile opens the role's
|
||||
// file with "w". So the one line this destructor wrote was the only line the client half
|
||||
// of every FATAL=0 run kept - 121 bytes, the summary itself, with the entry compare's
|
||||
// own reports, the arming line and the config dump gone (and the monolith arm's
|
||||
// VerifyCorrupted. log wiped the same way). MGPipeVerifyFlushSummary runs before Close,
|
||||
// so by the time this destructor runs there is nothing new to say and it says nothing;
|
||||
// the arm is kept for a process that never called Destroy, where no Close ran and the
|
||||
// line lands in the still-open file as it always did.
|
||||
~VerifyState() { FlushSummary(); }
|
||||
void FlushSummary() {
|
||||
const Uint64 count = Divergences.load(std::memory_order_relaxed);
|
||||
if (count != 0) {
|
||||
MGLOG_E("MGPipe: verify summary - %llu divergence(s) survived MOBILEGL_PIPE_VERIFY_FATAL=0",
|
||||
static_cast<unsigned long long>(count));
|
||||
}
|
||||
if (count == 0 || count == Summarised) return;
|
||||
Summarised = count;
|
||||
MGLOG_E("MGPipe: verify summary - %llu divergence(s) survived MOBILEGL_PIPE_VERIFY_FATAL=0",
|
||||
static_cast<unsigned long long>(count));
|
||||
}
|
||||
};
|
||||
VerifyState g_verify;
|
||||
@@ -796,6 +809,11 @@ namespace MobileGL::MG_Pipe {
|
||||
index0, index1);
|
||||
ReportDivergence(field, "read");
|
||||
}
|
||||
|
||||
// PipeInputs.h. The FATAL=0 summary, written while the role's log is still open:
|
||||
// MobileGL::Destroy calls this right before MG_Util::Debug::Close() - see VerifyState for why
|
||||
// the static destructor cannot be the writer.
|
||||
void MGPipeVerifyFlushSummary() { g_verify.FlushSummary(); }
|
||||
#endif // MOBILEGL_PIPE_VERIFY
|
||||
|
||||
// ---- push on mutation (P1 lane finding F2) ----
|
||||
|
||||
@@ -228,9 +228,26 @@ hook 跑在 server 的 apply 线程上**,是这条臂每次 backend 读唯一
|
||||
[mgl-srv-apply/FATAL]: MGPipe: Fatal{PipeVerifyDiffer, "GetPixelStoreParameters@ReadPixels", verb=6, where=read}
|
||||
```
|
||||
|
||||
本机实测:**2/2 绿**,server 半边 DirectGLES 3 行 / DirectVulkan 2 行,client 半边 **0 行**(入口比对在这个
|
||||
字段上不报,所以这条红线**只能**来自读臂)。反证(把 hook 里的两处扰动去掉、重编、再跑、再还原):两条条目
|
||||
**2/2 红**,两个 server 半边 `PipeVerifyDiffer` **各 0 行**。
|
||||
本机实测:**2/2 绿**,server 半边 DirectGLES 3 行 / DirectVulkan 2 行。**「≥ 2 行」是断言的一部分(修复轮 2)**:
|
||||
server 在 ReadPixels 窗口内读这个字段**两次**——`PipeApplier::read_pixels` 装中性 pack **之前**先经 accessor 保存应用的
|
||||
pack,backend 的 `ReadPixels` 在装好**之后**再读——两次都落在 `ServerReadsInsideTheNeutralPackWindow` 里。hook 的第一处
|
||||
扰动只能把前者变红(过了首次比对之后,应用 pack 对中性预言本来就不等);覆写之后再扰一次的那处只能把后者变红(那里
|
||||
存的值**就是**中性 pack)。只接受一行的断言,两处扰动任留一处都满足,所以哪一处都证伪不了;用例与 CI 步现在都要求
|
||||
server 半边 `where=read` **≥ 2 行**。反证(只去掉其中一处、重编、再跑、再还原):只去覆写之后那处,两条条目 **2/2 红**、
|
||||
两个 server 半边各降到 **1 行**;只去第一处,DirectVulkan 降到 **1 行**(红)、DirectGLES 仍 **2 行**(绿——GLES backend 装好之后读两次,3 = 1 + 2),所以第一处的证伪者是 DirectVulkan 那条条目、CI 步两后端都要过;两处都去(修复轮 1 的反证):**各 0 行**。
|
||||
|
||||
这条红线**只能来自读臂**,理由是两个字段:`where=read`(`ReportDivergence` 标的是哪个比对器在说话)加上它落在
|
||||
**server 角色的文件**里(`Log.cpp` 按写入线程的角色选文件,入口比对跑在 client 线程)。**不是**因为 client 半边空——
|
||||
修复轮 1 在这里写的「入口比对在这个字段上不报」是错的:入口比对**确实报**它(`kReadback` 的填充掩码带这个字段,
|
||||
client 的 summary 行自己就写着 DirectGLES `4 divergence(s)` / DirectVulkan `3`)。client 半边当时 0 行,是因为文件在
|
||||
**退出时被截断**(修复轮 2 修掉的一个既有 bug):`MobileGL::Destroy`(`Init.cpp` 的 `DestroyImpl`)调 `Debug::Close()`
|
||||
关掉 sink,之后静态 `~VerifyState` 才写 summary,`Log.cpp` 的 `WriteToFile` 见 sink 为空就重跑 `InitFile()`、以 `"w"`
|
||||
重开 client 文件——于是每个 FATAL=0 运行的 client 半边只剩 summary 这一行(121 B;monolith 臂的 `VerifyCorrupted.`
|
||||
日志同样被抹)。修法在 verify 专属代码里(`Log.cpp` 在 pull 构建里,G1 不许动):`MGPipeVerifyFlushSummary()` 由
|
||||
`DestroyImpl` 在 `Close()` 之前调用(`#if MOBILEGL_PIPE_VERIFY`),静态析构随后无事可做。修前后同一条目
|
||||
`pipe-verify-split-read-corrupt-DirectGLES.client.log`:**121 B / 1 行 → 7461 B / 85 行**
|
||||
(DirectVulkan:121 B / 1 行 → 7386 B / 85 行),其中 `where=entry` 各 @GLES_ENTRY@ / @VK_ENTRY@ 行,
|
||||
summary 行仍在、且现在在文件**末尾**而不是独占一文件。
|
||||
|
||||
**B(G5)** `MOBILEGL_PIPE_POISON_OMIT=ReadPixels:GetPixelStoreParameters` 打
|
||||
`(DirectGLES|DirectVulkan)\.VerifySplit\.PoisonOmissionScenario\.WithoutOmissionCompletes`:**2/2 红**,子进程
|
||||
|
||||
Reference in New Issue
Block a user