blob: 95b0d60159fc6a5397346d5c55aabfdbd5e26eca [file] [log] [blame]
// Copyright 2016 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 <errno.h>
#include <fidl/fuchsia.boot/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <unistd.h>
#include <zircon/syscalls/log.h>
void usage() {
fprintf(stderr,
"usage: dlog dump the zircon debug log\n"
"\n"
"options: -f don't exit, keep waiting for new messages\n"
" -p <pid> only show messages from specified pid\n"
" -t only show the text of messages (no metadata)\n"
" -h show help\n");
}
int main(int argc, char** argv) {
bool tail = false;
bool filter_pid = false;
bool plain = false;
zx_koid_t pid = 0;
while (argc > 1) {
if (!strcmp(argv[1], "-h")) {
usage();
return 0;
}
if (!strcmp(argv[1], "-f")) {
tail = true;
} else if (!strcmp(argv[1], "-t")) {
plain = true;
} else if (!strcmp(argv[1], "-p")) {
argc--;
argv++;
if (argc < 2) {
usage();
return -1;
}
errno = 0;
pid = strtoull(argv[1], nullptr, 0);
if (errno) {
fprintf(stderr, "dlog: invalid pid\n");
return -1;
}
filter_pid = true;
} else {
usage();
return -1;
}
argc--;
argv++;
}
zx::result client = component::Connect<fuchsia_boot::ReadOnlyLog>();
if (client.is_error()) {
fprintf(stderr, "failed to connect to read only log: %s\n", client.status_string());
return -1;
}
const fidl::WireResult result = fidl::WireCall(client.value())->Get();
if (!result.ok()) {
fprintf(stderr, "failed to get read only log handle: %s\n", result.status_string());
return -1;
}
const zx::debuglog& log = result.value().log;
alignas(zx_log_record_t) char buf[ZX_LOG_RECORD_MAX];
for (;;) {
// Per zx_debuglog_read:
//
// The length of the record in bytes is given in the syscall's return
// value.
zx_status_t status = log.read(0, buf, sizeof(buf));
if (status < 0) {
if (status == ZX_ERR_SHOULD_WAIT) {
if (tail) {
if (zx_status_t status = log.wait_one(ZX_LOG_READABLE, zx::time::infinite(), nullptr);
status != ZX_OK) {
fprintf(stderr, "failed to wait for read only log handle: %s\n",
zx_status_get_string(status));
return -1;
}
continue;
}
break;
}
fprintf(stderr, "failed to read from read only log handle: %s\n",
zx_status_get_string(status));
return -1;
}
uint32_t read = static_cast<uint32_t>(status);
ZX_ASSERT_MSG(read >= sizeof(zx_log_record_t), "read %d/%lu", read, sizeof(zx_log_record_t));
ZX_ASSERT_MSG(read <= sizeof(buf), "read %d/%lu", read, sizeof(buf));
zx_log_record_t& rec = *reinterpret_cast<zx_log_record_t*>(buf);
ZX_ASSERT_MSG(read == sizeof(rec) + rec.datalen, "inconsistent read of %d with datalen %d",
read, rec.datalen);
if (filter_pid && (pid != rec.pid)) {
continue;
}
// Calculate needed capacity for the log line.
constexpr size_t ts_len = 1 + 5 + 1 + 3 + 2;
constexpr char endl = '\n';
const bool has_newline = rec.datalen != 0 && rec.data[rec.datalen - sizeof(endl)] == endl;
const size_t cap = [plain, has_newline, datalen = rec.datalen]() {
const size_t ts_cap = !plain ? ts_len : 0;
const size_t newline_cap = !has_newline ? sizeof(endl) : 0;
return ts_cap + datalen + newline_cap;
}();
char buf[cap];
size_t len = 0;
if (!plain) {
const zx_time_t millis = rec.timestamp / 1000000;
const zx_time_t secs = millis / 1000;
const zx_time_t ms = millis % 1000;
int ret = snprintf(&buf[len], sizeof(buf) - len, "[%05ld.%03ld] ", secs, ms);
ZX_ASSERT_MSG(ret >= 0, "snprintf failed: %d", ret);
size_t n = static_cast<size_t>(ret);
ZX_ASSERT_MSG(n == ts_len, "unexpected timestamp length %zu/%zu", n, ts_len);
len += n;
}
memcpy(&buf[len], rec.data, rec.datalen);
len += rec.datalen;
if (!has_newline) {
memcpy(&buf[len], &endl, sizeof(endl));
len += sizeof(endl);
}
std::string_view view(buf, len);
while (!view.empty()) {
ssize_t ret = write(STDOUT_FILENO, view.data(), view.size());
if (ret < 0) {
fprintf(stderr, "write failed: %s\n", strerror(errno));
return -1;
}
size_t n = static_cast<size_t>(ret);
view = view.substr(n);
}
}
return 0;
}