]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: shortcut 'list LINENO'
authorAndrew Burgess <aburgess@redhat.com>
Sat, 15 Nov 2025 14:55:19 +0000 (14:55 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 16 Dec 2025 15:29:11 +0000 (15:29 +0000)
While working on a test for a weird interaction between the DWARF
parser, debuginfod, and the 'list' command, I noticed that performing
'list LINENO' can incur a significant amount of work trying to figure
out which symtab the source should be listed from.  This seems a
little weird as a plain 'list' just uses the default symtab with no
searching through all of the symtabs.

The symtab lookup is all hidden behind the decode_line_1 call, which
is made from list_command (cli/cli-cmds.c).

The thing is, in list_command we already have code which (basically)
checks if the argument to 'list' is a line number, here's the code:

  for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
  linenum_beg = (p == arg1);

And we already have code within list_command which depends on the
default symtab, look at how 'list .', 'list +', and 'list -' are
handled.

I think that 'list LINENO' is such a common use case that is makes
sense to optimise this case in order to avoid the need to perform
symtab lookup.  I think this can be achieved without any significant
changes to the list_command function; we'll just move the existing
line number check (see above code) a little earlier in the function
and change it to a strtol call so that the actual line number is
recorded.  Then there's a little error checking, before finally we can
skip straight to listing the source code using the default symtab.

For anything other than 'list LINENO' we will handle the command just
as we've always done.

I think there's actually scope for list_command to handle more cases
internally, without calling out to decode_line_1, but I thought I'd
see how happy people were with this smaller change first before I
tried anything larger.

There should be no user visible changes after this commit, other than
'list LINENO' might be a little faster.

Reviewed-By: Keith Seitz <keiths@redhat.com>
gdb/cli/cli-cmds.c

index 8b8f928e1a2707d2e7ef914604f4a7edd65e0c0c..32bcf962e4c212237571ddcd049682a8fd0295ae 100644 (file)
@@ -1356,12 +1356,49 @@ list_command (const char *arg, int from_tty)
   std::vector<symtab_and_line> sals;
   symtab_and_line sal, sal_end;
   bool dummy_beg = false;
+  bool linenum_beg = false;
 
-  const char *arg1 = arg;
+  const char *arg1 = skip_spaces (arg);
   if (*arg1 == ',')
     dummy_beg = true;
   else
     {
+      /* Are we looking at a number?  */
+      char *end_ptr;
+      long int lineno = strtol (arg1, &end_ptr, 10);
+
+      /* If END_PTR is different to ARG1 then strtol parsed something, but
+        strtol will accept numbers with a '+' or '-' prefix, which we
+        don't want to handle here, hence the c_isdigit check.  */
+      if (end_ptr != arg1 && c_isdigit (*arg1))
+       {
+         /* Some digits were found.  */
+         end_ptr = skip_spaces (end_ptr);
+
+         /* Check for valid line format or for an invalid line number.  */
+         if (*end_ptr != '\0' && *end_ptr != ',')
+           error (_("Junk at end of line specification: %s"), end_ptr);
+
+         if (*end_ptr == '\0')
+           {
+             /* Ensure LINENO isn't going to overflow when we convert it
+                to an integer below.  */
+             if (lineno >= INT_MAX || lineno <= INT_MIN)
+               error (_("Line number %.*s out of range"),
+                      (int) (end_ptr - arg1), arg1);
+
+             /* We only have a line number.  */
+             set_default_source_symtab_and_line ();
+             symtab_and_line cursal
+               = get_current_source_symtab_and_line (current_program_space);
+             cursal.line = static_cast<int> (lineno);
+             list_around_line (nullptr, cursal);
+             return;
+           }
+
+         linenum_beg = true;
+       }
+
       location_spec_up locspec
        = string_to_location_spec (&arg1, current_language);
 
@@ -1385,12 +1422,6 @@ list_command (const char *arg, int from_tty)
       sal = sals[0];
     }
 
-  /* Record whether the BEG arg is all digits.  */
-
-  const char *p;
-  for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
-  bool linenum_beg = (p == arg1);
-
   /* Save the range of the first argument, in case we need to let the
      user know it was ambiguous.  */
   const char *beg = arg;
@@ -1491,7 +1522,7 @@ list_command (const char *arg, int from_tty)
      imply a symtab, it must be an undebuggable symbol which means no
      source code.  */
 
-  if (!linenum_beg && sal.symtab == nullptr)
+  if (!dummy_beg && !linenum_beg && sal.symtab == nullptr)
     error (_("No line number known for %s."), arg);
 
   /* If this command is repeated with RET,