Experimental BinDump Patch for APC by Brian Shire Version 1 This patch provides experimental support for dumping and loading binary versions of the APC cache. It is provided for testing and feedback, you can use this patch freely provided that you provide the author with some sort of feedback etc ;-). Requires APC-3.0.15 Basic API: ---------- (all functions return FALSE on failure) mixed apc_bin_dump(optional array files, optional array user_vars) Returns a binary dump of the given files and user variables from the APC cache. A NULL for files or user_vars signals a dump of every entry, while array() will dump nothing. mixed apc_bin_dumpfile(array files, array user_vars, string filename, int flags, resource context) Output a binary dump of the given files and user variables from the APC cache to the named file. mixed apc_bin_load(string data, [int flags]) Load the given binary dump into the APC file/user cache. mixed apc_bin_loadfile(string filename, [resource context, [int flags]]) Load the given binary dump from the named file into the APC file/user cache. flags argument specifies a bitmask which can contain the constants APC_BIN_VERIFY_MD5 and/or APC_BIN_VERIFY_CRC32 for MD5 and CRC32 checks respectively. Please contact me with any problems or suggestions regarding this patch, thanks. diff --git a/apc_bin.c b/apc_bin.c new file mode 100644 index 0000000..adf377b --- /dev/null +++ b/apc_bin.c @@ -0,0 +1,941 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id$ */ + +/* Creates a binary architecture specific output to a string or file containing + * the current cache contents for both fies and user variables. This is accomplished + * via the apc_copy_* functions and "swizzling" pointer values to a position + * independent value, and unswizzling them on restoration. + */ + +#include "apc_globals.h" +#include "apc_bin.h" +#include "apc_zend.h" +#include "apc_sma.h" +#include "ext/standard/md5.h" +#include "ext/standard/crc32.h" + +extern apc_cache_t* apc_cache; +extern apc_cache_t* apc_user_cache; + + +#define APC_BINDUMP_DEBUG 0 + + +#if APC_BINDUMP_DEBUG + +#define SWIZZLE(bd, ptr) \ + do { \ + if((long)bd < (long)ptr && (long)ptr < ((long)bd + bd->size)) { \ + printf("SWIZZLE: %x ~> ", ptr); \ + ptr = (void*)((long)(ptr) - (long)(bd)); \ + printf("%x in %s on line %d", ptr, __FILE__, __LINE__); \ + } else if((long)ptr > bd->size) { /* not swizzled */ \ + apc_eprint("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d", (long)bd, ptr, ((long)bd + bd->size), __FILE__, __LINE__); \ + } \ + printf("\n"); \ + } while(0); + +#define UNSWIZZLE(bd, ptr) \ + do { \ + printf("UNSWIZZLE: %x -> ", ptr); \ + ptr = (void*)((long)(ptr) + (long)(bd)); \ + printf("%x in %s on line %d \n", ptr, __FILE__, __LINE__); \ + } while(0); + +#else /* !APC_BINDUMP_DEBUG */ + +#define SWIZZLE(bd, ptr) \ + do { \ + if((long)bd < (long)ptr && (long)ptr < ((long)bd + bd->size)) { \ + ptr = (void*)((long)(ptr) - (long)(bd)); \ + } else if((long)ptr > bd->size) { /* not swizzled */ \ + apc_eprint("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d", (long)bd, ptr, ((long)bd + bd->size), __FILE__, __LINE__); \ + } \ + } while(0); + +#define UNSWIZZLE(bd, ptr) \ + do { \ + ptr = (void*)((long)(ptr) + (long)(bd)); \ + } while(0); + +#endif + + +static void *apc_bd_alloc(size_t size); +static void apc_bd_free(void *ptr); +static void *apc_bd_alloc_ex(void *ptr_new, size_t size); + +typedef void (*apc_swizzle_cb_t)(apc_bd_t *bd, zend_llist *ll, void *ptr TSRMLS_DC); + +#if APC_BINDUMP_DEBUG +#define apc_swizzle_ptr(bd, ll, ptr) _apc_swizzle_ptr(bd, ll, (void*)ptr, __FILE__, __LINE__ TSRMLS_CC) +#else +#define apc_swizzle_ptr(bd, ll, ptr) _apc_swizzle_ptr(bd, ll, (void*)ptr, NULL, 0 TSRMLS_CC) +#endif + +static void _apc_swizzle_ptr(apc_bd_t *bd, zend_llist *ll, void **ptr, const char* file, int line TSRMLS_DC); +static void apc_swizzle_function(apc_bd_t *bd, zend_llist *ll, zend_function *func TSRMLS_DC); +static void apc_swizzle_class_entry(apc_bd_t *bd, zend_llist *ll, zend_class_entry *ce TSRMLS_DC); +static void apc_swizzle_hashtable(apc_bd_t *bd, zend_llist *ll, HashTable *ht, apc_swizzle_cb_t swizzle_cb, int is_ptr TSRMLS_DC); +static void apc_swizzle_zval(apc_bd_t *bd, zend_llist *ll, zval *zv TSRMLS_DC); +static void apc_swizzle_op_array(apc_bd_t *bd, zend_llist *ll, zend_op_array *op_array TSRMLS_DC); +static void apc_swizzle_property_info(apc_bd_t *bd, zend_llist *ll, zend_property_info *pi TSRMLS_DC); +static void apc_swizzle_function_entry(apc_bd_t *bd, zend_llist *ll, zend_function_entry *fe TSRMLS_DC); +static void apc_swizzle_arg_info_array(apc_bd_t *bd, zend_llist *ll, zend_arg_info* arg_info_array, uint num_args TSRMLS_DC); + +static apc_bd_t* apc_swizzle_bd(apc_bd_t* bd, zend_llist *ll TSRMLS_DC); +static int apc_unswizzle_bd(apc_bd_t *bd, int flags TSRMLS_DC); + + +/* {{{ apc_bd_alloc + * callback for copy_* functions */ +static void *apc_bd_alloc(size_t size) { + return apc_bd_alloc_ex(NULL, size); +} /* }}} */ + + +/* {{{ apc_bd_free + * callback for copy_* functions */ +static void apc_bd_free(void *ptr) { + size_t *size; + TSRMLS_FETCH(); + if(zend_hash_index_find(&APCG(apc_bd_alloc_list), (ulong)ptr, (void**)&size) == FAILURE) { + apc_eprint("apc_bd_free could not free pointer (not found in list: %x)", ptr); + } + APCG(apc_bd_alloc_ptr) -= *size; + zend_hash_index_del(&APCG(apc_bd_alloc_list), (ulong)ptr); +} /* }}} */ + + +/* {{{ apc_bd_alloc_ex + * set ranges or allocate a block of data from an already (e)malloc'd range. + * if ptr_new is not NULL, it will reset the pointer to start at ptr_new, + * with a range of size. If ptr_new is NULL, returns the next available + * block of given size. + */ +static void *apc_bd_alloc_ex(void *ptr_new, size_t size) { + void *rval; + TSRMLS_FETCH(); + + rval = APCG(apc_bd_alloc_ptr); + if(ptr_new != NULL) { /* reset ptrs */ + APCG(apc_bd_alloc_ptr) = ptr_new; + APCG(apc_bd_alloc_ubptr) = (void*)(ptr_new + size); + } else { /* alloc block */ + APCG(apc_bd_alloc_ptr) += size; +#if APC_BINDUMP_DEBUG + apc_nprint("apc_bd_alloc: rval: 0x%x ptr: 0x%x ubptr: 0x%x size: %d", rval, APCG(apc_bd_alloc_ptr), APCG(apc_bd_alloc_ubptr), size); +#endif + if(APCG(apc_bd_alloc_ptr) > APCG(apc_bd_alloc_ubptr)) { + apc_eprint("Exceeded bounds check in apc_bd_alloc_ex by %d bytes.", APCG(apc_bd_alloc_ptr) - APCG(apc_bd_alloc_ubptr)); + } + zend_hash_index_update(&APCG(apc_bd_alloc_list), (ulong)rval, &size, sizeof(size_t), NULL); + } + + return rval; +} /* }}} */ + + +/* {{{ _apc_swizzle_ptr */ +static void _apc_swizzle_ptr(apc_bd_t *bd, zend_llist *ll, void **ptr, const char* file, int line TSRMLS_DC) { + if(*ptr) { + if((long)bd < (long)*ptr && (long)*ptr < ((long)bd + bd->size)) { + zend_llist_add_element(ll, &ptr); +#if APC_BINDUMP_DEBUG + printf("[%06d] apc_swizzle_ptr: %x -> %x ", zend_llist_count(ll), ptr, *ptr); + printf(" in %s on line %d \n", file, line); +#endif + } else if((long)ptr > bd->size) { + apc_eprint("pointer to be swizzled is not within allowed memory range! (%x < %x < %x) in %s on %d", (long)bd, *ptr, ((long)bd + bd->size), file, line); \ + } + } +} /* }}} */ + + +/* {{{ apc_swizzle_op_array */ +static void apc_swizzle_op_array(apc_bd_t *bd, zend_llist *ll, zend_op_array *op_array TSRMLS_DC) { + zval *ztmp; + void *tmp; + int i; + +#ifdef ZEND_ENGINE_2 + apc_swizzle_arg_info_array(bd, ll, op_array->arg_info, op_array->num_args TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &op_array->arg_info); +#else + if (op_array->arg_types) { + apc_swizzle_ptr(bd, ll, &op_array->arg_types); + } +#endif + + apc_swizzle_ptr(bd, ll, &op_array->function_name); + apc_swizzle_ptr(bd, ll, &op_array->filename); + apc_swizzle_ptr(bd, ll, &op_array->refcount); + + /* swizzle op_array */ + for(i=0; i < op_array->last; i++) { + if(op_array->opcodes[i].result.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].result.u.constant TSRMLS_CC); + } + if(op_array->opcodes[i].op1.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].op1.u.constant TSRMLS_CC); + } + if(op_array->opcodes[i].op2.op_type == IS_CONST) { + apc_swizzle_zval(bd, ll, &op_array->opcodes[i].op2.u.constant TSRMLS_CC); + } + } + apc_swizzle_ptr(bd, ll, &op_array->opcodes); + + /* break-continue array ptr */ + if(op_array->brk_cont_array) { + apc_swizzle_ptr(bd, ll, &op_array->brk_cont_array); + } + + /* static voriables */ + if(op_array->static_variables) { + apc_swizzle_hashtable(bd, ll, op_array->static_variables, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &op_array->static_variables); + } + +#ifdef ZEND_ENGINE_2 + /* try-catch */ + if(op_array->try_catch_array) { + apc_swizzle_ptr(bd, ll, &op_array->try_catch_array); + } +#endif + +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */ + /* vars */ + if(op_array->vars) { + for(i=0; i < op_array->last_var; i++) { + apc_swizzle_ptr(bd, ll, &op_array->vars[i].name); + } + apc_swizzle_ptr(bd, ll, &op_array->vars); + } +#endif + +#ifdef ZEND_ENGINE_2 + /* doc comment */ + if(op_array->doc_comment) { + apc_swizzle_ptr(bd, ll, &op_array->doc_comment); + } +#endif + +} /* }}} */ + + +/* {{{ apc_swizzle_function */ +static void apc_swizzle_function(apc_bd_t *bd, zend_llist *ll, zend_function *func TSRMLS_DC) { + zend_op_array *ftmp; + apc_swizzle_op_array(bd, ll, &func->op_array TSRMLS_CC); +#ifdef ZEND_ENGINE_2 + if(func->common.scope) { + apc_swizzle_ptr(bd, ll, &func->common.scope); + } +#endif +} /* }}} */ + + +/* {{{ apc_swizzle_class_entry */ +static void apc_swizzle_class_entry(apc_bd_t *bd, zend_llist *ll, zend_class_entry *ce TSRMLS_DC) { + + zend_function_entry *fe_tmp; + uint i; + + if(ce->name) { + apc_swizzle_ptr(bd, ll, &ce->name); + } + + if(ce->doc_comment) { + apc_swizzle_ptr(bd, ll, &ce->doc_comment); + } + +#ifndef ZEND_ENGINE_2 + apc_swizzle_ptr(bd, ll, &ce->refcount); +#endif + + apc_swizzle_hashtable(bd, ll, &ce->function_table, (apc_swizzle_cb_t)apc_swizzle_function, 0 TSRMLS_CC); + apc_swizzle_hashtable(bd, ll, &ce->default_properties, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + +#ifdef ZEND_ENGINE_2 + apc_swizzle_hashtable(bd, ll, &ce->properties_info, (apc_swizzle_cb_t)apc_swizzle_property_info, 0 TSRMLS_CC); +#endif + + apc_swizzle_hashtable(bd, ll, &ce->default_static_members, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + + if(ce->static_members != &ce->default_static_members) { + apc_swizzle_hashtable(bd, ll, ce->static_members, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + } else { + apc_swizzle_ptr(bd, ll, &ce->static_members); + } + + apc_swizzle_hashtable(bd, ll, &ce->constants_table, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + + if(ce->builtin_functions) { + for(i=0; ce->builtin_functions[i].fname; i++) { + apc_swizzle_function_entry(bd, ll, &ce->builtin_functions[i] TSRMLS_CC); + } + } + + apc_swizzle_ptr(bd, ll, &ce->constructor); + apc_swizzle_ptr(bd, ll, &ce->destructor); + apc_swizzle_ptr(bd, ll, &ce->clone); + apc_swizzle_ptr(bd, ll, &ce->__get); + apc_swizzle_ptr(bd, ll, &ce->__set); + apc_swizzle_ptr(bd, ll, &ce->__unset); + apc_swizzle_ptr(bd, ll, &ce->__isset); + apc_swizzle_ptr(bd, ll, &ce->__call); + apc_swizzle_ptr(bd, ll, &ce->serialize_func); + apc_swizzle_ptr(bd, ll, &ce->unserialize_func); + +#ifdef ZEND_ENGINE_2_2 + apc_swizzle_ptr(bd, ll, &ce->__tostring); +#endif + + apc_swizzle_ptr(bd, ll, &ce->filename); +} /* }}} */ + + +/* {{{ apc_swizzle_property_info */ +static void apc_swizzle_property_info(apc_bd_t *bd, zend_llist *ll, zend_property_info *pi TSRMLS_DC) { + apc_swizzle_ptr(bd, ll, &pi->name); + apc_swizzle_ptr(bd, ll, &pi->doc_comment); + +#ifdef ZEND_ENGINE_2_2 + apc_swizzle_ptr(bd, ll, &pi->ce); +#endif +} /* }}} */ + + +/* {{{ apc_swizzle_function_entry */ +static void apc_swizzle_function_entry(apc_bd_t *bd, zend_llist *ll, zend_function_entry *fe TSRMLS_DC) { + apc_swizzle_ptr(bd, ll, &fe->fname); + apc_swizzle_arg_info_array(bd, ll, fe->arg_info, fe->num_args TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &fe->arg_info); +} /* }}} */ + + +/* {{{ apc_swizzle_arg_info_array */ +static void apc_swizzle_arg_info_array(apc_bd_t *bd, zend_llist *ll, zend_arg_info* arg_info_array, uint num_args TSRMLS_DC) { + + int i; + + if(arg_info_array) { + for(i=0; i < num_args; i++) { + apc_swizzle_ptr(bd, ll, &arg_info_array[i].name); + apc_swizzle_ptr(bd, ll, &arg_info_array[i].class_name); + } + } + +} /* }}} */ + + +/* {{{ apc_swizzle_hashtable */ +static void apc_swizzle_hashtable(apc_bd_t *bd, zend_llist *ll, HashTable *ht, apc_swizzle_cb_t swizzle_cb, int is_ptr TSRMLS_DC) { + int i; + Bucket **bp, **bp_prev; + + bp = &ht->pListHead; + while(*bp) { + bp_prev = bp; + bp = &(*bp)->pListNext; + if(is_ptr) { + swizzle_cb(bd, ll, *(void**)(*bp_prev)->pData TSRMLS_CC); + apc_swizzle_ptr(bd, ll, (*bp_prev)->pData); + } else { + swizzle_cb(bd, ll, (void**)(*bp_prev)->pData TSRMLS_CC); + } + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pData); + if((*bp_prev)->pDataPtr) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pDataPtr); + } + if((*bp_prev)->pListLast) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pListLast); + } + if((*bp_prev)->pNext) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pNext); + } + if((*bp_prev)->pLast) { + apc_swizzle_ptr(bd, ll, &(*bp_prev)->pLast); + } + apc_swizzle_ptr(bd, ll, bp_prev); + } + for(i=0; i < ht->nTableSize; i++) { + if(ht->arBuckets[i]) { + apc_swizzle_ptr(bd, ll, &ht->arBuckets[i]); + } + } + apc_swizzle_ptr(bd, ll, &ht->pListTail); + + apc_swizzle_ptr(bd, ll, &ht->arBuckets); +} /* }}} */ + + +/* {{{ apc_swizzle_zval */ +static void apc_swizzle_zval(apc_bd_t *bd, zend_llist *ll, zval *zv TSRMLS_DC) { + + zval *tmp; + + if(APCG(copied_zvals)) { + if(zend_hash_index_exists(APCG(copied_zvals), (ulong)zv)) { + return; + } + zend_hash_index_update(APCG(copied_zvals), (ulong)zv, (void**)&zv, sizeof(zval*), NULL); + } + + switch(zv->type & ~IS_CONSTANT_INDEX) { + case IS_NULL: + case IS_LONG: + case IS_DOUBLE: + case IS_BOOL: + case IS_RESOURCE: + /* nothing to do */ + break; + case IS_CONSTANT: + case IS_STRING: + apc_swizzle_ptr(bd, ll, &zv->value.str.val); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + apc_swizzle_hashtable(bd, ll, zv->value.ht, (apc_swizzle_cb_t)apc_swizzle_zval, 1 TSRMLS_CC); + apc_swizzle_ptr(bd, ll, &zv->value.ht); + break; + case IS_OBJECT: + break; + default: + assert(0); /* shouldn't happen */ + } +} /* }}} */ + + +/* {{{ apc_swizzle_bd */ +static apc_bd_t* apc_swizzle_bd(apc_bd_t* bd, zend_llist *ll TSRMLS_DC) { + int count, i; + PHP_MD5_CTX context; + unsigned char digest[16]; + register php_uint32 crc; + php_uint32 crcinit = 0; + char *crc_p; + void ***ptr; + void ***ptr_list; + + count = zend_llist_count(ll); + ptr_list = emalloc(count * sizeof(void**)); + ptr = zend_llist_get_first(ll); + for(i=0; i < count; i++) { +#if APC_BINDUMP_DEBUG + printf("[%06d] ", i+1); +#endif + SWIZZLE(bd, **ptr); /* swizzle ptr */ + if((long)bd < (long)*ptr && (long)*ptr < ((long)bd + bd->size)) { /* exclude ptrs that aren't actually included in the ptr list */ +#if APC_BINDUMP_DEBUG + printf("[------] "); +#endif + SWIZZLE(bd, *ptr); /* swizzle ptr list */ + ptr_list[i] = *ptr; + } + ptr = zend_llist_get_next(ll); + } + SWIZZLE(bd, bd->entries); + + if(count > 0) { + bd = erealloc(bd, bd->size + (count * sizeof(void**))); + bd->num_swizzled_ptrs = count; + bd->swizzled_ptrs = (void***)((void*)bd + bd->size -2); /* extra -1 for null termination */ + bd->size += count * sizeof(void**); + memcpy(bd->swizzled_ptrs, ptr_list, count * sizeof(void**)); + SWIZZLE(bd, bd->swizzled_ptrs); + } else { + bd->num_swizzled_ptrs = 0; + bd->swizzled_ptrs = NULL; + } + ((char*)bd)[bd->size-1] = 0; /* silence null termination for zval strings */ + efree(ptr_list); + bd->swizzled = 1; + + /* Generate MD5/CRC32 checksum */ + for(i=0; i<16; i++) { bd->md5[i] = 0; } + bd->crc=0; + PHP_MD5Init(&context); + PHP_MD5Update(&context, (const unsigned char*)bd, bd->size); + PHP_MD5Final(digest, &context); + crc = crcinit^0xFFFFFFFF; + crc_p = (char*)bd; + for(i=bd->size; i--; ++crc_p) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*crc_p)) & 0xFF ]; + } + memcpy(bd->md5, digest, 16); + bd->crc = crc; + + return bd; +} /* }}} */ + + +/* {{{ apc_unswizzle_bd */ +static int apc_unswizzle_bd(apc_bd_t *bd, int flags TSRMLS_DC) { + int i; + unsigned char md5_orig[16]; + unsigned char digest[16]; + PHP_MD5_CTX context; + register php_uint32 crc; + php_uint32 crcinit = 0; + php_uint32 crc_orig; + char *crc_p; + + /* Verify the md5 or crc32 before we unswizzle */ + memcpy(md5_orig, bd->md5, 16); + for(i=0; i<16; i++) { bd->md5[i] = 0; } + crc_orig = bd->crc; + bd->crc=0; + if(flags & APC_BIN_VERIFY_MD5) { + PHP_MD5Init(&context); + PHP_MD5Update(&context, (const unsigned char*)bd, bd->size); + PHP_MD5Final(digest, &context); + if(memcmp(md5_orig, digest, 16)) { + apc_eprint("MD5 checksum of binary dump failed."); + memcpy(bd->md5, md5_orig, 16); /* add back md5 checksum */ + return -1; + } + } + if(flags & APC_BIN_VERIFY_CRC32) { + crc = crcinit^0xFFFFFFFF; + crc_p = (char*)bd; + for(i=bd->size; i--; ++crc_p) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*crc_p)) & 0xFF ]; + } + if(crc_orig != crc) { + apc_eprint("CRC32 checksum of binary dump failed."); + bd->crc = crc_orig; + return -1; + } + } + memcpy(bd->md5, md5_orig, 16); /* add back md5 checksum */ + bd->crc = crc_orig; + + UNSWIZZLE(bd, bd->entries); + UNSWIZZLE(bd, bd->swizzled_ptrs); + for(i=0; i < bd->num_swizzled_ptrs; i++) { + if(bd->swizzled_ptrs[i]) { + UNSWIZZLE(bd, bd->swizzled_ptrs[i]); + if(*bd->swizzled_ptrs[i] && (*bd->swizzled_ptrs[i] < (void*)bd)) { + UNSWIZZLE(bd, *bd->swizzled_ptrs[i]); + } + } + } + + bd->swizzled=0; + + return 0; +} /* }}} */ + + +/* {{{ apc_bin_checkfilter */ +static int apc_bin_checkfilter(HashTable *filter, const char *key, uint key_len) { + zval **zptr; + + if(filter == NULL) { + return 1; + } + + if(zend_hash_find(filter, (char*)key, key_len, (void**)&zptr) == SUCCESS) { + if(Z_TYPE_PP(zptr) == IS_LONG && Z_LVAL_PP(zptr) == 0) { + return 0; + } + } else { + return 0; + } + + + return 1; +} /* }}} */ + +/* {{{ apc_flip_hash() */ +static HashTable* apc_flip_hash(HashTable *hash) { + zval **entry, *data; + HashTable *new_hash; + HashPosition pos; + char *string_key; + uint str_key_len; + ulong num_key; + + if(hash == NULL) return hash; + + MAKE_STD_ZVAL(data); + ZVAL_LONG(data, 1); + new_hash = emalloc(sizeof(HashTable)); + zend_hash_init(new_hash, hash->nTableSize, NULL, ZVAL_PTR_DTOR, 0); + + zend_hash_internal_pointer_reset_ex(hash, &pos); + while (zend_hash_get_current_data_ex(hash, (void **)&entry, &pos) == SUCCESS) { + if(Z_TYPE_PP(entry) == IS_STRING) { + zend_hash_update(new_hash, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) +1, &data, sizeof(data), NULL); + } + zend_hash_move_forward_ex(hash, &pos); + } + efree(data); + + return new_hash; +} +/* }}} */ + +/* {{{ apc_bin_dump */ +apc_bd_t* apc_bin_dump(HashTable *files, HashTable *user_vars TSRMLS_DC) { + + int i, fcount; + slot_t *sp; + apc_bd_entry_t *ep; + int count=0; + apc_bd_t *bd; + zend_llist ll; + zend_function *efp, *sfp; + zend_op_array *tmp_op_array; + int size=0; + HashTable *orig_copied_zvals; + + zend_llist_init(&ll, sizeof(void*), NULL, 0); + zend_hash_init(&APCG(apc_bd_alloc_list), 0, NULL, NULL, 0); + + /* flip the hash for faster filter checking */ + files = apc_flip_hash(files); + user_vars = apc_flip_hash(user_vars); + + /* get size and entry counts */ + for(i=0; i < apc_user_cache->num_slots; i++) { + sp = apc_user_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(apc_bin_checkfilter(user_vars, sp->key.data.user.identifier, sp->key.data.user.identifier_len)) { + size += sizeof(apc_bd_entry_t*) + sizeof(apc_bd_entry_t); + size += sp->value->mem_size - (sizeof(apc_cache_entry_t) - sizeof(apc_cache_entry_value_t)); + count++; + } + } + } + for(i=0; i < apc_cache->num_slots; i++) { + sp = apc_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(sp->key.type == APC_CACHE_KEY_FPFILE) { + if(apc_bin_checkfilter(files, sp->key.data.fpfile.fullpath, sp->key.data.fpfile.fullpath_len+1)) { + size += sizeof(apc_bd_entry_t*) + sizeof(apc_bd_entry_t); + size += sp->value->mem_size - (sizeof(apc_cache_entry_t) - sizeof(apc_cache_entry_value_t)); + count++; + } + } else { + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load */ + apc_wprint("Excluding some files from apc_bin_dump[file]. Cached files must be included using full path with apc.stat=0."); + } + } + } + + size += sizeof(apc_bd_t) +1; /* +1 for null termination */ + bd = emalloc(size); + bd->size = size; + apc_bd_alloc_ex((void*)((long)bd + sizeof(apc_bd_t)), bd->size); + bd->num_entries = count; + bd->entries = apc_bd_alloc_ex(NULL, sizeof(apc_bd_entry_t) * count); + + /* User entries */ + orig_copied_zvals = APCG(copied_zvals); + APCG(copied_zvals) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); + count = 0; + for(i=0; i < apc_user_cache->num_slots; i++) { + sp = apc_user_cache->slots[i]; + for(; sp != NULL; sp = sp->next) { + if(apc_bin_checkfilter(user_vars, sp->key.data.user.identifier, sp->key.data.user.identifier_len)) { + ep = &bd->entries[count]; + ep->type = sp->value->type; + ep->val.user.info = apc_bd_alloc(sp->value->data.user.info_len); + memcpy(ep->val.user.info, sp->value->data.user.info, sp->value->data.user.info_len); + ep->val.user.info_len = sp->value->data.user.info_len; + ep->val.user.val = apc_copy_zval(NULL, sp->value->data.user.val, apc_bd_alloc, apc_bd_free); + zend_hash_clean(APCG(copied_zvals)); + ep->val.user.ttl = sp->value->data.user.ttl; + + /* swizzle pointers */ + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.user.info); + zend_hash_clean(APCG(copied_zvals)); + apc_swizzle_zval(bd, &ll, bd->entries[count].val.user.val TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.user.val); + + count++; + } + } + } + zend_hash_destroy(APCG(copied_zvals)); + efree(APCG(copied_zvals)); + APCG(copied_zvals) = orig_copied_zvals; + + /* File entries */ + for(i=0; i < apc_cache->num_slots; i++) { + for(sp=apc_cache->slots[i]; sp != NULL; sp = sp->next) { + if(sp->key.type == APC_CACHE_KEY_FPFILE) { + if(apc_bin_checkfilter(files, sp->key.data.fpfile.fullpath, sp->key.data.fpfile.fullpath_len+1)) { + ep = &bd->entries[count]; + ep->type = sp->key.type; + ep->val.file.filename = apc_bd_alloc(strlen(sp->value->data.file.filename)+1); + strcpy(ep->val.file.filename, sp->value->data.file.filename); + ep->val.file.op_array = apc_copy_op_array(NULL, sp->value->data.file.op_array, apc_bd_alloc, apc_bd_free TSRMLS_CC); + + for(ep->num_functions=0; sp->value->data.file.functions[ep->num_functions].function != NULL;) { ep->num_functions++; } + ep->val.file.functions = apc_bd_alloc(sizeof(apc_function_t) * ep->num_functions); + for(fcount=0; fcount < ep->num_functions; fcount++) { + memcpy(&ep->val.file.functions[fcount], &sp->value->data.file.functions[fcount], sizeof(apc_function_t)); + ep->val.file.functions[fcount].name = apc_xstrdup(sp->value->data.file.functions[fcount].name, apc_bd_alloc); + ep->val.file.functions[fcount].name_len = sp->value->data.file.functions[fcount].name_len; + ep->val.file.functions[fcount].function = apc_bd_alloc(sizeof(zend_function)); + efp = ep->val.file.functions[fcount].function; + sfp = sp->value->data.file.functions[fcount].function; + switch(sfp->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + efp->op_array = sfp->op_array; + break; + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + apc_copy_op_array(&efp->op_array, &sfp->op_array, apc_bd_alloc, apc_bd_free TSRMLS_CC); + break; + default: + assert(0); + } +#ifdef ZEND_ENGINE_2 + efp->common.prototype = NULL; + efp->common.fn_flags = sfp->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); +#endif + apc_swizzle_ptr(bd, &ll, &ep->val.file.functions[fcount].name); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.functions[fcount].function); + apc_swizzle_op_array(bd, &ll, &efp->op_array TSRMLS_CC); + } + + + for(ep->num_classes=0; sp->value->data.file.classes[ep->num_classes].class_entry != NULL;) { ep->num_classes++; } + ep->val.file.classes = apc_bd_alloc(sizeof(apc_class_t) * ep->num_classes); + for(fcount=0; fcount < ep->num_classes; fcount++) { + ep->val.file.classes[fcount].name = apc_xmemcpy(sp->value->data.file.classes[fcount].name, sp->value->data.file.classes[fcount].name_len+1, apc_bd_alloc); + ep->val.file.classes[fcount].name_len = sp->value->data.file.classes[fcount].name_len; + ep->val.file.classes[fcount].class_entry = apc_copy_class_entry(NULL, sp->value->data.file.classes[fcount].class_entry, apc_bd_alloc, apc_bd_free); + ep->val.file.classes[fcount].parent_name = apc_xstrdup(sp->value->data.file.classes[fcount].parent_name, apc_bd_alloc); + ep->val.file.classes[fcount].is_derived = sp->value->data.file.classes[fcount].is_derived; + + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].name); + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].parent_name); + apc_swizzle_class_entry(bd, &ll, ep->val.file.classes[fcount].class_entry TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &ep->val.file.classes[fcount].class_entry); + } + + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.file.filename); + apc_swizzle_op_array(bd, &ll, bd->entries[count].val.file.op_array TSRMLS_CC); + apc_swizzle_ptr(bd, &ll, &bd->entries[count].val.file.op_array); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.functions); + apc_swizzle_ptr(bd, &ll, (void**)&ep->val.file.classes); + + count++; + } else { + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load */ + } + } + } + } + + /* append swizzle pointer list to bd */ + bd = apc_swizzle_bd(bd, &ll TSRMLS_CC); + zend_llist_destroy(&ll); + zend_hash_destroy(&APCG(apc_bd_alloc_list)); + + if(files) { + zend_hash_destroy(files); + efree(files); + } + if(user_vars) { + zend_hash_destroy(user_vars); + efree(user_vars); + } + + return bd; +} /* }}} */ + + +/* {{{ apc_bin_load */ +int apc_bin_load(apc_bd_t *bd, int flags TSRMLS_DC) { + + apc_bd_entry_t *ep; + int i, i2, i3, ret; + size_t mem_size; + time_t t; + zend_op_array *alloc_op_array = NULL; + apc_function_t *alloc_functions = NULL; + apc_class_t *alloc_classes = NULL; + apc_cache_entry_t *cache_entry; + apc_cache_key_t cache_key; + + if (bd->swizzled) { + if(apc_unswizzle_bd(bd, flags TSRMLS_CC) < 0) { + return -1; + } + } + +#if PHP_API_VERSION <= 20041225 +#if HAVE_APACHE && defined(APC_PHP4_STAT) + t = ((request_rec *)SG(server_context))->request_time; +#else + t = time(0); +#endif +#else + t = sapi_get_request_time(TSRMLS_C); +#endif + + for(i = 0; i < bd->num_entries; i++) { + ep = &bd->entries[i]; + switch (ep->type) { + case APC_CACHE_KEY_FILE: + /* TODO: Currently we don't support APC_CACHE_KEY_FILE type. We need to store the path and re-stat on load (or something else perhaps?) */ + break; + case APC_CACHE_KEY_FPFILE: + + HANDLE_BLOCK_INTERRUPTIONS(); +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + if(!apc_cache_write_lock(apc_cache)) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + return -1; + } + } +#endif + mem_size = 0; + APCG(mem_size_ptr) = &mem_size; + if(! (alloc_op_array = apc_copy_op_array(NULL, ep->val.file.op_array, apc_sma_malloc, apc_sma_free TSRMLS_CC))) { + goto sma_cleanup; + } + + if(! (alloc_functions = apc_sma_malloc(sizeof(apc_function_t) * (ep->num_functions + 1)))) { + goto sma_cleanup; + } + for(i2=0; i2 < ep->num_functions; i2++) { + if(! (alloc_functions[i2].name = apc_xstrdup(ep->val.file.functions[i2].name, apc_sma_malloc))) { + goto sma_cleanup; + } + alloc_functions[i2].name_len = ep->val.file.functions[i2].name_len; + if(! (alloc_functions[i2].function = apc_sma_malloc(sizeof(zend_function)))) { + goto sma_cleanup; + } + switch(ep->val.file.functions[i2].function->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + alloc_functions[i2].function->op_array = ep->val.file.functions[i2].function->op_array; + break; + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + if (!apc_copy_op_array(&alloc_functions[i2].function->op_array, &ep->val.file.functions[i2].function->op_array, apc_sma_malloc, apc_sma_free TSRMLS_CC)) { + goto sma_cleanup; + } + break; + default: + assert(0); + } +#ifdef ZEND_ENGINE_2 + alloc_functions[i2].function->common.prototype=NULL; + alloc_functions[i2].function->common.fn_flags=ep->val.file.functions[i2].function->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); +#endif + } + alloc_functions[i2].name = NULL; + alloc_functions[i2].function = NULL; + + if(! (alloc_classes = apc_sma_malloc(sizeof(apc_class_t) * (ep->num_classes + 1)))) { + goto sma_cleanup; + } + for(i2=0; i2 < ep->num_classes; i2++) { + if(! (alloc_classes[i2].name = apc_xmemcpy(ep->val.file.classes[i2].name, ep->val.file.classes[i2].name_len+1, apc_sma_malloc))) { + goto sma_cleanup; + } + alloc_classes[i2].name_len = ep->val.file.classes[i2].name_len; + if(! (alloc_classes[i2].class_entry = apc_copy_class_entry(NULL, ep->val.file.classes[i2].class_entry, apc_sma_malloc, apc_sma_free))) { + goto sma_cleanup; + } + if(! (alloc_classes[i2].parent_name = apc_xstrdup(ep->val.file.classes[i2].parent_name, apc_sma_malloc))) { + if(ep->val.file.classes[i2].parent_name != NULL) { + goto sma_cleanup; + } + } + alloc_classes[i2].is_derived = ep->val.file.classes[i2].is_derived; + } + alloc_classes[i2].name = NULL; + alloc_classes[i2].class_entry = NULL; + + if(!(cache_entry = apc_cache_make_file_entry(ep->val.file.filename, alloc_op_array, alloc_functions, alloc_classes))) { + goto sma_cleanup; + } + APCG(mem_size_ptr) = NULL; + cache_entry->mem_size = mem_size; + + if (!apc_cache_make_file_key(&cache_key, ep->val.file.filename, PG(include_path), t TSRMLS_CC)) { + goto sma_cleanup; + } + + if ((ret = apc_cache_insert(apc_cache, cache_key, cache_entry, t)) != 1) { + apc_cache_free_entry(cache_entry); + if(ret==-1) { + goto failure; + } + } + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + + break; + case APC_CACHE_KEY_USER: + _apc_store(ep->val.user.info, ep->val.user.info_len-1, ep->val.user.val, ep->val.user.ttl, 0 TSRMLS_CC); /* We do a info_len-1 here because _store increments it by one */ + break; + default: + break; + } + } + + return 0; + +sma_cleanup: + apc_free_op_array(alloc_op_array, apc_sma_free); + apc_free_functions(alloc_functions, apc_sma_free); + apc_free_classes(alloc_classes, apc_sma_free); + apc_cache_expunge(apc_cache, t); + apc_cache_expunge(apc_user_cache, t); + APCG(mem_size_ptr) = NULL; +failure: + apc_wprint("Unable to allocate memory for apc binary load/dump functionality."); +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return -1; +} /* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_bin.h b/apc_bin.h new file mode 100644 index 0000000..2c2bd47 --- /dev/null +++ b/apc_bin.h @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id$ */ + +#ifndef APC_BINDUMP_H +#define APC_BINDUMP_H + +#include "apc.h" +#include "apc_php.h" +#include "php/ext/standard/basic_functions.h" + +/* APC binload flags */ +#define APC_BIN_VERIFY_MD5 1 << 0 +#define APC_BIN_VERIFY_CRC32 1 << 1 + +typedef struct _apc_bd_entry_t { + unsigned char type; + uint num_functions; + uint num_classes; + apc_cache_entry_value_t val; +} apc_bd_entry_t; + +typedef struct _apc_bd_t { + unsigned int size; + int swizzled; + unsigned char md5[16]; + php_uint32 crc; + unsigned int num_entries; + apc_bd_entry_t *entries; + int num_swizzled_ptrs; + void ***swizzled_ptrs; +} apc_bd_t; + +apc_bd_t* apc_bin_dump(HashTable *files, HashTable *user_vars TSRMLS_DC); +int apc_bin_load(apc_bd_t *bd, int flags TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_cache.c b/apc_cache.c index 7595abf..8a0cfcc 100644 --- a/apc_cache.c +++ b/apc_cache.c @@ -31,7 +31,6 @@ /* $Id: apc_cache.c,v 3.145 2007/10/05 23:06:56 gopalv Exp $ */ #include "apc_cache.h" -#include "apc_lock.h" #include "apc_sma.h" #include "apc_globals.h" #include "SAPI.h" @@ -50,70 +49,6 @@ #define UNLOCK(c) { apc_lck_unlock(c->header->lock); HANDLE_UNBLOCK_INTERRUPTIONS(); } /* }}} */ -/* {{{ struct definition: slot_t */ -typedef struct slot_t slot_t; -struct slot_t { - apc_cache_key_t key; /* slot key */ - apc_cache_entry_t* value; /* slot value */ - slot_t* next; /* next slot in linked list */ - int num_hits; /* number of hits to this bucket */ - time_t creation_time; /* time slot was initialized */ - time_t deletion_time; /* time slot was removed from cache */ - time_t access_time; /* time slot was last accessed */ -}; -/* }}} */ - -/* {{{ struct definition: header_t - Any values that must be shared among processes should go in here. */ -typedef struct header_t header_t; -struct header_t { - apc_lck_t lock; /* read/write lock (exclusive blocking cache lock) */ - apc_lck_t wrlock; /* write lock (non-blocking used to prevent cache slams) */ - int num_hits; /* total successful hits in cache */ - int num_misses; /* total unsuccessful hits in cache */ - int num_inserts; /* total successful inserts in cache */ - slot_t* deleted_list; /* linked list of to-be-deleted slots */ - time_t start_time; /* time the above counters were reset */ - int expunges; /* total number of expunges */ - zend_bool busy; /* Flag to tell clients when we are busy cleaning the cache */ - int num_entries; /* Statistic on the number of entries */ - size_t mem_size; /* Statistic on the memory size used by this cache */ -}; -/* }}} */ - -/* {{{ struct definition: apc_cache_t */ -struct apc_cache_t { - void* shmaddr; /* process (local) address of shared cache */ - header_t* header; /* cache header (stored in SHM) */ - slot_t** slots; /* array of cache slots (stored in SHM) */ - int num_slots; /* number of slots in cache */ - int gc_ttl; /* maximum time on GC list for a slot */ - int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ -}; -/* }}} */ - -/* {{{ struct definition local_slot_t */ -typedef struct local_slot_t local_slot_t; -struct local_slot_t { - slot_t *original; /* the original slot in shm */ - int num_hits; /* number of hits */ - time_t creation_time; /* creation time */ - apc_cache_entry_t *value; /* shallow copy of slot->value */ - local_slot_t *next; /* only for dead list */ -}; -/* }}} */ -/* {{{ struct definition apc_local_cache_t */ -struct apc_local_cache_t { - apc_cache_t* shmcache; /* the real cache in shm */ - local_slot_t* slots; /* process (local) cache of objects */ - local_slot_t* dead_list; /* list of objects pending removal */ - int num_slots; /* number of slots in cache */ - int ttl; /* time to live */ - int num_hits; /* number of hits */ - int generation; /* every generation lives between expunges */ -}; -/* }}} */ - /* {{{ key_equals */ #define key_equals(a, b) (a.inode==b.inode && a.device==b.device) /* }}} */ @@ -304,7 +239,7 @@ apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) num_slots = size_hint > 0 ? size_hint*2 : 2000; cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t)); - cache_size = sizeof(header_t) + num_slots*sizeof(slot_t*); + cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*); cache->shmaddr = apc_sma_malloc(cache_size); if(!cache->shmaddr) { @@ -312,7 +247,7 @@ apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) } memset(cache->shmaddr, 0, cache_size); - cache->header = (header_t*) cache->shmaddr; + cache->header = (cache_header_t*) cache->shmaddr; cache->header->num_hits = 0; cache->header->num_misses = 0; cache->header->deleted_list = NULL; @@ -320,7 +255,7 @@ apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) cache->header->expunges = 0; cache->header->busy = 0; - cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(header_t)); + cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t)); cache->num_slots = num_slots; cache->gc_ttl = gc_ttl; cache->ttl = ttl; diff --git a/apc_cache.h b/apc_cache.h index a8bdfb8..0339f7f 100644 --- a/apc_cache.h +++ b/apc_cache.h @@ -38,6 +38,7 @@ #include "apc.h" #include "apc_compile.h" +#include "apc_lock.h" #define APC_CACHE_ENTRY_FILE 1 #define APC_CACHE_ENTRY_USER 2 @@ -278,6 +279,70 @@ struct apc_cache_info_t { }; /* }}} */ +/* {{{ struct definition: slot_t */ +typedef struct slot_t slot_t; +struct slot_t { + apc_cache_key_t key; /* slot key */ + apc_cache_entry_t* value; /* slot value */ + slot_t* next; /* next slot in linked list */ + int num_hits; /* number of hits to this bucket */ + time_t creation_time; /* time slot was initialized */ + time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ +}; +/* }}} */ + +/* {{{ struct definition: cache_header_t + Any values that must be shared among processes should go in here. */ +typedef struct cache_header_t cache_header_t; +struct cache_header_t { + apc_lck_t lock; /* read/write lock (exclusive blocking cache lock) */ + apc_lck_t wrlock; /* write lock (non-blocking used to prevent cache slams) */ + int num_hits; /* total successful hits in cache */ + int num_misses; /* total unsuccessful hits in cache */ + int num_inserts; /* total successful inserts in cache */ + slot_t* deleted_list; /* linked list of to-be-deleted slots */ + time_t start_time; /* time the above counters were reset */ + int expunges; /* total number of expunges */ + zend_bool busy; /* Flag to tell clients when we are busy cleaning the cache */ + int num_entries; /* Statistic on the number of entries */ + size_t mem_size; /* Statistic on the memory size used by this cache */ +}; +/* }}} */ + +/* {{{ struct definition: apc_cache_t */ +struct apc_cache_t { + void* shmaddr; /* process (local) address of shared cache */ + cache_header_t* header; /* cache header (stored in SHM) */ + slot_t** slots; /* array of cache slots (stored in SHM) */ + int num_slots; /* number of slots in cache */ + int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ +}; +/* }}} */ + +/* {{{ struct definition local_slot_t */ +typedef struct local_slot_t local_slot_t; +struct local_slot_t { + slot_t *original; /* the original slot in shm */ + int num_hits; /* number of hits */ + time_t creation_time; /* creation time */ + apc_cache_entry_t *value; /* shallow copy of slot->value */ + local_slot_t *next; /* only for dead list */ +}; +/* }}} */ +/* {{{ struct definition apc_local_cache_t */ +struct apc_local_cache_t { + apc_cache_t* shmcache; /* the real cache in shm */ + local_slot_t* slots; /* process (local) cache of objects */ + local_slot_t* dead_list; /* list of objects pending removal */ + int num_slots; /* number of slots in cache */ + int ttl; /* time to live */ + int num_hits; /* number of hits */ + int generation; /* every generation lives between expunges */ +}; +/* }}} */ + extern apc_cache_info_t* apc_cache_info(T cache, zend_bool limited); extern void apc_cache_free_info(apc_cache_info_t* info); extern void apc_cache_expunge(apc_cache_t* cache, time_t t); diff --git a/apc_compile.c b/apc_compile.c index f583cce..533c624 100644 --- a/apc_compile.c +++ b/apc_compile.c @@ -655,6 +655,12 @@ cleanup: /* }}} */ #endif +/* {{{ apc_copy_class_entry */ +zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + return my_copy_class_entry(dst, src, allocate, deallocate); +} + /* {{{ my_copy_class_entry */ static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate) { @@ -730,6 +736,7 @@ static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_e /* these will either be set inside my_fixup_hashtable or * they will be copied out from parent inside zend_do_inheritance */ + dst->parent = NULL; dst->constructor = NULL; dst->destructor = NULL; dst->clone = NULL; @@ -1379,6 +1386,7 @@ cleanup: } /* }}} */ + /* {{{ apc_copy_new_functions */ apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC) { @@ -1493,7 +1501,6 @@ apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_ma #ifdef ZEND_ENGINE_2 elem = *((zend_class_entry**)elem); #endif - if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) { int ii; diff --git a/apc_compile.h b/apc_compile.h index 20ff076..2df8fb4 100644 --- a/apc_compile.h +++ b/apc_compile.h @@ -88,6 +88,7 @@ extern zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_ extern apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC); extern apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC); extern zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate); +zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate); /* * Deallocation functions corresponding to the copy functions above. diff --git a/apc_globals.h b/apc_globals.h index df6368f..afec240 100644 --- a/apc_globals.h +++ b/apc_globals.h @@ -87,6 +87,9 @@ ZEND_BEGIN_MODULE_GLOBALS(apc) apc_local_cache_t* lcache; /* unlocked local cache */ zend_bool force_file_update; /* force files to be updated during apc_compile_file */ char canon_path[MAXPATHLEN]; /* canonical path for key data */ + void *apc_bd_alloc_ptr; /* bindump alloc() ptr */ + void *apc_bd_alloc_ubptr; /* bindump alloc() upper bound ptr */ + HashTable apc_bd_alloc_list; /* bindump alloc() ptr list */ ZEND_END_MODULE_GLOBALS(apc) /* (the following declaration is defined in php_apc.c) */ diff --git a/apc_main.c b/apc_main.c index 302e382..4170628 100644 --- a/apc_main.c +++ b/apc_main.c @@ -97,7 +97,6 @@ static int install_class(apc_class_t cl TSRMLS_DC) zend_class_entry** allocated_ce = NULL; #endif - /* Special case for mangled names. Mangled names are unique to a file. * There is no way two classes with the same mangled name will occur, * unless a file is included twice. And if in case, a file is included diff --git a/apc_sma.c b/apc_sma.c index 74e7e16..e2f12f0 100644 --- a/apc_sma.c +++ b/apc_sma.c @@ -54,8 +54,8 @@ static int* sma_segments; /* array of shm segment ids */ static void** sma_shmaddrs; /* array of shm segment addresses */ static int sma_lastseg = 0; /* index of MRU segment */ -typedef struct header_t header_t; -struct header_t { +typedef struct sma_header_t sma_header_t; +struct sma_header_t { apc_lck_t sma_lock; /* segment lock, MUST BE ALIGNED for futex locks */ size_t segsize; /* size of entire segment */ size_t avail; /* bytes available (not necessarily contiguous) */ @@ -124,7 +124,7 @@ typedef union { void* p; int i; long l; double d; void (*f)(); } apc_word_t; /* {{{ sma_allocate: tries to allocate size bytes in a segment */ static int sma_allocate(void* shmaddr, size_t size) { - header_t* header; /* header of shared memory segment */ + sma_header_t* header; /* header of shared memory segment */ block_t* prv; /* block prior to working block */ block_t* cur; /* working block in list */ block_t* prvnextfit; /* block before next fit */ @@ -139,7 +139,7 @@ static int sma_allocate(void* shmaddr, size_t size) * First, insure that the segment contains at least realsize free bytes, * even if they are not contiguous. */ - header = (header_t*) shmaddr; + header = (sma_header_t*) shmaddr; if (header->avail < realsize) { return -1; } @@ -151,7 +151,7 @@ static int sma_allocate(void* shmaddr, size_t size) if(header->nfoffset) { prv = BLOCKAT(header->nfoffset); } else { - prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); } CHECK_CANARY(prv); @@ -172,7 +172,7 @@ static int sma_allocate(void* shmaddr, size_t size) /* Check to see if we need to wrap around and search from the top */ if(header->nfoffset && prv->next == 0) { - prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); #ifdef __APC_SMA_DEBUG__ CHECK_CANARY(prv); #endif @@ -236,7 +236,7 @@ static int sma_allocate(void* shmaddr, size_t size) /* {{{ sma_deallocate: deallocates the block at the given offset */ static int sma_deallocate(void* shmaddr, int offset) { - header_t* header; /* header of shared memory segment */ + sma_header_t* header; /* header of shared memory segment */ block_t* cur; /* the new block to insert */ block_t* prv; /* the block before cur */ block_t* nxt; /* the block after cur */ @@ -247,7 +247,7 @@ static int sma_deallocate(void* shmaddr, int offset) /* find position of new block in free list */ cur = BLOCKAT(offset); - prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); CHECK_CANARY(cur); @@ -274,7 +274,7 @@ static int sma_deallocate(void* shmaddr, int offset) #endif /* update the block header */ - header = (header_t*) shmaddr; + header = (sma_header_t*) shmaddr; header->avail += cur->size; size = cur->size; @@ -337,7 +337,7 @@ void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask) sma_shmaddrs = (void**) apc_emalloc(sma_numseg*sizeof(void*)); for (i = 0; i < sma_numseg; i++) { - header_t* header; + sma_header_t* header; block_t* block; void* shmaddr; @@ -351,10 +351,10 @@ void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask) #endif shmaddr = sma_shmaddrs[i]; - header = (header_t*) shmaddr; + header = (sma_header_t*) shmaddr; apc_lck_create(NULL, 0, 1, header->sma_lock); header->segsize = sma_segsize; - header->avail = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t)); + header->avail = sma_segsize - ALIGNWORD(sizeof(sma_header_t)) - ALIGNWORD(sizeof(block_t)); header->nfoffset = 0; #if ALLOC_DISTRIBUTION { @@ -362,9 +362,9 @@ void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask) for(j=0; j<30; j++) header->adist[j] = 0; } #endif - block = BLOCKAT(ALIGNWORD(sizeof(header_t))); + block = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); block->size = 0; - block->next = ALIGNWORD(sizeof(header_t)) + ALIGNWORD(sizeof(block_t)); + block->next = ALIGNWORD(sizeof(sma_header_t)) + ALIGNWORD(sizeof(block_t)); SET_CANARY(block); #ifdef __APC_SMA_DEBUG__ block->id = -1; @@ -388,7 +388,7 @@ void apc_sma_cleanup() assert(sma_initialized); for (i = 0; i < sma_numseg; i++) { - apc_lck_destroy(((header_t*)sma_shmaddrs[i])->sma_lock); + apc_lck_destroy(((sma_header_t*)sma_shmaddrs[i])->sma_lock); #if APC_MMAP apc_unmap(sma_shmaddrs[i], sma_segments[i]); #else @@ -409,31 +409,31 @@ void* apc_sma_malloc(size_t n) TSRMLS_FETCH(); assert(sma_initialized); - LOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); + LOCK(((sma_header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); off = sma_allocate(sma_shmaddrs[sma_lastseg], n); if (off != -1) { void* p = (void *)(((char *)(sma_shmaddrs[sma_lastseg])) + off); if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; } - UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); return p; } - UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); for (i = 0; i < sma_numseg; i++) { if (i == sma_lastseg) { continue; } - LOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + LOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); off = sma_allocate(sma_shmaddrs[i], n); if (off != -1) { void* p = (void *)(((char *)(sma_shmaddrs[i])) + off); if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; } - UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); sma_lastseg = i; return p; } - UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); } return NULL; @@ -479,15 +479,15 @@ void apc_sma_free(void* p) assert(sma_initialized); for (i = 0; i < sma_numseg; i++) { - LOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + LOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i])); if (p >= sma_shmaddrs[i] && offset < sma_segsize) { d_size = sma_deallocate(sma_shmaddrs[i], offset); if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; } - UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); return; } - UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); } apc_eprint("apc_sma_free: could not locate address %p", p); @@ -509,7 +509,7 @@ apc_sma_info_t* apc_sma_info(zend_bool limited) info = (apc_sma_info_t*) apc_emalloc(sizeof(apc_sma_info_t)); info->num_seg = sma_numseg; - info->seg_size = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t)); + info->seg_size = sma_segsize - ALIGNWORD(sizeof(sma_header_t)) - ALIGNWORD(sizeof(block_t)); info->list = apc_emalloc(info->num_seg * sizeof(apc_sma_link_t*)); for (i = 0; i < sma_numseg; i++) { @@ -520,9 +520,9 @@ apc_sma_info_t* apc_sma_info(zend_bool limited) /* For each segment */ for (i = 0; i < sma_numseg; i++) { - RDLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + RDLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); shmaddr = sma_shmaddrs[i]; - prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); link = &info->list[i]; @@ -541,7 +541,7 @@ apc_sma_info_t* apc_sma_info(zend_bool limited) prv = cur; } - UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + UNLOCK(((sma_header_t*)sma_shmaddrs[i])->sma_lock); } return info; @@ -573,7 +573,7 @@ size_t apc_sma_get_avail_mem() int i; for (i = 0; i < sma_numseg; i++) { - header_t* header = (header_t*) sma_shmaddrs[i]; + sma_header_t* header = (sma_header_t*) sma_shmaddrs[i]; avail_mem += header->avail; } return avail_mem; @@ -582,7 +582,7 @@ size_t apc_sma_get_avail_mem() #if ALLOC_DISTRIBUTION size_t *apc_sma_get_alloc_distribution(void) { - header_t* header = (header_t*) sma_shmaddrs[0]; + sma_header_t* header = (sma_header_t*) sma_shmaddrs[0]; return header->adist; } #endif @@ -596,8 +596,8 @@ void apc_sma_check_integrity() /* For each segment */ for (i = 0; i < sma_numseg; i++) { char* shmaddr = sma_shmaddrs[i]; - header_t* header = (header_t*) shmaddr; - block_t* prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + sma_header_t* header = (sma_header_t*) shmaddr; + block_t* prv = BLOCKAT(ALIGNWORD(sizeof(sma_header_t))); int avail = 0; /* For each block in this segment */ diff --git a/config.m4 b/config.m4 index a754f51..59b4b69 100644 --- a/config.m4 +++ b/config.m4 @@ -203,7 +203,8 @@ if test "$PHP_APC" != "no"; then apc_sma.c \ apc_stack.c \ apc_zend.c \ - apc_rfc1867.c " + apc_rfc1867.c \ + apc_bin.c " PHP_CHECK_LIBRARY(rt, shm_open, [PHP_ADD_LIBRARY(rt,,APC_SHARED_LIBADD)]) PHP_NEW_EXTENSION(apc, $apc_sources, $ext_shared,, \\$(APC_CFLAGS)) diff --git a/php_apc.c b/php_apc.c index f91b993..8d4392b 100644 --- a/php_apc.c +++ b/php_apc.c @@ -33,9 +33,15 @@ #include "apc_main.h" #include "apc_sma.h" #include "apc_lock.h" +#include "apc_bin.h" #include "php_globals.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/flock_compat.h" +#ifdef HAVE_SYS_FILE_H +#include +#endif #include "SAPI.h" #include "rfc1867.h" #include "php_apc.h" @@ -59,6 +65,10 @@ PHP_FUNCTION(apc_compile_file); PHP_FUNCTION(apc_define_constants); PHP_FUNCTION(apc_load_constants); PHP_FUNCTION(apc_add); +PHP_FUNCTION(apc_bin_dump); +PHP_FUNCTION(apc_bin_load); +PHP_FUNCTION(apc_bin_dumpfile); +PHP_FUNCTION(apc_bin_loadfile); /* }}} */ /* {{{ ZEND_DECLARE_MODULE_GLOBALS(apc) */ @@ -262,6 +272,9 @@ static PHP_MINIT_FUNCTION(apc) } #endif } + + zend_register_long_constant("APC_BIN_VERIFY_MD5", sizeof("APC_BIN_VERIFY_MD5"), APC_BIN_VERIFY_MD5, (CONST_CS | CONST_PERSISTENT), module_number TSRMLS_CC); + zend_register_long_constant("APC_BIN_VERIFY_CRC32", sizeof("APC_BIN_VERIFY_CRC32"), APC_BIN_VERIFY_CRC32, (CONST_CS | CONST_PERSISTENT), module_number TSRMLS_CC); } return SUCCESS; @@ -936,6 +949,197 @@ PHP_FUNCTION(apc_compile_file) { } /* }}} */ + +/* {{{ proto mixed apc_bin_dump(optional array files, optional array user_vars) + Returns a binary dump of the given files and user variables from the APC cache. + A NULL for files or user_vars signals a dump of every entry, while array() will dump nothing. + */ +PHP_FUNCTION(apc_bin_dump) { + + zval *z_files, *z_user_vars; + HashTable *h_files, *h_user_vars; + apc_bd_t *bd; + zval *rval; + + if(!APCG(enabled)) { + apc_wprint("APC is not enabled, apc_bin_dump not available."); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!a!", &z_files, &z_user_vars) == FAILURE) { + return; + } + + h_files = z_files ? Z_ARRVAL_P(z_files) : NULL; + h_user_vars = z_user_vars ? Z_ARRVAL_P(z_user_vars) : NULL; + bd = apc_bin_dump(h_files, h_user_vars TSRMLS_CC); + if(bd) { + RETVAL_STRINGL((char*)bd, bd->size-1, 0); + } else { + apc_eprint("Unkown error encounterd during apc_bin_dump."); + RETVAL_NULL(); + } + + return; +} + +/* {{{ proto mixed apc_bin_dumpfile(array files, array user_vars, string filename, int flags, resource context) + Output a binary dump of the given files and user variables from the APC cache to the named file. + */ +PHP_FUNCTION(apc_bin_dumpfile) { + + zval *z_files, *z_user_vars; + HashTable *h_files, *h_user_vars; + char *filename; + int filename_len; + int flags; + zval *zcontext = NULL; + php_stream_context *context = NULL; + php_stream *stream; + int numbytes = 0; + apc_bd_t *bd; + + if(!APCG(enabled)) { + apc_wprint("APC is not enabled, apc_bin_dumpfile not available."); + RETURN_FALSE; + } + + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!s|lr!", &z_files, &z_user_vars, &filename, &filename_len, &flags, &zcontext) == FAILURE) { + return; + } + + if(!filename_len) { + apc_eprint("apc_bin_dumpfile filename argument must be a valid filename."); + RETURN_FALSE; + } + + h_files = z_files ? Z_ARRVAL_P(z_files) : NULL; + h_user_vars = z_user_vars ? Z_ARRVAL_P(z_user_vars) : NULL; + bd = apc_bin_dump(h_files, h_user_vars TSRMLS_CC); + if(!bd) { + apc_eprint("Unkown error uncountered during apc binary dump."); + RETURN_FALSE; + } + + + /* Most of the following has been taken from the file_get/put_contents functions */ + + context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); + stream = php_stream_open_wrapper_ex(filename, (flags & PHP_FILE_APPEND) ? "ab" : "wb", + ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); + if (stream == NULL) { + efree(bd); + apc_eprint("Unable to write to file in apc_bin_dumpfile."); + RETURN_FALSE; + } + + if (flags & LOCK_EX && php_stream_lock(stream, LOCK_EX)) { + php_stream_close(stream); + efree(bd); + apc_eprint("Unable to get a lock on file in apc_bin_dumpfile."); + RETURN_FALSE; + } + + numbytes = php_stream_write(stream, (char*)bd, bd->size); + if(numbytes != bd->size) { + numbytes = -1; + } + + php_stream_close(stream); + efree(bd); + + if(numbytes < 0) { + apc_wprint("Only %d of %d bytes written, possibly out of free disk space", numbytes, bd->size); + RETURN_FALSE; + } + + RETURN_LONG(numbytes); +} + +/* {{{ proto mixed apc_bin_load(string data, [int flags]) + Load the given binary dump into the APC file/user cache. + */ +PHP_FUNCTION(apc_bin_load) { + + int data_len; + char *data; + long flags = 0; + + if(!APCG(enabled)) { + apc_wprint("APC is not enabled, apc_bin_load not available."); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &flags) == FAILURE) { + return; + } + + if(!data_len || data_len != ((apc_bd_t*)data)->size -1) { + apc_eprint("apc_bin_load string argument does not appear to be a valid APC binary dump due to size (%d vs expected %d).", data_len, ((apc_bd_t*)data)->size -1); + RETURN_FALSE; + } + + apc_bin_load((apc_bd_t*)data, (int)flags TSRMLS_CC); + + RETURN_TRUE; +} + +/* {{{ proto mixed apc_bin_loadfile(string filename, [resource context, [int flags]]) + Load the given binary dump from the named file into the APC file/user cache. + */ +PHP_FUNCTION(apc_bin_loadfile) { + + char *filename; + int filename_len; + zval *zcontext = NULL; + long flags; + php_stream_context *context = NULL; + php_stream *stream; + char *data; + int len; + + if(!APCG(enabled)) { + apc_wprint("APC is not enabled, apc_bin_loadfile not available."); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r!l", &filename, &filename_len, &zcontext, &flags) == FAILURE) { + return; + } + + if(!filename_len) { + apc_eprint("apc_bin_loadfile filename argument must be a valid filename."); + RETURN_FALSE; + } + + context = php_stream_context_from_zval(zcontext, 0); + stream = php_stream_open_wrapper_ex(filename, "rb", + ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); + if (!stream) { + apc_eprint("Unable to read from file in apc_bin_loadfile."); + RETURN_FALSE; + } + + len = php_stream_copy_to_mem(stream, &data, PHP_STREAM_COPY_ALL, 0); + if(len == 0) { + apc_wprint("File passed to apc_bin_loadfile was empty: %s.", filename); + RETURN_FALSE; + } else if(len < 0) { + apc_wprint("Error reading file passed to apc_bin_loadfile: %s.", filename); + RETURN_FALSE; + } else if(len != ((apc_bd_t*)data)->size) { + apc_wprint("file passed to apc_bin_loadfile does not appear to be valid due to size (%d vs expected %d).", len, ((apc_bd_t*)data)->size -1); + RETURN_FALSE; + } + php_stream_close(stream); + + apc_bin_load((apc_bd_t*)data, (int)flags TSRMLS_CC); + efree(data); + + RETURN_TRUE; +} + /* {{{ apc_functions[] */ function_entry apc_functions[] = { PHP_FE(apc_cache_info, NULL) @@ -948,6 +1152,10 @@ function_entry apc_functions[] = { PHP_FE(apc_load_constants, NULL) PHP_FE(apc_compile_file, NULL) PHP_FE(apc_add, NULL) + PHP_FE(apc_bin_dump, NULL) + PHP_FE(apc_bin_load, NULL) + PHP_FE(apc_bin_dumpfile, NULL) + PHP_FE(apc_bin_loadfile, NULL) {NULL, NULL, NULL} }; /* }}} */ diff --git a/tests/apc_bin_001.phpt b/tests/apc_bin_001.phpt new file mode 100755 index 0000000..c4f5e70 --- /dev/null +++ b/tests/apc_bin_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +APC: bindump user cache +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +--FILE-- + +===DONE=== + +--EXPECTF-- +bool(false) +string(9) "testvalue" +===DONE=== diff --git a/tests/apc_bin_002-1.inc b/tests/apc_bin_002-1.inc new file mode 100644 index 0000000..d8cb4a4 --- /dev/null +++ b/tests/apc_bin_002-1.inc @@ -0,0 +1,42 @@ +my_method()."\n"; +echo "class static property: ".my_class::$my_static_property."\n"; +echo "class dynamic property: ".$my_class->my_property."\n"; +echo "class constant: ".my_class::my_constant."\n"; +echo "\n"; +echo "inherited class static method: ".my_i_class::my_static_method()."\n"; +echo "inherited class dynamic method: ".$my_i_class->my_method()."\n"; +echo "inherited class static property: ".my_i_class::$my_static_property."\n"; +echo "inherited class dynamic property: ".$my_i_class->my_property."\n"; +echo "inherited class constant: ".my_i_class::my_constant."\n"; +echo "\n"; + + + +function my_function() { return "Success"; } + + +class my_class { + static $my_static_property = "Success"; + var $my_property = "Success"; + const my_constant = "Success"; + static function my_static_method() { return "Success"; } + function my_method() { return "Success"; } +} + +class my_i_class extends my_class { + function dummy() { return 1; } +} + diff --git a/tests/apc_bin_002-2.inc b/tests/apc_bin_002-2.inc new file mode 100644 index 0000000..e076f97 --- /dev/null +++ b/tests/apc_bin_002-2.inc @@ -0,0 +1,5 @@ + diff --git a/tests/apc_bin_002.phpt b/tests/apc_bin_002.phpt new file mode 100755 index 0000000..9171f85 --- /dev/null +++ b/tests/apc_bin_002.phpt @@ -0,0 +1,53 @@ +--TEST-- +APC: bindump file cache part 1 +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.stat=0 +apc.cache_by_default=1 +apc.filters= +report_memleaks = Off +--FILE-- + +===DONE=== + +--EXPECTF-- +apc bindump 002 test + +global scope execution: Success + +function execution: Success + +class static method: Success +class dynamic method: Success +class static property: Success +class dynamic property: Success +class constant: Success + +inherited class static method: Success +inherited class dynamic method: Success +inherited class static property: Success +inherited class dynamic property: Success +inherited class constant: Success + +===DONE===