Skip to content

Normalize TDLib fatal log lines before hashing - #3374

Open
FrayxRulez wants to merge 1 commit into
developfrom
tdlib-fatal-message
Open

Normalize TDLib fatal log lines before hashing#3374
FrayxRulez wants to merge 1 commit into
developfrom
tdlib-fatal-message

Conversation

@FrayxRulez

@FrayxRulez FrayxRulez commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

When TDLib hits an internal LOG(FATAL) it aborts, and the log line it printed becomes the crash report's message — which is what the backend hashes. Those lines look like this:

[ 0][t 5][crypto.cpp:420][#1][!Session:4:download#0][&res != 1]
[ 0][t 3][crypto.cpp:420][#1][!Session:1:main][&res != 1]
[ 0][t 5][MessageInputReplyTo.cpp:129][#1][!MessagesManager] local message 840.18 in not a topic in chat 0 with flags 0

Everything volatile in them is in the message: the thread number, the scheduler tag, the datacenter index inside the session actor, and the message/chat/file ids and offsets in the trailing text. So one assertion fragments into a group per report, every group looks like a singleton, and the whole class reads as noise.

TryTranslateTdlibLog follows TryTranslateAstaCall: a recogniser that rewrites a line it recognises to a canonical form, and returns the line completely unchanged when it does not match. It matches TDLib's prefix by shape — level, thread, file.ext:line, then any number of context tags — not against a list of known files, so a new assertion normalises without a change here.

What it keeps, because it is the identity of the assertion:

  • the source location (crypto.cpp:420),
  • the actor name minus its instance index (Session:4:download#0Session:download; download is a different code path from main, the 4 is only which datacenter the account is on),
  • the stringified condition (`id == 0 || is_server()`, [&res != 1]) verbatim, digits included,
  • any second source reference in the free text (UserManager.cpp 4821, DialogDb.cpp at line 170) — Status.h and LogEvent.h are generic helpers whose real site is that trailing reference, and two sites in one file would otherwise merge.

What it drops: the thread and scheduler tags, the actor's instance index, and — the one genuinely arguable call — the numbers and quoted strings left in the free text. Those are the values the assertion happened to print, the file:line above already identifies which assertion failed, and the quoted string is invariably the database path, which carries the user's profile directory into the report.

Checked against every distinct TdException message the dashboard holds for the last 28 days: the rule collapses that set to well under half as many distinct forms, it is idempotent, and it leaves every message of every other error type in that set byte-for-byte unchanged. Note only the first line of a message is hashed, so that is the line this has to get right.

Not built. A .NET Native build was not available; the file was checked with CSharpSyntaxTree.ParseText(...).GetDiagnostics() (clean, so no syntax errors — that catches typos, not type errors), and the four regexes were run as real .NET Regex objects over the message corpus described above, which is what produced the figures.

🤖 Generated with Claude Code

A LOG(FATAL) line becomes the crash report message, and its thread number,
scheduler tag, session index and printed values differ on every crash, so a
single assertion is hashed into a group per report. Keep the source location,
the actor name and the stringified condition, drop the rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant