Line data Source code
1 : /* Copyright 2018 Wikimedia Foundation
2 : *
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 :
16 : #ifdef HAVE_CONFIG_H
17 : #include "config.h"
18 : #endif
19 :
20 : #include <time.h>
21 :
22 : #include "php.h"
23 : #include "zend_exceptions.h"
24 : #include "zend_interfaces.h"
25 : #include "ext/spl/spl_exceptions.h"
26 : #include "ext/standard/php_mt_rand.h"
27 : #include "ext/standard/info.h"
28 :
29 : #if PHP_VERSION_ID < 70200
30 : /* For spl_ce_Countable */
31 : #include "ext/spl/spl_iterators.h"
32 : #endif
33 :
34 : #include "php_excimer.h"
35 : #include "excimer_timer.h"
36 : #include "excimer_log.h"
37 :
38 : #define EXCIMER_OBJ(type, object) \
39 : ((type ## _obj*)excimer_check_object(object, XtOffsetOf(type ## _obj, std), &type ## _handlers))
40 :
41 : #define EXCIMER_OBJ_Z(type, zval) (Z_TYPE(zval) == IS_OBJECT ? EXCIMER_OBJ(type, Z_OBJ(zval)) : NULL)
42 :
43 : #define EXCIMER_OBJ_ZP(type, zval_ptr) EXCIMER_OBJ(type, Z_OBJ_P(zval_ptr))
44 :
45 : #define EXCIMER_NEW_OBJECT(type, ce) \
46 : excimer_object_alloc_init(sizeof(type ## _obj), &type ## _handlers, ce)
47 :
48 : #define EXCIMER_DEFAULT_PERIOD 0.1
49 : #define EXCIMER_BILLION 1000000000LL
50 : /* {{{ types */
51 :
52 : /**
53 : * ExcimerProfiler_obj: underlying storage for ExcimerProfiler
54 : */
55 : typedef struct {
56 : /** The period which will be used when the timer is next started */
57 : struct timespec period;
58 :
59 : /** The initial interval */
60 : struct timespec initial;
61 :
62 : /** The event type, either EXCIMER_CPU or EXCIMER_REAL */
63 : zend_long event_type;
64 :
65 : /** The currently-attached log */
66 : zval z_log;
67 :
68 : /** The flush callback. If this is set, max_samples will also be set. */
69 : zval z_callback;
70 :
71 : /** The maximum number of samples in z_log before z_callback is called. */
72 : zend_long max_samples;
73 :
74 : /** The timer backend object */
75 : excimer_timer timer;
76 : zend_object std;
77 : } ExcimerProfiler_obj;
78 :
79 : /**
80 : * ExcimerLog_iterator: Iterator object returned by get_iterator handler, used
81 : * by foreach.
82 : */
83 : typedef struct {
84 : /**
85 : * FIXME Iterators use PHP 5 style inheritance. This is actually a
86 : * zend_object header. Maybe it is harmless but it seems dodgy to me,
87 : * should be fixed upstream.
88 : */
89 : zend_user_iterator intern;
90 :
91 : /** Cached (lazy-initialised) value to use for current() */
92 : zval z_current;
93 :
94 : /** Current log index */
95 : zend_long index;
96 : } ExcimerLog_iterator;
97 :
98 : /**
99 : * ExcimerLog_obj: underlying storage for ExcimerLog
100 : */
101 : typedef struct {
102 : /** The log backend object */
103 : excimer_log log;
104 :
105 : /** The cached value to use for current() */
106 : zval z_current;
107 :
108 : /** The current index, for key() etc. */
109 : zend_long iter_entry_index;
110 : zend_object std;
111 : } ExcimerLog_obj;
112 :
113 : /**
114 : * ExcimerLogEntry_obj: underlying storage for ExcimerLogEntry
115 : */
116 : typedef struct {
117 : /**
118 : * The ExcimerLog. Note that this can be a circular reference if
119 : * ExcimerLog_obj.z_current points here.
120 : */
121 : zval z_log;
122 :
123 : /** The index of this entry in the ExcimerLog */
124 : zend_long index;
125 : zend_object std;
126 : } ExcimerLogEntry_obj;
127 :
128 : /**
129 : * ExcimerTimer_obj: underlying storage for ExcimerTimer
130 : */
131 : typedef struct {
132 : /** The timer backend object */
133 : excimer_timer timer;
134 :
135 : /** The timer period */
136 : struct timespec period;
137 :
138 : /** The initial expiry, or zero to use the period */
139 : struct timespec initial;
140 :
141 : /** The event type, EXCIMER_REAL or EXCIMER_CPU */
142 : zend_long event_type;
143 :
144 : /** The event function, or null for no callback */
145 : zval z_callback;
146 : zend_object std;
147 : } ExcimerTimer_obj;
148 : /* }}} */
149 :
150 : /* {{{ static function declarations */
151 : static void ExcimerProfiler_start(ExcimerProfiler_obj *profiler);
152 : static void ExcimerProfiler_stop(ExcimerProfiler_obj *profiler);
153 : static void ExcimerProfiler_event(zend_long event_count, void *user_data);
154 : static void ExcimerProfiler_flush(ExcimerProfiler_obj *profiler, zval *zp_old_log);
155 :
156 : static zend_object *ExcimerProfiler_new(zend_class_entry *ce);
157 : static void ExcimerProfiler_free_object(zend_object *object);
158 : static void ExcimerProfiler_dtor(zend_object *object);
159 : static PHP_METHOD(ExcimerProfiler, setPeriod);
160 : static PHP_METHOD(ExcimerProfiler, setEventType);
161 : static PHP_METHOD(ExcimerProfiler, setMaxDepth);
162 : static PHP_METHOD(ExcimerProfiler, setFlushCallback);
163 : static PHP_METHOD(ExcimerProfiler, clearFlushCallback);
164 : static PHP_METHOD(ExcimerProfiler, start);
165 : static PHP_METHOD(ExcimerProfiler, stop);
166 : static PHP_METHOD(ExcimerProfiler, getLog);
167 : static PHP_METHOD(ExcimerProfiler, flush);
168 :
169 : static zend_object *ExcimerLog_new(zend_class_entry *ce);
170 : static void ExcimerLog_free_object(zend_object *object);
171 : static zend_object_iterator *ExcimerLog_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
172 :
173 : #if PHP_VERSION_ID < 80000
174 : static int ExcimerLog_count_elements(zval *zp_log, zend_long *lp_count);
175 : #else
176 : static int ExcimerLog_count_elements(zend_object *object, zend_long *lp_count);
177 : #endif
178 :
179 : static void ExcimerLog_init_entry(zval *zp_dest, zval *zp_log, zend_long index);
180 :
181 : static void ExcimerLog_iterator_dtor(zend_object_iterator *iter);
182 : static int ExcimerLog_iterator_valid(zend_object_iterator *iter);
183 : static zval *ExcimerLog_iterator_get_current_data(zend_object_iterator *iter);
184 : static void ExcimerLog_iterator_get_current_key(zend_object_iterator *iter, zval *key);
185 : static void ExcimerLog_iterator_move_forward(zend_object_iterator *iter);
186 : static void ExcimerLog_iterator_rewind(zend_object_iterator *iter);
187 : static void ExcimerLog_iterator_invalidate_current(zend_object_iterator *iter);
188 :
189 : static PHP_METHOD(ExcimerLog, __construct);
190 : static PHP_METHOD(ExcimerLog, formatCollapsed);
191 : static PHP_METHOD(ExcimerLog, getSpeedscopeData);
192 : static PHP_METHOD(ExcimerLog, aggregateByFunction);
193 : static PHP_METHOD(ExcimerLog, getEventCount);
194 : static PHP_METHOD(ExcimerLog, current);
195 : static PHP_METHOD(ExcimerLog, key);
196 : static PHP_METHOD(ExcimerLog, next);
197 : static PHP_METHOD(ExcimerLog, rewind);
198 : static PHP_METHOD(ExcimerLog, valid);
199 : static PHP_METHOD(ExcimerLog, count);
200 : static PHP_METHOD(ExcimerLog, offsetExists);
201 : static PHP_METHOD(ExcimerLog, offsetGet);
202 : static PHP_METHOD(ExcimerLog, offsetSet);
203 : static PHP_METHOD(ExcimerLog, offsetUnset);
204 :
205 : static zend_object *ExcimerLogEntry_new(zend_class_entry *ce);
206 : static void ExcimerLogEntry_free_object(zend_object *object);
207 :
208 : static PHP_METHOD(ExcimerLogEntry, __construct);
209 : static PHP_METHOD(ExcimerLogEntry, getTimestamp);
210 : static PHP_METHOD(ExcimerLogEntry, getEventCount);
211 : static PHP_METHOD(ExcimerLogEntry, getTrace);
212 :
213 : static zend_object *ExcimerTimer_new(zend_class_entry *ce);
214 : static void ExcimerTimer_free_object(zend_object *object);
215 : static PHP_METHOD(ExcimerTimer, setEventType);
216 : static PHP_METHOD(ExcimerTimer, setInterval);
217 : static PHP_METHOD(ExcimerTimer, setPeriod);
218 : static PHP_METHOD(ExcimerTimer, setCallback);
219 : static PHP_METHOD(ExcimerTimer, start);
220 : static PHP_METHOD(ExcimerTimer, stop);
221 : static PHP_METHOD(ExcimerTimer, getTime);
222 :
223 : static void ExcimerTimer_start(ExcimerTimer_obj *timer_obj);
224 : static void ExcimerTimer_stop(ExcimerTimer_obj *timer_obj);
225 : static void ExcimerTimer_event(zend_long event_count, void *user_data);
226 : static int ExcimerTimer_set_callback(ExcimerTimer_obj *timer_obj, zval *zp_callback);
227 :
228 : static PHP_FUNCTION(excimer_set_timeout);
229 : /* }}} */
230 :
231 : static zend_class_entry *ExcimerProfiler_ce;
232 : static zend_class_entry *ExcimerLog_ce;
233 : static zend_class_entry *ExcimerLogEntry_ce;
234 : static zend_class_entry *ExcimerTimer_ce;
235 :
236 : static zend_object_handlers ExcimerProfiler_handlers;
237 : static zend_object_handlers ExcimerLog_handlers;
238 : static zend_object_handlers ExcimerLogEntry_handlers;
239 : static zend_object_handlers ExcimerTimer_handlers;
240 :
241 : /** {{{ arginfo */
242 : #ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX
243 : #define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
244 : ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args)
245 : #endif
246 :
247 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_setPeriod, 0)
248 : ZEND_ARG_INFO(0, period)
249 : ZEND_END_ARG_INFO()
250 :
251 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_setEventType, 0)
252 : ZEND_ARG_INFO(0, event_type)
253 : ZEND_END_ARG_INFO()
254 :
255 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_setMaxDepth, 0)
256 : ZEND_ARG_INFO(0, max_depth)
257 : ZEND_END_ARG_INFO()
258 :
259 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_setFlushCallback, 0)
260 : ZEND_ARG_INFO(0, callback)
261 : ZEND_ARG_INFO(0, max_samples)
262 : ZEND_END_ARG_INFO()
263 :
264 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_clearFlushCallback, 0)
265 : ZEND_END_ARG_INFO()
266 :
267 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_start, 0)
268 : ZEND_END_ARG_INFO()
269 :
270 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_stop, 0)
271 : ZEND_END_ARG_INFO()
272 :
273 : #if PHP_VERSION_ID >= 70200
274 : ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_ExcimerProfiler_getLog, 0, 0, ExcimerLog, 0)
275 : #else
276 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_getLog, 0)
277 : #endif
278 : ZEND_END_ARG_INFO()
279 :
280 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerProfiler_flush, 0)
281 : ZEND_END_ARG_INFO()
282 :
283 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLog___construct, 0)
284 : ZEND_END_ARG_INFO()
285 :
286 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLog_formatCollapsed, 0)
287 : ZEND_END_ARG_INFO()
288 :
289 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLog_getSpeedscopeData, 0)
290 : ZEND_END_ARG_INFO()
291 :
292 : #if PHP_VERSION_ID < 70200
293 : ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_ExcimerLog_aggregateByFunction, IS_ARRAY, NULL, 0)
294 : #else
295 : ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_ExcimerLog_aggregateByFunction, IS_ARRAY, 0)
296 : #endif
297 : ZEND_END_ARG_INFO()
298 :
299 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLog_getEventCount, 0)
300 : ZEND_END_ARG_INFO()
301 :
302 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_current, 0, 0, IS_MIXED, 0)
303 : ZEND_END_ARG_INFO()
304 :
305 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_key, 0, 0, IS_MIXED, 0)
306 : ZEND_END_ARG_INFO()
307 :
308 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_next, 0, 0, IS_VOID, 0)
309 : ZEND_END_ARG_INFO()
310 :
311 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_rewind, 0, 0, IS_VOID, 0)
312 : ZEND_END_ARG_INFO()
313 :
314 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_valid, 0, 0, _IS_BOOL, 0)
315 : ZEND_END_ARG_INFO()
316 :
317 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_count, 0, 0, IS_LONG, 0)
318 : ZEND_END_ARG_INFO()
319 :
320 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_offsetExists, 0, 1, _IS_BOOL, 0)
321 : ZEND_ARG_INFO(0, offset)
322 : ZEND_END_ARG_INFO()
323 :
324 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_offsetGet, 0, 1, IS_MIXED, 0)
325 : ZEND_ARG_INFO(0, offset)
326 : ZEND_END_ARG_INFO()
327 :
328 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_offsetSet, 0, 2, IS_VOID, 0)
329 : ZEND_ARG_INFO(0, offset)
330 : ZEND_ARG_INFO(0, value)
331 : ZEND_END_ARG_INFO()
332 :
333 : ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_ExcimerLog_offsetUnset, 0, 1, IS_VOID, 0)
334 : ZEND_ARG_INFO(0, offset)
335 : ZEND_END_ARG_INFO()
336 :
337 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLogEntry___construct, 0)
338 : ZEND_END_ARG_INFO()
339 :
340 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLogEntry_getTimestamp, 0)
341 : ZEND_END_ARG_INFO()
342 :
343 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLogEntry_getEventCount, 0)
344 : ZEND_END_ARG_INFO()
345 :
346 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerLogEntry_getTrace, 0)
347 : ZEND_END_ARG_INFO()
348 :
349 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_setEventType, 0)
350 : ZEND_ARG_INFO(0, event_type)
351 : ZEND_END_ARG_INFO()
352 :
353 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_setInterval, 0)
354 : ZEND_ARG_INFO(0, interval)
355 : ZEND_END_ARG_INFO()
356 :
357 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_setPeriod, 0)
358 : ZEND_ARG_INFO(0, period)
359 : ZEND_END_ARG_INFO()
360 :
361 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_setCallback, 0)
362 : ZEND_ARG_INFO(0, callback)
363 : ZEND_END_ARG_INFO()
364 :
365 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_start, 0)
366 : ZEND_END_ARG_INFO()
367 :
368 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_stop, 0)
369 : ZEND_END_ARG_INFO()
370 :
371 : ZEND_BEGIN_ARG_INFO(arginfo_ExcimerTimer_getTime, 0)
372 : ZEND_END_ARG_INFO()
373 :
374 : ZEND_BEGIN_ARG_INFO(arginfo_excimer_set_timeout, 0)
375 : ZEND_ARG_INFO(0, callback)
376 : ZEND_ARG_INFO(0, interval)
377 : ZEND_END_ARG_INFO()
378 :
379 : /* }}} */
380 :
381 : /** {{{ function entries */
382 : static const zend_function_entry ExcimerProfiler_methods[] = {
383 : PHP_ME(ExcimerProfiler, setPeriod, arginfo_ExcimerProfiler_setPeriod, 0)
384 : PHP_ME(ExcimerProfiler, setEventType, arginfo_ExcimerProfiler_setEventType, 0)
385 : PHP_ME(ExcimerProfiler, setMaxDepth, arginfo_ExcimerProfiler_setMaxDepth, 0)
386 : PHP_ME(ExcimerProfiler, setFlushCallback, arginfo_ExcimerProfiler_setFlushCallback, 0)
387 : PHP_ME(ExcimerProfiler, clearFlushCallback, arginfo_ExcimerProfiler_clearFlushCallback, 0)
388 : PHP_ME(ExcimerProfiler, start, arginfo_ExcimerProfiler_start, 0)
389 : PHP_ME(ExcimerProfiler, stop, arginfo_ExcimerProfiler_stop, 0)
390 : PHP_ME(ExcimerProfiler, getLog, arginfo_ExcimerProfiler_getLog, 0)
391 : PHP_ME(ExcimerProfiler, flush, arginfo_ExcimerProfiler_flush, 0)
392 : PHP_FE_END
393 : };
394 :
395 : static const zend_function_entry ExcimerLog_methods[] = {
396 : PHP_ME(ExcimerLog, __construct, arginfo_ExcimerLog___construct,
397 : ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
398 : PHP_ME(ExcimerLog, formatCollapsed, arginfo_ExcimerLog_formatCollapsed, 0)
399 : PHP_ME(ExcimerLog, getSpeedscopeData, arginfo_ExcimerLog_getSpeedscopeData, 0)
400 : PHP_ME(ExcimerLog, aggregateByFunction, arginfo_ExcimerLog_aggregateByFunction, 0)
401 : PHP_ME(ExcimerLog, getEventCount, arginfo_ExcimerLog_getEventCount, 0)
402 : PHP_ME(ExcimerLog, current, arginfo_ExcimerLog_current, 0)
403 : PHP_ME(ExcimerLog, key, arginfo_ExcimerLog_key, 0)
404 : PHP_ME(ExcimerLog, next, arginfo_ExcimerLog_next, 0)
405 : PHP_ME(ExcimerLog, rewind, arginfo_ExcimerLog_rewind, 0)
406 : PHP_ME(ExcimerLog, valid, arginfo_ExcimerLog_valid, 0)
407 : PHP_ME(ExcimerLog, count, arginfo_ExcimerLog_count, 0)
408 : PHP_ME(ExcimerLog, offsetExists, arginfo_ExcimerLog_offsetExists, 0)
409 : PHP_ME(ExcimerLog, offsetGet, arginfo_ExcimerLog_offsetGet, 0)
410 : PHP_ME(ExcimerLog, offsetSet, arginfo_ExcimerLog_offsetSet, 0)
411 : PHP_ME(ExcimerLog, offsetUnset, arginfo_ExcimerLog_offsetUnset, 0)
412 : PHP_FE_END
413 : };
414 :
415 : static zend_object_iterator_funcs ExcimerLog_iterator_funcs = {
416 : ExcimerLog_iterator_dtor,
417 : ExcimerLog_iterator_valid,
418 : ExcimerLog_iterator_get_current_data,
419 : ExcimerLog_iterator_get_current_key,
420 : ExcimerLog_iterator_move_forward,
421 : ExcimerLog_iterator_rewind,
422 : ExcimerLog_iterator_invalidate_current
423 : };
424 :
425 : static const zend_function_entry ExcimerLogEntry_methods[] = {
426 : PHP_ME(ExcimerLogEntry, __construct, arginfo_ExcimerLogEntry___construct,
427 : ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
428 : PHP_ME(ExcimerLogEntry, getTimestamp, arginfo_ExcimerLogEntry_getTimestamp, 0)
429 : PHP_ME(ExcimerLogEntry, getEventCount, arginfo_ExcimerLogEntry_getEventCount, 0)
430 : PHP_ME(ExcimerLogEntry, getTrace, arginfo_ExcimerLogEntry_getTrace, 0)
431 : PHP_FE_END
432 : };
433 :
434 : static const zend_function_entry ExcimerTimer_methods[] = {
435 : PHP_ME(ExcimerTimer, setEventType, arginfo_ExcimerTimer_setEventType, 0)
436 : PHP_ME(ExcimerTimer, setInterval, arginfo_ExcimerTimer_setInterval, 0)
437 : PHP_ME(ExcimerTimer, setPeriod, arginfo_ExcimerTimer_setPeriod, 0)
438 : PHP_ME(ExcimerTimer, setCallback, arginfo_ExcimerTimer_setCallback, 0)
439 : PHP_ME(ExcimerTimer, start, arginfo_ExcimerTimer_start, 0)
440 : PHP_ME(ExcimerTimer, stop, arginfo_ExcimerTimer_stop, 0)
441 : PHP_ME(ExcimerTimer, getTime, arginfo_ExcimerTimer_getTime, 0)
442 : PHP_FE_END
443 : };
444 :
445 : static const zend_function_entry excimer_functions[] = {
446 : PHP_FE(excimer_set_timeout, arginfo_excimer_set_timeout)
447 : PHP_FE_END
448 : };
449 : /* }}} */
450 :
451 : /* {{{ INI Settings */
452 : PHP_INI_BEGIN()
453 : PHP_INI_ENTRY("excimer.default_max_depth", "1000", PHP_INI_ALL, NULL)
454 : PHP_INI_END()
455 : /* }}} */
456 :
457 106 : static void *excimer_object_alloc_init(size_t object_size, zend_object_handlers *handlers, zend_class_entry *ce) /* {{{ */
458 : {
459 : #if PHP_VERSION_ID < 70300
460 : char *intern = ecalloc(1, object_size + zend_object_properties_size(ce));
461 : #else
462 106 : char *intern = zend_object_alloc(object_size, ce);
463 : #endif
464 106 : const size_t header_size = object_size - sizeof(zend_object);
465 106 : zend_object *object = (zend_object*)(intern + header_size);
466 106 : zend_object_std_init(object, ce);
467 106 : object_properties_init(object, ce);
468 106 : object->handlers = handlers;
469 106 : return intern;
470 : }
471 : /* }}} */
472 :
473 20 : static void excimer_set_timespec(struct timespec *dest, double source) /* {{{ */
474 : {
475 : double fractional, integral;
476 20 : if (source < 0) {
477 0 : dest->tv_sec = dest->tv_nsec = 0;
478 0 : return;
479 : }
480 :
481 20 : fractional = modf(source, &integral);
482 20 : dest->tv_sec = (time_t)integral;
483 20 : dest->tv_nsec = (long)(fractional * 1000000000.0);
484 20 : if (dest->tv_nsec >= EXCIMER_BILLION) {
485 0 : dest->tv_nsec -= EXCIMER_BILLION;
486 0 : dest->tv_sec ++;
487 : }
488 : }
489 : /* }}} */
490 :
491 67 : static inline uint64_t excimer_timespec_to_ns(struct timespec *ts)
492 : {
493 67 : return (uint64_t)ts->tv_nsec + (uint64_t)ts->tv_sec * EXCIMER_BILLION;
494 : }
495 :
496 3 : static inline double excimer_timespec_to_double(struct timespec *ts)
497 : {
498 3 : return excimer_timespec_to_ns(ts) * 1e-9;
499 : }
500 :
501 1339 : static inline void* excimer_check_object(zend_object *object, size_t offset, const zend_object_handlers *handlers)
502 : {
503 1339 : if (object->handlers != handlers) {
504 0 : return NULL;
505 : } else {
506 1339 : return (void*)((char*)object - offset);
507 : }
508 : }
509 :
510 : /* {{{ PHP_MINIT_FUNCTION
511 : */
512 22 : static PHP_MINIT_FUNCTION(excimer)
513 : {
514 : zend_class_entry ce;
515 :
516 22 : REGISTER_INI_ENTRIES();
517 :
518 22 : REGISTER_LONG_CONSTANT("EXCIMER_REAL", EXCIMER_REAL, CONST_CS | CONST_PERSISTENT);
519 :
520 : // Only define EXCIMER_CPU if the current platform supports POSIX timers,
521 : // which are necessary for CPU profiling.
522 : // This allows application code to detect and gracefully handle a lack of CPU profiling support.
523 : #ifdef HAVE_TIMER_CREATE
524 22 : REGISTER_LONG_CONSTANT("EXCIMER_CPU", EXCIMER_CPU, CONST_CS | CONST_PERSISTENT);
525 : #endif
526 :
527 : #define REGISTER_EXCIMER_CLASS(class_name) \
528 : INIT_CLASS_ENTRY(ce, #class_name, class_name ## _methods); \
529 : class_name ## _ce = zend_register_internal_class(&ce); \
530 : class_name ## _ce->create_object = class_name ## _new; \
531 : memcpy(&class_name ## _handlers, zend_get_std_object_handlers(), \
532 : sizeof(zend_object_handlers)); \
533 : class_name ## _handlers.offset = XtOffsetOf(class_name ## _obj, std); \
534 : class_name ## _handlers.free_obj = class_name ## _free_object;
535 :
536 22 : REGISTER_EXCIMER_CLASS(ExcimerProfiler);
537 22 : ExcimerProfiler_handlers.dtor_obj = ExcimerProfiler_dtor;
538 :
539 22 : REGISTER_EXCIMER_CLASS(ExcimerLog);
540 22 : ExcimerLog_ce->get_iterator = ExcimerLog_get_iterator;
541 22 : ExcimerLog_handlers.count_elements = ExcimerLog_count_elements;
542 :
543 22 : zend_class_implements(ExcimerLog_ce, 1, zend_ce_iterator);
544 : #if PHP_VERSION_ID >= 70200
545 22 : zend_class_implements(ExcimerLog_ce, 1, zend_ce_countable);
546 22 : zend_class_implements(ExcimerLog_ce, 1, zend_ce_arrayaccess);
547 : #elif defined(HAVE_SPL)
548 : zend_class_implements(ExcimerLog_ce, 1, spl_ce_Countable);
549 : zend_class_implements(ExcimerLog_ce, 1, spl_ce_ArrayAccess);
550 : #endif
551 :
552 22 : REGISTER_EXCIMER_CLASS(ExcimerLogEntry);
553 22 : REGISTER_EXCIMER_CLASS(ExcimerTimer);
554 :
555 : #undef REGISTER_EXCIMER_CLASS
556 :
557 22 : excimer_timer_module_init();
558 :
559 22 : return SUCCESS;
560 : }
561 : /* }}} */
562 :
563 : /* {{{ PHP_MSHUTDOWN_FUNCTION
564 : */
565 22 : static PHP_MSHUTDOWN_FUNCTION(excimer)
566 : {
567 22 : UNREGISTER_INI_ENTRIES();
568 22 : excimer_timer_module_shutdown();
569 22 : return SUCCESS;
570 : }
571 : /* }}} */
572 :
573 : /* {{{ PHP_RINIT_FUNCTION
574 : */
575 22 : static PHP_RINIT_FUNCTION(excimer)
576 : {
577 22 : excimer_timer_thread_init();
578 22 : return SUCCESS;
579 : }
580 : /* }}} */
581 :
582 : /* {{{ ZEND_MODULE_POST_ZEND_DEACTIVATE_D */
583 22 : static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(excimer)
584 : {
585 22 : excimer_timer_thread_shutdown();
586 22 : return SUCCESS;
587 : }
588 : /* }}} */
589 :
590 : /* {{{ PHP_MINFO_FUNCTION
591 : */
592 0 : static PHP_MINFO_FUNCTION(excimer)
593 : {
594 0 : php_info_print_table_start();
595 0 : php_info_print_table_header(2, "excimer support", "enabled");
596 0 : php_info_print_table_row(2, "excimer version", PHP_EXCIMER_VERSION);
597 0 : php_info_print_table_end();
598 0 : DISPLAY_INI_ENTRIES();
599 0 : }
600 : /* }}} */
601 :
602 3 : static zend_object *ExcimerProfiler_new(zend_class_entry *ce) /* {{{ */
603 : {
604 3 : ExcimerProfiler_obj *profiler = EXCIMER_NEW_OBJECT(ExcimerProfiler, ce);
605 : ExcimerLog_obj *log_obj;
606 : struct timespec now_ts;
607 : double initial;
608 :
609 3 : clock_gettime(CLOCK_MONOTONIC, &now_ts);
610 :
611 3 : object_init_ex(&profiler->z_log, ExcimerLog_ce);
612 3 : log_obj = EXCIMER_OBJ_Z(ExcimerLog, profiler->z_log);
613 3 : log_obj->log.max_depth = INI_INT("excimer.default_max_depth");
614 3 : log_obj->log.epoch = excimer_timespec_to_ns(&now_ts);
615 :
616 3 : ZVAL_NULL(&profiler->z_callback);
617 3 : profiler->event_type = EXCIMER_REAL;
618 :
619 : // Stagger start time
620 3 : initial = php_mt_rand() * EXCIMER_DEFAULT_PERIOD / UINT32_MAX;
621 3 : excimer_set_timespec(&profiler->initial, initial);
622 3 : excimer_set_timespec(&profiler->period, EXCIMER_DEFAULT_PERIOD);
623 3 : log_obj->log.period = EXCIMER_DEFAULT_PERIOD * EXCIMER_BILLION;
624 :
625 3 : return &profiler->std;
626 : }
627 : /* }}} */
628 :
629 3 : static void ExcimerProfiler_free_object(zend_object *object) /* {{{ */
630 : {
631 3 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ(ExcimerProfiler, object);
632 :
633 3 : if (profiler->timer.is_valid) {
634 0 : excimer_timer_destroy(&profiler->timer);
635 : }
636 3 : zval_ptr_dtor(&profiler->z_log);
637 3 : ZVAL_UNDEF(&profiler->z_log);
638 3 : zval_ptr_dtor(&profiler->z_callback);
639 3 : ZVAL_UNDEF(&profiler->z_callback);
640 3 : zend_object_std_dtor(object);
641 3 : }
642 : /* }}} */
643 :
644 3 : static void ExcimerProfiler_dtor(zend_object *object) /* {{{ */
645 : {
646 3 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ(ExcimerProfiler, object);
647 3 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_Z(ExcimerLog, profiler->z_log);
648 : zval z_old_log;
649 :
650 3 : if (log_obj->log.entries_size) {
651 0 : ExcimerProfiler_flush(profiler, &z_old_log);
652 0 : zval_ptr_dtor(&z_old_log);
653 : }
654 3 : }
655 : /* }}} */
656 :
657 : /* {{{ proto void ExcimerProfiler::setPeriod(float period)
658 : */
659 3 : static PHP_METHOD(ExcimerProfiler, setPeriod)
660 : {
661 : double period, initial;
662 3 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
663 :
664 3 : ZEND_PARSE_PARAMETERS_START(1, 1)
665 6 : Z_PARAM_DOUBLE(period)
666 3 : ZEND_PARSE_PARAMETERS_END();
667 :
668 : // Stagger start time
669 3 : initial = php_mt_rand() * period / UINT32_MAX;
670 :
671 3 : excimer_set_timespec(&profiler->period, period);
672 3 : excimer_set_timespec(&profiler->initial, initial);
673 :
674 3 : ExcimerLog_obj *log = EXCIMER_OBJ_ZP(ExcimerLog, &profiler->z_log);
675 3 : log->log.period = period * EXCIMER_BILLION;
676 : }
677 : /* }}} */
678 :
679 : /* {{{ proto void ExcimerProfiler::setEventType(int event_type)
680 : */
681 3 : static PHP_METHOD(ExcimerProfiler, setEventType)
682 : {
683 : zend_long event_type;
684 3 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
685 :
686 3 : ZEND_PARSE_PARAMETERS_START(1, 1)
687 6 : Z_PARAM_LONG(event_type)
688 3 : ZEND_PARSE_PARAMETERS_END();
689 :
690 3 : if (event_type != EXCIMER_CPU && event_type != EXCIMER_REAL) {
691 0 : php_error_docref(NULL, E_WARNING, "Invalid event type");
692 0 : return;
693 : }
694 :
695 3 : profiler->event_type = event_type;
696 : }
697 : /* }}} */
698 :
699 : /* {{{ proto void ExcimerProfiler::setMaxDepth(int max_depth)
700 : */
701 1 : static PHP_METHOD(ExcimerProfiler, setMaxDepth)
702 : {
703 : zend_long max_depth;
704 :
705 1 : ZEND_PARSE_PARAMETERS_START(1, 1)
706 2 : Z_PARAM_LONG(max_depth)
707 1 : ZEND_PARSE_PARAMETERS_END();
708 :
709 1 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
710 1 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &profiler->z_log);
711 1 : excimer_log_set_max_depth(&log_obj->log, max_depth);
712 : }
713 : /* }}} */
714 :
715 : /* {{{ proto void ExcimerProfiler::setFlushCallback(callable callback, mixed max_samples)
716 : */
717 0 : static PHP_METHOD(ExcimerProfiler, setFlushCallback)
718 : {
719 : zval *z_callback;
720 : zend_long max_samples;
721 0 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
722 : char *is_callable_error;
723 :
724 0 : ZEND_PARSE_PARAMETERS_START(2, 2)
725 0 : Z_PARAM_ZVAL(z_callback)
726 0 : Z_PARAM_LONG(max_samples)
727 0 : ZEND_PARSE_PARAMETERS_END();
728 :
729 0 : if (!zend_is_callable_ex(z_callback, NULL, 0, NULL, NULL, &is_callable_error)) {
730 0 : php_error_docref(NULL, E_WARNING, "flush callback is not callable: %s",
731 : is_callable_error);
732 0 : return;
733 : }
734 :
735 0 : ZVAL_COPY(&profiler->z_callback, z_callback);
736 0 : profiler->max_samples = max_samples;
737 : }
738 : /* }}} */
739 :
740 : /* {{{ proto void ExcimerProfiler::clearFlushCallback()
741 : */
742 0 : static PHP_METHOD(ExcimerProfiler, clearFlushCallback)
743 : {
744 0 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
745 0 : zval_ptr_dtor(&profiler->z_callback);
746 0 : ZVAL_NULL(&profiler->z_callback);
747 0 : profiler->max_samples = 0;
748 0 : }
749 : /* }}} */
750 :
751 : /* {{{ proto void ExcimerProfiler::start()
752 : */
753 5 : static PHP_METHOD(ExcimerProfiler, start)
754 : {
755 5 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
756 :
757 5 : ZEND_PARSE_PARAMETERS_START(0, 0);
758 5 : ZEND_PARSE_PARAMETERS_END();
759 :
760 5 : if (profiler->timer.is_running) {
761 0 : ExcimerProfiler_stop(profiler);
762 : }
763 5 : ExcimerProfiler_start(profiler);
764 : }
765 : /* }}} */
766 :
767 : /* {{{ proto void ExcimerProfiler::stop()
768 : */
769 5 : static PHP_METHOD(ExcimerProfiler, stop)
770 : {
771 5 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
772 :
773 5 : ZEND_PARSE_PARAMETERS_START(0, 0);
774 5 : ZEND_PARSE_PARAMETERS_END();
775 :
776 5 : ExcimerProfiler_stop(profiler);
777 : }
778 : /* }}} */
779 :
780 : /* {{{ proto ExcimerLog ExcimerProfiler::getLog()
781 : */
782 55 : static PHP_METHOD(ExcimerProfiler, getLog)
783 : {
784 55 : ExcimerProfiler_obj * profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
785 :
786 55 : ZEND_PARSE_PARAMETERS_START(0, 0);
787 55 : ZEND_PARSE_PARAMETERS_END();
788 :
789 110 : RETURN_ZVAL(&profiler->z_log, 1, 0);
790 : }
791 : /* }}} */
792 :
793 : /* {{{ proto ExcimerLog ExcimerProfiler::flush() */
794 3 : static PHP_METHOD(ExcimerProfiler, flush)
795 : {
796 3 : ExcimerProfiler_obj *profiler = EXCIMER_OBJ_ZP(ExcimerProfiler, getThis());
797 :
798 3 : ZEND_PARSE_PARAMETERS_START(0, 0);
799 3 : ZEND_PARSE_PARAMETERS_END();
800 :
801 3 : ExcimerProfiler_flush(profiler, return_value);
802 : }
803 : /* }}} */
804 :
805 5 : static void ExcimerProfiler_start(ExcimerProfiler_obj *profiler) /* {{{ */
806 : {
807 5 : if (profiler->timer.is_valid) {
808 0 : excimer_timer_destroy(&profiler->timer);
809 : }
810 5 : if (excimer_timer_init(&profiler->timer,
811 5 : profiler->event_type,
812 : ExcimerProfiler_event,
813 : (void*)profiler) == FAILURE)
814 : {
815 : /* Error message already sent */
816 0 : return;
817 : }
818 5 : excimer_timer_start(&profiler->timer,
819 : &profiler->period,
820 : &profiler->initial);
821 : }
822 : /* }}} */
823 :
824 5 : static void ExcimerProfiler_stop(ExcimerProfiler_obj *profiler) /* {{{ */
825 : {
826 5 : if (profiler->timer.is_valid) {
827 5 : excimer_timer_destroy(&profiler->timer);
828 : }
829 5 : }
830 : /* }}} */
831 :
832 61 : static void ExcimerProfiler_event(zend_long event_count, void *user_data) /* {{{ */
833 : {
834 : uint64_t now_ns;
835 : struct timespec now_ts;
836 61 : ExcimerProfiler_obj *profiler = (ExcimerProfiler_obj*)user_data;
837 61 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &profiler->z_log);
838 : excimer_log *log;
839 :
840 61 : log = &log_obj->log;
841 :
842 61 : clock_gettime(CLOCK_MONOTONIC, &now_ts);
843 61 : now_ns = excimer_timespec_to_ns(&now_ts);
844 :
845 61 : excimer_log_add(log, EG(current_execute_data), event_count, now_ns);
846 :
847 61 : if (profiler->max_samples && log->entries_size >= profiler->max_samples) {
848 : zval z_old_log;
849 0 : ExcimerProfiler_flush(profiler, &z_old_log);
850 0 : zval_ptr_dtor(&z_old_log);
851 : }
852 61 : }
853 : /* }}} */
854 :
855 3 : static void ExcimerProfiler_flush(ExcimerProfiler_obj *profiler, zval *zp_old_log) /* {{{ */
856 : {
857 3 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &profiler->z_log);
858 3 : excimer_log *log = &log_obj->log;
859 :
860 : zend_fcall_info fci;
861 : zend_fcall_info_cache fcc;
862 3 : char *is_callable_error = NULL;
863 : zval retval;
864 : int status;
865 :
866 : /* Rotate the log */
867 3 : ZVAL_COPY(zp_old_log, &profiler->z_log);
868 3 : Z_DELREF(profiler->z_log);
869 3 : object_init_ex(&profiler->z_log, ExcimerLog_ce);
870 3 : excimer_log_copy_options(&EXCIMER_OBJ_ZP(ExcimerLog, &profiler->z_log)->log, log);
871 :
872 3 : if (Z_ISNULL(profiler->z_callback)) {
873 3 : return;
874 : }
875 :
876 : /* Prepare to call the flush callback */
877 0 : if (zend_fcall_info_init(&profiler->z_callback, 0, &fci, &fcc, NULL,
878 : &is_callable_error) != SUCCESS)
879 : {
880 0 : php_error(E_WARNING, "ExcimerProfiler callback is not callable (during event): %s",
881 : is_callable_error);
882 0 : ExcimerProfiler_stop(profiler);
883 0 : return;
884 : }
885 :
886 0 : fci.retval = &retval;
887 :
888 : /* Call it */
889 0 : zend_fcall_info_argn(&fci, 1, zp_old_log);
890 0 : status = zend_call_function(&fci, &fcc);
891 0 : if (status == SUCCESS) {
892 0 : zval_ptr_dtor(&retval);
893 : }
894 0 : zend_fcall_info_args_clear(&fci, 1);
895 : }
896 : /* }}} */
897 :
898 6 : static zend_object *ExcimerLog_new(zend_class_entry *ce) /* {{{ */
899 : {
900 6 : ExcimerLog_obj *log_obj = EXCIMER_NEW_OBJECT(ExcimerLog, ce);
901 6 : excimer_log_init(&log_obj->log);
902 : /* Lazy-initialise z_current to minimise circular references */
903 6 : ZVAL_NULL(&log_obj->z_current);
904 6 : log_obj->iter_entry_index = 0;
905 6 : return &log_obj->std;
906 : }
907 : /* }}} */
908 :
909 6 : static void ExcimerLog_free_object(zend_object *object) /* {{{ */
910 : {
911 6 : ExcimerLog_obj *log_obj = EXCIMER_OBJ(ExcimerLog, object);
912 6 : excimer_log_destroy(&log_obj->log);
913 6 : zval_ptr_dtor(&log_obj->z_current);
914 6 : zend_object_std_dtor(object);
915 6 : }
916 : /* }}} */
917 :
918 : /* {{{ ExcimerLog_get_iterator */
919 2 : static zend_object_iterator *ExcimerLog_get_iterator(
920 : zend_class_entry *ce, zval *zp_log, int by_ref)
921 : {
922 : ExcimerLog_iterator *iterator;
923 :
924 2 : if (by_ref) {
925 0 : zend_throw_exception(spl_ce_RuntimeException, "An iterator cannot be used with foreach by reference", 0);
926 0 : return NULL;
927 : }
928 :
929 2 : iterator = emalloc(sizeof(ExcimerLog_iterator));
930 2 : zend_iterator_init((zend_object_iterator*)iterator);
931 :
932 2 : ZVAL_COPY(&iterator->intern.it.data, zp_log);
933 :
934 2 : iterator->intern.it.funcs = &ExcimerLog_iterator_funcs;
935 2 : iterator->intern.ce = ce;
936 2 : iterator->index = 0;
937 2 : ZVAL_NULL(&iterator->z_current);
938 :
939 2 : return &iterator->intern.it;
940 : }
941 : /* }}} */
942 :
943 2 : static void ExcimerLog_iterator_dtor(zend_object_iterator *iter) /* {{{ */
944 : {
945 2 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
946 :
947 2 : zval_ptr_dtor(&iterator->z_current);
948 2 : ZVAL_UNDEF(&iterator->z_current);
949 2 : zval_ptr_dtor(&iterator->intern.it.data);
950 2 : ZVAL_UNDEF(&iterator->intern.it.data);
951 2 : }
952 : /* }}} */
953 :
954 62 : static int ExcimerLog_iterator_valid(zend_object_iterator *iter) /* {{{ */
955 : {
956 62 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
957 62 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_Z(ExcimerLog, iterator->intern.it.data);
958 :
959 62 : if (iterator->index < log_obj->log.entries_size) {
960 60 : return SUCCESS;
961 : } else {
962 2 : return FAILURE;
963 : }
964 : }
965 : /* }}} */
966 :
967 60 : static zval *ExcimerLog_iterator_get_current_data(zend_object_iterator *iter) /* {{{ */
968 : {
969 60 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
970 60 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_Z(ExcimerLog, iterator->intern.it.data);
971 :
972 60 : if (Z_ISNULL(iterator->z_current)) {
973 60 : if (iterator->index < log_obj->log.entries_size) {
974 60 : ExcimerLog_init_entry(&iterator->z_current, &iterator->intern.it.data, iterator->index);
975 : } else {
976 0 : return NULL;
977 : }
978 : }
979 60 : return &iterator->z_current;
980 : }
981 : /* }}} */
982 :
983 0 : static void ExcimerLog_iterator_get_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
984 : {
985 0 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
986 0 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_Z(ExcimerLog, iterator->intern.it.data);
987 :
988 0 : if (iterator->index < log_obj->log.entries_size) {
989 0 : ZVAL_LONG(key, iterator->index);
990 : } else {
991 0 : ZVAL_NULL(key);
992 : }
993 0 : }
994 : /* }}} */
995 :
996 60 : static void ExcimerLog_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
997 : {
998 60 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
999 60 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_Z(ExcimerLog, iterator->intern.it.data);
1000 :
1001 60 : zval_ptr_dtor(&iterator->z_current);
1002 60 : ZVAL_NULL(&iterator->z_current);
1003 :
1004 60 : if (iterator->index < log_obj->log.entries_size) {
1005 60 : iterator->index++;
1006 : }
1007 60 : }
1008 : /* }}} */
1009 :
1010 2 : static void ExcimerLog_iterator_rewind(zend_object_iterator *iter) /* {{{ */
1011 : {
1012 2 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
1013 :
1014 2 : zval_ptr_dtor(&iterator->z_current);
1015 2 : ZVAL_NULL(&iterator->z_current);
1016 2 : iterator->index = 0;
1017 2 : }
1018 : /* }}} */
1019 :
1020 0 : static void ExcimerLog_iterator_invalidate_current(zend_object_iterator *iter) /* {{{ */
1021 : {
1022 0 : ExcimerLog_iterator *iterator = (ExcimerLog_iterator*)iter;
1023 :
1024 0 : zval_ptr_dtor(&iterator->z_current);
1025 0 : ZVAL_NULL(&iterator->z_current);
1026 0 : }
1027 : /* }}} */
1028 :
1029 90 : static void ExcimerLog_init_entry(zval *zp_dest, zval *zp_log, zend_long index) /* {{{ */
1030 : {
1031 90 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, zp_log);
1032 90 : excimer_log_entry *entry = excimer_log_get_entry(&log_obj->log, index);
1033 : ExcimerLogEntry_obj *entry_obj;
1034 :
1035 90 : if (entry) {
1036 90 : object_init_ex(zp_dest, ExcimerLogEntry_ce);
1037 90 : entry_obj = EXCIMER_OBJ_ZP(ExcimerLogEntry, zp_dest);
1038 90 : ZVAL_COPY(&entry_obj->z_log, zp_log);
1039 90 : entry_obj->index = index;
1040 : } else {
1041 0 : ZVAL_NULL(zp_dest);
1042 : }
1043 90 : }
1044 : /* }}} */
1045 :
1046 : #if PHP_VERSION_ID < 80000
1047 56 : static int ExcimerLog_count_elements(zval *zp_log, zend_long *lp_count) /* {{{ */
1048 : {
1049 56 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, zp_log);
1050 56 : *lp_count = log_obj->log.entries_size;
1051 56 : return SUCCESS;
1052 : }
1053 : /* }}} */
1054 : #else
1055 : static int ExcimerLog_count_elements(zend_object *object, zend_long *lp_count) /* {{{ */
1056 : {
1057 : ExcimerLog_obj *log_obj = EXCIMER_OBJ(ExcimerLog, object);
1058 : *lp_count = log_obj->log.entries_size;
1059 : return SUCCESS;
1060 : }
1061 : /* }}} */
1062 : #endif
1063 :
1064 : /* {{{ proto void ExcimerLog::__construct()
1065 : */
1066 0 : static PHP_METHOD(ExcimerLog, __construct)
1067 : {
1068 0 : php_error_docref(NULL, E_ERROR, "ExcimerLog cannot be constructed directly");
1069 0 : }
1070 : /* }}} */
1071 :
1072 : /* {{{ proto string ExcimerLog::formatCollapsed()
1073 : */
1074 2 : static PHP_METHOD(ExcimerLog, formatCollapsed)
1075 : {
1076 2 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1077 2 : RETURN_STR(excimer_log_format_collapsed(&log_obj->log));
1078 : }
1079 : /* }}} */
1080 :
1081 : /* {{{ proto string ExcimerLog::getSpeedscopeData()
1082 : */
1083 1 : static PHP_METHOD(ExcimerLog, getSpeedscopeData)
1084 : {
1085 1 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1086 1 : excimer_log_get_speedscope_data(&log_obj->log, return_value);
1087 1 : }
1088 : /* }}} */
1089 :
1090 : /* {{{ proto string ExcimerLog::aggregateByFunction()
1091 : */
1092 1 : static PHP_METHOD(ExcimerLog, aggregateByFunction)
1093 : {
1094 1 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1095 1 : RETURN_ARR(excimer_log_aggr_by_func(&log_obj->log));
1096 : }
1097 : /* }}} */
1098 :
1099 : /* {{{ proto string ExcimerLog::getEventCount()
1100 : */
1101 1 : static PHP_METHOD(ExcimerLog, getEventCount)
1102 : {
1103 1 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1104 1 : RETURN_LONG(log_obj->log.event_count);
1105 : }
1106 : /* }}} */
1107 :
1108 : /* {{{ proto array ExcimerLog::current()
1109 : */
1110 30 : static PHP_METHOD(ExcimerLog, current)
1111 : {
1112 30 : ExcimerLog_obj * log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1113 :
1114 30 : ZEND_PARSE_PARAMETERS_START(0, 0);
1115 30 : ZEND_PARSE_PARAMETERS_END();
1116 :
1117 30 : if (Z_ISNULL(log_obj->z_current) && log_obj->iter_entry_index < log_obj->log.entries_size) {
1118 30 : ExcimerLog_init_entry(&log_obj->z_current, getThis(), log_obj->iter_entry_index);
1119 : }
1120 :
1121 60 : RETURN_ZVAL(&log_obj->z_current, 1, 0);
1122 : }
1123 : /* }}} */
1124 :
1125 : /* {{{ proto int ExcimerLog::key()
1126 : */
1127 0 : static PHP_METHOD(ExcimerLog, key)
1128 : {
1129 0 : ExcimerLog_obj * log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1130 :
1131 0 : ZEND_PARSE_PARAMETERS_START(0, 0);
1132 0 : ZEND_PARSE_PARAMETERS_END();
1133 :
1134 0 : if (log_obj->iter_entry_index < log_obj->log.entries_size) {
1135 0 : RETURN_LONG(log_obj->iter_entry_index);
1136 : } else {
1137 0 : RETURN_NULL();
1138 : }
1139 : }
1140 : /* }}} */
1141 :
1142 : /* {{{ proto void ExcimerLog::next()
1143 : */
1144 30 : static PHP_METHOD(ExcimerLog, next)
1145 : {
1146 30 : ExcimerLog_obj * log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1147 :
1148 30 : ZEND_PARSE_PARAMETERS_START(0, 0);
1149 30 : ZEND_PARSE_PARAMETERS_END();
1150 :
1151 30 : zval_ptr_dtor(&log_obj->z_current);
1152 30 : ZVAL_NULL(&log_obj->z_current);
1153 30 : if (log_obj->iter_entry_index < log_obj->log.entries_size) {
1154 30 : log_obj->iter_entry_index++;
1155 : }
1156 : }
1157 : /* }}} */
1158 :
1159 : /* {{{ proto void ExcimerLog::rewind()
1160 : */
1161 1 : static PHP_METHOD(ExcimerLog, rewind)
1162 : {
1163 1 : ExcimerLog_obj * log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1164 :
1165 1 : ZEND_PARSE_PARAMETERS_START(0, 0);
1166 1 : ZEND_PARSE_PARAMETERS_END();
1167 :
1168 1 : log_obj->iter_entry_index = 0;
1169 1 : zval_ptr_dtor(&log_obj->z_current);
1170 1 : ZVAL_NULL(&log_obj->z_current);
1171 : }
1172 : /* }}} */
1173 :
1174 : /* {{{ proto bool ExcimerLog::valid()
1175 : */
1176 31 : static PHP_METHOD(ExcimerLog, valid)
1177 : {
1178 31 : ExcimerLog_obj * log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1179 :
1180 31 : ZEND_PARSE_PARAMETERS_START(0, 0);
1181 31 : ZEND_PARSE_PARAMETERS_END();
1182 :
1183 31 : if (log_obj->iter_entry_index < log_obj->log.entries_size) {
1184 30 : RETURN_TRUE;
1185 : } else {
1186 1 : RETURN_FALSE;
1187 : }
1188 : }
1189 : /* }}} */
1190 :
1191 : /* {{{ proto int ExcimerLog::count()
1192 : */
1193 2 : static PHP_METHOD(ExcimerLog, count)
1194 : {
1195 2 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1196 :
1197 2 : ZEND_PARSE_PARAMETERS_START(0, 0);
1198 2 : ZEND_PARSE_PARAMETERS_END();
1199 :
1200 2 : RETURN_LONG(log_obj->log.entries_size);
1201 : }
1202 : /* }}} */
1203 :
1204 : /* {{{ proto bool ExcimerLog::offsetExists(mixed offset) */
1205 0 : static PHP_METHOD(ExcimerLog, offsetExists)
1206 : {
1207 0 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1208 : zend_long offset;
1209 :
1210 0 : ZEND_PARSE_PARAMETERS_START(1, 1);
1211 0 : Z_PARAM_LONG(offset)
1212 0 : ZEND_PARSE_PARAMETERS_END();
1213 :
1214 0 : if (offset >= 0 && offset < log_obj->log.entries_size) {
1215 0 : RETURN_TRUE;
1216 : } else {
1217 0 : RETURN_FALSE;
1218 : }
1219 : }
1220 : /* }}} */
1221 :
1222 : /* {{{ proto mixed ExcimerLog::offsetGet(mixed offset) */
1223 0 : static PHP_METHOD(ExcimerLog, offsetGet)
1224 : {
1225 0 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, getThis());
1226 : zend_long offset;
1227 :
1228 0 : ZEND_PARSE_PARAMETERS_START(1, 1);
1229 0 : Z_PARAM_LONG(offset)
1230 0 : ZEND_PARSE_PARAMETERS_END();
1231 :
1232 0 : if (offset < 0 || offset >= log_obj->log.entries_size) {
1233 0 : RETURN_NULL();
1234 : }
1235 :
1236 0 : ExcimerLog_init_entry(return_value, getThis(), offset);
1237 : }
1238 : /* }}} */
1239 :
1240 : /* {{{ proto void ExcimerLog::offsetSet(mixed offset, mixed value) */
1241 0 : static PHP_METHOD(ExcimerLog, offsetSet)
1242 : {
1243 0 : php_error_docref(NULL, E_WARNING, "ExcimerLog cannot be modified");
1244 0 : }
1245 : /* }}} */
1246 :
1247 : /* {{{ proto void ExcimerLog::offsetUnset(mixed offset) */
1248 0 : static PHP_METHOD(ExcimerLog, offsetUnset)
1249 : {
1250 0 : php_error_docref(NULL, E_WARNING, "ExcimerLog cannot be modified");
1251 0 : }
1252 : /* }}} */
1253 :
1254 90 : static zend_object *ExcimerLogEntry_new(zend_class_entry *ce) /* {{{ */
1255 : {
1256 90 : ExcimerLogEntry_obj *entry_obj = EXCIMER_NEW_OBJECT(ExcimerLogEntry, ce);
1257 90 : ZVAL_NULL(&entry_obj->z_log);
1258 90 : entry_obj->index = 0;
1259 90 : return &entry_obj->std;
1260 : }
1261 : /* }}} */
1262 :
1263 90 : static void ExcimerLogEntry_free_object(zend_object *object) /* {{{ */
1264 : {
1265 90 : ExcimerLogEntry_obj *entry_obj = EXCIMER_OBJ(ExcimerLogEntry, object);
1266 90 : zval_ptr_dtor(&entry_obj->z_log);
1267 90 : ZVAL_UNDEF(&entry_obj->z_log);
1268 90 : zend_object_std_dtor(object);
1269 90 : }
1270 : /* }}} */
1271 :
1272 : /* {{{ proto void ExcimerLogEntry::__construct()
1273 : */
1274 0 : static PHP_METHOD(ExcimerLogEntry, __construct)
1275 : {
1276 0 : php_error_docref(NULL, E_ERROR, "ExcimerLogEntry cannot be constructed directly");
1277 0 : }
1278 : /* }}} */
1279 :
1280 : /* {{{ proto float ExcimerLogEntry::getTimestamp()
1281 : */
1282 118 : static PHP_METHOD(ExcimerLogEntry, getTimestamp)
1283 : {
1284 118 : ExcimerLogEntry_obj *entry_obj = EXCIMER_OBJ_ZP(ExcimerLogEntry, getThis());
1285 118 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &entry_obj->z_log);
1286 118 : excimer_log_entry *entry = excimer_log_get_entry(&log_obj->log, entry_obj->index);
1287 :
1288 118 : ZEND_PARSE_PARAMETERS_START(0, 0);
1289 118 : ZEND_PARSE_PARAMETERS_END();
1290 :
1291 118 : RETURN_DOUBLE((entry->timestamp - log_obj->log.epoch) / 1e9);
1292 : }
1293 : /* }}} */
1294 :
1295 : /* {{{ proto float ExcimerLogEntry::getEventCount()
1296 : */
1297 60 : static PHP_METHOD(ExcimerLogEntry, getEventCount)
1298 : {
1299 60 : ExcimerLogEntry_obj *entry_obj = EXCIMER_OBJ_ZP(ExcimerLogEntry, getThis());
1300 60 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &entry_obj->z_log);
1301 60 : excimer_log_entry *entry = excimer_log_get_entry(&log_obj->log, entry_obj->index);
1302 :
1303 60 : ZEND_PARSE_PARAMETERS_START(0, 0);
1304 60 : ZEND_PARSE_PARAMETERS_END();
1305 :
1306 60 : RETURN_LONG(entry->event_count);
1307 : }
1308 : /* }}} */
1309 :
1310 : /* {{{ proto array ExcimerLogEntry::getTrace()
1311 : */
1312 90 : static PHP_METHOD(ExcimerLogEntry, getTrace)
1313 : {
1314 90 : ExcimerLogEntry_obj *entry_obj = EXCIMER_OBJ_ZP(ExcimerLogEntry, getThis());
1315 90 : ExcimerLog_obj *log_obj = EXCIMER_OBJ_ZP(ExcimerLog, &entry_obj->z_log);
1316 90 : excimer_log_entry *entry = excimer_log_get_entry(&log_obj->log, entry_obj->index);
1317 :
1318 90 : ZEND_PARSE_PARAMETERS_START(0, 0);
1319 90 : ZEND_PARSE_PARAMETERS_END();
1320 :
1321 90 : RETURN_ARR(excimer_log_trace_to_array(&log_obj->log, entry->frame_index));
1322 : }
1323 : /* }}} */
1324 :
1325 7 : static zend_object *ExcimerTimer_new(zend_class_entry *ce) /* {{{ */
1326 : {
1327 7 : ExcimerTimer_obj *timer_obj = EXCIMER_NEW_OBJECT(ExcimerTimer, ce);
1328 7 : ZVAL_UNDEF(&timer_obj->z_callback);
1329 7 : timer_obj->event_type = EXCIMER_REAL;
1330 7 : return &timer_obj->std;
1331 : }
1332 : /* }}} */
1333 :
1334 7 : static void ExcimerTimer_free_object(zend_object *object) /* {{{ */
1335 : {
1336 7 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ(ExcimerTimer, object);
1337 7 : if (timer_obj->timer.is_valid) {
1338 5 : excimer_timer_destroy(&timer_obj->timer);
1339 : }
1340 7 : zval_ptr_dtor(&timer_obj->z_callback);
1341 7 : ZVAL_UNDEF(&timer_obj->z_callback);
1342 7 : }
1343 : /* }}} */
1344 :
1345 : /* {{{ proto void ExcimerTimer::setEventType(int event_type)
1346 : */
1347 2 : static PHP_METHOD(ExcimerTimer, setEventType)
1348 : {
1349 2 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1350 : zend_long event_type;
1351 :
1352 2 : ZEND_PARSE_PARAMETERS_START(1, 1)
1353 4 : Z_PARAM_LONG(event_type)
1354 2 : ZEND_PARSE_PARAMETERS_END();
1355 :
1356 2 : if (event_type != EXCIMER_CPU && event_type != EXCIMER_REAL) {
1357 0 : php_error_docref(NULL, E_WARNING, "Invalid event type");
1358 0 : return;
1359 : }
1360 :
1361 2 : timer_obj->event_type = event_type;
1362 : }
1363 : /* }}} */
1364 :
1365 : /* {{{ proto void ExcimerTimer::setInterval(float interval)
1366 : */
1367 4 : static PHP_METHOD(ExcimerTimer, setInterval)
1368 : {
1369 4 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1370 : double initial;
1371 :
1372 4 : ZEND_PARSE_PARAMETERS_START(1, 1)
1373 8 : Z_PARAM_DOUBLE(initial)
1374 4 : ZEND_PARSE_PARAMETERS_END();
1375 :
1376 4 : excimer_set_timespec(&timer_obj->initial, initial);
1377 : }
1378 : /* }}} */
1379 :
1380 : /* {{{ proto void ExcimerTimer::setPeriod(float period)
1381 : */
1382 3 : static PHP_METHOD(ExcimerTimer, setPeriod)
1383 : {
1384 3 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1385 : double period;
1386 :
1387 3 : ZEND_PARSE_PARAMETERS_START(1, 1)
1388 6 : Z_PARAM_DOUBLE(period)
1389 3 : ZEND_PARSE_PARAMETERS_END();
1390 :
1391 3 : excimer_set_timespec(&timer_obj->period, period);
1392 : }
1393 : /* }}} */
1394 :
1395 : /* {{{ proto void ExcimerTimer::setCallback(callback callback)
1396 : */
1397 4 : static PHP_METHOD(ExcimerTimer, setCallback)
1398 : {
1399 4 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1400 : zval *zp_callback;
1401 :
1402 4 : ZEND_PARSE_PARAMETERS_START(1, 1)
1403 4 : Z_PARAM_ZVAL(zp_callback)
1404 4 : ZEND_PARSE_PARAMETERS_END();
1405 :
1406 4 : if (Z_TYPE_P(zp_callback) == IS_NULL) {
1407 0 : zval_ptr_dtor(&timer_obj->z_callback);
1408 0 : ZVAL_NULL(&timer_obj->z_callback);
1409 : } else {
1410 4 : ExcimerTimer_set_callback(timer_obj, zp_callback);
1411 : }
1412 : }
1413 : /* }}} */
1414 :
1415 5 : static int ExcimerTimer_set_callback(ExcimerTimer_obj *timer_obj, zval *zp_callback) /* {{{ */
1416 : {
1417 : char *is_callable_error;
1418 :
1419 5 : if (!zend_is_callable_ex(zp_callback, NULL, 0, NULL, NULL, &is_callable_error)) {
1420 0 : php_error_docref(NULL, E_WARNING, "timer callback is not callable: %s",
1421 : is_callable_error);
1422 0 : return FAILURE;
1423 : }
1424 :
1425 5 : zval_ptr_dtor(&timer_obj->z_callback);
1426 5 : ZVAL_COPY(&timer_obj->z_callback, zp_callback);
1427 5 : return SUCCESS;
1428 : }
1429 : /* }}} */
1430 :
1431 : /* {{{ proto void ExcimerTimer::start()
1432 : */
1433 6 : static PHP_METHOD(ExcimerTimer, start)
1434 : {
1435 6 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1436 :
1437 6 : ZEND_PARSE_PARAMETERS_START(0, 0);
1438 6 : ZEND_PARSE_PARAMETERS_END();
1439 :
1440 6 : if (timer_obj->timer.is_running) {
1441 0 : ExcimerTimer_stop(timer_obj);
1442 : }
1443 6 : ExcimerTimer_start(timer_obj);
1444 : }
1445 : /* }}} */
1446 :
1447 : /* {{{ proto void ExcimerTimer::stop()
1448 : */
1449 2 : static PHP_METHOD(ExcimerTimer, stop)
1450 : {
1451 2 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1452 :
1453 2 : ZEND_PARSE_PARAMETERS_START(0, 0);
1454 2 : ZEND_PARSE_PARAMETERS_END();
1455 :
1456 2 : ExcimerTimer_stop(timer_obj);
1457 : }
1458 : /* }}} */
1459 :
1460 : /* {{{ proto float ExcimerTimer::getTime()
1461 : */
1462 3 : static PHP_METHOD(ExcimerTimer, getTime)
1463 : {
1464 3 : ExcimerTimer_obj *timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, getThis());
1465 : struct timespec ts;
1466 :
1467 3 : ZEND_PARSE_PARAMETERS_START(0, 0);
1468 3 : ZEND_PARSE_PARAMETERS_END();
1469 :
1470 3 : excimer_timer_get_time(&timer_obj->timer, &ts);
1471 3 : RETURN_DOUBLE(excimer_timespec_to_double(&ts));
1472 : }
1473 : /* }}} */
1474 :
1475 7 : static void ExcimerTimer_start(ExcimerTimer_obj *timer_obj) /* {{{ */
1476 : {
1477 7 : if (timer_obj->timer.is_valid) {
1478 0 : excimer_timer_destroy(&timer_obj->timer);
1479 : }
1480 7 : if (excimer_timer_init(&timer_obj->timer,
1481 7 : timer_obj->event_type,
1482 : ExcimerTimer_event,
1483 : (void*)timer_obj) == FAILURE)
1484 : {
1485 : /* Error message already sent */
1486 0 : return;
1487 : }
1488 7 : excimer_timer_start(&timer_obj->timer,
1489 : &timer_obj->period,
1490 : &timer_obj->initial);
1491 : }
1492 : /* }}} */
1493 :
1494 2 : static void ExcimerTimer_stop(ExcimerTimer_obj *timer_obj) /* {{{ */
1495 : {
1496 2 : if (timer_obj->timer.is_valid) {
1497 2 : excimer_timer_destroy(&timer_obj->timer);
1498 : }
1499 2 : }
1500 : /* }}} */
1501 :
1502 16 : static void ExcimerTimer_event(zend_long event_count, void *user_data) /* {{{ */
1503 : {
1504 16 : ExcimerTimer_obj *timer_obj = (ExcimerTimer_obj*)user_data;
1505 : zend_fcall_info fci;
1506 : zend_fcall_info_cache fcc;
1507 : zval retval;
1508 : zval z_event_count;
1509 : char *is_callable_error;
1510 :
1511 16 : if (Z_ISNULL(timer_obj->z_callback) || Z_ISUNDEF(timer_obj->z_callback)) {
1512 0 : return;
1513 : }
1514 :
1515 16 : if (zend_fcall_info_init(&timer_obj->z_callback, 0, &fci, &fcc, NULL,
1516 : &is_callable_error) != SUCCESS)
1517 : {
1518 0 : php_error(E_WARNING, "ExcimerTimer callback is not callable (during event): %s",
1519 : is_callable_error);
1520 0 : ExcimerTimer_stop(timer_obj);
1521 0 : return;
1522 : }
1523 :
1524 16 : fci.retval = &retval;
1525 16 : ZVAL_LONG(&z_event_count, event_count);
1526 :
1527 16 : zend_fcall_info_argn(&fci, 1, &z_event_count);
1528 16 : if (zend_call_function(&fci, &fcc) == SUCCESS) {
1529 16 : zval_ptr_dtor(&retval);
1530 : }
1531 16 : zend_fcall_info_args_clear(&fci, 1);
1532 : }
1533 : /* }}} */
1534 :
1535 : /* {{{ proto ExcimerTimer excimer_set_timeout(callable callback, float interval)
1536 : */
1537 1 : PHP_FUNCTION(excimer_set_timeout)
1538 : {
1539 : ExcimerTimer_obj *timer_obj;
1540 : zval * zp_callback;
1541 : double initial;
1542 :
1543 1 : ZEND_PARSE_PARAMETERS_START(2, 2)
1544 1 : Z_PARAM_ZVAL(zp_callback)
1545 2 : Z_PARAM_DOUBLE(initial)
1546 1 : ZEND_PARSE_PARAMETERS_END();
1547 :
1548 1 : object_init_ex(return_value, ExcimerTimer_ce);
1549 1 : timer_obj = EXCIMER_OBJ_ZP(ExcimerTimer, return_value);
1550 1 : if (ExcimerTimer_set_callback(timer_obj, zp_callback) == FAILURE) {
1551 0 : zval_ptr_dtor(return_value);
1552 0 : ZVAL_NULL(return_value);
1553 : }
1554 :
1555 1 : excimer_set_timespec(&timer_obj->initial, initial);
1556 1 : ExcimerTimer_start(timer_obj);
1557 : }
1558 : /* }}} */
1559 :
1560 : static const zend_module_dep excimer_deps[] = {
1561 : #if PHP_VERSION_ID < 70200
1562 : ZEND_MOD_REQUIRED("spl")
1563 : #endif
1564 : ZEND_MOD_END
1565 : };
1566 :
1567 : /* {{{ excimer_module_entry */
1568 : zend_module_entry excimer_module_entry = {
1569 : STANDARD_MODULE_HEADER_EX,
1570 : NULL,
1571 : excimer_deps,
1572 : "excimer",
1573 : excimer_functions,
1574 : PHP_MINIT(excimer),
1575 : PHP_MSHUTDOWN(excimer),
1576 : PHP_RINIT(excimer),
1577 : NULL, /* RSHUTDOWN */
1578 : PHP_MINFO(excimer),
1579 : PHP_EXCIMER_VERSION,
1580 : NO_MODULE_GLOBALS,
1581 : ZEND_MODULE_POST_ZEND_DEACTIVATE_N(excimer),
1582 : STANDARD_MODULE_PROPERTIES_EX
1583 : };
1584 : /* }}} */
1585 :
1586 : #ifdef COMPILE_DL_EXCIMER
1587 : #ifdef ZTS
1588 : ZEND_TSRMLS_CACHE_DEFINE()
1589 : #endif
1590 22 : ZEND_GET_MODULE(excimer)
1591 : #endif
1592 :
1593 : /*
1594 : * Local variables:
1595 : * tab-width: 4
1596 : * c-basic-offset: 4
1597 : * End:
1598 : * vim600: noet sw=4 ts=4 fdm=marker
1599 : * vim<600: noet sw=4 ts=4
1600 : */
|