Remove excess calls to gdb_flush

Message ID 20190219205426.4168-1-tromey@adacore.com
State New
Headers show
Series
  • Remove excess calls to gdb_flush
Related show

Commit Message

Tom Tromey Feb. 19, 2019, 8:54 p.m.
A customer noticed some mildly odd MI output, where CLI output was
split into multiple MI strings at unusual boundaries, like this:

    ~"$1 = (b => true"
    ~", p => 0x407260"

This is technically correct according to the MI spec, but still
unusual, in that there's no particular reason for the string to be
split where it is.

I tracked this down to a call to gdb_flush in generic_val_print.
Then, I went through all calls to gdb_flush and removed the ones I
thought were superfluous.  In particular:

* Any call in the value-printing code;
* Likewise the type-printing code (just a single call); and
* Any call that immediately followed a printf that obviously
  ended with a newline, my belief being that gdb's standard output
  streams are line buffered (by inheriting the behavior from stdio)

Regression tested on x86-64 Fedora 29.

I didn't add a new test case.  I tend to think we don't necessarily
want to specify this behavior in the tests.  Let me know what you
think of this.

gdb/ChangeLog
2019-02-19  Tom Tromey  <tromey@adacore.com>

	* windows-nat.c (windows_nat_target::attach)
	(windows_nat_target::detach): Don't call gdb_flush.
	* valprint.c (generic_val_print, val_print, val_print_string):
	Don't call gdb_flush.
	* utils.c (defaulted_query): Don't call gdb_flush.
	* typeprint.c (print_type_scalar): Don't call gdb_flush.
	* target.c (target_announce_detach): Don't call gdb_flush.
	* sparc64-tdep.c (adi_print_versions): Don't call gdb_flush.
	* remote.c (extended_remote_target::attach): Don't call
	gdb_flush.
	* procfs.c (procfs_target::detach): Don't call gdb_flush.
	* printcmd.c (do_examine): Don't call gdb_flush.
	(info_display_command): Don't call gdb_flush.
	* p-valprint.c (pascal_val_print): Don't call gdb_flush.
	* nto-procfs.c (nto_procfs_target::attach): Don't call gdb_flush.
	* memattr.c (info_mem_command): Don't call gdb_flush.
	* mdebugread.c (mdebug_build_psymtabs): Don't call gdb_flush.
	* m2-valprint.c (m2_val_print): Don't call gdb_flush.
	* infrun.c (follow_exec, handle_command): Don't call gdb_flush.
	* inf-ptrace.c (inf_ptrace_target::attach): Don't call gdb_flush.
	* hppa-tdep.c (unwind_command): Don't call gdb_flush.
	* gnu-nat.c (gnu_nat_target::attach): Don't call gdb_flush.
	(gnu_nat_target::detach): Don't call gdb_flush.
	* f-valprint.c (f_val_print): Don't call gdb_flush.
	* darwin-nat.c (darwin_nat_target::attach): Don't call gdb_flush.
	* cli/cli-script.c (read_command_lines): Don't call gdb_flush.
	* cli/cli-cmds.c (shell_escape, print_disassembly): Don't call
	gdb_flush.
	* c-valprint.c (c_val_print): Don't call gdb_flush.
	* ada-valprint.c (ada_print_scalar): Don't call gdb_flush.
---
 gdb/ChangeLog        | 33 +++++++++++++++++++++++++++++++++
 gdb/ada-valprint.c   |  1 -
 gdb/c-valprint.c     |  1 -
 gdb/cli/cli-cmds.c   | 14 +++-----------
 gdb/cli/cli-script.c |  5 +----
 gdb/darwin-nat.c     |  2 --
 gdb/f-valprint.c     |  1 -
 gdb/gnu-nat.c        |  3 ---
 gdb/hppa-tdep.c      |  2 --
 gdb/inf-ptrace.c     |  2 --
 gdb/infrun.c         |  7 +------
 gdb/m2-valprint.c    |  2 --
 gdb/mdebugread.c     |  1 -
 gdb/memattr.c        |  2 --
 gdb/nto-procfs.c     |  2 --
 gdb/p-valprint.c     |  2 --
 gdb/printcmd.c       |  2 --
 gdb/procfs.c         |  1 -
 gdb/remote.c         |  2 --
 gdb/sparc64-tdep.c   |  1 -
 gdb/target.c         |  1 -
 gdb/typeprint.c      |  1 -
 gdb/utils.c          |  1 -
 gdb/valprint.c       |  4 ----
 gdb/windows-nat.c    |  3 ---
 25 files changed, 38 insertions(+), 58 deletions(-)

