1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#include <stdio.h>
5#include <math.h>
6#include <assert.h>
7
8#include "nvim/assert.h"
9#include "nvim/profile.h"
10#include "nvim/os/time.h"
11#include "nvim/func_attr.h"
12#include "nvim/os/os_defs.h"
13
14#include "nvim/globals.h" // for the global `time_fd` (startuptime)
15
16#ifdef INCLUDE_GENERATED_DECLARATIONS
17# include "profile.c.generated.h"
18#endif
19
20static proftime_T prof_wait_time;
21
22/// Gets the current time.
23///
24/// @return the current time
25proftime_T profile_start(void) FUNC_ATTR_WARN_UNUSED_RESULT
26{
27 return os_hrtime();
28}
29
30/// Computes the time elapsed.
31///
32/// @return Elapsed time from `tm` until now.
33proftime_T profile_end(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT
34{
35 return profile_sub(os_hrtime(), tm);
36}
37
38/// Gets a string representing time `tm`.
39///
40/// @warning Do not modify or free this string, not multithread-safe.
41///
42/// @param tm Time
43/// @return Static string representing `tm` in the form "seconds.microseconds".
44const char *profile_msg(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT
45{
46 static char buf[50];
47 snprintf(buf, sizeof(buf), "%10.6lf",
48 (double)profile_signed(tm) / 1000000000.0);
49 return buf;
50}
51
52/// Gets the time `msec` into the future.
53///
54/// @param msec milliseconds, the maximum number of milliseconds is
55/// (2^63 / 10^6) - 1 = 9.223372e+12.
56/// @return if msec > 0, returns the time msec past now. Otherwise returns
57/// the zero time.
58proftime_T profile_setlimit(int64_t msec) FUNC_ATTR_WARN_UNUSED_RESULT
59{
60 if (msec <= 0) {
61 // no limit
62 return profile_zero();
63 }
64 assert(msec <= (INT64_MAX / 1000000LL) - 1);
65 proftime_T nsec = (proftime_T)msec * 1000000ULL;
66 return os_hrtime() + nsec;
67}
68
69/// Checks if current time has passed `tm`.
70///
71/// @return true if the current time is past `tm`, false if not or if the
72/// timer was not set.
73bool profile_passed_limit(proftime_T tm) FUNC_ATTR_WARN_UNUSED_RESULT
74{
75 if (tm == 0) {
76 // timer was not set
77 return false;
78 }
79
80 return profile_cmp(os_hrtime(), tm) < 0;
81}
82
83/// Gets the zero time.
84///
85/// @return the zero time
86proftime_T profile_zero(void) FUNC_ATTR_CONST
87{
88 return 0;
89}
90
91/// Divides time `tm` by `count`.
92///
93/// @return 0 if count <= 0, otherwise tm / count
94proftime_T profile_divide(proftime_T tm, int count) FUNC_ATTR_CONST
95{
96 if (count <= 0) {
97 return profile_zero();
98 }
99
100 return (proftime_T) round((double) tm / (double) count);
101}
102
103/// Adds time `tm2` to `tm1`.
104///
105/// @return `tm1` + `tm2`
106proftime_T profile_add(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
107{
108 return tm1 + tm2;
109}
110
111/// Subtracts time `tm2` from `tm1`.
112///
113/// Unsigned overflow (wraparound) occurs if `tm2` is greater than `tm1`.
114/// Use `profile_signed()` to get the signed integer value.
115///
116/// @see profile_signed
117///
118/// @return `tm1` - `tm2`
119proftime_T profile_sub(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
120{
121 return tm1 - tm2;
122}
123
124/// Adds the `self` time from the total time and the `children` time.
125///
126/// @return if `total` <= `children`, then self, otherwise `self` + `total` -
127/// `children`
128proftime_T profile_self(proftime_T self, proftime_T total, proftime_T children)
129 FUNC_ATTR_CONST
130{
131 // check that the result won't be negative, which can happen with
132 // recursive calls.
133 if (total <= children) {
134 return self;
135 }
136
137 // add the total time to self and subtract the children's time from self
138 return profile_sub(profile_add(self, total), children);
139}
140
141/// Gets the current waittime.
142///
143/// @return the current waittime
144proftime_T profile_get_wait(void) FUNC_ATTR_PURE
145{
146 return prof_wait_time;
147}
148
149/// Sets the current waittime.
150void profile_set_wait(proftime_T wait)
151{
152 prof_wait_time = wait;
153}
154
155/// Subtracts the passed waittime since `tm`.
156///
157/// @return `tma` - (waittime - `tm`)
158proftime_T profile_sub_wait(proftime_T tm, proftime_T tma) FUNC_ATTR_PURE
159{
160 proftime_T tm3 = profile_sub(profile_get_wait(), tm);
161 return profile_sub(tma, tm3);
162}
163
164/// Checks if time `tm1` is equal to `tm2`.
165///
166/// @return true if `tm1` == `tm2`
167bool profile_equal(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
168{
169 return tm1 == tm2;
170}
171
172/// Converts time duration `tm` (`profile_sub` result) to a signed integer.
173///
174/// @return signed representation of the given time value
175int64_t profile_signed(proftime_T tm)
176 FUNC_ATTR_CONST
177{
178 // (tm > INT64_MAX) is >=150 years, so we can assume it was produced by
179 // arithmetic of two proftime_T values. For human-readable representation
180 // (and Vim-compat) we want the difference after unsigned wraparound. #10452
181 return (tm <= INT64_MAX) ? (int64_t)tm : -(int64_t)(UINT64_MAX - tm);
182}
183
184/// Compares profiling times.
185///
186/// Times `tm1` and `tm2` must be less than 150 years apart.
187///
188/// @return <0: `tm2` < `tm1`
189/// 0: `tm2` == `tm1`
190/// >0: `tm2` > `tm1`
191int profile_cmp(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
192{
193 if (tm1 == tm2) {
194 return 0;
195 }
196 return profile_signed(tm2 - tm1) < 0 ? -1 : 1;
197}
198
199/// globals for use in the startuptime related functionality (time_*).
200static proftime_T g_start_time;
201static proftime_T g_prev_time;
202
203/// Saves the previous time before doing something that could nest.
204///
205/// After calling this function, the static global `g_prev_time` will
206/// contain the current time.
207///
208/// @param[out] rel to the time elapsed so far
209/// @param[out] start the current time
210void time_push(proftime_T *rel, proftime_T *start)
211{
212 proftime_T now = profile_start();
213
214 // subtract the previous time from now, store it in `rel`
215 *rel = profile_sub(now, g_prev_time);
216 *start = now;
217
218 // reset global `g_prev_time` for the next call
219 g_prev_time = now;
220}
221
222/// Computes the prev time after doing something that could nest.
223///
224/// Subtracts `tp` from the static global `g_prev_time`.
225///
226/// @param tp the time to subtract
227void time_pop(proftime_T tp)
228{
229 g_prev_time -= tp;
230}
231
232/// Prints the difference between `then` and `now`.
233///
234/// the format is "msec.usec".
235static void time_diff(proftime_T then, proftime_T now)
236{
237 proftime_T diff = profile_sub(now, then);
238 fprintf(time_fd, "%07.3lf", (double)diff / 1.0E6);
239}
240
241/// Initializes the startuptime code.
242///
243/// Must be called once before calling other startuptime code (such as
244/// time_{push,pop,msg,...}).
245///
246/// @param message the message that will be displayed
247void time_start(const char *message)
248{
249 if (time_fd == NULL) {
250 return;
251 }
252
253 // intialize the global variables
254 g_prev_time = g_start_time = profile_start();
255
256 fprintf(time_fd, "\n\ntimes in msec\n");
257 fprintf(time_fd, " clock self+sourced self: sourced script\n");
258 fprintf(time_fd, " clock elapsed: other lines\n\n");
259
260 time_msg(message, NULL);
261}
262
263/// Prints out timing info.
264///
265/// @warning don't forget to call `time_start()` once before calling this.
266///
267/// @param mesg the message to display next to the timing information
268/// @param start only for do_source: start time
269void time_msg(const char *mesg, const proftime_T *start)
270{
271 if (time_fd == NULL) {
272 return;
273 }
274
275 // print out the difference between `start` (init earlier) and `now`
276 proftime_T now = profile_start();
277 time_diff(g_start_time, now);
278
279 // if `start` was supplied, print the diff between `start` and `now`
280 if (start != NULL) {
281 fprintf(time_fd, " ");
282 time_diff(*start, now);
283 }
284
285 // print the difference between the global `g_prev_time` and `now`
286 fprintf(time_fd, " ");
287 time_diff(g_prev_time, now);
288
289 // reset `g_prev_time` and print the message
290 g_prev_time = now;
291 fprintf(time_fd, ": %s\n", mesg);
292}
293