Subject: bug#79082: [emacs-bug] bug#79082: 30.1; [reproducibility] sysinfo call during profile dump

From: Eli Zaretskii <[email-masked]>
Date: Mon, 28 Jul 2025 17:58:15 +0300

> Cc: [email-masked], Liliana Marie Prikler <[email-masked]>
> Date: Mon, 28 Jul 2025 16:13:08 +0200
> From: "Dr. Werner Fink" <[email-masked]>
> 
> > Hi emacs,
> > 
> > I recently worked a bit on reproducibility which is not guaranteed in
> > particular in the profile dump.  I found that undeterminism during the
> > profile dump comes from two sources : calls to clock_gettime and
> > sysinfo.
> > 
> > clock_gettime is not that hard to patch using faketime, but it's called
> > multiple times so it'll be harder to find where in the codebase.
> > 
> > We don't have dedicated tools to patch sysinfo without compiling an
> > additional file an using the same LD_PRELOAD trick as faketime, but
> > there's actually an OK solution on the lisp side, IMHO (this is what I
> > propose for guix's [email-masked], but it'd be great to add that on the next
> > release: 
> > 
> > (add-after 'unpack 'avoid-sysinfo-call-at-build-time
> >  (lambda _
> >   ;; This is a useful trick for reproducibility: when we configured
> >   ;; with --disable-build-details, (system-name) is nil at build
> >   ;; time on the lisp side.
> >   ;; Find those places with strace -k -e sysinfo.
> >   (substitute* "lisp/jit-lock.el"
> >     (("\\(condition-case nil \\(load-average\\) \\(error\\)\\)"
> >       all)
> >      (format #f "(and (system-name) ~a)" all)))))
> > 
> > It's only a single line change (with maybe a comment addition), please
> > proceed without my feedback if I don't answer, it doesn't deserve a
> > copyright citation.
> 
> After some debugging with a helper function I've identified two
> lisp objects, a Lisp Float which are set by the garbage collector,
> and a Lisp Cons set by the initial lisp timestamp at startup.
> Both are changing at every dump therefore avoid them or use
> a fixed value at dump time.
> 
> During debugging I stumbled over some inconsistencies in
> pdumper.c which are already fixed in upstream master branch.

Thanks.

However, in order for us to accept these patches, they must fulfill
the following conditions:

 . the special code which avoids causing the build to become not
   reproducible must be conditioned on an optional variable, or at
   least on some compile-time symbol (like, for example, something
   derived from --disable-build-details); and
 . the code changes should either be portable, or, if they are
   specific to GNU/Linux, they should be conditioned on an appropriate
   preprocessor macro to avoid compiling it on other platforms

> --- src/pdumper.c
> +++ src/pdumper.c	2025-07-09 07:26:00.889706813 +0000
> @@ -2140,9 +2140,9 @@ dump_interval_node (struct dump_context
>    if (node->parent)
>      dump_field_fixup_later (ctx, &out, node, &node->parent);
>    if (node->left)
> -    dump_field_fixup_later (ctx, &out, node, &node->parent);
> +    dump_field_fixup_later (ctx, &out, node, &node->left);
>    if (node->right)
> -    dump_field_fixup_later (ctx, &out, node, &node->parent);
> +    dump_field_fixup_later (ctx, &out, node, &node->right);

This code looks differently on master.

> @@ -2213,9 +2213,9 @@ dump_finalizer (struct dump_context *ctx
>    /* Do _not_ call dump_pseudovector_lisp_fields here: we dump the
>       only Lisp field, finalizer->function, manually, so we can give it
>       a low weight.  */
> -  dump_field_lv (ctx, &out, finalizer, &finalizer->function, WEIGHT_NONE);
> -  dump_field_finalizer_ref (ctx, &out, finalizer, &finalizer->prev);
> -  dump_field_finalizer_ref (ctx, &out, finalizer, &finalizer->next);
> +  dump_field_lv (ctx, out, finalizer, &finalizer->function, WEIGHT_NONE);
> +  dump_field_finalizer_ref (ctx, out, finalizer, &finalizer->prev);
> +  dump_field_finalizer_ref (ctx, out, finalizer, &finalizer->next);
>    return finish_dump_pvec (ctx, &out->header);

This is already in the code on master.

> --- src/timefns.c
> +++ src/timefns.c	2025-07-11 07:32:33.928031852 +0000
> @@ -600,6 +600,40 @@ make_lisp_time (struct timespec t)
>  Lisp_Object
>  timespec_to_lisp (struct timespec t)
>  {
> +  if (will_dump_p())	/* Use provided epoch at dump to get reproducible pdmp images */
> +    {
> +      char *epoch;
> +      epoch = secure_getenv("SOURCE_DATE_EPOCH");

secure_getenv is specific to glibc.  If we want to use it on other
platforms, we need to import the Gnulib secure_getenv module.

We should also make sure this change doesn't interfere with the build,
since we do have code in Emacs that manipulates time and is used
during the build.