The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 2970c8df92ff346efcd2653682027e99efa750d3 $
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  */
27 RCSIDH(time_tracking_h, "$Id: 2970c8df92ff346efcd2653682027e99efa750d3 $")
28 
29 #ifdef __cplusplus
30 extern "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 
40 typedef 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) \
90 do { \
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) \
102 do { \
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) \
114 do { \
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) \
127 do { \
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  */
139 static 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  */
152 static inline CC_HINT(nonnull(2)) void fr_time_tracking_start(fr_time_tracking_t *parent,
153  fr_time_tracking_t *tt, fr_time_t now)
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 
167  UPDATE_PARENT_START_TIME(tt, now);
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  */
178  fr_time_tracking_t *tt, fr_time_t now)
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  */
202 static 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  */
224 static 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  */
245 static 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  */
270 static inline void fr_time_tracking_end(fr_time_delta_t *predicted,
271  fr_time_tracking_t *tt, fr_time_t now)
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  */
299 static 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 " = %"PRIu64"\n", fr_time_unwrap(tt->_x))
302 #define DPRINT(_x) fprintf(fp, "\t" #_x " = %"PRIu64"\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:482
#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
fr_assert(0)
#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
Definition: time_tracking.h:63
size_t fr_time_tracking_state_table_len
Definition: time_tracking.c:34
fr_time_tracking_state_t
Definition: time_tracking.h:40
@ FR_TIME_TRACKING_YIELDED
We're currently tracking time in the yielded state.
Definition: time_tracking.h:44
@ FR_TIME_TRACKING_STOPPED
Time tracking is not running.
Definition: time_tracking.h:41
@ FR_TIME_TRACKING_RUNNING
We're currently tracking time in the running state.
Definition: time_tracking.h:42
fr_time_tracking_state_t state
What state we're currently in.
Definition: time_tracking.h:61
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;.
Definition: time_tracking.h:72
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[]
Definition: time_tracking.c:29
#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.
Definition: time_tracking.h:65
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
Definition: time_tracking.h:80
fr_time_delta_t running_total
total time spent running
Definition: time_tracking.h:79
fr_time_t ended
Last time this tracked entity or a child left the running state, or popped a time tracked parent.
Definition: time_tracking.h:75
#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.
Definition: time_tracking.h:69
fr_time_tracking_t * parent
To update with our time tracking data when tracking is complete.
Definition: time_tracking.h:82
#define ASSERT_ON_TIME_TRAVEL(_tt, _now)
We use a monotonic time source.
Definition: time_tracking.h:89
#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))