-- 
2.20.1

Comments

Kevin Buettner Feb. 19, 2019, 10:12 p.m. | #1
On Tue, 19 Feb 2019 13:54:26 -0700
Tom Tromey <tromey@adacore.com> wrote:

> A customer noticed some mildly odd MI output, where CLI output was

> split into multiple MI strings at unusual boundaries, like this:

> 

>     ~"$1 = (b => true"

>     ~", p => 0x407260"

> 

> This is technically correct according to the MI spec, but still

> unusual, in that there's no particular reason for the string to be

> split where it is.

> 

> I tracked this down to a call to gdb_flush in generic_val_print.

> Then, I went through all calls to gdb_flush and removed the ones I

> thought were superfluous.  In particular:

> 

> * Any call in the value-printing code;

> * Likewise the type-printing code (just a single call); and

> * Any call that immediately followed a printf that obviously

>   ended with a newline, my belief being that gdb's standard output

>   streams are line buffered (by inheriting the behavior from stdio)

> 

> Regression tested on x86-64 Fedora 29.

> 

> I didn't add a new test case.  I tend to think we don't necessarily

> want to specify this behavior in the tests.  Let me know what you

> think of this.


I agree with you regarding the test case.

I looked over the patch and didn't see any problems.

Kevin
Tom Tromey March 5, 2019, 3:56 p.m. | #2
>>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:


>> I tracked this down to a call to gdb_flush in generic_val_print.

>> Then, I went through all calls to gdb_flush and removed the ones I

>> thought were superfluous.  In particular:

[...]

Kevin> I looked over the patch and didn't see any problems.

Thanks, I'm checking this in now.

Tom
Pedro Alves March 5, 2019, 5:06 p.m. | #3
On 02/19/2019 08:54 PM, Tom Tromey wrote:
> 

> my belief being that gdb's standard output

>   streams are line buffered (by inheriting the behavior from stdio)


That's only true if gdb is connected to a pty.  If gdb is started with
a pipe or regular file for stdout/stderr instead, then it won't be.  I
guess we could fix that with a setvbuf call at startup though.

Thanks,
Pedro Alves
Tom Tromey March 5, 2019, 5:54 p.m. | #4
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:


Pedro> On 02/19/2019 08:54 PM, Tom Tromey wrote:
>> 

>> my belief being that gdb's standard output

>> streams are line buffered (by inheriting the behavior from stdio)


Pedro> That's only true if gdb is connected to a pty.  If gdb is started with
Pedro> a pipe or regular file for stdout/stderr instead, then it won't be.  I
Pedro> guess we could fix that with a setvbuf call at startup though.

I'll take a look.

Tom
Joel Brobecker March 7, 2019, 6:06 a.m. | #5
> >>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

> 

> Pedro> On 02/19/2019 08:54 PM, Tom Tromey wrote:

> >> 

> >> my belief being that gdb's standard output

> >> streams are line buffered (by inheriting the behavior from stdio)

> 

> Pedro> That's only true if gdb is connected to a pty.  If gdb is started with

> Pedro> a pipe or regular file for stdout/stderr instead, then it won't be.  I

> Pedro> guess we could fix that with a setvbuf call at startup though.

> 

> I'll take a look.


I think the scenario being discussed here is slightly different
from what the diff below is for, but I thought I'd mention it,
just in case. At AdaCore, we have the following local change:

--- a/gdb/main.c
+++ b/gdb/main.c
@@ -527,6 +527,17 @@ captured_main_1 (struct captured_main_args *context)
     error (_("fatal error: libbfd ABI mismatch"));

 #ifdef __MINGW32__
+  /* On Windows hosts, when built using MinGW, we have some problems
+     with the terminal when running the debugger either under a cygwin
+     environment, or when connected to a GUI: When the debugger prints
+     an error, the actual printing on screen of the error message is
+     delayed and only finally printed after the next GDB prompt.
+     We avoid this issue by turning off buffering of stdout and stderr.  */
+  setvbuf (stdout, NULL, _IONBF, 0);
+  setvbuf (stderr, NULL, _IONBF, 0);
+#endif
+
+#ifdef __MINGW32__
   /* On Windows, argv[0] is not necessarily set to absolute form when
      GDB is found along PATH, without which relocation doesn't work.  */
   gdb_program_name = windows_get_absolute_argv0 (argv[0]);


