LCOV - code coverage report
Current view: top level - src - excimer.c (source / functions) Hit Total Coverage
Test: mediawiki/php/excimer test coverage report Lines: 399 495 80.6 %
Date: 2025-08-22 19:33:56 Functions: 61 71 85.9 %

          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             :  */

Generated by: LCOV version 1.14