| // 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; |
| } |