Subject: Re: bug#72165: 31.0.50; Intermittent crashing with recent emacs build

From: Dima Kogan <[email-masked]>
Date: Mon, 29 Jul 2024 05:49:54 -0700

> IOW, it uses 'oldpoint', which is a marker, not a simple number.  It
> was initialized like this:
>
>       oldpoint = message_dolog_marker1;
>       set_marker_restricted_both (oldpoint, Qnil, PT, PT_BYTE);
>
> Since 'oldpoint' is a marker, it should have been moved by
> del_range_both so that it still points to the same text.  Moreover,
> the char <-> byte correspondence was not supposed to be disrupted by
> that.

That is excellent to know! I'm not super familiar with the internals of
emacs, so I don't know what is supposed to be handled, and might have
jumped to the conclusion.

Let's look again. I'm at a different cycle from before, but it still
fails from a message-log-max truncation.

- I'm at the top of redisplay_window() that has a crashing state, right
  after a redisplay_window() cycle with a non-crashing state. The
  crashing state has z_byte-z = 8 but pt_byte-pt = 12. Both are at the
  end of the buffer, and 8 is the correct value.

- I run backwards until the truncation in message_dolog():

	  if (FIXNATP (Vmessage_log_max))
	    {
	      scan_newline (Z, Z_BYTE, BEG, BEG_BYTE,
			    -XFIXNAT (Vmessage_log_max) - 1, false);
	      del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, false);
	    }

  Right after this both z_byte-z=8 (correct) and the point is at the
  beginning. This is correct, and the truncation itself didn't break
  anything. Note: this was (eventually) called from redisplay_window(),
  in the display_mode_lines() call in xdisp.c:20941

- Then we restore the oldpoint; what I suggested was the problem
  previously:

      if (point_at_end)
	TEMP_SET_PT_BOTH (Z, Z_BYTE);
      else
	/* We can't do Fgoto_char (oldpoint) because it will run some
           Lisp code.  */
	TEMP_SET_PT_BOTH (marker_position (oldpoint),
			  marker_byte_position (oldpoint));

  After that, both z_byte-z and pt_byte-pt are 8. This is correct.

- Then we do this, in redisplay_window() in xdisp.c:21908

  /* Restore current_buffer and value of point in it.  The window
     update may have changed the buffer, so first make sure `opoint'
     is still valid (Bug#6177).  */
  if (CHARPOS (opoint) < BEGV)
    TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE);
  else if (CHARPOS (opoint) > ZV)
    TEMP_SET_PT_BOTH (Z, Z_BYTE);
  else
    TEMP_SET_PT_BOTH (CHARPOS (opoint), BYTEPOS (opoint));

  We pick the "else" branch. The end result is the exact failing state
  we hit on the next pass through redisplay_window().

This sounds like a different flavor of what I described yesterday. So in
summary: in a redisplay_window() call:

- We set the opoint on line 20064. This is a text_pos, NOT a marker. So
  it wouldn't be updated due to the buffer changing.

- On line 20941 we call display_mode_lines() which eventually deletes
  lines at the start of *Messages* and moves the point.

- On line 21908 we restore the opoint, saved prior to the lines being
  deleted, and no longer valid

Does this sequence sound more reasonable to you? The description from
the previous email made sense to me yesterday, but maybe I was too
sleep-deprived to tell for certain. Looking through yesterday's gdb
session, I don't see the smoking gun anymore.

Thanks much!