The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
time_tracking.h
Go to the documentation of this file.
1#pragma once
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16 */
17
18/**
19 * $Id: 720f54f7672c4d4e29610938413cbaa61d830b5b $
20 *
21 * @file lib/io/time_tracking.h
22 * @brief Request time tracking
23 *
24 * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 * @copyright 2016 Alan DeKok (aland@freeradius.org)
26 */
27RCSIDH(time_tracking_h, "$Id: 720f54f7672c4d4e29610938413cbaa61d830b5b $")
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#include <freeradius-devel/util/debug.h>
34#include <freeradius-devel/util/table.h>
35#include <freeradius-devel/util/time.h>
36
39
40typedef enum {
41 FR_TIME_TRACKING_STOPPED = 0, //!< Time tracking is not running.
42 FR_TIME_TRACKING_RUNNING, //!< We're currently tracking time in the
43 ///< running state.
44 FR_TIME_TRACKING_YIELDED //!< We're currently tracking time in the
45 ///< yielded state.
47
48/** A structure to track the time spent processing a request.
49 *
50 * The same structure is used by threads to track when they are
51 * running / waiting. The functions modifying fr_time_tracking_t all
52 * take an explicit "when" parameter. This parameter allows the
53 * thread to update a requests tracking structure, and then use that
54 * same fr_time_t to update the threads tracking structure.
55 *
56 * While fr_time() is fast, it is also called very often. We should
57 * therefore be careful to call it only when necessary.
58 */
61 fr_time_tracking_state_t state; //!< What state we're currently in.
62 ///< only used for the leaf node.
63 fr_time_t last_changed; //!< last time we changed a field
64
65 fr_time_t started; //!< Last time this tracked entity or a child
66 ///< entered the running state, or entered
67 ///< a time tracked parent.
68
69 fr_time_t last_yielded; //!< Last time this tracked entity or a child
70 ///< yielded.
71
72 fr_time_t last_resumed; //!< Last time this tracked entity or a child
73 ///< resumed;
74
75 fr_time_t ended; //!< Last time this tracked entity or a child
76 ///< left the running state, or popped a time
77 ///< tracked parent.
78
79 fr_time_delta_t running_total; //!< total time spent running
80 fr_time_delta_t waiting_total; //!< total time spent waiting
81
82 fr_time_tracking_t *parent; //!< To update with our time tracking data when
83 ///< tracking is complete.
84};
85
86/** We use a monotonic time source
87 *
88 */
89#define ASSERT_ON_TIME_TRAVEL(_tt, _now) \
90do { \
91 fr_assert(fr_time_lteq((_tt)->last_changed, (_now))); \
92 fr_assert(fr_time_lteq((_tt)->started, (_now))); \
93 fr_assert(fr_time_lteq((_tt)->ended, (_now))); \
94 fr_assert(fr_time_lteq((_tt)->last_yielded, (_now))); \
95 fr_assert(fr_time_lteq((_tt)->last_resumed, (_now))); \
96} while(0)
97
98/** Set the last time a tracked entity started in its list of parents
99 *
100 */
101#define UPDATE_PARENT_START_TIME(_tt, _now) \
102do { \
103 fr_time_tracking_t *_parent; \
104 for (_parent = (_tt)->parent; _parent; _parent = _parent->parent) { \
105 _parent->started = _now; \
106 _parent->last_changed = _now; \
107 } \
108} while (0)
109
110/** Update total run time up the list of parents
111 *
112 */
113#define UPDATE_PARENT_RUN_TIME(_tt, _run_time, _event, _now) \
114do { \
115 fr_time_tracking_t *_parent; \
116 for (_parent = (_tt)->parent; _parent; _parent = _parent->parent) { \
117 _parent->_event = _now; \
118 _parent->last_changed = _now; \
119 _parent->running_total = fr_time_delta_add(_parent->running_total, _run_time); \
120 } \
121} while (0)
122
123/** Update total wait time up the list of parents
124 *
125 */
126#define UPDATE_PARENT_WAIT_TIME(_tt, _wait_time, _event, _now) \
127do { \
128 fr_time_tracking_t *_parent; \
129 for (_parent = (_tt)->parent; _parent; _parent = _parent->parent){ \
130 _parent->_event = _now; \
131 _parent->last_changed = _now; \
132 _parent->waiting_total = fr_time_delta_add(_parent->waiting_total, _wait_time); \
133 } \
134} while (0)
135
136/** Initialise a time tracking structure
137 *
138 */
139static inline CC_HINT(nonnull) void fr_time_tracking_init(fr_time_tracking_t *tt)
140{
141 memset(tt, 0, sizeof(*tt));
142}
143
144/** Start time tracking for a tracked entity
145 *
146 * Should be called when the tracked entity starts running.
147 *
148 * @param[in] parent to update when time tracking ends.
149 * @param[in] tt the time tracked entity.
150 * @param[in] now the current time.
151 */
154{
155
156 fr_assert_msg(tt->state == FR_TIME_TRACKING_STOPPED, "Unexpected time tracking state state %s",
157 fr_table_str_by_value(fr_time_tracking_state_table, tt->state, "<INVALID>"));
158 fr_assert(!tt->parent);
159
160 ASSERT_ON_TIME_TRAVEL(tt, now);
161
162 tt->state = FR_TIME_TRACKING_RUNNING;
163 tt->started = tt->last_changed = tt->last_resumed = now;
164
165 tt->parent = parent;
166
168}
169
170/** Tracked entity entered a deeper time tracked code area
171 *
172 * @param[in] parent we entered. Must be a direct descendent of the
173 * current tt->parent.
174 * @param[in] tt the time tracked entity.
175 * @param[in] now the current time.
176 */
179{
180 fr_time_delta_t run_time;
181
182 fr_assert(tt->parent);
183 fr_assert(parent->parent == tt->parent);
184
185 fr_assert_msg(tt->state == FR_TIME_TRACKING_RUNNING, "Unexpected time tracking state state %s",
187 run_time = fr_time_sub(now, tt->last_changed);
188 tt->last_changed = parent->started = now;
189
190 UPDATE_PARENT_RUN_TIME(tt, run_time, last_changed, now);
191
192 tt->parent = parent;
193}
194
195/** Tracked entity left a tracked nested code area
196 *
197 * Updates parent to point to the current time tracking parent.
198 *
199 * @param[in] tt the time tracked entity.
200 * @param[in] now the current time.
201 */
202static inline CC_HINT(nonnull) void fr_time_tracking_pop(fr_time_tracking_t *tt, fr_time_t now)
203{
204 fr_time_delta_t run_time;
205
206 fr_assert_msg(tt->state == FR_TIME_TRACKING_RUNNING, "Unexpected time tracking state state %s",
208 fr_assert(tt->parent);
209
210 run_time = fr_time_sub(now, tt->last_changed);
211 tt->last_changed = tt->parent->ended = now;
212
213 tt->running_total = fr_time_delta_add(tt->running_total, run_time);
214 UPDATE_PARENT_RUN_TIME(tt, run_time, last_changed, now);
215
216 tt->parent = tt->parent->parent;
217}
218
219/** Transition to the yielded state, recording the time we just spent running
220 *
221 * @param[in] tt the time tracked entity.
222 * @param[in] now the current time.
223 */
224static inline CC_HINT(nonnull) void fr_time_tracking_yield(fr_time_tracking_t *tt, fr_time_t now)
225{
226 fr_time_delta_t run_time;
227
228 ASSERT_ON_TIME_TRAVEL(tt, now);
229
230 fr_assert_msg(tt->state == FR_TIME_TRACKING_RUNNING, "Unexpected time tracking state state %s",
233 tt->last_yielded = tt->last_changed = now;
234
235 run_time = fr_time_sub(now, tt->last_resumed);
236 tt->running_total = fr_time_delta_add(tt->running_total, run_time);
237 UPDATE_PARENT_RUN_TIME(tt, run_time, last_yielded, now);
238}
239
240/** Track that a request resumed.
241 *
242 * @param[in] tt the time tracked entity.
243 * @param[in] now the current time.
244 */
245static inline CC_HINT(nonnull) void fr_time_tracking_resume(fr_time_tracking_t *tt, fr_time_t now)
246{
247 fr_time_delta_t wait_time;
248
249 ASSERT_ON_TIME_TRAVEL(tt, now);
250
251 fr_assert_msg(tt->state == FR_TIME_TRACKING_YIELDED, "Unexpected time tracking state state %s",
254 tt->last_resumed = tt->last_changed = now;
255
256 wait_time = fr_time_sub(now, tt->last_yielded);
257 tt->waiting_total = fr_time_delta_add(tt->waiting_total, wait_time);
258 UPDATE_PARENT_WAIT_TIME(tt, wait_time, last_resumed, now);
259}
260
261#define IALPHA (8)
262#define RTT(_old, _new) fr_time_delta_wrap((fr_time_delta_unwrap(_new) + (fr_time_delta_unwrap(_old) * (IALPHA - 1))) / IALPHA)
263
264/** End time tracking for this entity
265 *
266 * @param[in,out] predicted Update our prediction of how long requests should run for.
267 * @param[in] tt the time tracking structure.
268 * @param[in] now the current time.
269 */
270static inline void fr_time_tracking_end(fr_time_delta_t *predicted,
272{
273 fr_time_delta_t run_time;
274
275 fr_assert_msg(tt->state == FR_TIME_TRACKING_RUNNING, "Unexpected time tracking state state %s",
277 ASSERT_ON_TIME_TRAVEL(tt, now);
278
280 tt->ended = tt->last_changed = now;
281
282 run_time = fr_time_sub(now, tt->last_resumed);
283 tt->running_total = fr_time_delta_add(tt->running_total, run_time);
284 UPDATE_PARENT_RUN_TIME(tt, run_time, ended, now);
285
286 if (predicted) {
287 *predicted = !fr_time_delta_ispos(*predicted) ?
288 tt->running_total : RTT((*predicted), tt->running_total);
289 }
290
291 tt->parent = NULL;
292}
293
294/** Print debug information about the time tracking structure
295 *
296 * @param[in] tt the time tracking structure
297 * @param[in] fp the file where the debug output is printed.
298 */
299static inline CC_HINT(nonnull) void fr_time_tracking_debug(fr_time_tracking_t *tt, FILE *fp)
300{
301#define DPRINT_TIME(_x) fprintf(fp, "\t" #_x " = %"PRId64"\n", fr_time_unwrap(tt->_x))
302#define DPRINT(_x) fprintf(fp, "\t" #_x " = %"PRId64"\n", fr_time_delta_unwrap(tt->_x))
303
304 DPRINT_TIME(started);
305 DPRINT_TIME(ended);
306 DPRINT_TIME(last_changed);
307
308 DPRINT_TIME(last_yielded);
309 DPRINT_TIME(last_resumed);
310
311 DPRINT(running_total);
312 DPRINT(waiting_total);
313}
314
315#ifdef __cplusplus
316}
317#endif
#define RCSIDH(h, id)
Definition build.h:484
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define fr_assert(_expr)
Definition rad_assert.h:38
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
static fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:255
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
fr_time_t last_changed
last time we changed a field
size_t fr_time_tracking_state_table_len
fr_time_tracking_state_t
@ FR_TIME_TRACKING_YIELDED
We're currently tracking time in the yielded state.
@ FR_TIME_TRACKING_STOPPED
Time tracking is not running.
@ FR_TIME_TRACKING_RUNNING
We're currently tracking time in the running state.
fr_time_tracking_state_t state
What state we're currently in.
static void fr_time_tracking_yield(fr_time_tracking_t *tt, fr_time_t now)
Transition to the yielded state, recording the time we just spent running.
fr_time_t last_resumed
Last time this tracked entity or a child resumed;.
static void fr_time_tracking_end(fr_time_delta_t *predicted, fr_time_tracking_t *tt, fr_time_t now)
End time tracking for this entity.
#define RTT(_old, _new)
fr_table_num_ordered_t fr_time_tracking_state_table[]
#define UPDATE_PARENT_WAIT_TIME(_tt, _wait_time, _event, _now)
Update total wait time up the list of parents.
#define UPDATE_PARENT_START_TIME(_tt, _now)
Set the last time a tracked entity started in its list of parents.
#define DPRINT(_x)
fr_time_t started
Last time this tracked entity or a child entered the running state, or entered a time tracked parent.
static void fr_time_tracking_init(fr_time_tracking_t *tt)
Initialise a time tracking structure.
static void fr_time_tracking_pop(fr_time_tracking_t *tt, fr_time_t now)
Tracked entity left a tracked nested code area.
fr_time_delta_t waiting_total
total time spent waiting
fr_time_delta_t running_total
total time spent running
fr_time_t ended
Last time this tracked entity or a child left the running state, or popped a time tracked parent.
#define DPRINT_TIME(_x)
static void fr_time_tracking_start(fr_time_tracking_t *parent, fr_time_tracking_t *tt, fr_time_t now)
Start time tracking for a tracked entity.
static void fr_time_tracking_resume(fr_time_tracking_t *tt, fr_time_t now)
Track that a request resumed.
fr_time_t last_yielded
Last time this tracked entity or a child yielded.
fr_time_tracking_t * parent
To update with our time tracking data when tracking is complete.
#define ASSERT_ON_TIME_TRAVEL(_tt, _now)
We use a monotonic time source.
#define UPDATE_PARENT_RUN_TIME(_tt, _run_time, _event, _now)
Update total run time up the list of parents.
static void fr_time_tracking_push(fr_time_tracking_t *parent, fr_time_tracking_t *tt, fr_time_t now)
Tracked entity entered a deeper time tracked code area.
static void fr_time_tracking_debug(fr_time_tracking_t *tt, FILE *fp)
Print debug information about the time tracking structure.
static fr_slen_t parent
Definition pair.h:851
int nonnull(2, 5))