xref: /PHP-7.3/ext/opcache/zend_persist_calc.c (revision 9afce019)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "zend.h"
23 #include "ZendAccelerator.h"
24 #include "zend_persist.h"
25 #include "zend_extensions.h"
26 #include "zend_shared_alloc.h"
27 #include "zend_operators.h"
28 
29 #define ADD_DUP_SIZE(m,s)  ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s)
30 #define ADD_SIZE(m)        ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m)
31 
32 #define ADD_ARENA_SIZE(m)        ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m)
33 
34 # define ADD_STRING(str) ADD_DUP_SIZE((str), _ZSTR_STRUCT_SIZE(ZSTR_LEN(str)))
35 
36 # define ADD_INTERNED_STRING(str, do_free) do { \
37 		if (ZCG(current_persistent_script)->corrupted) { \
38 			ADD_STRING(str); \
39 		} else if (!IS_ACCEL_INTERNED(str)) { \
40 			zend_string *tmp = accel_new_interned_string(str); \
41 			if (tmp != (str)) { \
42 				(str) = tmp; \
43 			} else { \
44 				ADD_STRING(str); \
45 			} \
46 		} \
47 	} while (0)
48 
49 static void zend_persist_zval_calc(zval *z);
50 
zend_hash_persist_calc(HashTable * ht,void (* pPersistElement)(zval * pElement))51 static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval *pElement))
52 {
53 	uint32_t idx;
54 	Bucket *p;
55 
56 	if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED) || ht->nNumUsed == 0) {
57 		return;
58 	}
59 
60 	if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
61 		/* compact table */
62 		uint32_t hash_size;
63 
64 		if (ht->nNumUsed <= HT_MIN_SIZE) {
65 			hash_size = HT_MIN_SIZE * 2;
66 		} else {
67 			hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
68 			while (hash_size >> 2 > ht->nNumUsed) {
69 				hash_size >>= 1;
70 			}
71 		}
72 		ADD_SIZE(hash_size * sizeof(uint32_t) + ht->nNumUsed * sizeof(Bucket));
73 	} else {
74 		ADD_SIZE(HT_USED_SIZE(ht));
75 	}
76 
77 	for (idx = 0; idx < ht->nNumUsed; idx++) {
78 		p = ht->arData + idx;
79 		if (Z_TYPE(p->val) == IS_UNDEF) continue;
80 
81 		/* persist bucket and key */
82 		if (p->key) {
83 			ADD_INTERNED_STRING(p->key, 1);
84 		}
85 
86 		pPersistElement(&p->val);
87 	}
88 }
89 
zend_persist_ast_calc(zend_ast * ast)90 static void zend_persist_ast_calc(zend_ast *ast)
91 {
92 	uint32_t i;
93 
94 	if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
95 		ADD_SIZE(sizeof(zend_ast_zval));
96 		zend_persist_zval_calc(&((zend_ast_zval*)(ast))->val);
97 	} else if (zend_ast_is_list(ast)) {
98 		zend_ast_list *list = zend_ast_get_list(ast);
99 		ADD_SIZE(sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children);
100 		for (i = 0; i < list->children; i++) {
101 			if (list->child[i]) {
102 				zend_persist_ast_calc(list->child[i]);
103 			}
104 		}
105 	} else {
106 		uint32_t children = zend_ast_get_num_children(ast);
107 		ADD_SIZE(sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children);
108 		for (i = 0; i < children; i++) {
109 			if (ast->child[i]) {
110 				zend_persist_ast_calc(ast->child[i]);
111 			}
112 		}
113 	}
114 }
115 
zend_persist_zval_calc(zval * z)116 static void zend_persist_zval_calc(zval *z)
117 {
118 	uint32_t size;
119 
120 	switch (Z_TYPE_P(z)) {
121 		case IS_STRING:
122 			ADD_INTERNED_STRING(Z_STR_P(z), 0);
123 			if (ZSTR_IS_INTERNED(Z_STR_P(z))) {
124 				Z_TYPE_FLAGS_P(z) = 0;
125 			}
126 			break;
127 		case IS_ARRAY:
128 			size = zend_shared_memdup_size(Z_ARR_P(z), sizeof(zend_array));
129 			if (size) {
130 				ADD_SIZE(size);
131 				zend_hash_persist_calc(Z_ARRVAL_P(z), zend_persist_zval_calc);
132 			}
133 			break;
134 		case IS_REFERENCE:
135 			size = zend_shared_memdup_size(Z_REF_P(z), sizeof(zend_reference));
136 			if (size) {
137 				ADD_SIZE(size);
138 				zend_persist_zval_calc(Z_REFVAL_P(z));
139 			}
140 			break;
141 		case IS_CONSTANT_AST:
142 			size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref));
143 			if (size) {
144 				ADD_SIZE(size);
145 				zend_persist_ast_calc(Z_ASTVAL_P(z));
146 			}
147 			break;
148 	}
149 }
150 
zend_persist_op_array_calc_ex(zend_op_array * op_array)151 static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
152 {
153 	if (op_array->static_variables) {
154 		if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) {
155 			HashTable *old = op_array->static_variables;
156 
157 			ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable));
158 			zend_hash_persist_calc(op_array->static_variables, zend_persist_zval_calc);
159 			zend_shared_alloc_register_xlat_entry(old, op_array->static_variables);
160 		}
161 	}
162 
163 	if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
164 		/* already stored */
165 		if (op_array->function_name) {
166 			zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
167 			if (new_name) {
168 				op_array->function_name = new_name;
169 			}
170 		}
171 		return;
172 	}
173 
174 	if (op_array->literals) {
175 		zval *p = op_array->literals;
176 		zval *end = p + op_array->last_literal;
177 		ADD_DUP_SIZE(op_array->literals, sizeof(zval) * op_array->last_literal);
178 		while (p < end) {
179 			zend_persist_zval_calc(p);
180 			p++;
181 		}
182 	}
183 
184 	ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last);
185 
186 	if (op_array->function_name) {
187 		zend_string *old_name = op_array->function_name;
188 		zend_string *new_name = zend_shared_alloc_get_xlat_entry(old_name);
189 
190 		if (new_name) {
191 			op_array->function_name = new_name;
192 		} else {
193 			ADD_INTERNED_STRING(op_array->function_name, 0);
194 			zend_shared_alloc_register_xlat_entry(old_name, op_array->function_name);
195 		}
196     }
197 
198 	if (op_array->filename) {
199 		ADD_STRING(op_array->filename);
200 	}
201 
202 	if (op_array->arg_info) {
203 		zend_arg_info *arg_info = op_array->arg_info;
204 		uint32_t num_args = op_array->num_args;
205 		uint32_t i;
206 
207 		num_args = op_array->num_args;
208 		if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
209 			num_args++;
210 		}
211 		if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
212 			arg_info--;
213 			num_args++;
214 		}
215 		ADD_DUP_SIZE(arg_info, sizeof(zend_arg_info) * num_args);
216 		for (i = 0; i < num_args; i++) {
217 			if (arg_info[i].name) {
218 				ADD_INTERNED_STRING(arg_info[i].name, 1);
219 			}
220 			if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
221 				zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type);
222 				zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
223 
224 				ADD_INTERNED_STRING(type_name, 1);
225 				arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
226 			}
227 		}
228 	}
229 
230 	if (op_array->live_range) {
231 		ADD_DUP_SIZE(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
232 	}
233 
234 	if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
235 		ADD_STRING(op_array->doc_comment);
236 	}
237 
238 	if (op_array->try_catch_array) {
239 		ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
240 	}
241 
242 	if (op_array->vars) {
243 		int i;
244 
245 		ADD_DUP_SIZE(op_array->vars, sizeof(zend_string*) * op_array->last_var);
246 		for (i = 0; i < op_array->last_var; i++) {
247 			ADD_INTERNED_STRING(op_array->vars[i], 0);
248 		}
249 	}
250 
251 	ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
252 }
253 
zend_persist_op_array_calc(zval * zv)254 static void zend_persist_op_array_calc(zval *zv)
255 {
256 	zend_op_array *op_array = Z_PTR_P(zv);
257 
258 	ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
259 	ADD_SIZE(sizeof(zend_op_array));
260 	zend_persist_op_array_calc_ex(Z_PTR_P(zv));
261 }
262 
zend_persist_class_method_calc(zval * zv)263 static void zend_persist_class_method_calc(zval *zv)
264 {
265 	zend_op_array *op_array = Z_PTR_P(zv);
266 	zend_op_array *old_op_array;
267 
268 	ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
269 	old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
270 	if (old_op_array) {
271 		Z_PTR_P(zv) = old_op_array;
272 	} else {
273 		ADD_ARENA_SIZE(sizeof(zend_op_array));
274 		zend_persist_op_array_calc_ex(Z_PTR_P(zv));
275 		zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv));
276 	}
277 }
278 
zend_persist_property_info_calc(zval * zv)279 static void zend_persist_property_info_calc(zval *zv)
280 {
281 	zend_property_info *prop = Z_PTR_P(zv);
282 
283 	if (!zend_shared_alloc_get_xlat_entry(prop)) {
284 		zend_shared_alloc_register_xlat_entry(prop, prop);
285 		ADD_ARENA_SIZE(sizeof(zend_property_info));
286 		ADD_INTERNED_STRING(prop->name, 0);
287 		if (ZCG(accel_directives).save_comments && prop->doc_comment) {
288 			ADD_STRING(prop->doc_comment);
289 		}
290 	}
291 }
292 
zend_persist_class_constant_calc(zval * zv)293 static void zend_persist_class_constant_calc(zval *zv)
294 {
295 	zend_class_constant *c = Z_PTR_P(zv);
296 
297 	if (!zend_shared_alloc_get_xlat_entry(c)) {
298 		zend_shared_alloc_register_xlat_entry(c, c);
299 		ADD_ARENA_SIZE(sizeof(zend_class_constant));
300 		zend_persist_zval_calc(&c->value);
301 		if (ZCG(accel_directives).save_comments && c->doc_comment) {
302 			ADD_STRING(c->doc_comment);
303 		}
304 	}
305 }
306 
307 
zend_persist_class_entry_calc(zval * zv)308 static void zend_persist_class_entry_calc(zval *zv)
309 {
310 	zend_class_entry *ce = Z_PTR_P(zv);
311 
312 	if (ce->type == ZEND_USER_CLASS) {
313 		ADD_ARENA_SIZE(sizeof(zend_class_entry));
314 		ADD_INTERNED_STRING(ce->name, 0);
315 		zend_hash_persist_calc(&ce->function_table, zend_persist_class_method_calc);
316 		if (ce->default_properties_table) {
317 		    int i;
318 
319 			ADD_SIZE(sizeof(zval) * ce->default_properties_count);
320 			for (i = 0; i < ce->default_properties_count; i++) {
321 				zend_persist_zval_calc(&ce->default_properties_table[i]);
322 			}
323 		}
324 		if (ce->default_static_members_table) {
325 		    int i;
326 
327 			ADD_SIZE(sizeof(zval) * ce->default_static_members_count);
328 			for (i = 0; i < ce->default_static_members_count; i++) {
329 				if (Z_TYPE(ce->default_static_members_table[i]) != IS_INDIRECT) {
330 					zend_persist_zval_calc(&ce->default_static_members_table[i]);
331 				}
332 			}
333 		}
334 		zend_hash_persist_calc(&ce->constants_table, zend_persist_class_constant_calc);
335 
336 		if (ce->info.user.filename) {
337 			ADD_STRING(ce->info.user.filename);
338 		}
339 		if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
340 			ADD_STRING(ce->info.user.doc_comment);
341 		}
342 
343 		zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc);
344 
345 		if (ce->trait_aliases) {
346 			int i = 0;
347 			while (ce->trait_aliases[i]) {
348 				if (ce->trait_aliases[i]->trait_method.method_name) {
349 					ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.method_name, 0);
350 				}
351 				if (ce->trait_aliases[i]->trait_method.class_name) {
352 					ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.class_name, 0);
353 				}
354 
355 				if (ce->trait_aliases[i]->alias) {
356 					ADD_INTERNED_STRING(ce->trait_aliases[i]->alias, 0);
357 				}
358 				ADD_SIZE(sizeof(zend_trait_alias));
359 				i++;
360 			}
361 			ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1));
362 		}
363 
364 		if (ce->trait_precedences) {
365 			int i = 0;
366 			int j;
367 
368 			while (ce->trait_precedences[i]) {
369 				ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.method_name, 0);
370 				ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.class_name, 0);
371 
372 				for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
373 					ADD_INTERNED_STRING(ce->trait_precedences[i]->exclude_class_names[j], 0);
374 				}
375 				ADD_SIZE(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
376 				i++;
377 			}
378 			ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
379 		}
380 	}
381 }
382 
zend_accel_persist_class_table_calc(HashTable * class_table)383 static void zend_accel_persist_class_table_calc(HashTable *class_table)
384 {
385 	zend_hash_persist_calc(class_table, zend_persist_class_entry_calc);
386 }
387 
zend_accel_script_persist_calc(zend_persistent_script * new_persistent_script,const char * key,unsigned int key_length,int for_shm)388 uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, const char *key, unsigned int key_length, int for_shm)
389 {
390 	new_persistent_script->mem = NULL;
391 	new_persistent_script->size = 0;
392 	new_persistent_script->arena_mem = NULL;
393 	new_persistent_script->arena_size = 0;
394 	new_persistent_script->corrupted = 0;
395 	ZCG(current_persistent_script) = new_persistent_script;
396 
397 	if (!for_shm) {
398 		/* script is not going to be saved in SHM */
399 		new_persistent_script->corrupted = 1;
400 	}
401 
402 	ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script));
403 	if (key) {
404 		ADD_DUP_SIZE(key, key_length + 1);
405 	}
406 	ADD_STRING(new_persistent_script->script.filename);
407 
408 #if defined(__AVX__) || defined(__SSE2__)
409 	/* Align size to 64-byte boundary */
410 	new_persistent_script->size = (new_persistent_script->size + 63) & ~63;
411 #endif
412 
413 	if (new_persistent_script->script.class_table.nNumUsed != new_persistent_script->script.class_table.nNumOfElements) {
414 		zend_hash_rehash(&new_persistent_script->script.class_table);
415 	}
416 	zend_accel_persist_class_table_calc(&new_persistent_script->script.class_table);
417 	if (new_persistent_script->script.function_table.nNumUsed != new_persistent_script->script.function_table.nNumOfElements) {
418 		zend_hash_rehash(&new_persistent_script->script.function_table);
419 	}
420 	zend_hash_persist_calc(&new_persistent_script->script.function_table, zend_persist_op_array_calc);
421 	zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array);
422 
423 #if defined(__AVX__) || defined(__SSE2__)
424 	/* Align size to 64-byte boundary */
425 	new_persistent_script->arena_size = (new_persistent_script->arena_size + 63) & ~63;
426 #endif
427 
428 	new_persistent_script->size += new_persistent_script->arena_size;
429 	new_persistent_script->corrupted = 0;
430 
431 	ZCG(current_persistent_script) = NULL;
432 
433 	return new_persistent_script->size;
434 }
435