-- 
Joel

Patch

diff --git a/gdb/ada-valprint.c b/gdb/ada-valprint.c
index f2aed32cc86..d4661e1f1d4 100644
--- a/gdb/ada-valprint.c
+++ b/gdb/ada-valprint.c
@@ -465,7 +465,6 @@  ada_print_scalar (struct type *type, LONGEST val, struct ui_file *stream)
     default:
       error (_("Invalid type code in symbol table."));
     }
-  gdb_flush (stream);
 }
 
 /* Print the character string STRING, printing at most LENGTH characters.
diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
index 7a1de70f6a1..d5ddd3ece7a 100644
--- a/gdb/c-valprint.c
+++ b/gdb/c-valprint.c
@@ -564,7 +564,6 @@  c_val_print (struct type *type,
 			 &c_decorations);
       break;
     }
-  gdb_flush (stream);
 }
 
 void
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index fa99503b0ca..5dc94a5361a 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -709,16 +709,10 @@  shell_escape (const char *arg, int from_tty)
     arg = "inferior shell";
 
   if (rc == -1)
-    {
-      fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", arg,
-			  safe_strerror (errno));
-      gdb_flush (gdb_stderr);
-    }
+    fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", arg,
+			safe_strerror (errno));
   else if (rc)
-    {
-      fprintf_unfiltered (gdb_stderr, "%s exited with status %d\n", arg, rc);
-      gdb_flush (gdb_stderr);
-    }
+    fprintf_unfiltered (gdb_stderr, "%s exited with status %d\n", arg, rc);
 #ifdef GLOBAL_CURDIR
   /* Make sure to return to the directory GDB thinks it is, in case
      the shell command we just ran changed it.  */
@@ -743,7 +737,6 @@  shell_escape (const char *arg, int from_tty)
 
       fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", user_shell,
 			  safe_strerror (errno));
-      gdb_flush (gdb_stderr);
       _exit (0177);
     }
 
@@ -1130,7 +1123,6 @@  print_disassembly (struct gdbarch *gdbarch, const char *name,
 	    }
 	}
       printf_filtered ("End of assembler dump.\n");
-      gdb_flush (gdb_stdout);
     }
 #if defined(TUI)
   else
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index aaead00a101..9783b9c3321 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1186,10 +1186,7 @@  read_command_lines (const char *prompt_arg, int from_tty, int parse_commands,
 					     END_MESSAGE);
 	}
       else
-	{
-	  printf_unfiltered ("%s\n%s\n", prompt_arg, END_MESSAGE);
-	  gdb_flush (gdb_stdout);
-	}
+	printf_unfiltered ("%s\n%s\n", prompt_arg, END_MESSAGE);
     }
 
 
diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c
index 8c34aa8a3f2..0b7cfb69dc8 100644
--- a/gdb/darwin-nat.c
+++ b/gdb/darwin-nat.c
@@ -2056,8 +2056,6 @@  darwin_nat_target::attach (const char *args, int from_tty)
       else
 	printf_unfiltered (_("Attaching to %s\n"),
 			   target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
     }
 
   if (pid == 0 || ::kill (pid, 0) < 0)
diff --git a/gdb/f-valprint.c b/gdb/f-valprint.c
index d9fe26f1d6a..eee0a62fe88 100644
--- a/gdb/f-valprint.c
+++ b/gdb/f-valprint.c
@@ -366,7 +366,6 @@  f_val_print (struct type *type, int embedded_offset,
 			 &f_decorations);
       break;
     }
-  gdb_flush (stream);
 }
 
 static void
diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c
index 53f23068a40..7a0c726c8e9 100644
--- a/gdb/gnu-nat.c
+++ b/gdb/gnu-nat.c
@@ -2217,8 +2217,6 @@  gnu_nat_target::attach (const char *args, int from_tty)
 			   exec_file, pid);
       else
 	printf_unfiltered ("Attaching to pid %d\n", pid);
-
-      gdb_flush (gdb_stdout);
     }
 
   inf_debug (inf, "attaching to pid: %d", pid);
@@ -2273,7 +2271,6 @@  gnu_nat_target::detach (inferior *inf, int from_tty)
 			   exec_file, gnu_current_inf->pid);
       else
 	printf_unfiltered ("Detaching from pid %d\n", gnu_current_inf->pid);
-      gdb_flush (gdb_stdout);
     }
 
   pid = gnu_current_inf->pid;
diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c
index 866e6cf6dea..9748405bd54 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2588,10 +2588,8 @@  unwind_command (const char *exp, int from_tty)
   printf_unfiltered ("unwind_table_entry (%s):\n", host_address_to_string (u));
 
   printf_unfiltered ("\tregion_start = %s\n", hex_string (u->region_start));
-  gdb_flush (gdb_stdout);
 
   printf_unfiltered ("\tregion_end = %s\n", hex_string (u->region_end));
-  gdb_flush (gdb_stdout);
 
 #define pif(FLD) if (u->FLD) printf_unfiltered (" "#FLD);
 
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 0956094c6a4..d4b9a3a6469 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -217,8 +217,6 @@  inf_ptrace_target::attach (const char *args, int from_tty)
       else
 	printf_unfiltered (_("Attaching to %s\n"),
 			   target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
     }
 
 #ifdef PT_ATTACH
diff --git a/gdb/infrun.c b/gdb/infrun.c
index b32635fc422..6d1d04ee695 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1159,8 +1159,6 @@  follow_exec (ptid_t ptid, char *exec_file_target)
   /* We've followed the inferior through an exec.  Therefore, the
      inferior has essentially been killed & reborn.  */
 
-  gdb_flush (gdb_stdout);
-
   breakpoint_init_inferior (inf_execd);
 
   gdb::unique_xmalloc_ptr<char> exec_file_host
@@ -8406,10 +8404,7 @@  Are you sure you want to change it? "),
 		      sigs[signum] = 1;
 		    }
 		  else
-		    {
-		      printf_unfiltered (_("Not confirmed, unchanged.\n"));
-		      gdb_flush (gdb_stdout);
-		    }
+		    printf_unfiltered (_("Not confirmed, unchanged.\n"));
 		}
 	      break;
 	    case GDB_SIGNAL_0:
diff --git a/gdb/m2-valprint.c b/gdb/m2-valprint.c
index b011d65aed5..74f89ee45fb 100644
--- a/gdb/m2-valprint.c
+++ b/gdb/m2-valprint.c
@@ -408,7 +408,6 @@  m2_val_print (struct type *type, int embedded_offset,
       if (TYPE_STUB (elttype))
 	{
 	  fprintf_filtered (stream, _("<incomplete type>"));
-	  gdb_flush (stream);
 	  break;
 	}
       else
@@ -499,5 +498,4 @@  m2_val_print (struct type *type, int embedded_offset,
 			 &m2_decorations);
       break;
     }
-  gdb_flush (stream);
 }
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index accf07c2780..35e7890f357 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -377,7 +377,6 @@  mdebug_build_psymtabs (minimal_symbol_reader &reader,
 			   objfile->name);
       printf_unfiltered (_("You should compile with -g2 or "
 			   "-g3 for best debugging support.\n"));
-      gdb_flush (gdb_stdout);
     }
 #endif
 }
diff --git a/gdb/memattr.c b/gdb/memattr.c
index 0769a80a067..858a41abfb4 100644
--- a/gdb/memattr.c
+++ b/gdb/memattr.c
@@ -459,8 +459,6 @@  info_mem_command (const char *args, int from_tty)
 #endif
 
       printf_filtered ("\n");
-
-      gdb_flush (gdb_stdout);
     }
 }
 
diff --git a/gdb/nto-procfs.c b/gdb/nto-procfs.c
index 40959d7715d..8108c0e3888 100644
--- a/gdb/nto-procfs.c
+++ b/gdb/nto-procfs.c
@@ -712,8 +712,6 @@  nto_procfs_target::attach (const char *args, int from_tty)
       else
 	printf_unfiltered ("Attaching to %s\n",
 			   target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
     }
   inferior_ptid = do_attach (ptid_t (pid));
   inf = current_inferior ();
diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c
index 713a2889493..0e816977181 100644
--- a/gdb/p-valprint.c
+++ b/gdb/p-valprint.c
@@ -349,7 +349,6 @@  pascal_val_print (struct type *type,
       if (TYPE_STUB (elttype))
 	{
 	  fprintf_filtered (stream, "<incomplete type>");
-	  gdb_flush (stream);
 	  break;
 	}
       else
@@ -418,7 +417,6 @@  pascal_val_print (struct type *type,
       error (_("Invalid pascal type code %d in symbol table."),
 	     TYPE_CODE (type));
     }
-  gdb_flush (stream);
 }
 
 void
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 03763d577ab..c442bb4e07e 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -1098,7 +1098,6 @@  do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
 	    count += branch_delay_insns;
 	}
       printf_filtered ("\n");
