Here's a quick test program I wrote that reaches into libc internals to directly exercise both methods of getting a timestamp:
```
/*
* Demonstration of FreeBSD vdso gettimeofday().
* MUST be compiled static due to abuse of binding to weak symbols.
*
* cc -static -o gettimeofday_vdso gettimeofday_vdso.c
*/
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <sys/vdso.h>
extern int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz);
extern int __sys_gettimeofday(struct timeval *tv, struct timezone *tz);
int
main(int argc, char *argv[])
{
struct timeval tv[5];
int i, j, err;
for (i = 0; i < 10; i++) {
for (j = 0; j < 4; j++) {
err = __vdso_gettimeofday(&tv[j], 0);
}
for (j = 0; j < 4; j++) {
if (err)
printf("VDSO call failed. %s\n", strerror(err));
else
printf("VDSO: %ju.%ju\n", (uintmax_t)tv[j].tv_sec, (uintmax_t)tv[j].tv_usec);
}
for (j = 0; j < 4; j++) {
err = __sys_gettimeofday(&tv[j], 0);
}
for (j = 0; j < 4; j++) {
if (err)
printf("Fallback call failed. (?) %s\n", strerror(err));
else
printf("SYSCALL: %ju.%ju\n", (uintmax_t)tv[j].tv_sec, (uintmax_t)tv[j].tv_usec);
}
}
return (0);
}
```
And here is a test program I wrote to dump the timekeeping information on the VDSO page.
```
/*
* Test program to decode the portable parts of the timekeeping structure
* on the shared page on FreeBSD.
*/
#include <sys/types.h>
#include <sys/elf.h>
#include <sys/auxv.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/vdso.h>
/* See sys/sys/vdso.h */
#define VDSO_TH_NUM 4
int
main(int argc, char *argv[])
{
struct vdso_timekeep *tk;
int i;
if (elf_aux_info(AT_TIMEKEEP, &tk, sizeof(tk))) {
printf("No AT_TIMEKEEP?\n");
}
printf("Version: %d Enabled: %d Current: %d\n", tk->tk_ver, tk->tk_enabled, tk->tk_current);
for (i = 0; i < VDSO_TH_NUM; i++) {
printf("th %u algo %u gen %u scale 0x%jx offset_count %u counter_mask 0x%x offset %ju.%ju boottime %ju.%ju\n",
i, tk->tk_th[i].th_algo, tk->tk_th[i].th_gen, (uintmax_t)tk->tk_th[i].th_scale, tk->tk_th[i].th_offset_count,
tk->tk_th[i].th_counter_mask,
(uintmax_t)tk->tk_th[i].th_offset.sec, (uintmax_t)tk->tk_th[i].th_offset.frac,
(uintmax_t)tk->tk_th[i].th_boottime.sec, (uintmax_t)tk->tk_th[i].th_boottime.frac);
}
return (0);
}
```