* [rfc] Prompt memory management/cleanups @ 2011-07-20 13:46 Phil Muldoon 2011-07-20 14:30 ` Pedro Alves 2011-07-20 15:05 ` Tom Tromey 0 siblings, 2 replies; 15+ messages in thread From: Phil Muldoon @ 2011-07-20 13:46 UTC (permalink / raw) To: gdb-patches While working on the Python patch to allow prompt substitution in Python, I noticed that in some cases prompts were leaking memory. In several cases, scenarios like: PROMPT (0) = xstrdup(someprompt), did not xfree the prompt first. I decided to use functions calls to replace access to PROMPT/SUFFIX/PREFIX so that memory management can be performed centrally. Attached is a patch. I'd appreciate comments on this; the testsuite shows no regressions, but my knowledge of the prompt area of GDB is about three days old. I'd especially like comments on the acceptance on NULL in the set_prompt/prefix/suffix functions. Pop/push prompt uses different prompt levels and requires that, after use, they are cleaned. It normally just does this with xfree. I'm unsure if my API for that scenario is preferable. Cheers Phil -- 2011-07-20 Phil Muldoon <pmuldoon@redhat.com> * event-top.c (cli_command_loop): Use get_prompt, get_suffix, get_prefix. (display_gdb_prompt): Likewise. (change_annotation_level): Likewise. (push_prompt): Likewise. (pop_prompt): Likewise. (handle_stop_sig): Use get_prompt with a level. * top.c (command_loop): Use get_prompt with a level. (set_async_annotation_level): Use set_prompt with a level. (get_prefix): New function. (set_prefix): Ditto. (set_suffix): Ditto. (get_suffix): Ditto. (get_prompt): Accept a level argument. (set_prompt): Accept a level argument. Account for NULL prompts. Free old prompts. Set new_async_prompt if level is 0. (init_main): Use set_prompt with a level. Do not set new_async_prompt. * top.h: Declare set_suffix, get_suffix, set_prefix, get_prefix. Modify set_prompt, get_prompt to account for levels. * tui/tui-interp.c (tui_command_loop): Use get_prompt with a level. -- diff --git a/gdb/event-top.c b/gdb/event-top.c index 72cbfdc..3546ac0 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -190,17 +190,17 @@ cli_command_loop (void) { int length; char *a_prompt; - char *gdb_prompt = get_prompt (); + char *gdb_prompt = get_prompt (0); /* Tell readline what the prompt to display is and what function it will need to call after a whole line is read. This also displays the first prompt. */ - length = strlen (PREFIX (0)) - + strlen (gdb_prompt) + strlen (SUFFIX (0)) + 1; + length = strlen (get_prefix (0)) + + strlen (gdb_prompt) + strlen (get_suffix(0)) + 1; a_prompt = (char *) alloca (length); - strcpy (a_prompt, PREFIX (0)); + strcpy (a_prompt, get_prefix (0)); strcat (a_prompt, gdb_prompt); - strcat (a_prompt, SUFFIX (0)); + strcat (a_prompt, get_suffix (0)); rl_callback_handler_install (a_prompt, input_handler); } else @@ -258,7 +258,7 @@ void display_gdb_prompt (char *new_prompt) { int prompt_length = 0; - char *gdb_prompt = get_prompt (); + char *gdb_prompt = get_prompt (0); /* Reset the nesting depth used when trace-commands is set. */ reset_command_nest_depth (); @@ -291,19 +291,21 @@ display_gdb_prompt (char *new_prompt) if (!new_prompt) { /* Just use the top of the prompt stack. */ - prompt_length = strlen (PREFIX (0)) + - strlen (SUFFIX (0)) + + prompt_length = strlen (get_prefix (0)) + + strlen (get_suffix (0)) + strlen (gdb_prompt) + 1; new_prompt = (char *) alloca (prompt_length); /* Prefix needs to have new line at end. */ - strcpy (new_prompt, PREFIX (0)); + strcpy (new_prompt, get_prefix (0)); strcat (new_prompt, gdb_prompt); /* Suffix needs to have a new line at end and \032 \032 at beginning. */ - strcat (new_prompt, SUFFIX (0)); + strcat (new_prompt, get_suffix (0)); } if (async_command_editing_p) @@ -333,7 +335,7 @@ change_annotation_level (void) { char *prefix, *suffix; - if (!PREFIX (0) || !PROMPT (0) || !SUFFIX (0)) + if (!get_prefix (0) || !get_prompt (0) || !get_suffix (0)) { /* The prompt stack has not been initialized to "", we are using gdb w/o the --async switch. */ @@ -343,7 +345,7 @@ change_annotation_level (void) if (annotation_level > 1) { - if (!strcmp (PREFIX (0), "") && !strcmp (SUFFIX (0), "")) + if (!strcmp (get_prefix (0), "") && !strcmp (get_suffix (0), "")) { /* Push a new prompt if the previous annotation_level was not >1. */ prefix = (char *) alloca (strlen (async_annotation_suffix) + 10); @@ -361,7 +363,7 @@ change_annotation_level (void) } else { - if (strcmp (PREFIX (0), "") && strcmp (SUFFIX (0), "")) + if (strcmp (get_prefix (0), "") && strcmp (get_suffix (0), "")) { /* Pop the top of the stack, we are going back to annotation < 1. */ pop_prompt (); @@ -377,17 +379,17 @@ void push_prompt (char *prefix, char *prompt, char *suffix) { the_prompts.top++; - PREFIX (0) = xstrdup (prefix); + set_prefix (prefix, 0); /* Note that this function is used by the set annotate 2 command. This is why we take care of saving the old prompt in case a new one is not specified. */ if (prompt) - PROMPT (0) = xstrdup (prompt); + set_prompt (prompt, 0); else - PROMPT (0) = xstrdup (PROMPT (-1)); + set_prompt (get_prompt (-1), 0); - SUFFIX (0) = xstrdup (suffix); + set_suffix (suffix, 0); } /* Pops the top of the prompt stack, and frees the memory allocated @@ -397,20 +399,17 @@ pop_prompt (void) { /* If we are not during a 'synchronous' execution command, in which case, the top prompt would be empty. */ - if (strcmp (PROMPT (0), "")) + if (strcmp (get_prompt (0), "")) /* This is for the case in which the prompt is set while the annotation level is 2. The top prompt will be changed, but when we return to annotation level < 2, we want that new prompt to be in effect, until the user does another 'set prompt'. */ - if (strcmp (PROMPT (0), PROMPT (-1))) - { - xfree (PROMPT (-1)); - PROMPT (-1) = xstrdup (PROMPT (0)); - } - - xfree (PREFIX (0)); - xfree (PROMPT (0)); - xfree (SUFFIX (0)); + if (strcmp (get_prompt (0), get_prompt (-1))) + set_prompt (get_prompt (0), -1); + + set_prefix (NULL, 0); + set_prompt (NULL, 0); + set_suffix (NULL, 0); the_prompts.top--; } @@ -955,7 +954,7 @@ handle_stop_sig (int sig) static void async_stop_sig (gdb_client_data arg) { - char *prompt = get_prompt (); + char *prompt = get_prompt (0); #if STOP_SIGNAL == SIGTSTP signal (SIGTSTP, SIG_DFL); @@ -1033,7 +1032,7 @@ set_async_annotation_level (char *args, int from_tty, void set_async_prompt (char *args, int from_tty, struct cmd_list_element *c) { - PROMPT (0) = xstrdup (new_async_prompt); + set_prompt (new_async_prompt, 0); } /* Set things up for readline to be invoked via the alternate diff --git a/gdb/top.c b/gdb/top.c index ecb91f2..b0ac474 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -533,7 +533,7 @@ command_loop (void) while (instream && !feof (instream)) { if (window_hook && instream == stdin) - (*window_hook) (instream, get_prompt ()); + (*window_hook) (instream, get_prompt (0)); quit_flag = 0; if (instream == stdin && stdin_is_tty) @@ -542,7 +542,7 @@ command_loop (void) /* Get a command-line. This calls the readline package. */ command = command_line_input (instream == stdin ? - get_prompt () : (char *) NULL, + get_prompt (0) : (char *) NULL, instream == stdin, "prompt"); if (command == 0) { @@ -1124,24 +1124,104 @@ and \"show warranty\" for details.\n"); } } \f + + +/* get_prefix: access method for the GDB prefix string. */ + +char * +get_prefix (int level) +{ + return PREFIX (level); +} + +/* set_prefix: set method for the GDB prefix string. */ + +void +set_prefix (const char *s, int level) +{ + /* If S is NULL, just free the PREFIX at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (PREFIX (level)); + PREFIX (level) = NULL; + } + else + /* If S == PREFIX then do not free it or set it. If we did + that, S (which points to PREFIX), would become garbage. */ + if (s != PREFIX (level)) + { + xfree (PREFIX (level)); + PREFIX (level) = xstrdup (s); + } +} + +/* get_suffix: access method for the GDB suffix string. */ + +char * +get_suffix (int level) +{ + return SUFFIX (level); +} + +/* set_suffix: set method for the GDB suffix string. */ + +void +set_suffix (const char *s, int level) +{ + /* If S is NULL, just free the SUFFIX at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (SUFFIX (level)); + SUFFIX (level) = NULL; + } + else + /* If S == SUFFIX then do not free it or set it. If we did + that, S (which points to SUFFIX), would become garbage. */ + if (s != SUFFIX (level)) + { + xfree (SUFFIX (level)); + SUFFIX (level) = xstrdup (s); + } +} + /* get_prompt: access method for the GDB prompt string. */ char * -get_prompt (void) +get_prompt (int level) { - return PROMPT (0); + return PROMPT (level); } void -set_prompt (char *s) +set_prompt (const char *s, int level) { -/* ??rehrauer: I don't know why this fails, since it looks as though - assignments to prompt are wrapped in calls to xstrdup... - if (prompt != NULL) - xfree (prompt); - */ - PROMPT (0) = xstrdup (s); + /* If S is NULL, just free the PROMPT at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (PROMPT (level)); + PROMPT (level) = NULL; + } + else + /* If S == PROMPT then do not free it or set it. If we did + that, S (which points to PROMPT), would become garbage. */ + if (s != PROMPT (level)) + { + xfree (PROMPT (level)); + PROMPT (level) = xstrdup (s); + + if (level == 0) + { + /* Also, free and set new_async_prompt so prompt changes sync up + with set/show prompt. */ + xfree (new_async_prompt); + new_async_prompt = xstrdup (PROMPT (level)); + } + } } + \f struct qt_args @@ -1531,13 +1611,11 @@ init_main (void) whatever the DEFAULT_PROMPT is. */ the_prompts.top = 0; PREFIX (0) = ""; - PROMPT (0) = xstrdup (DEFAULT_PROMPT); + set_prompt (DEFAULT_PROMPT, 0); SUFFIX (0) = ""; /* Set things up for annotation_level > 1, if the user ever decides to use it. */ async_annotation_suffix = "prompt"; - /* Set the variable associated with the setshow prompt command. */ - new_async_prompt = xstrdup (PROMPT (0)); /* If gdb was started with --annotate=2, this is equivalent to the user entering the command 'set annotate 2' at the gdb prompt, so diff --git a/gdb/top.h b/gdb/top.h index 4a6e8fb..bdc29c0 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -51,11 +51,27 @@ extern struct cleanup *prepare_execute_command (void); /* This function returns a pointer to the string that is used by gdb for its command prompt. */ -extern char *get_prompt (void); +extern char *get_prompt (int); /* This function copies the specified string into the string that is used by gdb for its command prompt. */ -extern void set_prompt (char *); +extern void set_prompt (const char *, int); + +/* This function returns a pointer to the string that is used + by gdb for its command prompt prefix. */ +extern char *get_prefix (int); + +/* This function copies the specified string into the string that + is used by gdb for its command prompt prefix. */ +extern void set_prefix (const char *, int); + +/* This function returns a pointer to the string that is used + by gdb for its command prompt suffix. */ +extern char *get_suffix (int); + +/* This function copies the specified string into the string that + is used by gdb for its command prompt suffix. */ +extern void set_suffix (const char *, int); /* From random places. */ extern int readnow_symbol_files; diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c index d79de2b..5ed692a 100644 --- a/gdb/tui/tui-interp.c +++ b/gdb/tui/tui-interp.c @@ -145,7 +145,7 @@ tui_command_loop (void *data) { int length; char *a_prompt; - char *gdb_prompt = get_prompt (); + char *gdb_prompt = get_prompt (0); /* Tell readline what the prompt to display is and what function it will need to call after a whole line is read. This also ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 13:46 [rfc] Prompt memory management/cleanups Phil Muldoon @ 2011-07-20 14:30 ` Pedro Alves 2011-07-20 14:37 ` Phil Muldoon 2011-07-20 15:05 ` Tom Tromey 1 sibling, 1 reply; 15+ messages in thread From: Pedro Alves @ 2011-07-20 14:30 UTC (permalink / raw) To: gdb-patches, pmuldoon On Wednesday 20 July 2011 14:05:11, Phil Muldoon wrote: > +set_prompt (const char *s, int level) > { > -/* ??rehrauer: I don't know why this fails, since it looks as though > - assignments to prompt are wrapped in calls to xstrdup... > - if (prompt != NULL) > - xfree (prompt); > - */ > - PROMPT (0) = xstrdup (s); > + /* If S is NULL, just free the PROMPT at level LEVEL and set to > + NULL. */ > + if (s == NULL) > + { > + xfree (PROMPT (level)); > + PROMPT (level) = NULL; > + } > + else > + /* If S == PROMPT then do not free it or set it. If we did > + that, S (which points to PROMPT), would become garbage. */ > + if (s != PROMPT (level)) > + { This looks strange, and I suppose complicates the callers' life a bit, having to know when are they giving up ownership of the string or not. What would need to change at the callers if we dropped that s != PROMPT() check? -- Pedro Alves ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 14:30 ` Pedro Alves @ 2011-07-20 14:37 ` Phil Muldoon 2011-07-20 15:01 ` Pedro Alves 0 siblings, 1 reply; 15+ messages in thread From: Phil Muldoon @ 2011-07-20 14:37 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches Pedro Alves <pedro@codesourcery.com> writes: > On Wednesday 20 July 2011 14:05:11, Phil Muldoon wrote: >> +set_prompt (const char *s, int level) >> { >> -/* ??rehrauer: I don't know why this fails, since it looks as though >> - assignments to prompt are wrapped in calls to xstrdup... >> - if (prompt != NULL) >> - xfree (prompt); >> - */ >> - PROMPT (0) = xstrdup (s); >> + /* If S is NULL, just free the PROMPT at level LEVEL and set to >> + NULL. */ >> + if (s == NULL) >> + { >> + xfree (PROMPT (level)); >> + PROMPT (level) = NULL; >> + } > > >> + else >> + /* If S == PROMPT then do not free it or set it. If we did >> + that, S (which points to PROMPT), would become garbage. */ >> + if (s != PROMPT (level)) >> + { > > This looks strange, and I suppose complicates the callers' life a bit, > having to know when are they giving up ownership of the string > or not. What would need to change at the callers if we dropped > that s != PROMPT() check? This is a fail-safe for the Python prompt substitution patch. So if they did (the equivalent in Python): s = get_prompt (0) set_prompt (s, 0) Without that check, 'PROMPT (level)' would be freed, but 's' points to that. So you set garbage. get_prompt returns a pointer, not a copy. Internal uses of set_prompt we can just do the checks wherever we want. Basically checking if your pointer to the prompt is in-fact the current prompt. I thought about doing this at the callers, but it just replicated the code everywhere, and in static (IE internal GDB code) that seemed pointless. We could just xstrdup s, then free the prompt anyway, and then set it. But then the s they passed to the function, will still be pointing to xfree'd space; I am not sure if that matters. This seemed the safest way, and not allow prompts to become corrupted. I am completely open to changing it if there is a better way. Cheers, Phil ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 14:37 ` Phil Muldoon @ 2011-07-20 15:01 ` Pedro Alves 2011-07-20 15:06 ` Phil Muldoon 0 siblings, 1 reply; 15+ messages in thread From: Pedro Alves @ 2011-07-20 15:01 UTC (permalink / raw) To: gdb-patches, pmuldoon On Wednesday 20 July 2011 15:30:19, Phil Muldoon wrote: > s = get_prompt (0) > set_prompt (s, 0) > > Without that check, 'PROMPT (level)' would be freed, but 's' points to > that. So you set garbage. get_prompt returns a pointer, not a copy. I'm probably missing something, but isn't it just a matter of instead of having: + xfree (PROMPT (level)); + PROMPT (level) = xstrdup (s); you have: + char *newp = xstrdup (s); + xfree (PROMPT (level)); + PROMPT (level) = newp; ? -- Pedro Alves ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:01 ` Pedro Alves @ 2011-07-20 15:06 ` Phil Muldoon 2011-07-20 15:15 ` Pedro Alves 0 siblings, 1 reply; 15+ messages in thread From: Phil Muldoon @ 2011-07-20 15:06 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches Pedro Alves <pedro@codesourcery.com> writes: > On Wednesday 20 July 2011 15:30:19, Phil Muldoon wrote: >> s = get_prompt (0) >> set_prompt (s, 0) >> >> Without that check, 'PROMPT (level)' would be freed, but 's' points to >> that. So you set garbage. get_prompt returns a pointer, not a copy. > > I'm probably missing something, but isn't it just > a matter of instead of having: > > + xfree (PROMPT (level)); > + PROMPT (level) = xstrdup (s); > > you have: > > + char *newp = xstrdup (s); > + xfree (PROMPT (level)); > + PROMPT (level) = newp; > > ? Yeah I noted we could do that in my reply. Sure we can do that, I'm not opposed to it. But I am not sure on your objection to the check we make first instead of the xstrdup? If PROMPT (level) == s, then there is no need to copy the contents of s into PROMPT, it is already there? The user is effectively asking for a noop? Cheers, Phil ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:06 ` Phil Muldoon @ 2011-07-20 15:15 ` Pedro Alves 2011-07-20 15:45 ` Tom Tromey 0 siblings, 1 reply; 15+ messages in thread From: Pedro Alves @ 2011-07-20 15:15 UTC (permalink / raw) To: pmuldoon; +Cc: gdb-patches On Wednesday 20 July 2011 16:04:16, Phil Muldoon wrote: > Pedro Alves <pedro@codesourcery.com> writes: > > > On Wednesday 20 July 2011 15:30:19, Phil Muldoon wrote: > >> s = get_prompt (0) > >> set_prompt (s, 0) > >> > >> Without that check, 'PROMPT (level)' would be freed, but 's' points to > >> that. So you set garbage. get_prompt returns a pointer, not a copy. > > > > I'm probably missing something, but isn't it just > > a matter of instead of having: > > > > + xfree (PROMPT (level)); > > + PROMPT (level) = xstrdup (s); > > > > you have: > > > > + char *newp = xstrdup (s); > > + xfree (PROMPT (level)); > > + PROMPT (level) = newp; > > > > ? > > Yeah I noted we could do that in my reply. Sure we can do that, I'm not > opposed to it. But I am not sure on your objection to the check we make > first instead of the xstrdup? If PROMPT (level) == s, then there is no > need to copy the contents of s into PROMPT, it is already there? The > user is effectively asking for a noop? You've asked for comments on the API, and IMO this makes for a weird API, because the caller of set_prompt needs to know whether set_prompt will take ownership of the pointer or not depending on where the pointer came from. I haven't looked at the callers -- that's why I asked what would need to change. :-) -- Pedro Alves ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:15 ` Pedro Alves @ 2011-07-20 15:45 ` Tom Tromey 2011-07-20 16:04 ` Pedro Alves 0 siblings, 1 reply; 15+ messages in thread From: Tom Tromey @ 2011-07-20 15:45 UTC (permalink / raw) To: Pedro Alves; +Cc: pmuldoon, gdb-patches >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes: Pedro> You've asked for comments on the API, and IMO this makes for Pedro> a weird API, because the caller of set_prompt needs to know Pedro> whether set_prompt will take ownership of the pointer or not Pedro> depending on where the pointer came from. I haven't looked Pedro> at the callers -- that's why I asked what would need to Pedro> change. :-) The problem case is set_prompt(get_prompt()), but here the prompt code already owns the pointer. I guess it is a little weird, but it still falls under the general rule of "you have to call get_prompt again to get the prompt after set_prompt". Tom ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:45 ` Tom Tromey @ 2011-07-20 16:04 ` Pedro Alves 2011-07-20 16:06 ` Tom Tromey 0 siblings, 1 reply; 15+ messages in thread From: Pedro Alves @ 2011-07-20 16:04 UTC (permalink / raw) To: Tom Tromey; +Cc: pmuldoon, gdb-patches On Wednesday 20 July 2011 16:20:57, Tom Tromey wrote: > >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes: > > Pedro> You've asked for comments on the API, and IMO this makes for > Pedro> a weird API, because the caller of set_prompt needs to know > Pedro> whether set_prompt will take ownership of the pointer or not > Pedro> depending on where the pointer came from. I haven't looked > Pedro> at the callers -- that's why I asked what would need to > Pedro> change. :-) > > The problem case is set_prompt(get_prompt()), but here the prompt code > already owns the pointer. I don't think that would change if you make set_prompt xstrdup before xfree. > I guess it is a little weird, but it still > falls under the general rule of "you have to call get_prompt again to > get the prompt after set_prompt". The current code allows: p = get_prompt(0); set_prompt(p, 0); // p is still valid here whereas: p = get_prompt(0); set_prompt("foo", 0); // p is invalid here But if we already have that rule, then we can treat the former as undefined. I think we should still xstrdup before xfree though as a general rule in these sort of scenarios. Otherwise: p = get_prompt(0); (p's len being > 0) set_prompt(p + 1, 0); will end up with garbage. -- Pedro Alves ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 16:04 ` Pedro Alves @ 2011-07-20 16:06 ` Tom Tromey 0 siblings, 0 replies; 15+ messages in thread From: Tom Tromey @ 2011-07-20 16:06 UTC (permalink / raw) To: Pedro Alves; +Cc: pmuldoon, gdb-patches Pedro> I think we should still xstrdup before xfree though as Pedro> a general rule in these sort of scenarios. Yeah. Tom ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 13:46 [rfc] Prompt memory management/cleanups Phil Muldoon 2011-07-20 14:30 ` Pedro Alves @ 2011-07-20 15:05 ` Tom Tromey 2011-07-20 15:21 ` Phil Muldoon 2011-07-21 17:15 ` Phil Muldoon 1 sibling, 2 replies; 15+ messages in thread From: Tom Tromey @ 2011-07-20 15:05 UTC (permalink / raw) To: pmuldoon; +Cc: gdb-patches >>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes: Phil> While working on the Python patch to allow prompt substitution in Phil> Python, I noticed that in some cases prompts were leaking memory. In Phil> several cases, scenarios like: PROMPT (0) = xstrdup(someprompt), did not Phil> xfree the prompt first. I decided to use functions calls to replace Phil> access to PROMPT/SUFFIX/PREFIX so that memory management can be Phil> performed centrally. Attached is a patch. I'd appreciate comments on Phil> this; the testsuite shows no regressions, but my knowledge of the prompt Phil> area of GDB is about three days old. I'd especially like comments on the Phil> acceptance on NULL in the set_prompt/prefix/suffix functions. Pop/push Phil> prompt uses different prompt levels and requires that, after use, they Phil> are cleaned. It normally just does this with xfree. I'm unsure if my Phil> API for that scenario is preferable. The patch seems fine, but I have a few questions. Does anything ever set the prefix or suffix to a non-empty value? I couldn't find an instance. So how about just removing all this stuff instead? Do we still need the PROMPT, PREFIX, and SUFFIX defines in event-top.h? Tom ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:05 ` Tom Tromey @ 2011-07-20 15:21 ` Phil Muldoon 2011-07-20 15:35 ` Tom Tromey 2011-07-21 17:15 ` Phil Muldoon 1 sibling, 1 reply; 15+ messages in thread From: Phil Muldoon @ 2011-07-20 15:21 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Tom Tromey <tromey@redhat.com> writes: >>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes: > > Phil> While working on the Python patch to allow prompt substitution in > Phil> Python, I noticed that in some cases prompts were leaking memory. In > Phil> several cases, scenarios like: PROMPT (0) = xstrdup(someprompt), did not > Phil> xfree the prompt first. I decided to use functions calls to replace > Phil> access to PROMPT/SUFFIX/PREFIX so that memory management can be > Phil> performed centrally. Attached is a patch. I'd appreciate comments on > Phil> this; the testsuite shows no regressions, but my knowledge of the prompt > Phil> area of GDB is about three days old. I'd especially like comments on the > Phil> acceptance on NULL in the set_prompt/prefix/suffix functions. Pop/push > Phil> prompt uses different prompt levels and requires that, after use, they > Phil> are cleaned. It normally just does this with xfree. I'm unsure if my > Phil> API for that scenario is preferable. > > The patch seems fine, but I have a few questions. > > Does anything ever set the prefix or suffix to a non-empty value? > I couldn't find an instance. So how about just removing all this stuff > instead? The only use I can find is "", which may have meaning to the annotations stuff. This seems unlikely to me too. If we think nobody uses it or is likely to use it in the future, we can declare those elements dead, and just remove them? > Do we still need the PROMPT, PREFIX, and SUFFIX defines in event-top.h? No. They should not be accessible except from top.c and event-top.c anymore. So I will remove them. Cheers, Phil ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:21 ` Phil Muldoon @ 2011-07-20 15:35 ` Tom Tromey 0 siblings, 0 replies; 15+ messages in thread From: Tom Tromey @ 2011-07-20 15:35 UTC (permalink / raw) To: pmuldoon; +Cc: gdb-patches Phil> The only use I can find is "", which may have meaning to the annotations Phil> stuff. This seems unlikely to me too. If we think nobody uses it or is Phil> likely to use it in the future, we can declare those elements dead, and Phil> just remove them? Yes, zap them. Tom ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-20 15:05 ` Tom Tromey 2011-07-20 15:21 ` Phil Muldoon @ 2011-07-21 17:15 ` Phil Muldoon 2011-07-21 20:42 ` Tom Tromey 1 sibling, 1 reply; 15+ messages in thread From: Phil Muldoon @ 2011-07-21 17:15 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Tom Tromey <tromey@redhat.com> writes: >>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes: > > Phil> While working on the Python patch to allow prompt substitution in > Phil> Python, I noticed that in some cases prompts were leaking memory. In > Phil> several cases, scenarios like: PROMPT (0) = xstrdup(someprompt), did not > Phil> xfree the prompt first. I decided to use functions calls to replace > Phil> access to PROMPT/SUFFIX/PREFIX so that memory management can be > Phil> performed centrally. Attached is a patch. I'd appreciate comments on > Phil> this; the testsuite shows no regressions, but my knowledge of the prompt > Phil> area of GDB is about three days old. I'd especially like comments on the > Phil> acceptance on NULL in the set_prompt/prefix/suffix functions. Pop/push > Phil> prompt uses different prompt levels and requires that, after use, they > Phil> are cleaned. It normally just does this with xfree. I'm unsure if my > Phil> API for that scenario is preferable. > > The patch seems fine, but I have a few questions. > > Does anything ever set the prefix or suffix to a non-empty value? > I couldn't find an instance. So how about just removing all this stuff > instead? Well it turns out we do. change_annotation_level inserts valid prefix and suffixes. So I abandoned that rewrite. > Do we still need the PROMPT, PREFIX, and SUFFIX defines in event-top.h? I have moved them to top.c I have also incorporated Pedro's comments in the patch. The ChangeLog remains the same as the initial patch. OK? Cheers, Phil -- diff --git a/gdb/event-top.c b/gdb/event-top.c index 22f9440..37882728 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -191,17 +191,17 @@ cli_command_loop (void) { int length; char *a_prompt; - char *gdb_prompt = get_prompt (); + char *gdb_prompt = get_prompt (0); /* Tell readline what the prompt to display is and what function it will need to call after a whole line is read. This also displays the first prompt. */ - length = strlen (PREFIX (0)) - + strlen (gdb_prompt) + strlen (SUFFIX (0)) + 1; + length = strlen (get_prefix (0)) + + strlen (gdb_prompt) + strlen (get_suffix(0)) + 1; a_prompt = (char *) alloca (length); - strcpy (a_prompt, PREFIX (0)); + strcpy (a_prompt, get_prefix (0)); strcat (a_prompt, gdb_prompt); - strcat (a_prompt, SUFFIX (0)); + strcat (a_prompt, get_suffix (0)); rl_callback_handler_install (a_prompt, input_handler); } else @@ -276,10 +276,10 @@ display_gdb_prompt (char *new_prompt) if (! new_prompt) { char *post_gdb_prompt = NULL; - char *pre_gdb_prompt = xstrdup (get_prompt ()); + char *pre_gdb_prompt = xstrdup (get_prompt (0)); observer_notify_before_prompt (pre_gdb_prompt); - post_gdb_prompt = get_prompt (); + post_gdb_prompt = get_prompt (0); /* If the observer changed the prompt, use that prompt. */ if (strcmp (pre_gdb_prompt, post_gdb_prompt) != 0) @@ -317,18 +317,18 @@ display_gdb_prompt (char *new_prompt) if (! new_prompt) { /* Just use the top of the prompt stack. */ - prompt_length = strlen (PREFIX (0)) + - strlen (SUFFIX (0)) + - strlen (get_prompt()) + 1; + prompt_length = strlen (get_prefix (0)) + + strlen (get_suffix (0)) + + strlen (get_prompt (0)) + 1; actual_gdb_prompt = (char *) alloca (prompt_length); /* Prefix needs to have new line at end. */ - strcpy (actual_gdb_prompt, PREFIX (0)); - strcat (actual_gdb_prompt, get_prompt()); + strcpy (actual_gdb_prompt, get_prefix (0)); + strcat (actual_gdb_prompt, get_prompt (0)); /* Suffix needs to have a new line at end and \032 \032 at beginning. */ - strcat (actual_gdb_prompt, SUFFIX (0)); + strcat (actual_gdb_prompt, get_suffix (0)); } else actual_gdb_prompt = new_prompt;; @@ -361,7 +361,7 @@ change_annotation_level (void) { char *prefix, *suffix; - if (!PREFIX (0) || !PROMPT (0) || !SUFFIX (0)) + if (!get_prefix (0) || !get_prompt (0) || !get_suffix (0)) { /* The prompt stack has not been initialized to "", we are using gdb w/o the --async switch. */ @@ -371,7 +371,7 @@ change_annotation_level (void) if (annotation_level > 1) { - if (!strcmp (PREFIX (0), "") && !strcmp (SUFFIX (0), "")) + if (!strcmp (get_prefix (0), "") && !strcmp (get_suffix (0), "")) { /* Push a new prompt if the previous annotation_level was not >1. */ prefix = (char *) alloca (strlen (async_annotation_suffix) + 10); @@ -389,7 +389,7 @@ change_annotation_level (void) } else { - if (strcmp (PREFIX (0), "") && strcmp (SUFFIX (0), "")) + if (strcmp (get_prefix (0), "") && strcmp (get_suffix (0), "")) { /* Pop the top of the stack, we are going back to annotation < 1. */ pop_prompt (); @@ -405,17 +405,17 @@ void push_prompt (char *prefix, char *prompt, char *suffix) { the_prompts.top++; - PREFIX (0) = xstrdup (prefix); + set_prefix (prefix, 0); /* Note that this function is used by the set annotate 2 command. This is why we take care of saving the old prompt in case a new one is not specified. */ if (prompt) - PROMPT (0) = xstrdup (prompt); + set_prompt (prompt, 0); else - PROMPT (0) = xstrdup (PROMPT (-1)); + set_prompt (get_prompt (-1), 0); - SUFFIX (0) = xstrdup (suffix); + set_suffix (suffix, 0); } /* Pops the top of the prompt stack, and frees the memory allocated @@ -425,20 +425,17 @@ pop_prompt (void) { /* If we are not during a 'synchronous' execution command, in which case, the top prompt would be empty. */ - if (strcmp (PROMPT (0), "")) + if (strcmp (get_prompt (0), "")) /* This is for the case in which the prompt is set while the annotation level is 2. The top prompt will be changed, but when we return to annotation level < 2, we want that new prompt to be in effect, until the user does another 'set prompt'. */ - if (strcmp (PROMPT (0), PROMPT (-1))) - { - xfree (PROMPT (-1)); - PROMPT (-1) = xstrdup (PROMPT (0)); - } - - xfree (PREFIX (0)); - xfree (PROMPT (0)); - xfree (SUFFIX (0)); + if (strcmp (get_prompt (0), get_prompt (-1))) + set_prompt (get_prompt (0), -1); + + set_prefix (NULL, 0); + set_prompt (NULL, 0); + set_suffix (NULL, 0); the_prompts.top--; } @@ -983,7 +980,7 @@ handle_stop_sig (int sig) static void async_stop_sig (gdb_client_data arg) { - char *prompt = get_prompt (); + char *prompt = get_prompt (0); #if STOP_SIGNAL == SIGTSTP signal (SIGTSTP, SIG_DFL); @@ -1061,7 +1058,7 @@ set_async_annotation_level (char *args, int from_tty, void set_async_prompt (char *args, int from_tty, struct cmd_list_element *c) { - PROMPT (0) = xstrdup (new_async_prompt); + set_prompt (new_async_prompt, 0); } /* Set things up for readline to be invoked via the alternate diff --git a/gdb/event-top.h b/gdb/event-top.h index 8550164..952abdb 100644 --- a/gdb/event-top.h +++ b/gdb/event-top.h @@ -69,9 +69,6 @@ struct prompts int top; }; -#define PROMPT(X) the_prompts.prompt_stack[the_prompts.top + X].prompt -#define PREFIX(X) the_prompts.prompt_stack[the_prompts.top + X].prefix -#define SUFFIX(X) the_prompts.prompt_stack[the_prompts.top + X].suffix /* Exported functions from event-top.c. FIXME: these should really go into top.h. */ diff --git a/gdb/python/python.c b/gdb/python/python.c index e3a8d19..16c0a6e 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -745,7 +745,7 @@ before_prompt_hook (const char *current_gdb_prompt) /* If a prompt has been set, PROMPT will not be NULL. If it is NULL, do not set the prompt. */ if (prompt != NULL) - set_prompt (prompt); + set_prompt (prompt, 0); do_cleanups (cleanup); return; diff --git a/gdb/top.c b/gdb/top.c index 3ffd000..8dba0e0 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -65,6 +65,10 @@ #include "ui-out.h" #include "cli-out.h" +#define PROMPT(X) the_prompts.prompt_stack[the_prompts.top + X].prompt +#define PREFIX(X) the_prompts.prompt_stack[the_prompts.top + X].prefix +#define SUFFIX(X) the_prompts.prompt_stack[the_prompts.top + X].suffix + /* Default command line prompt. This is overriden in some configs. */ #ifndef DEFAULT_PROMPT @@ -533,7 +537,7 @@ command_loop (void) while (instream && !feof (instream)) { if (window_hook && instream == stdin) - (*window_hook) (instream, get_prompt ()); + (*window_hook) (instream, get_prompt (0)); quit_flag = 0; if (instream == stdin && stdin_is_tty) @@ -542,7 +546,7 @@ command_loop (void) /* Get a command-line. This calls the readline package. */ command = command_line_input (instream == stdin ? - get_prompt () : (char *) NULL, + get_prompt (0) : (char *) NULL, instream == stdin, "prompt"); if (command == 0) { @@ -1124,26 +1128,98 @@ and \"show warranty\" for details.\n"); } } \f -/* get_prompt: access method for the GDB prompt string. */ + +/* get_prefix: access method for the GDB prefix string. */ char * -get_prompt (void) +get_prefix (int level) { - return PROMPT (0); + return PREFIX (level); } +/* set_prefix: set method for the GDB prefix string. */ + void -set_prompt (const char *s) +set_prefix (const char *s, int level) +{ + /* If S is NULL, just free the PREFIX at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (PREFIX (level)); + PREFIX (level) = NULL; + } + else + { + char *p = xstrdup (s); + + xfree (PREFIX (level)); + PREFIX (level) = p; + } +} + +/* get_suffix: access method for the GDB suffix string. */ + +char * +get_suffix (int level) { - char *p = xstrdup (s); + return SUFFIX (level); +} - xfree (PROMPT (0)); - PROMPT (0) = p; +/* set_suffix: set method for the GDB suffix string. */ - /* Also, free and set new_async_prompt so prompt changes sync up - with set/show prompt. */ - xfree (new_async_prompt); - new_async_prompt = xstrdup (PROMPT (0)); +void +set_suffix (const char *s, int level) +{ + /* If S is NULL, just free the SUFFIX at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (SUFFIX (level)); + SUFFIX (level) = NULL; + } + else + { + char *p = xstrdup (s); + + xfree (SUFFIX (level)); + SUFFIX (level) = p; + } +} + + /* get_prompt: access method for the GDB prompt string. */ + +char * +get_prompt (int level) +{ + return PROMPT (level); +} + +void +set_prompt (const char *s, int level) +{ + /* If S is NULL, just free the PROMPT at level LEVEL and set to + NULL. */ + if (s == NULL) + { + xfree (PROMPT (level)); + PROMPT (level) = NULL; + } + else + { + char *p = xstrdup (s); + + xfree (PROMPT (0)); + PROMPT (0) = p; + + if (level == 0) + { + /* Also, free and set new_async_prompt so prompt changes sync up + with set/show prompt. */ + xfree (new_async_prompt); + new_async_prompt = xstrdup (PROMPT (0)); + } + } } \f @@ -1534,13 +1610,11 @@ init_main (void) whatever the DEFAULT_PROMPT is. */ the_prompts.top = 0; PREFIX (0) = ""; - PROMPT (0) = xstrdup (DEFAULT_PROMPT); + set_prompt (DEFAULT_PROMPT, 0); SUFFIX (0) = ""; /* Set things up for annotation_level > 1, if the user ever decides to use it. */ async_annotation_suffix = "prompt"; - /* Set the variable associated with the setshow prompt command. */ - new_async_prompt = xstrdup (PROMPT (0)); /* If gdb was started with --annotate=2, this is equivalent to the user entering the command 'set annotate 2' at the gdb prompt, so diff --git a/gdb/top.h b/gdb/top.h index 24ec2f2..3c1ec14 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -51,11 +51,27 @@ extern struct cleanup *prepare_execute_command (void); /* This function returns a pointer to the string that is used by gdb for its command prompt. */ -extern char *get_prompt (void); +extern char *get_prompt (int); /* This function copies the specified string into the string that is used by gdb for its command prompt. */ -extern void set_prompt (const char *); +extern void set_prompt (const char *, int level); + +/* This function returns a pointer to the string that is used + by gdb for its command prompt prefix. */ +extern char *get_prefix (int); + +/* This function copies the specified string into the string that + is used by gdb for its command prompt prefix. */ +extern void set_prefix (const char *, int); + +/* This function returns a pointer to the string that is used + by gdb for its command prompt suffix. */ +extern char *get_suffix (int); + +/* This function copies the specified string into the string that + is used by gdb for its command prompt suffix. */ +extern void set_suffix (const char *, int); /* From random places. */ extern int readnow_symbol_files; diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c index d79de2b..919d1ac 100644 --- a/gdb/tui/tui-interp.c +++ b/gdb/tui/tui-interp.c @@ -145,17 +145,17 @@ tui_command_loop (void *data) { int length; char *a_prompt; - char *gdb_prompt = get_prompt (); + char *gdb_prompt = get_prompt (0); /* Tell readline what the prompt to display is and what function it will need to call after a whole line is read. This also displays the first prompt. */ - length = strlen (PREFIX (0)) - + strlen (gdb_prompt) + strlen (SUFFIX (0)) + 1; + length = strlen (get_prefix (0)) + + strlen (gdb_prompt) + strlen (get_suffix (0)) + 1; a_prompt = (char *) alloca (length); - strcpy (a_prompt, PREFIX (0)); + strcpy (a_prompt, get_prefix (0)); strcat (a_prompt, gdb_prompt); - strcat (a_prompt, SUFFIX (0)); + strcat (a_prompt, get_suffix (0)); rl_callback_handler_install (a_prompt, input_handler); } else ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-21 17:15 ` Phil Muldoon @ 2011-07-21 20:42 ` Tom Tromey 2011-07-22 13:26 ` Phil Muldoon 0 siblings, 1 reply; 15+ messages in thread From: Tom Tromey @ 2011-07-21 20:42 UTC (permalink / raw) To: pmuldoon; +Cc: gdb-patches >>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes: Phil> I have moved them to top.c Phil> I have also incorporated Pedro's comments in the patch. Phil> The ChangeLog remains the same as the initial patch. Phil> OK? Ok, thanks. Tom ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [rfc] Prompt memory management/cleanups 2011-07-21 20:42 ` Tom Tromey @ 2011-07-22 13:26 ` Phil Muldoon 0 siblings, 0 replies; 15+ messages in thread From: Phil Muldoon @ 2011-07-22 13:26 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Tom Tromey <tromey@redhat.com> writes: >>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes: > > Phil> I have moved them to top.c > Phil> I have also incorporated Pedro's comments in the patch. > > Phil> The ChangeLog remains the same as the initial patch. > > Phil> OK? > > Ok, thanks. > > Tom Thanks, so committed. Cheers, Phil ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2011-07-22 10:01 UTC | newest] Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2011-07-20 13:46 [rfc] Prompt memory management/cleanups Phil Muldoon 2011-07-20 14:30 ` Pedro Alves 2011-07-20 14:37 ` Phil Muldoon 2011-07-20 15:01 ` Pedro Alves 2011-07-20 15:06 ` Phil Muldoon 2011-07-20 15:15 ` Pedro Alves 2011-07-20 15:45 ` Tom Tromey 2011-07-20 16:04 ` Pedro Alves 2011-07-20 16:06 ` Tom Tromey 2011-07-20 15:05 ` Tom Tromey 2011-07-20 15:21 ` Phil Muldoon 2011-07-20 15:35 ` Tom Tromey 2011-07-21 17:15 ` Phil Muldoon 2011-07-21 20:42 ` Tom Tromey 2011-07-22 13:26 ` Phil Muldoon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox