#include "opt_ddb.h"
#include "opt_ktr.h"
#include <sys/param.h>
#include <sys/cons.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/ktr.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/spinlock.h>
#include <sys/kbio.h>
#include <sys/ctype.h>
#include <sys/limits.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/md_var.h>
#include <ddb/ddb.h>
#ifndef KTR_ENTRIES
#define KTR_ENTRIES 2048
#elif (KTR_ENTRIES & KTR_ENTRIES - 1)
#error KTR_ENTRIES must be a power of two
#endif
#define KTR_ENTRIES_MASK (KTR_ENTRIES - 1)
#if (KTR_ENTRIES < 256)
#define KTR_ENTRIES_BOOT0 KTR_ENTRIES
#else
#define KTR_ENTRIES_BOOT0 256
#endif
#define KTR_ENTRIES_BOOT0_MASK (KTR_ENTRIES_BOOT0 - 1)
#if !defined(KTR_TESTLOG)
#define KTR_TESTLOG KTR_ALL
#endif
KTR_INFO_MASTER(testlog);
#if KTR_TESTLOG
KTR_INFO(KTR_TESTLOG, testlog, charfmt, 0,
"charfmt %hhd %hhi %#hho %hhu %#hhx %#hhX\n",
signed char d1, signed char d2,
unsigned char d3, unsigned char d4,
unsigned char d5, unsigned char d6);
KTR_INFO(KTR_TESTLOG, testlog, shortfmt, 1,
"shortfmt %hd %hi %#ho %hu %#hx %#hX\n",
short d1, short d2,
unsigned short d3, unsigned short d4,
unsigned short d5, unsigned short d6);
KTR_INFO(KTR_TESTLOG, testlog, longfmt, 2,
"longfmt %ld %li %#lo %lu %#lx %#lX\n",
long d1, long d2,
unsigned long d3, unsigned long d4,
unsigned long d5, unsigned long d6);
KTR_INFO(KTR_TESTLOG, testlog, longlongfmt, 3,
"longlongfmt %lld %lli %#llo %llu %#llx %#llX\n",
long long d1, long long d2,
unsigned long long d3, unsigned long long d4,
unsigned long long d5, unsigned long long d6);
KTR_INFO(KTR_TESTLOG, testlog, intmaxfmt, 4,
"intmaxfmt %jd %ji %#jo %ju %#jx %#jX\n",
intmax_t d1, intmax_t d2,
uintmax_t d3, uintmax_t d4,
uintmax_t d5, uintmax_t d6);
KTR_INFO(KTR_TESTLOG, testlog, ptrdifffmt, 5,
"ptrdifffmt %td %ti\n",
ptrdiff_t d1, ptrdiff_t d2);
KTR_INFO(KTR_TESTLOG, testlog, sizefmt, 6,
"sizefmt %zd %zi %#zo %zu %#zx %#zX\n",
ssize_t d1, ssize_t d2,
size_t d3, size_t d4,
size_t d5, size_t d6);
KTR_INFO(KTR_TESTLOG, testlog, pingpong, 17, "pingpong");
KTR_INFO(KTR_TESTLOG, testlog, pipeline, 18, "pipeline");
KTR_INFO(KTR_TESTLOG, testlog, crit_beg, 19, "crit_beg");
KTR_INFO(KTR_TESTLOG, testlog, crit_end, 20, "crit_end");
KTR_INFO(KTR_TESTLOG, testlog, spin_beg, 21, "spin_beg");
KTR_INFO(KTR_TESTLOG, testlog, spin_end, 22, "spin_end");
#define logtest_noargs(name) KTR_LOG(testlog_ ## name)
#endif
MALLOC_DEFINE(M_KTR, "ktr", "ktr buffers");
SYSCTL_NODE(_debug, OID_AUTO, ktr, CTLFLAG_RW, 0, "ktr");
static int ktr_entries = KTR_ENTRIES_BOOT0;
SYSCTL_INT(_debug_ktr, OID_AUTO, entries, CTLFLAG_RD, &ktr_entries, 0,
"Size of the event buffer");
static int ktr_entries_mask = KTR_ENTRIES_BOOT0_MASK;
static int ktr_version = KTR_VERSION;
SYSCTL_INT(_debug_ktr, OID_AUTO, version, CTLFLAG_RD, &ktr_version, 0, "");
static int ktr_stacktrace = 1;
SYSCTL_INT(_debug_ktr, OID_AUTO, stacktrace, CTLFLAG_RD, &ktr_stacktrace, 0, "");
static int ktr_resynchronize = 0;
SYSCTL_INT(_debug_ktr, OID_AUTO, resynchronize, CTLFLAG_RW,
&ktr_resynchronize, 0, "Resynchronize TSC 10 times a second");
#if KTR_TESTLOG
static int ktr_testlogcnt = 0;
SYSCTL_INT(_debug_ktr, OID_AUTO, testlogcnt, CTLFLAG_RW, &ktr_testlogcnt, 0, "");
static int ktr_testipicnt = 0;
static int ktr_testipicnt_remainder;
SYSCTL_INT(_debug_ktr, OID_AUTO, testipicnt, CTLFLAG_RW, &ktr_testipicnt, 0, "");
static int ktr_testcritcnt = 0;
SYSCTL_INT(_debug_ktr, OID_AUTO, testcritcnt, CTLFLAG_RW, &ktr_testcritcnt, 0, "");
static int ktr_testspincnt = 0;
SYSCTL_INT(_debug_ktr, OID_AUTO, testspincnt, CTLFLAG_RW, &ktr_testspincnt, 0, "");
#endif
static struct ktr_entry ktr_buf0[KTR_ENTRIES_BOOT0];
struct ktr_cpu ktr_cpu[MAXCPU] = {
{ .core.ktr_buf = &ktr_buf0[0] }
};
static int64_t ktr_sync_tsc;
struct callout ktr_resync_callout;
#ifdef KTR_VERBOSE
int ktr_verbose = KTR_VERBOSE;
TUNABLE_INT("debug.ktr.verbose", &ktr_verbose);
SYSCTL_INT(_debug_ktr, OID_AUTO, verbose, CTLFLAG_RW, &ktr_verbose, 0,
"Log events to the console as well");
#endif
static void ktr_resync_callback(void *dummy __unused);
extern int64_t tsc_offsets[];
static void
ktr_sysinit(void *dummy)
{
struct ktr_cpu_core *kcpu;
int i;
for (i = 0; i < ncpus; ++i) {
kcpu = &ktr_cpu[i].core;
kcpu->ktr_buf = kmalloc(KTR_ENTRIES * sizeof(struct ktr_entry),
M_KTR, M_WAITOK | M_ZERO);
if (i == 0) {
memcpy(kcpu->ktr_buf, ktr_buf0, sizeof(ktr_buf0));
}
}
cpu_sfence();
ktr_entries = KTR_ENTRIES;
ktr_entries_mask = KTR_ENTRIES_MASK;
callout_init_mp(&ktr_resync_callout);
callout_reset(&ktr_resync_callout, hz / 10, ktr_resync_callback, NULL);
}
SYSINIT(ktr_sysinit, SI_BOOT2_KLD, SI_ORDER_ANY, ktr_sysinit, NULL);
#if KTR_TESTLOG
static void ktr_pingpong_remote(void *dummy);
static void ktr_pipeline_remote(void *dummy);
#endif
#ifdef _RDTSC_SUPPORTED_
static void ktr_resync_remote(void *dummy);
static
void
ktr_resync_callback(void *dummy __unused)
{
struct lwkt_cpusync cs;
#if KTR_TESTLOG
int count;
#endif
KKASSERT(mycpu->gd_cpuid == 0);
#if KTR_TESTLOG
if (ktr_testlogcnt) {
--ktr_testlogcnt;
cpu_disable_intr();
KTR_LOG(testlog_charfmt,
(signed char)UCHAR_MAX, (signed char)UCHAR_MAX,
(unsigned char)-1, (unsigned char)-1,
(unsigned char)-1, (unsigned char)-1);
KTR_LOG(testlog_shortfmt,
(short)USHRT_MAX, (short)USHRT_MAX,
(unsigned short)-1, (unsigned short)-1,
(unsigned short)-1, (unsigned short)-1);
KTR_LOG(testlog_longfmt,
(long)ULONG_MAX, (long)ULONG_MAX,
(unsigned long)-1, (unsigned long)-1,
(unsigned long)-1, (unsigned long)-1);
KTR_LOG(testlog_longlongfmt,
(long long)ULLONG_MAX, (long long)ULLONG_MAX,
(unsigned long long)-1, (unsigned long long)-1,
(unsigned long long)-1, (unsigned long long)-1);
KTR_LOG(testlog_intmaxfmt,
(intmax_t)UINTMAX_MAX, (intmax_t)UINTMAX_MAX,
(uintmax_t)-1, (uintmax_t)-1,
(uintmax_t)-1, (uintmax_t)-1);
KTR_LOG(testlog_ptrdifffmt,
(ptrdiff_t)PTRDIFF_MAX, (ptrdiff_t)PTRDIFF_MAX);
KTR_LOG(testlog_sizefmt,
(ssize_t)SIZE_T_MAX, (ssize_t)SIZE_T_MAX,
(size_t)-1, (size_t)-1,
(size_t)-1, (size_t)-1);
cpu_enable_intr();
}
if (ktr_testipicnt && ktr_testipicnt_remainder == 0 && ncpus > 1) {
ktr_testipicnt_remainder = ktr_testipicnt;
ktr_testipicnt = 0;
lwkt_send_ipiq_bycpu(1, ktr_pingpong_remote, NULL);
}
if (ktr_testcritcnt) {
crit_enter();
crit_exit();
logtest_noargs(crit_beg);
for (count = ktr_testcritcnt; count; --count) {
crit_enter();
crit_exit();
}
logtest_noargs(crit_end);
ktr_testcritcnt = 0;
}
if (ktr_testspincnt) {
struct spinlock spin;
spin_init(&spin, "ktrresync");
spin_lock(&spin);
spin_unlock(&spin);
logtest_noargs(spin_beg);
for (count = ktr_testspincnt; count; --count) {
spin_lock(&spin);
spin_unlock(&spin);
}
logtest_noargs(spin_end);
ktr_testspincnt = 0;
}
#endif
if (ktr_resynchronize == 0)
goto done;
if ((cpu_feature & CPUID_TSC) == 0)
return;
crit_enter();
lwkt_cpusync_init(&cs, smp_active_mask, ktr_resync_remote,
(void *)(intptr_t)mycpu->gd_cpuid);
lwkt_cpusync_interlock(&cs);
ktr_sync_tsc = rdtsc();
lwkt_cpusync_deinterlock(&cs);
crit_exit();
done:
callout_reset(&ktr_resync_callout, hz / 10, ktr_resync_callback, NULL);
}
static void
ktr_resync_remote(void *arg)
{
globaldata_t gd = mycpu;
int64_t delta;
int i;
if (gd->gd_cpuid == (int)(intptr_t)arg) {
for (i = 0; i < 2000; ++i)
ktr_sync_tsc = rdtsc();
} else {
delta = rdtsc() - ktr_sync_tsc;
if (tsc_offsets[gd->gd_cpuid] == 0)
tsc_offsets[gd->gd_cpuid] = delta;
tsc_offsets[gd->gd_cpuid] =
(tsc_offsets[gd->gd_cpuid] * 7 + delta) / 8;
}
}
#if KTR_TESTLOG
static
void
ktr_pingpong_remote(void *dummy __unused)
{
int other_cpu;
logtest_noargs(pingpong);
other_cpu = 1 - mycpu->gd_cpuid;
if (ktr_testipicnt_remainder) {
--ktr_testipicnt_remainder;
lwkt_send_ipiq_bycpu(other_cpu, ktr_pingpong_remote, NULL);
} else {
lwkt_send_ipiq_bycpu(other_cpu, ktr_pipeline_remote, NULL);
lwkt_send_ipiq_bycpu(other_cpu, ktr_pipeline_remote, NULL);
lwkt_send_ipiq_bycpu(other_cpu, ktr_pipeline_remote, NULL);
lwkt_send_ipiq_bycpu(other_cpu, ktr_pipeline_remote, NULL);
lwkt_send_ipiq_bycpu(other_cpu, ktr_pipeline_remote, NULL);
}
}
static
void
ktr_pipeline_remote(void *dummy __unused)
{
logtest_noargs(pipeline);
}
#endif
#else
static
void
ktr_resync_callback(void *dummy __unused)
{
#if KTR_TESTLOG
if (ktr_testlogcnt) {
--ktr_testlogcnt;
cpu_disable_intr();
KTR_LOG(testlog_charfmt,
(signed char)UCHAR_MAX, (signed char)UCHAR_MAX,
(unsigned char)-1, (unsigned char)-1,
(unsigned char)-1, (unsigned char)-1);
KTR_LOG(testlog_shortfmt,
(short)USHRT_MAX, (short)USHRT_MAX,
(unsigned short)-1, (unsigned short)-1,
(unsigned short)-1, (unsigned short)-1);
KTR_LOG(testlog_longfmt,
(long)ULONG_MAX, (long)ULONG_MAX,
(unsigned long)-1, (unsigned long)-1,
(unsigned long)-1, (unsigned long)-1);
KTR_LOG(testlog_longlongfmt,
(long long)ULLONG_MAX, (long long)ULLONG_MAX,
(unsigned long long)-1, (unsigned long long)-1,
(unsigned long long)-1, (unsigned long long)-1);
KTR_LOG(testlog_intmaxfmt,
(intmax_t)UINTMAX_MAX, (intmax_t)UINTMAX_MAX,
(uintmax_t)-1, (uintmax_t)-1,
(uintmax_t)-1, (uintmax_t)-1);
KTR_LOG(testlog_ptrdifffmt,
(ptrdiff_t)PTRDIFF_MAX, (ptrdiff_t)PTRDIFF_MAX);
KTR_LOG(testlog_sizefmt,
(ssize_t)SIZE_T_MAX, (ssize_t)SIZE_T_MAX,
(size_t)-1, (size_t)-1,
(size_t)-1, (size_t)-1);
cpu_enable_intr();
}
callout_reset(&ktr_resync_callout, hz / 10, ktr_resync_callback, NULL);
#endif
}
#endif
struct ktr_entry *
ktr_begin_write_entry(struct ktr_info *info, const char *file, int line)
{
struct ktr_cpu_core *kcpu;
struct ktr_entry *entry;
int cpu;
cpu = mycpu->gd_cpuid;
kcpu = &ktr_cpu[cpu].core;
if (panicstr)
return NULL;
if (kcpu->ktr_buf == NULL)
return NULL;
crit_enter();
entry = kcpu->ktr_buf + (kcpu->ktr_idx & ktr_entries_mask);
++kcpu->ktr_idx;
#ifdef _RDTSC_SUPPORTED_
if (cpu_feature & CPUID_TSC) {
entry->ktr_timestamp = rdtsc() - tsc_offsets[cpu];
} else
#endif
{
entry->ktr_timestamp = get_approximate_time_t();
}
entry->ktr_info = info;
entry->ktr_file = file;
entry->ktr_line = line;
crit_exit();
return entry;
}
int
ktr_finish_write_entry(struct ktr_info *info, struct ktr_entry *entry)
{
if (ktr_stacktrace)
cpu_ktr_caller(entry);
#ifdef KTR_VERBOSE
if (ktr_verbose && info->kf_format) {
kprintf("cpu%d ", mycpu->gd_cpuid);
if (ktr_verbose > 1) {
kprintf("%s.%d\t", entry->ktr_file, entry->ktr_line);
}
return !0;
}
#endif
return 0;
}
#ifdef DDB
#define NUM_LINES_PER_PAGE 19
struct tstate {
int cur;
int first;
};
static int db_ktr_verbose;
static int db_mach_vtrace(int cpu, struct ktr_entry *kp, int idx);
DB_SHOW_COMMAND(ktr, db_ktr_all)
{
struct ktr_cpu_core *kcpu;
int a_flag = 0;
int c;
int nl = 0;
int i;
struct tstate tstate[MAXCPU];
int printcpu = -1;
for(i = 0; i < ncpus; i++) {
kcpu = &ktr_cpu[i].core;
tstate[i].first = -1;
tstate[i].cur = (kcpu->ktr_idx - 1) & ktr_entries_mask;
}
db_ktr_verbose = 0;
while ((c = *(modif++)) != '\0') {
if (c == 'v') {
db_ktr_verbose = 1;
}
else if (c == 'a') {
a_flag = 1;
}
else if (c == 'c') {
printcpu = 0;
while ((c = *(modif++)) != '\0') {
if (isdigit(c)) {
printcpu *= 10;
printcpu += c - '0';
}
else {
modif++;
break;
}
}
modif--;
}
}
if (printcpu > ncpus - 1) {
db_printf("Invalid cpu number\n");
return;
}
while (1) {
int counter;
u_int64_t highest_ts;
struct ktr_entry *kp;
int highest_cpu;
int c;
if (a_flag == 1) {
c = cncheckc();
if (c != -1 && c != NOKEY)
return;
}
highest_ts = 0;
highest_cpu = -1;
for (i = 0, counter = 0; i < ncpus; i++) {
kcpu = &ktr_cpu[i].core;
if (kcpu->ktr_buf == NULL)
continue;
if (printcpu != -1 && printcpu != i)
continue;
if (tstate[i].cur == -1) {
counter++;
if (counter == ncpus) {
db_printf("--- End of trace buffer ---\n");
return;
}
continue;
}
if (kcpu->ktr_buf[tstate[i].cur].ktr_timestamp > highest_ts) {
highest_ts = kcpu->ktr_buf[tstate[i].cur].ktr_timestamp;
highest_cpu = i;
}
}
if (highest_cpu < 0) {
db_printf("no KTR data available\n");
break;
}
i = highest_cpu;
kcpu = &ktr_cpu[i].core;
kp = &kcpu->ktr_buf[tstate[i].cur];
if (tstate[i].first == -1)
tstate[i].first = tstate[i].cur;
if (--tstate[i].cur < 0)
tstate[i].cur = ktr_entries - 1;
if (tstate[i].first == tstate[i].cur) {
db_mach_vtrace(i, kp, tstate[i].cur + 1);
tstate[i].cur = -1;
continue;
}
if (kcpu->ktr_buf[tstate[i].cur].ktr_info == NULL)
tstate[i].cur = -1;
if (db_more(&nl) == -1)
break;
if (db_mach_vtrace(i, kp, tstate[i].cur + 1) == 0)
tstate[i].cur = -1;
}
}
static int
db_mach_vtrace(int cpu, struct ktr_entry *kp, int idx)
{
if (kp->ktr_info == NULL)
return(0);
db_printf("cpu%d ", cpu);
db_printf("%d: ", idx);
if (db_ktr_verbose) {
db_printf("%10.10lld %s.%d\t", (long long)kp->ktr_timestamp,
kp->ktr_file, kp->ktr_line);
}
db_printf("%s\t", kp->ktr_info->kf_name);
db_printf("from(%p,%p) ", kp->ktr_caller1, kp->ktr_caller2);
db_printf("\n");
return(1);
}
#endif