-      gdb_flush (gdb_stdout);
     }
 
   if (need_to_update_next_address)
@@ -2068,7 +2067,6 @@  Num Enb Expression\n"));
       if (d->block && !contained_in (get_selected_block (0), d->block))
 	printf_filtered (_(" (cannot be evaluated in the current context)"));
       printf_filtered ("\n");
-      gdb_flush (gdb_stdout);
     }
 }
 
diff --git a/gdb/procfs.c b/gdb/procfs.c
index b790f112b26..0b990506a4f 100644
--- a/gdb/procfs.c
+++ b/gdb/procfs.c
@@ -1906,7 +1906,6 @@  procfs_target::detach (inferior *inf, int from_tty)
 
       printf_filtered (_("Detaching from program: %s, %s\n"), exec_file,
 		       target_pid_to_str (ptid_t (pid)));
-      gdb_flush (gdb_stdout);
     }
 
   do_detach ();
diff --git a/gdb/remote.c b/gdb/remote.c
index 36136e3e3ee..279f48669be 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5826,8 +5826,6 @@  extended_remote_target::attach (const char *args, int from_tty)
       else
 	printf_unfiltered (_("Attaching to %s\n"),
 			   target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
     }
 
   xsnprintf (rs->buf.data (), get_remote_packet_size (), "vAttach;%x", pid);
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index 030850955ac..5b7d57a2163 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -406,7 +406,6 @@  adi_print_versions (CORE_ADDR vaddr, size_t cnt, gdb_byte *tags)
           ++v_idx;
         }
       printf_filtered ("\n");
-      gdb_flush (gdb_stdout);
       vaddr += maxelts;
     }
 }
diff --git a/gdb/target.c b/gdb/target.c
index 116510e8cb8..88d744ded0f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3157,7 +3157,6 @@  target_announce_detach (int from_tty)
   pid = inferior_ptid.pid ();
   printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file,
 		     target_pid_to_str (ptid_t (pid)));
-  gdb_flush (gdb_stdout);
 }
 
 /* The inferior process has died.  Long live the inferior!  */
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 6c499b6467e..114725bc695 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -691,7 +691,6 @@  print_type_scalar (struct type *type, LONGEST val, struct ui_file *stream)
     default:
       error (_("Invalid type code in symbol table."));
     }
-  gdb_flush (stream);
 }
 
 /* Dump details of a type specified either directly or indirectly.
diff --git a/gdb/utils.c b/gdb/utils.c
index ec2619642a1..f8f8d4ff6b0 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -890,7 +890,6 @@  defaulted_query (const char *ctlstr, const char defchar, va_list args)
       printf_filtered (_("(%s or %s) [answered %c; "
 			 "input not from terminal]\n"),
 		       y_string, n_string, def_answer);
-      gdb_flush (gdb_stdout);
 
       return def_value;
     }
diff --git a/gdb/valprint.c b/gdb/valprint.c
index a079c0025a7..10020901c2d 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -986,7 +986,6 @@  generic_val_print (struct type *type,
       error (_("Unhandled type code %d in symbol table."),
 	     TYPE_CODE (type));
     }
-  gdb_flush (stream);
 }
 
 /* Print using the given LANGUAGE the data of type TYPE located at
@@ -1032,7 +1031,6 @@  val_print (struct type *type, LONGEST embedded_offset,
   if (TYPE_STUB (real_type))
     {
       fprintf_filtered (stream, _("<incomplete type>"));
-      gdb_flush (stream);
       return;
     }
 
@@ -2860,8 +2858,6 @@  val_print_string (struct type *elttype, const char *encoding,
       fprintf_filtered (stream, ">");
     }
 
-  gdb_flush (stream);
-
   return (bytes_read / width);
 }
 
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index e47fcb1b40a..99bcedcc2cf 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2015,8 +2015,6 @@  windows_nat_target::attach (const char *args, int from_tty)
       else
 	printf_unfiltered ("Attaching to %s\n",
 			   target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
     }
 
   do_initial_windows_stuff (this, pid, 1);
@@ -2046,7 +2044,6 @@  windows_nat_target::detach (inferior *inf, int from_tty)
 	exec_file = "";
       printf_unfiltered ("Detaching from program: %s, Pid %u\n", exec_file,
 			 (unsigned) current_event.dwProcessId);
-      gdb_flush (gdb_stdout);
     }
 
   x86_cleanup_dregs ();