| // Copyright 2017 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 "debuglog.h" |
| |
| #include <fuchsia/boot/llcpp/fidl.h> |
| #include <inttypes.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/zx/clock.h> |
| #include <lib/zx/debuglog.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <zircon/boot/netboot.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/log.h> |
| |
| #include "netsvc.h" |
| #include "tftp.h" |
| |
| #define MAX_LOG_LINE (ZX_LOG_RECORD_MAX + 32) |
| |
| static zx::debuglog debuglog; |
| static logpacket_t pkt; |
| static size_t pkt_len; |
| |
| static volatile uint32_t seqno = 1; |
| static volatile uint32_t pending = 0; |
| |
| static zx_time_t g_debuglog_next_timeout = ZX_TIME_INFINITE; |
| |
| zx_time_t debuglog_next_timeout() { return g_debuglog_next_timeout; } |
| |
| #define SEND_DELAY_SHORT ZX_MSEC(100) |
| #define SEND_DELAY_LONG ZX_SEC(4) |
| |
| // Number of consecutive unacknowledged packets we will send before reducing send rate. |
| static const unsigned kUnackedThreshold = 5; |
| |
| // Number of consecutive packets that went unacknowledged. Is reset on acknowledgment. |
| static unsigned num_unacked = 0; |
| |
| // How long to wait between sending. |
| static zx_duration_t send_delay = SEND_DELAY_SHORT; |
| |
| static size_t get_log_line(char* out) { |
| char buf[ZX_LOG_RECORD_MAX + 1]; |
| zx_log_record_t* rec = reinterpret_cast<zx_log_record_t*>(buf); |
| for (;;) { |
| if (debuglog.read(0, rec, ZX_LOG_RECORD_MAX) > 0) { |
| if (rec->datalen && (rec->data[rec->datalen - 1] == '\n')) { |
| rec->datalen--; |
| } |
| // records flagged for local display are ignored |
| if (rec->flags & ZX_LOG_LOCAL) { |
| continue; |
| } |
| rec->data[rec->datalen] = 0; |
| snprintf(out, MAX_LOG_LINE, "[%05d.%03d] %05" PRIu64 ".%05" PRIu64 "> %s\n", |
| static_cast<int>(rec->timestamp / 1000000000ULL), |
| static_cast<int>((rec->timestamp / 1000000ULL) % 1000ULL), rec->pid, rec->tid, |
| rec->data); |
| return strlen(out); |
| } else { |
| return 0; |
| } |
| } |
| } |
| |
| int debuglog_init() { |
| zx::channel local, remote; |
| zx_status_t status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = fdio_service_connect("/svc/fuchsia.boot.ReadOnlyLog", remote.release()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| llcpp::fuchsia::boot::ReadOnlyLog::SyncClient read_only_log(std::move(local)); |
| auto result = read_only_log.Get(); |
| if (result.status() != ZX_OK) { |
| return result.status(); |
| } |
| debuglog = std::move(result->log); |
| |
| // Set up our timeout to expire immediately, so that we check for pending log messages |
| g_debuglog_next_timeout = zx::clock::get_monotonic().get(); |
| |
| seqno = 1; |
| pending = 0; |
| |
| return 0; |
| } |
| |
| // If we have an outstanding (unacknowledged) log, resend it. Otherwise, send new logs, if we |
| // have any. |
| static void debuglog_send() { |
| if (pending == 0) { |
| pkt.magic = NB_DEBUGLOG_MAGIC; |
| pkt.seqno = seqno; |
| strncpy(pkt.nodename, nodename(), sizeof(pkt.nodename) - 1); |
| pkt_len = 0; |
| while (pkt_len < (MAX_LOG_DATA - MAX_LOG_LINE)) { |
| size_t r = get_log_line(pkt.data + pkt_len); |
| if (r > 0) { |
| pkt_len += r; |
| } else { |
| break; |
| } |
| } |
| if (pkt_len) { |
| // include header and nodename in length |
| pkt_len += MAX_NODENAME_LENGTH + sizeof(uint32_t) * 2; |
| pending = 1; |
| } else { |
| goto done; |
| } |
| } |
| udp6_send(&pkt, pkt_len, &ip6_ll_all_nodes, DEBUGLOG_PORT, DEBUGLOG_ACK_PORT, false); |
| done: |
| g_debuglog_next_timeout = zx_deadline_after(send_delay); |
| } |
| |
| void debuglog_recv(void* data, size_t len, bool is_mcast) { |
| // The only message we should be receiving is acknowledgement of our last transmission |
| if (!pending) { |
| return; |
| } |
| if ((len != 8) || is_mcast) { |
| return; |
| } |
| // Copied not cast in-place to satisfy alignment requirements flagged by ubsan (see |
| // fxbug.dev/45798). |
| logpacket_t pkt; |
| memcpy(&pkt, data, sizeof(logpacket_t)); |
| if ((pkt.magic != NB_DEBUGLOG_MAGIC) || (pkt.seqno != seqno)) { |
| return; |
| } |
| |
| // Received an ack. We have an active listener. Don't delay. |
| num_unacked = 0; |
| send_delay = SEND_DELAY_SHORT; |
| |
| seqno++; |
| pending = 0; |
| debuglog_send(); |
| } |
| |
| void debuglog_timeout_expired() { |
| if (pending) { |
| // No reply. If noone is listening, reduce send rate. |
| if (++num_unacked >= kUnackedThreshold) { |
| send_delay = SEND_DELAY_LONG; |
| } |
| } |
| debuglog_send(); |
| } |