We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Nailgun processes that fail to be spawned don't explain why
If a nailgun process fails to start, the message that's presented is 

```
Failed to read nailgun output after 10.0 seconds!
```

The actual error message is usually found in the tools logs, which are in `.pants.d/ng/<tool-name>/{stdout,stderr}`.

A workaround that doesn't involve looking up the logs is to turn off nailgun. Then the message is presented directly.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/python/pants/java/nailgun_executor.py`
Content:
```
1 # coding=utf-8
2 # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
3 # Licensed under the Apache License, Version 2.0 (see LICENSE).
4 
5 from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
6                         unicode_literals, with_statement)
7 
8 import hashlib
9 import logging
10 import os
11 import re
12 import select
13 import threading
14 import time
15 from contextlib import closing
16 
17 from six import string_types
18 from twitter.common.collections import maybe_list
19 
20 from pants.base.build_environment import get_buildroot
21 from pants.java.executor import Executor, SubprocessExecutor
22 from pants.java.nailgun_client import NailgunClient
23 from pants.pantsd.process_manager import FingerprintedProcessManager, ProcessGroup
24 from pants.util.dirutil import safe_file_dump, safe_open
25 
26 
27 logger = logging.getLogger(__name__)
28 
29 
30 class NailgunProcessGroup(ProcessGroup):
31   _NAILGUN_KILL_LOCK = threading.Lock()
32 
33   def __init__(self, metadata_base_dir=None):
34     super(NailgunProcessGroup, self).__init__(name='nailgun', metadata_base_dir=metadata_base_dir)
35     # TODO: this should enumerate the .pids dir first, then fallback to ps enumeration (& warn).
36 
37   def _iter_nailgun_instances(self, everywhere=False):
38     def predicate(proc):
39       if proc.name() == NailgunExecutor._PROCESS_NAME:
40         if not everywhere:
41           return NailgunExecutor._PANTS_NG_BUILDROOT_ARG in proc.cmdline()
42         else:
43           return any(arg.startswith(NailgunExecutor._PANTS_NG_ARG_PREFIX) for arg in proc.cmdline())
44 
45     return self.iter_instances(predicate)
46 
47   def killall(self, everywhere=False):
48     """Kills all nailgun servers started by pants.
49 
50        :param bool everywhere: If ``True``, kills all pants-started nailguns on this machine;
51                                otherwise restricts the nailguns killed to those started for the
52                                current build root.
53     """
54     with self._NAILGUN_KILL_LOCK:
55       for proc in self._iter_nailgun_instances(everywhere):
56         logger.info('killing nailgun server pid={pid}'.format(pid=proc.pid))
57         proc.terminate()
58 
59 
60 # TODO: Once we integrate standard logging into our reporting framework, we can consider making
61 # some of the log.debug() below into log.info(). Right now it just looks wrong on the console.
62 class NailgunExecutor(Executor, FingerprintedProcessManager):
63   """Executes java programs by launching them in nailgun server.
64 
65      If a nailgun is not available for a given set of jvm args and classpath, one is launched and
66      re-used for the given jvm args and classpath on subsequent runs.
67   """
68 
69   # 'NGServer 0.9.1 started on 127.0.0.1, port 53785.'
70   _NG_PORT_REGEX = re.compile(r'.*\s+port\s+(\d+)\.$')
71 
72   # Used to identify if we own a given nailgun server.
73   FINGERPRINT_CMD_KEY = b'-Dpants.nailgun.fingerprint'
74   _PANTS_NG_ARG_PREFIX = b'-Dpants.buildroot'
75   _PANTS_OWNER_ARG_PREFIX = b'-Dpants.nailgun.owner'
76   _PANTS_NG_BUILDROOT_ARG = '='.join((_PANTS_NG_ARG_PREFIX, get_buildroot()))
77 
78   _NAILGUN_SPAWN_LOCK = threading.Lock()
79   _SELECT_WAIT = 1
80   _PROCESS_NAME = b'java'
81 
82   def __init__(self, identity, workdir, nailgun_classpath, distribution,
83                connect_timeout=10, connect_attempts=5, metadata_base_dir=None):
84     Executor.__init__(self, distribution=distribution)
85     FingerprintedProcessManager.__init__(self,
86                                          name=identity,
87                                          process_name=self._PROCESS_NAME,
88                                          metadata_base_dir=metadata_base_dir)
89 
90     if not isinstance(workdir, string_types):
91       raise ValueError('Workdir must be a path string, not: {workdir}'.format(workdir=workdir))
92 
93     self._identity = identity
94     self._workdir = workdir
95     self._ng_stdout = os.path.join(workdir, 'stdout')
96     self._ng_stderr = os.path.join(workdir, 'stderr')
97     self._nailgun_classpath = maybe_list(nailgun_classpath)
98     self._connect_timeout = connect_timeout
99     self._connect_attempts = connect_attempts
100 
101   def __str__(self):
102     return 'NailgunExecutor({identity}, dist={dist}, pid={pid} socket={socket})'.format(
103       identity=self._identity, dist=self._distribution, pid=self.pid, socket=self.socket)
104 
105   def _create_owner_arg(self, workdir):
106     # Currently the owner is identified via the full path to the workdir.
107     return '='.join((self._PANTS_OWNER_ARG_PREFIX, workdir))
108 
109   def _create_fingerprint_arg(self, fingerprint):
110     return '='.join((self.FINGERPRINT_CMD_KEY, fingerprint))
111 
112   @staticmethod
113   def _fingerprint(jvm_options, classpath, java_version):
114     """Compute a fingerprint for this invocation of a Java task.
115 
116        :param list jvm_options: JVM options passed to the java invocation
117        :param list classpath: The -cp arguments passed to the java invocation
118        :param Revision java_version: return value from Distribution.version()
119        :return: a hexstring representing a fingerprint of the java invocation
120     """
121     digest = hashlib.sha1()
122     # TODO(John Sirois): hash classpath contents?
123     [digest.update(item) for item in (''.join(sorted(jvm_options)),
124                                       ''.join(sorted(classpath)),
125                                       repr(java_version))]
126     return digest.hexdigest()
127 
128   def _runner(self, classpath, main, jvm_options, args, cwd=None):
129     """Runner factory. Called via Executor.execute()."""
130     command = self._create_command(classpath, main, jvm_options, args)
131 
132     class Runner(self.Runner):
133       @property
134       def executor(this):
135         return self
136 
137       @property
138       def command(self):
139         return list(command)
140 
141       def run(this, stdout=None, stderr=None, stdin=None, cwd=None):
142         nailgun = self._get_nailgun_client(jvm_options, classpath, stdout, stderr, stdin)
143         try:
144           logger.debug('Executing via {ng_desc}: {cmd}'.format(ng_desc=nailgun, cmd=this.cmd))
145           return nailgun.execute(main, cwd, *args)
146         except nailgun.NailgunError as e:
147           self.terminate()
148           raise self.Error('Problem launching via {ng_desc} command {main} {args}: {msg}'
149                            .format(ng_desc=nailgun, main=main, args=' '.join(args), msg=e))
150 
151     return Runner()
152 
153   def _check_nailgun_state(self, new_fingerprint):
154     running = self.is_alive()
155     updated = self.needs_restart(new_fingerprint) or self.cmd != self._distribution.java
156     logging.debug('Nailgun {nailgun} state: updated={up!s} running={run!s} fingerprint={old_fp} '
157                   'new_fingerprint={new_fp} distribution={old_dist} new_distribution={new_dist}'
158                   .format(nailgun=self._identity, up=updated, run=running,
159                           old_fp=self.fingerprint, new_fp=new_fingerprint,
160                           old_dist=self.cmd, new_dist=self._distribution.java))
161     return running, updated
162 
163   def _get_nailgun_client(self, jvm_options, classpath, stdout, stderr, stdin):
164     """This (somewhat unfortunately) is the main entrypoint to this class via the Runner. It handles
165        creation of the running nailgun server as well as creation of the client."""
166     classpath = self._nailgun_classpath + classpath
167     new_fingerprint = self._fingerprint(jvm_options, classpath, self._distribution.version)
168 
169     with self._NAILGUN_SPAWN_LOCK:
170       running, updated = self._check_nailgun_state(new_fingerprint)
171 
172       if running and updated:
173         logger.debug('Found running nailgun server that needs updating, killing {server}'
174                      .format(server=self._identity))
175         self.terminate()
176 
177       if (not running) or (running and updated):
178         return self._spawn_nailgun_server(new_fingerprint, jvm_options, classpath, stdout, stderr, stdin)
179 
180     return self._create_ngclient(self.socket, stdout, stderr, stdin)
181 
182   def _await_socket(self, timeout):
183     """Blocks for the nailgun subprocess to bind and emit a listening port in the nailgun stdout."""
184     with safe_open(self._ng_stdout, 'r') as ng_stdout:
185       start_time = time.time()
186       while 1:
187         readable, _, _ = select.select([ng_stdout], [], [], self._SELECT_WAIT)
188         if readable:
189           line = ng_stdout.readline()                          # TODO: address deadlock risk here.
190           try:
191             return self._NG_PORT_REGEX.match(line).group(1)
192           except AttributeError:
193             pass
194 
195         if (time.time() - start_time) > timeout:
196           raise NailgunClient.NailgunError(
197             'Failed to read nailgun output after {sec} seconds!'.format(sec=timeout))
198 
199   def _create_ngclient(self, port, stdout, stderr, stdin):
200     return NailgunClient(port=port, ins=stdin, out=stdout, err=stderr, workdir=get_buildroot())
201 
202   def ensure_connectable(self, nailgun):
203     """Ensures that a nailgun client is connectable or raises NailgunError."""
204     attempt_count = 1
205     while 1:
206       try:
207         with closing(nailgun.try_connect()) as sock:
208           logger.debug('Verified new ng server is connectable at {}'.format(sock.getpeername()))
209           return
210       except nailgun.NailgunConnectionError:
211         if attempt_count >= self._connect_attempts:
212           logger.debug('Failed to connect to ng after {} attempts'.format(self._connect_attempts))
213           raise     # Re-raise the NailgunConnectionError which provides more context to the user.
214 
215       attempt_count += 1
216       time.sleep(self.WAIT_INTERVAL_SEC)
217 
218   def _spawn_nailgun_server(self, fingerprint, jvm_options, classpath, stdout, stderr, stdin):
219     """Synchronously spawn a new nailgun server."""
220     # Truncate the nailguns stdout & stderr.
221     safe_file_dump(self._ng_stdout, '')
222     safe_file_dump(self._ng_stderr, '')
223 
224     jvm_options = jvm_options + [self._PANTS_NG_BUILDROOT_ARG,
225                                  self._create_owner_arg(self._workdir),
226                                  self._create_fingerprint_arg(fingerprint)]
227 
228     post_fork_child_opts = dict(fingerprint=fingerprint,
229                                 jvm_options=jvm_options,
230                                 classpath=classpath,
231                                 stdout=stdout,
232                                 stderr=stderr)
233 
234     logger.debug('Spawning nailgun server {i} with fingerprint={f}, jvm_options={j}, classpath={cp}'
235                  .format(i=self._identity, f=fingerprint, j=jvm_options, cp=classpath))
236 
237     self.daemon_spawn(post_fork_child_opts=post_fork_child_opts)
238 
239     # Wait for and write the port information in the parent so we can bail on exception/timeout.
240     self.await_pid(self._connect_timeout)
241     self.write_socket(self._await_socket(self._connect_timeout))
242 
243     logger.debug('Spawned nailgun server {i} with fingerprint={f}, pid={pid} port={port}'
244                  .format(i=self._identity, f=fingerprint, pid=self.pid, port=self.socket))
245 
246     client = self._create_ngclient(self.socket, stdout, stderr, stdin)
247     self.ensure_connectable(client)
248 
249     return client
250 
251   def _check_process_buildroot(self, process):
252     """Matches only processes started from the current buildroot."""
253     return self._PANTS_NG_BUILDROOT_ARG in process.cmdline()
254 
255   def is_alive(self):
256     """A ProcessManager.is_alive() override that ensures buildroot flags are present in the process
257     command line arguments."""
258     return super(NailgunExecutor, self).is_alive(self._check_process_buildroot)
259 
260   def post_fork_child(self, fingerprint, jvm_options, classpath, stdout, stderr):
261     """Post-fork() child callback for ProcessManager.daemon_spawn()."""
262     java = SubprocessExecutor(self._distribution)
263 
264     subproc = java.spawn(classpath=classpath,
265                          main='com.martiansoftware.nailgun.NGServer',
266                          jvm_options=jvm_options,
267                          args=[':0'],
268                          stdin=safe_open('/dev/null', 'r'),
269                          stdout=safe_open(self._ng_stdout, 'w'),
270                          stderr=safe_open(self._ng_stderr, 'w'),
271                          close_fds=True)
272 
273     self.write_pid(subproc.pid)
274
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/python/pants/java/nailgun_executor.py b/src/python/pants/java/nailgun_executor.py
--- a/src/python/pants/java/nailgun_executor.py
+++ b/src/python/pants/java/nailgun_executor.py
@@ -21,7 +21,7 @@
 from pants.java.executor import Executor, SubprocessExecutor
 from pants.java.nailgun_client import NailgunClient
 from pants.pantsd.process_manager import FingerprintedProcessManager, ProcessGroup
-from pants.util.dirutil import safe_file_dump, safe_open
+from pants.util.dirutil import read_file, safe_file_dump, safe_open
 
 
 logger = logging.getLogger(__name__)
@@ -183,6 +183,7 @@
     """Blocks for the nailgun subprocess to bind and emit a listening port in the nailgun stdout."""
     with safe_open(self._ng_stdout, 'r') as ng_stdout:
       start_time = time.time()
+      accumulated_stdout = ''
       while 1:
         readable, _, _ = select.select([ng_stdout], [], [], self._SELECT_WAIT)
         if readable:
@@ -191,10 +192,18 @@
             return self._NG_PORT_REGEX.match(line).group(1)
           except AttributeError:
             pass
+          accumulated_stdout += line
 
         if (time.time() - start_time) > timeout:
+          stderr = read_file(self._ng_stderr)
           raise NailgunClient.NailgunError(
-            'Failed to read nailgun output after {sec} seconds!'.format(sec=timeout))
+            'Failed to read nailgun output after {sec} seconds!\n'
+            'Stdout:\n{stdout}\nStderr:\n{stderr}'.format(
+              sec=timeout,
+              stdout=accumulated_stdout,
+              stderr=stderr,
+            )
+          )
 
   def _create_ngclient(self, port, stdout, stderr, stdin):
     return NailgunClient(port=port, ins=stdin, out=stdout, err=stderr, workdir=get_buildroot())

{"golden_diff": "diff --git a/src/python/pants/java/nailgun_executor.py b/src/python/pants/java/nailgun_executor.py\n--- a/src/python/pants/java/nailgun_executor.py\n+++ b/src/python/pants/java/nailgun_executor.py\n@@ -21,7 +21,7 @@\n from pants.java.executor import Executor, SubprocessExecutor\n from pants.java.nailgun_client import NailgunClient\n from pants.pantsd.process_manager import FingerprintedProcessManager, ProcessGroup\n-from pants.util.dirutil import safe_file_dump, safe_open\n+from pants.util.dirutil import read_file, safe_file_dump, safe_open\n \n \n logger = logging.getLogger(__name__)\n@@ -183,6 +183,7 @@\n     \"\"\"Blocks for the nailgun subprocess to bind and emit a listening port in the nailgun stdout.\"\"\"\n     with safe_open(self._ng_stdout, 'r') as ng_stdout:\n       start_time = time.time()\n+      accumulated_stdout = ''\n       while 1:\n         readable, _, _ = select.select([ng_stdout], [], [], self._SELECT_WAIT)\n         if readable:\n@@ -191,10 +192,18 @@\n             return self._NG_PORT_REGEX.match(line).group(1)\n           except AttributeError:\n             pass\n+          accumulated_stdout += line\n \n         if (time.time() - start_time) > timeout:\n+          stderr = read_file(self._ng_stderr)\n           raise NailgunClient.NailgunError(\n-            'Failed to read nailgun output after {sec} seconds!'.format(sec=timeout))\n+            'Failed to read nailgun output after {sec} seconds!\\n'\n+            'Stdout:\\n{stdout}\\nStderr:\\n{stderr}'.format(\n+              sec=timeout,\n+              stdout=accumulated_stdout,\n+              stderr=stderr,\n+            )\n+          )\n \n   def _create_ngclient(self, port, stdout, stderr, stdin):\n     return NailgunClient(port=port, ins=stdin, out=stdout, err=stderr, workdir=get_buildroot())\n", "issue": "Nailgun processes that fail to be spawned don't explain why\nIf a nailgun process fails to start, the message that's presented is \r\n\r\n```\r\nFailed to read nailgun output after 10.0 seconds!\r\n```\r\n\r\nThe actual error message is usually found in the tools logs, which are in `.pants.d/ng/<tool-name>/{stdout,stderr}`.\r\n\r\nA workaround that doesn't involve looking up the logs is to turn off nailgun. Then the message is presented directly.\n", "before_files": [{"content": "# coding=utf-8\n# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport hashlib\nimport logging\nimport os\nimport re\nimport select\nimport threading\nimport time\nfrom contextlib import closing\n\nfrom six import string_types\nfrom twitter.common.collections import maybe_list\n\nfrom pants.base.build_environment import get_buildroot\nfrom pants.java.executor import Executor, SubprocessExecutor\nfrom pants.java.nailgun_client import NailgunClient\nfrom pants.pantsd.process_manager import FingerprintedProcessManager, ProcessGroup\nfrom pants.util.dirutil import safe_file_dump, safe_open\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NailgunProcessGroup(ProcessGroup):\n  _NAILGUN_KILL_LOCK = threading.Lock()\n\n  def __init__(self, metadata_base_dir=None):\n    super(NailgunProcessGroup, self).__init__(name='nailgun', metadata_base_dir=metadata_base_dir)\n    # TODO: this should enumerate the .pids dir first, then fallback to ps enumeration (& warn).\n\n  def _iter_nailgun_instances(self, everywhere=False):\n    def predicate(proc):\n      if proc.name() == NailgunExecutor._PROCESS_NAME:\n        if not everywhere:\n          return NailgunExecutor._PANTS_NG_BUILDROOT_ARG in proc.cmdline()\n        else:\n          return any(arg.startswith(NailgunExecutor._PANTS_NG_ARG_PREFIX) for arg in proc.cmdline())\n\n    return self.iter_instances(predicate)\n\n  def killall(self, everywhere=False):\n    \"\"\"Kills all nailgun servers started by pants.\n\n       :param bool everywhere: If ``True``, kills all pants-started nailguns on this machine;\n                               otherwise restricts the nailguns killed to those started for the\n                               current build root.\n    \"\"\"\n    with self._NAILGUN_KILL_LOCK:\n      for proc in self._iter_nailgun_instances(everywhere):\n        logger.info('killing nailgun server pid={pid}'.format(pid=proc.pid))\n        proc.terminate()\n\n\n# TODO: Once we integrate standard logging into our reporting framework, we can consider making\n# some of the log.debug() below into log.info(). Right now it just looks wrong on the console.\nclass NailgunExecutor(Executor, FingerprintedProcessManager):\n  \"\"\"Executes java programs by launching them in nailgun server.\n\n     If a nailgun is not available for a given set of jvm args and classpath, one is launched and\n     re-used for the given jvm args and classpath on subsequent runs.\n  \"\"\"\n\n  # 'NGServer 0.9.1 started on 127.0.0.1, port 53785.'\n  _NG_PORT_REGEX = re.compile(r'.*\\s+port\\s+(\\d+)\\.$')\n\n  # Used to identify if we own a given nailgun server.\n  FINGERPRINT_CMD_KEY = b'-Dpants.nailgun.fingerprint'\n  _PANTS_NG_ARG_PREFIX = b'-Dpants.buildroot'\n  _PANTS_OWNER_ARG_PREFIX = b'-Dpants.nailgun.owner'\n  _PANTS_NG_BUILDROOT_ARG = '='.join((_PANTS_NG_ARG_PREFIX, get_buildroot()))\n\n  _NAILGUN_SPAWN_LOCK = threading.Lock()\n  _SELECT_WAIT = 1\n  _PROCESS_NAME = b'java'\n\n  def __init__(self, identity, workdir, nailgun_classpath, distribution,\n               connect_timeout=10, connect_attempts=5, metadata_base_dir=None):\n    Executor.__init__(self, distribution=distribution)\n    FingerprintedProcessManager.__init__(self,\n                                         name=identity,\n                                         process_name=self._PROCESS_NAME,\n                                         metadata_base_dir=metadata_base_dir)\n\n    if not isinstance(workdir, string_types):\n      raise ValueError('Workdir must be a path string, not: {workdir}'.format(workdir=workdir))\n\n    self._identity = identity\n    self._workdir = workdir\n    self._ng_stdout = os.path.join(workdir, 'stdout')\n    self._ng_stderr = os.path.join(workdir, 'stderr')\n    self._nailgun_classpath = maybe_list(nailgun_classpath)\n    self._connect_timeout = connect_timeout\n    self._connect_attempts = connect_attempts\n\n  def __str__(self):\n    return 'NailgunExecutor({identity}, dist={dist}, pid={pid} socket={socket})'.format(\n      identity=self._identity, dist=self._distribution, pid=self.pid, socket=self.socket)\n\n  def _create_owner_arg(self, workdir):\n    # Currently the owner is identified via the full path to the workdir.\n    return '='.join((self._PANTS_OWNER_ARG_PREFIX, workdir))\n\n  def _create_fingerprint_arg(self, fingerprint):\n    return '='.join((self.FINGERPRINT_CMD_KEY, fingerprint))\n\n  @staticmethod\n  def _fingerprint(jvm_options, classpath, java_version):\n    \"\"\"Compute a fingerprint for this invocation of a Java task.\n\n       :param list jvm_options: JVM options passed to the java invocation\n       :param list classpath: The -cp arguments passed to the java invocation\n       :param Revision java_version: return value from Distribution.version()\n       :return: a hexstring representing a fingerprint of the java invocation\n    \"\"\"\n    digest = hashlib.sha1()\n    # TODO(John Sirois): hash classpath contents?\n    [digest.update(item) for item in (''.join(sorted(jvm_options)),\n                                      ''.join(sorted(classpath)),\n                                      repr(java_version))]\n    return digest.hexdigest()\n\n  def _runner(self, classpath, main, jvm_options, args, cwd=None):\n    \"\"\"Runner factory. Called via Executor.execute().\"\"\"\n    command = self._create_command(classpath, main, jvm_options, args)\n\n    class Runner(self.Runner):\n      @property\n      def executor(this):\n        return self\n\n      @property\n      def command(self):\n        return list(command)\n\n      def run(this, stdout=None, stderr=None, stdin=None, cwd=None):\n        nailgun = self._get_nailgun_client(jvm_options, classpath, stdout, stderr, stdin)\n        try:\n          logger.debug('Executing via {ng_desc}: {cmd}'.format(ng_desc=nailgun, cmd=this.cmd))\n          return nailgun.execute(main, cwd, *args)\n        except nailgun.NailgunError as e:\n          self.terminate()\n          raise self.Error('Problem launching via {ng_desc} command {main} {args}: {msg}'\n                           .format(ng_desc=nailgun, main=main, args=' '.join(args), msg=e))\n\n    return Runner()\n\n  def _check_nailgun_state(self, new_fingerprint):\n    running = self.is_alive()\n    updated = self.needs_restart(new_fingerprint) or self.cmd != self._distribution.java\n    logging.debug('Nailgun {nailgun} state: updated={up!s} running={run!s} fingerprint={old_fp} '\n                  'new_fingerprint={new_fp} distribution={old_dist} new_distribution={new_dist}'\n                  .format(nailgun=self._identity, up=updated, run=running,\n                          old_fp=self.fingerprint, new_fp=new_fingerprint,\n                          old_dist=self.cmd, new_dist=self._distribution.java))\n    return running, updated\n\n  def _get_nailgun_client(self, jvm_options, classpath, stdout, stderr, stdin):\n    \"\"\"This (somewhat unfortunately) is the main entrypoint to this class via the Runner. It handles\n       creation of the running nailgun server as well as creation of the client.\"\"\"\n    classpath = self._nailgun_classpath + classpath\n    new_fingerprint = self._fingerprint(jvm_options, classpath, self._distribution.version)\n\n    with self._NAILGUN_SPAWN_LOCK:\n      running, updated = self._check_nailgun_state(new_fingerprint)\n\n      if running and updated:\n        logger.debug('Found running nailgun server that needs updating, killing {server}'\n                     .format(server=self._identity))\n        self.terminate()\n\n      if (not running) or (running and updated):\n        return self._spawn_nailgun_server(new_fingerprint, jvm_options, classpath, stdout, stderr, stdin)\n\n    return self._create_ngclient(self.socket, stdout, stderr, stdin)\n\n  def _await_socket(self, timeout):\n    \"\"\"Blocks for the nailgun subprocess to bind and emit a listening port in the nailgun stdout.\"\"\"\n    with safe_open(self._ng_stdout, 'r') as ng_stdout:\n      start_time = time.time()\n      while 1:\n        readable, _, _ = select.select([ng_stdout], [], [], self._SELECT_WAIT)\n        if readable:\n          line = ng_stdout.readline()                          # TODO: address deadlock risk here.\n          try:\n            return self._NG_PORT_REGEX.match(line).group(1)\n          except AttributeError:\n            pass\n\n        if (time.time() - start_time) > timeout:\n          raise NailgunClient.NailgunError(\n            'Failed to read nailgun output after {sec} seconds!'.format(sec=timeout))\n\n  def _create_ngclient(self, port, stdout, stderr, stdin):\n    return NailgunClient(port=port, ins=stdin, out=stdout, err=stderr, workdir=get_buildroot())\n\n  def ensure_connectable(self, nailgun):\n    \"\"\"Ensures that a nailgun client is connectable or raises NailgunError.\"\"\"\n    attempt_count = 1\n    while 1:\n      try:\n        with closing(nailgun.try_connect()) as sock:\n          logger.debug('Verified new ng server is connectable at {}'.format(sock.getpeername()))\n          return\n      except nailgun.NailgunConnectionError:\n        if attempt_count >= self._connect_attempts:\n          logger.debug('Failed to connect to ng after {} attempts'.format(self._connect_attempts))\n          raise     # Re-raise the NailgunConnectionError which provides more context to the user.\n\n      attempt_count += 1\n      time.sleep(self.WAIT_INTERVAL_SEC)\n\n  def _spawn_nailgun_server(self, fingerprint, jvm_options, classpath, stdout, stderr, stdin):\n    \"\"\"Synchronously spawn a new nailgun server.\"\"\"\n    # Truncate the nailguns stdout & stderr.\n    safe_file_dump(self._ng_stdout, '')\n    safe_file_dump(self._ng_stderr, '')\n\n    jvm_options = jvm_options + [self._PANTS_NG_BUILDROOT_ARG,\n                                 self._create_owner_arg(self._workdir),\n                                 self._create_fingerprint_arg(fingerprint)]\n\n    post_fork_child_opts = dict(fingerprint=fingerprint,\n                                jvm_options=jvm_options,\n                                classpath=classpath,\n                                stdout=stdout,\n                                stderr=stderr)\n\n    logger.debug('Spawning nailgun server {i} with fingerprint={f}, jvm_options={j}, classpath={cp}'\n                 .format(i=self._identity, f=fingerprint, j=jvm_options, cp=classpath))\n\n    self.daemon_spawn(post_fork_child_opts=post_fork_child_opts)\n\n    # Wait for and write the port information in the parent so we can bail on exception/timeout.\n    self.await_pid(self._connect_timeout)\n    self.write_socket(self._await_socket(self._connect_timeout))\n\n    logger.debug('Spawned nailgun server {i} with fingerprint={f}, pid={pid} port={port}'\n                 .format(i=self._identity, f=fingerprint, pid=self.pid, port=self.socket))\n\n    client = self._create_ngclient(self.socket, stdout, stderr, stdin)\n    self.ensure_connectable(client)\n\n    return client\n\n  def _check_process_buildroot(self, process):\n    \"\"\"Matches only processes started from the current buildroot.\"\"\"\n    return self._PANTS_NG_BUILDROOT_ARG in process.cmdline()\n\n  def is_alive(self):\n    \"\"\"A ProcessManager.is_alive() override that ensures buildroot flags are present in the process\n    command line arguments.\"\"\"\n    return super(NailgunExecutor, self).is_alive(self._check_process_buildroot)\n\n  def post_fork_child(self, fingerprint, jvm_options, classpath, stdout, stderr):\n    \"\"\"Post-fork() child callback for ProcessManager.daemon_spawn().\"\"\"\n    java = SubprocessExecutor(self._distribution)\n\n    subproc = java.spawn(classpath=classpath,\n                         main='com.martiansoftware.nailgun.NGServer',\n                         jvm_options=jvm_options,\n                         args=[':0'],\n                         stdin=safe_open('/dev/null', 'r'),\n                         stdout=safe_open(self._ng_stdout, 'w'),\n                         stderr=safe_open(self._ng_stderr, 'w'),\n                         close_fds=True)\n\n    self.write_pid(subproc.pid)\n", "path": "src/python/pants/java/nailgun_executor.py"}], "after_files": [{"content": "# coding=utf-8\n# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport hashlib\nimport logging\nimport os\nimport re\nimport select\nimport threading\nimport time\nfrom contextlib import closing\n\nfrom six import string_types\nfrom twitter.common.collections import maybe_list\n\nfrom pants.base.build_environment import get_buildroot\nfrom pants.java.executor import Executor, SubprocessExecutor\nfrom pants.java.nailgun_client import NailgunClient\nfrom pants.pantsd.process_manager import FingerprintedProcessManager, ProcessGroup\nfrom pants.util.dirutil import read_file, safe_file_dump, safe_open\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NailgunProcessGroup(ProcessGroup):\n  _NAILGUN_KILL_LOCK = threading.Lock()\n\n  def __init__(self, metadata_base_dir=None):\n    super(NailgunProcessGroup, self).__init__(name='nailgun', metadata_base_dir=metadata_base_dir)\n    # TODO: this should enumerate the .pids dir first, then fallback to ps enumeration (& warn).\n\n  def _iter_nailgun_instances(self, everywhere=False):\n    def predicate(proc):\n      if proc.name() == NailgunExecutor._PROCESS_NAME:\n        if not everywhere:\n          return NailgunExecutor._PANTS_NG_BUILDROOT_ARG in proc.cmdline()\n        else:\n          return any(arg.startswith(NailgunExecutor._PANTS_NG_ARG_PREFIX) for arg in proc.cmdline())\n\n    return self.iter_instances(predicate)\n\n  def killall(self, everywhere=False):\n    \"\"\"Kills all nailgun servers started by pants.\n\n       :param bool everywhere: If ``True``, kills all pants-started nailguns on this machine;\n                               otherwise restricts the nailguns killed to those started for the\n                               current build root.\n    \"\"\"\n    with self._NAILGUN_KILL_LOCK:\n      for proc in self._iter_nailgun_instances(everywhere):\n        logger.info('killing nailgun server pid={pid}'.format(pid=proc.pid))\n        proc.terminate()\n\n\n# TODO: Once we integrate standard logging into our reporting framework, we can consider making\n# some of the log.debug() below into log.info(). Right now it just looks wrong on the console.\nclass NailgunExecutor(Executor, FingerprintedProcessManager):\n  \"\"\"Executes java programs by launching them in nailgun server.\n\n     If a nailgun is not available for a given set of jvm args and classpath, one is launched and\n     re-used for the given jvm args and classpath on subsequent runs.\n  \"\"\"\n\n  # 'NGServer 0.9.1 started on 127.0.0.1, port 53785.'\n  _NG_PORT_REGEX = re.compile(r'.*\\s+port\\s+(\\d+)\\.$')\n\n  # Used to identify if we own a given nailgun server.\n  FINGERPRINT_CMD_KEY = b'-Dpants.nailgun.fingerprint'\n  _PANTS_NG_ARG_PREFIX = b'-Dpants.buildroot'\n  _PANTS_OWNER_ARG_PREFIX = b'-Dpants.nailgun.owner'\n  _PANTS_NG_BUILDROOT_ARG = '='.join((_PANTS_NG_ARG_PREFIX, get_buildroot()))\n\n  _NAILGUN_SPAWN_LOCK = threading.Lock()\n  _SELECT_WAIT = 1\n  _PROCESS_NAME = b'java'\n\n  def __init__(self, identity, workdir, nailgun_classpath, distribution,\n               connect_timeout=10, connect_attempts=5, metadata_base_dir=None):\n    Executor.__init__(self, distribution=distribution)\n    FingerprintedProcessManager.__init__(self,\n                                         name=identity,\n                                         process_name=self._PROCESS_NAME,\n                                         metadata_base_dir=metadata_base_dir)\n\n    if not isinstance(workdir, string_types):\n      raise ValueError('Workdir must be a path string, not: {workdir}'.format(workdir=workdir))\n\n    self._identity = identity\n    self._workdir = workdir\n    self._ng_stdout = os.path.join(workdir, 'stdout')\n    self._ng_stderr = os.path.join(workdir, 'stderr')\n    self._nailgun_classpath = maybe_list(nailgun_classpath)\n    self._connect_timeout = connect_timeout\n    self._connect_attempts = connect_attempts\n\n  def __str__(self):\n    return 'NailgunExecutor({identity}, dist={dist}, pid={pid} socket={socket})'.format(\n      identity=self._identity, dist=self._distribution, pid=self.pid, socket=self.socket)\n\n  def _create_owner_arg(self, workdir):\n    # Currently the owner is identified via the full path to the workdir.\n    return '='.join((self._PANTS_OWNER_ARG_PREFIX, workdir))\n\n  def _create_fingerprint_arg(self, fingerprint):\n    return '='.join((self.FINGERPRINT_CMD_KEY, fingerprint))\n\n  @staticmethod\n  def _fingerprint(jvm_options, classpath, java_version):\n    \"\"\"Compute a fingerprint for this invocation of a Java task.\n\n       :param list jvm_options: JVM options passed to the java invocation\n       :param list classpath: The -cp arguments passed to the java invocation\n       :param Revision java_version: return value from Distribution.version()\n       :return: a hexstring representing a fingerprint of the java invocation\n    \"\"\"\n    digest = hashlib.sha1()\n    # TODO(John Sirois): hash classpath contents?\n    [digest.update(item) for item in (''.join(sorted(jvm_options)),\n                                      ''.join(sorted(classpath)),\n                                      repr(java_version))]\n    return digest.hexdigest()\n\n  def _runner(self, classpath, main, jvm_options, args, cwd=None):\n    \"\"\"Runner factory. Called via Executor.execute().\"\"\"\n    command = self._create_command(classpath, main, jvm_options, args)\n\n    class Runner(self.Runner):\n      @property\n      def executor(this):\n        return self\n\n      @property\n      def command(self):\n        return list(command)\n\n      def run(this, stdout=None, stderr=None, stdin=None, cwd=None):\n        nailgun = self._get_nailgun_client(jvm_options, classpath, stdout, stderr, stdin)\n        try:\n          logger.debug('Executing via {ng_desc}: {cmd}'.format(ng_desc=nailgun, cmd=this.cmd))\n          return nailgun.execute(main, cwd, *args)\n        except nailgun.NailgunError as e:\n          self.terminate()\n          raise self.Error('Problem launching via {ng_desc} command {main} {args}: {msg}'\n                           .format(ng_desc=nailgun, main=main, args=' '.join(args), msg=e))\n\n    return Runner()\n\n  def _check_nailgun_state(self, new_fingerprint):\n    running = self.is_alive()\n    updated = self.needs_restart(new_fingerprint) or self.cmd != self._distribution.java\n    logging.debug('Nailgun {nailgun} state: updated={up!s} running={run!s} fingerprint={old_fp} '\n                  'new_fingerprint={new_fp} distribution={old_dist} new_distribution={new_dist}'\n                  .format(nailgun=self._identity, up=updated, run=running,\n                          old_fp=self.fingerprint, new_fp=new_fingerprint,\n                          old_dist=self.cmd, new_dist=self._distribution.java))\n    return running, updated\n\n  def _get_nailgun_client(self, jvm_options, classpath, stdout, stderr, stdin):\n    \"\"\"This (somewhat unfortunately) is the main entrypoint to this class via the Runner. It handles\n       creation of the running nailgun server as well as creation of the client.\"\"\"\n    classpath = self._nailgun_classpath + classpath\n    new_fingerprint = self._fingerprint(jvm_options, classpath, self._distribution.version)\n\n    with self._NAILGUN_SPAWN_LOCK:\n      running, updated = self._check_nailgun_state(new_fingerprint)\n\n      if running and updated:\n        logger.debug('Found running nailgun server that needs updating, killing {server}'\n                     .format(server=self._identity))\n        self.terminate()\n\n      if (not running) or (running and updated):\n        return self._spawn_nailgun_server(new_fingerprint, jvm_options, classpath, stdout, stderr, stdin)\n\n    return self._create_ngclient(self.socket, stdout, stderr, stdin)\n\n  def _await_socket(self, timeout):\n    \"\"\"Blocks for the nailgun subprocess to bind and emit a listening port in the nailgun stdout.\"\"\"\n    with safe_open(self._ng_stdout, 'r') as ng_stdout:\n      start_time = time.time()\n      accumulated_stdout = ''\n      while 1:\n        readable, _, _ = select.select([ng_stdout], [], [], self._SELECT_WAIT)\n        if readable:\n          line = ng_stdout.readline()                          # TODO: address deadlock risk here.\n          try:\n            return self._NG_PORT_REGEX.match(line).group(1)\n          except AttributeError:\n            pass\n          accumulated_stdout += line\n\n        if (time.time() - start_time) > timeout:\n          stderr = read_file(self._ng_stderr)\n          raise NailgunClient.NailgunError(\n            'Failed to read nailgun output after {sec} seconds!\\n'\n            'Stdout:\\n{stdout}\\nStderr:\\n{stderr}'.format(\n              sec=timeout,\n              stdout=accumulated_stdout,\n              stderr=stderr,\n            )\n          )\n\n  def _create_ngclient(self, port, stdout, stderr, stdin):\n    return NailgunClient(port=port, ins=stdin, out=stdout, err=stderr, workdir=get_buildroot())\n\n  def ensure_connectable(self, nailgun):\n    \"\"\"Ensures that a nailgun client is connectable or raises NailgunError.\"\"\"\n    attempt_count = 1\n    while 1:\n      try:\n        with closing(nailgun.try_connect()) as sock:\n          logger.debug('Verified new ng server is connectable at {}'.format(sock.getpeername()))\n          return\n      except nailgun.NailgunConnectionError:\n        if attempt_count >= self._connect_attempts:\n          logger.debug('Failed to connect to ng after {} attempts'.format(self._connect_attempts))\n          raise     # Re-raise the NailgunConnectionError which provides more context to the user.\n\n      attempt_count += 1\n      time.sleep(self.WAIT_INTERVAL_SEC)\n\n  def _spawn_nailgun_server(self, fingerprint, jvm_options, classpath, stdout, stderr, stdin):\n    \"\"\"Synchronously spawn a new nailgun server.\"\"\"\n    # Truncate the nailguns stdout & stderr.\n    safe_file_dump(self._ng_stdout, '')\n    safe_file_dump(self._ng_stderr, '')\n\n    jvm_options = jvm_options + [self._PANTS_NG_BUILDROOT_ARG,\n                                 self._create_owner_arg(self._workdir),\n                                 self._create_fingerprint_arg(fingerprint)]\n\n    post_fork_child_opts = dict(fingerprint=fingerprint,\n                                jvm_options=jvm_options,\n                                classpath=classpath,\n                                stdout=stdout,\n                                stderr=stderr)\n\n    logger.debug('Spawning nailgun server {i} with fingerprint={f}, jvm_options={j}, classpath={cp}'\n                 .format(i=self._identity, f=fingerprint, j=jvm_options, cp=classpath))\n\n    self.daemon_spawn(post_fork_child_opts=post_fork_child_opts)\n\n    # Wait for and write the port information in the parent so we can bail on exception/timeout.\n    self.await_pid(self._connect_timeout)\n    self.write_socket(self._await_socket(self._connect_timeout))\n\n    logger.debug('Spawned nailgun server {i} with fingerprint={f}, pid={pid} port={port}'\n                 .format(i=self._identity, f=fingerprint, pid=self.pid, port=self.socket))\n\n    client = self._create_ngclient(self.socket, stdout, stderr, stdin)\n    self.ensure_connectable(client)\n\n    return client\n\n  def _check_process_buildroot(self, process):\n    \"\"\"Matches only processes started from the current buildroot.\"\"\"\n    return self._PANTS_NG_BUILDROOT_ARG in process.cmdline()\n\n  def is_alive(self):\n    \"\"\"A ProcessManager.is_alive() override that ensures buildroot flags are present in the process\n    command line arguments.\"\"\"\n    return super(NailgunExecutor, self).is_alive(self._check_process_buildroot)\n\n  def post_fork_child(self, fingerprint, jvm_options, classpath, stdout, stderr):\n    \"\"\"Post-fork() child callback for ProcessManager.daemon_spawn().\"\"\"\n    java = SubprocessExecutor(self._distribution)\n\n    subproc = java.spawn(classpath=classpath,\n                         main='com.martiansoftware.nailgun.NGServer',\n                         jvm_options=jvm_options,\n                         args=[':0'],\n                         stdin=safe_open('/dev/null', 'r'),\n                         stdout=safe_open(self._ng_stdout, 'w'),\n                         stderr=safe_open(self._ng_stderr, 'w'),\n                         close_fds=True)\n\n    self.write_pid(subproc.pid)\n", "path": "src/python/pants/java/nailgun_executor.py"}]}