From f0c4d94025f0b603967e2b86388e5450ed06bbd7 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Fri, 30 Mar 2018 08:31:09 +0200 Subject: [PATCH 1/6] doc: update link to freebsd uncrustify's config file Signed-off-by: Akim Demaille --- CODINGSTYLE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index 9b0bebca..a6788d31 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -5,7 +5,7 @@ coding style should follow the You may use tools like [uncrustify](http://uncrustify.sourceforge.net/) with this -[config file](https://github.com/freebsd/pkg/blob/master/freebsd.cfg) +[config file](https://raw.githubusercontent.com/freebsd/pkg/master/freebsd.cfg) for *new* code, though the result may not be perfect. Keep in mind that, especially for most of the `bhyve` derived code, it From 0b65533b7820390947a0ccdd4f721d6f23117071 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Tue, 3 Apr 2018 13:22:07 +0200 Subject: [PATCH 2/6] Style: use tabs Signed-off-by: Akim Demaille --- src/lib/uart_emul.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/uart_emul.c b/src/lib/uart_emul.c index 08796b77..ef236172 100644 --- a/src/lib/uart_emul.c +++ b/src/lib/uart_emul.c @@ -45,9 +45,9 @@ #include #include -#define COM1_BASE 0x3F8 +#define COM1_BASE 0x3F8 #define COM1_IRQ 4 -#define COM2_BASE 0x2F8 +#define COM2_BASE 0x2F8 #define COM2_IRQ 3 #define DEFAULT_RCLK 1843200 @@ -91,7 +91,7 @@ struct fifo { struct ttyfd { bool opened; int fd; /* tty device file descriptor */ - int sfd; + int sfd; char *name; /* slave pty name when using autopty*/ struct termios tio_orig, tio_new; /* I/O Terminals */ }; @@ -427,7 +427,7 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) } } - switch (offset) { + switch (offset) { case REG_DATA: if (sc->mcr & MCR_LOOPBACK) { if (rxfifo_putchar(sc, value) != 0) @@ -681,15 +681,15 @@ uart_tty_backend(struct uart_softc *sc, const char *backend) static char * copy_up_to_comma(const char *from) { - char *comma = strchr(from, ','); - char *tmp = NULL; - if (comma == NULL) { - tmp = strdup(from); /* rest of string */ - } else { - ptrdiff_t length = comma - from; - tmp = strndup(from, (size_t)length); - } - return tmp; + char *comma = strchr(from, ','); + char *tmp = NULL; + if (comma == NULL) { + tmp = strdup(from); /* rest of string */ + } else { + ptrdiff_t length = comma - from; + tmp = strndup(from, (size_t)length); + } + return tmp; } int From 7740375639cf1ae7eef2cb90cb4cf7639b61449a Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Thu, 29 Mar 2018 13:50:49 +0200 Subject: [PATCH 3/6] Offer a means to send the tty to the logs On Docker for Windows, all the logs are aggregated: GUI, LinuxKit, etc. Currently debugging on Docker for Mac is less easy, in particular because LinuxKit's log are sent to the consolefile rings of fixed sized buffers. As a result, it's hard to find the interleaving of actions between Docker for Mac and LinuxKit, the console files are fragmented and full of '\0', etc. It is possible to have hyperkit redirect the tty's output to its stderr/stdout, and then its caller (Docker for Mac's driver) could redirect these streams to the logger. Unfortunately on recent macOS releases, asl is replaced by os_log which uses exclusively the path of the executable as source (changing `argv[0]` is useless). As a result, hyperkit's logs are "credited" to the driver. The most elegant approach is to embrace asl/os_log. Currently Docker for Mac still targets 10.11, and os_log appeared in macOS 10.12, so we cannot use directly os_log's API, we use it through the asl shim. Add a new device configuration, asl, that sends the tty's output to asl. Signed-off-by: Akim Demaille --- Makefile | 1 + src/include/xhyve/log.h | 7 ++++++ src/lib/log.c | 50 +++++++++++++++++++++++++++++++++++++++++ src/lib/uart_emul.c | 8 +++++++ 4 files changed, 66 insertions(+) create mode 100644 src/include/xhyve/log.h create mode 100644 src/lib/log.c diff --git a/Makefile b/Makefile index f17b882d..6a1a6a6a 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ HYPERKIT_LIB_SRC := \ src/lib/fwctl.c \ src/lib/inout.c \ src/lib/ioapic.c \ + src/lib/log.c \ src/lib/md5c.c \ src/lib/mem.c \ src/lib/mevent.c \ diff --git a/src/include/xhyve/log.h b/src/include/xhyve/log.h new file mode 100644 index 00000000..6853e846 --- /dev/null +++ b/src/include/xhyve/log.h @@ -0,0 +1,7 @@ +#pragma once + +/* Initialize ASL logger and local buffer. */ +void log_init(void); + +/* Send one character to the logger: wait for full lines before actually sending. */ +void log_put(uint8_t _c); diff --git a/src/lib/log.c b/src/lib/log.c new file mode 100644 index 00000000..98cc7871 --- /dev/null +++ b/src/lib/log.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#include + +#include + +static aslclient log_client = NULL; +static aslmsg log_msg = NULL; + +static unsigned char buf[4096]; +/* Index of the _next_ character to insert in the buffer. */ +static size_t buf_idx = 0; + +/* asl is deprecated in favor of os_log starting with macOS 10.12. */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* Initialize ASL logger and local buffer. */ +void log_init(void) +{ + log_client = asl_open(NULL, NULL, 0); + log_msg = asl_new(ASL_TYPE_MSG); +} + + +/* Send the content of the buffer to the logger. */ +static void log_flush(void) +{ + buf[buf_idx] = 0; + asl_log(log_client, log_msg, ASL_LEVEL_NOTICE, "%s", buf); + buf_idx = 0; +} + + +/* Send one character to the logger: wait for full lines before actually sending. */ +void log_put(uint8_t c) +{ + if ((c == '\n') || (c == 0)) { + log_flush(); + } else { + if (buf_idx + 2 >= sizeof(buf)) { + log_flush(); + } + buf[buf_idx] = c; + ++buf_idx; + } +} diff --git a/src/lib/uart_emul.c b/src/lib/uart_emul.c index ef236172..69f23a4a 100644 --- a/src/lib/uart_emul.c +++ b/src/lib/uart_emul.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,7 @@ struct uart_softc { struct ttyfd tty; struct log log; + bool asl; /* Output to Apple logger. */ bool thre_int_pending; /* THRE interrupt pending */ void *arg; @@ -436,6 +438,8 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) ttywrite(&sc->tty, value); if (sc->log.ring) ringwrite(&sc->log, value); + if (sc->asl) + log_put(value); } /* else drop on floor */ sc->thre_int_pending = true; break; @@ -773,6 +777,10 @@ uart_set_backend(struct uart_softc *sc, const char *backend, const char *devname if (uart_mapring(sc, logname) == -1) { goto err; } + } else if (strcmp("asl", backend) == 0) { + sc->asl = true; + log_init(); + retval = 0; } else if (uart_tty_backend(sc, backend) == 0) { retval = 0; } else { From 432797597208472f508f988a9e51068932fa54af Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Fri, 30 Mar 2018 08:33:59 +0200 Subject: [PATCH 4/6] Go: factor and log the opening of the tty file In some earlier experiment this function was called several times. Since it made the code slightly easier to understand, I left it. Signed-off-by: Akim Demaille --- go/hyperkit.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/go/hyperkit.go b/go/hyperkit.go index 3579afd7..e6a63226 100644 --- a/go/hyperkit.go +++ b/go/hyperkit.go @@ -440,6 +440,22 @@ func (h *HyperKit) buildArgs(cmdline string) { log.Debugf("hyperkit: CmdLine: %#v", h.CmdLine) } +// openTTY opens the tty files for reading, and returns it. +func (h *HyperKit) openTTY() *os.File { + path := fmt.Sprintf("%s/tty", h.StateDir) + for { + if res, err := os.OpenFile(path, os.O_RDONLY, 0); err != nil { + log.Infof("hyperkit: openTTY: %v, retrying", err) + time.Sleep(10 * time.Millisecond) + } else { + log.Infof("hyperkit: openTTY: got %v", path) + saneTerminal(res) + setRaw(res) + return res + } + } +} + // execute forges the command to run hyperkit, runs and returns it. // It also plumbs stdin/stdout/stderr. func (h *HyperKit) execute() (*exec.Cmd, error) { @@ -469,20 +485,9 @@ func (h *HyperKit) execute() (*exec.Cmd, error) { cmd.Stderr = os.Stderr } else { go func() { - ttyPath := fmt.Sprintf("%s/tty", h.StateDir) - var tty *os.File - for { - var err error - tty, err = os.OpenFile(ttyPath, os.O_RDONLY, 0) - if err == nil { - break - } - time.Sleep(10 * time.Millisecond) - } - saneTerminal(tty) - setRaw(tty) + tty := h.openTTY() + defer tty.Close() io.Copy(os.Stdout, tty) - tty.Close() }() } } else if log != nil { From f310710f2d04cba01802510d1286d677b7fc2b49 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Fri, 30 Mar 2018 08:45:40 +0200 Subject: [PATCH 5/6] Go: propagate invariants Hyperkit.check verifies that if `h.Console == ConsoleStdio`, then either `isTerminal(os.Stdout)` or `h.StateDir != ""`. In the next commit, the device configuration becomes more complex, and simplifying this conditional by eliminating impossible situations makes this next commit clearer. Signed-off-by: Akim Demaille --- go/hyperkit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/hyperkit.go b/go/hyperkit.go index e6a63226..d1eb3ee3 100644 --- a/go/hyperkit.go +++ b/go/hyperkit.go @@ -422,7 +422,7 @@ func (h *HyperKit) buildArgs(cmdline string) { if h.Console == ConsoleStdio && isTerminal(os.Stdout) { a = append(a, "-l", "com1,stdio") - } else if h.StateDir != "" { + } else { a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) } From 06c3cf7ec253534b2d81f61ee3c85c5c9aafa4ee Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Tue, 3 Apr 2018 17:03:49 +0200 Subject: [PATCH 6/6] Go: add ConsoleLog to redirect the tty to the Apple logs Signed-off-by: Akim Demaille --- go/hyperkit.go | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/go/hyperkit.go b/go/hyperkit.go index d1eb3ee3..26ceab65 100644 --- a/go/hyperkit.go +++ b/go/hyperkit.go @@ -44,6 +44,8 @@ const ( ConsoleStdio = iota // ConsoleFile configures console to a tty and output to a file ConsoleFile + // ConsoleLog configures console to a tty and sends its contents to the logs + ConsoleLog defaultVPNKitSock = "Library/Containers/com.docker.docker/Data/s50" @@ -117,8 +119,7 @@ type HyperKit struct { // Memory is the amount of megabytes of memory for the VM. Memory int `json:"memory"` - // Console defines where the console of the VM should be - // connected to. ConsoleStdio and ConsoleFile are supported. + // Console defines where the console of the VM should be connected to. Console int `json:"console"` // Below here are internal members, but they are exported so @@ -199,11 +200,15 @@ func (h *HyperKit) check() error { log.Debugf("hyperkit: check %#v", h) var err error // Sanity checks on configuration - if h.Console == ConsoleFile && h.StateDir == "" { - return fmt.Errorf("If ConsoleFile is set, StateDir must be specified") - } - if h.Console == ConsoleStdio && !isTerminal(os.Stdout) && h.StateDir == "" { - return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + switch h.Console { + case ConsoleFile, ConsoleLog: + if h.StateDir == "" { + return fmt.Errorf("If ConsoleFile or ConsoleLog is set, StateDir must be specified") + } + case ConsoleStdio: + if !isTerminal(os.Stdout) && h.StateDir == "" { + return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + } } for _, image := range h.ISOImages { if _, err = os.Stat(image); os.IsNotExist(err) { @@ -420,10 +425,20 @@ func (h *HyperKit) buildArgs(cmdline string) { nextSlot++ } - if h.Console == ConsoleStdio && isTerminal(os.Stdout) { - a = append(a, "-l", "com1,stdio") - } else { - a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) + // -l: LPC device configuration. + { + cfg := "com1" + if h.Console == ConsoleStdio && isTerminal(os.Stdout) { + cfg += fmt.Sprintf(",stdio") + } else { + cfg += fmt.Sprintf(",autopty=%s/tty", h.StateDir) + } + if h.Console == ConsoleLog { + cfg += fmt.Sprintf(",asl") + } else { + cfg += fmt.Sprintf(",log=%s/console-ring", h.StateDir) + } + a = append(a, "-l", cfg) } if h.Bootrom == "" {