1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 #define ADD_ARENA_SIZE(m) ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m)
32
33 #define ADD_SIZE_EX(m) do { \
34 if (ZCG(is_immutable_class)) { \
35 ADD_SIZE(m); \
36 } else { \
37 ADD_ARENA_SIZE(m); \
38 } \
39 } while (0)
40
41 # define ADD_STRING(str) ADD_DUP_SIZE((str), _ZSTR_STRUCT_SIZE(ZSTR_LEN(str)))
42
43 # define ADD_INTERNED_STRING(str) do { \
44 if (ZCG(current_persistent_script)->corrupted) { \
45 ADD_STRING(str); \
46 } else if (!IS_ACCEL_INTERNED(str)) { \
47 zend_string *tmp = accel_new_interned_string(str); \
48 if (tmp != (str)) { \
49 (str) = tmp; \
50 } else { \
51 ADD_STRING(str); \
52 } \
53 } \
54 } while (0)
55
56 static void zend_persist_zval_calc(zval *z);
57
zend_hash_persist_calc(HashTable * ht)58 static void zend_hash_persist_calc(HashTable *ht)
59 {
60 if ((HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) || ht->nNumUsed == 0) {
61 return;
62 }
63
64 if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
65 /* compact table */
66 uint32_t hash_size;
67
68 hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
69 while (hash_size >> 2 > ht->nNumUsed) {
70 hash_size >>= 1;
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
zend_persist_ast_calc(zend_ast * ast)78 static void zend_persist_ast_calc(zend_ast *ast)
79 {
80 uint32_t i;
81
82 if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
83 ADD_SIZE(sizeof(zend_ast_zval));
84 zend_persist_zval_calc(&((zend_ast_zval*)(ast))->val);
85 } else if (zend_ast_is_list(ast)) {
86 zend_ast_list *list = zend_ast_get_list(ast);
87 ADD_SIZE(sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children);
88 for (i = 0; i < list->children; i++) {
89 if (list->child[i]) {
90 zend_persist_ast_calc(list->child[i]);
91 }
92 }
93 } else {
94 uint32_t children = zend_ast_get_num_children(ast);
95 ADD_SIZE(sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children);
96 for (i = 0; i < children; i++) {
97 if (ast->child[i]) {
98 zend_persist_ast_calc(ast->child[i]);
99 }
100 }
101 }
102 }
103
zend_persist_zval_calc(zval * z)104 static void zend_persist_zval_calc(zval *z)
105 {
106 uint32_t size;
107
108 switch (Z_TYPE_P(z)) {
109 case IS_STRING:
110 ADD_INTERNED_STRING(Z_STR_P(z));
111 if (ZSTR_IS_INTERNED(Z_STR_P(z))) {
112 Z_TYPE_FLAGS_P(z) = 0;
113 }
114 break;
115 case IS_ARRAY:
116 size = zend_shared_memdup_size(Z_ARR_P(z), sizeof(zend_array));
117 if (size) {
118 Bucket *p;
119
120 ADD_SIZE(size);
121 zend_hash_persist_calc(Z_ARRVAL_P(z));
122 ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) {
123 if (p->key) {
124 ADD_INTERNED_STRING(p->key);
125 }
126 zend_persist_zval_calc(&p->val);
127 } ZEND_HASH_FOREACH_END();
128 }
129 break;
130 case IS_REFERENCE:
131 size = zend_shared_memdup_size(Z_REF_P(z), sizeof(zend_reference));
132 if (size) {
133 ADD_SIZE(size);
134 zend_persist_zval_calc(Z_REFVAL_P(z));
135 }
136 break;
137 case IS_CONSTANT_AST:
138 size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref));
139 if (size) {
140 ADD_SIZE(size);
141 zend_persist_ast_calc(Z_ASTVAL_P(z));
142 }
143 break;
144 default:
145 ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT);
146 ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE);
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->scope && zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
154 /* already stored */
155 if (op_array->function_name) {
156 zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
157 if (new_name) {
158 op_array->function_name = new_name;
159 }
160 }
161 ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
162 return;
163 }
164
165 if (op_array->static_variables) {
166 if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) {
167 Bucket *p;
168
169 zend_shared_alloc_register_xlat_entry(op_array->static_variables, op_array->static_variables);
170 ADD_SIZE(sizeof(HashTable));
171 zend_hash_persist_calc(op_array->static_variables);
172 ZEND_HASH_FOREACH_BUCKET(op_array->static_variables, p) {
173 ZEND_ASSERT(p->key != NULL);
174 ADD_INTERNED_STRING(p->key);
175 zend_persist_zval_calc(&p->val);
176 } ZEND_HASH_FOREACH_END();
177 }
178 }
179
180 if (op_array->literals) {
181 zval *p = op_array->literals;
182 zval *end = p + op_array->last_literal;
183 ADD_SIZE(sizeof(zval) * op_array->last_literal);
184 while (p < end) {
185 zend_persist_zval_calc(p);
186 p++;
187 }
188 }
189
190 zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
191 ADD_SIZE(sizeof(zend_op) * op_array->last);
192
193 if (op_array->function_name) {
194 zend_string *old_name = op_array->function_name;
195 if (!zend_shared_alloc_get_xlat_entry(old_name)) {
196 ADD_INTERNED_STRING(op_array->function_name);
197 if (!zend_shared_alloc_get_xlat_entry(op_array->function_name)) {
198 zend_shared_alloc_register_xlat_entry(old_name, op_array->function_name);
199 }
200 }
201 }
202
203 if (op_array->filename) {
204 ADD_STRING(op_array->filename);
205 }
206
207 if (op_array->arg_info) {
208 zend_arg_info *arg_info = op_array->arg_info;
209 uint32_t num_args = op_array->num_args;
210 uint32_t i;
211
212 num_args = op_array->num_args;
213 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
214 num_args++;
215 }
216 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
217 arg_info--;
218 num_args++;
219 }
220 ADD_SIZE(sizeof(zend_arg_info) * num_args);
221 for (i = 0; i < num_args; i++) {
222 if (arg_info[i].name) {
223 ADD_INTERNED_STRING(arg_info[i].name);
224 }
225 if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
226 zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type);
227 zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
228
229 ADD_INTERNED_STRING(type_name);
230 arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
231 }
232 }
233 }
234
235 if (op_array->live_range) {
236 ADD_SIZE(sizeof(zend_live_range) * op_array->last_live_range);
237 }
238
239 if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
240 ADD_STRING(op_array->doc_comment);
241 }
242
243 if (op_array->try_catch_array) {
244 ADD_SIZE(sizeof(zend_try_catch_element) * op_array->last_try_catch);
245 }
246
247 if (op_array->vars) {
248 int i;
249
250 ADD_SIZE(sizeof(zend_string*) * op_array->last_var);
251 for (i = 0; i < op_array->last_var; i++) {
252 ADD_INTERNED_STRING(op_array->vars[i]);
253 }
254 }
255
256 ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
257 }
258
zend_persist_op_array_calc(zval * zv)259 static void zend_persist_op_array_calc(zval *zv)
260 {
261 zend_op_array *op_array = Z_PTR_P(zv);
262
263 ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
264 ADD_SIZE(sizeof(zend_op_array));
265 zend_persist_op_array_calc_ex(Z_PTR_P(zv));
266 if (ZCG(current_persistent_script)->corrupted) {
267 ADD_ARENA_SIZE(sizeof(void*));
268 }
269 }
270
zend_persist_class_method_calc(zval * zv)271 static void zend_persist_class_method_calc(zval *zv)
272 {
273 zend_op_array *op_array = Z_PTR_P(zv);
274 zend_op_array *old_op_array;
275
276 if (op_array->type != ZEND_USER_FUNCTION) {
277 ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION);
278 if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) {
279 old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
280 if (!old_op_array) {
281 ADD_SIZE_EX(sizeof(zend_internal_function));
282 zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv));
283 }
284 }
285 return;
286 }
287
288 old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
289 if (!old_op_array) {
290 ADD_SIZE_EX(sizeof(zend_op_array));
291 zend_persist_op_array_calc_ex(Z_PTR_P(zv));
292 zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv));
293 if (!ZCG(is_immutable_class)) {
294 ADD_ARENA_SIZE(sizeof(void*));
295 }
296 }
297 }
298
zend_persist_property_info_calc(zval * zv)299 static void zend_persist_property_info_calc(zval *zv)
300 {
301 zend_property_info *prop = Z_PTR_P(zv);
302
303 if (!zend_shared_alloc_get_xlat_entry(prop)) {
304 zend_shared_alloc_register_xlat_entry(prop, prop);
305 ADD_SIZE_EX(sizeof(zend_property_info));
306 ADD_INTERNED_STRING(prop->name);
307 if (ZEND_TYPE_IS_NAME(prop->type)) {
308 zend_string *class_name = ZEND_TYPE_NAME(prop->type);
309 ADD_INTERNED_STRING(class_name);
310 prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type));
311 }
312 if (ZCG(accel_directives).save_comments && prop->doc_comment) {
313 ADD_STRING(prop->doc_comment);
314 }
315 }
316 }
317
zend_persist_class_constant_calc(zval * zv)318 static void zend_persist_class_constant_calc(zval *zv)
319 {
320 zend_class_constant *c = Z_PTR_P(zv);
321
322 if (!zend_shared_alloc_get_xlat_entry(c)) {
323 zend_shared_alloc_register_xlat_entry(c, c);
324 ADD_SIZE_EX(sizeof(zend_class_constant));
325 zend_persist_zval_calc(&c->value);
326 if (ZCG(accel_directives).save_comments && c->doc_comment) {
327 ADD_STRING(c->doc_comment);
328 }
329 }
330 }
331
check_property_type_resolution(zend_class_entry * ce)332 static void check_property_type_resolution(zend_class_entry *ce) {
333 zend_property_info *prop;
334 if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) {
335 /* Preloading might have computed this already. */
336 return;
337 }
338
339 if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
340 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
341 if (ZEND_TYPE_IS_NAME(prop->type)) {
342 return;
343 }
344 } ZEND_HASH_FOREACH_END();
345 }
346 ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
347 }
348
zend_persist_class_entry_calc(zval * zv)349 static void zend_persist_class_entry_calc(zval *zv)
350 {
351 zend_class_entry *ce = Z_PTR_P(zv);
352 Bucket *p;
353
354 if (ce->type == ZEND_USER_CLASS) {
355 /* The same zend_class_entry may be reused by class_alias */
356 if (zend_shared_alloc_get_xlat_entry(ce)) {
357 return;
358 }
359 zend_shared_alloc_register_xlat_entry(ce, ce);
360
361 check_property_type_resolution(ce);
362
363 ZCG(is_immutable_class) =
364 (ce->ce_flags & ZEND_ACC_LINKED) &&
365 (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) &&
366 (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) &&
367 !ZCG(current_persistent_script)->corrupted;
368
369 ADD_SIZE_EX(sizeof(zend_class_entry));
370 ADD_INTERNED_STRING(ce->name);
371 if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
372 ADD_INTERNED_STRING(ce->parent_name);
373 }
374 zend_hash_persist_calc(&ce->function_table);
375 ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) {
376 ZEND_ASSERT(p->key != NULL);
377 ADD_INTERNED_STRING(p->key);
378 zend_persist_class_method_calc(&p->val);
379 } ZEND_HASH_FOREACH_END();
380 if (ce->default_properties_table) {
381 int i;
382
383 ADD_SIZE(sizeof(zval) * ce->default_properties_count);
384 for (i = 0; i < ce->default_properties_count; i++) {
385 zend_persist_zval_calc(&ce->default_properties_table[i]);
386 }
387 }
388 if (ce->default_static_members_table) {
389 int i;
390
391 ADD_SIZE(sizeof(zval) * ce->default_static_members_count);
392 for (i = 0; i < ce->default_static_members_count; i++) {
393 if (Z_TYPE(ce->default_static_members_table[i]) != IS_INDIRECT) {
394 zend_persist_zval_calc(&ce->default_static_members_table[i]);
395 }
396 }
397 }
398 zend_hash_persist_calc(&ce->constants_table);
399 ZEND_HASH_FOREACH_BUCKET(&ce->constants_table, p) {
400 ZEND_ASSERT(p->key != NULL);
401 ADD_INTERNED_STRING(p->key);
402 zend_persist_class_constant_calc(&p->val);
403 } ZEND_HASH_FOREACH_END();
404
405 if (ce->info.user.filename) {
406 ADD_STRING(ce->info.user.filename);
407 }
408 if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
409 ADD_STRING(ce->info.user.doc_comment);
410 }
411
412 zend_hash_persist_calc(&ce->properties_info);
413 ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) {
414 ZEND_ASSERT(p->key != NULL);
415 ADD_INTERNED_STRING(p->key);
416 zend_persist_property_info_calc(&p->val);
417 } ZEND_HASH_FOREACH_END();
418
419 if (ce->properties_info_table) {
420 ADD_SIZE_EX(sizeof(zend_property_info *) * ce->default_properties_count);
421 }
422
423 if (ce->num_interfaces) {
424 uint32_t i;
425
426 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
427 for (i = 0; i < ce->num_interfaces; i++) {
428 ADD_INTERNED_STRING(ce->interface_names[i].name);
429 ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
430 }
431 ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
432 } else {
433 ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces);
434 }
435 }
436
437 if (ce->num_traits) {
438 uint32_t i;
439
440 for (i = 0; i < ce->num_traits; i++) {
441 ADD_INTERNED_STRING(ce->trait_names[i].name);
442 ADD_INTERNED_STRING(ce->trait_names[i].lc_name);
443 }
444 ADD_SIZE(sizeof(zend_class_name) * ce->num_traits);
445
446 if (ce->trait_aliases) {
447 i = 0;
448 while (ce->trait_aliases[i]) {
449 if (ce->trait_aliases[i]->trait_method.method_name) {
450 ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.method_name);
451 }
452 if (ce->trait_aliases[i]->trait_method.class_name) {
453 ADD_INTERNED_STRING(ce->trait_aliases[i]->trait_method.class_name);
454 }
455
456 if (ce->trait_aliases[i]->alias) {
457 ADD_INTERNED_STRING(ce->trait_aliases[i]->alias);
458 }
459 ADD_SIZE(sizeof(zend_trait_alias));
460 i++;
461 }
462 ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1));
463 }
464
465 if (ce->trait_precedences) {
466 int j;
467
468 i = 0;
469 while (ce->trait_precedences[i]) {
470 ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.method_name);
471 ADD_INTERNED_STRING(ce->trait_precedences[i]->trait_method.class_name);
472
473 for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
474 ADD_INTERNED_STRING(ce->trait_precedences[i]->exclude_class_names[j]);
475 }
476 ADD_SIZE(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
477 i++;
478 }
479 ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1));
480 }
481 }
482
483 if (ce->iterator_funcs_ptr) {
484 ADD_SIZE(sizeof(zend_class_iterator_funcs));
485 }
486 }
487 }
488
zend_accel_persist_class_table_calc(HashTable * class_table)489 static void zend_accel_persist_class_table_calc(HashTable *class_table)
490 {
491 Bucket *p;
492
493 zend_hash_persist_calc(class_table);
494 ZEND_HASH_FOREACH_BUCKET(class_table, p) {
495 ZEND_ASSERT(p->key != NULL);
496 ADD_INTERNED_STRING(p->key);
497 zend_persist_class_entry_calc(&p->val);
498 } ZEND_HASH_FOREACH_END();
499 }
500
zend_accel_script_persist_calc(zend_persistent_script * new_persistent_script,const char * key,unsigned int key_length,int for_shm)501 uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, const char *key, unsigned int key_length, int for_shm)
502 {
503 Bucket *p;
504
505 new_persistent_script->mem = NULL;
506 new_persistent_script->size = 0;
507 new_persistent_script->arena_mem = NULL;
508 new_persistent_script->arena_size = 0;
509 new_persistent_script->corrupted = 0;
510 ZCG(current_persistent_script) = new_persistent_script;
511
512 if (!for_shm) {
513 /* script is not going to be saved in SHM */
514 new_persistent_script->corrupted = 1;
515 }
516
517 ADD_SIZE(sizeof(zend_persistent_script));
518 if (key) {
519 ADD_SIZE(key_length + 1);
520 zend_shared_alloc_register_xlat_entry(key, key);
521 }
522 ADD_STRING(new_persistent_script->script.filename);
523
524 #if defined(__AVX__) || defined(__SSE2__)
525 /* Align size to 64-byte boundary */
526 new_persistent_script->size = (new_persistent_script->size + 63) & ~63;
527 #endif
528
529 if (new_persistent_script->script.class_table.nNumUsed != new_persistent_script->script.class_table.nNumOfElements) {
530 zend_hash_rehash(&new_persistent_script->script.class_table);
531 }
532 zend_accel_persist_class_table_calc(&new_persistent_script->script.class_table);
533 if (new_persistent_script->script.function_table.nNumUsed != new_persistent_script->script.function_table.nNumOfElements) {
534 zend_hash_rehash(&new_persistent_script->script.function_table);
535 }
536 zend_hash_persist_calc(&new_persistent_script->script.function_table);
537 ZEND_HASH_FOREACH_BUCKET(&new_persistent_script->script.function_table, p) {
538 ZEND_ASSERT(p->key != NULL);
539 ADD_INTERNED_STRING(p->key);
540 zend_persist_op_array_calc(&p->val);
541 } ZEND_HASH_FOREACH_END();
542 zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array);
543
544 #if defined(__AVX__) || defined(__SSE2__)
545 /* Align size to 64-byte boundary */
546 new_persistent_script->arena_size = (new_persistent_script->arena_size + 63) & ~63;
547 #endif
548
549 new_persistent_script->size += new_persistent_script->arena_size;
550 new_persistent_script->corrupted = 0;
551
552 ZCG(current_persistent_script) = NULL;
553
554 return new_persistent_script->size;
555 }
556