#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/tslog.h>
#include <machine/atomic.h>
#include <machine/cpu.h>
#ifndef TSLOGSIZE
#define TSLOGSIZE 262144
#endif
static volatile long nrecs = 0;
static struct timestamp {
void * td;
int type;
const char * f;
const char * s;
uint64_t tsc;
} timestamps[TSLOGSIZE];
void
tslog(void * td, int type, const char * f, const char * s)
{
uint64_t tsc = get_cyclecount();
long pos;
if (td == NULL)
td = &thread0;
pos = atomic_fetchadd_long(&nrecs, 1);
if (pos < nitems(timestamps)) {
timestamps[pos].td = td;
timestamps[pos].type = type;
timestamps[pos].f = f;
timestamps[pos].s = s;
timestamps[pos].tsc = tsc;
}
}
static int
sysctl_debug_tslog(SYSCTL_HANDLER_ARGS)
{
int error;
struct sbuf *sb;
size_t i, limit;
caddr_t loader_tslog;
void * loader_tslog_buf;
size_t loader_tslog_len;
sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
loader_tslog = preload_search_by_type("TSLOG data");
if (loader_tslog != NULL) {
loader_tslog_buf = preload_fetch_addr(loader_tslog);
loader_tslog_len = preload_fetch_size(loader_tslog);
sbuf_bcat(sb, loader_tslog_buf, loader_tslog_len);
}
limit = MIN(nrecs, nitems(timestamps));
for (i = 0; i < limit; i++) {
sbuf_printf(sb, "%p", timestamps[i].td);
sbuf_printf(sb, " %llu",
(unsigned long long)timestamps[i].tsc);
switch (timestamps[i].type) {
case TS_ENTER:
sbuf_cat(sb, " ENTER");
break;
case TS_EXIT:
sbuf_cat(sb, " EXIT");
break;
case TS_THREAD:
sbuf_cat(sb, " THREAD");
break;
case TS_EVENT:
sbuf_cat(sb, " EVENT");
break;
}
sbuf_printf(sb, " %s", timestamps[i].f ? timestamps[i].f : "(null)");
if (timestamps[i].s)
sbuf_printf(sb, " %s\n", timestamps[i].s);
else
sbuf_putc(sb, '\n');
}
error = sbuf_finish(sb);
sbuf_delete(sb);
return (error);
}
SYSCTL_PROC(_debug, OID_AUTO, tslog,
CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP,
0, 0, sysctl_debug_tslog, "", "Dump recorded event timestamps");
MALLOC_DEFINE(M_TSLOGUSER, "tsloguser", "Strings used by userland tslog");
static struct procdata {
pid_t ppid;
uint64_t tsc_forked;
uint64_t tsc_exited;
char * execname;
char * namei;
int reused;
} procs[PID_MAX + 1];
void
tslog_user(pid_t pid, pid_t ppid, const char * execname, const char * namei)
{
uint64_t tsc = get_cyclecount();
if (procs[pid].reused)
return;
if (ppid != (pid_t)(-1)) {
if (procs[pid].ppid) {
procs[pid].reused = 1;
return;
}
procs[pid].ppid = ppid;
procs[pid].tsc_forked = tsc;
return;
}
if (execname != NULL) {
if (procs[pid].execname != NULL)
free(procs[pid].execname, M_TSLOGUSER);
procs[pid].execname = strdup(execname, M_TSLOGUSER);
return;
}
if (namei != NULL) {
if (procs[pid].namei == NULL)
procs[pid].namei = strdup(namei, M_TSLOGUSER);
return;
}
procs[pid].tsc_exited = tsc;
}
static int
sysctl_debug_tslog_user(SYSCTL_HANDLER_ARGS)
{
int error;
struct sbuf *sb;
pid_t pid;
sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
for (pid = 0; pid <= PID_MAX; pid++) {
sbuf_printf(sb, "%zu", (size_t)pid);
sbuf_printf(sb, " %zu", (size_t)procs[pid].ppid);
sbuf_printf(sb, " %llu",
(unsigned long long)procs[pid].tsc_forked);
sbuf_printf(sb, " %llu",
(unsigned long long)procs[pid].tsc_exited);
sbuf_printf(sb, " \"%s\"", procs[pid].execname ?
procs[pid].execname : "");
sbuf_printf(sb, " \"%s\"", procs[pid].namei ?
procs[pid].namei : "");
sbuf_putc(sb, '\n');
}
error = sbuf_finish(sb);
sbuf_delete(sb);
return (error);
}
SYSCTL_PROC(_debug, OID_AUTO, tslog_user,
CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP,
0, 0, sysctl_debug_tslog_user,
"", "Dump recorded userland event timestamps");
void
sysinit_tslog_shim(const void *data)
{
const struct sysinit_tslog *x = data;
tslog(curthread, TS_ENTER, "SYSINIT", x->name);
(x->func)(x->data);
tslog(curthread, TS_EXIT, "SYSINIT", x->name);
}