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