linux/tools/perf/util/tsc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <errno.h>
   3#include <inttypes.h>
   4#include <string.h>
   5
   6#include <linux/compiler.h>
   7#include <linux/perf_event.h>
   8#include <linux/stddef.h>
   9#include <linux/types.h>
  10
  11#include <asm/barrier.h>
  12
  13#include "event.h"
  14#include "synthetic-events.h"
  15#include "debug.h"
  16#include "tsc.h"
  17
  18u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc)
  19{
  20        u64 t, quot, rem;
  21
  22        t = ns - tc->time_zero;
  23        quot = t / tc->time_mult;
  24        rem  = t % tc->time_mult;
  25        return (quot << tc->time_shift) +
  26               (rem << tc->time_shift) / tc->time_mult;
  27}
  28
  29u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
  30{
  31        u64 quot, rem;
  32
  33        if (tc->cap_user_time_short)
  34                cyc = tc->time_cycles +
  35                        ((cyc - tc->time_cycles) & tc->time_mask);
  36
  37        quot = cyc >> tc->time_shift;
  38        rem  = cyc & (((u64)1 << tc->time_shift) - 1);
  39        return tc->time_zero + quot * tc->time_mult +
  40               ((rem * tc->time_mult) >> tc->time_shift);
  41}
  42
  43int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
  44                             struct perf_tsc_conversion *tc)
  45{
  46        u32 seq;
  47        int i = 0;
  48
  49        while (1) {
  50                seq = pc->lock;
  51                rmb();
  52                tc->time_mult = pc->time_mult;
  53                tc->time_shift = pc->time_shift;
  54                tc->time_zero = pc->time_zero;
  55                tc->time_cycles = pc->time_cycles;
  56                tc->time_mask = pc->time_mask;
  57                tc->cap_user_time_zero = pc->cap_user_time_zero;
  58                tc->cap_user_time_short = pc->cap_user_time_short;
  59                rmb();
  60                if (pc->lock == seq && !(seq & 1))
  61                        break;
  62                if (++i > 10000) {
  63                        pr_debug("failed to get perf_event_mmap_page lock\n");
  64                        return -EINVAL;
  65                }
  66        }
  67
  68        if (!tc->cap_user_time_zero)
  69                return -EOPNOTSUPP;
  70
  71        return 0;
  72}
  73
  74int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
  75                                struct perf_tool *tool,
  76                                perf_event__handler_t process,
  77                                struct machine *machine)
  78{
  79        union perf_event event = {
  80                .time_conv = {
  81                        .header = {
  82                                .type = PERF_RECORD_TIME_CONV,
  83                                .size = sizeof(struct perf_record_time_conv),
  84                        },
  85                },
  86        };
  87        struct perf_tsc_conversion tc;
  88        int err;
  89
  90        if (!pc)
  91                return 0;
  92        err = perf_read_tsc_conversion(pc, &tc);
  93        if (err == -EOPNOTSUPP)
  94                return 0;
  95        if (err)
  96                return err;
  97
  98        pr_debug2("Synthesizing TSC conversion information\n");
  99
 100        event.time_conv.time_mult  = tc.time_mult;
 101        event.time_conv.time_shift = tc.time_shift;
 102        event.time_conv.time_zero  = tc.time_zero;
 103        event.time_conv.time_cycles = tc.time_cycles;
 104        event.time_conv.time_mask = tc.time_mask;
 105        event.time_conv.cap_user_time_zero = tc.cap_user_time_zero;
 106        event.time_conv.cap_user_time_short = tc.cap_user_time_short;
 107
 108        return process(tool, &event, NULL, machine);
 109}
 110
 111u64 __weak rdtsc(void)
 112{
 113        return 0;
 114}
 115
 116size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp)
 117{
 118        struct perf_record_time_conv *tc = (struct perf_record_time_conv *)event;
 119        size_t ret;
 120
 121        ret  = fprintf(fp, "\n... Time Shift      %" PRI_lu64 "\n", tc->time_shift);
 122        ret += fprintf(fp, "... Time Muliplier  %" PRI_lu64 "\n", tc->time_mult);
 123        ret += fprintf(fp, "... Time Zero       %" PRI_lu64 "\n", tc->time_zero);
 124
 125        /*
 126         * The event TIME_CONV was extended for the fields from "time_cycles"
 127         * when supported cap_user_time_short, for backward compatibility,
 128         * prints the extended fields only if they are contained in the event.
 129         */
 130        if (event_contains(*tc, time_cycles)) {
 131                ret += fprintf(fp, "... Time Cycles     %" PRI_lu64 "\n",
 132                               tc->time_cycles);
 133                ret += fprintf(fp, "... Time Mask       %#" PRI_lx64 "\n",
 134                               tc->time_mask);
 135                ret += fprintf(fp, "... Cap Time Zero   %" PRId32 "\n",
 136                               tc->cap_user_time_zero);
 137                ret += fprintf(fp, "... Cap Time Short  %" PRId32 "\n",
 138                               tc->cap_user_time_short);
 139        }
 140
 141        return ret;
 142}
 143