LCOV - code coverage report
Current view: top level - src - excimer.c (source / functions) Hit Total Coverage
Test: mediawiki/php/excimer test coverage report Lines: 386 501 77.0 %
Date: 2024-02-28 22:03:29 Functions: 62 74 83.8 %

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

Generated by: LCOV version 1.13