blob: bf248ffee36a8888ba92806acc1aafdea452f657 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async/cpp/wait.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
#include <optional>
#include "vc.h"
vc_t* g_log_vc;
static zx_koid_t proc_koid;
static std::optional<async::Wait> log_wait;
static async_dispatcher_t* log_dispatcher = nullptr;
static void log_reader_cb(async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t wait_status, const zx_packet_signal_t* signal);
// This is the list for logs on displays other than the main display.
static struct list_node log_list = LIST_INITIAL_VALUE(log_list);
void set_log_listener_active(bool active) {
if (active) {
log_wait->Begin(log_dispatcher);
} else {
log_wait->Cancel();
}
}
zx_status_t log_create_vc(vc_gfx_t* graphics, vc_t** vc_out) {
vc_t* vc;
zx_status_t status = vc_alloc(&vc, &color_schemes[kDefaultColorScheme]);
if (status != ZX_OK) {
return status;
}
// Copy the log buffer into the new vc.
size_t textbuf_size = g_log_vc->rows * g_log_vc->columns * sizeof(*vc->text_buf);
memcpy(vc->text_buf, g_log_vc->text_buf, textbuf_size);
vc->cursor_x = g_log_vc->cursor_x;
vc->cursor_y = g_log_vc->cursor_y;
// Set the new vc graphics and flush the text.
vc->active = true;
vc->graphics = graphics;
vc_attach_gfx(vc);
vc_full_repaint(vc);
list_add_tail(&log_list, &vc->node);
*vc_out = vc;
return ZX_OK;
}
void log_delete_vc(vc_t* vc) {
list_delete(&vc->node);
vc_free(vc);
}
int log_start(async_dispatcher_t* dispatcher, zx::debuglog read_only_debuglog,
const color_scheme_t* color_scheme) {
log_dispatcher = dispatcher;
// Create initial console for debug log.
if (vc_create(&g_log_vc, color_scheme) != ZX_OK) {
return -1;
}
snprintf(g_log_vc->title, sizeof(g_log_vc->title), "debuglog");
// Get our process koid so the log reader can
// filter out our own debug messages from the log.
zx_info_handle_basic_t info;
if (zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info), NULL,
NULL) == ZX_OK) {
proc_koid = info.koid;
}
log_wait.emplace(read_only_debuglog.release(), ZX_LOG_READABLE, 0, log_reader_cb);
return 0;
}
static void write_to_log(vc_t* vc, zx_log_record_t* rec) {
char tmp[64];
snprintf(tmp, 64,
"\033[32m%05d.%03d\033[39m] \033[31m%05" PRIu64 ".\033[36m%05" PRIu64 "\033[39m> ",
(int)(rec->timestamp / 1000000000ULL), (int)((rec->timestamp / 1000000ULL) % 1000ULL),
rec->pid, rec->tid);
vc_write(vc, tmp, strlen(tmp), 0);
vc_write(vc, rec->data, rec->datalen, 0);
if ((rec->datalen == 0) || (rec->data[rec->datalen - 1] != '\n')) {
vc_write(vc, "\n", 1, 0);
}
}
static void log_reader_cb(async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t wait_status, const zx_packet_signal_t* signal) {
char buf[ZX_LOG_RECORD_MAX];
zx_log_record_t* rec = (zx_log_record_t*)buf;
zx_status_t status;
for (;;) {
status = zx_debuglog_read(wait->object(), 0, rec, ZX_LOG_RECORD_MAX);
// zx_debuglog_read returns >0 for success.
if (status < 0) {
break;
}
// Don't print log messages from ourself.
if (rec->pid == proc_koid) {
continue;
}
write_to_log(g_log_vc, rec);
vc_t* vc = NULL;
list_for_every_entry (&log_list, vc, vc_t, node) { write_to_log(vc, rec); }
}
if (status != ZX_ERR_SHOULD_WAIT) {
const char* oops = "<<LOG ERROR>>\n";
vc_write(g_log_vc, oops, strlen(oops), 0);
}
// Queue up another read.
wait->Begin(dispatcher);
}