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 | |
20 | static proftime_T prof_wait_time; |
21 | |
22 | /// Gets the current time. |
23 | /// |
24 | /// @return the current time |
25 | proftime_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. |
33 | proftime_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". |
44 | const 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. |
58 | proftime_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. |
73 | bool 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 |
86 | proftime_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 |
94 | proftime_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` |
106 | proftime_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` |
119 | proftime_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` |
128 | proftime_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 |
144 | proftime_T profile_get_wait(void) FUNC_ATTR_PURE |
145 | { |
146 | return prof_wait_time; |
147 | } |
148 | |
149 | /// Sets the current waittime. |
150 | void 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`) |
158 | proftime_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` |
167 | bool 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 |
175 | int64_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` |
191 | int 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_*). |
200 | static proftime_T g_start_time; |
201 | static 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 |
210 | void 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 |
227 | void 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". |
235 | static 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 |
247 | void 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 |
269 | void 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 | |