[bitbake-devel] [PATCH] server/process: Various server startup logging fixes
Joshua Watt
jpewhacker at gmail.com
Wed Sep 5 17:19:31 UTC 2018
Looks good to me. I have a few (optional) suggestions.
On Wed, 2018-09-05 at 18:01 +0100, Richard Purdie wrote:
> There were various problems in the server startup loggin:
>
> a) stdout/stderr were not being flushed before forking which could
> potentially
> duplicate output
>
> b) there were separate buffers for stdout/stderr leading to confusing
> logs
> where the entries could be reordered. This was particularly
> confusing
> due to the separator the logs use to idendify new messages
>
> c) an fd wasn't being closed during server startup meaning if the
> server failed to start, the closed fd wasn't detected as it was
> held
> open by the other reference
>
> d) If the pipe was detected as being closed, the code incorrectly
> retried
> server startup
>
> e) The event code would remap stdout/stderr without flushing them,
> leading
> to lose log messages
>
> Signed-off-by: Richard Purdie <richard.purdie at linuxfoundation.org>
> ---
> lib/bb/daemonize.py | 12 +++++++++---
> lib/bb/event.py | 3 +++
> lib/bb/server/process.py | 20 ++++++++++----------
> 3 files changed, 22 insertions(+), 13 deletions(-)
>
> diff --git a/lib/bb/daemonize.py b/lib/bb/daemonize.py
> index bf16793468..613fb35536 100644
> --- a/lib/bb/daemonize.py
> +++ b/lib/bb/daemonize.py
> @@ -16,6 +16,10 @@ def createDaemon(function, logfile):
> background as a daemon, returning control to the caller.
> """
>
> + # Ensure stdout/stderror are flushed before forking to avoid
> duplicate output
> + sys.stdout.flush()
> + sys.stderr.flush()
> +
> try:
> # Fork a child process so the parent can exit. This returns
> control to
> # the command-line or shell. It also guarantees that the
> child will not
> @@ -66,12 +70,14 @@ def createDaemon(function, logfile):
>
> try:
> so = open(logfile, 'a+')
> - se = so
> os.dup2(so.fileno(), sys.stdout.fileno())
> - os.dup2(se.fileno(), sys.stderr.fileno())
> + os.dup2(so.fileno(), sys.stderr.fileno())
I think so "leaks" here. Granted, it was doing that before so perhaps
it could be saved for a separate patch:
with open(logfile, 'a+') as so:
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(so.fileno(), sys.stderr.fileno())
> except io.UnsupportedOperation:
> sys.stdout = open(logfile, 'a+')
> - sys.stderr = sys.stdout
> +
> + # Have stdout and stderr be the same so log output matches
> chronologically
> + # and there aren't two seperate buffers
> + sys.stderr = sys.stdout
>
> try:
> function()
> diff --git a/lib/bb/event.py b/lib/bb/event.py
> index c0ec605209..5b1b094a80 100644
> --- a/lib/bb/event.py
> +++ b/lib/bb/event.py
> @@ -141,6 +141,9 @@ def print_ui_queue():
> logger = logging.getLogger("BitBake")
> if not _uiready:
> from bb.msg import BBLogFormatter
> + # Flush any existing buffered content
> + sys.stdout.flush()
> + sys.stderr.flush()
> stdout = logging.StreamHandler(sys.stdout)
> stderr = logging.StreamHandler(sys.stderr)
> formatter = BBLogFormatter("%(levelname)s: %(message)s")
> diff --git a/lib/bb/server/process.py b/lib/bb/server/process.py
> index 9e5e709f04..38b923fe2d 100644
> --- a/lib/bb/server/process.py
> +++ b/lib/bb/server/process.py
> @@ -395,11 +395,16 @@ class BitBakeServer(object):
> bb.daemonize.createDaemon(self._startServer, logfile)
> self.sock.close()
> self.bitbake_lock.close()
> + os.close(self.readypipein)
>
> ready = ConnectionReader(self.readypipe)
> r = ready.poll(30)
> if r:
> - r = ready.get()
> + try:
> + r = ready.get()
> + except EOFError:
> + # Trap the child exitting/closing the pipe and error
> out
> + r = None
> if not r or r != "ready":
> ready.close()
> bb.error("Unable to start bitbake server")
> @@ -425,21 +430,16 @@ class BitBakeServer(object):
> bb.error("Server log for this session
> (%s):\n%s" % (logfile, "".join(lines)))
> raise SystemExit(1)
> ready.close()
> - os.close(self.readypipein)
>
> def _startServer(self):
> print(self.start_log_format % (os.getpid(),
> datetime.datetime.now().strftime(self.start_log_datetime_format)))
> server = ProcessServer(self.bitbake_lock, self.sock,
> self.sockname)
> self.configuration.setServerRegIdleCallback(server.register_
> idle_function)
> + os.close(self.readypipe)
> writer = ConnectionWriter(self.readypipein)
> - try:
> - self.cooker = bb.cooker.BBCooker(self.configuration,
> self.featureset)
> - writer.send("ready")
> - except:
> - writer.send("fail")
> - raise
> - finally:
> - os.close(self.readypipein)
> + self.cooker = bb.cooker.BBCooker(self.configuration,
> self.featureset)
> + writer.send("ready")
> + writer.close()
If we want to ensure that writer is always explicitly closed, would
this be better?
import contextlib
with contextlib.closing(ConnectionWriter(self.readypipein)) as writer:
self.cooker = bb.cooker.BBCooker(...)
writer.send("ready")
> server.cooker = self.cooker
> server.server_timeout = self.configuration.server_timeout
> server.xmlrpcinterface = self.configuration.xmlrpcinterface
> --
> 2.17.1
>
--
Joshua Watt <JPEWhacker at gmail.com>
More information about the bitbake-devel
mailing list