|
| 1 | +// Python-level interface for the _trace module. Written in HPy itself, the |
| 2 | +// idea is that it should be reusable by other implementations |
| 3 | + |
| 4 | +// NOTE: hpy.trace._trace is loaded using the UNIVERSAL ctx. To make it |
| 5 | +// clearer, we will use "uctx" and "tctx" to distinguish them. |
| 6 | + |
| 7 | +#include "hpy.h" |
| 8 | +#include "trace_internal.h" |
| 9 | + |
| 10 | +#ifndef _WIN32 |
| 11 | +#include <limits.h> |
| 12 | +#define MAX_SEC (LLONG_MAX / FREQ_NSEC) |
| 13 | +#endif |
| 14 | + |
| 15 | +HPY_MOD_EMBEDDABLE(_trace) |
| 16 | + |
| 17 | +static inline int is_empty(const char *s) |
| 18 | +{ |
| 19 | + return s[0] == '\0'; |
| 20 | +} |
| 21 | + |
| 22 | +#ifdef _WIN32 |
| 23 | +static inline HPy win_time_to_ns(HPyContext *uctx, const LONGLONG to_ns, _HPyTime_t t) |
| 24 | +{ |
| 25 | + return HPyLong_FromLongLong(uctx, t.QuadPart * to_ns); |
| 26 | +} |
| 27 | + |
| 28 | +#else |
| 29 | +static inline HPy posix_time_to_ns(HPyContext *uctx, HPy *s_to_ns, _HPyTime_t t) |
| 30 | +{ |
| 31 | + /* Fast-path: If we can fit into a signed 64-bit integer, then do the |
| 32 | + computation in C. This is the case if we can do 't.tv_sec * FREQ_SEC' |
| 33 | + (i.e. converting seconds to nanoseconds) and add 'tv.tv_nsec' without |
| 34 | + overflowing. */ |
| 35 | + if (t.tv_sec < MAX_SEC) { |
| 36 | + return HPyLong_FromLongLong(uctx, (long long)t.tv_sec * FREQ_NSEC + |
| 37 | + (long long)t.tv_nsec); |
| 38 | + } else { |
| 39 | + /* Slow-path: do the computation with an (unbound) Python long */ |
| 40 | + if (HPy_IsNull(*s_to_ns)) { |
| 41 | + *s_to_ns = HPyLong_FromLongLong(uctx, FREQ_NSEC); |
| 42 | + } |
| 43 | + |
| 44 | + HPy h_tv_sec = HPyLong_FromLongLong(uctx, t.tv_sec); |
| 45 | + HPy h_tv_sec_as_ns = HPy_Multiply(uctx, h_tv_sec, *s_to_ns); |
| 46 | + HPy_Close(uctx, h_tv_sec); |
| 47 | + |
| 48 | + HPy tv_nsec = HPyLong_FromLong(uctx, t.tv_nsec); |
| 49 | + HPy res = HPy_Add(uctx, h_tv_sec_as_ns, tv_nsec); |
| 50 | + HPy_Close(uctx, h_tv_sec_as_ns); |
| 51 | + HPy_Close(uctx, tv_nsec); |
| 52 | + |
| 53 | + return res; |
| 54 | + } |
| 55 | +} |
| 56 | +#endif |
| 57 | + |
| 58 | +HPyDef_METH(get_durations, "get_durations", HPyFunc_NOARGS) |
| 59 | +static HPy get_durations_impl(HPyContext *uctx, HPy self) |
| 60 | +{ |
| 61 | + HPyContext *tctx = hpy_trace_get_ctx(uctx); |
| 62 | + HPyTraceInfo *info = get_info(tctx); |
| 63 | + HPyTracker ht = HPyTracker_New(uctx, hpy_trace_get_nfunc()); |
| 64 | + |
| 65 | +#ifdef _WIN32 |
| 66 | + const LONGLONG to_ns = FREQ_NSEC / info->counter_freq.QuadPart; |
| 67 | +#else |
| 68 | + HPy s_to_ns = HPy_NULL; |
| 69 | +#endif |
| 70 | + HPy res = HPyDict_New(uctx); |
| 71 | + const char *func_name; |
| 72 | + for (int i=0; (func_name = hpy_trace_get_func_name(i)); i++) |
| 73 | + { |
| 74 | + /* skip empty names; those indices denote a context handle */ |
| 75 | + if (!is_empty(func_name)) |
| 76 | + { |
| 77 | +#ifdef _WIN32 |
| 78 | + HPy value = win_time_to_ns(uctx, to_ns, info->durations[i]); |
| 79 | +#else |
| 80 | + HPy value = posix_time_to_ns(uctx, &s_to_ns, info->durations[i]); |
| 81 | +#endif |
| 82 | + HPyTracker_Add(uctx, ht, value); |
| 83 | + if (HPy_IsNull(value)) |
| 84 | + goto fail; |
| 85 | + if (HPy_SetItem_s(uctx, res, func_name, value) < 0) |
| 86 | + goto fail; |
| 87 | + } |
| 88 | + } |
| 89 | +#ifndef _WIN32 |
| 90 | + HPy_Close(uctx, s_to_ns); |
| 91 | +#endif |
| 92 | + HPyTracker_Close(uctx, ht); |
| 93 | + return res; |
| 94 | +fail: |
| 95 | + HPy_Close(uctx, res); |
| 96 | + HPyTracker_Close(uctx, ht); |
| 97 | + return HPy_NULL; |
| 98 | +} |
| 99 | + |
| 100 | +HPyDef_METH(get_call_counts, "get_call_counts", HPyFunc_NOARGS) |
| 101 | +static HPy get_call_counts_impl(HPyContext *uctx, HPy self) |
| 102 | +{ |
| 103 | + HPyContext *tctx = hpy_trace_get_ctx(uctx); |
| 104 | + HPyTraceInfo *info = get_info(tctx); |
| 105 | + HPyTracker ht = HPyTracker_New(uctx, hpy_trace_get_nfunc()); |
| 106 | + HPy res = HPyDict_New(uctx); |
| 107 | + const char *func_name; |
| 108 | + for (int i=0; (func_name = hpy_trace_get_func_name(i)); i++) |
| 109 | + { |
| 110 | + /* skip empty names; those indices denote a context handle */ |
| 111 | + if (!is_empty(func_name)) |
| 112 | + { |
| 113 | + HPy value = HPyLong_FromUnsignedLongLong(uctx, |
| 114 | + (unsigned long long)info->call_counts[i]); |
| 115 | + HPyTracker_Add(uctx, ht, value); |
| 116 | + if (HPy_IsNull(value)) |
| 117 | + goto fail; |
| 118 | + if (HPy_SetItem_s(uctx, res, func_name, value) < 0) |
| 119 | + goto fail; |
| 120 | + } |
| 121 | + } |
| 122 | + HPyTracker_Close(uctx, ht); |
| 123 | + return res; |
| 124 | +fail: |
| 125 | + HPy_Close(uctx, res); |
| 126 | + HPyTracker_Close(uctx, ht); |
| 127 | + return HPy_NULL; |
| 128 | +} |
| 129 | + |
| 130 | +static int check_and_set_func(HPyContext *uctx, HPy arg, HPy *out) |
| 131 | +{ |
| 132 | + if (HPy_IsNull(arg)) { |
| 133 | + // not provided -> do not change value |
| 134 | + return 0; |
| 135 | + } else if (HPy_Is(uctx, arg, uctx->h_None)) { |
| 136 | + // None -> clear function |
| 137 | + *out = HPy_NULL; |
| 138 | + return 0; |
| 139 | + } else if (!HPyCallable_Check(uctx, arg)) { |
| 140 | + // not null, not None, not callable -> error |
| 141 | + HPyErr_SetString(uctx, uctx->h_TypeError, "Expected a callable object or None"); |
| 142 | + return -1; |
| 143 | + } |
| 144 | + // a callable -> set function |
| 145 | + *out = HPy_Dup(uctx, arg); |
| 146 | + return 0; |
| 147 | +} |
| 148 | + |
| 149 | +HPyDef_METH(set_trace_functions, "set_trace_functions", HPyFunc_KEYWORDS, |
| 150 | + .doc="Set the functions to call if an HPy API is entered/exited.") |
| 151 | +static HPy set_trace_functions_impl(HPyContext *uctx, HPy self, const HPy *args, |
| 152 | + size_t nargs, HPy kwnames) |
| 153 | +{ |
| 154 | + HPy h_on_enter = HPy_NULL; |
| 155 | + HPy h_on_exit = HPy_NULL; |
| 156 | + HPyContext *dctx = hpy_trace_get_ctx(uctx); |
| 157 | + HPyTraceInfo *info = get_info(dctx); |
| 158 | + HPyTracker ht; |
| 159 | + |
| 160 | + static const char *kwlist[] = { "on_enter", "on_exit", NULL }; |
| 161 | + if (!HPyArg_ParseKeywords(uctx, &ht, args, nargs, kwnames, "|OO", kwlist, |
| 162 | + &h_on_enter, &h_on_exit)) { |
| 163 | + return HPy_NULL; |
| 164 | + } |
| 165 | + |
| 166 | + int r = check_and_set_func(uctx, h_on_enter, &info->on_enter_func) < 0 || |
| 167 | + check_and_set_func(uctx, h_on_exit, &info->on_exit_func) < 0; |
| 168 | + HPyTracker_Close(uctx, ht); |
| 169 | + if (r) { |
| 170 | + return HPy_NULL; |
| 171 | + } |
| 172 | + return HPy_Dup(uctx, uctx->h_None); |
| 173 | +} |
| 174 | + |
| 175 | +HPyDef_METH(get_frequency, "get_frequency", HPyFunc_NOARGS, |
| 176 | + .doc="Resolution of the used clock in Hertz.") |
| 177 | +static HPy get_frequency_impl(HPyContext *uctx, HPy self) |
| 178 | +{ |
| 179 | + HPyContext *tctx = hpy_trace_get_ctx(uctx); |
| 180 | + HPyTraceInfo *info = get_info(tctx); |
| 181 | +#ifdef _WIN32 |
| 182 | + long long f = (long long) info->counter_freq.QuadPart; |
| 183 | +#else |
| 184 | + long long f = (long long) info->counter_freq.tv_sec + |
| 185 | + (long long)info->counter_freq.tv_nsec * FREQ_NSEC; |
| 186 | +#endif |
| 187 | + return HPyLong_FromLongLong(uctx, f); |
| 188 | +} |
| 189 | + |
| 190 | + |
| 191 | +/* ~~~~~~ definition of the module hpy.trace._trace ~~~~~~~ */ |
| 192 | + |
| 193 | +static HPyDef *module_defines[] = { |
| 194 | + &get_durations, |
| 195 | + &get_call_counts, |
| 196 | + &set_trace_functions, |
| 197 | + &get_frequency, |
| 198 | + NULL |
| 199 | +}; |
| 200 | + |
| 201 | +static HPyModuleDef moduledef = { |
| 202 | + .doc = "HPy trace mode", |
| 203 | + .size = 0, |
| 204 | + .defines = module_defines |
| 205 | +}; |
| 206 | + |
| 207 | +HPy_MODINIT(_trace, moduledef) |
0 commit comments