[PATCH i-g-t] lib/igt_core: Free log_buffer entries on exit

Vivekanandan, Balasubramani balasubramani.vivekanandan at intel.com
Mon Nov 18 15:11:05 UTC 2024


On 07.11.2024 12:36, Lucas De Marchi wrote:
> On Thu, Nov 07, 2024 at 06:31:45PM +0100, Kamil Konieczny wrote:
> > Hi Lucas,
> > On 2024-11-06 at 23:02:08 -0800, Lucas De Marchi wrote:
> > > Make sure the buffer entries are released, otherwise valgrind output
> > > becomes each used entry as still reachable on exit (up to 256).
> > > 
> > > Test:
> > > 	# valgrind --leak-check=full  ./build/tests/xe_live_ktest  --r xe_mocs
> > > 
> > > Before:
> > > 	==16082== LEAK SUMMARY:
> > > 	==16082==    definitely lost: 0 bytes in 0 blocks
> > > 	==16082==    indirectly lost: 0 bytes in 0 blocks
> > > 	==16082==      possibly lost: 0 bytes in 0 blocks
> > > 	==16082==    still reachable: 33,473 bytes in 59 blocks
> > > 
> > > After:
> > > 	==15992== LEAK SUMMARY:
> > > 	==15992==    definitely lost: 0 bytes in 0 blocks
> > > 	==15992==    indirectly lost: 0 bytes in 0 blocks
> > > 	==15992==      possibly lost: 0 bytes in 0 blocks
> > > 	==15992==    still reachable: 31,083 bytes in 42 blocks
> > 
> > Thank you for a patch, there are still some blocks left,
> > maybe because that buffer is a ring? e.g. it could have
> > end < start
> 
> no, those blocks are other things.
> 
> Note that we are looping with a uint8_t. When we have start == 250
> and end == 2 (impossible due to how we place log messages, but just for
> illustration), it's expected to free the positions [250, 255], [0, 1].
> 
> Some more context explanation of this change.
> 
> I sent this because the first leaks I was seeing were related to libkmod
> and as the libkmod maintainer I was curious if it was a leak in libkmod
> that was getting masked by us not releasing its ctx. It was not, and
> just releasing the ctx (see
> https://patchwork.freedesktop.org/patch/623301/?series=140746&rev=3)
> made those go away.  While doing that and running other more log-heavy
> tests, I was getting a flood of these 256 blocks, so I fixed that just
> to get them out of the way of what I was really after.
> 
> Looking back to this patch, not sure there's much value as whatever
> binary using it is already on its way out.
> 
> > 
> > > 
> > > Signed-off-by: Lucas De Marchi <lucas.demarchi at intel.com>
> > > ---
> > >  lib/igt_core.c | 15 ++++++++++++---
> > >  1 file changed, 12 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/lib/igt_core.c b/lib/igt_core.c
> > > index 407f7b551..879d1ecd8 100644
> > > --- a/lib/igt_core.c
> > > +++ b/lib/igt_core.c
> > > @@ -469,11 +469,21 @@ static void _igt_log_buffer_append(char *line)
> > >  	pthread_mutex_unlock(&log_buffer_mutex);
> > >  }
> > > 
> > > +static void _igt_log_buffer_reset_locked(void)
> > > +{
> > > +	for (; log_buffer.start != log_buffer.end; log_buffer.start++) {
> > > +		free(log_buffer.entries[log_buffer.start]);
> > > +		log_buffer.entries[log_buffer.start] = NULL;
> > > +	}
> > 
> > Here you should use the same loop as in _dump, so
> > 
> > 	i = log_buffer.start;
> > 	do {
> > 		// do ops here
> > 		i++;
> >        } while (i != log_buffer.start && i != log_buffer.end);
> 
> there's an off-by-one bug in this loop that I didn't want to reproduce
> here: if we never ever logged anything, at the beggining start == end == 0,
> and in the first check i == 1. Which means we will print (or free in
> this case) the 256 entries. Oops.  I think we always log at least the
> test name, so it's not a bug we reproduce.
> 
> > 
> > Or even simpler, init with NULLs and then just free all?
> 
> it's a static variable, it's already zero initialized.
> 
> > For example, if start=2, end=1, there is still a valid pointer
> > at end==1
> 
> no, end marks where the *next* entry will be. At the beginning we have:
> 
> start == end == 0. There are no entries in the ringbuffer.
> 
> After the first logged message:
> start == 0, end == 1.
> 
> If the ringbuffer ever wraps, then we will always have:
> 
> start == x + 1; end == x, or
> start == 0; end == 255

When the ringbuffer wraps to the beginning, the end would now be
pointing to a valid address of a allocated memory. It needs to be freed
while reseting the buffer.
The condition in your for loop 'log_buffer.start != log_buffer.end'
skips freeing the buffer at log_buffer.end.

Regards,
Bala

> 
> Except for when dumping the contents, this ringbuffer doesn't have any
> consumer incrementing the start AFAICS.
> 
> > 
> > Or do one more ops for case 'end < start'
> 
> in the wrap case, see above. The key of the implementation is that we
> are using a uint8_t and relying on it wrapping back to 0, so we can just
> increment the head until it reaches the tail.
> 
> As said above, there isn't much value in this patch other than "let's
> get it right", so consider it optional.
> 
> 
> Lucas De Marchi
> 
> > 
> > Regards,
> > Kamil
> > 
> > > +
> > > +	log_buffer.start = log_buffer.end = 0;
> > > +}
> > > +
> > >  static void _igt_log_buffer_reset(void)
> > >  {
> > >  	pthread_mutex_lock(&log_buffer_mutex);
> > > 
> > > -	log_buffer.start = log_buffer.end = 0;
> > > +	_igt_log_buffer_reset_locked();
> > > 
> > >  	pthread_mutex_unlock(&log_buffer_mutex);
> > >  }
> > > @@ -624,8 +634,7 @@ static void _igt_log_buffer_dump(void)
> > >  		i++;
> > >  	} while (i != log_buffer.start && i != log_buffer.end);
> > > 
> > > -	/* reset the buffer */
> > > -	log_buffer.start = log_buffer.end = 0;
> > > +	_igt_log_buffer_reset_locked();
> > > 
> > >  	_log_line_fprintf(stderr, "****  END  ****\n");
> > >  	pthread_mutex_unlock(&log_buffer_mutex);
> > > --
> > > 2.47.0
> > > 


More information about the igt-dev mailing list