/* * phc_phase_log - 1Hz logger of CLOCK_REALTIME vs ENA PHC, plus kernel * NTP discipline state, for chrony A/B benchmarking. * * Per sample: * - PTP_SYS_OFFSET_EXTENDED (n samples, keep the tightest bracket): * realtime - phc, with bracket width as quality bound * - adjtimex() readback: freq, offset, status * - /sys/kernel/debug/tk_sample: real_ns snapshot_err_ns ntp_error * mult ntp_err_mult ntp_err_frac * * CSV to stdout (append via shell): * iso8601,phc_offset_ns,bracket_ns,adj_freq_scaled,adj_offset,adj_status, * snap_err_ns,ntp_error,mult,ntp_err_mult,ntp_err_frac * * Usage: phc_phase_log [/dev/ptp0 [interval_s]] >> phc_phase.csv */ #include #include #include #include #include #include #include #include #include #include #include #define NSAMPLES 5 static int64_t ts_ns(const struct ptp_clock_time *t) { return (int64_t)t->sec * 1000000000LL + t->nsec; } int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/ptp0"; int interval = argc > 2 ? atoi(argv[2]) : 1; int fd = open(dev, O_RDONLY); int tkfd; if (fd < 0) { perror(dev); return 1; } tkfd = open("/sys/kernel/debug/tk_sample", O_RDONLY); printf("timestamp,phc_offset_ns,bracket_ns,adj_freq_scaled," "adj_offset,adj_status,snap_err_ns,ntp_error,mult," "ntp_err_mult,ntp_err_frac\n"); for (;;) { struct ptp_sys_offset_extended sox = { .n_samples = NSAMPLES }; int64_t best_off = 0, best_br = INT64_MAX; char tkbuf[128] = "0 0 0 0 0 0"; struct timex tx = { 0 }; char tbuf[64]; struct tm tm; time_t now; if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, &sox) < 0) { perror("PTP_SYS_OFFSET_EXTENDED"); sleep(interval); continue; } for (unsigned int i = 0; i < sox.n_samples; i++) { int64_t t1 = ts_ns(&sox.ts[i][0]); int64_t tp = ts_ns(&sox.ts[i][1]); int64_t t2 = ts_ns(&sox.ts[i][2]); if (t2 - t1 < best_br) { best_br = t2 - t1; best_off = (t1 + t2) / 2 - tp; } } adjtimex(&tx); if (tkfd >= 0) { ssize_t n = pread(tkfd, tkbuf, sizeof(tkbuf) - 1, 0); if (n > 0) { tkbuf[n] = 0; tkbuf[strcspn(tkbuf, "\n")] = 0; } } /* tk_sample: real snap_err ntp_error mult err_mult frac */ long long tk_real, tk_snap_err, tk_ntp_error, tk_frac; unsigned int tk_mult, tk_err_mult; if (sscanf(tkbuf, "%lld %lld %lld %u %u %lld", &tk_real, &tk_snap_err, &tk_ntp_error, &tk_mult, &tk_err_mult, &tk_frac) != 6) tk_snap_err = tk_ntp_error = tk_mult = tk_err_mult = tk_frac = 0; time(&now); gmtime_r(&now, &tm); strftime(tbuf, sizeof(tbuf), "%Y-%m-%dT%H:%M:%SZ", &tm); printf("%s,%lld,%lld,%ld,%ld,0x%x,%lld,%lld,%u,%u,%lld\n", tbuf, (long long)best_off, (long long)best_br, tx.freq, tx.offset, tx.status, tk_snap_err, tk_ntp_error, tk_mult, tk_err_mult, tk_frac); fflush(stdout); sleep(interval); } }