diff mbox series

[9/9] ref-filter: add ref_format_clear() function

Message ID 20240909232118.GI921834@coredump.intra.peff.net (mailing list archive)
State Accepted
Commit db629c61f0be3665a36750fe2353b9ee958b0376
Headers show
Series ref-filter %(trailer) fixes | expand

Commit Message

Jeff King Sept. 9, 2024, 11:21 p.m. UTC
After using the ref-filter API, callers should use ref_filter_clear() to
free any used memory. However, there's not a matching function to clear
the ref_format struct.

Traditionally this did not need to be cleaned up, as it was just a way
for the caller to store and pass format options as a single unit. Even
though the parsing step of some placeholders may allocate data, that's
usually inside their "used_atom" structs, which are part of the
ref_filter itself.

But a few placeholders keep data outside of there. The %(ahead-behind)
and %(is-base) parsers both keep a master list of bases, because they
perform a single filtering pass outside of the use of any particular
atom. And since the format parser does not have access to the ref_filter
struct, they store their cross-atom data in the ref_format struct
itself.

And thus when they are finished, the ref_format also needs to be cleaned
up. So let's add a function to do so, and call it from all of the users
of the ref-filter API.

The %(is-base) case is found by running LSan on t6300. After this patch,
the script can now be marked leak-free.

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin/branch.c        |  1 +
 builtin/for-each-ref.c  |  1 +
 builtin/tag.c           |  1 +
 builtin/verify-tag.c    |  1 +
 ref-filter.c            | 13 +++++++++++++
 ref-filter.h            |  3 +++
 t/t6300-for-each-ref.sh |  1 +
 7 files changed, 21 insertions(+)

Comments

Patrick Steinhardt Sept. 10, 2024, 6:09 a.m. UTC | #1
On Mon, Sep 09, 2024 at 07:21:18PM -0400, Jeff King wrote:
> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
> index e8db612f95..b3163629c5 100755
> --- a/t/t6300-for-each-ref.sh
> +++ b/t/t6300-for-each-ref.sh
> @@ -5,6 +5,7 @@
>  
>  test_description='for-each-ref test'
>  
> +TEST_PASSES_SANITIZE_LEAK=true
>  . ./test-lib.sh
>  GNUPGHOME_NOT_USED=$GNUPGHOME
>  . "$TEST_DIRECTORY"/lib-gpg.sh

Nice! There's also t6302, which has been failing due to all the memory
leaks in our atom handling, as well. After your series there's a single
memory leak left to make it pass. So we may want to add below patch on
top as a low-hanging fruit.

Patrick

-- >8 --

diff --git a/ref-filter.c b/ref-filter.c
index ce1bcfad857..b06e18a569a 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1001,6 +1001,7 @@ struct ref_formatting_stack {
 	struct ref_formatting_stack *prev;
 	struct strbuf output;
 	void (*at_end)(struct ref_formatting_stack **stack);
+	void (*at_end_data_free)(void *data);
 	void *at_end_data;
 };
 
@@ -1169,6 +1170,8 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
 	if (prev)
 		strbuf_addbuf(&prev->output, &current->output);
 	strbuf_release(&current->output);
+	if (current->at_end_data_free)
+		current->at_end_data_free(current->at_end_data);
 	free(current);
 	*stack = prev;
 }
@@ -1228,15 +1231,13 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
 	}
 
 	*stack = cur;
-	free(if_then_else);
 }
 
 static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
 			   struct strbuf *err UNUSED)
 {
 	struct ref_formatting_stack *new_stack;
-	struct if_then_else *if_then_else = xcalloc(1,
-						    sizeof(struct if_then_else));
+	struct if_then_else *if_then_else = xcalloc(1, sizeof(*if_then_else));
 
 	if_then_else->str = atomv->atom->u.if_then_else.str;
 	if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
@@ -1245,6 +1246,7 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state
 	new_stack = state->stack;
 	new_stack->at_end = if_then_else_handler;
 	new_stack->at_end_data = if_then_else;
+	new_stack->at_end_data_free = free;
 	return 0;
 }
 
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 163c378cfd1..7f44d3c3f22 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -2,6 +2,7 @@
 
 test_description='test for-each-refs usage of ref-filter APIs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
