Read logd in-process and recover the admin socket from a dead fd - #266
Merged
Conversation
JingMatrix
force-pushed
the
fix/logd-in-process-reader
branch
from
August 20, 2026 09:52
da96ef2 to
474ec43
Compare
Two failures compounded once the KeyAdmin listen fd was closed under the daemon (#265) -- on the reporter's device, MIUI power management reclaiming it on screen-off. The accept loop caught the resulting EBADF and immediately retried on the same dead fd, with no backoff and a full stack trace each time: ~7000 throws a second, ~2.4 cores, and a logcat flood that pushed logd to drop other readers' entries. The socket node stayed on disk with no listener behind it, so the WebUI got ECONNREFUSED and never recovered -- the daemon stays alive under Looper.loop, so service.sh's respawn never fires. serve() now binds through a bindListener() helper and, on accept failure, backs off (capped at 1s), logs only the first failure of a burst, and rebinds a fresh LocalServerSocket after three consecutive failures. A one-time external fd close now costs a short pause and a rebind instead of a permanent spin, and the WebUI comes back on its own. The logging path made the flood worse: LogTail spawned a `logcat -v threadtime *:I` child, so logd copied every app's info-and-above line into a pipe that the JVM then regex-filtered -- the whole device stream carried at full volume and amplified by the reader meant to observe it. It is replaced by an in-process native reader (libteesim_logcat.so) built on liblog's reader API, the same one `logcat` uses. It reads logd directly, filters in C to the lines we keep (TEESimulator, AndroidRuntime, fatal-level, the crash buffer, and the injected keystore pid), and serves them two ways: a bounded ring the WebUI polls incrementally over the /logs cursor, and rotating files under /data/adb/teesim/log so a crash's context survives a restart. /logs/download now streams those files rather than the in-memory ring. The reader emits nothing through logd, so it can never observe and re-capture itself. The liblog reader symbols are absent from the NDK stub; they are declared locally and left undefined at link (-Wl,-z,undefs), resolving at runtime against the device's real liblog. sepolicy grants the daemon's root-manager domain the logd connect and logdr_socket write it now needs directly. This is the first in-process JNI in the daemon, so the release build needs a keep rule: R8 lists only the custom proguard file (no getDefaultProguardFile), so the stock "keep native method names" rule was missing and R8 renamed LogTail and its natives -- ART then looked up Java_<obfuscated> and the daemon crash-looped with UnsatisfiedLinkError on the log-reader thread while the .so still exported the plain Java_org_matrix_teesim_LogTail_* names. The rule is restored, and the reader thread now contains any throwable so a log-reader failure disables the Logs panel instead of taking the whole daemon down.
JingMatrix
force-pushed
the
fix/logd-in-process-reader
branch
from
August 20, 2026 10:30
474ec43 to
675aad4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two failures compounded once the KeyAdmin listen fd was closed under the daemon (#265) -- on the reporter's device, MIUI power management reclaiming it on screen-off. The accept loop caught the resulting EBADF and immediately retried on the same dead fd, with no backoff and a full stack trace each time: ~7000 throws a second, ~2.4 cores, and a logcat flood that pushed logd to drop other readers' entries. The socket node stayed on disk with no listener behind it, so the WebUI got ECONNREFUSED and never recovered -- the daemon stays alive under Looper.loop, so service.sh's respawn never fires.
serve() now binds through a bindListener() helper and, on accept failure, backs off (capped at 1s), logs only the first failure of a burst, and rebinds a fresh LocalServerSocket after three consecutive failures. A one-time external fd close now costs a short pause and a rebind instead of a permanent spin, and the WebUI comes back on its own.
The logging path made the flood worse: LogTail spawned a
logcat -v threadtime *:Ichild, so logd copied every app's info-and-above line into a pipe that the JVM then regex-filtered -- the whole device stream carried at full volume and amplified by the reader meant to observe it. It is replaced by an in-process native reader (libteesim_logcat.so) built on liblog's reader API, the same onelogcatuses. It reads logd directly, filters in C to the lines we keep (TEESimulator, AndroidRuntime, fatal-level, the crash buffer, and the injected keystore pid), and serves them two ways: a bounded ring the WebUI polls incrementally over the unchanged /logs cursor, and rotating files under /data/adb/teesim/log so a crash's context survives a restart. /logs/download now streams those files rather than the in-memory ring. The reader emits nothing through logd, so it can never observe and re-capture itself.The liblog reader symbols are absent from the NDK stub; they are declared locally and left undefined at link (-Wl,-z,undefs), resolving at runtime against the device's real liblog. sepolicy grants the daemon's root-manager domain the logd connect and logdr_socket write it now needs directly.