1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <inttypes.h>
6#include <string.h>
7
8#include <zircon/syscalls.h>
9#include <zircon/syscalls/log.h>
10
11#include "netsvc.h"
12
13#define MAX_LOG_LINE (ZX_LOG_RECORD_MAX + 32)
14
15static zx_handle_t loghandle;
16static logpacket_t pkt;
17static int pkt_len;
18
19static volatile uint32_t seqno = 1;
20static volatile uint32_t pending = 0;
21
22zx_time_t debuglog_next_timeout = ZX_TIME_INFINITE;
23
24#define SEND_DELAY_SHORT ZX_MSEC(100)
25#define SEND_DELAY_LONG ZX_SEC(4)
26
27// Number of consecutive unacknowledged packets we will send before reducing send rate.
28static const unsigned kUnackedThreshold = 5;
29
30// Number of consecutive packets that went unacknowledged. Is reset on acknowledgment.
31static unsigned num_unacked = 0;
32
33// How long to wait between sending.
34static zx_duration_t send_delay = SEND_DELAY_SHORT;
35
36static int get_log_line(char* out) {
37    char buf[ZX_LOG_RECORD_MAX + 1];
38    zx_log_record_t* rec = (zx_log_record_t*)buf;
39    for (;;) {
40        if (zx_debuglog_read(loghandle, 0, rec, ZX_LOG_RECORD_MAX) > 0) {
41            if (rec->datalen && (rec->data[rec->datalen - 1] == '\n')) {
42                rec->datalen--;
43            }
44            // records flagged for local display are ignored
45            if (rec->flags & ZX_LOG_LOCAL) {
46                continue;
47            }
48            rec->data[rec->datalen] = 0;
49            snprintf(out, MAX_LOG_LINE, "[%05d.%03d] %05" PRIu64 ".%05" PRIu64 "> %s\n",
50                     (int)(rec->timestamp / 1000000000ULL),
51                     (int)((rec->timestamp / 1000000ULL) % 1000ULL),
52                     rec->pid, rec->tid, rec->data);
53            return strlen(out);
54        } else {
55            return 0;
56        }
57    }
58}
59
60int debuglog_init(void) {
61    if (zx_debuglog_create(ZX_HANDLE_INVALID, ZX_LOG_FLAG_READABLE, &loghandle) < 0) {
62        return -1;
63    }
64
65    // Set up our timeout to expire immediately, so that we check for pending log messages
66    debuglog_next_timeout = zx_clock_get_monotonic();
67
68    seqno = 1;
69    pending = 0;
70
71    return 0;
72}
73
74// If we have an outstanding (unacknowledged) log, resend it. Otherwise, send new logs, if we
75// have any.
76static void debuglog_send(void) {
77    if (pending == 0) {
78        pkt.magic = NB_DEBUGLOG_MAGIC;
79        pkt.seqno = seqno;
80        strncpy(pkt.nodename, nodename, sizeof(pkt.nodename) - 1);
81        pkt_len = 0;
82        while (pkt_len < (MAX_LOG_DATA - MAX_LOG_LINE)) {
83            int r = get_log_line(pkt.data + pkt_len);
84            if (r > 0) {
85                pkt_len += r;
86            } else {
87                break;
88            }
89        }
90        if (pkt_len) {
91            // include header and nodename in length
92            pkt_len += MAX_NODENAME_LENGTH + sizeof(uint32_t) * 2;
93            pending = 1;
94        } else {
95            goto done;
96        }
97    }
98    udp6_send(&pkt, pkt_len, &ip6_ll_all_nodes, DEBUGLOG_PORT, DEBUGLOG_ACK_PORT, false);
99done:
100    debuglog_next_timeout = zx_deadline_after(send_delay);
101}
102
103void debuglog_recv(void* data, size_t len, bool is_mcast) {
104    // The only message we should be receiving is acknowledgement of our last transmission
105    if (!pending) {
106        return;
107    }
108    if ((len != 8) || is_mcast) {
109        return;
110    }
111    logpacket_t* pkt = data;
112    if ((pkt->magic != NB_DEBUGLOG_MAGIC) || (pkt->seqno != seqno)) {
113        return;
114    }
115
116    // Received an ack. We have an active listener. Don't delay.
117    num_unacked = 0;
118    send_delay = SEND_DELAY_SHORT;
119
120    seqno++;
121    pending = 0;
122    debuglog_send();
123}
124
125void debuglog_timeout_expired(void) {
126    if (pending) {
127        // No reply. If noone is listening, reduce send rate.
128        if (++num_unacked >= kUnackedThreshold) {
129            send_delay = SEND_DELAY_LONG;
130        }
131    }
132    debuglog_send();
133}
134
135