source: trunk/xcache.c @ 8

Last change on this file since 8 was 8, checked in by moo, 8 years ago

make opcode_spec_def.h optional

File size: 47.1 KB
Line 
1
2#undef DEBUG
3
4/* {{{ macros */
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8
9#include <malloc.h>
10#include <signal.h>
11
12#include "php.h"
13#include "ext/standard/info.h"
14#include "zend_extensions.h"
15#include "SAPI.h"
16
17#include "xcache.h"
18#include "optimizer.h"
19#include "coverage.h"
20#include "disassembler.h"
21#include "align.h"
22#include "stack.h"
23#include "xcache_globals.h"
24#include "processor.h"
25#include "utils.h"
26#include "const_string.h"
27#include "opcode_spec.h"
28
29#ifdef DEBUG
30#   undef NDEBUG
31#   undef inline
32#   define inline
33#else
34#   define NDEBUG
35#endif
36#include <assert.h>
37
38#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
39#define LOCK(x) xc_lock(x->lck)
40#define UNLOCK(x) xc_unlock(x->lck)
41#define ENTER_LOCK(x) do { \
42    int catched = 0; \
43    xc_lock(x->lck); \
44    zend_try { \
45        do
46#define LEAVE_LOCK(x) \
47        while (0); \
48    } zend_catch { \
49        catched = 1; \
50    } zend_end_try(); \
51    xc_unlock(x->lck); \
52} while(0)
53/* }}} */
54
55/* {{{ globals */
56static char *xc_mmap_path = NULL;
57static char *xc_coredump_dir = NULL;
58
59static xc_hash_t xc_php_hcache = {0};
60static xc_hash_t xc_php_hentry = {0};
61static xc_hash_t xc_var_hcache = {0};
62static xc_hash_t xc_var_hentry = {0};
63
64/* total size */
65static zend_ulong xc_php_size  = 0;
66static zend_ulong xc_var_size  = 0;
67
68static xc_cache_t **xc_php_caches = NULL;
69static xc_cache_t **xc_var_caches = NULL;
70
71static zend_bool xc_initized = 0;
72static zend_compile_file_t *origin_compile_file;
73
74static zend_bool xc_test = 0;
75static zend_bool xc_readonly_protection = 0;
76
77static zend_bool xc_module_gotup = 0;
78static zend_bool xc_zend_extension_gotup = 0;
79#if !COMPILE_DL_XCACHE
80#   define zend_extension_entry xcache_zend_extension_entry
81#endif
82ZEND_DLEXPORT zend_extension zend_extension_entry;
83ZEND_DECLARE_MODULE_GLOBALS(xcache);
84/* }}} */
85
86/* any function in *_dmz is only safe be called within locked(single thread) area */
87
88static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
89{
90    /* this function isn't required but can be in dmz */
91
92    if (a->type != b->type) {
93        return 0;
94    }
95    switch (a->type) {
96        case XC_TYPE_PHP:
97#ifdef HAVE_INODE
98            do {
99                xc_entry_data_php_t *ap = a->data.php;
100                xc_entry_data_php_t *bp = b->data.php;
101                return ap->inode == bp->inode
102                    && ap->device == bp->device;
103            } while(0);
104#endif
105            /* fall */
106
107        case XC_TYPE_VAR:
108            do {
109#ifdef IS_UNICODE
110                if (a->name_type == IS_UNICODE) {
111                    if (a->name.ustr.len != b->name.ustr.len) {
112                        return 0;
113                    }
114                    return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
115                }
116                else {
117                    return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
118                }
119#else
120                return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
121#endif
122
123            } while(0);
124        default:
125            assert(0);
126    }
127    return 0;
128}
129/* }}} */
130static void xc_entry_free_dmz(volatile xc_entry_t *xce) /* {{{ */
131{
132    xc_mem_free(xce->cache->mem, (xc_entry_t *)xce);
133}
134/* }}} */
135static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
136{
137    xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
138    xce->next = *head;
139    *head = xce;
140}
141/* }}} */
142static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
143{
144    xc_entry_t *stored_xce;
145
146    xce->hits  = 0;
147    xce->ctime = XG(request_time);
148    xce->atime = XG(request_time);
149    stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
150    if (stored_xce) {
151        xc_entry_add_dmz(stored_xce);
152        return stored_xce;
153    }
154    else {
155        xce->cache->ooms ++;
156        return NULL;
157    }
158}
159/* }}} */
160static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
161{
162    xc_entry_t **last = &(xce->cache->entries[xce->hvalue]);
163    xc_entry_t *p;
164    for (p = *last; p; last = &(p->next), p = p->next) {
165        if (xc_entry_equal_dmz(xce, p)) {
166            *last = p->next;
167            if (p->refcount == 0) {
168                xc_entry_free_dmz(p);
169            }
170            else {
171                p->next = p->cache->deletes;
172                p->cache->deletes = p;
173                p->dtime = XG(request_time);
174            }
175            return;
176        }
177    }
178    assert(0);
179}
180/* }}} */
181static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
182{
183    xc_entry_t *p;
184    for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
185        if (xc_entry_equal_dmz(xce, p)) {
186            if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
187                p->hits ++;
188                p->atime = XG(request_time);
189                return p;
190            }
191            else {
192                xc_entry_remove_dmz(p TSRMLS_CC);
193                return NULL;
194            }
195        }
196    }
197    return NULL;
198}
199/* }}} */
200static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
201{
202    xce->refcount ++;
203    xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
204}
205/* }}} */
206#if 0
207static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
208{
209    xce->refcount ++;
210    xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
211}
212/* }}} */
213#endif
214
215/* helper functions for user functions */
216static void xc_fillinfo_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
217{
218    zval *blocks;
219    const xc_block_t *b;
220#ifndef NDEBUG
221    xc_memsize_t avail = 0;
222#endif
223    xc_mem_t *mem = cache->mem;
224    int i, c, count;
225    xc_entry_t *e;
226
227    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
228    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
229    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->misses);
230    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
231    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
232    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
233
234    count = 0;
235    for (i = 0, c = cache->hentry->size; i < c; i ++) {
236        for (e = cache->entries[i]; e; e = e->next) {
237            count ++;
238        }
239    }
240    add_assoc_long_ex(return_value, ZEND_STRS("cached"), count);
241
242    count = 0;
243    for (e = cache->deletes; e; e = e->next) {
244        count ++;
245    }
246    add_assoc_long_ex(return_value, ZEND_STRS("deleted"), count);
247
248    MAKE_STD_ZVAL(blocks);
249    array_init(blocks);
250
251    add_assoc_long_ex(return_value, ZEND_STRS("size"),  xc_mem_size(mem));
252    add_assoc_long_ex(return_value, ZEND_STRS("avail"), xc_mem_avail(mem));
253    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
254
255    for (b = xc_mem_freeblock_first(mem); b; b = xc_mem_freeblock_next(b)) {
256        zval *bi;
257
258        MAKE_STD_ZVAL(bi);
259        array_init(bi);
260
261        add_assoc_long_ex(bi, ZEND_STRS("size"),   xc_mem_block_size(b));
262        add_assoc_long_ex(bi, ZEND_STRS("offset"), xc_mem_block_offset(mem, b));
263        add_next_index_zval(blocks, bi);
264#ifndef NDEBUG
265        avail += b->size;
266#endif
267    }
268    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
269    assert(avail == xc_mem_avail(mem));
270}
271/* }}} */
272static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
273{
274    zval* ei;
275    xc_entry_data_php_t *php;
276    xc_entry_data_var_t *var;
277
278    ALLOC_INIT_ZVAL(ei);
279    array_init(ei);
280
281    add_assoc_long_ex(ei, ZEND_STRS("size"),     entry->size);
282    add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
283    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
284    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
285    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
286    add_assoc_long_ex(ei, ZEND_STRS("dtime"),    entry->dtime);
287#ifdef IS_UNICODE
288    do {
289        zval *zv;
290        ALLOC_INIT_ZVAL(zv);
291        switch (entry->name_type) {
292            case IS_UNICODE:
293                    ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
294                break;
295            case IS_STRING:
296                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
297                break;
298            default:
299                assert(0);
300        }
301        zv->type = entry->name_type;
302        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
303    } while (0);
304#else
305    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len + 1, 1);
306#endif
307    switch (entry->type) {
308        case XC_TYPE_PHP:
309            php = entry->data.php;
310            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),   php->sourcesize);
311#ifdef HAVE_INODE
312            add_assoc_long_ex(ei, ZEND_STRS("device"),       php->device);
313            add_assoc_long_ex(ei, ZEND_STRS("inode"),        php->inode);
314#endif
315            add_assoc_long_ex(ei, ZEND_STRS("mtime"),        php->mtime);
316
317            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"), php->funcinfo_cnt);
318            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),    php->classinfo_cnt);
319            break;
320        case XC_TYPE_VAR:
321            var = entry->data.var;
322            break;
323
324        default:
325            assert(0);
326    }
327
328    add_next_index_zval(list, ei);
329}
330/* }}} */
331static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
332{
333    zval* list;
334    int i, c;
335    xc_entry_t *e;
336
337    ALLOC_INIT_ZVAL(list);
338    array_init(list);
339
340    for (i = 0, c = cache->hentry->size; i < c; i ++) {
341        for (e = cache->entries[i]; e; e = e->next) {
342            xc_fillentry_dmz(e, 0, list);
343        }
344    }
345    add_assoc_zval(return_value, "cache_list", list);
346
347    ALLOC_INIT_ZVAL(list);
348    array_init(list);
349    for (e = cache->deletes; e; e = e->next) {
350        xc_fillentry_dmz(e, 1, list);
351    }
352    add_assoc_zval(return_value, "deleted_list", list);
353}
354/* }}} */
355
356static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
357{
358    zend_uint i;
359    xc_entry_data_php_t *p = xce->data.php;
360#ifndef ZEND_ENGINE_2
361    /* new ptr which is stored inside CG(class_table) */
362    xc_cest_t **new_cest_ptrs = (xc_cest_t **)do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt);
363#endif
364
365    /* install function */
366    for (i = 0; i < p->funcinfo_cnt; i ++) {
367        xc_funcinfo_t  *fi = &p->funcinfos[i];
368        xc_install_function(xce->name.str.val, &fi->func,
369                UNISW(0, fi->type), fi->key, fi->key_size TSRMLS_CC);
370    }
371
372    /* install class */
373    for (i = 0; i < p->classinfo_cnt; i ++) {
374        xc_classinfo_t *ci = &p->classinfos[i];
375#ifndef ZEND_ENGINE_2
376        zend_class_entry *ce = CestToCePtr(ci->cest);
377        /* fix pointer to the be which inside class_table */
378        if (ce->parent) {
379            zend_uint class_idx = (/* class_num */ (int) ce->parent) - 1;
380            assert(class_idx < i);
381            ci->cest.parent = new_cest_ptrs[class_idx];
382        }
383        new_cest_ptrs[i] =
384#endif
385        xc_install_class(xce->name.str.val, &ci->cest,
386                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
387    }
388
389    i = 1;
390    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
391    zend_llist_add_element(&CG(open_files), h);
392
393#ifndef ZEND_ENGINE_2
394    free_alloca(new_cest_ptrs);
395#endif
396    return p->op_array;
397}
398/* }}} */
399static void xc_entry_gc_real(xc_cache_t **caches, int size TSRMLS_DC) /* {{{ */
400{
401    time_t t = XG(request_time);
402    int i;
403    xc_cache_t *cache;
404    typeof(cache->deletes) p, *last;
405
406    for (i = 0; i < size; i ++) {
407        cache = caches[i];
408        ENTER_LOCK(cache) {
409            if (cache->deletes) {
410                last = (typeof(last)) &cache->deletes;
411                for (p = *last; p; p = p->next) {
412                    if (t - p->dtime > 3600) {
413                        p->refcount = 0;
414                        /* issue warning here */
415                    }
416                    if (p->refcount == 0) {
417                        *last = p->next;
418                        xc_entry_free_dmz(p);
419                    }
420                    else {
421                        last = &(p->next);
422                    }
423                }
424            }
425        } LEAVE_LOCK(cache);
426    }
427}
428/* }}} */
429static void xc_entry_gc(TSRMLS_D) /* {{{ */
430{
431    xc_entry_gc_real(xc_php_caches, xc_php_hcache.size TSRMLS_CC);
432    xc_entry_gc_real(xc_var_caches, xc_var_hcache.size TSRMLS_CC);
433}
434/* }}} */
435static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount) /* {{{ */
436{
437    int i;
438    xc_stack_t *s;
439    xc_cache_t *cache;
440    xc_entry_t *xce;
441
442    for (i = 0; i < cachecount; i ++) {
443        s = &holds[i];
444        if (xc_stack_size(s)) {
445            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
446            ENTER_LOCK(cache) {
447                while (xc_stack_size(holds)) {
448                    xce = (xc_entry_t*) xc_stack_pop(holds);
449                    xce->refcount --;
450                    assert(xce->refcount >= 0);
451                }
452            } LEAVE_LOCK(cache);
453        }
454    }
455}
456/* }}} */
457static void xc_entry_unholds(TSRMLS_D) /* {{{ */
458{
459    xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size);
460    xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size);
461}
462/* }}} */
463static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf) /* {{{ */
464{
465    char filepath[1024];
466    char *paths, *path;
467    char *tokbuf;
468    int size = strlen(include_path) + 1;
469    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
470
471    paths = (char *)do_alloca(size);
472    memcpy(paths, include_path, size);
473
474    for (path = strtok_r(paths, tokens, &tokbuf); path; path = strtok_r(NULL, tokens, &tokbuf)) {
475        if (strlen(path) + strlen(filename) + 1 > 1024) {
476            continue;
477        }
478        snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
479        if (VCWD_STAT(filepath, pbuf) == 0) {
480            free_alloca(paths);
481            return 0;
482        }
483    }
484
485    free_alloca(paths);
486
487    return 1;
488}
489/* }}} */
490
491#define HASH(i) (i)
492#define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
493#define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
494#define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
495#define HASH_NUM(n) HASH(n)
496static inline xc_hash_value_t xc_entry_hash_var(xc_entry_t *xce) /* {{{ */
497{
498    return UNISW(, UG(unicode) ? HASH_USTR_L(xce->name_type, (char *)xce->name.ustr.val, xce->name.ustr.len) :)
499        HASH_STR_L(xce->name.str.val, xce->name.str.len);
500}
501/* }}} */
502static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce) /* {{{ */
503{
504#ifdef HAVE_INODE
505    return HASH(xce->data.php->device + xce->data.php->inode);
506#else
507    return xc_entry_hash_var(xce);
508#endif
509}
510/* }}} */
511static int xc_entry_init_key_php(xc_entry_t *xce, char *filename TSRMLS_DC) /* {{{ */
512{
513    struct stat buf, *pbuf;
514    xc_hash_value_t hv;
515    int cacheid;
516    xc_entry_data_php_t *php;
517
518    if (!filename || !SG(request_info).path_translated) {
519        return 0;
520    }
521
522    do {
523        if (strcmp(SG(request_info).path_translated, filename) == 0) {
524            /* sapi has already done this stat() for us */
525            pbuf = sapi_get_stat(TSRMLS_C);
526            if (pbuf) {
527                break;
528            }
529        }
530
531        pbuf = &buf;
532        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
533            if (VCWD_STAT(filename, pbuf) != 0) {
534                return 0;
535            }
536        }
537        else {
538            if (xc_stat(filename, PG(include_path), pbuf) != 0) {   
539                return 0;
540            }
541        }
542    } while (0);
543
544    if (XG(request_time) - pbuf->st_mtime < 2) {
545        return 0;
546    }
547
548    UNISW(, xce->name_type = IS_STRING;)
549    xce->name.str.val = filename;
550    xce->name.str.len = strlen(filename);
551
552    php = xce->data.php;
553    php->mtime        = pbuf->st_mtime;
554#ifdef HAVE_INODE
555    php->device       = pbuf->st_dev;
556    php->inode        = pbuf->st_ino;
557#endif
558    php->sourcesize   = pbuf->st_size;
559
560
561    hv = xc_entry_hash_php(xce);
562    cacheid = (hv & xc_php_hcache.mask);
563    xce->cache = xc_php_caches[cacheid];
564    hv >>= xc_php_hcache.bits;
565    xce->hvalue = (hv & xc_php_hentry.mask);
566
567    xce->type = XC_TYPE_PHP;
568    return 1;
569}
570/* }}} */
571static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
572{
573    xc_sandbox_t sandbox;
574    zend_op_array *op_array;
575    xc_entry_t xce, *stored_xce;
576    xc_entry_data_php_t php;
577    xc_cache_t *cache;
578    zend_bool clogged = 0;
579    zend_bool catched = 0;
580    char *filename;
581
582    if (!xc_initized) {
583        assert(0);
584    }
585
586    if (!XG(cacher)) {
587        op_array = origin_compile_file(h, type TSRMLS_CC);
588#ifdef HAVE_XCACHE_OPTIMIZER
589        if (XG(optimizer)) {
590            xc_optimize(op_array TSRMLS_CC);
591        }
592#endif
593        return op_array;
594    }
595
596    /* {{{ prepare key
597     * include_once() and require_once() gives us opened_path
598     * however, include() and require() non-absolute path which break
599     * included_files, and may confuse with (include|require)_once
600     * -- Xuefer
601     */
602
603    filename = h->opened_path ? h->opened_path : h->filename;
604    xce.data.php = &php;
605    if (!xc_entry_init_key_php(&xce, filename TSRMLS_CC)) {
606        return origin_compile_file(h, type TSRMLS_CC);
607    }
608    cache = xce.cache;
609    /* }}} */
610    /* {{{ restore */
611    /* stale precheck */
612    if (cache->compiling) {
613        cache->clogs ++; /* is it safe here? */
614        return origin_compile_file(h, type TSRMLS_CC);
615    }
616
617    stored_xce = NULL;
618    op_array = NULL;
619    ENTER_LOCK(cache) {
620        /* clogged */
621        if (cache->compiling) {
622            cache->clogs ++;
623            op_array = NULL;
624            clogged = 1;
625            break;
626        }
627
628        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
629        /* found */
630        if (stored_xce) {
631#ifdef DEBUG
632            fprintf(stderr, "found %s, catch it\n", stored_xce->name.str.val);
633#endif
634            xc_entry_hold_php_dmz(stored_xce TSRMLS_CC);
635            cache->hits ++;
636            break;
637        }
638
639        cache->compiling = XG(request_time);
640        cache->misses ++;
641    } LEAVE_LOCK(cache);
642
643    /* found */
644    if (stored_xce) {
645        goto restore;
646    }
647
648    /* clogged */
649    if (clogged) {
650        return origin_compile_file(h, type TSRMLS_CC);
651    }
652    /* }}} */
653
654    /* {{{ compile */
655#ifdef DEBUG
656    fprintf(stderr, "compiling %s\n", filename);
657#endif
658
659    /* make compile inside sandbox */
660    xc_sandbox_init(&sandbox, filename TSRMLS_CC);
661
662    zend_try {
663        op_array = origin_compile_file(h, type TSRMLS_CC);
664    } zend_catch {
665        catched = 1;
666    } zend_end_try();
667
668    if (catched) {
669        goto err_bailout;
670    }
671
672    if (op_array == NULL) {
673        goto err_oparray;
674    }
675
676#ifdef HAVE_XCACHE_OPTIMIZER
677    if (XG(optimizer)) {
678        xc_optimize(op_array TSRMLS_CC);
679    }
680#endif
681
682    php.op_array      = op_array;
683
684    php.funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
685    php.classinfo_cnt = zend_hash_num_elements(CG(class_table));
686
687    php.funcinfos     = ECALLOC_N(php.funcinfos, php.funcinfo_cnt);
688    if (!php.funcinfos) {
689        goto err_func;
690    }
691    php.classinfos    = ECALLOC_N(php.classinfos, php.classinfo_cnt);
692    if (!php.classinfos) {
693        goto err_class;
694    }
695    /* }}} */
696    /* {{{ shallow copy, pointers only */ {
697        Bucket *b;
698        unsigned int i;
699
700        b = CG(function_table)->pListHead;
701        for (i = 0; b; i ++, b = b->pListNext) {
702            xc_funcinfo_t *fi = &php.funcinfos[i];
703
704            assert(i < php.funcinfo_cnt);
705            assert(b->pData);
706            memcpy(&fi->func, b->pData, sizeof(zend_function));
707            UNISW(, fi->type = b->key.type;)
708            fi->key        = BUCKET_KEY(b);
709            fi->key_size   = b->nKeyLength;
710        }
711
712        b = CG(class_table)->pListHead;
713        for (i = 0; b; i ++, b = b->pListNext) {
714            xc_classinfo_t *ci = &php.classinfos[i];
715
716            assert(i < php.classinfo_cnt);
717            assert(b->pData);
718            memcpy(&ci->cest, b->pData, sizeof(xc_cest_t));
719            UNISW(, ci->type = b->key.type;)
720            ci->key        = BUCKET_KEY(b);
721            ci->key_size   = b->nKeyLength;
722            /* need to fix inside store */
723        }
724    }
725    /* }}} */
726    xc_entry_gc(TSRMLS_C);
727    ENTER_LOCK(cache) { /* {{{ store/add entry */
728        stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
729    } LEAVE_LOCK(cache);
730    /* }}} */
731#ifdef DEBUG
732    fprintf(stderr, "stored\n");
733#endif
734
735    efree(xce.data.php->classinfos);
736err_class:
737    efree(xce.data.php->funcinfos);
738err_func:
739err_oparray:
740err_bailout:
741
742    if (xc_test && stored_xce) {
743        /* no install, keep open_files too for h */
744        xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
745        sandbox.tmp_open_files->dtor = NULL;
746    }
747    else {
748        xc_sandbox_free(&sandbox, 1 TSRMLS_CC);
749    }
750
751    ENTER_LOCK(cache) {
752        cache->compiling = 0;
753    } LEAVE_LOCK(cache);
754    if (catched) {
755        zend_bailout();
756    }
757    if (xc_test && stored_xce) {
758        goto restore;
759    }
760    return op_array;
761
762restore:
763#ifdef DEBUG
764    fprintf(stderr, "restoring\n");
765#endif
766    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
767    op_array = xc_entry_install(&xce, h TSRMLS_CC);
768
769    efree(xce.data.php->funcinfos);
770    efree(xce.data.php->classinfos);
771    efree(xce.data.php);
772#ifdef DEBUG
773    fprintf(stderr, "restored\n");
774#endif
775    return op_array;
776}
777/* }}} */
778
779/* gdb helper functions, but N/A for coredump */
780int xc_is_rw(const void *p) /* {{{ */
781{
782    int i;
783    if (!xc_initized) {
784        return 0;
785    }
786    for (i = 0; i < xc_php_hcache.size; i ++) {
787        if (xc_shm_is_readwrite(xc_php_caches[i]->shm, p)) {
788            return 1;
789        }
790    }
791    for (i = 0; i < xc_var_hcache.size; i ++) {
792        if (xc_shm_is_readwrite(xc_var_caches[i]->shm, p)) {
793            return 1;
794        }
795    }
796    return 0;
797}
798/* }}} */
799int xc_is_ro(const void *p) /* {{{ */
800{
801    int i;
802    if (!xc_initized) {
803        return 0;
804    }
805    for (i = 0; i < xc_php_hcache.size; i ++) {
806        if (xc_shm_is_readonly(xc_php_caches[i]->shm, p)) {
807            return 1;
808        }
809    }
810    for (i = 0; i < xc_var_hcache.size; i ++) {
811        if (xc_shm_is_readonly(xc_var_caches[i]->shm, p)) {
812            return 1;
813        }
814    }
815    return 0;
816}
817/* }}} */
818int xc_is_shm(const void *p) /* {{{ */
819{
820    return xc_is_ro(p) || xc_is_rw(p);
821}
822/* }}} */
823
824/* module helper function */
825static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
826{
827    struct {
828        const char *prefix;
829        int (*getsize)();
830        const char *(*get)(zend_uchar i);
831    } nameinfos[] = {
832        { "",        xc_get_op_type_count,   xc_get_op_type   },
833        { "",        xc_get_data_type_count, xc_get_data_type },
834        { "",        xc_get_opcode_count,    xc_get_opcode    },
835        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
836        { NULL, NULL, NULL }
837    };
838    typeof(nameinfos[0])* p;
839    int i;
840    char const_name[96];
841    int const_name_len;
842    int undefdone = 0;
843
844    for (p = nameinfos; p->getsize; p ++) {
845        for (i = p->getsize() - 1; i >= 0; i --) {
846            const char *name = p->get(i);
847            if (!name) continue;
848            if (strcmp(name, "UNDEF") == 0) {
849                if (undefdone) continue;
850                undefdone = 1;
851            }
852            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
853            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
854        }
855    }
856
857    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
858    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
859    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
860    return 0;
861}
862/* }}} */
863static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache TSRMLS_DC) /* {{{ */
864{
865    int i;
866    xc_cache_t *cache;
867    xc_shm_t *shm;
868
869    if (!caches) {
870        return NULL;
871    }
872    shm = NULL;
873    for (i = 0; i < hcache->size; i ++) {
874        cache = caches[i];
875        if (cache) {
876            if (cache->lck) {
877                xc_lock_destroy(cache->lck);
878            }
879            /* do NOT free
880            if (cache->entries) {
881                xc_mem_free(cache->mem, cache->entries);
882            }
883            xc_mem_free(cache->mem, cache);
884            */
885            xc_mem_destroy(cache->mem);
886            shm = cache->shm;
887        }
888    }
889    free(caches);
890    return shm;
891}
892/* }}} */
893static xc_cache_t **xc_cache_init(xc_shm_t *shm, char *ptr, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize TSRMLS_DC) /* {{{ */
894{
895    xc_cache_t **caches = NULL, *cache;
896    xc_mem_t *mem;
897    int i;
898    xc_memsize_t memsize = shmsize / hcache->size;
899
900    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
901
902    for (i = 0; i < hcache->size; i ++) {
903        mem = xc_mem_init(ptr, memsize);
904        ptr += ALIGN(memsize);
905        CHECK(cache          = xc_mem_calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
906        CHECK(cache->entries = xc_mem_calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
907        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
908
909        cache->hcache  = hcache;
910        cache->hentry  = hentry;
911        cache->shm     = shm;
912        cache->mem     = mem;
913        cache->cacheid = i;
914        caches[i] = cache;
915    }
916    assert(ptr <= (char*)xc_shm_ptr(shm) + shmsize);
917    return caches;
918
919err:
920    if (caches) {
921        xc_cache_destroy(caches, hcache);
922    }
923    return NULL;
924}
925/* }}} */
926static void xc_destroy() /* {{{ */
927{
928    xc_shm_t *shm = NULL;
929
930    if (origin_compile_file) {
931        zend_compile_file = origin_compile_file;
932        origin_compile_file = NULL;
933    }
934
935    if (xc_php_caches) {
936        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
937        xc_php_caches = NULL;
938    }
939    if (xc_var_caches) {
940        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
941        xc_var_caches = NULL;
942    }
943    if (shm) {
944        xc_shm_destroy(shm);
945    }
946}
947/* }}} */
948static int xc_init(int module_number TSRMLS_DC) /* {{{ */
949{
950    xc_php_caches = xc_var_caches = NULL;
951    xc_shm_t *shm;
952    char *ptr;
953
954    if (xc_php_size || xc_var_size) {
955        CHECK(shm = xc_shm_init(xc_mmap_path, ALIGN(xc_php_size) + ALIGN(xc_var_size), xc_readonly_protection), "Cannot create shm");
956        if (!xc_shm_can_readonly(shm)) {
957            xc_readonly_protection = 0;
958        }
959
960        ptr = (char *)xc_shm_ptr(shm);
961        if (xc_php_size) {
962            origin_compile_file = zend_compile_file;
963            zend_compile_file = xc_compile_file;
964
965            CHECK(xc_php_caches = xc_cache_init(shm, ptr, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
966            ptr += ALIGN(xc_php_size);
967        }
968
969        if (xc_var_size) {
970            CHECK(xc_var_caches = xc_cache_init(shm, ptr, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
971        }
972    }
973    return 1;
974
975err:
976    if (xc_php_caches || xc_var_caches) {
977        xc_destroy();
978        /* shm destroied */
979    }
980    else if (shm) {
981        xc_shm_destroy(shm);
982    }
983    return 0;
984}
985/* }}} */
986static void xc_request_init(TSRMLS_D) /* {{{ */
987{
988    if (XG(cacher)) {
989#if PHP_API_VERSION <= 20041225
990        XG(request_time) = time(NULL);
991#else
992        XG(request_time) = sapi_get_request_time(TSRMLS_C);
993#endif
994    }
995#ifdef HAVE_XCACHE_COVERAGE
996    xc_coverage_request_init(TSRMLS_C);
997#endif
998}
999/* }}} */
1000static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1001{
1002    xc_entry_unholds(TSRMLS_C);
1003#ifdef HAVE_XCACHE_COVERAGE
1004    xc_coverage_request_shutdown(TSRMLS_C);
1005#endif
1006}
1007/* }}} */
1008static void xc_init_globals(zend_xcache_globals* xc_globals TSRMLS_DC) /* {{{ */
1009{
1010    int i;
1011
1012    if (xc_php_hcache.size) {
1013        xc_globals->php_holds = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1014        for (i = 0; i < xc_php_hcache.size; i ++) {
1015            xc_stack_init(&xc_globals->php_holds[i]);
1016        }
1017    }
1018    else {
1019        xc_globals->php_holds = NULL;
1020    }
1021
1022    if (xc_var_hcache.size) {
1023        xc_globals->var_holds = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1024        for (i = 0; i < xc_var_hcache.size; i ++) {
1025            xc_stack_init(&xc_globals->var_holds[i]);
1026        }
1027    }
1028    else {
1029        xc_globals->var_holds = NULL;
1030    }
1031
1032#ifdef HAVE_XCACHE_COVERAGE
1033    xc_globals->coverages = NULL;
1034#endif
1035}
1036/* }}} */
1037static void xc_shutdown_globals(zend_xcache_globals* xc_globals TSRMLS_DC) /* {{{ */
1038{
1039    int i;
1040
1041    if (xc_globals->php_holds != NULL) {
1042        for (i = 0; i < xc_php_hcache.size; i ++) {
1043            xc_stack_destroy(&xc_globals->php_holds[i]);
1044        }
1045        free(xc_globals->php_holds);
1046        xc_globals->php_holds = NULL;
1047    }
1048
1049    if (xc_globals->var_holds != NULL) {
1050        for (i = 0; i < xc_var_hcache.size; i ++) {
1051            xc_stack_destroy(&xc_globals->var_holds[i]);
1052        }
1053        free(xc_globals->var_holds);
1054        xc_globals->var_holds = NULL;
1055    }
1056}
1057/* }}} */
1058
1059#define NEED_INITIZED() do { \
1060    if (!xc_initized) { \
1061        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache is not initized"); \
1062        RETURN_FALSE; \
1063    } \
1064} while (0)
1065
1066/* user functions */
1067/* {{{ xcache_op */
1068typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
1069static void xcache_op(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
1070{
1071    NEED_INITIZED();
1072    long type;
1073    int size;
1074    xc_cache_t **caches, *cache;
1075    long id = 0;
1076
1077    if (optype == XC_OP_COUNT) {
1078        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1079            return;
1080        }
1081    }
1082    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1083        return;
1084    }
1085
1086    switch (type) {
1087        case XC_TYPE_PHP:
1088            size = xc_php_hcache.size;
1089            caches = xc_php_caches;
1090            break;
1091
1092        case XC_TYPE_VAR:
1093            size = xc_var_hcache.size;
1094            caches = xc_var_caches;
1095            break;
1096
1097        default:
1098            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1099            RETURN_FALSE;
1100    }
1101
1102    switch (optype) {
1103        case XC_OP_COUNT:
1104            RETURN_LONG(size)
1105            break;
1106
1107        case XC_OP_INFO:
1108        case XC_OP_LIST:
1109            if (id < 0 || id >= size) {
1110                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1111                RETURN_FALSE;
1112            }
1113
1114            array_init(return_value);
1115
1116            cache = caches[id];
1117            ENTER_LOCK(cache) {
1118                if (optype == XC_OP_INFO) {
1119                    xc_fillinfo_dmz(cache, return_value TSRMLS_CC);
1120                }
1121                else {
1122                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1123                }
1124            } LEAVE_LOCK(cache);
1125            break;
1126        case XC_OP_CLEAR:
1127            {
1128                xc_entry_t *e;
1129                int i, c;
1130
1131                if (id < 0 || id >= size) {
1132                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1133                    RETURN_FALSE;
1134                }
1135
1136                cache = caches[id];
1137                ENTER_LOCK(cache) {
1138                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
1139                        for (e = cache->entries[i]; e; e = e->next) {
1140                            xc_entry_remove_dmz(e TSRMLS_CC);
1141                        }
1142                        cache->entries[i] = NULL;
1143                    }
1144                } LEAVE_LOCK(cache);
1145                xc_entry_gc(TSRMLS_C);
1146            }
1147            break;
1148
1149        default:
1150            assert(0);
1151    }
1152}
1153/* }}} */
1154/* {{{ proto array xcache_count(int type) */
1155PHP_FUNCTION(xcache_count)
1156{
1157    xcache_op(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1158}
1159/* }}} */
1160/* {{{ proto array xcache_info(int type, int id) */
1161PHP_FUNCTION(xcache_info)
1162{
1163    xcache_op(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1164}
1165/* }}} */
1166/* {{{ proto array xcache_list(int type, int id) */
1167PHP_FUNCTION(xcache_list)
1168{
1169    xcache_op(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1170}
1171/* }}} */
1172/* {{{ proto array xcache_clear_cache(int type, int id) */
1173PHP_FUNCTION(xcache_clear_cache)
1174{
1175    xcache_op(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1176}
1177/* }}} */
1178
1179static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1180{
1181    xc_hash_value_t hv;
1182    int cacheid;
1183
1184    switch (Z_TYPE_P(name)) {
1185#ifdef IS_UNICODE
1186        case IS_UNICODE:
1187#endif
1188        case IS_STRING:
1189            break;
1190        default:
1191#ifdef IS_UNICODE
1192            convert_to_text(name);
1193#else
1194            convert_to_string(name);
1195#endif
1196    }
1197#ifdef IS_UNICODE
1198    xce->name_type = name->type;
1199#endif
1200    xce->name = name->value;
1201
1202    hv = xc_entry_hash_var(xce);
1203
1204    cacheid = (hv & xc_var_hcache.mask);
1205    xce->cache = xc_var_caches[cacheid];
1206    hv >>= xc_var_hcache.bits;
1207    xce->hvalue = (hv & xc_var_hentry.mask);
1208
1209    xce->type = XC_TYPE_VAR;
1210    return SUCCESS;
1211}
1212/* }}} */
1213#define TIME_MAX (sizeof(time_t) == sizeof(long) ? LONG_MAX : INT_MAX)
1214/* {{{ proto mixed xcache_get(string name) */
1215PHP_FUNCTION(xcache_get)
1216{
1217    xc_entry_t xce, *stored_xce;
1218    xc_entry_data_var_t var;
1219    zval *name;
1220
1221    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1222        return;
1223    }
1224    xce.data.var = &var;
1225    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1226
1227    ENTER_LOCK(xce.cache) {
1228        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1229        if (stored_xce) {
1230            if (XG(request_time) <= stored_xce->data.var->etime) {
1231                xc_processor_restore_zval(return_value, stored_xce->data.var->value TSRMLS_CC);
1232                /* return */
1233                break;
1234            }
1235            else {
1236                xc_entry_remove_dmz(stored_xce);
1237            }
1238        }
1239
1240        RETVAL_NULL();
1241    } LEAVE_LOCK(xce.cache);
1242}
1243/* }}} */
1244/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl]) */
1245PHP_FUNCTION(xcache_set)
1246{
1247    xc_entry_t xce, *stored_xce;
1248    xc_entry_data_var_t var;
1249    zval *name;
1250    zval *value;
1251    long ttl = 0;
1252
1253    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &ttl) == FAILURE) {
1254        return;
1255    }
1256    xce.data.var = &var;
1257    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1258
1259    ENTER_LOCK(xce.cache) {
1260        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1261        if (stored_xce) {
1262            xc_entry_remove_dmz(stored_xce);
1263        }
1264        var.value = value;
1265        var.etime = ttl ? XG(request_time) + ttl : TIME_MAX;
1266        RETVAL_BOOL(xc_entry_store_dmz(&xce) != NULL ? 1 : 0);
1267    } LEAVE_LOCK(xce.cache);
1268}
1269/* }}} */
1270/* {{{ proto mixed xcache_isset(string name) */
1271PHP_FUNCTION(xcache_isset)
1272{
1273    xc_entry_t xce, *stored_xce;
1274    xc_entry_data_var_t var;
1275    zval *name;
1276
1277    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1278        return;
1279    }
1280    xce.data.var = &var;
1281    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1282
1283    ENTER_LOCK(xce.cache) {
1284        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1285        if (stored_xce) {
1286            if (XG(request_time) <= stored_xce->data.var->etime) {
1287                RETVAL_TRUE;
1288                /* return */
1289                break;
1290            }
1291            else {
1292                xc_entry_remove_dmz(stored_xce);
1293            }
1294        }
1295
1296        RETVAL_FALSE;
1297    } LEAVE_LOCK(xce.cache);
1298}
1299/* }}} */
1300/* {{{ proto bool  xcache_unset(string name) */
1301PHP_FUNCTION(xcache_unset)
1302{
1303    xc_entry_t xce, *stored_xce;
1304    xc_entry_data_var_t var;
1305    zval *name;
1306
1307    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1308        return;
1309    }
1310    xce.data.var = &var;
1311    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1312
1313    ENTER_LOCK(xce.cache) {
1314        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1315        if (stored_xce) {
1316            xc_entry_remove_dmz(stored_xce);
1317            RETVAL_TRUE;
1318        }
1319        else {
1320            RETVAL_FALSE;
1321        }
1322    } LEAVE_LOCK(xce.cache);
1323}
1324/* }}} */
1325static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1326{
1327    xc_entry_t xce, *stored_xce;
1328    xc_entry_data_var_t var, *stored_var;
1329    zval *name;
1330    long count = 1;
1331    long ttl = 0;
1332    long value = 0;
1333    zval oldzval;
1334
1335    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &ttl) == FAILURE) {
1336        return;
1337    }
1338    xce.data.var = &var;
1339    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1340
1341    ENTER_LOCK(xce.cache) {
1342        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1343        if (stored_xce) {
1344#ifdef DEBUG
1345            fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
1346#endif
1347            stored_var = stored_xce->data.var;
1348            /* timeout */
1349            if (XG(request_time) > stored_var->etime) {
1350#ifdef DEBUG
1351                fprintf(stderr, "incdec: expired\n");
1352#endif
1353                xc_entry_remove_dmz(stored_xce);
1354                stored_xce = NULL;
1355            }
1356            else {
1357                /* do it in place */
1358                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
1359#ifdef DEBUG
1360                    fprintf(stderr, "incdec: islong\n");
1361#endif
1362                    value = Z_LVAL_P(stored_var->value);
1363                    value += (inc == 1 ? count : - count);
1364                    RETVAL_LONG(value);
1365                    Z_LVAL_P(stored_var->value) = value;
1366                    break;
1367                }
1368                else {
1369#ifdef DEBUG
1370                    fprintf(stderr, "incdec: notlong\n");
1371#endif
1372                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value TSRMLS_CC);
1373                    convert_to_long(&oldzval);
1374                    value = Z_LVAL(oldzval);
1375                    zval_dtor(&oldzval);
1376                }
1377            }
1378        }
1379#ifdef DEBUG
1380        else {
1381            fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
1382        }
1383#endif
1384
1385        value += (inc == 1 ? count : - count);
1386        RETVAL_LONG(value);
1387        var.value = return_value;
1388        var.etime = ttl ? XG(request_time) + ttl : TIME_MAX;
1389        if (stored_xce) {
1390            xce.atime = stored_xce->atime;
1391            xce.ctime = stored_xce->ctime;
1392            xce.hits  = stored_xce->hits;
1393            xc_entry_remove_dmz(stored_xce);
1394        }
1395        xc_entry_store_dmz(&xce);
1396
1397    } LEAVE_LOCK(xce.cache);
1398}
1399/* }}} */
1400/* {{{ proto int xcache_inc(string name [, int value [, int ttl]]) */
1401PHP_FUNCTION(xcache_inc)
1402{
1403    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1404}
1405/* }}} */
1406/* {{{ proto int xcache_dec(string name [, int value [, int ttl]]) */
1407PHP_FUNCTION(xcache_dec)
1408{
1409    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1410}
1411/* }}} */
1412/* {{{ proto string xcache_asm(string filename) */
1413#ifdef HAVE_XCACHE_ASSEMBLER
1414PHP_FUNCTION(xcache_asm)
1415{
1416}
1417#endif
1418/* }}} */
1419#ifdef HAVE_XCACHE_DISASSEMBLER
1420/* {{{ proto string xcache_dasm_file(string filename) */
1421PHP_FUNCTION(xcache_dasm_file)
1422{
1423    char *filename;
1424    long filename_len;
1425
1426    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
1427        return;
1428    }
1429    if (!filename_len) RETURN_FALSE;
1430
1431    xc_dasm_file(return_value, filename TSRMLS_CC);
1432}
1433/* }}} */
1434/* {{{ proto string xcache_dasm_string(string code) */
1435PHP_FUNCTION(xcache_dasm_string)
1436{
1437    zval *code;
1438
1439    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
1440        return;
1441    }
1442    xc_dasm_string(return_value, code TSRMLS_CC);
1443}
1444/* }}} */
1445#endif
1446/* {{{ proto string xcache_encode(string filename) */
1447#ifdef HAVE_XCACHE_ENCODER
1448PHP_FUNCTION(xcache_encode)
1449{
1450}
1451#endif
1452/* }}} */
1453/* {{{ proto bool xcache_decode(string filename) */
1454#ifdef HAVE_XCACHE_DECODER
1455PHP_FUNCTION(xcache_decode)
1456{
1457}
1458#endif
1459/* }}} */
1460/* {{{ xc_call_getter */
1461typedef const char *(xc_name_getter_t)(zend_uchar type);
1462static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
1463{
1464    long spec;
1465    const char *name;
1466
1467    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1468        return;
1469    }
1470    if (spec >= 0 && spec < count) {
1471        name = getter(spec);
1472        if (name) {
1473            /* RETURN_STRING */
1474            int len = strlen(name);
1475            return_value->value.str.len = len;
1476            return_value->value.str.val = estrndup(name, len);
1477            return_value->type = IS_STRING; 
1478            return;
1479        }
1480    }
1481    RETURN_NULL();
1482}
1483/* }}} */
1484/* {{{ proto string xcache_get_op_type(int op_type) */
1485PHP_FUNCTION(xcache_get_op_type)
1486{
1487    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1488}
1489/* }}} */
1490/* {{{ proto string xcache_get_data_type(int type) */
1491PHP_FUNCTION(xcache_get_data_type)
1492{
1493    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1494}
1495/* }}} */
1496/* {{{ proto string xcache_get_opcode(int opcode) */
1497PHP_FUNCTION(xcache_get_opcode)
1498{
1499    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1500}
1501/* }}} */
1502/* {{{ proto string xcache_get_op_spec(int op_type) */
1503PHP_FUNCTION(xcache_get_op_spec)
1504{
1505    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1506}
1507/* }}} */
1508#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1509/* {{{ proto string xcache_get_opcode_spec(int opcode) */
1510PHP_FUNCTION(xcache_get_opcode_spec)
1511{
1512    long spec;
1513    const xc_opcode_spec_t *opspec;
1514
1515    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1516        return;
1517    }
1518    if (spec <= xc_get_opcode_spec_count()) {
1519        opspec = xc_get_opcode_spec(spec);
1520        if (opspec) {
1521            array_init(return_value);
1522            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
1523            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
1524            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
1525            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
1526            return;
1527        }
1528    }
1529    RETURN_NULL();
1530}
1531/* }}} */
1532#endif
1533/* {{{ proto mixed xcache_get_special_value(zval value) */
1534PHP_FUNCTION(xcache_get_special_value)
1535{
1536    zval *value;
1537
1538    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
1539        return;
1540    }
1541
1542    if (value->type == IS_CONSTANT) {
1543        *return_value = *value;
1544        zval_copy_ctor(return_value);
1545        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
1546        return;
1547    }
1548
1549    if (value->type == IS_CONSTANT_ARRAY) {
1550        *return_value = *value;
1551        zval_copy_ctor(return_value);
1552        return_value->type = IS_ARRAY;
1553        return;
1554    }
1555
1556    RETURN_NULL();
1557}
1558/* }}} */
1559/* {{{ proto string xcache_coredump(int op_type) */
1560PHP_FUNCTION(xcache_coredump)
1561{
1562    raise(SIGSEGV);
1563}
1564/* }}} */
1565/* {{{ proto string xcache_is_autoglobal(string name) */
1566PHP_FUNCTION(xcache_is_autoglobal)
1567{
1568    char *name;
1569    long name_len;
1570
1571    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
1572        return;
1573    }
1574
1575    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
1576}
1577/* }}} */
1578static function_entry xcache_functions[] = /* {{{ */
1579{
1580    PHP_FE(xcache_count,             NULL)
1581    PHP_FE(xcache_info,              NULL)
1582    PHP_FE(xcache_list,              NULL)
1583    PHP_FE(xcache_clear_cache,       NULL)
1584    PHP_FE(xcache_coredump,          NULL)
1585#ifdef HAVE_XCACHE_ASSEMBLER
1586    PHP_FE(xcache_asm,               NULL)
1587#endif
1588#ifdef HAVE_XCACHE_DISASSEMBLER
1589    PHP_FE(xcache_dasm_file,         NULL)
1590    PHP_FE(xcache_dasm_string,       NULL)
1591#endif
1592#ifdef HAVE_XCACHE_ENCODER
1593    PHP_FE(xcache_encode,            NULL)
1594#endif
1595#ifdef HAVE_XCACHE_DECODER
1596    PHP_FE(xcache_decode,            NULL)
1597#endif
1598#ifdef HAVE_XCACHE_COVERAGE
1599    PHP_FE(xcache_coverage_decode,   NULL)
1600#endif
1601    PHP_FE(xcache_get_special_value, NULL)
1602    PHP_FE(xcache_get_op_type,       NULL)
1603    PHP_FE(xcache_get_data_type,     NULL)
1604    PHP_FE(xcache_get_opcode,        NULL)
1605#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1606    PHP_FE(xcache_get_opcode_spec,   NULL)
1607#endif
1608    PHP_FE(xcache_is_autoglobal,     NULL)
1609    PHP_FE(xcache_inc,               NULL)
1610    PHP_FE(xcache_dec,               NULL)
1611    PHP_FE(xcache_get,               NULL)
1612    PHP_FE(xcache_set,               NULL)
1613    PHP_FE(xcache_isset,             NULL)
1614    PHP_FE(xcache_unset,             NULL)
1615    {NULL, NULL,                     NULL}
1616};
1617/* }}} */
1618
1619/* signal handler */
1620static void (*original_sigsegv_handler)(int) = NULL;
1621static void xcache_sigsegv_handler(int dummy) /* {{{ */
1622{
1623    if (original_sigsegv_handler == xcache_sigsegv_handler) {
1624        signal(SIGSEGV, original_sigsegv_handler);
1625    } else {
1626        signal(SIGSEGV, SIG_DFL);
1627    }
1628    if (xc_coredump_dir && xc_coredump_dir[0]) {
1629        chdir(xc_coredump_dir);
1630    }
1631    if (original_sigsegv_handler != xcache_sigsegv_handler) {
1632        original_sigsegv_handler(dummy);
1633    }
1634}
1635/* }}} */
1636
1637/* {{{ PHP_INI */
1638static PHP_INI_MH(xc_OnUpdateLong)
1639{
1640    long *p = (long *)mh_arg1;
1641    *p = zend_atoi(new_value, new_value_length);
1642    return SUCCESS;
1643}
1644
1645static PHP_INI_MH(xc_OnUpdateBool)
1646{
1647    zend_bool *p = (zend_bool *)mh_arg1;
1648
1649    if (strncasecmp("on", new_value, sizeof("on"))) {
1650        *p = (zend_bool) atoi(new_value);
1651    }
1652    else {
1653        *p = (zend_bool) 1;
1654    }
1655    return SUCCESS;
1656}
1657
1658static PHP_INI_MH(xc_OnUpdateHashInfo)
1659{
1660    xc_hash_t *p = (xc_hash_t *)mh_arg1;
1661    int bits, size;
1662
1663    p->size = zend_atoi(new_value, new_value_length);
1664    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
1665        /* empty body */
1666    }
1667    p->size = size;
1668    p->bits = bits;
1669    p->mask = size - 1;
1670
1671    return SUCCESS;
1672}
1673
1674static PHP_INI_MH(xc_OnUpdateString)
1675{
1676    char **p = (char**)mh_arg1;
1677    if (*p) {
1678        pefree(*p, 1);
1679    }
1680    *p = pemalloc(strlen(new_value) + 1, 1);
1681    strcpy(*p, new_value);
1682    return SUCCESS;
1683}
1684#ifdef ZEND_ENGINE_2
1685#define OnUpdateInt OnUpdateLong
1686#endif
1687
1688PHP_INI_BEGIN()
1689    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateLong,     &xc_php_size)
1690    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateHashInfo, &xc_php_hcache)
1691    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateHashInfo, &xc_php_hentry)
1692
1693    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateLong,     &xc_var_size)
1694    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateHashInfo, &xc_var_hcache)
1695    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateHashInfo, &xc_var_hentry)
1696
1697    PHP_INI_ENTRY1     ("xcache.mmap_path",      "/dev/zero", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
1698    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
1699    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
1700    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
1701
1702    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
1703#ifdef HAVE_XCACHE_OPTIMIZER
1704    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
1705#endif
1706#ifdef HAVE_XCACHE_COVERAGE
1707    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coveragedump_dir)
1708    STD_PHP_INI_BOOLEAN("xcache.coveragedumper" ,        "1", PHP_INI_ALL,    OnUpdateBool,        coveragedumper,    zend_xcache_globals, xcache_globals)
1709#endif
1710PHP_INI_END()
1711/* }}} */
1712/* {{{ PHP_MINFO_FUNCTION(xcache) */
1713static PHP_MINFO_FUNCTION(xcache)
1714{
1715    php_info_print_table_start();
1716    php_info_print_table_header(2, "XCache Support", (xc_php_size || xc_var_size) ? "enabled" : "disabled");
1717    php_info_print_table_row(2, "Version", XCACHE_VERSION);
1718    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
1719    php_info_print_table_row(2, "Opcode Cache", xc_php_size ? "enabled" : "disabled");
1720    php_info_print_table_row(2, "Variable Cache", xc_var_size ? "enabled" : "disabled");
1721    php_info_print_table_end();
1722    DISPLAY_INI_ENTRIES();
1723}
1724/* }}} */
1725/* {{{ extension startup */
1726static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
1727{
1728    zend_extension extension;
1729
1730    extension = *new_extension;
1731    extension.handle = handle;
1732
1733    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
1734
1735    zend_llist_add_element(&zend_extensions, &extension);
1736#ifdef DEBUG
1737    fprintf(stderr, "registered\n");
1738#endif
1739}
1740
1741/* dirty check */
1742#if defined(COMPILE_DL_XCACHE) && (defined(ZEND_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__))
1743#   define zend_append_version_info(x) do { } while (0)
1744#else
1745extern void zend_append_version_info(zend_extension *extension);
1746#endif
1747static int xc_zend_extension_startup(zend_extension *extension)
1748{
1749    if (extension->startup) {
1750        if (extension->startup(extension) != SUCCESS) {
1751            return FAILURE;
1752        }
1753        zend_append_version_info(extension);
1754    }
1755    return SUCCESS;
1756}
1757/* }}} */
1758/* {{{ PHP_MINIT_FUNCTION(xcache) */
1759static PHP_MINIT_FUNCTION(xcache)
1760{
1761    char *env;
1762
1763    REGISTER_INI_ENTRIES();
1764
1765    xc_module_gotup = 1;
1766    if (!xc_zend_extension_gotup) {
1767        if (zend_get_extension(XCACHE_NAME) == NULL) {
1768            xc_zend_extension_register(&zend_extension_entry, 0);
1769            xc_zend_extension_startup(&zend_extension_entry);
1770        }
1771    }
1772
1773    if (strcmp(sapi_module.name, "cli") == 0) {
1774        if ((env = getenv("XCACHE_TEST")) != NULL) {
1775            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
1776        }
1777        if (!xc_test) {
1778            /* disable cache for cli except for test */
1779            xc_php_size = xc_var_size = 0;
1780        }
1781    }
1782
1783    if (xc_php_size <= 0) {
1784        xc_php_size = xc_php_hcache.size = 0;
1785    }
1786    if (xc_var_size <= 0) {
1787        xc_var_size = xc_var_hcache.size = 0;
1788    }
1789    /* xc_init_globals depends on all the aboves */
1790
1791    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
1792
1793    original_sigsegv_handler = signal(SIGSEGV, xcache_sigsegv_handler);
1794
1795    xc_init_constant(module_number TSRMLS_CC);
1796
1797    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
1798        if (!xc_init(module_number TSRMLS_CC)) {
1799            zend_error(E_ERROR, "XCache: Cannot init xcache");
1800            goto err_init;
1801        }
1802        xc_initized = 1;
1803    }
1804
1805#ifdef HAVE_XCACHE_COVERAGE
1806    xc_coverage_init(module_number TSRMLS_CC);
1807#endif
1808
1809    return SUCCESS;
1810
1811err_init:
1812    return FAILURE;
1813}
1814/* }}} */
1815/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
1816static PHP_MSHUTDOWN_FUNCTION(xcache)
1817{
1818    if (xc_initized) {
1819        xc_destroy();
1820        xc_initized = 0;
1821    }
1822    if (xc_mmap_path) {
1823        pefree(xc_mmap_path, 1);
1824        xc_mmap_path = NULL;
1825    }
1826
1827#ifdef HAVE_XCACHE_COVERAGE
1828    xc_coverage_destroy();
1829#endif
1830
1831    signal(SIGSEGV, original_sigsegv_handler);
1832    if (xc_coredump_dir) {
1833        pefree(xc_coredump_dir, 1);
1834        xc_coredump_dir = NULL;
1835    }
1836#ifndef ZTS
1837    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
1838#endif
1839
1840    UNREGISTER_INI_ENTRIES();
1841    return SUCCESS;
1842}
1843/* }}} */
1844/* {{{ PHP_RINIT_FUNCTION(xcache) */
1845static PHP_RINIT_FUNCTION(xcache)
1846{
1847    xc_request_init(TSRMLS_C);
1848    return SUCCESS;
1849}
1850/* }}} */
1851/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
1852#ifndef ZEND_ENGINE_2
1853static PHP_RSHUTDOWN_FUNCTION(xcache)
1854#else
1855static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
1856#endif
1857{
1858    xc_request_shutdown(TSRMLS_C);
1859    return SUCCESS;
1860}
1861/* }}} */
1862/* {{{ module definition structure */
1863
1864zend_module_entry xcache_module_entry = {
1865    STANDARD_MODULE_HEADER,
1866    "xcache",
1867    xcache_functions,
1868    PHP_MINIT(xcache),
1869    PHP_MSHUTDOWN(xcache),
1870    PHP_RINIT(xcache),
1871#ifndef ZEND_ENGINE_2
1872    PHP_RSHUTDOWN(xcache),
1873#else
1874    NULL,
1875#endif
1876    PHP_MINFO(xcache),
1877    XCACHE_VERSION,
1878#ifdef ZEND_ENGINE_2
1879    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
1880#else
1881    NULL,
1882    NULL,
1883#endif
1884    STANDARD_MODULE_PROPERTIES_EX
1885};
1886
1887#ifdef COMPILE_DL_XCACHE
1888ZEND_GET_MODULE(xcache)
1889#endif
1890/* }}} */
1891ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
1892{
1893    if (xc_zend_extension_gotup) {
1894        return FAILURE;
1895    }
1896    xc_zend_extension_gotup = 1;
1897    if (!xc_module_gotup) {
1898        return zend_startup_module(&xcache_module_entry);
1899    }
1900    return SUCCESS;
1901}
1902/* }}} */
1903ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
1904{
1905    /* empty */
1906}
1907/* }}} */
1908ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
1909{
1910#ifdef HAVE_XCACHE_COVERAGE
1911    xc_coverage_handle_ext_stmt(op_array, ZEND_EXT_STMT);
1912#endif
1913}
1914/* }}} */
1915ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
1916{
1917#if 0
1918    xc_coverage_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
1919#endif
1920}
1921/* }}} */
1922ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
1923{
1924#if 0
1925    xc_coverage_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
1926#endif
1927}
1928/* }}} */
1929/* {{{ zend extension definition structure */
1930ZEND_DLEXPORT zend_extension zend_extension_entry = {
1931    XCACHE_NAME,
1932    XCACHE_VERSION,
1933    XCACHE_AUTHOR,
1934    XCACHE_URL,
1935    XCACHE_COPYRIGHT,
1936    xcache_zend_startup,
1937    xcache_zend_shutdown,
1938    NULL,           /* activate_func_t */
1939    NULL,           /* deactivate_func_t */
1940    NULL,           /* message_handler_func_t */
1941    NULL,           /* op_array_handler_func_t */
1942    xcache_statement_handler,
1943    xcache_fcall_begin_handler,
1944    xcache_fcall_end_handler,
1945    NULL,           /* op_array_ctor_func_t */
1946    NULL,           /* op_array_dtor_func_t */
1947    STANDARD_ZEND_EXTENSION_PROPERTIES
1948};
1949
1950#ifndef ZEND_EXT_API
1951#   define ZEND_EXT_API ZEND_DLEXPORT
1952#endif
1953#if COMPILE_DL_XCACHE
1954ZEND_EXTENSION();
1955#endif
1956/* }}} */
Note: See TracBrowser for help on using the repository browser.