blob: 3bc90041eaadb3803a97a326a9d8b66ee20d234b [file] [log] [blame]
/* Copyright 2020 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PACKET_STATUS_LOGGER_
#define PACKET_STATUS_LOGGER_
#include <stdint.h>
#include <stdbool.h>
#define PACKET_STATUS_LEN_BYTES 64
#define WBS_FRAME_NS 7500000
/* Avoid 32, 40, 64 consecutive hex characters so CrOS feedback redact
* tool doesn't trim our dump. */
#define PACKET_STATUS_LOG_LINE_WRAP 50
/*
* Object to log consecutive packets' status.
* Members:
* data - Bytes to store packets' status.
* size - Total number of bits in |data|.
* wp - Position of the next bit to log packet status.
* num_wraps - Number of times the ring buffer has wrapped.
* ts - The timestamp of the last time when the first bit of |data| updated.
*/
struct packet_status_logger {
uint8_t data[PACKET_STATUS_LEN_BYTES];
int size;
int wp;
int num_wraps;
struct timespec ts;
};
/* Initializes the packet status logger. */
void packet_status_logger_init(struct packet_status_logger *logger);
/* Updates the next packet status to logger. */
void packet_status_logger_update(struct packet_status_logger *logger, bool val);
/* Rewinds logger's time stamp to calculate the beginning.
* If logger's ring buffer hasn't wrapped, simply return logger_ts.
* Otherwise beginning_ts = logger_ts - WBS_FRAME_NS * (size - wp)
*/
static inline void
packet_status_logger_begin_ts(const struct packet_status_logger *logger,
struct timespec *ts)
{
long nsec = WBS_FRAME_NS * (logger->size - logger->wp);
*ts = logger->ts;
if (logger->num_wraps == 0)
return;
while (nsec > 1000000000L) {
ts->tv_sec--;
nsec -= 1000000000L;
}
ts->tv_nsec -= nsec;
if (ts->tv_nsec < 0) {
ts->tv_sec--;
ts->tv_nsec += 1000000000L;
}
}
/* Fast-forwards the logger's time stamp to calculate the end.
* In other words, end_ts = logger_ts + WBS_FRAME_NS * wp
*/
static inline void
packet_status_logger_end_ts(const struct packet_status_logger *logger,
struct timespec *ts)
{
*ts = logger->ts;
ts->tv_nsec += WBS_FRAME_NS * logger->wp;
while (ts->tv_nsec > 1000000000L) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000L;
}
}
/* Prints the logger data in hex format */
static inline void
packet_status_logger_dump_hex(const struct packet_status_logger *logger)
{
int i = logger->wp / 8;
/* Print the bits after wp only if buffer has wrapped. */
if (logger->num_wraps) {
if (logger->wp % 8)
printf("%.2x",
logger->data[i] & (0xff << (logger->wp % 8)));
for (; i < PACKET_STATUS_LEN_BYTES; i++)
printf("%.2x", logger->data[i]);
}
for (i = 0; i < logger->wp / 8; i++)
printf("%.2x", logger->data[i]);
if (logger->wp % 8)
printf("%.2x", logger->data[i] & (~(0xff << (logger->wp % 8))));
printf("\n");
}
/* Prints the logger data in binary format */
static inline void
packet_status_logger_dump_binary(const struct packet_status_logger *logger)
{
/* Don't print the bits after wp if buffer hasn't wrapped. */
int head = logger->num_wraps ? logger->wp : 0;
int len = logger->num_wraps ? logger->size : logger->wp;
int i, j;
for (i = 0; i < len; ++i) {
j = (head + i) % logger->size;
printf("%d", (logger->data[j / 8] >> (j % 8)) & 1U);
if ((i + 1) % PACKET_STATUS_LOG_LINE_WRAP == 0)
printf("\n");
}
/* Fill indicator digit 'D' until the last line wraps. */
if (len % PACKET_STATUS_LOG_LINE_WRAP) {
while (len % PACKET_STATUS_LOG_LINE_WRAP) {
printf("D");
++len;
}
printf("\n");
}
}
#endif /* PACKET_STATUS_LOGGER_ */