199 lines
4.9 KiB
C++

#include <stdint.h>
#include <pthread.h>
#include <iostream>
#include <iomanip>
#include "util.h"
#include "SerialLog.h"
SerialLog::SerialLog(unsigned log_size, unsigned timeout)
: _c_buf(log_size), _mutex_initted(false), _timeout_us(timeout), _last_up_time_us(0),
_enabled(false)
{
if ((_c_buf.free() > 0) && (pthread_mutex_init(&_mutex, NULL) == 0)) {
_mutex_initted = true;
_enabled = true;
}
} // SerialLog::SerialLog
SerialLog::~SerialLog()
{
if (_mutex_initted) {
pthread_mutex_destroy(&_mutex);
_mutex_initted = false;
}
_enabled = false;
} // SerialLog::~SerialLog()
// Discard packet at tail.
// Assume already locked. If the log is corrupt, e.g. there is enough data for
// PacketEntry but not enough for the packet data, then this won't work (but
// it is already corrupt).
bool SerialLog::discard_packet(void)
{
PacketEntry pe;
// get PacketEntry
if (!_c_buf.get(&pe))
return false;
// discard variable-length packet data
return _c_buf.discard(pe.num_bytes);
} // SerialLog::discard_packet
void SerialLog::check_timeout(uint64_t now)
{
if (_enabled && (_last_up_time_us != 0) && (_timeout_us != 0) &&
((now - _last_up_time_us) > _timeout_us)) {
_enabled = false;
}
} // SerialLog::check_timeout
bool SerialLog::log_packet(const void *packet, unsigned num_bytes, uint8_t flags)
{
lock();
// Timestamp logged is CLOCK_REALTIME. It is believed this is more
// useful when looking at logs.
// Timestamp for timeout detection is CLOCK_MONOTONIC. This is required
// to avoid false triggers (or missed triggers) when the time changes.
uint64_t now_mt = clock_gettime_us(CLOCK_MONOTONIC);
uint64_t now_rt = clock_gettime_us(CLOCK_REALTIME);
check_timeout(now_mt);
if (!_enabled) {
unlock();
return false;
}
// discard from the tail until there is room
unsigned total_bytes = sizeof(PacketEntry) + num_bytes;
while (_c_buf.free() < total_bytes) {
if (!discard_packet()) {
unlock();
return false;
}
}
PacketEntry pe;
pe.timestamp = now_rt;
pe.num_bytes = num_bytes;
pe.flags = flags;
_c_buf.put(pe);
_c_buf.put(packet, num_bytes);
if ((flags & PKTFLG_DIR) == PKTFLG_UP)
_last_up_time_us = now_mt;
unlock();
return true;
} // SerialLog::log_packet
std::ostream &operator<<(std::ostream &os, SerialLog::PacketEntry &pe)
{
#if 0
// Dump raw
const uint8_t *p = (const uint8_t *)&pe;
os << "PacketEntry:";
for (unsigned i = 0; i < sizeof(pe); i++)
os << ' ' << std::setfill('0') << std::setw(2) << std::hex
<< unsigned(*p++);
#elif 1
// Dump formatted
os << "time=" << pe.timestamp << " bytes=" << pe.num_bytes << " flags=" << std::setfill('0')
<< std::setw(2) << std::hex << unsigned(uint8_t(pe.flags));
#endif
os << std::setfill(' ') << std::setw(0) << std::dec;
return os;
} // operator<<
std::ostream &operator<<(std::ostream &os, SerialLog &sl)
{
os << "size=" << sl.size() << " used=" << sl.used() << " free=" << sl.free()
<< " enabled=" << (sl.enabled() ? "true" : "false") << std::endl;
#if 0
// Dump raw
static const unsigned pkt_buf_size = 16;
char pkt_buf[pkt_buf_size];
while (sl.used() > 0)
{
unsigned n = pkt_buf_size;
if (n > sl.used())
n = sl.used();
sl.get(pkt_buf, n);
for (unsigned i = 0; i < n; i++)
os << ' ' << std::setfill('0') << std::setw(2) << std::hex
<< unsigned(uint8_t(pkt_buf[i]));
os << std::endl;
os << std::dec;
} // while (sl.used()...)
#elif 1
// Dump formatted
static const unsigned pkt_buf_size = 256;
uint8_t pkt_buf[pkt_buf_size];
char time_buf[32];
while (sl.used() >= sizeof(SerialLog::PacketEntry)) {
SerialLog::PacketEntry pe;
if (!sl.get(&pe))
return os;
unsigned keep = pe.num_bytes;
unsigned discard = 0;
if (keep > pkt_buf_size) {
keep = pkt_buf_size;
discard = pe.num_bytes - pkt_buf_size;
}
if (!sl.get(pkt_buf, keep))
return os;
if (!sl.discard(discard))
return os;
clock_tostr_r(pe.timestamp, time_buf);
const char *dir =
((pe.flags & SerialLog::PKTFLG_DIR) == SerialLog::PKTFLG_UP) ? "UP" : "DN";
os << time_buf << ' ' << std::setw(3) << pe.num_bytes << ' ' << dir << ':';
for (unsigned i = 0; i < keep; i++)
os << ' ' << std::setfill('0') << std::setw(2) << std::hex << unsigned(pkt_buf[i]);
if (discard == 0)
os << std::endl;
else
os << " ..." << std::endl; // show that we did not print it all
os << std::setfill(' ') << std::dec;
} // while (sl.used()...)
#endif
return os;
} // operator<<