Print this page
11585 ::scalehrtime could be more precise
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>

*** 768,777 **** --- 768,797 ---- "rooted at the given cr3 value / physical address.\n" "\n" "-w run ::whatis on mapping start addresses\n"); } + static const char *const scalehrtime_desc = + "Scales a timestamp from ticks to nanoseconds. Unscaled timestamps\n" + "are used as both a quick way of accumulating relative time (as for\n" + "usage) and as a quick way of getting the absolute current time.\n" + "These uses require slightly different scaling algorithms. By\n" + "default, if a specified time is greater than half of the unscaled\n" + "time at the last tick (that is, if the unscaled time represents\n" + "more than half the time since boot), the timestamp is assumed to\n" + "be absolute, and the scaling algorithm used mimics that which the\n" + "kernel uses in gethrtime(). Otherwise, the timestamp is assumed to\n" + "be relative, and the algorithm mimics scalehrtime(). This behavior\n" + "can be overridden by forcing the unscaled time to be interpreted\n" + "as relative (via -r) or absolute (via -a).\n"; + + static void + scalehrtime_help(void) + { + mdb_printf("%s", scalehrtime_desc); + } + /* * NSEC_SHIFT is replicated here (it is not defined in a header file), * but for amusement, the reader is directed to the comment that explains * the rationale for this particular value on x86. Spoiler: the value is * selected to accommodate 60 MHz Pentiums! (And a confession: if the voice
*** 783,808 **** /*ARGSUSED*/ static int scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint32_t nsec_scale; ! hrtime_t tsc = addr, hrt; unsigned int *tscp = (unsigned int *)&tsc; uintptr_t scalehrtimef; uint64_t scale; GElf_Sym sym; ! if (!(flags & DCMD_ADDRSPEC)) { ! if (argc != 1) return (DCMD_USAGE); ! switch (argv[0].a_type) { case MDB_TYPE_STRING: ! tsc = mdb_strtoull(argv[0].a_un.a_str); break; case MDB_TYPE_IMMEDIATE: ! tsc = argv[0].a_un.a_val; break; default: return (DCMD_USAGE); } } --- 803,837 ---- /*ARGSUSED*/ static int scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint32_t nsec_scale; ! hrtime_t tsc = addr, hrt, tsc_last, base, mult = 1; unsigned int *tscp = (unsigned int *)&tsc; uintptr_t scalehrtimef; uint64_t scale; GElf_Sym sym; + int expected = !(flags & DCMD_ADDRSPEC); + uint_t absolute = FALSE, relative = FALSE; ! if (mdb_getopts(argc, argv, ! 'a', MDB_OPT_SETBITS, TRUE, &absolute, ! 'r', MDB_OPT_SETBITS, TRUE, &relative, NULL) != argc - expected) return (DCMD_USAGE); ! if (absolute && relative) { ! mdb_warn("can't specify both -a and -r\n"); ! return (DCMD_USAGE); ! } ! ! if (expected == 1) { ! switch (argv[argc - 1].a_type) { case MDB_TYPE_STRING: ! tsc = mdb_strtoull(argv[argc - 1].a_un.a_str); break; case MDB_TYPE_IMMEDIATE: ! tsc = argv[argc - 1].a_un.a_val; break; default: return (DCMD_USAGE); } }
*** 827,842 **** if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) { mdb_warn("couldn't read 'nsec_scale'"); return (DCMD_ERR); } scale = (uint64_t)nsec_scale; hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT; hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT); ! mdb_printf("0x%llx\n", hrt); return (DCMD_OK); } /* --- 856,899 ---- if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) { mdb_warn("couldn't read 'nsec_scale'"); return (DCMD_ERR); } + if (mdb_readsym(&tsc_last, sizeof (tsc_last), "tsc_last") == -1) { + mdb_warn("couldn't read 'tsc_last'"); + return (DCMD_ERR); + } + + if (mdb_readsym(&base, sizeof (base), "tsc_hrtime_base") == -1) { + mdb_warn("couldn't read 'tsc_hrtime_base'"); + return (DCMD_ERR); + } + + /* + * If our time is greater than half of tsc_last, we will take our + * delta against tsc_last, convert it, and add that to (or subtract it + * from) tsc_hrtime_base. This mimics what the kernel actually does + * in gethrtime() (modulo the tsc_sync_tick_delta) and gets us a much + * higher precision result than trying to convert a large tsc value. + */ + if (absolute || (tsc > (tsc_last >> 1) && !relative)) { + if (tsc > tsc_last) { + tsc = tsc - tsc_last; + } else { + tsc = tsc_last - tsc; + mult = -1; + } + } else { + base = 0; + } + scale = (uint64_t)nsec_scale; hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT; hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT); ! mdb_printf("0x%llx\n", base + (hrt * mult)); return (DCMD_OK); } /*
*** 998,1009 **** { "pfntomfn", ":", "convert physical page to hypervisor machine page", pfntomfn_dcmd }, { "mfntopfn", ":", "convert hypervisor machine page to physical page", mfntopfn_dcmd }, { "memseg_list", ":", "show memseg list", memseg_list }, ! { "scalehrtime", ":", ! "scale an unscaled high-res time", scalehrtime_cmd }, { "x86_featureset", NULL, "dump the x86_featureset vector", x86_featureset_cmd }, #ifdef _KMDB { "sysregs", NULL, "dump system registers", sysregs_dcmd }, #endif --- 1055,1066 ---- { "pfntomfn", ":", "convert physical page to hypervisor machine page", pfntomfn_dcmd }, { "mfntopfn", ":", "convert hypervisor machine page to physical page", mfntopfn_dcmd }, { "memseg_list", ":", "show memseg list", memseg_list }, ! { "scalehrtime", ":[-a|-r]", "scale an unscaled high-res time", ! scalehrtime_cmd, scalehrtime_help }, { "x86_featureset", NULL, "dump the x86_featureset vector", x86_featureset_cmd }, #ifdef _KMDB { "sysregs", NULL, "dump system registers", sysregs_dcmd }, #endif