1#!/usr/bin/env python3
2
3# Copyright 2007 Google Inc.
4#
5# This program is free software; you can redistribute it and/or
6# modify it under the terms of the GNU General Public License
7# as published by the Free Software Foundation; either version 2
8# of the License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
18# USA.
19
20"""Conservative approximation of include dependencies for C/C++."""
21
22__author__ = "Nils Klarlund"
23
24# TODO (klarlund) Implement abort mechanism: regularly check whether
25# ppid is 0; if so, then abort.
26
27# Python imports
28import gc
29import getopt
30import glob
31import os
32import re
33import shutil
34import signal
35import socketserver
36import sys
37import tempfile
38import traceback
39
40# Include server imports
41import basics
42import distcc_pump_c_extensions
43import include_analyzer_memoizing_node
44import statistics
45
46# The default size passed to listen by a streaming socket server of
47# socketserver is only 5. Make it 128 (which appears to be the hard
48# built-in limit for Linux). This enables requests to the include
49# server to be buffered better.
50REQUEST_QUEUE_SIZE = 128
51
52Debug = basics.Debug
53DEBUG_TRACE = basics.DEBUG_TRACE
54DEBUG_WARNING = basics.DEBUG_WARNING
55# Exceptions.
56SignalSIGTERM = basics.SignalSIGTERM
57NotCoveredError = basics.NotCoveredError
58NotCoveredTimeOutError = basics.NotCoveredTimeOutError
59
60
61# USAGE
62
63def Usage():
64  print("""Usage:
65
66include_server --port INCLUDE_SERVER_PORT [OPTIONS]
67
68where INCLUDE_SERVER_PORT is a socket name. Fork the include server
69for incremental include analysis. The include server answers queries
70from the distcc client about which files to include in a C/C++
71compilation. This command itself terminates as soon as the include
72server has been spawned.
73
74OPTIONS:
75
76 -dPAT, --debug_pattern=PAT  Bit vector for turning on warnings and debugging
77                               1 = warnings
78                               2 = trace some functions
79                             other powers of two: see basics.py.
80
81 -e, --email                 Send email to discc-pump developers when include
82                             server gets in trouble.
83
84 --email_bound NUMBER        Maximal number of emails to send (in addition to
85                             a final email). Default: 3.
86
87 --no-email                  Do not send email.
88
89 --path_observation_re=RE    Issue warning message whenever a filename is
90                             resolved to a realpath that is matched by RE,
91                             which is a regular expression in Python syntax.
92                             This is useful for finding out where files included
93                             actually come from. Use RE="" to find them all.
94                             Note: warnings must be enabled with at least -d1.
95
96 --pid_file FILEPATH         The pid of the include server is written to file
97                             FILEPATH.
98
99 -s, --statistics            Print information to stdout about include analysis.
100
101 --stat_reset_triggers=LIST  Flush stat caches when the timestamp of any
102                             filepath in LIST changes or the filepath comes in
103                             or out of existence.  LIST is a colon separated
104                             string of filepaths, possibly containing simple
105                             globs (as allowed by Python's glob module). Print
106                             a warning whenever such a change happens (if
107                             warnings are enabled). This option allows limited
108                             exceptions to distcc_pump's normal assumption that
109                             source files are not modified during the build.
110
111 -t, --time                  Print elapsed, user, and system time to stderr.
112
113 --unsafe_absolute_includes  Do preprocessing on the compilation server even if
114                             includes of absolute filepaths are encountered.
115                             Such includes are then ignored for the purposes of
116                             gathering the include closure. See the
117                             include_server(1) man page for further information.
118                             Using this option may lead to incorrect results.
119
120 --no_force_dirs             Do not force the creation of all directories used
121                             in an include path.  May improve performance for
122                             some cases, but will break builds which use
123                             include structures like "<foo/../file.h>" without
124                             including other files in foo/.
125
126 -v, --verify                Verify that files in CPP closure are contained in
127                             closure calculated by include processor.
128
129 -w, --write_include_closure Write a .d_approx file which lists all the
130                             included files calculated by the include server;
131                             with -x, additionally write the included files
132                             as calculated by CPP to a .d_exact file.
133
134 -x, --exact_analysis        Use CPP instead, do not omit system headers files.
135""")
136
137# TODO(klarlund)
138#   --simple_algorithm         not currently implemented
139
140
141# UTILITIES
142
143def _PrintStackTrace(fd):
144  """Print stacktrace to file object."""
145  print('------- Include server stack trace -----------', file=fd)
146  # Limit is 1000 entries.
147  traceback.print_exc(1000, fd)
148  print('----------------------------------------------', file=fd)
149
150
151class _EmailSender(object):
152  """For sending emails. We limit their number to avoid email storms."""
153
154  def __init__(self):
155    self.number_sent = 0
156
157  def TryToSend(self, fd, force=False, never=False):
158    """Send the contents of file to predefined blame address.
159    Arguments:
160      fd: open file descriptor, will remain open
161      force: send even if bound has been reached
162    """
163    if not basics.opt_send_email: return
164    if self.number_sent >= basics.opt_email_bound and not force: return
165    if never: return
166    self.number_sent += 1
167    # For efficiency, we postpone reading needed libraries for emailing until
168    # now.
169    import smtplib
170    import getpass
171    import socket
172    try:
173      user_addr = "%s@%s" %  (getpass.getuser(), socket.gethostname())
174      fd.seek(0)
175      msg = "Subject: %s\nTo: %s\nFrom: %s\n\n%s\n%s" % (
176        basics.EMAIL_SUBJECT,
177        basics.DCC_EMAILLOG_WHOM_TO_BLAME,
178        user_addr,
179        "Automated email number %d in include server session.\n" %
180        self.number_sent,
181        fd.read())
182      s = smtplib.SMTP()
183      s.connect()
184      s.sendmail(user_addr, [basics.DCC_EMAILLOG_WHOM_TO_BLAME], msg)
185      Debug(DEBUG_WARNING, "Include server sent email to %s",
186            basics.DCC_EMAILLOG_WHOM_TO_BLAME)
187      s.close()
188    except:
189      Debug(DEBUG_WARNING, basics.CANT_SEND_MESSAGE)
190      traceback.print_exc()
191
192  def MaybeSendEmail(self, fd, force=False, never=False):
193    """Print warning and maybe send email; the contents is from file object.
194
195    Arguments:
196      fd: a file object that will be closed.
197      force: send the mail even if number of emails sent exceed
198        basics.opt_email_bound
199    """
200    fd.seek(0, 0)
201    Debug(DEBUG_WARNING, "%s", fd.read())
202    self.TryToSend(fd, force, never)
203    fd.close()
204
205
206NEWLINE_RE = re.compile(r"\n", re.MULTILINE)
207BACKSLASH_NEWLINE_RE = re.compile(r"\\\n", re.MULTILINE)
208
209
210def ExactDependencies(cmd, realpath_map, systemdir_prefix_cache,
211                      translation_unit):
212  """The dependencies as calculated by CPP, the C Preprocessor.
213  Arguments:
214    cmd:  the compilation command, a string
215    realpath_map: map from filesystem paths (no symlink) to idx
216    systemdir_prefix_cache: says whether realpath starts with a systemdir
217    translation_unit: string
218  Returns:
219    the set of realpath indices of the include dependencies.
220  Raises:
221    NotCoveredError
222  """
223
224  # Safely get a couple of temporary files.
225  (fd_o, name_o) = tempfile.mkstemp("distcc-pump")
226  (fd_d, name_d) = tempfile.mkstemp("distcc-pump")
227
228  def _delete_temp_files():
229    os.close(fd_d)
230    os.close(fd_o)
231    os.unlink(name_o)
232    os.unlink(name_d)
233
234  # Remove -o option and call with -E, -M, and -MF flags.
235  preprocessing_command = (
236    (re.sub(r"\s-o[ ]?(\w|[./+-])+", " ", cmd) # delete -o option
237     + " -o %(name_o)s"                        # add it back, but to temp file,
238     + " -E"                                   # macro processing only
239     + " -M -MF %(name_d)s") %                  # output .d file
240    {'name_o':name_o, 'name_d':name_d})
241
242  ret = os.system(preprocessing_command)
243  if ret:
244    _delete_temp_files()
245    raise NotCoveredError("Could not execute '%s'" %
246                          preprocessing_command,
247                          translation_unit)
248  # Using the primitive fd_d file descriptor for reading is cumbersome, so open
249  # normally as well.
250  fd_d_ = open(name_d, "rb", encoding='latin-1')
251  # Massage the contents of fd_d_
252  dotd = re.sub("^.*:", # remove Makefile target
253                "",
254                NEWLINE_RE.sub(
255                  "", # remove newlines
256                  BACKSLASH_NEWLINE_RE.sub("", # remove backslashes
257                                   fd_d_.read())))
258  fd_d_.close()
259  _delete_temp_files()
260  # The sets of dependencies is a set the of realpath indices of the
261  # absolute filenames corresponding to files in the dotd file.
262  deps = set([ rp_idx
263               for filepath in dotd.split()
264               for rp_idx in [ realpath_map.Index(os.path.join(os.getcwd(),
265                                                               filepath)) ]
266               if not systemdir_prefix_cache.StartsWithSystemdir(rp_idx,
267                                                                 realpath_map)
268              ])
269  statistics.len_exact_closure = len(deps)
270  return deps
271
272
273def WriteDependencies(deps, result_file, realpath_map):
274  """Write the list of deps to result_file.
275  Arguments:
276    deps: a list of realpath indices
277    result_file: a filepath
278    realpath_map: map from filesystem paths (no symlink) to idx
279  """
280  try:
281    fd = open(result_file, "w")
282    fd.write("\n".join([realpath_map.string[d] for d in deps]))
283    fd.write("\n")
284    fd.close()
285  except (IOError, OSError) as  why:
286    raise NotCoveredError("Could not write to '%s': %s" % (result_file, why))
287
288
289def VerifyExactDependencies(include_closure,
290                            exact_no_system_header_dependency_set,
291                            realpath_map,
292                            translation_unit):
293  """Compare computed and real include closures, ignoring system
294  header files (such as those in /usr/include).
295  Arguments:
296    include_closure: a dictionary whose keys are realpath indices
297    exact_no_system_header_dependency_set: set of realpath indices
298    realpath_map: map from filesystem paths (no symlink) to idx
299    translation_unit: string
300  Raises:
301    NotCoveredError
302"""
303  diff = exact_no_system_header_dependency_set - set(include_closure)
304  statistics.len_surplus_nonsys = (
305    len(set(include_closure) - exact_no_system_header_dependency_set))
306
307  if diff != set([]):
308    # Pick one bad dependency.
309    bad_dep = diff.pop()
310    raise NotCoveredError(
311      ("Calculated include closure does not contain: '%s'.\n"
312       + "There %s %d such missing %s.")
313      % (realpath_map.string[bad_dep],
314         len(diff) == 0 and "is" or "are",
315         len(diff) + 1,
316         len(diff) == 0 and "dependency" or "dependencies"),
317      translation_unit)
318
319
320# A SOCKET SERVER
321
322class Queuingsocketserver(socketserver.UnixStreamServer):
323  """A socket server whose request queue have size REQUEST_QUEUE_SIZE."""
324  request_queue_size = REQUEST_QUEUE_SIZE
325
326  def handle_error(self, _, client_address):
327    """Re-raise current exception; overrides socketserver.handle_error.
328    """
329    raise
330
331
332# HANDLER FOR SOCKETSERVER
333
334def DistccIncludeHandlerGenerator(include_analyzer):
335  """Wrap a socketserver based on the include_analyzer object inside a new
336  type that is a class named IncludeHandler."""
337
338  # TODO(klarlund): Can we do this without dynamic type generation?
339
340  class IncludeHandler(socketserver.StreamRequestHandler):
341    """Define a handle() method that invokes the include closure algorithm ."""
342
343    def handle(self):
344      """Using distcc protocol, read command and return include closure.
345
346      Do the following:
347       - Read from the socket, using the RPC protocol of distcc:
348          - the current directory, and
349          - the compilation command, already broken down into an argv vector.
350       - Parse the command to find options like -I, -iquote,...
351       - Invoke the include server's closure algorithm to yield a set of files
352         and set of symbolic links --- both sets of files under client_root,
353         which duplicates the part of the file system that CPP will need.
354       - Transmit the file and link names on the socket using the RPC protocol.
355      """
356      statistics.StartTiming()
357      currdir = distcc_pump_c_extensions.RCwd(self.rfile.fileno())
358      cmd = distcc_pump_c_extensions.RArgv(self.rfile.fileno())
359
360      try:
361        try:
362          # We do timeout the include_analyzer using the crude mechanism of
363          # SIGALRM. This signal is problematic if raised while Python is doing
364          # I/O in the C extensions and during use of the subprocess
365          # module.
366          #
367          # TODO(klarlund) The Python library manual states: "When a signal
368          # arrives during an I/O operation, it is possible that the I/O
369          # operation raises an exception after the signal handler returns. This
370          # is dependent on the underlying Unix system's semantics regarding
371          # interrupted system calls."  We must clarify this. Currently, there
372          # is I/O during DoCompilationCommand:
373          #
374          #  - when a link is created in mirror_path.py
375          #  - module compress_files is used
376          #
377          # TODO(klarlund): Modify mirror_path so that is accumulates symbolic
378          # link operations instead of actually executing them on the spot. The
379          # accumulated operations can be executed after DoCompilationCommand
380          # when the timer has been cancelled.
381          include_analyzer.timer = basics.IncludeAnalyzerTimer()
382          files_and_links = (
383              include_analyzer.
384                  DoCompilationCommand(cmd, currdir,
385                                       include_analyzer.client_root_keeper))
386        finally:
387          # The timer should normally be cancelled during normal execution
388          # flow. Still, we want to make sure that this is indeed the case in
389          # all circumstances.
390          include_analyzer.timer.Cancel()
391
392      except NotCoveredError as inst:
393        # Warn user. The 'Preprocessing locally' message is meant to
394        # assure the user that the build process is otherwise intact.
395        fd = tempfile.TemporaryFile(mode='w+')
396        print(("Preprocessing locally. Include server not covering: %s for "
397          + "translation unit '%s'") % (
398            (inst.args and inst.args[0] or "unknown reason",
399             include_analyzer.translation_unit)), file=fd, end=' ')
400        # We don't include a stack trace here.
401        include_analyzer.email_sender.MaybeSendEmail(fd,
402                                                     never=not inst.send_email)
403        # The empty argv list denotes failure. Communicate this
404        # information back to the distcc client, so that it can fall
405        # back to preprocessing on the client.
406        distcc_pump_c_extensions.XArgv(self.wfile.fileno(), [])
407        if isinstance(inst, NotCoveredTimeOutError):
408          Debug(DEBUG_TRACE,
409                "Clearing caches because of include server timeout.")
410          include_analyzer.ClearStatCaches()
411
412      except SignalSIGTERM:
413        # Normally, we will get this exception when the include server is no
414        # longer needed. But we also handle it here, during the servicing of a
415        # request. See basics.RaiseSignalSIGTERM.
416        Debug(DEBUG_TRACE, "SIGTERM received while handling request.")
417        raise
418      except KeyboardInterrupt:
419        # Propagate to the last-chance exception handler in Main.
420        raise
421      except SystemExit as inst:
422        # When handler tries to exit (by invoking sys.exit, which in turn raises
423        # SystemExit), something is really wrong. Terminate the include
424        # server. But, print out an informative message first.
425        fd = tempfile.TemporaryFile(mode='w+')
426        print(("Preprocessing locally. Include server fatal error: '%s' for "
427           + "translation unit '%s'") % (
428          (inst.args, include_analyzer.translation_unit)), file=fd, end=' ')
429        _PrintStackTrace(fd)
430        include_analyzer.email_sender.MaybeSendEmail(fd, force=True)
431        distcc_pump_c_extensions.XArgv(self.wfile.fileno(), [])
432        sys.exit("Now terminating include server.")
433      # All other exceptions are trapped here.
434      except Exception as inst:
435        # Internal error. Better be safe than sorry: terminate include
436        # server. But show error to user on stderr. We hope this message will be
437        # reported.
438        fd = tempfile.TemporaryFile(mode='w+')
439        print(("Preprocessing locally. Include server internal error: '%s: %s'"
440           + " for translation unit '%s'") % (
441          (inst.__class__, inst.args, include_analyzer.translation_unit)), file=fd)
442        _PrintStackTrace(fd)
443        # # Force this email through (if basics.opt_send_email is True), because
444        # # this is the last one and this is an important case to report.
445        include_analyzer.email_sender.MaybeSendEmail(fd, force=True)
446        distcc_pump_c_extensions.XArgv(self.wfile.fileno(), [])
447        raise SignalSIGTERM  # to be caught in Main with no further stack trace
448      else:
449        # No exception raised, include closure can be trusted.
450        distcc_pump_c_extensions.XArgv(self.wfile.fileno(), files_and_links)
451      # Print out observed paths.
452      if basics.opt_path_observation_re:
453         include_analyzer.build_stat_cache.WarnAboutPathObservations(
454             include_analyzer.translation_unit)
455      # Finally, stop the clock and report statistics if needed.
456      statistics.EndTiming()
457      if basics.opt_statistics:
458        statistics.PrintStatistics(include_analyzer)
459
460  return IncludeHandler
461
462
463def _ParseCommandLineOptions():
464  """Parse arguments and options for the include server command.
465
466  Returns:
467    (include_server_port, pid_file), where include_server_port
468    is a string and pid_file is a string or None
469  Modifies:
470    option variables in module basics
471  """
472  try:
473    opts, args = getopt.getopt(sys.argv[1:],
474			       "d:estvwx",
475			       ["port=",
476                                "pid_file=",
477                                "debug_pattern=",
478                                "email",
479                                "no-email",
480                                "email_bound=",
481                                "exact_analysis",
482                                "path_observation_re=",
483                                "stat_reset_triggers=",
484                                "simple_algorithm",
485                                "statistics",
486                                "time",
487                                "unsafe_absolute_includes",
488                                "no_force_dirs",
489                                "verify",
490                                "write_include_closure"])
491  except getopt.GetoptError:
492    # Print help information and exit.
493    Usage()
494    sys.exit(1)
495  pid_file = None
496  include_server_port = None
497  for opt, arg in opts:
498    try:
499      if opt in ("-d", "--debug_pattern"):
500        basics.opt_debug_pattern = int(arg)
501      if opt in ("--port", ):
502        include_server_port = arg
503      if opt in ("--pid_file",):
504        pid_file = arg
505      if opt in ("-e", "--email"):
506        basics.opt_send_email = True
507      if opt in ("--no-email",):
508        basics.opt_send_email = False
509      if opt in ("--email_bound",):
510        basics.opt_email_bound = int(arg)
511      if opt in ("--path_observation_re",):
512        basics.opt_path_observation_re = re.compile(arg)
513      if opt in ("--stat_reset_triggers",):
514        basics.opt_stat_reset_triggers = (
515          dict([ (glob_expr,
516                  dict ([ (path, basics.Stamp(path))
517                          for path in glob.glob(glob_expr) ]))
518                 for glob_expr in arg.split(':') ]))
519      if opt in ("--simple_algorithm",):
520        basics.opt_simple_algorithm = True
521        sys.exit("Not implemented")
522      if opt in ("--unsafe_absolute_includes",):
523        basics.opt_unsafe_absolute_includes = True
524      if opt in ("--no_force_dirs",):
525        basics.opt_no_force_dirs = True
526      if opt in ("-s", "--statistics"):
527        basics.opt_statistics = True
528      if opt in ("-t", "--time"):
529        basics.opt_print_times = True
530      if opt in ("-v", "--verify"):
531        basics.opt_verify = True
532      if opt in ("-w", "--write_include_closure"):
533        basics.opt_write_include_closure = True
534      if opt in ("-x", "--exact_analysis"):
535        basics.opt_exact_include_analysis = True
536    except ValueError:
537      Usage()
538      sys.exit(1)
539  # We must have a port!
540  if not include_server_port:
541    print("INCLUDE_SERVER_PORT not provided. Aborting.", file=sys.stderr)
542    print("-------------------------------------------", "\n", file=sys.stderr)
543    Usage()
544    sys.exit(1)
545  return (include_server_port, pid_file)
546
547
548def _PrintTimes(times_at_start, times_at_fork, times_child):
549  """Print elapsed, user, system, and user + system times."""
550  # The os.times format stores user time in positions 0 and 2 (for parent and
551  # children, resp.) Similarly, system time is stored in positions 1 and
552  # 3. Elapsed time is in position 4. Elapsed time is measured relative to some
553  # epoch whereas user and system time are 0 at the time of process creation.
554  total_u = (times_at_fork[0] + times_at_fork[2]
555             + times_child[0] + times_child[2])
556  total_s = (times_at_fork[1] + times_at_fork[3]
557             + times_child[1] + times_child[1])
558  total_cpu = total_u + total_s
559  total_e = times_child[4] - times_at_start[4]
560  print("Include server timing. ", sys.stderr)
561  print("Elapsed: %3.1fs User: %3.1fs System: %3.1fs User + System: %3.1fs" %
562    (total_e, total_u, total_s, total_cpu), file=sys.stderr)
563
564
565class _IncludeServerPortReady(object):
566  """A simple semaphore for forked processes.
567
568   The implementation uses an unnamed pipe."""
569
570  def __init__(self):
571    """Constructor.
572
573    Should be called before fork.
574    """
575    (self.read_fd, self.write_fd) = os.pipe()
576
577  def Acquire(self):
578    """Acquire the semaphore after fork;  blocks until a call of Release."""
579    if os.read(self.read_fd, 1) != b'\n':
580      sys.exit("Include server: _IncludeServerPortReady.Acquire failed.")
581
582  def Release(self):
583    """Release the semaphore after fork."""
584    if os.write(self.write_fd, b'\n') != 1:
585      sys.exit("Include server: _IncludeServerPortReady.Release failed.")
586
587
588def _SetUp(include_server_port):
589  """Setup include_analyzer and socket server.
590
591  Returns: (include_analyzer, server)"""
592
593  try:
594    os.unlink(include_server_port)
595  except (IOError, OSError):
596    pass  # this would be expected, the port provided should not exist
597
598  if os.sep != '/':
599    sys.exit("Expected '/' as separator in filepaths.")
600
601  client_root_keeper = basics.ClientRootKeeper()
602  # Clean out any junk left over from prior runs.
603  client_root_keeper.CleanOutOthers()
604
605  Debug(DEBUG_TRACE, "Starting socketserver %s" % include_server_port)
606
607  # Create the analyser.
608  include_analyzer = (
609      include_analyzer_memoizing_node.IncludeAnalyzerMemoizingNode(
610           client_root_keeper,
611           basics.opt_stat_reset_triggers))
612  include_analyzer.email_sender = _EmailSender()
613
614  # Wrap it inside a handler that is a part of a UnixStreamServer.
615  server = Queuingsocketserver(
616    include_server_port,
617    # Now, produce a StreamRequestHandler subclass whose new objects has
618    # a handler which calls the include_analyzer just made.
619    DistccIncludeHandlerGenerator(include_analyzer))
620
621  return (include_analyzer, server)
622
623
624def _CleanOut(include_analyzer, include_server_port):
625  """Prepare shutdown by cleaning out files and unlinking port."""
626  if include_analyzer and include_analyzer.client_root_keeper:
627    include_analyzer.client_root_keeper.CleanOutClientRoots()
628  try:
629    os.unlink(include_server_port)
630  except OSError:
631    pass
632
633
634def Main():
635  """Parse command line, fork, and start stream request handler."""
636  # Remember the time spent in the parent.
637  times_at_start = os.times()
638  include_server_port, pid_file = _ParseCommandLineOptions()
639  # Get locking mechanism.
640  include_server_port_ready = _IncludeServerPortReady()
641  # Now spawn child so that parent can exit immediately after writing
642  # the process id of child to the pid file.
643  times_at_fork = os.times()
644  pid = os.fork()
645  if pid != 0:
646    # In parent.
647    #
648    if pid_file:
649      pid_file_fd = open(pid_file, "w")
650      print(pid, file=pid_file_fd)
651      pid_file_fd.close()
652    # Just run to completion now -- after making sure that child is ready.
653    include_server_port_ready.Acquire()
654    # concerned.
655  else:
656    # In child.
657    #
658    # We call _Setup only now, because the process id, used in naming the client
659    # root, must be that of this process, not that of the parent process. See
660    # _CleanOutOthers for the importance of the process id.
661    (include_analyzer, server) = _SetUp(include_server_port)
662    include_server_port_ready.Release()
663    try:
664      try:
665        gc.set_threshold(basics.GC_THRESHOLD)
666        # Use commented-out line below to have a message printed for each
667        # collection.
668        # gc.set_debug(gc.DEBUG_STATS + gc.DEBUG_COLLECTABLE)
669        server.serve_forever()
670      except KeyboardInterrupt:
671        print("Include server: keyboard interrupt, quitting after cleaning up.",
672            file=sys.stderr)
673        _CleanOut(include_analyzer, include_server_port)
674      except SignalSIGTERM:
675        Debug(DEBUG_TRACE, "Include server shutting down.")
676        _CleanOut(include_analyzer, include_server_port)
677      except:
678        print("Include server: exception occurred, quitting after cleaning up.",
679                file=sys.stderr)
680        _PrintStackTrace(sys.stderr)
681        _CleanOut(include_analyzer, include_server_port)
682        raise # reraise exception
683    finally:
684      if basics.opt_print_times:
685        _PrintTimes(times_at_start, times_at_fork, os.times())
686
687
688if __name__ == "__main__":
689  # Treat SIGTERM (the default of kill) as Ctrl-C.
690  signal.signal(signal.SIGTERM, basics.RaiseSignalSIGTERM)
691  Main()
692