Skip to content

ext-rdump: in-process RDUMP memory dumps for reli#1

Merged
sj-i merged 95 commits into
mainfrom
claude/sharp-bardeen-j35RZ
May 29, 2026
Merged

ext-rdump: in-process RDUMP memory dumps for reli#1
sj-i merged 95 commits into
mainfrom
claude/sharp-bardeen-j35RZ

Conversation

@sj-i

@sj-i sj-i commented May 29, 2026

Copy link
Copy Markdown
Member

What this is

A PHP extension that writes a memory dump of the current process in reli's
RDUMP format, from inside the process, without ptrace. The file is the same
format reli produces externally, so it is analysed with the usual
reli inspector:memory:analyze / inspector:memory:report flow.

rdump_dump('/tmp/app.rdump');                 // bool rdump_dump(string $path, bool $full = false)

The motivation is production capture: reli can already snapshot a process from
outside via process_vm_readv/ptrace, but that needs ptrace granted to the
runtime. This dumps from within instead, which is often easier to ship.

Output parity with reli

Verified on the same process: the default dump is byte-format identical and
captures the same regions as reli's external dump (same maps/region counts and
size; reli reads the file directly). $full = true additionally embeds
read-only file-backed segments for host-independent analysis (broader than
reli's targeted --include-binary, so larger but never short on segments).

In-process dumping is also markedly faster than the external read (~3.2 GB/s
direct vs reli's ~0.5 GB/s per-byte in a local tmpfs benchmark), since it avoids
the cross-process copy and PHP/FFI overhead.

memory_limit auto-dump

Opt-in via php.ini, written straight from zend_error_cb on an
Allowed memory size … exhausted fatal — catching even the OOMs a
register_shutdown_function cannot. Guarded so it can't run away:

  • rdump.oom_dump — path (supports %p PID, %i thread id, %t time, %%).
  • rdump.oom_dump_max — dumps per worker process (default 1; 0 = unlimited).
  • rdump.oom_dump_min_interval — minimum seconds between dumps.
  • rdump.oom_dump_max_total — byte budget across *.rdump in the dir (K/M/G/T,
    e.g. 1.5G), counting the incoming dump's own size.
  • rdump.oom_dump_marker — also drop a <path>.done once complete, so a
    watcher never ships a half-written file.
  • rdump_set_oom_dump(?string $path, bool $full = false): bool — per-request
    runtime override.

Safety / security

  • The dump is a verbatim copy of process memory, so the output file is created
    0600 with O_EXCL / O_NOFOLLOW / O_CLOEXEC; an existing regular file is
    unlinked and replaced by a fresh inode (a stale read fd can't see the new
    dump), and non-regular targets (symlink/FIFO/device/dir) are refused.
  • rdump.safe_read reads regions through /proc/self/mem, so a region another
    thread unmaps mid-dump yields zero-filled bytes instead of a crash.
    Default on under ZTS (e.g. FrankenPHP), off under NTS (no concurrency, and the
    direct copy is faster).
  • fclose failures (deferred NFS/quota errors) are surfaced as dump failures.

Compatibility / tests

  • Linux, 64-bit little-endian, PHP 7.0 – 8.5 (NTS and ZTS); compile-time
    guards reject unsupported targets. Typed arginfo without a stub (7.0-safe).
  • 6 .phpt tests plus CI across the full PHP × NTS/ZTS matrix: smoke (incl.
    safe_read), the OOM hook (INI + runtime), %p/%i/%% expansion, the
    count/interval/byte-budget guards, the completion marker (incl. stale-marker
    handling), a valgrind leak check, and an integration job that has reli
    actually inspect/analyze an ext-rdump dump.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5


Generated by Claude Code

claude added 30 commits May 28, 2026 14:22
ext-rdump exposes rdump_dump(string $path, bool $full = false), which
writes the calling PHP process's own memory into reli's RDUMP v3 dump
format from inside the process -- no ptrace, no process_vm_readv.

It resolves executor_globals / compiler_globals / basic_globals by their
real addresses (offsetof from a stable member, so the same code works for
both NTS and ZTS on PHP 7.0+), reads /proc/self/maps for the VMA list,
and copies the relevant regions straight out of its own address space.

Captured by default: anonymous, [heap]/[stack], writable file-backed
segments and opcache shared memory -- enough for reli to walk ZendMM
chunks, huge allocations, VM stacks and compiler arenas offline.
$full = true also embeds read-only file-backed segments for a fully
self-contained dump.

The output is byte-compatible with Reli\Inspector\MemoryDump\
MemoryDumpWriter and analysable with:
  php reli inspector:memory:dump:inspect <file>
  php reli inspector:memory:analyze     <file>

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Mirrors reli's supported target range (PHP 7.0 .. 8.5) across both
thread-safety builds. A GitHub Actions matrix runs each combination
inside the official php:<ver>-cli (NTS) and php:<ver>-zts (ZTS) images.

Checkout happens on the host runner and the build runs via `docker run`
with the tree mounted, so node20-based actions don't have to execute on
the old glibc of the 7.0-7.4 images. ci/build-and-test.sh installs
PHPIZE_DEPS (falling back to archive.debian.org for the archived Debian
releases those images use), builds with phpize/configure/make, runs the
phpt suite when available, and ends with a version-agnostic smoke gate
that asserts rdump_dump() writes a valid RDUMP file.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
The php:7.0-7.x images run on archived Debian releases (stretch et al.)
whose archive signing keys have expired, so apt-get install aborted with
"repository is not signed". Permit the insecure archive explicitly in
the fallback path (AllowInsecureRepositories + --allow-unauthenticated)
so PHPIZE_DEPS installs on those containers.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Older run-tests.php (PHP 7.0-7.x) does not auto-detect the PHP binary
and aborts with "TEST_PHP_EXECUTABLE must be set", which made the phpt
suite silently no-op on those versions. Set it explicitly and also fail
the step on run-tests' own ERROR: lines so a non-running suite can't
pass as green.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
actions/checkout@v4 runs on the deprecated Node.js 20 runtime. v5 runs
on Node.js 24, clearing the deprecation warning.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
- composer.json: type "php-ext" with a php-ext block (extension-name
  rdump, NTS+ZTS, linux only) so the extension can be installed with
  PIE, e.g. `pie install reliforp/ext-rdump`.
- package.xml: PEAR/PECL 2.0 package definition so it can be built and
  installed with `pecl install package.xml` (or published to the PECL
  channel and installed via `pecl install rdump`).

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Benefit-focused overview: take a point-in-time dump of the current PHP
process and analyse it with reli (call stack, per-type memory and
references, leak / resource-leak / cycle / bottleneck detection).
Covers install (source / PIE / PECL), the rdump_dump() API with trigger
examples (incl. a memory_limit-exhaustion shutdown handler), analysing
with reli, and limitations.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
AddressSanitizer cannot be used here: ASan instruments the same process
whose whole address space this extension dumps, so reading ASan's own
shadow-memory regions (which appear in /proc/self/maps under ASan) aborts
in the fwrite interceptor before leak detection runs. valgrind completes
the full dump path instead.

The new leak-check job dumps repeatedly under USE_ZEND_ALLOC=0 valgrind
and gates purely on the leak summary's lost-block counts. valgrind's
"Invalid read" / "uninitialised write" notes are inherent to dumping raw
process memory (page tails past the brk, never-written bytes) and are not
leaks, so they are ignored.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
phpize/autoheader leave config.h.in~ and configure~ backups in the tree.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Lead with what users get (point-in-time self-snapshot analysed by reli:
call stack, per-object memory and references, automatic leak / resource-
leak / cycle / bottleneck detection) instead of the internal mechanism.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
The PECL summary and composer description show up in listings, so lead
with the benefit ("capture a PHP process's memory and analyse it with
reli to find leaks, reference cycles and memory bottlenecks") instead of
jargon like "in-process self-memory dump in RDUMP format".

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
"...and analyse it with reli" read as if the extension analyses too.
Reword to "...for offline analysis with reli" so the split is clear: the
extension captures, reli analyses.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
A trailing noun list ("(leaks, cycles, bottlenecks)") is idiomatic but
flat. Reword to "reli then analyses it to find leaks, reference cycles
and memory bottlenecks" so there is a clear verb and reli is the subject
doing the finding.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
"for offline analysis with reli (finding leaks, reference cycles, memory
bottlenecks, etc.)" attaches the verb to reli's analysis and the "etc."
keeps the list open-ended, reading more naturally than the bare noun
list.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
"without ptrace" is insider framing that doesn't mean much to a general
user; match the package.xml summary wording instead.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
rdump_dump() allocates via libc malloc, not the memory_limit-governed
Zend heap, so it runs fine straight after a memory_limit fatal without an
emergency reserve -- verified by exhausting a 16M limit and dumping from
the shutdown handler. Simplify the example and note that the reserve the
sidecar client needs is for its PHP-level work, not for this extension.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Earlier text wrongly said the bailout unwinds the VM stack. It does not:
a bailout only cuts the call chain and leaves EG(vm_stack_top) where the
OOM happened. rdump_dump()'s body uses libc malloc, but *calling* it
pushes a VM-stack frame; the VM stack grows one ~256 KB page at a time,
so the call reuses the current page (no emalloc) unless the stack sits at
a page boundary, when a fresh page must be allocated and can fail at the
limit. Note the ~256 KB reserve as the hard guarantee, matching why
reli-prof-sidecar-client's MemoryLimitHandler keeps one.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Free a ~256 KB reserve at the top of the handler so the dump call's
VM-stack frame is guaranteed headroom even when the stack sits at a page
boundary. Comment kept terse; the dump body itself stays off the
memory_limit heap (libc malloc).

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Measured the moving parts: error_get_last() allocates ~376 B (the message
is refcount-shared, so its length doesn't matter), strpos() 0 B, and a VM
stack page is exactly 256 KB (262144). A VM-stack page extension is
all-or-nothing, so a small reserve can't guarantee it anyway; its real
job is the handler's own small post-OOM bookkeeping, which a few KB
covers comfortably. Use a 4 KB reserve and describe it accurately.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
rdump.oom_dump=<path> makes the extension dump from the engine's error
callback when an "Allowed memory size exhausted" fatal fires. This
captures OOMs a register_shutdown_function cannot: when the fatal is a
VM-stack allocation (or the stack sits at a page boundary), pushing the
shutdown closure's own frame also fails and the handler never runs. The
C hook fires on the intact stack before any unwind, and rdump_dump()'s
libc-malloc path is unaffected by the exhausted limit.

zend_error_cb's signature is version-branched (7.0-7.1 / 7.2-7.4 / 8.0 /
8.1+), mirroring php-memory-profiler. Adds module globals + INI
(rdump.oom_dump, rdump.oom_dump_full), MINIT/MSHUTDOWN install/restore,
and a re-entrancy guard. CI now exercises the hook on every version by
OOM-ing via pure recursion and asserting a dump is written.

Verified: pure recursion at memory_limit=2M produces no shutdown-handler
invocation ("in Unknown on line 0") yet the hook writes a valid 12 MB
RDUMP that reli inspects cleanly; phpt still passes; valgrind still
reports zero leaks.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Lets code enable/disable and re-point the memory_limit auto-dump at
runtime, overriding the rdump.oom_dump INI for the current request:
  - non-empty path -> enable (so callers can use a per-request/per-pid
    filename the static INI cannot express);
  - ""             -> force-disable even if the INI set a path;
  - null           -> clear the override, fall back to the INI.

The runtime path is libc strdup/free-owned and reset in RSHUTDOWN so a
setting never leaks into the next request. The zend_error_cb hook now
resolves runtime-over-INI. Verified all three behaviours via real OOMs
and confirmed zero valgrind leaks across set/overwrite/null/strdup
cycles; CI now also exercises the runtime path on every version.

README notes the no-steady-state-overhead property (no executor/allocator
hooks; only a zend_error_cb chain on the error path) and that it
co-exists with other zend_error_cb users like php-memory-profiler.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
rdump_zend_error_cb runs on every diagnostic routed through
zend_error_cb (notices, warnings, deprecations, ...). Wrap the
"this is an actionable E_ERROR" test in UNEXPECTED() and move the
runtime-vs-INI path resolution inside it, so the common path is a single
predicted-not-taken comparison plus the chained call to the original
handler -- no field reads on the hot error path. Mirrors memprof's
EXPECTED-bail should_autodump.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Replace the two-paragraph, bullet-style blurb with a terse factual
description in the style typical of PECL packages (a few present-tense
sentences, no marketing).

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Frames reli analysis as an affordance rather than asserting it as an
automatic step, keeping the capture (extension) / analysis (reli) split
clear.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Drop the "no ptrace" comparison (insider framing) and the PHP version
line (already in <dependencies>); keep just "Linux only" as the one
constraint not encoded elsewhere.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
Collapse the three-sentence blurb into one relative-clause sentence
(extension writes the dump, which reli analyses) plus "Linux only",
matching the tighter feel of the composer.json description.

https://claude.ai/code/session_017XEGHnz2ZakVXTdgLrtC7s
- Create the dump 0600 with O_NOFOLLOW/O_CLOEXEC (and fchmod a pre-existing
  file) so a memory image is never left group/world-readable.
- Expand %p (pid) and %t (unix time) in the OOM auto-dump path so multi-worker
  setups (FPM) write distinct files instead of racing on one path.
- Document the mid-dump crash risk under concurrent munmap/mprotect.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
claude added 28 commits May 29, 2026 03:42
ZEND_INI_GET_ADDR() isn't available on the older (7.x) Zend headers, so the
custom OnUpdateRdumpBytes handler compiled to an implicit declaration and the
.so failed to load with "undefined symbol: ZEND_INI_GET_ADDR". Store
rdump.oom_dump_max_total as a plain string (OnUpdateString) and parse it with
rdump_parse_bytes() at use time instead. It's only read on the cold OOM path,
and OnUpdateString works on every supported version.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
The analyze step piped only stdout and checked grep, so a fatal/exception
(printed to stderr) showed up as a bare "did not produce a report". Capture
stdout+stderr, check the exit code, and dump the full output on failure so the
real cause is visible in the CI log.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- README: the signal-handler example needs pcntl_async_signals(true) or the
  handler never fires on modern PHP.
- rdump_parse_bytes(): clamp to ZEND_LONG_MAX instead of letting the suffix
  multiply overflow long long (or exceed zend_long on a 32-bit build) and wrap
  to a bogus/negative budget.

(The other two suggestions are already in: 0600 + symlink tests in
tests/file_perms.phpt, and the reli dump:inspect/analyze integration in
ci/reli-integration.sh.)

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
"Don't let it run away" was colloquial and vague; match the descriptive style
of the other subsections.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
reli's own getting-started leads with the Docker image (a one-liner sets up a
`reli` shell function), which avoids local PHP 8.4 / FFI / composer setup.
Point there and drop the `php ` prefix so the examples work with the wrapper or
a source install alike.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
Show the docker:print-wrapper eval line inline so analysis is copy-paste
runnable, and point to reli's getting-started for persisting it across shells.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
The wrapper bind-mounts only the current directory, so the examples used a
cwd-relative dump path now, and a note explains that paths outside cwd
(including --dependency-root) need RELI_DOCKER_EXTRA_ARGS='-v /path:/path'.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
inspector:memory:analyze outputs the analyzed graph (JSON by default); the type
breakdown, retention, leaks, cycles, and findings come from the report
formatter (-f report).

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
reli's analysis flow writes the analyzed graph to .rmem (json isn't suited to
analysis); .rmem then feeds rmem:viz / rmem:explore.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
inspector:memory:report reads the already-analyzed .rmem (or sqlite), so the
example now runs analyze once into .rmem and reports from it rather than
re-running analysis on the raw dump.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
Under ZTS (e.g. FrankenPHP) a concurrent thread can unmap a region mid-dump
and crash the process. rdump.safe_read=1 copies regions through
/proc/self/mem with pread, so a concurrent unmap yields zero-filled bytes
instead of a SIGSEGV (it doesn't fix inconsistency, only the crash). Off by
default (direct copy is faster and NTS can't hit the race); recommended under
ZTS. Add a %i (thread id) path token so concurrent OOM dumps from threads
sharing one PID don't collide on a single file. CI smoke now also runs with
safe_read=1.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- Correct the rdump_write_file() comment (no longer "default on for ZTS")
  and the "recommended on under ZTS" wording.
- tests/safe_read.phpt: a dump written with rdump.safe_read=1 (regions via
  /proc/self/mem) is still a well-formed RDUMP.
- CI: assert %i expands into the OOM dump path (oom-<pid>-<tid>.rdump).

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
…lback

- package.xml: ship all four phpt tests (was just dump.phpt) and README.md, so
  PECL installs don't drop the suite.
- README: the signal example used an arrow fn (PHP 7.4+); use a plain closure
  so it runs on the stated 7.0 floor.
- rdump_parse_bytes(): the comment claimed "garbage -> 0" but the parse is
  lenient (trailing junk ignored); describe the actual behaviour.
- OOM path: if a %p/%i template is too long to expand, skip the dump instead of
  falling back to the literal "%p" path, which would make every worker/thread
  clobber one shared file.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
Previously any pread <= 0 was zero-filled, so a signal interruption (EINTR)
would blank readable bytes. Retry the same offset on EINTR; zero-fill only on a
real fault (EFAULT) or EOF, matching how rdump_read_maps() handles read().

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- rdump_write_file(): fold a fclose() failure into rc, so a deferred write
  error (NFS / quota / delayed writeback) surfacing only at the final flush
  fails the dump instead of looking successful (and no OOM .done marker is
  written for it).
- safe_read: on a pread fault, zero just one page and resume past it rather
  than blanking the rest of the 1 MiB block, so a single concurrently-unmapped
  page doesn't lose the readable pages after it.
- oom_dump_max_total: lstat() instead of stat() when summing *.rdump, so a
  symlink in the dump directory can't skew the budget with its target's size.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- Securing perms: only fchmod a pre-existing file (a new one is already 0600),
  and abort the dump if fchmod fails, so secret memory is never written into a
  file left group/world-readable.
- safe_read: retry the bounce buffer at smaller sizes, and if /proc/self/mem
  still can't be set up, fail loudly instead of silently falling back to the
  crash-prone direct copy the user opted out of.
- README: state the supported target is 64-bit little-endian Linux.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- open the dump (and the .done marker) with O_NONBLOCK and require S_ISREG, so
  a FIFO can't hang the OOM death path and devices/sockets are rejected, not
  just symlinks (O_NOFOLLOW).
- Compile-time #error guards for non-Linux, non-64-bit, and big-endian targets,
  matching the documented support surface instead of silently emitting a
  malformed/wrong-width dump.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- file_perms.phpt: a non-regular target (/dev/null) is refused.
- CI: %% expands to a literal percent; rdump.oom_dump_min_interval suppresses a
  second OOM dump within the window (built-in server, two requests).

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
Use ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX / ZEND_ARG_TYPE_INFO (both present
since 7.0) so reflection and IDEs see the real signatures:
  rdump_dump(string $path, bool $full = false): bool
  rdump_set_oom_dump(?string $path, bool $full = false): bool
No stub/gen_stub, which would emit arginfo that won't compile on the 7.0 floor.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
Re-introduce the build-aware default: ZTS builds (where a concurrent thread can
unmap a region mid-dump) enable safe_read by default, NTS builds keep the
faster direct copy. Still overridable via the INI on either build.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
…example

- rdump_write_file(): instead of truncating an existing file in place (which
  leaves the new secret dump readable through any read fd another process
  already holds, and fchmod can't revoke), lstat the path, refuse anything not
  a regular file, then unlink and create a fresh inode with O_EXCL|0600. A
  stale fd then keeps the old content; the new dump lands on a new inode.
  tests/overwrite_inode.phpt locks this in.
- README: the first php.ini example now lists oom_dump_max_total,
  oom_dump_marker, and safe_read (commented), so the important knobs are
  visible up front instead of only later.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
…dump

- rdump_parse_bytes(): use strtod so "1.5G" means ~1.5 GiB instead of being
  misread as 1 byte (which silently disabled the budget). A stray decimal
  point from a locale mismatch is skipped when locating the suffix, so it still
  can't collapse to a tiny budget; clamp huge values to ZEND_LONG_MAX.
- .done marker: create with O_CREAT|O_EXCL|O_NOFOLLOW (the stale one is
  unlinked before the dump) and give up if the path already exists, instead of
  O_TRUNC-ing it -- matching the dump body's caution against truncating a
  regular file/hardlink or following a symlink.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX takes a class_name arg (6 params) on
PHP 7.0/7.1 but 5 on 7.2+, so the 5-arg form broke the 7.0/7.1 build. Use it
only on >= 7.2 and fall back to ZEND_BEGIN_ARG_INFO_EX (no return type) on
older; ZEND_ARG_TYPE_INFO is 4-arg on every version, so the typed arguments
still apply everywhere.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
- rdump_parse_bytes(): replace strtod with a hand-rolled decimal parser so '.'
  is always the decimal point. Under a "," locale strtod would mis-read "1.5G";
  now it's parsed the same everywhere.
- README: the dump replaces an existing regular file with a fresh 0600 inode
  (not fchmod-in-place), and `$full` embeds every readable mapping (read-only
  file-backed segments too, not just code), so the dump is larger.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
MINFO hardcoded "3" while the dump header uses the RDUMP_FORMAT_VERSION macro;
stringize the macro (RDUMP_FORMAT_VERSION_STR) so bumping the version updates
both in one place.

https://claude.ai/code/session_017skw8BsHkF8wur7Pf4G9W5
@sj-i
sj-i merged commit cdf2561 into main May 29, 2026
50 checks passed
@sj-i
sj-i deleted the claude/sharp-bardeen-j35RZ branch May 29, 2026 15:35
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.

2 participants