Jeff King Sept. 10, 2024, 6:37 a.m. UTC | #2
On Tue, Sep 10, 2024 at 08:09:16AM +0200, Patrick Steinhardt wrote:

> On Mon, Sep 09, 2024 at 07:21:18PM -0400, Jeff King wrote:
> > diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
> > index e8db612f95..b3163629c5 100755
> > --- a/t/t6300-for-each-ref.sh
> > +++ b/t/t6300-for-each-ref.sh
> > @@ -5,6 +5,7 @@
> >  
> >  test_description='for-each-ref test'
> >  
> > +TEST_PASSES_SANITIZE_LEAK=true
> >  . ./test-lib.sh
> >  GNUPGHOME_NOT_USED=$GNUPGHOME
> >  . "$TEST_DIRECTORY"/lib-gpg.sh
> 
> Nice! There's also t6302, which has been failing due to all the memory
> leaks in our atom handling, as well. After your series there's a single
> memory leak left to make it pass. So we may want to add below patch on
> top as a low-hanging fruit.

I was afraid to go looking for other almost-there scripts, knowing what
a rabbit hole it can turn into (which I know you are also familiar
with).

> -- >8 --
> diff --git a/ref-filter.c b/ref-filter.c
> index ce1bcfad857..b06e18a569a 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c

This looks plausibly correct to me, but I'm not at all familiar with the
conditional placeholders. I think it would make more sense for you to
wrap it up with a commit message.

-Peff
diff mbox series

Patch

diff --git a/builtin/branch.c b/builtin/branch.c
index 3f870741bf..c98601c6fe 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -878,6 +878,7 @@  int cmd_branch(int argc, const char **argv, const char *prefix)
 		string_list_clear(&output, 0);
 		ref_sorting_release(sorting);
 		ref_filter_clear(&filter);
+		ref_format_clear(&format);
 		return 0;
 	} else if (edit_description) {
 		const char *branch_name;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 5517a4a1c0..c72fa05bcb 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -104,6 +104,7 @@  int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 	filter_and_format_refs(&filter, flags, sorting, &format);
 
 	ref_filter_clear(&filter);
+	ref_format_clear(&format);
 	ref_sorting_release(sorting);
 	strvec_clear(&vec);
 	return 0;
diff --git a/builtin/tag.c b/builtin/tag.c
index a1fb218512..607e48e311 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -702,6 +702,7 @@  int cmd_tag(int argc, const char **argv, const char *prefix)
 cleanup:
 	ref_sorting_release(sorting);
 	ref_filter_clear(&filter);
+	ref_format_clear(&format);
 	strbuf_release(&buf);
 	strbuf_release(&ref);
 	strbuf_release(&reflog_msg);
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index c731e2f87b..77becf7e75 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -65,5 +65,6 @@  int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 		if (format.format)
 			pretty_print_ref(name, &oid, &format);
 	}
+	ref_format_clear(&format);
 	return had_error;
 }
diff --git a/ref-filter.c b/ref-filter.c
index 0f51095bbd..ce1bcfad85 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -3621,3 +3621,16 @@  void ref_filter_clear(struct ref_filter *filter)
 	free_commit_list(filter->unreachable_from);
 	ref_filter_init(filter);
 }
+
+void ref_format_init(struct ref_format *format)
+{
+	struct ref_format blank = REF_FORMAT_INIT;
+	memcpy(format, &blank, sizeof(blank));
+}
+
+void ref_format_clear(struct ref_format *format)
+{
+	string_list_clear(&format->bases, 0);
+	string_list_clear(&format->is_base_tips, 0);
+	ref_format_init(format);
+}
diff --git a/ref-filter.h b/ref-filter.h
index e794b8a676..754038ab07 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -221,4 +221,7 @@  void filter_is_base(struct repository *r,
 void ref_filter_init(struct ref_filter *filter);
 void ref_filter_clear(struct ref_filter *filter);
 
+void ref_format_init(struct ref_format *format);
+void ref_format_clear(struct ref_format *format);
+
 #endif /*  REF_FILTER_H  */
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index e8db612f95..b3163629c5 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -5,6 +5,7 @@ 
 
 test_description='for-each-ref test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 GNUPGHOME_NOT_USED=$GNUPGHOME
 . "$TEST_DIRECTORY"/lib-gpg.sh