2015-05-07 04:46:49 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend OPcache |
|
|
|
|
+----------------------------------------------------------------------+
|
2019-02-05 15:33:28 +08:00
|
|
|
| Copyright (c) The PHP Group |
|
2015-05-07 04:46:49 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| 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: |
|
2021-05-06 18:16:35 +08:00
|
|
|
| https://www.php.net/license/3_01.txt |
|
2015-05-07 04:46:49 +08:00
|
|
|
| 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. |
|
|
|
|
+----------------------------------------------------------------------+
|
2018-11-02 00:30:28 +08:00
|
|
|
| Authors: Dmitry Stogov <dmitry@php.net> |
|
2015-05-07 04:46:49 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "zend.h"
|
|
|
|
#include "zend_virtual_cwd.h"
|
|
|
|
#include "zend_compile.h"
|
|
|
|
#include "zend_vm.h"
|
2015-11-04 18:59:30 +08:00
|
|
|
#include "zend_interfaces.h"
|
2020-05-25 02:57:00 +08:00
|
|
|
#include "zend_attributes.h"
|
2020-07-17 07:31:10 +08:00
|
|
|
#include "zend_system_id.h"
|
2022-09-22 20:07:51 +08:00
|
|
|
#include "zend_enum.h"
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
#include "php.h"
|
2018-03-05 22:16:55 +08:00
|
|
|
#ifdef ZEND_WIN32
|
|
|
|
#include "ext/standard/md5.h"
|
|
|
|
#endif
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
#include "ZendAccelerator.h"
|
|
|
|
#include "zend_file_cache.h"
|
|
|
|
#include "zend_shared_alloc.h"
|
|
|
|
#include "zend_accelerator_util_funcs.h"
|
|
|
|
#include "zend_accelerator_hash.h"
|
|
|
|
|
2024-06-12 04:47:05 +08:00
|
|
|
#ifdef HAVE_JIT
|
2020-05-18 15:23:06 +08:00
|
|
|
#include "jit/zend_jit.h"
|
|
|
|
#endif
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2024-06-09 20:23:41 +08:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2015-11-07 22:15:19 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
#ifdef HAVE_SYS_UIO_H
|
|
|
|
# include <sys/uio.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_FILE_H
|
|
|
|
# include <sys/file.h>
|
|
|
|
#endif
|
|
|
|
|
2019-07-01 17:02:31 +08:00
|
|
|
#if __has_feature(memory_sanitizer)
|
|
|
|
# include <sanitizer/msan_interface.h>
|
|
|
|
#endif
|
|
|
|
|
2018-07-14 18:51:37 +08:00
|
|
|
#ifndef ZEND_WIN32
|
|
|
|
#define zend_file_cache_unlink unlink
|
|
|
|
#define zend_file_cache_open open
|
|
|
|
#else
|
|
|
|
#define zend_file_cache_unlink php_win32_ioutil_unlink
|
|
|
|
#define zend_file_cache_open php_win32_ioutil_open
|
|
|
|
#endif
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
#ifdef ZEND_WIN32
|
|
|
|
# define LOCK_SH 0
|
|
|
|
# define LOCK_EX 1
|
|
|
|
# define LOCK_UN 2
|
|
|
|
static int zend_file_cache_flock(int fd, int op)
|
|
|
|
{
|
|
|
|
OVERLAPPED offset = {0,0,0,0,NULL};
|
|
|
|
if (op == LOCK_EX) {
|
|
|
|
if (LockFileEx((HANDLE)_get_osfhandle(fd),
|
|
|
|
LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (op == LOCK_SH) {
|
|
|
|
if (LockFileEx((HANDLE)_get_osfhandle(fd),
|
|
|
|
0, 0, 1, 0, &offset) == TRUE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (op == LOCK_UN) {
|
|
|
|
if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
|
|
|
|
0, 1, 0, &offset) == TRUE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#elif defined(HAVE_FLOCK)
|
|
|
|
# define zend_file_cache_flock flock
|
|
|
|
#else
|
|
|
|
# define LOCK_SH 0
|
|
|
|
# define LOCK_EX 1
|
|
|
|
# define LOCK_UN 2
|
|
|
|
static int zend_file_cache_flock(int fd, int type)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef O_BINARY
|
|
|
|
# define O_BINARY 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SUFFIX ".bin"
|
|
|
|
|
|
|
|
#define IS_SERIALIZED_INTERNED(ptr) \
|
|
|
|
((size_t)(ptr) & Z_UL(1))
|
2018-04-27 22:58:30 +08:00
|
|
|
|
2021-02-24 18:45:25 +08:00
|
|
|
/* Allowing == on the upper bound accounts for a potential empty allocation at the end of the
|
|
|
|
* memory region. This can also happen for a return-type-only arg_info, where &arg_info[1] is
|
|
|
|
* stored, which may point to the end of the region. */
|
2015-05-07 04:46:49 +08:00
|
|
|
#define IS_SERIALIZED(ptr) \
|
2018-04-27 22:58:30 +08:00
|
|
|
((char*)(ptr) <= (char*)script->size)
|
2015-05-07 04:46:49 +08:00
|
|
|
#define IS_UNSERIALIZED(ptr) \
|
2021-02-24 18:45:25 +08:00
|
|
|
(((char*)(ptr) >= (char*)script->mem && (char*)(ptr) <= (char*)script->mem + script->size) || \
|
2018-04-28 18:26:32 +08:00
|
|
|
IS_ACCEL_INTERNED(ptr))
|
2015-05-07 04:46:49 +08:00
|
|
|
#define SERIALIZE_PTR(ptr) do { \
|
2015-05-07 16:36:01 +08:00
|
|
|
if (ptr) { \
|
2018-04-28 18:26:32 +08:00
|
|
|
ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
|
2015-05-07 16:36:01 +08:00
|
|
|
(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define UNSERIALIZE_PTR(ptr) do { \
|
|
|
|
if (ptr) { \
|
|
|
|
ZEND_ASSERT(IS_SERIALIZED(ptr)); \
|
|
|
|
(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define SERIALIZE_STR(ptr) do { \
|
2015-05-07 04:46:49 +08:00
|
|
|
if (ptr) { \
|
|
|
|
if (IS_ACCEL_INTERNED(ptr)) { \
|
|
|
|
(ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
|
|
|
|
} else { \
|
|
|
|
ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
|
2015-05-12 18:45:52 +08:00
|
|
|
/* script->corrupted shows if the script in SHM or not */ \
|
|
|
|
if (EXPECTED(script->corrupted)) { \
|
2018-02-28 05:52:12 +08:00
|
|
|
GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
|
|
|
|
GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
|
2015-05-12 18:45:52 +08:00
|
|
|
} \
|
2015-05-07 04:46:49 +08:00
|
|
|
(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2015-05-07 16:36:01 +08:00
|
|
|
#define UNSERIALIZE_STR(ptr) do { \
|
2015-05-07 04:46:49 +08:00
|
|
|
if (ptr) { \
|
|
|
|
if (IS_SERIALIZED_INTERNED(ptr)) { \
|
2015-05-21 04:33:55 +08:00
|
|
|
(ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \
|
2015-05-07 04:46:49 +08:00
|
|
|
} else { \
|
|
|
|
ZEND_ASSERT(IS_SERIALIZED(ptr)); \
|
|
|
|
(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
|
2016-02-25 04:46:11 +08:00
|
|
|
/* script->corrupted shows if the script in SHM or not */ \
|
|
|
|
if (EXPECTED(!script->corrupted)) { \
|
2018-02-28 05:52:12 +08:00
|
|
|
GC_ADD_FLAGS(ptr, IS_STR_INTERNED | IS_STR_PERMANENT); \
|
2016-02-25 04:46:11 +08:00
|
|
|
} else { \
|
2018-02-28 05:52:12 +08:00
|
|
|
GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
|
|
|
|
GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
|
2016-02-25 04:46:11 +08:00
|
|
|
} \
|
2015-05-07 04:46:49 +08:00
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2020-05-25 02:57:00 +08:00
|
|
|
#define SERIALIZE_ATTRIBUTES(attributes) do { \
|
|
|
|
if ((attributes) && !IS_SERIALIZED(attributes)) { \
|
|
|
|
HashTable *ht; \
|
|
|
|
SERIALIZE_PTR(attributes); \
|
|
|
|
ht = (attributes); \
|
|
|
|
UNSERIALIZE_PTR(ht); \
|
|
|
|
zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_attribute); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define UNSERIALIZE_ATTRIBUTES(attributes) do { \
|
|
|
|
if ((attributes) && !IS_UNSERIALIZED(attributes)) { \
|
|
|
|
HashTable *ht; \
|
|
|
|
UNSERIALIZE_PTR(attributes); \
|
|
|
|
ht = (attributes); \
|
|
|
|
zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_attribute, NULL); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
|
|
|
|
{HT_INVALID_IDX, HT_INVALID_IDX};
|
|
|
|
|
|
|
|
typedef struct _zend_file_cache_metainfo {
|
|
|
|
char magic[8];
|
|
|
|
char system_id[32];
|
|
|
|
size_t mem_size;
|
|
|
|
size_t str_size;
|
|
|
|
size_t script_offset;
|
|
|
|
accel_time_t timestamp;
|
|
|
|
uint32_t checksum;
|
|
|
|
} zend_file_cache_metainfo;
|
|
|
|
|
|
|
|
static int zend_file_cache_mkdir(char *filename, size_t start)
|
|
|
|
{
|
|
|
|
char *s = filename + start;
|
|
|
|
|
|
|
|
while (*s) {
|
|
|
|
if (IS_SLASH(*s)) {
|
|
|
|
char old = *s;
|
|
|
|
*s = '\000';
|
2018-07-11 20:28:57 +08:00
|
|
|
#ifndef ZEND_WIN32
|
2015-05-07 04:46:49 +08:00
|
|
|
if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
|
2018-07-11 20:28:57 +08:00
|
|
|
#else
|
|
|
|
if (php_win32_ioutil_mkdir(filename, 0700) < 0 && errno != EEXIST) {
|
|
|
|
#endif
|
2015-05-07 04:46:49 +08:00
|
|
|
*s = old;
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
*s = old;
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void (*serialize_callback_t)(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf);
|
|
|
|
|
|
|
|
typedef void (*unserialize_callback_t)(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf);
|
|
|
|
|
|
|
|
static void zend_file_cache_serialize_zval(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf);
|
|
|
|
static void zend_file_cache_unserialize_zval(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf);
|
|
|
|
|
|
|
|
static void *zend_file_cache_serialize_interned(zend_string *str,
|
|
|
|
zend_file_cache_metainfo *info)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
/* check if the same interned string was already stored */
|
|
|
|
ret = zend_shared_alloc_get_xlat_entry(str);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-06-29 21:44:54 +08:00
|
|
|
len = ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(ZSTR_LEN(str)));
|
2015-05-07 04:46:49 +08:00
|
|
|
ret = (void*)(info->str_size | Z_UL(1));
|
|
|
|
zend_shared_alloc_register_xlat_entry(str, ret);
|
2022-03-24 22:03:53 +08:00
|
|
|
|
|
|
|
zend_string *s = (zend_string*)ZCG(mem);
|
|
|
|
if (info->str_size + len > ZSTR_LEN(s)) {
|
2015-05-07 04:46:49 +08:00
|
|
|
size_t new_len = info->str_size + len;
|
2022-03-24 22:03:53 +08:00
|
|
|
s = zend_string_realloc(
|
|
|
|
s,
|
2015-06-29 21:44:54 +08:00
|
|
|
((_ZSTR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_ZSTR_HEADER_SIZE + 1),
|
2015-05-07 04:46:49 +08:00
|
|
|
0);
|
2022-03-24 22:03:53 +08:00
|
|
|
ZCG(mem) = (void*)s;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
2021-08-11 16:28:52 +08:00
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
zend_string *new_str = (zend_string *) (ZSTR_VAL(s) + info->str_size);
|
2021-08-11 16:28:52 +08:00
|
|
|
memcpy(new_str, str, len);
|
|
|
|
GC_ADD_FLAGS(new_str, IS_STR_INTERNED);
|
|
|
|
GC_DEL_FLAGS(new_str, IS_STR_PERMANENT|IS_STR_CLASS_NAME_MAP_PTR);
|
2015-05-07 04:46:49 +08:00
|
|
|
info->str_size += len;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
static void *zend_file_cache_unserialize_interned(zend_string *str, bool in_shm)
|
2015-05-07 04:46:49 +08:00
|
|
|
{
|
|
|
|
str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
|
2021-08-11 16:28:52 +08:00
|
|
|
if (!in_shm) {
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
zend_string *ret = accel_new_interned_string(str);
|
|
|
|
if (ret == str) {
|
|
|
|
/* We have to create new SHM allocated string */
|
|
|
|
size_t size = _ZSTR_STRUCT_SIZE(ZSTR_LEN(str));
|
|
|
|
ret = zend_shared_alloc(size);
|
|
|
|
if (!ret) {
|
|
|
|
zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
|
|
|
|
LONGJMP(*EG(bailout), FAILURE);
|
2015-05-07 16:36:01 +08:00
|
|
|
}
|
2021-08-11 16:28:52 +08:00
|
|
|
memcpy(ret, str, size);
|
|
|
|
/* String wasn't interned but we will use it as interned anyway */
|
|
|
|
GC_SET_REFCOUNT(ret, 1);
|
|
|
|
GC_TYPE_INFO(ret) = GC_STRING | ((IS_STR_INTERNED | IS_STR_PERSISTENT | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_serialize_hash(HashTable *ht,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf,
|
|
|
|
serialize_callback_t func)
|
|
|
|
{
|
2018-12-28 16:22:18 +08:00
|
|
|
if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
|
2015-05-07 04:46:49 +08:00
|
|
|
ht->arData = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (IS_SERIALIZED(ht->arData)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-11-03 20:18:26 +08:00
|
|
|
if (HT_IS_PACKED(ht)) {
|
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(ht->arPacked);
|
|
|
|
p = ht->arPacked;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + ht->nNumUsed;
|
|
|
|
while (p < end) {
|
|
|
|
if (Z_TYPE_P(p) != IS_UNDEF) {
|
|
|
|
func(p, script, info, buf);
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Bucket *p, *end;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(ht->arData);
|
|
|
|
p = ht->arData;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + ht->nNumUsed;
|
|
|
|
while (p < end) {
|
|
|
|
if (Z_TYPE(p->val) != IS_UNDEF) {
|
|
|
|
SERIALIZE_STR(p->key);
|
|
|
|
func(&p->val, script, info, buf);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-11 00:37:46 +08:00
|
|
|
static void zend_file_cache_serialize_ast(zend_ast *ast,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
2015-05-07 04:46:49 +08:00
|
|
|
{
|
|
|
|
uint32_t i;
|
2017-10-11 00:37:46 +08:00
|
|
|
zend_ast *tmp;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2017-10-10 15:11:05 +08:00
|
|
|
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
|
|
|
|
} else if (zend_ast_is_list(ast)) {
|
|
|
|
zend_ast_list *list = zend_ast_get_list(ast);
|
|
|
|
for (i = 0; i < list->children; i++) {
|
2017-10-11 00:37:46 +08:00
|
|
|
if (list->child[i] && !IS_SERIALIZED(list->child[i])) {
|
|
|
|
SERIALIZE_PTR(list->child[i]);
|
|
|
|
tmp = list->child[i];
|
|
|
|
UNSERIALIZE_PTR(tmp);
|
|
|
|
zend_file_cache_serialize_ast(tmp, script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint32_t children = zend_ast_get_num_children(ast);
|
|
|
|
for (i = 0; i < children; i++) {
|
2017-10-11 00:37:46 +08:00
|
|
|
if (ast->child[i] && !IS_SERIALIZED(ast->child[i])) {
|
|
|
|
SERIALIZE_PTR(ast->child[i]);
|
|
|
|
tmp = ast->child[i];
|
|
|
|
UNSERIALIZE_PTR(tmp);
|
|
|
|
zend_file_cache_serialize_ast(tmp, script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_serialize_zval(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
switch (Z_TYPE_P(zv)) {
|
|
|
|
case IS_STRING:
|
|
|
|
if (!IS_SERIALIZED(Z_STR_P(zv))) {
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(Z_STR_P(zv));
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IS_ARRAY:
|
|
|
|
if (!IS_SERIALIZED(Z_ARR_P(zv))) {
|
|
|
|
HashTable *ht;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_ARR_P(zv));
|
|
|
|
ht = Z_ARR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(ht);
|
|
|
|
zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IS_CONSTANT_AST:
|
|
|
|
if (!IS_SERIALIZED(Z_AST_P(zv))) {
|
|
|
|
zend_ast_ref *ast;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_AST_P(zv));
|
|
|
|
ast = Z_AST_P(zv);
|
|
|
|
UNSERIALIZE_PTR(ast);
|
2017-10-11 00:37:46 +08:00
|
|
|
zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
break;
|
2020-05-20 16:55:36 +08:00
|
|
|
case IS_INDIRECT:
|
|
|
|
/* Used by static properties. */
|
|
|
|
SERIALIZE_PTR(Z_INDIRECT_P(zv));
|
|
|
|
break;
|
2021-08-17 23:13:04 +08:00
|
|
|
default:
|
|
|
|
ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING);
|
|
|
|
break;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 02:57:00 +08:00
|
|
|
static void zend_file_cache_serialize_attribute(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
zend_attribute *attr = Z_PTR_P(zv);
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
attr = Z_PTR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(attr);
|
|
|
|
|
|
|
|
SERIALIZE_STR(attr->name);
|
|
|
|
SERIALIZE_STR(attr->lcname);
|
|
|
|
|
|
|
|
for (i = 0; i < attr->argc; i++) {
|
2020-04-06 18:46:52 +08:00
|
|
|
SERIALIZE_STR(attr->args[i].name);
|
|
|
|
zend_file_cache_serialize_zval(&attr->args[i].value, script, info, buf);
|
2020-05-25 02:57:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 19:21:13 +08:00
|
|
|
static void zend_file_cache_serialize_type(
|
|
|
|
zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
|
|
|
|
{
|
|
|
|
if (ZEND_TYPE_HAS_LIST(*type)) {
|
|
|
|
zend_type_list *list = ZEND_TYPE_LIST(*type);
|
|
|
|
SERIALIZE_PTR(list);
|
|
|
|
ZEND_TYPE_SET_PTR(*type, list);
|
|
|
|
UNSERIALIZE_PTR(list);
|
|
|
|
|
2020-01-17 00:04:11 +08:00
|
|
|
zend_type *list_type;
|
|
|
|
ZEND_TYPE_LIST_FOREACH(list, list_type) {
|
|
|
|
zend_file_cache_serialize_type(list_type, script, info, buf);
|
2019-09-25 19:21:13 +08:00
|
|
|
} ZEND_TYPE_LIST_FOREACH_END();
|
|
|
|
} else if (ZEND_TYPE_HAS_NAME(*type)) {
|
|
|
|
zend_string *type_name = ZEND_TYPE_NAME(*type);
|
|
|
|
SERIALIZE_STR(type_name);
|
|
|
|
ZEND_TYPE_SET_PTR(*type, type_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_serialize_op_array(zend_op_array *op_array,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
|
|
|
|
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
/* Check whether this op_array has already been serialized. */
|
|
|
|
if (IS_SERIALIZED(op_array->opcodes)) {
|
|
|
|
ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op_array->scope) {
|
2017-03-03 21:10:46 +08:00
|
|
|
if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
|
|
|
|
op_array->refcount = (uint32_t*)(intptr_t)-1;
|
2021-03-17 19:19:09 +08:00
|
|
|
SERIALIZE_PTR(op_array->static_variables);
|
2017-03-03 21:10:46 +08:00
|
|
|
SERIALIZE_PTR(op_array->literals);
|
|
|
|
SERIALIZE_PTR(op_array->opcodes);
|
|
|
|
SERIALIZE_PTR(op_array->arg_info);
|
|
|
|
SERIALIZE_PTR(op_array->vars);
|
|
|
|
SERIALIZE_STR(op_array->function_name);
|
|
|
|
SERIALIZE_STR(op_array->filename);
|
2017-03-03 21:12:41 +08:00
|
|
|
SERIALIZE_PTR(op_array->live_range);
|
2017-03-03 21:10:46 +08:00
|
|
|
SERIALIZE_PTR(op_array->scope);
|
|
|
|
SERIALIZE_STR(op_array->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
SERIALIZE_ATTRIBUTES(op_array->attributes);
|
2017-03-03 21:10:46 +08:00
|
|
|
SERIALIZE_PTR(op_array->try_catch_array);
|
|
|
|
SERIALIZE_PTR(op_array->prototype);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
|
|
|
|
}
|
|
|
|
|
2021-03-17 19:19:09 +08:00
|
|
|
if (op_array->static_variables) {
|
|
|
|
HashTable *ht;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(op_array->static_variables);
|
|
|
|
ht = op_array->static_variables;
|
|
|
|
UNSERIALIZE_PTR(ht);
|
|
|
|
zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
if (op_array->literals) {
|
2015-05-07 04:46:49 +08:00
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(op_array->literals);
|
|
|
|
p = op_array->literals;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + op_array->last_literal;
|
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_serialize_zval(p, script, info, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
{
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_op *opline, *end;
|
|
|
|
|
2017-12-11 05:33:47 +08:00
|
|
|
#if !ZEND_USE_ABS_CONST_ADDR
|
|
|
|
zval *literals = op_array->literals;
|
|
|
|
UNSERIALIZE_PTR(literals);
|
|
|
|
#endif
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(op_array->opcodes);
|
|
|
|
opline = op_array->opcodes;
|
|
|
|
UNSERIALIZE_PTR(opline);
|
|
|
|
end = opline + op_array->last;
|
|
|
|
while (opline < end) {
|
2016-04-09 16:43:28 +08:00
|
|
|
#if ZEND_USE_ABS_CONST_ADDR
|
2015-11-12 07:02:24 +08:00
|
|
|
if (opline->op1_type == IS_CONST) {
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(opline->op1.zv);
|
|
|
|
}
|
2015-11-12 07:02:24 +08:00
|
|
|
if (opline->op2_type == IS_CONST) {
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(opline->op2.zv);
|
|
|
|
}
|
2017-10-04 21:53:01 +08:00
|
|
|
#else
|
|
|
|
if (opline->op1_type == IS_CONST) {
|
2017-12-11 05:33:47 +08:00
|
|
|
opline->op1.constant = RT_CONSTANT(opline, opline->op1) - literals;
|
2017-10-04 21:53:01 +08:00
|
|
|
}
|
|
|
|
if (opline->op2_type == IS_CONST) {
|
2017-12-11 05:33:47 +08:00
|
|
|
opline->op2.constant = RT_CONSTANT(opline, opline->op2) - literals;
|
2017-10-04 21:53:01 +08:00
|
|
|
}
|
2016-04-09 16:43:28 +08:00
|
|
|
#endif
|
|
|
|
#if ZEND_USE_ABS_JMP_ADDR
|
2015-05-07 04:46:49 +08:00
|
|
|
switch (opline->opcode) {
|
|
|
|
case ZEND_JMP:
|
|
|
|
case ZEND_FAST_CALL:
|
|
|
|
SERIALIZE_PTR(opline->op1.jmp_addr);
|
|
|
|
break;
|
|
|
|
case ZEND_JMPZ:
|
|
|
|
case ZEND_JMPNZ:
|
|
|
|
case ZEND_JMPZ_EX:
|
|
|
|
case ZEND_JMPNZ_EX:
|
|
|
|
case ZEND_JMP_SET:
|
|
|
|
case ZEND_COALESCE:
|
|
|
|
case ZEND_FE_RESET_R:
|
|
|
|
case ZEND_FE_RESET_RW:
|
|
|
|
case ZEND_ASSERT_CHECK:
|
2020-05-24 18:42:48 +08:00
|
|
|
case ZEND_JMP_NULL:
|
2022-08-11 23:42:54 +08:00
|
|
|
case ZEND_BIND_INIT_STATIC_OR_JMP:
|
2024-02-07 21:02:27 +08:00
|
|
|
case ZEND_JMP_FRAMELESS:
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(opline->op2.jmp_addr);
|
|
|
|
break;
|
2018-02-01 03:39:30 +08:00
|
|
|
case ZEND_CATCH:
|
2018-02-06 00:41:47 +08:00
|
|
|
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
2018-02-01 03:39:30 +08:00
|
|
|
SERIALIZE_PTR(opline->op2.jmp_addr);
|
|
|
|
}
|
|
|
|
break;
|
2015-05-13 17:55:42 +08:00
|
|
|
case ZEND_FE_FETCH_R:
|
|
|
|
case ZEND_FE_FETCH_RW:
|
2017-03-18 06:45:05 +08:00
|
|
|
case ZEND_SWITCH_LONG:
|
|
|
|
case ZEND_SWITCH_STRING:
|
2020-04-10 04:36:37 +08:00
|
|
|
case ZEND_MATCH:
|
2015-05-13 17:55:42 +08:00
|
|
|
/* relative extended_value don't have to be changed */
|
|
|
|
break;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
2016-04-09 16:43:28 +08:00
|
|
|
#endif
|
2016-03-18 03:00:27 +08:00
|
|
|
zend_serialize_opcode_handler(opline);
|
2015-05-07 04:46:49 +08:00
|
|
|
opline++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op_array->arg_info) {
|
|
|
|
zend_arg_info *p, *end;
|
|
|
|
SERIALIZE_PTR(op_array->arg_info);
|
|
|
|
p = op_array->arg_info;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + op_array->num_args;
|
|
|
|
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
while (p < end) {
|
|
|
|
if (!IS_SERIALIZED(p->name)) {
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(p->name);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
2019-09-25 19:21:13 +08:00
|
|
|
zend_file_cache_serialize_type(&p->type, script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op_array->vars) {
|
|
|
|
zend_string **p, **end;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(op_array->vars);
|
|
|
|
p = op_array->vars;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + op_array->last_var;
|
|
|
|
while (p < end) {
|
|
|
|
if (!IS_SERIALIZED(*p)) {
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(*p);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
2020-05-18 21:46:06 +08:00
|
|
|
if (op_array->num_dynamic_func_defs) {
|
|
|
|
zend_op_array **defs;
|
|
|
|
SERIALIZE_PTR(op_array->dynamic_func_defs);
|
|
|
|
defs = op_array->dynamic_func_defs;
|
|
|
|
UNSERIALIZE_PTR(defs);
|
|
|
|
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
|
|
|
|
zend_op_array *def;
|
|
|
|
SERIALIZE_PTR(defs[i]);
|
|
|
|
def = defs[i];
|
|
|
|
UNSERIALIZE_PTR(def);
|
|
|
|
zend_file_cache_serialize_op_array(def, script, info, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(op_array->function_name);
|
|
|
|
SERIALIZE_STR(op_array->filename);
|
2015-11-11 02:48:03 +08:00
|
|
|
SERIALIZE_PTR(op_array->live_range);
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(op_array->scope);
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(op_array->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
SERIALIZE_ATTRIBUTES(op_array->attributes);
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(op_array->try_catch_array);
|
|
|
|
SERIALIZE_PTR(op_array->prototype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_serialize_func(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
2020-06-11 05:10:18 +08:00
|
|
|
zend_function *func;
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(Z_PTR_P(zv));
|
2020-06-11 05:10:18 +08:00
|
|
|
func = Z_PTR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(func);
|
|
|
|
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
|
|
|
|
zend_file_cache_serialize_op_array(&func->op_array, script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_serialize_prop_info(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
if (!IS_SERIALIZED(Z_PTR_P(zv))) {
|
|
|
|
zend_property_info *prop;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
prop = Z_PTR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(prop);
|
|
|
|
|
2018-05-03 17:10:33 +08:00
|
|
|
ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
|
|
|
|
if (!IS_SERIALIZED(prop->ce)) {
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(prop->ce);
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(prop->name);
|
2018-05-03 17:10:33 +08:00
|
|
|
if (prop->doc_comment) {
|
|
|
|
SERIALIZE_STR(prop->doc_comment);
|
|
|
|
}
|
2020-05-25 02:57:00 +08:00
|
|
|
SERIALIZE_ATTRIBUTES(prop->attributes);
|
2019-09-25 19:21:13 +08:00
|
|
|
zend_file_cache_serialize_type(&prop->type, script, info, buf);
|
2019-01-07 19:28:51 +08:00
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 17:40:42 +08:00
|
|
|
static void zend_file_cache_serialize_class_constant(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
if (!IS_SERIALIZED(Z_PTR_P(zv))) {
|
|
|
|
zend_class_constant *c;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
c = Z_PTR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(c);
|
|
|
|
|
2018-05-03 17:10:33 +08:00
|
|
|
ZEND_ASSERT(c->ce != NULL);
|
|
|
|
if (!IS_SERIALIZED(c->ce)) {
|
2015-12-08 17:40:42 +08:00
|
|
|
SERIALIZE_PTR(c->ce);
|
2018-05-03 17:10:33 +08:00
|
|
|
|
|
|
|
zend_file_cache_serialize_zval(&c->value, script, info, buf);
|
|
|
|
if (c->doc_comment) {
|
|
|
|
SERIALIZE_STR(c->doc_comment);
|
|
|
|
}
|
2020-05-25 02:57:00 +08:00
|
|
|
|
|
|
|
SERIALIZE_ATTRIBUTES(c->attributes);
|
2023-04-17 04:20:26 +08:00
|
|
|
zend_file_cache_serialize_type(&c->type, script, info, buf);
|
2015-12-08 17:40:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_serialize_class(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
zend_class_entry *ce;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
ce = Z_PTR_P(zv);
|
|
|
|
UNSERIALIZE_PTR(ce);
|
|
|
|
|
2015-05-07 16:36:01 +08:00
|
|
|
SERIALIZE_STR(ce->name);
|
2018-09-18 16:41:40 +08:00
|
|
|
if (ce->parent) {
|
|
|
|
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
|
|
|
|
SERIALIZE_STR(ce->parent_name);
|
|
|
|
} else {
|
|
|
|
SERIALIZE_PTR(ce->parent);
|
|
|
|
}
|
2018-08-24 05:20:57 +08:00
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
|
|
|
|
if (ce->default_properties_table) {
|
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(ce->default_properties_table);
|
|
|
|
p = ce->default_properties_table;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
end = p + ce->default_properties_count;
|
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_serialize_zval(p, script, info, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ce->default_static_members_table) {
|
2020-05-20 16:55:36 +08:00
|
|
|
zval *p, *end;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
SERIALIZE_PTR(ce->default_static_members_table);
|
2020-05-20 16:55:36 +08:00
|
|
|
p = ce->default_static_members_table;
|
|
|
|
UNSERIALIZE_PTR(p);
|
2018-06-22 18:58:48 +08:00
|
|
|
|
2020-05-20 16:55:36 +08:00
|
|
|
end = p + ce->default_static_members_count;
|
2015-05-07 04:46:49 +08:00
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_serialize_zval(p, script, info, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2015-12-08 17:40:42 +08:00
|
|
|
zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant);
|
2015-11-12 07:02:24 +08:00
|
|
|
SERIALIZE_STR(ce->info.user.filename);
|
2024-02-25 15:41:31 +08:00
|
|
|
SERIALIZE_STR(ce->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
SERIALIZE_ATTRIBUTES(ce->attributes);
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
|
|
|
|
|
2019-01-07 19:28:51 +08:00
|
|
|
if (ce->properties_info_table) {
|
|
|
|
uint32_t i;
|
|
|
|
zend_property_info **table;
|
|
|
|
|
|
|
|
SERIALIZE_PTR(ce->properties_info_table);
|
|
|
|
table = ce->properties_info_table;
|
|
|
|
UNSERIALIZE_PTR(table);
|
|
|
|
|
|
|
|
for (i = 0; i < ce->default_properties_count; i++) {
|
|
|
|
SERIALIZE_PTR(table[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (ce->num_interfaces) {
|
|
|
|
uint32_t i;
|
|
|
|
zend_class_name *interface_names;
|
|
|
|
|
2018-09-18 16:41:40 +08:00
|
|
|
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
|
2018-08-23 22:16:28 +08:00
|
|
|
|
|
|
|
SERIALIZE_PTR(ce->interface_names);
|
|
|
|
interface_names = ce->interface_names;
|
|
|
|
UNSERIALIZE_PTR(interface_names);
|
|
|
|
|
|
|
|
for (i = 0; i < ce->num_interfaces; i++) {
|
|
|
|
SERIALIZE_STR(interface_names[i].name);
|
|
|
|
SERIALIZE_STR(interface_names[i].lc_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
if (ce->num_traits) {
|
|
|
|
uint32_t i;
|
|
|
|
zend_class_name *trait_names;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
SERIALIZE_PTR(ce->trait_names);
|
|
|
|
trait_names = ce->trait_names;
|
|
|
|
UNSERIALIZE_PTR(trait_names);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
for (i = 0; i < ce->num_traits; i++) {
|
|
|
|
SERIALIZE_STR(trait_names[i].name);
|
|
|
|
SERIALIZE_STR(trait_names[i].lc_name);
|
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
if (ce->trait_aliases) {
|
|
|
|
zend_trait_alias **p, *q;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
SERIALIZE_PTR(ce->trait_aliases);
|
|
|
|
p = ce->trait_aliases;
|
|
|
|
UNSERIALIZE_PTR(p);
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
SERIALIZE_PTR(*p);
|
|
|
|
q = *p;
|
|
|
|
UNSERIALIZE_PTR(q);
|
|
|
|
|
|
|
|
if (q->trait_method.method_name) {
|
|
|
|
SERIALIZE_STR(q->trait_method.method_name);
|
|
|
|
}
|
|
|
|
if (q->trait_method.class_name) {
|
|
|
|
SERIALIZE_STR(q->trait_method.class_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (q->alias) {
|
|
|
|
SERIALIZE_STR(q->alias);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
if (ce->trait_precedences) {
|
|
|
|
zend_trait_precedence **p, *q;
|
2019-01-09 05:08:40 +08:00
|
|
|
uint32_t j;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
SERIALIZE_PTR(ce->trait_precedences);
|
|
|
|
p = ce->trait_precedences;
|
|
|
|
UNSERIALIZE_PTR(p);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
while (*p) {
|
|
|
|
SERIALIZE_PTR(*p);
|
|
|
|
q = *p;
|
|
|
|
UNSERIALIZE_PTR(q);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
if (q->trait_method.method_name) {
|
|
|
|
SERIALIZE_STR(q->trait_method.method_name);
|
|
|
|
}
|
|
|
|
if (q->trait_method.class_name) {
|
|
|
|
SERIALIZE_STR(q->trait_method.class_name);
|
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
for (j = 0; j < q->num_excludes; j++) {
|
|
|
|
SERIALIZE_STR(q->exclude_class_names[j]);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SERIALIZE_PTR(ce->constructor);
|
|
|
|
SERIALIZE_PTR(ce->destructor);
|
|
|
|
SERIALIZE_PTR(ce->clone);
|
|
|
|
SERIALIZE_PTR(ce->__get);
|
|
|
|
SERIALIZE_PTR(ce->__set);
|
|
|
|
SERIALIZE_PTR(ce->__call);
|
2020-06-26 16:54:40 +08:00
|
|
|
SERIALIZE_PTR(ce->__serialize);
|
|
|
|
SERIALIZE_PTR(ce->__unserialize);
|
2015-05-07 04:46:49 +08:00
|
|
|
SERIALIZE_PTR(ce->__isset);
|
|
|
|
SERIALIZE_PTR(ce->__unset);
|
|
|
|
SERIALIZE_PTR(ce->__tostring);
|
|
|
|
SERIALIZE_PTR(ce->__callstatic);
|
|
|
|
SERIALIZE_PTR(ce->__debugInfo);
|
2018-10-17 20:52:50 +08:00
|
|
|
|
|
|
|
if (ce->iterator_funcs_ptr) {
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
|
|
|
|
SERIALIZE_PTR(ce->iterator_funcs_ptr);
|
|
|
|
}
|
|
|
|
|
2021-12-02 07:43:37 +08:00
|
|
|
if (ce->arrayaccess_funcs_ptr) {
|
|
|
|
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
|
|
|
|
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
|
|
|
|
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
|
|
|
|
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
|
|
|
|
SERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
|
|
|
|
}
|
|
|
|
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
|
|
|
|
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
|
2023-10-11 14:57:18 +08:00
|
|
|
|
|
|
|
ce->inheritance_cache = NULL;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
2020-05-19 21:01:18 +08:00
|
|
|
static void zend_file_cache_serialize_warnings(
|
|
|
|
zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
|
|
|
|
{
|
|
|
|
if (script->warnings) {
|
2021-04-29 17:50:54 +08:00
|
|
|
zend_error_info **warnings;
|
2020-05-19 21:01:18 +08:00
|
|
|
SERIALIZE_PTR(script->warnings);
|
|
|
|
warnings = script->warnings;
|
|
|
|
UNSERIALIZE_PTR(warnings);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < script->num_warnings; i++) {
|
2021-04-29 17:50:54 +08:00
|
|
|
zend_error_info *warning;
|
2020-05-19 21:01:18 +08:00
|
|
|
SERIALIZE_PTR(warnings[i]);
|
|
|
|
warning = warnings[i];
|
|
|
|
UNSERIALIZE_PTR(warning);
|
2021-04-29 17:50:54 +08:00
|
|
|
SERIALIZE_STR(warning->filename);
|
|
|
|
SERIALIZE_STR(warning->message);
|
2020-05-19 21:01:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-27 18:21:40 +08:00
|
|
|
static void zend_file_cache_serialize_early_bindings(
|
|
|
|
zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
|
|
|
|
{
|
|
|
|
if (script->early_bindings) {
|
|
|
|
SERIALIZE_PTR(script->early_bindings);
|
|
|
|
zend_early_binding *early_bindings = script->early_bindings;
|
|
|
|
UNSERIALIZE_PTR(early_bindings);
|
|
|
|
for (uint32_t i = 0; i < script->num_early_bindings; i++) {
|
|
|
|
SERIALIZE_STR(early_bindings[i].lcname);
|
|
|
|
SERIALIZE_STR(early_bindings[i].rtd_key);
|
|
|
|
SERIALIZE_STR(early_bindings[i].lc_parent_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_serialize(zend_persistent_script *script,
|
|
|
|
zend_file_cache_metainfo *info,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
zend_persistent_script *new_script;
|
|
|
|
|
|
|
|
memcpy(info->magic, "OPCACHE", 8);
|
2020-07-17 07:31:10 +08:00
|
|
|
memcpy(info->system_id, zend_system_id, 32);
|
2015-05-07 04:46:49 +08:00
|
|
|
info->mem_size = script->size;
|
|
|
|
info->str_size = 0;
|
|
|
|
info->script_offset = (char*)script - (char*)script->mem;
|
|
|
|
info->timestamp = script->timestamp;
|
|
|
|
|
|
|
|
memcpy(buf, script->mem, script->size);
|
|
|
|
|
|
|
|
new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
|
2015-11-12 07:02:24 +08:00
|
|
|
SERIALIZE_STR(new_script->script.filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2015-11-12 07:02:24 +08:00
|
|
|
zend_file_cache_serialize_hash(&new_script->script.class_table, script, info, buf, zend_file_cache_serialize_class);
|
|
|
|
zend_file_cache_serialize_hash(&new_script->script.function_table, script, info, buf, zend_file_cache_serialize_func);
|
|
|
|
zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
|
2020-05-19 21:01:18 +08:00
|
|
|
zend_file_cache_serialize_warnings(new_script, info, buf);
|
2021-09-27 18:21:40 +08:00
|
|
|
zend_file_cache_serialize_early_bindings(new_script, info, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
new_script->mem = NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-17 21:14:25 +08:00
|
|
|
static char *zend_file_cache_get_bin_file_path(zend_string *script_path)
|
2015-05-07 04:46:49 +08:00
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
char *filename;
|
|
|
|
|
2018-03-05 22:16:55 +08:00
|
|
|
#ifndef ZEND_WIN32
|
2015-05-07 04:46:49 +08:00
|
|
|
len = strlen(ZCG(accel_directives).file_cache);
|
2015-07-17 21:14:25 +08:00
|
|
|
filename = emalloc(len + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
|
2015-05-07 04:46:49 +08:00
|
|
|
memcpy(filename, ZCG(accel_directives).file_cache, len);
|
|
|
|
filename[len] = '/';
|
2020-07-17 07:31:10 +08:00
|
|
|
memcpy(filename + len + 1, zend_system_id, 32);
|
2015-07-17 21:14:25 +08:00
|
|
|
memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
|
|
|
|
memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
|
2015-07-17 19:50:40 +08:00
|
|
|
#else
|
2018-03-05 22:16:55 +08:00
|
|
|
len = strlen(ZCG(accel_directives).file_cache);
|
|
|
|
|
|
|
|
filename = emalloc(len + 33 + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
|
|
|
|
|
|
|
|
memcpy(filename, ZCG(accel_directives).file_cache, len);
|
|
|
|
filename[len] = '\\';
|
Allow multiple cache instances per user/host on Windows
Formerly, there was at most a single OPcache instance per user and the
so called system ID (which is determined from the PHP version).
Sometimes multiple OPcaches might be desired, though, particularly for
unrelated CLI scripts, which may even be necessary (e.g. for our test
suite in parallel mode).
We therefore introduce a new INI directive `opcache.cache_id` which
allows to configure independent OPcache instances for the same user.
We also use `GetUserNameW()` instead of `php_win32_get_username()`,
because the latter retrieves the user name encoded in the
`default_charset`, which can obviously yield different results for
different charsets, leading to OPcache "incompatibilities". Slightly
worse, some characters may not even be encodeable in the
`default_charset` and would be replaced by question marks, which could
result in different users sharing the same OPcache.
We also refactor, and re-use existing APIs to avoid duplicated code.
2019-07-17 03:14:57 +08:00
|
|
|
memcpy(filename + 1 + len, accel_uname_id, 32);
|
2019-06-20 23:53:10 +08:00
|
|
|
len += 1 + 32;
|
2015-07-17 19:50:40 +08:00
|
|
|
filename[len] = '\\';
|
2018-03-05 22:16:55 +08:00
|
|
|
|
2020-07-17 07:31:10 +08:00
|
|
|
memcpy(filename + len + 1, zend_system_id, 32);
|
2019-04-10 20:55:15 +08:00
|
|
|
|
|
|
|
if (ZSTR_LEN(script_path) >= 7 && ':' == ZSTR_VAL(script_path)[4] && '/' == ZSTR_VAL(script_path)[5] && '/' == ZSTR_VAL(script_path)[6]) {
|
|
|
|
/* phar:// or file:// */
|
|
|
|
*(filename + len + 33) = '\\';
|
|
|
|
memcpy(filename + len + 34, ZSTR_VAL(script_path), 4);
|
|
|
|
if (ZSTR_LEN(script_path) - 7 >= 2 && ':' == ZSTR_VAL(script_path)[8]) {
|
|
|
|
*(filename + len + 38) = '\\';
|
|
|
|
*(filename + len + 39) = ZSTR_VAL(script_path)[7];
|
|
|
|
memcpy(filename + len + 40, ZSTR_VAL(script_path) + 9, ZSTR_LEN(script_path) - 9);
|
|
|
|
memcpy(filename + len + 40 + ZSTR_LEN(script_path) - 9, SUFFIX, sizeof(SUFFIX));
|
|
|
|
} else {
|
|
|
|
memcpy(filename + len + 38, ZSTR_VAL(script_path) + 7, ZSTR_LEN(script_path) - 7);
|
|
|
|
memcpy(filename + len + 38 + ZSTR_LEN(script_path) - 7, SUFFIX, sizeof(SUFFIX));
|
|
|
|
}
|
|
|
|
} else if (ZSTR_LEN(script_path) >= 2 && ':' == ZSTR_VAL(script_path)[1]) {
|
2015-07-17 19:50:40 +08:00
|
|
|
/* local fs */
|
|
|
|
*(filename + len + 33) = '\\';
|
2015-07-17 21:14:25 +08:00
|
|
|
*(filename + len + 34) = ZSTR_VAL(script_path)[0];
|
|
|
|
memcpy(filename + len + 35, ZSTR_VAL(script_path) + 2, ZSTR_LEN(script_path) - 2);
|
|
|
|
memcpy(filename + len + 35 + ZSTR_LEN(script_path) - 2, SUFFIX, sizeof(SUFFIX));
|
2015-07-17 19:50:40 +08:00
|
|
|
} else {
|
|
|
|
/* network path */
|
2015-07-17 21:14:25 +08:00
|
|
|
memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
|
|
|
|
memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
|
2015-07-17 19:50:40 +08:00
|
|
|
}
|
|
|
|
#endif
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2015-07-17 21:14:25 +08:00
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
/**
|
|
|
|
* Helper function for zend_file_cache_script_store().
|
|
|
|
*
|
2022-09-03 17:23:43 +08:00
|
|
|
* @return true on success, false on error and errno is set to indicate the cause of the error
|
2022-03-24 22:03:53 +08:00
|
|
|
*/
|
|
|
|
static bool zend_file_cache_script_write(int fd, const zend_persistent_script *script, const zend_file_cache_metainfo *info, const void *buf, const zend_string *s)
|
|
|
|
{
|
2022-09-03 17:23:43 +08:00
|
|
|
ssize_t written;
|
|
|
|
const ssize_t total_size = (ssize_t)(sizeof(*info) + script->size + info->str_size);
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
#ifdef HAVE_SYS_UIO_H
|
|
|
|
const struct iovec vec[] = {
|
|
|
|
{ .iov_base = (void *)info, .iov_len = sizeof(*info) },
|
|
|
|
{ .iov_base = (void *)buf, .iov_len = script->size },
|
|
|
|
{ .iov_base = (void *)ZSTR_VAL(s), .iov_len = info->str_size },
|
|
|
|
};
|
|
|
|
|
2022-09-03 17:23:43 +08:00
|
|
|
written = writev(fd, vec, sizeof(vec) / sizeof(vec[0]));
|
|
|
|
if (EXPECTED(written == total_size)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = written == -1 ? errno : EAGAIN;
|
|
|
|
return false;
|
2022-03-24 22:03:53 +08:00
|
|
|
#else
|
2022-09-03 17:23:43 +08:00
|
|
|
if (UNEXPECTED(ZEND_LONG_MAX < (zend_long)total_size)) {
|
|
|
|
# ifdef EFBIG
|
|
|
|
errno = EFBIG;
|
|
|
|
# else
|
|
|
|
errno = ERANGE;
|
|
|
|
# endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
written = write(fd, info, sizeof(*info));
|
|
|
|
if (UNEXPECTED(written != sizeof(*info))) {
|
|
|
|
errno = written == -1 ? errno : EAGAIN;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
written = write(fd, buf, script->size);
|
|
|
|
if (UNEXPECTED(written != script->size)) {
|
|
|
|
errno = written == -1 ? errno : EAGAIN;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
written = write(fd, ZSTR_VAL(s), info->str_size);
|
|
|
|
if (UNEXPECTED(written != info->str_size)) {
|
|
|
|
errno = written == -1 ? errno : EAGAIN;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2022-03-24 22:03:53 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm)
|
2015-07-17 21:14:25 +08:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
char *filename;
|
|
|
|
zend_file_cache_metainfo info;
|
|
|
|
void *mem, *buf;
|
|
|
|
|
2019-04-11 18:52:50 +08:00
|
|
|
#ifdef HAVE_JIT
|
2019-04-01 15:27:11 +08:00
|
|
|
/* FIXME: dump jited codes out to file cache? */
|
2020-05-18 15:23:06 +08:00
|
|
|
if (JIT_G(on)) {
|
2019-04-01 15:27:11 +08:00
|
|
|
return FAILURE;
|
|
|
|
}
|
2019-04-11 18:52:50 +08:00
|
|
|
#endif
|
2019-04-01 15:27:11 +08:00
|
|
|
|
2015-11-12 07:02:24 +08:00
|
|
|
filename = zend_file_cache_get_bin_file_path(script->script.filename);
|
2015-07-17 21:14:25 +08:00
|
|
|
|
|
|
|
if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
|
2018-07-11 20:34:18 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s', %s\n", filename, strerror(errno));
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2018-07-14 18:51:37 +08:00
|
|
|
fd = zend_file_cache_open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
|
2015-05-07 04:46:49 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
if (errno != EEXIST) {
|
2018-07-11 20:34:18 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s', %s\n", filename, strerror(errno));
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
efree(filename);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
|
|
|
|
close(fd);
|
|
|
|
efree(filename);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2018-02-15 00:18:47 +08:00
|
|
|
#if defined(__AVX__) || defined(__SSE2__)
|
2015-05-07 04:46:49 +08:00
|
|
|
/* Align to 64-byte boundary */
|
|
|
|
mem = emalloc(script->size + 64);
|
2023-02-19 03:31:28 +08:00
|
|
|
buf = (void*)(((uintptr_t)mem + 63L) & ~63L);
|
2015-05-07 04:46:49 +08:00
|
|
|
#else
|
|
|
|
mem = buf = emalloc(script->size);
|
|
|
|
#endif
|
|
|
|
|
2015-06-29 21:44:54 +08:00
|
|
|
ZCG(mem) = zend_string_alloc(4096 - (_ZSTR_HEADER_SIZE + 1), 0);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
zend_shared_alloc_init_xlat_table();
|
2015-05-21 04:33:55 +08:00
|
|
|
if (!in_shm) {
|
2022-03-24 22:03:53 +08:00
|
|
|
script->corrupted = true; /* used to check if script restored to SHM or process memory */
|
2015-05-21 04:33:55 +08:00
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_serialize(script, &info, buf);
|
2015-05-21 04:33:55 +08:00
|
|
|
if (!in_shm) {
|
2022-03-24 22:03:53 +08:00
|
|
|
script->corrupted = false;
|
2015-05-21 04:33:55 +08:00
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_shared_alloc_destroy_xlat_table();
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
zend_string *const s = (zend_string*)ZCG(mem);
|
|
|
|
|
2019-07-01 17:02:31 +08:00
|
|
|
#if __has_feature(memory_sanitizer)
|
|
|
|
/* The buffer may contain uninitialized regions. However, the uninitialized parts will not be
|
|
|
|
* used when reading the cache. We should probably still try to get things fully initialized
|
|
|
|
* for reproducibility, but for now ignore this issue. */
|
|
|
|
__msan_unpoison(&info, sizeof(info));
|
|
|
|
__msan_unpoison(buf, script->size);
|
|
|
|
#endif
|
|
|
|
|
2023-08-03 01:23:54 +08:00
|
|
|
info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
|
|
|
|
info.checksum = zend_adler32(info.checksum, (unsigned char*)ZSTR_VAL(s), info.str_size);
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
if (!zend_file_cache_script_write(fd, script, &info, buf, s)) {
|
2022-09-03 17:23:43 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s': %s\n", filename, strerror(errno));
|
2022-03-24 22:03:53 +08:00
|
|
|
zend_string_release_ex(s, 0);
|
2016-06-27 14:42:49 +08:00
|
|
|
close(fd);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(mem);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
zend_string_release_ex(s, 0);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(mem);
|
|
|
|
if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
|
2022-09-03 17:23:43 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s': %s\n", filename, strerror(errno));
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
efree(filename);
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_unserialize_hash(HashTable *ht,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf,
|
2015-11-04 18:59:30 +08:00
|
|
|
unserialize_callback_t func,
|
|
|
|
dtor_func_t dtor)
|
2015-05-07 04:46:49 +08:00
|
|
|
{
|
2015-11-04 18:59:30 +08:00
|
|
|
ht->pDestructor = dtor;
|
2018-12-28 16:22:18 +08:00
|
|
|
if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
|
2018-03-12 16:18:12 +08:00
|
|
|
if (EXPECTED(!file_cache_only)) {
|
|
|
|
HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
|
|
|
|
} else {
|
|
|
|
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
|
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (IS_UNSERIALIZED(ht->arData)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
UNSERIALIZE_PTR(ht->arData);
|
2021-11-03 20:18:26 +08:00
|
|
|
if (HT_IS_PACKED(ht)) {
|
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
p = ht->arPacked;
|
|
|
|
end = p + ht->nNumUsed;
|
|
|
|
while (p < end) {
|
|
|
|
if (Z_TYPE_P(p) != IS_UNDEF) {
|
|
|
|
func(p, script, buf);
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Bucket *p, *end;
|
|
|
|
|
|
|
|
p = ht->arData;
|
|
|
|
end = p + ht->nNumUsed;
|
|
|
|
while (p < end) {
|
|
|
|
if (Z_TYPE(p->val) != IS_UNDEF) {
|
|
|
|
UNSERIALIZE_STR(p->key);
|
|
|
|
func(&p->val, script, buf);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-11 00:37:46 +08:00
|
|
|
static void zend_file_cache_unserialize_ast(zend_ast *ast,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
2015-05-07 04:46:49 +08:00
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
|
2017-10-10 15:11:05 +08:00
|
|
|
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
|
|
|
|
} else if (zend_ast_is_list(ast)) {
|
|
|
|
zend_ast_list *list = zend_ast_get_list(ast);
|
|
|
|
for (i = 0; i < list->children; i++) {
|
2018-04-28 18:26:32 +08:00
|
|
|
if (list->child[i] && !IS_UNSERIALIZED(list->child[i])) {
|
2017-10-11 00:37:46 +08:00
|
|
|
UNSERIALIZE_PTR(list->child[i]);
|
|
|
|
zend_file_cache_unserialize_ast(list->child[i], script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint32_t children = zend_ast_get_num_children(ast);
|
|
|
|
for (i = 0; i < children; i++) {
|
2018-04-28 18:26:32 +08:00
|
|
|
if (ast->child[i] && !IS_UNSERIALIZED(ast->child[i])) {
|
2017-10-11 00:37:46 +08:00
|
|
|
UNSERIALIZE_PTR(ast->child[i]);
|
|
|
|
zend_file_cache_unserialize_ast(ast->child[i], script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_unserialize_zval(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
switch (Z_TYPE_P(zv)) {
|
|
|
|
case IS_STRING:
|
2021-08-18 16:55:17 +08:00
|
|
|
/* We can't use !IS_UNSERIALIZED here, because that does not recognize unserialized
|
|
|
|
* interned strings in non-shm mode. */
|
|
|
|
if (IS_SERIALIZED(Z_STR_P(zv)) || IS_SERIALIZED_INTERNED(Z_STR_P(zv))) {
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(Z_STR_P(zv));
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IS_ARRAY:
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
|
2015-05-07 04:46:49 +08:00
|
|
|
HashTable *ht;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(Z_ARR_P(zv));
|
|
|
|
ht = Z_ARR_P(zv);
|
2015-11-04 18:59:30 +08:00
|
|
|
zend_file_cache_unserialize_hash(ht,
|
|
|
|
script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IS_CONSTANT_AST:
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(Z_AST_P(zv));
|
2017-10-09 21:57:51 +08:00
|
|
|
zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
break;
|
2020-05-20 16:55:36 +08:00
|
|
|
case IS_INDIRECT:
|
|
|
|
/* Used by static properties. */
|
|
|
|
UNSERIALIZE_PTR(Z_INDIRECT_P(zv));
|
|
|
|
break;
|
2021-08-17 23:13:04 +08:00
|
|
|
default:
|
|
|
|
ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING);
|
|
|
|
break;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 02:57:00 +08:00
|
|
|
static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_script *script, void *buf)
|
|
|
|
{
|
|
|
|
zend_attribute *attr;
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
attr = Z_PTR_P(zv);
|
|
|
|
|
|
|
|
UNSERIALIZE_STR(attr->name);
|
|
|
|
UNSERIALIZE_STR(attr->lcname);
|
|
|
|
|
|
|
|
for (i = 0; i < attr->argc; i++) {
|
2020-04-06 18:46:52 +08:00
|
|
|
UNSERIALIZE_STR(attr->args[i].name);
|
|
|
|
zend_file_cache_unserialize_zval(&attr->args[i].value, script, buf);
|
2020-05-25 02:57:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 19:21:13 +08:00
|
|
|
static void zend_file_cache_unserialize_type(
|
2021-02-10 20:22:09 +08:00
|
|
|
zend_type *type, zend_class_entry *scope, zend_persistent_script *script, void *buf)
|
2019-09-25 19:21:13 +08:00
|
|
|
{
|
|
|
|
if (ZEND_TYPE_HAS_LIST(*type)) {
|
|
|
|
zend_type_list *list = ZEND_TYPE_LIST(*type);
|
|
|
|
UNSERIALIZE_PTR(list);
|
|
|
|
ZEND_TYPE_SET_PTR(*type, list);
|
|
|
|
|
2020-01-17 00:04:11 +08:00
|
|
|
zend_type *list_type;
|
|
|
|
ZEND_TYPE_LIST_FOREACH(list, list_type) {
|
2021-02-10 20:22:09 +08:00
|
|
|
zend_file_cache_unserialize_type(list_type, scope, script, buf);
|
2019-09-25 19:21:13 +08:00
|
|
|
} ZEND_TYPE_LIST_FOREACH_END();
|
|
|
|
} else if (ZEND_TYPE_HAS_NAME(*type)) {
|
|
|
|
zend_string *type_name = ZEND_TYPE_NAME(*type);
|
|
|
|
UNSERIALIZE_STR(type_name);
|
|
|
|
ZEND_TYPE_SET_PTR(*type, type_name);
|
2021-04-09 04:37:40 +08:00
|
|
|
if (!script->corrupted) {
|
2021-08-10 16:29:20 +08:00
|
|
|
zend_accel_get_class_name_map_ptr(type_name);
|
2021-08-11 16:28:52 +08:00
|
|
|
} else {
|
|
|
|
zend_alloc_ce_cache(type_name);
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
}
|
2019-09-25 19:21:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_unserialize_op_array(zend_op_array *op_array,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
2021-04-15 22:21:17 +08:00
|
|
|
if (!script->corrupted) {
|
|
|
|
if (op_array != &script->script.main_op_array) {
|
|
|
|
op_array->fn_flags |= ZEND_ACC_IMMUTABLE;
|
|
|
|
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
|
|
|
|
} else {
|
|
|
|
ZEND_ASSERT(!(op_array->fn_flags & ZEND_ACC_IMMUTABLE));
|
|
|
|
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
|
|
|
|
}
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
if (op_array->static_variables) {
|
|
|
|
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
|
2021-10-14 17:16:18 +08:00
|
|
|
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
|
|
|
|
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
}
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
/* Check whether this op_array has already been unserialized. */
|
|
|
|
if (IS_UNSERIALIZED(op_array->opcodes)) {
|
|
|
|
ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-03 21:10:46 +08:00
|
|
|
if (op_array->refcount) {
|
|
|
|
op_array->refcount = NULL;
|
2021-03-17 19:19:09 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->static_variables);
|
2017-03-03 21:10:46 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->literals);
|
|
|
|
UNSERIALIZE_PTR(op_array->opcodes);
|
|
|
|
UNSERIALIZE_PTR(op_array->arg_info);
|
|
|
|
UNSERIALIZE_PTR(op_array->vars);
|
|
|
|
UNSERIALIZE_STR(op_array->function_name);
|
|
|
|
UNSERIALIZE_STR(op_array->filename);
|
2017-03-03 21:12:41 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->live_range);
|
2017-03-03 21:10:46 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->scope);
|
|
|
|
UNSERIALIZE_STR(op_array->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
UNSERIALIZE_ATTRIBUTES(op_array->attributes);
|
2017-03-03 21:10:46 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->try_catch_array);
|
|
|
|
UNSERIALIZE_PTR(op_array->prototype);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-17 19:19:09 +08:00
|
|
|
if (op_array->static_variables) {
|
|
|
|
HashTable *ht;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(op_array->static_variables);
|
|
|
|
ht = op_array->static_variables;
|
|
|
|
zend_file_cache_unserialize_hash(ht,
|
|
|
|
script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
if (op_array->literals) {
|
2015-05-07 04:46:49 +08:00
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(op_array->literals);
|
|
|
|
p = op_array->literals;
|
|
|
|
end = p + op_array->last_literal;
|
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_unserialize_zval(p, script, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:42:13 +08:00
|
|
|
{
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_op *opline, *end;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(op_array->opcodes);
|
|
|
|
opline = op_array->opcodes;
|
|
|
|
end = opline + op_array->last;
|
|
|
|
while (opline < end) {
|
2017-10-04 21:53:01 +08:00
|
|
|
#if ZEND_USE_ABS_CONST_ADDR
|
2015-11-12 07:02:24 +08:00
|
|
|
if (opline->op1_type == IS_CONST) {
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(opline->op1.zv);
|
|
|
|
}
|
2015-11-12 07:02:24 +08:00
|
|
|
if (opline->op2_type == IS_CONST) {
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(opline->op2.zv);
|
|
|
|
}
|
2017-10-04 21:53:01 +08:00
|
|
|
#else
|
|
|
|
if (opline->op1_type == IS_CONST) {
|
|
|
|
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
|
|
|
|
}
|
|
|
|
if (opline->op2_type == IS_CONST) {
|
|
|
|
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if ZEND_USE_ABS_JMP_ADDR
|
2015-05-07 04:46:49 +08:00
|
|
|
switch (opline->opcode) {
|
|
|
|
case ZEND_JMP:
|
|
|
|
case ZEND_FAST_CALL:
|
|
|
|
UNSERIALIZE_PTR(opline->op1.jmp_addr);
|
|
|
|
break;
|
|
|
|
case ZEND_JMPZ:
|
|
|
|
case ZEND_JMPNZ:
|
|
|
|
case ZEND_JMPZ_EX:
|
|
|
|
case ZEND_JMPNZ_EX:
|
|
|
|
case ZEND_JMP_SET:
|
|
|
|
case ZEND_COALESCE:
|
|
|
|
case ZEND_FE_RESET_R:
|
|
|
|
case ZEND_FE_RESET_RW:
|
|
|
|
case ZEND_ASSERT_CHECK:
|
2020-05-24 18:42:48 +08:00
|
|
|
case ZEND_JMP_NULL:
|
2022-08-11 23:42:54 +08:00
|
|
|
case ZEND_BIND_INIT_STATIC_OR_JMP:
|
2024-02-07 21:02:27 +08:00
|
|
|
case ZEND_JMP_FRAMELESS:
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(opline->op2.jmp_addr);
|
|
|
|
break;
|
2018-02-01 03:39:30 +08:00
|
|
|
case ZEND_CATCH:
|
2018-02-06 00:41:47 +08:00
|
|
|
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
2018-02-01 03:39:30 +08:00
|
|
|
UNSERIALIZE_PTR(opline->op2.jmp_addr);
|
|
|
|
}
|
|
|
|
break;
|
2015-05-13 17:55:42 +08:00
|
|
|
case ZEND_FE_FETCH_R:
|
|
|
|
case ZEND_FE_FETCH_RW:
|
2017-03-18 06:45:05 +08:00
|
|
|
case ZEND_SWITCH_LONG:
|
|
|
|
case ZEND_SWITCH_STRING:
|
2015-05-13 17:55:42 +08:00
|
|
|
/* relative extended_value don't have to be changed */
|
|
|
|
break;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
2017-10-04 21:53:01 +08:00
|
|
|
#endif
|
2016-03-18 03:00:27 +08:00
|
|
|
zend_deserialize_opcode_handler(opline);
|
2015-05-07 04:46:49 +08:00
|
|
|
opline++;
|
|
|
|
}
|
|
|
|
|
2021-02-11 04:55:59 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->scope);
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
if (op_array->arg_info) {
|
|
|
|
zend_arg_info *p, *end;
|
|
|
|
UNSERIALIZE_PTR(op_array->arg_info);
|
|
|
|
p = op_array->arg_info;
|
|
|
|
end = p + op_array->num_args;
|
|
|
|
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
while (p < end) {
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(p->name)) {
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(p->name);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
2021-02-11 04:55:59 +08:00
|
|
|
zend_file_cache_unserialize_type(&p->type, (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op_array->vars) {
|
|
|
|
zend_string **p, **end;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(op_array->vars);
|
|
|
|
p = op_array->vars;
|
|
|
|
end = p + op_array->last_var;
|
|
|
|
while (p < end) {
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(*p)) {
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(*p);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
2020-05-18 21:46:06 +08:00
|
|
|
if (op_array->num_dynamic_func_defs) {
|
|
|
|
UNSERIALIZE_PTR(op_array->dynamic_func_defs);
|
|
|
|
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
|
|
|
|
UNSERIALIZE_PTR(op_array->dynamic_func_defs[i]);
|
|
|
|
zend_file_cache_unserialize_op_array(op_array->dynamic_func_defs[i], script, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(op_array->function_name);
|
|
|
|
UNSERIALIZE_STR(op_array->filename);
|
2015-11-11 02:48:03 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->live_range);
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(op_array->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
UNSERIALIZE_ATTRIBUTES(op_array->attributes);
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(op_array->try_catch_array);
|
|
|
|
UNSERIALIZE_PTR(op_array->prototype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_unserialize_func(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
2020-06-11 05:10:18 +08:00
|
|
|
zend_function *func;
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(Z_PTR_P(zv));
|
2020-06-11 05:10:18 +08:00
|
|
|
func = Z_PTR_P(zv);
|
|
|
|
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
|
|
|
|
zend_file_cache_unserialize_op_array(&func->op_array, script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void zend_file_cache_unserialize_prop_info(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_property_info *prop;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
prop = Z_PTR_P(zv);
|
|
|
|
|
2018-05-03 17:10:33 +08:00
|
|
|
ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
|
|
|
|
if (!IS_UNSERIALIZED(prop->ce)) {
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(prop->ce);
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(prop->name);
|
2018-05-03 17:10:33 +08:00
|
|
|
if (prop->doc_comment) {
|
|
|
|
UNSERIALIZE_STR(prop->doc_comment);
|
|
|
|
}
|
2020-05-25 02:57:00 +08:00
|
|
|
UNSERIALIZE_ATTRIBUTES(prop->attributes);
|
2021-02-10 20:22:09 +08:00
|
|
|
zend_file_cache_unserialize_type(&prop->type, prop->ce, script, buf);
|
2019-01-07 19:28:51 +08:00
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 17:40:42 +08:00
|
|
|
static void zend_file_cache_unserialize_class_constant(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
2018-04-28 18:26:32 +08:00
|
|
|
if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
|
2015-12-08 17:40:42 +08:00
|
|
|
zend_class_constant *c;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
c = Z_PTR_P(zv);
|
|
|
|
|
2018-05-03 17:10:33 +08:00
|
|
|
ZEND_ASSERT(c->ce != NULL);
|
|
|
|
if (!IS_UNSERIALIZED(c->ce)) {
|
2015-12-08 17:40:42 +08:00
|
|
|
UNSERIALIZE_PTR(c->ce);
|
2018-05-03 17:10:33 +08:00
|
|
|
|
|
|
|
zend_file_cache_unserialize_zval(&c->value, script, buf);
|
|
|
|
|
|
|
|
if (c->doc_comment) {
|
|
|
|
UNSERIALIZE_STR(c->doc_comment);
|
|
|
|
}
|
2020-05-25 02:57:00 +08:00
|
|
|
UNSERIALIZE_ATTRIBUTES(c->attributes);
|
2023-04-17 04:20:26 +08:00
|
|
|
zend_file_cache_unserialize_type(&c->type, c->ce, script, buf);
|
2015-12-08 17:40:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_unserialize_class(zval *zv,
|
|
|
|
zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
zend_class_entry *ce;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(Z_PTR_P(zv));
|
|
|
|
ce = Z_PTR_P(zv);
|
|
|
|
|
2015-05-07 16:36:01 +08:00
|
|
|
UNSERIALIZE_STR(ce->name);
|
2021-08-11 16:28:52 +08:00
|
|
|
if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
|
|
|
|
if (!script->corrupted) {
|
|
|
|
zend_accel_get_class_name_map_ptr(ce->name);
|
|
|
|
} else {
|
|
|
|
zend_alloc_ce_cache(ce->name);
|
|
|
|
}
|
2021-04-09 04:37:40 +08:00
|
|
|
}
|
2018-09-18 16:41:40 +08:00
|
|
|
if (ce->parent) {
|
|
|
|
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
|
|
|
|
UNSERIALIZE_STR(ce->parent_name);
|
|
|
|
} else {
|
|
|
|
UNSERIALIZE_PTR(ce->parent);
|
|
|
|
}
|
2018-08-24 05:20:57 +08:00
|
|
|
}
|
2015-11-04 18:59:30 +08:00
|
|
|
zend_file_cache_unserialize_hash(&ce->function_table,
|
|
|
|
script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
|
2015-05-07 04:46:49 +08:00
|
|
|
if (ce->default_properties_table) {
|
|
|
|
zval *p, *end;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(ce->default_properties_table);
|
|
|
|
p = ce->default_properties_table;
|
|
|
|
end = p + ce->default_properties_count;
|
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_unserialize_zval(p, script, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ce->default_static_members_table) {
|
2020-05-20 16:55:36 +08:00
|
|
|
zval *p, *end;
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(ce->default_static_members_table);
|
2020-05-20 16:55:36 +08:00
|
|
|
p = ce->default_static_members_table;
|
|
|
|
end = p + ce->default_static_members_count;
|
2015-05-07 04:46:49 +08:00
|
|
|
while (p < end) {
|
|
|
|
zend_file_cache_unserialize_zval(p, script, buf);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2015-11-04 18:59:30 +08:00
|
|
|
zend_file_cache_unserialize_hash(&ce->constants_table,
|
2015-12-08 17:40:42 +08:00
|
|
|
script, buf, zend_file_cache_unserialize_class_constant, NULL);
|
2015-11-12 07:02:24 +08:00
|
|
|
UNSERIALIZE_STR(ce->info.user.filename);
|
2024-02-25 15:41:31 +08:00
|
|
|
UNSERIALIZE_STR(ce->doc_comment);
|
2020-05-25 02:57:00 +08:00
|
|
|
UNSERIALIZE_ATTRIBUTES(ce->attributes);
|
2015-11-04 18:59:30 +08:00
|
|
|
zend_file_cache_unserialize_hash(&ce->properties_info,
|
2018-03-06 05:46:19 +08:00
|
|
|
script, buf, zend_file_cache_unserialize_prop_info, NULL);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2019-01-07 19:28:51 +08:00
|
|
|
if (ce->properties_info_table) {
|
|
|
|
uint32_t i;
|
|
|
|
UNSERIALIZE_PTR(ce->properties_info_table);
|
|
|
|
|
|
|
|
for (i = 0; i < ce->default_properties_count; i++) {
|
|
|
|
UNSERIALIZE_PTR(ce->properties_info_table[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (ce->num_interfaces) {
|
|
|
|
uint32_t i;
|
|
|
|
|
2018-09-18 16:41:40 +08:00
|
|
|
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
|
2018-08-23 22:16:28 +08:00
|
|
|
UNSERIALIZE_PTR(ce->interface_names);
|
|
|
|
|
|
|
|
for (i = 0; i < ce->num_interfaces; i++) {
|
|
|
|
UNSERIALIZE_STR(ce->interface_names[i].name);
|
|
|
|
UNSERIALIZE_STR(ce->interface_names[i].lc_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 07:02:26 +08:00
|
|
|
if (ce->num_traits) {
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(ce->trait_names);
|
|
|
|
|
|
|
|
for (i = 0; i < ce->num_traits; i++) {
|
|
|
|
UNSERIALIZE_STR(ce->trait_names[i].name);
|
|
|
|
UNSERIALIZE_STR(ce->trait_names[i].lc_name);
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (ce->trait_aliases) {
|
|
|
|
zend_trait_alias **p, *q;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
UNSERIALIZE_PTR(ce->trait_aliases);
|
|
|
|
p = ce->trait_aliases;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
while (*p) {
|
|
|
|
UNSERIALIZE_PTR(*p);
|
|
|
|
q = *p;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (q->trait_method.method_name) {
|
|
|
|
UNSERIALIZE_STR(q->trait_method.method_name);
|
|
|
|
}
|
|
|
|
if (q->trait_method.class_name) {
|
|
|
|
UNSERIALIZE_STR(q->trait_method.class_name);
|
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (q->alias) {
|
|
|
|
UNSERIALIZE_STR(q->alias);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (ce->trait_precedences) {
|
|
|
|
zend_trait_precedence **p, *q;
|
2019-01-09 05:08:40 +08:00
|
|
|
uint32_t j;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
UNSERIALIZE_PTR(ce->trait_precedences);
|
|
|
|
p = ce->trait_precedences;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
while (*p) {
|
|
|
|
UNSERIALIZE_PTR(*p);
|
|
|
|
q = *p;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
if (q->trait_method.method_name) {
|
|
|
|
UNSERIALIZE_STR(q->trait_method.method_name);
|
|
|
|
}
|
|
|
|
if (q->trait_method.class_name) {
|
|
|
|
UNSERIALIZE_STR(q->trait_method.class_name);
|
|
|
|
}
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-08-23 22:16:28 +08:00
|
|
|
for (j = 0; j < q->num_excludes; j++) {
|
|
|
|
UNSERIALIZE_STR(q->exclude_class_names[j]);
|
|
|
|
}
|
|
|
|
p++;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UNSERIALIZE_PTR(ce->constructor);
|
|
|
|
UNSERIALIZE_PTR(ce->destructor);
|
|
|
|
UNSERIALIZE_PTR(ce->clone);
|
|
|
|
UNSERIALIZE_PTR(ce->__get);
|
|
|
|
UNSERIALIZE_PTR(ce->__set);
|
|
|
|
UNSERIALIZE_PTR(ce->__call);
|
2020-06-26 16:54:40 +08:00
|
|
|
UNSERIALIZE_PTR(ce->__serialize);
|
|
|
|
UNSERIALIZE_PTR(ce->__unserialize);
|
2015-05-07 04:46:49 +08:00
|
|
|
UNSERIALIZE_PTR(ce->__isset);
|
|
|
|
UNSERIALIZE_PTR(ce->__unset);
|
|
|
|
UNSERIALIZE_PTR(ce->__tostring);
|
|
|
|
UNSERIALIZE_PTR(ce->__callstatic);
|
|
|
|
UNSERIALIZE_PTR(ce->__debugInfo);
|
2015-11-04 18:59:30 +08:00
|
|
|
|
2018-10-17 20:52:50 +08:00
|
|
|
if (ce->iterator_funcs_ptr) {
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current);
|
|
|
|
UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
|
|
|
|
}
|
2021-12-02 07:43:37 +08:00
|
|
|
if (ce->arrayaccess_funcs_ptr) {
|
|
|
|
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
|
|
|
|
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
|
|
|
|
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
|
|
|
|
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
|
|
|
|
UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
|
|
|
|
}
|
2018-10-17 20:52:50 +08:00
|
|
|
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
if (!(script->corrupted)) {
|
|
|
|
ce->ce_flags |= ZEND_ACC_IMMUTABLE;
|
|
|
|
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
|
2021-07-22 17:41:24 +08:00
|
|
|
ZEND_MAP_PTR_NEW(ce->mutable_data);
|
|
|
|
if (ce->default_static_members_count) {
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
ZEND_MAP_PTR_NEW(ce->static_members_table);
|
|
|
|
}
|
2018-10-17 20:52:50 +08:00
|
|
|
} else {
|
Added Inheritance Cache.
This is a new transparent technology that eliminates overhead of PHP class inheritance.
PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request.
Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking).
The patch shows 8% improvement on Symphony "Hello World" app.
2021-02-10 03:53:57 +08:00
|
|
|
ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
|
|
|
|
ce->ce_flags |= ZEND_ACC_FILE_CACHED;
|
|
|
|
ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
|
2021-10-14 17:16:18 +08:00
|
|
|
ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
|
2018-10-17 20:52:50 +08:00
|
|
|
}
|
2022-09-22 20:07:51 +08:00
|
|
|
|
|
|
|
// Memory addresses of object handlers are not stable. They can change due to ASLR or order of linking dynamic. To
|
|
|
|
// avoid pointing to invalid memory we relink default_object_handlers here.
|
|
|
|
ce->default_object_handlers = ce->ce_flags & ZEND_ACC_ENUM ? &zend_enum_object_handlers : &std_object_handlers;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
2020-05-19 21:01:18 +08:00
|
|
|
static void zend_file_cache_unserialize_warnings(zend_persistent_script *script, void *buf)
|
|
|
|
{
|
|
|
|
if (script->warnings) {
|
|
|
|
UNSERIALIZE_PTR(script->warnings);
|
|
|
|
for (uint32_t i = 0; i < script->num_warnings; i++) {
|
|
|
|
UNSERIALIZE_PTR(script->warnings[i]);
|
2021-04-29 17:50:54 +08:00
|
|
|
UNSERIALIZE_STR(script->warnings[i]->filename);
|
|
|
|
UNSERIALIZE_STR(script->warnings[i]->message);
|
2020-05-19 21:01:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-27 18:21:40 +08:00
|
|
|
static void zend_file_cache_unserialize_early_bindings(zend_persistent_script *script, void *buf)
|
|
|
|
{
|
|
|
|
if (script->early_bindings) {
|
|
|
|
UNSERIALIZE_PTR(script->early_bindings);
|
|
|
|
for (uint32_t i = 0; i < script->num_early_bindings; i++) {
|
|
|
|
UNSERIALIZE_STR(script->early_bindings[i].lcname);
|
|
|
|
UNSERIALIZE_STR(script->early_bindings[i].rtd_key);
|
|
|
|
UNSERIALIZE_STR(script->early_bindings[i].lc_parent_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 04:46:49 +08:00
|
|
|
static void zend_file_cache_unserialize(zend_persistent_script *script,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
script->mem = buf;
|
|
|
|
|
2015-11-12 07:02:24 +08:00
|
|
|
UNSERIALIZE_STR(script->script.filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2015-11-12 07:02:24 +08:00
|
|
|
zend_file_cache_unserialize_hash(&script->script.class_table,
|
2015-11-04 18:59:30 +08:00
|
|
|
script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
|
2015-11-12 07:02:24 +08:00
|
|
|
zend_file_cache_unserialize_hash(&script->script.function_table,
|
2015-11-04 18:59:30 +08:00
|
|
|
script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
|
2015-11-12 07:02:24 +08:00
|
|
|
zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
|
2020-05-19 21:01:18 +08:00
|
|
|
zend_file_cache_unserialize_warnings(script, buf);
|
2021-09-27 18:21:40 +08:00
|
|
|
zend_file_cache_unserialize_early_bindings(script, buf);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
|
|
|
|
{
|
|
|
|
zend_string *full_path = file_handle->opened_path;
|
|
|
|
int fd;
|
|
|
|
char *filename;
|
|
|
|
zend_persistent_script *script;
|
|
|
|
zend_file_cache_metainfo info;
|
|
|
|
zend_accel_hash_entry *bucket;
|
2021-04-10 18:44:21 +08:00
|
|
|
void *mem, *checkpoint, *buf;
|
2022-03-24 22:03:53 +08:00
|
|
|
bool cache_it = true;
|
2019-10-09 16:38:11 +08:00
|
|
|
unsigned int actual_checksum;
|
2022-03-24 22:03:53 +08:00
|
|
|
bool ok;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
if (!full_path) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-07-17 21:14:25 +08:00
|
|
|
filename = zend_file_cache_get_bin_file_path(full_path);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-07-14 18:51:37 +08:00
|
|
|
fd = zend_file_cache_open(filename, O_RDONLY | O_BINARY);
|
2015-05-07 04:46:49 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
|
|
|
|
close(fd);
|
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read(fd, &info, sizeof(info)) != sizeof(info)) {
|
2018-07-14 01:01:43 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (info)\n", filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_flock(fd, LOCK_UN);
|
|
|
|
close(fd);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify header */
|
2015-12-09 18:34:45 +08:00
|
|
|
if (memcmp(info.magic, "OPCACHE", 8) != 0) {
|
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename);
|
|
|
|
zend_file_cache_flock(fd, LOCK_UN);
|
|
|
|
close(fd);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-12-09 18:34:45 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-07-17 07:31:10 +08:00
|
|
|
if (memcmp(info.system_id, zend_system_id, 32) != 0) {
|
2015-12-09 18:34:45 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_flock(fd, LOCK_UN);
|
|
|
|
close(fd);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify timestamp */
|
|
|
|
if (ZCG(accel_directives).validate_timestamps &&
|
|
|
|
zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
|
|
|
|
if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
|
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
|
|
|
|
}
|
|
|
|
close(fd);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-04-10 18:44:21 +08:00
|
|
|
checkpoint = zend_arena_checkpoint(CG(arena));
|
2018-02-15 00:18:47 +08:00
|
|
|
#if defined(__AVX__) || defined(__SSE2__)
|
2015-05-07 04:46:49 +08:00
|
|
|
/* Align to 64-byte boundary */
|
2021-04-10 18:44:21 +08:00
|
|
|
mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
|
2023-02-19 03:31:28 +08:00
|
|
|
mem = (void*)(((uintptr_t)mem + 63L) & ~63L);
|
2015-05-07 04:46:49 +08:00
|
|
|
#else
|
2021-04-10 18:44:21 +08:00
|
|
|
mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
|
2015-05-07 04:46:49 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
|
2018-07-14 01:01:43 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (mem)\n", filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
zend_file_cache_flock(fd, LOCK_UN);
|
|
|
|
close(fd);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2021-04-10 18:44:21 +08:00
|
|
|
zend_arena_release(&CG(arena), checkpoint);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
|
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
/* verify checksum */
|
|
|
|
if (ZCG(accel_directives).file_cache_consistency_checks &&
|
2019-10-09 16:38:11 +08:00
|
|
|
(actual_checksum = zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size)) != info.checksum) {
|
|
|
|
zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s' excepted checksum: 0x%08x actual checksum: 0x%08x\n", filename, info.checksum, actual_checksum);
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2021-04-10 18:44:21 +08:00
|
|
|
zend_arena_release(&CG(arena), checkpoint);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-03-05 21:01:43 +08:00
|
|
|
if (!file_cache_only &&
|
2016-02-25 02:15:53 +08:00
|
|
|
!ZCSG(restart_in_progress) &&
|
2023-07-27 19:54:29 +08:00
|
|
|
!ZCSG(restart_pending) &&
|
2017-12-22 04:34:21 +08:00
|
|
|
!ZSMMG(memory_exhausted) &&
|
2016-02-25 02:15:53 +08:00
|
|
|
accelerator_shm_read_lock() == SUCCESS) {
|
2015-05-07 04:46:49 +08:00
|
|
|
/* exclusive lock */
|
|
|
|
zend_shared_alloc_lock();
|
|
|
|
|
|
|
|
/* Check if we still need to put the file into the cache (may be it was
|
|
|
|
* already stored by another process. This final check is done under
|
|
|
|
* exclusive lock) */
|
|
|
|
bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
|
|
|
|
if (bucket) {
|
|
|
|
script = (zend_persistent_script *)bucket->data;
|
|
|
|
if (!script->corrupted) {
|
|
|
|
zend_shared_alloc_unlock();
|
2021-04-10 18:44:21 +08:00
|
|
|
zend_arena_release(&CG(arena), checkpoint);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
return script;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zend_accel_hash_is_full(&ZCSG(hash))) {
|
|
|
|
zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
|
|
|
|
ZSMMG(memory_exhausted) = 1;
|
|
|
|
zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
|
|
|
|
zend_shared_alloc_unlock();
|
|
|
|
goto use_process_mem;
|
|
|
|
}
|
|
|
|
|
2022-03-18 22:49:19 +08:00
|
|
|
buf = zend_shared_alloc_aligned(info.mem_size);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
|
|
|
|
zend_shared_alloc_unlock();
|
|
|
|
goto use_process_mem;
|
|
|
|
}
|
|
|
|
memcpy(buf, mem, info.mem_size);
|
2018-10-17 20:52:50 +08:00
|
|
|
zend_map_ptr_extend(ZCSG(map_ptr_last));
|
2015-05-07 04:46:49 +08:00
|
|
|
} else {
|
|
|
|
use_process_mem:
|
|
|
|
buf = mem;
|
2022-03-24 22:03:53 +08:00
|
|
|
cache_it = false;
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ZCG(mem) = ((char*)mem + info.mem_size);
|
|
|
|
script = (zend_persistent_script*)((char*)buf + info.script_offset);
|
2015-05-21 04:33:55 +08:00
|
|
|
script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */
|
2017-12-19 02:04:41 +08:00
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
ok = true;
|
2017-12-19 02:04:41 +08:00
|
|
|
zend_try {
|
|
|
|
zend_file_cache_unserialize(script, buf);
|
|
|
|
} zend_catch {
|
2022-03-24 22:03:53 +08:00
|
|
|
ok = false;
|
2017-12-19 02:04:41 +08:00
|
|
|
} zend_end_try();
|
2017-12-22 04:34:21 +08:00
|
|
|
if (!ok) {
|
|
|
|
if (cache_it) {
|
|
|
|
zend_shared_alloc_unlock();
|
|
|
|
goto use_process_mem;
|
|
|
|
} else {
|
2021-04-10 18:44:21 +08:00
|
|
|
zend_arena_release(&CG(arena), checkpoint);
|
2017-12-22 04:34:21 +08:00
|
|
|
efree(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2017-12-19 02:04:41 +08:00
|
|
|
|
2022-03-24 22:03:53 +08:00
|
|
|
script->corrupted = false;
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
if (cache_it) {
|
2018-10-17 20:52:50 +08:00
|
|
|
ZCSG(map_ptr_last) = CG(map_ptr_last);
|
2016-02-25 02:15:53 +08:00
|
|
|
script->dynamic_members.last_used = ZCG(request_time);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2021-03-22 19:56:16 +08:00
|
|
|
zend_accel_hash_update(&ZCSG(hash), script->script.filename, 0, script);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
|
|
|
zend_shared_alloc_unlock();
|
2019-10-09 16:38:11 +08:00
|
|
|
zend_accel_error(ACCEL_LOG_INFO, "File cached script loaded into memory '%s'", ZSTR_VAL(script->script.filename));
|
|
|
|
|
2021-04-10 18:44:21 +08:00
|
|
|
zend_arena_release(&CG(arena), checkpoint);
|
2015-05-07 04:46:49 +08:00
|
|
|
}
|
|
|
|
efree(filename);
|
|
|
|
|
|
|
|
return script;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zend_file_cache_invalidate(zend_string *full_path)
|
|
|
|
{
|
|
|
|
char *filename;
|
|
|
|
|
2015-07-17 21:14:25 +08:00
|
|
|
filename = zend_file_cache_get_bin_file_path(full_path);
|
2015-05-07 04:46:49 +08:00
|
|
|
|
2018-07-14 18:51:37 +08:00
|
|
|
zend_file_cache_unlink(filename);
|
2015-05-07 04:46:49 +08:00
|
|
|
efree(filename);
|
|
|
|
}
|