[incremental] Patch: FYI: initial server-izing
Tom Tromey
tromey@redhat.com
Thu Aug 30 17:36:00 GMT 2007
I'm checking this in on the incremental-compiler branch.
This is the first draft of server-izing GCC. This works well enough
to compile a single file, so it seemed like a good time to check it
in. It still has bugs, for instance it crashes if you try to compile
a second file.
Tom
Index: ChangeLog
from Tom Tromey <tromey@redhat.com>
* Makefile.in (OBJS-common): Added server.o.
(GCC_OBJS): Likewise.
(toplev.o): Added server.h.
(gcc.o): Likewise.
(server.o): New target.
* toplev.c (toplev_main): Handle -fserver.
Include server.h.
(init_asm_output): Don't open file if already open.
(server_callback): New function.
* server.h: New file.
* server.c: New file.
* gcc.c (server): New global.
(use_server): Likewise.
(option_map): Added --server.
(process_command): Recognize -server.
Include server.h.
(execute): Send commands to server.
(server_callback): New function.
Index: gcc.c
===================================================================
--- gcc.c (revision 127650)
+++ gcc.c (working copy)
@@ -86,6 +86,7 @@
#include "gcc.h"
#include "flags.h"
#include "opts.h"
+#include "server.h"
/* By default there is no special suffix for target executables. */
/* FIXME: when autoconf is fixed, remove the host check - dj */
@@ -195,6 +196,15 @@
static int report_times;
+/* Flag indicating that we should start a server process. */
+
+static int server;
+
+/* Flag indicating that we should attempt to connect to a server
+ process. */
+
+static int use_server;
+
/* Nonzero means place this string before uses of /, so that include
and library files can be found in an alternate location. */
@@ -1154,6 +1164,7 @@
{"--quiet", "-q", 0},
{"--resource", "-fcompile-resource=", "aj"},
{"--save-temps", "-save-temps", 0},
+ {"--server", "-server", 0},
{"--shared", "-shared", 0},
{"--silent", "-q", 0},
{"--specs", "-specs=", "aj"},
@@ -2931,6 +2942,23 @@
#endif /* DEBUG */
}
+ if (use_server)
+ {
+ for (i = 0; i < n_commands; ++i)
+ {
+ if (i == 0)
+ {
+ if (!client_connect (commands[i].argv[0]))
+ fatal ("couldn't start server: %s", commands[i].argv[0]);
+ }
+ if (!client_send_command (commands[i].argv))
+ fatal ("couldn't send command to server: %s", commands[i].argv[0]);
+ }
+ client_wait ();
+ execution_count++;
+ return 0;
+ }
+
#ifdef ENABLE_VALGRIND_CHECKING
/* Run the each command through valgrind. To simplify prepending the
path to valgrind and the option "-q" (for quiet operation unless
@@ -3788,6 +3816,11 @@
use_pipes = 1;
n_switches++;
}
+ else if (strcmp (argv[i], "-server") == 0)
+ {
+ server = 1;
+ n_switches++;
+ }
else if (strcmp (argv[i], "-###") == 0)
{
/* This is similar to -v except that there is no execution
@@ -4159,6 +4192,8 @@
;
else if (strcmp (argv[i], "-time") == 0)
;
+ else if (strcmp (argv[i], "-server") == 0)
+ ;
else if (strcmp (argv[i], "-###") == 0)
;
else if (argv[i][0] == '-' && argv[i][1] != 0)
@@ -6128,6 +6163,12 @@
xmalloc_set_program_name (programname);
+ if (getenv ("GCCSERVER") != NULL)
+ {
+ use_server = 1;
+ use_pipes = 1; /* FIXME... need better processing */
+ }
+
expandargv (&argc, &argv);
/* Determine if any expansions were made. */
@@ -6484,6 +6525,13 @@
on the various sub-processes, along with the --help switch. */
}
+ if (server)
+ {
+ /* FIXME: allow more than cc1... */
+ server_start (find_a_file (&exec_prefixes, "cc1", X_OK, 0));
+ return 0;
+ }
+
if (verbose_flag)
{
int n;
@@ -7938,3 +7986,13 @@
fflush (stdout);
return NULL;
}
+
+
+
+void
+server_callback (int ARG_UNUSED (fd),
+ char ** ARG_UNUSED (cc1_argv),
+ char ** ARG_UNUSED (as_argv))
+{
+ gcc_unreachable ();
+}
Index: toplev.c
===================================================================
--- toplev.c (revision 127650)
+++ toplev.c (working copy)
@@ -82,6 +82,7 @@
#include "alloc-pool.h"
#include "tree-mudflap.h"
#include "tree-pass.h"
+#include "server.h"
#if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO)
#include "dwarf2out.h"
@@ -1386,7 +1387,11 @@
static void
init_asm_output (const char *name)
{
- if (name == NULL && asm_file_name == 0)
+ if (asm_out_file)
+ {
+ /* Already initialized elsewhere. */
+ }
+ else if (name == NULL && asm_file_name == 0)
asm_out_file = stdout;
else
{
@@ -2159,6 +2164,73 @@
timevar_print (stderr);
}
+static struct pex_obj *
+start_as (char **as_argv)
+{
+ struct pex_obj *px;
+ int pxerr;
+
+ px = pex_init (PEX_USE_PIPES, "as", NULL);
+ if (px)
+ {
+ const char *errstr;
+
+ /* Note that asm_out_file is conditional on BUFSIZ and is
+ redeclared in a number of files. Yay. */
+ asm_out_file = pex_input_pipe (px, 0);
+
+ errstr = pex_run (px, PEX_LAST | PEX_SEARCH, as_argv[0], as_argv + 1,
+ NULL, NULL, &pxerr);
+ if (errstr)
+ {
+ /* FIXME. */
+ error ("failed to exec as: %s", errstr);
+ pex_free (px);
+ px = NULL;
+ }
+ }
+
+ return px;
+}
+
+void
+server_callback (int fd, char **cc1_argv, char **as_argv)
+{
+ struct pex_obj *px;
+
+ /* For now, work single-threaded and just stomp on global state as
+ needed. */
+
+ /* The diagnostic machinery doesn't need this, but pex does. Also
+ GCC itself seems to write to stderr a lot ... */
+ dup2 (fd, 2);
+
+ /* FIXME: reset errorcount and sorrycount? Make a new
+ global_dc? */
+
+ px = start_as (as_argv);
+
+ if (px)
+ {
+ int n;
+ for (n = 0; cc1_argv[n]; ++n)
+ ;
+ decode_options (n, (const char **) cc1_argv);
+ init_local_tick (); /* FIXME... */
+ do_compile ();
+
+ /* FIXME: send a single byte back to the client for status?
+ FIXME: this function should return an error indication. */
+
+ pex_free (px);
+ }
+
+ /* Make sure to close dup'd stderr, so that client will terminate
+ properly. The server loop will take care of the fd we were
+ passed. */
+ close (2);
+}
+
/* Entry point of cc1, cc1plus, jc1, f771, etc.
Exit code is FATAL_EXIT_CODE if can't open files or if there were
any errors, or SUCCESS_EXIT_CODE if compilation succeeded.
@@ -2173,6 +2245,13 @@
/* Initialization of GCC's environment, and diagnostics. */
general_init (argv[0]);
+ if (argc == 2 && !strncmp (argv[1], "-fserver=", 9))
+ {
+ int fd = atoi (argv[1] + 9);
+ server_main_loop (argv[0], fd);
+ return SUCCESS_EXIT_CODE;
+ }
+
/* Parse the options and do minimal processing; basically just
enough to default flags appropriately. */
decode_options (argc, argv);
Index: server.c
===================================================================
--- server.c (revision 0)
+++ server.c (revision 0)
@@ -0,0 +1,406 @@
+/* Shared code for GCC server
+ Copyright (C) 2007
+ Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "server.h"
+#include "errors.h"
+#include "dyn-string.h"
+#include "opts.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* The name of the server socket we're using. */
+static char *server_socket_name;
+
+/* The file descriptor of our connection to the server. */
+static int connection_fd = -1;
+
+/* Return the name of the server socket. The value is cached in
+ 'server_socket_name'. PROGNAME is the path name of the server
+ executable and is used to construct the socket name. */
+static char *
+get_socket_name (const char *progname)
+{
+ if (!server_socket_name)
+ {
+ const char *basename;
+
+ basename = strrchr (progname, '/');
+ if (basename == NULL)
+ basename = progname;
+
+ /* FIXME: put in subdir and use UID. */
+ server_socket_name = concat ("/tmp/", ".", basename, "-server", NULL);
+ }
+
+ return server_socket_name;
+}
+
+/* Start a compile server. PROGRAM is the full path name of the
+ server to start. Returns when the server is ready, or prints a
+ message to stderr and exits on failure. */
+void
+server_start (char *program)
+{
+ int fds[2];
+
+ /* Make a pipe so we can tell when the server is ready. */
+ pipe (fds); /* FIXME: error checking */
+
+ switch (fork ())
+ {
+ case 0:
+ {
+ /* Child. */
+ char fdstr[40];
+ char *args[5];
+ int i = 0;
+#ifdef ENABLE_VALGRIND_CHECKING
+ args[i++] = VALGRIND_PATH;
+ args[i++] = "-q";
+#endif
+ args[i++] = program;
+ sprintf (fdstr, "-fserver=%d", fds[1]);
+ args[i++] = fdstr;
+ args[i++] = NULL;
+ /* Close read end of notification pipe. */
+ close (fds[0]);
+ /* Don't need these any more -- but keep stderr while
+ debugging. */
+ close (0);
+ close (1);
+ execv (args[0], args);
+ error ("exec of server failed: %s", xstrerror (errno));
+ exit (FATAL_EXIT_CODE);
+ }
+ break;
+
+ case -1:
+ error ("fork failed while starting server: %s", xstrerror (errno));
+ exit (FATAL_EXIT_CODE);
+ break;
+
+ default:
+ {
+ /* Parent. */
+ char x;
+ close (fds[1]);
+ int r = read (fds[0], &x, 1);
+ if (r == -1)
+ error ("server failed to start: %s", xstrerror (errno));
+ close (fds[0]);
+ }
+ }
+}
+
+/* Open a server socket. PROGNAME is the path name of the server.
+ Returns the new file descriptor, or -1 on error. */
+static int
+open_socket (const char *progname)
+{
+ struct sockaddr_un address;
+ int sockfd, save_mask;
+ char *sname = get_socket_name (progname);
+
+ sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ return -1;
+
+ address.sun_family = AF_UNIX;
+ /* FIXME: buffer overflow. */
+ strcpy (address.sun_path, sname);
+
+ save_mask = umask (077);
+ if (bind (sockfd, (struct sockaddr *) &address, SUN_LEN (&address)) < 0)
+ return -1;
+
+ umask (save_mask);
+ return sockfd;
+}
+
+/* Free the contents of an ARGV array, then the array itself. */
+static void
+free_argv (char **argv)
+{
+ int i;
+ for (i = 0; argv[i]; ++i)
+ free (argv[i]);
+ free (argv);
+}
+
+/* Split a string into elements at spaces. A backslash in the string
+ quotes the following character. The resulting array should later
+ be freed with free_argv. */
+static char **
+split_argv (char *buffer)
+{
+ char **argv = NULL;
+ int alloc = 0;
+ int used = 0;
+ char *p = buffer;
+ dyn_string_t str = dyn_string_new (20);
+
+ while (*p)
+ {
+ if (*p == '\\')
+ {
+ ++p;
+ if (!*p)
+ {
+ /* Not actually valid... */
+ break;
+ }
+ dyn_string_append_char (str, *p);
+ }
+ else if (*p == ' ')
+ {
+ if (used == alloc)
+ {
+ alloc = 2 * alloc + 20;
+ argv = (char **) xrealloc (argv, alloc * sizeof (char *));
+ }
+ argv[used++] = dyn_string_release (str);
+ if (!*p)
+ {
+ str = NULL;
+ break;
+ }
+ str = dyn_string_new (20);
+ }
+ else
+ dyn_string_append_char (str, *p);
+ ++p;
+ }
+
+ if (used + 2 >= alloc)
+ {
+ alloc = used + 2;
+ argv = (char **) xrealloc (argv, alloc * sizeof (char *));
+ }
+
+ if (str)
+ argv[used++] = dyn_string_release (str);
+ argv[used] = NULL;
+
+ return argv;
+}
+
+/* Helper function for server_main_loop. Read a request from the file
+ descriptor REQFD and take action. */
+static void
+request_and_response (int reqfd)
+{
+ char **argvs[2];
+ int count = 0;
+
+ while (true)
+ {
+ char cmd;
+ if (read (reqfd, &cmd, 1) != 1)
+ break;
+ if (cmd == 'X')
+ {
+ /* Execute a command. */
+ int len;
+ char *buffer, **argv;
+
+ if (read (reqfd, &len, sizeof (len)) != sizeof (len))
+ break;
+ buffer = (char *) xmalloc (len + 1);
+ if (read (reqfd, buffer, len) != len)
+ break;
+ buffer[len] = '\0';
+
+ argv = split_argv (buffer);
+ free (buffer);
+ /* FIXME: obviously this is pretty lame. */
+ if (count >= 2)
+ {
+ fprintf (stderr, "DIE!\n");
+ continue;
+ }
+
+ argvs[count++] = argv;
+ }
+ else if (cmd == 'D')
+ {
+ /* Done with requests, compile away. */
+ if (count != 2)
+ {
+ fprintf (stderr, "DIE 2!\n");
+ count = 0; /* And leak memory while we're at it. */
+ continue;
+ }
+ server_callback (reqfd, argvs[0], argvs[1]);
+ free_argv (argvs[0]);
+ free_argv (argvs[1]);
+ count = 0;
+ break;
+ }
+ else if (cmd == 'K')
+ {
+ /* Kill server. FIXME. */
+ }
+ else
+ {
+ /* FIXME: error. */
+ break;
+ }
+ }
+}
+
+/* Main loop of the server. Creates a server socket and listens to
+ it, accepting requests and acting on them. PROGNAME is the full
+ path to the server executable. FD is the completion file
+ descriptor, used to notify the gcc driver when the server is ready
+ to accept connections. */
+void
+server_main_loop (const char *progname, int fd)
+{
+ int sockfd = open_socket (progname);
+ char reply = 't';
+
+ if (sockfd < 0)
+ {
+ error ("couldn't create server socket: %s", xstrerror (errno));
+ /* FIXME: unlink the socket if it exists. */
+ /* Simply exiting is ok -- GCC will detect that the socket has
+ been closed. */
+ exit (FATAL_EXIT_CODE);
+ }
+
+ write (fd, &reply, 1);
+ close (fd);
+ fprintf (stderr, "server is ready\n");
+
+ listen (sockfd, 5);
+
+ while (true)
+ {
+ int reqfd = accept (sockfd, NULL, 0);
+ if (reqfd < 0)
+ {
+ error ("error while accepting: %s", xstrerror (errno));
+ break;
+ }
+ request_and_response (reqfd);
+ close (reqfd);
+ }
+
+ close (sockfd);
+ if (server_socket_name)
+ unlink (server_socket_name);
+}
+
+/* Make a connection to a running server. PROGNAME is the name of the
+ server to connect to. Returns true on success, false on
+ failure. */
+bool
+client_connect (const char *progname)
+{
+ char *sname = get_socket_name (progname);
+ struct sockaddr_un address;
+
+ connection_fd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (connection_fd < 0)
+ return -1;
+
+ address.sun_family = AF_UNIX;
+ /* FIXME: buffer overflow. */
+ strcpy (address.sun_path, sname);
+
+ if (connect (connection_fd, (struct sockaddr *) &address,
+ SUN_LEN (&address)) < 0)
+ return false;
+
+ return true;
+}
+
+/* Send a command to the server. 'client_connect' must already have
+ been called successfully. ARGV is the command line that the server
+ should execute (or "emulate"). Returns true on success, false on
+ failure. */
+bool
+client_send_command (const char **argv)
+{
+ dyn_string_t quoted;
+ char cmd, *line;
+ int i, len;
+ bool result;
+
+ gcc_assert (connection_fd >= 0);
+
+ quoted = dyn_string_new (50);
+
+ for (i = 0; argv[i]; ++i)
+ {
+ int j;
+ if (i > 0)
+ dyn_string_append_char (quoted, ' ');
+ for (j = 0; argv[i][j]; ++j)
+ {
+ if (argv[i][j] == ' ' || argv[i][j] == '\\')
+ dyn_string_append_char (quoted, '\\');
+ dyn_string_append_char (quoted, argv[i][j]);
+ }
+ }
+
+ line = dyn_string_release (quoted);
+
+ len = strlen (line);
+ cmd = 'X';
+
+ if (write (connection_fd, &cmd, sizeof (cmd)) != sizeof (cmd)
+ || write (connection_fd, &len, sizeof (len)) != sizeof (len))
+ result = false;
+ else
+ result = (write (connection_fd, line, len) == len);
+ free (line);
+ return result;
+}
+
+/* Called by the client after submitting all its jobs. This waits for
+ the server to complete the tasks. It reads from the server
+ connection and prints errors to stderr. Both 'client_connect' and
+ 'client_send_command' must have been successfully called before
+ calling this. */
+void
+client_wait (void)
+{
+ gcc_assert (connection_fd >= 0);
+ char cmd = 'D';
+
+ if (write (connection_fd, &cmd, 1) != 1)
+ return;
+
+ while (true)
+ {
+ int len;
+ char buffer[100];
+
+ len = read (connection_fd, buffer, 100);
+ if (len <= 0)
+ break;
+ fwrite (buffer, 1, len, stderr);
+ }
+}
Index: server.h
===================================================================
--- server.h (revision 0)
+++ server.h (revision 0)
@@ -0,0 +1,35 @@
+/* Declarations for shared server code
+ Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_SERVER_H
+#define GCC_SERVER_H
+
+/* Functions for the server to use. */
+extern void server_start (char *);
+extern void server_main_loop (const char *progname, int);
+
+/* Functions for the client to use. */
+extern bool client_connect (const char *);
+extern bool client_send_command (const char **);
+extern void client_wait (void);
+
+/* The main loop calls this when a command is read. */
+extern void server_callback (int, char **, char **);
+
+#endif /* GCC_SERVER_H */
Index: Makefile.in
===================================================================
--- Makefile.in (revision 127650)
+++ Makefile.in (working copy)
@@ -962,7 +962,7 @@
CXX_TARGET_OBJS=@cxx_target_objs@
# Object files for gcc driver.
-GCC_OBJS = gcc.o opts-common.o gcc-options.o
+GCC_OBJS = gcc.o opts-common.o gcc-options.o server.o
# Language-specific object files for C and Objective C.
C_AND_OBJC_OBJS = attribs.o c-errors.o c-lex.o c-pragma.o c-decl.o c-typeck.o \
@@ -1116,6 +1116,7 @@
sched-vis.o \
sdbout.o \
see.o \
+ server.o \
simplify-rtx.o \
sreal.o \
stack-ptr-mod.o \
@@ -1843,7 +1844,7 @@
gcc.o: gcc.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) intl.h multilib.h \
Makefile $(lang_specs_files) specs.h prefix.h $(GCC_H) $(FLAGS_H) \
- configargs.h $(OBSTACK_H) opts.h
+ configargs.h $(OBSTACK_H) opts.h server.h
(SHLIB_LINK='$(SHLIB_LINK)' \
SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) \
@@ -1942,6 +1943,7 @@
double-int.o: double-int.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H)
+server.o : server.c $(CONFIG_H) $(SYSTEM_H) coretypes.h server.h errors.h
langhooks.o : langhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(TREE_H) toplev.h $(TREE_INLINE_H) $(RTL_H) insn-config.h $(INTEGRATE_H) \
langhooks.h $(LANGHOOKS_DEF_H) $(FLAGS_H) $(GGC_H) $(DIAGNOSTIC_H) intl.h \
@@ -2311,7 +2313,7 @@
value-prof.h $(PARAMS_H) $(TM_P_H) reload.h dwarf2asm.h $(TARGET_H) \
langhooks.h insn-flags.h $(CFGLAYOUT_H) $(CFGLOOP_H) hosthooks.h \
$(CGRAPH_H) $(COVERAGE_H) alloc-pool.h $(GGC_H) $(INTEGRATE_H) \
- $(CPPLIB_H) opts.h params.def tree-mudflap.h $(REAL_H) tree-pass.h
+ $(CPPLIB_H) opts.h params.def tree-mudflap.h $(REAL_H) tree-pass.h server.h
$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) \
-DTARGET_NAME=\"$(target_noncanonical)\" \
-c $(srcdir)/toplev.c $(OUTPUT_OPTION)
More information about the Gcc-patches
mailing list