PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/opcache - zend_shared_alloc.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 135 197 68.5 %
Date: 2016-02-05 Functions: 19 23 82.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend OPcache                                                         |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2016 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@zend.com>                                |
      16             :    |          Zeev Suraski <zeev@zend.com>                                |
      17             :    |          Stanislav Malyshev <stas@zend.com>                          |
      18             :    |          Dmitry Stogov <dmitry@zend.com>                             |
      19             :    +----------------------------------------------------------------------+
      20             : */
      21             : 
      22             : #include <errno.h>
      23             : #include "ZendAccelerator.h"
      24             : #include "zend_shared_alloc.h"
      25             : #ifdef HAVE_UNISTD_H
      26             : # include <unistd.h>
      27             : #endif
      28             : #include <fcntl.h>
      29             : #ifndef ZEND_WIN32
      30             : # include <sys/types.h>
      31             : # include <dirent.h>
      32             : # include <signal.h>
      33             : # include <sys/stat.h>
      34             : # include <stdio.h>
      35             : #endif
      36             : 
      37             : #ifdef HAVE_MPROTECT
      38             : # include "sys/mman.h"
      39             : #endif
      40             : 
      41             : #define TMP_DIR "/tmp"
      42             : #define SEM_FILENAME_PREFIX ".ZendSem."
      43             : #define S_H(s) g_shared_alloc_handler->s
      44             : 
      45             : /* True globals */
      46             : /* old/new mapping. We can use true global even for ZTS because its usage
      47             :    is wrapped with exclusive lock anyway */
      48             : static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
      49             : static const char *g_shared_model;
      50             : /* pointer to globals allocated in SHM and shared across processes */
      51             : zend_smm_shared_globals *smm_shared_globals;
      52             : 
      53             : #ifndef ZEND_WIN32
      54             : #ifdef ZTS
      55             : static MUTEX_T zts_lock;
      56             : #endif
      57             : int lock_file;
      58             : static char lockfile_name[sizeof(TMP_DIR) + sizeof(SEM_FILENAME_PREFIX) + 8];
      59             : #endif
      60             : 
      61             : static const zend_shared_memory_handler_entry handler_table[] = {
      62             : #ifdef USE_MMAP
      63             :         { "mmap", &zend_alloc_mmap_handlers },
      64             : #endif
      65             : #ifdef USE_SHM
      66             :         { "shm", &zend_alloc_shm_handlers },
      67             : #endif
      68             : #ifdef USE_SHM_OPEN
      69             :         { "posix", &zend_alloc_posix_handlers },
      70             : #endif
      71             : #ifdef ZEND_WIN32
      72             :         { "win32", &zend_alloc_win32_handlers },
      73             : #endif
      74             :         { NULL, NULL}
      75             : };
      76             : 
      77             : #ifndef ZEND_WIN32
      78         378 : void zend_shared_alloc_create_lock(void)
      79             : {
      80             :         int val;
      81             : 
      82             : #ifdef ZTS
      83             :     zts_lock = tsrm_mutex_alloc();
      84             : #endif
      85             : 
      86         378 :         sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX);
      87         378 :         lock_file = mkstemp(lockfile_name);
      88         378 :         fchmod(lock_file, 0666);
      89             : 
      90         378 :         if (lock_file == -1) {
      91           0 :                 zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
      92             :         }
      93         378 :         val = fcntl(lock_file, F_GETFD, 0);
      94         378 :         val |= FD_CLOEXEC;
      95         378 :         fcntl(lock_file, F_SETFD, val);
      96             : 
      97         378 :         unlink(lockfile_name);
      98         378 : }
      99             : #endif
     100             : 
     101           0 : static void no_memory_bailout(size_t allocate_size, char *error)
     102             : {
     103           0 :         zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %ld bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
     104           0 : }
     105             : 
     106         756 : static void copy_shared_segments(void *to, void *from, int count, int size)
     107             : {
     108         756 :         zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
     109         756 :         void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
     110         756 :         void *shared_segments_from_p = from;
     111             :         int i;
     112             : 
     113        1512 :         for (i = 0; i < count; i++) {
     114         756 :                 shared_segments_v[i] =  shared_segments_to_p;
     115         756 :                 memcpy(shared_segments_to_p, shared_segments_from_p, size);
     116         756 :                 shared_segments_to_p = ((char *)shared_segments_to_p + size);
     117         756 :                 shared_segments_from_p = ((char *)shared_segments_from_p + size);
     118             :         }
     119         756 : }
     120             : 
     121         378 : static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
     122             : {
     123             :         int res;
     124         378 :         g_shared_alloc_handler = he->handler;
     125         378 :         g_shared_model = he->name;
     126         378 :         ZSMMG(shared_segments) = NULL;
     127         378 :         ZSMMG(shared_segments_count) = 0;
     128             : 
     129         378 :         res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
     130             : 
     131         378 :         if (res) {
     132             :                 /* this model works! */
     133         378 :                 return res;
     134             :         }
     135           0 :         if (*shared_segments_p) {
     136             :                 int i;
     137             :                 /* cleanup */
     138           0 :                 for (i = 0; i < *shared_segments_count; i++) {
     139           0 :                         if ((*shared_segments_p)[i]->p && (*shared_segments_p)[i]->p != (void *)-1) {
     140           0 :                                 S_H(detach_segment)((*shared_segments_p)[i]);
     141             :                         }
     142             :                 }
     143           0 :                 free(*shared_segments_p);
     144           0 :                 *shared_segments_p = NULL;
     145             :         }
     146           0 :         g_shared_alloc_handler = NULL;
     147           0 :         return ALLOC_FAILURE;
     148             : }
     149             : 
     150         378 : int zend_shared_alloc_startup(size_t requested_size)
     151             : {
     152             :         zend_shared_segment **tmp_shared_segments;
     153             :         size_t shared_segments_array_size;
     154             :         zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
     155         378 :         char *error_in = NULL;
     156             :         const zend_shared_memory_handler_entry *he;
     157         378 :         int res = ALLOC_FAILURE;
     158             : 
     159             : 
     160             :         /* shared_free must be valid before we call zend_shared_alloc()
     161             :          * - make it temporarily point to a local variable
     162             :          */
     163         378 :         smm_shared_globals = &tmp_shared_globals;
     164         378 :         ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
     165             : 
     166         378 :         zend_shared_alloc_create_lock();
     167             : 
     168         378 :         if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
     169           0 :                 char *model = ZCG(accel_directives).memory_model;
     170             :                 /* "cgi" is really "shm"... */
     171           0 :                 if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) {
     172           0 :                         model = "shm";
     173             :                 }
     174             : 
     175           0 :                 for (he = handler_table; he->name; he++) {
     176           0 :                         if (strcmp(model, he->name) == 0) {
     177           0 :                                 res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
     178             :                                 if (res) {
     179             :                                         /* this model works! */
     180             :                                 }
     181           0 :                                 break;
     182             :                         }
     183             :                 }
     184             :         }
     185             : 
     186         378 :         if (res == FAILED_REATTACHED) {
     187           0 :                 smm_shared_globals = NULL;
     188           0 :                 return res;
     189             :         }
     190             : #if ENABLE_FILE_CACHE_FALLBACK
     191             :         if (ALLOC_FALLBACK == res) {
     192             :                 return ALLOC_FALLBACK;
     193             :         }
     194             : #endif
     195             : 
     196         378 :         if (!g_shared_alloc_handler) {
     197             :                 /* try memory handlers in order */
     198         378 :                 for (he = handler_table; he->name; he++) {
     199         378 :                         res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
     200         378 :                         if (res) {
     201             :                                 /* this model works! */
     202         378 :                                 break;
     203             :                         }
     204             :                 }
     205             :         }
     206             : 
     207         378 :         if (!g_shared_alloc_handler) {
     208           0 :                 no_memory_bailout(requested_size, error_in);
     209           0 :                 return ALLOC_FAILURE;
     210             :         }
     211             : 
     212         378 :         if (res == SUCCESSFULLY_REATTACHED) {
     213           0 :                 return res;
     214             :         }
     215             : #if ENABLE_FILE_CACHE_FALLBACK
     216             :         if (ALLOC_FALLBACK == res) {
     217             :                 return ALLOC_FALLBACK;
     218             :         }
     219             : #endif
     220             : 
     221         378 :         shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)();
     222             : 
     223             :         /* move shared_segments and shared_free to shared memory */
     224         378 :         ZCG(locked) = 1; /* no need to perform a real lock at this point */
     225         378 :         p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
     226         378 :         if (!p_tmp_shared_globals) {
     227           0 :                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
     228           0 :                 return ALLOC_FAILURE;;
     229             :         }
     230             : 
     231         378 :         tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
     232         378 :         if (!tmp_shared_segments) {
     233           0 :                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
     234           0 :                 return ALLOC_FAILURE;;
     235             :         }
     236             : 
     237         378 :         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
     238             : 
     239         378 :         *p_tmp_shared_globals = tmp_shared_globals;
     240         378 :         smm_shared_globals = p_tmp_shared_globals;
     241             : 
     242         378 :         free(ZSMMG(shared_segments));
     243         378 :         ZSMMG(shared_segments) = tmp_shared_segments;
     244             : 
     245         378 :         ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count));
     246         378 :         if (!ZSMMG(shared_memory_state).positions) {
     247           0 :                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
     248           0 :                 return ALLOC_FAILURE;;
     249             :         }
     250             : 
     251         378 :         ZCG(locked) = 0;
     252             : 
     253         378 :         return res;
     254             : }
     255             : 
     256         378 : void zend_shared_alloc_shutdown(void)
     257             : {
     258             :         zend_shared_segment **tmp_shared_segments;
     259             :         size_t shared_segments_array_size;
     260             :         zend_smm_shared_globals tmp_shared_globals;
     261             :         int i;
     262             : 
     263         378 :         tmp_shared_globals = *smm_shared_globals;
     264         378 :         smm_shared_globals = &tmp_shared_globals;
     265         378 :         shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *));
     266         378 :         tmp_shared_segments = emalloc(shared_segments_array_size);
     267         378 :         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
     268         378 :         ZSMMG(shared_segments) = tmp_shared_segments;
     269             : 
     270         756 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     271         378 :                 S_H(detach_segment)(ZSMMG(shared_segments)[i]);
     272             :         }
     273         378 :         efree(ZSMMG(shared_segments));
     274         378 :         ZSMMG(shared_segments) = NULL;
     275         378 :         g_shared_alloc_handler = NULL;
     276             : #ifndef ZEND_WIN32
     277         378 :         close(lock_file);
     278             : #endif
     279         378 : }
     280             : 
     281           0 : static size_t zend_shared_alloc_get_largest_free_block(void)
     282             : {
     283             :         int i;
     284           0 :         size_t largest_block_size = 0;
     285             : 
     286           0 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     287           0 :                 size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
     288             : 
     289           0 :                 if (block_size>largest_block_size) {
     290           0 :                         largest_block_size = block_size;
     291             :                 }
     292             :         }
     293           0 :         return largest_block_size;
     294             : }
     295             : 
     296             : #define MIN_FREE_MEMORY 64*1024
     297             : 
     298             : #define SHARED_ALLOC_FAILED() do {              \
     299             :                 zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %pd bytes (%pd bytes free)", (zend_long)size, (zend_long)ZSMMG(shared_free)); \
     300             :                 if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
     301             :                         ZSMMG(memory_exhausted) = 1; \
     302             :                 } \
     303             :         } while (0)
     304             : 
     305        3454 : void *zend_shared_alloc(size_t size)
     306             : {
     307             :         int i;
     308        3454 :         unsigned int block_size = ZEND_ALIGNED_SIZE(size);
     309             : 
     310             : #if 1
     311        3454 :         if (!ZCG(locked)) {
     312           0 :                 zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
     313             :         }
     314             : #endif
     315        3454 :         if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
     316           0 :                 SHARED_ALLOC_FAILED();
     317           0 :                 return NULL;
     318             :         }
     319        3454 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     320        3454 :                 if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
     321        3454 :                         void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos);
     322             : 
     323        3454 :                         ZSMMG(shared_segments)[i]->pos += block_size;
     324        3454 :                         ZSMMG(shared_free) -= block_size;
     325        3454 :                         memset(retval, 0, block_size);
     326             :                         ZEND_ASSERT(((zend_uintptr_t)retval & 0x7) == 0); /* should be 8 byte aligned */
     327        3454 :                         return retval;
     328             :                 }
     329             :         }
     330           0 :         SHARED_ALLOC_FAILED();
     331           0 :         return NULL;
     332             : }
     333             : 
     334        2953 : int zend_shared_memdup_size(void *source, size_t size)
     335             : {
     336             :         void *old_p;
     337             : 
     338        5906 :         if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
     339             :                 /* we already duplicated this pointer */
     340         470 :                 return 0;
     341             :         }
     342        2483 :         zend_shared_alloc_register_xlat_entry(source, source);
     343        2483 :         return ZEND_ALIGNED_SIZE(size);
     344             : }
     345             : 
     346        2966 : void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source)
     347             : {
     348             :         void *old_p, *retval;
     349             : 
     350        5932 :         if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
     351             :                 /* we already duplicated this pointer */
     352         470 :                 return old_p;
     353             :         }
     354        2496 :         retval = ZCG(mem);
     355        2496 :         ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
     356        2496 :         memcpy(retval, source, size);
     357        2496 :         zend_shared_alloc_register_xlat_entry(source, retval);
     358        2496 :         if (free_source) {
     359         710 :                 efree(source);
     360             :         }
     361        2496 :         return retval;
     362             : }
     363             : 
     364         378 : void zend_shared_alloc_safe_unlock(void)
     365             : {
     366         378 :         if (ZCG(locked)) {
     367           0 :                 zend_shared_alloc_unlock();
     368             :         }
     369         378 : }
     370             : 
     371             : #ifndef ZEND_WIN32
     372             : /* name l_type l_whence l_start l_len */
     373             : static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1);
     374             : static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1);
     375             : #endif
     376             : 
     377        1234 : void zend_shared_alloc_lock(void)
     378             : {
     379             : #ifndef ZEND_WIN32
     380             : 
     381             : #ifdef ZTS
     382             :         tsrm_mutex_lock(zts_lock);
     383             : #endif
     384             : 
     385             : #if 0
     386             :         /* this will happen once per process, and will un-globalize mem_write_lock */
     387             :         if (mem_write_lock.l_pid == -1) {
     388             :                 mem_write_lock.l_pid = getpid();
     389             :         }
     390             : #endif
     391             : 
     392             :         while (1) {
     393        1234 :                 if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
     394           0 :                         if (errno == EINTR) {
     395           0 :                                 continue;
     396             :                         }
     397           0 :                         zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
     398             :                 }
     399        1234 :                 break;
     400           0 :         }
     401             : #else
     402             :         zend_shared_alloc_lock_win32();
     403             : #endif
     404             : 
     405        1234 :         ZCG(locked) = 1;
     406        1234 : }
     407             : 
     408        1234 : void zend_shared_alloc_unlock(void)
     409             : {
     410        1234 :         ZCG(locked) = 0;
     411             : 
     412             : #ifndef ZEND_WIN32
     413        1234 :         if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
     414           0 :                 zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
     415             :         }
     416             : #ifdef ZTS
     417             :         tsrm_mutex_unlock(zts_lock);
     418             : #endif
     419             : #else
     420             :         zend_shared_alloc_unlock_win32();
     421             : #endif
     422        1234 : }
     423             : 
     424         430 : void zend_shared_alloc_init_xlat_table(void)
     425             : {
     426             : 
     427             :         /* Prepare translation table
     428             :          *
     429             :          * Make it persistent so that it uses malloc() and allocated blocks
     430             :          * won't be taken from space which is freed by efree in memdup.
     431             :          * Otherwise it leads to false matches in memdup check.
     432             :          */
     433         430 :         zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 1);
     434         430 : }
     435             : 
     436         430 : void zend_shared_alloc_destroy_xlat_table(void)
     437             : {
     438             :         /* Destroy translation table */
     439         430 :         zend_hash_destroy(&ZCG(xlat_table));
     440         430 : }
     441             : 
     442         430 : void zend_shared_alloc_clear_xlat_table(void)
     443             : {
     444         430 :         zend_hash_clean(&ZCG(xlat_table));
     445         430 : }
     446             : 
     447        5112 : void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
     448             : {
     449        5112 :         zend_hash_index_add_new_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new);
     450        5112 : }
     451             : 
     452        1590 : void *zend_shared_alloc_get_xlat_entry(const void *old)
     453             : {
     454             :         void *retval;
     455             : 
     456        3180 :         if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)old)) == NULL) {
     457        1569 :                 return NULL;
     458             :         }
     459          21 :         return retval;
     460             : }
     461             : 
     462           2 : size_t zend_shared_alloc_get_free_memory(void)
     463             : {
     464           2 :         return ZSMMG(shared_free);
     465             : }
     466             : 
     467         378 : void zend_shared_alloc_save_state(void)
     468             : {
     469             :         int i;
     470             : 
     471         756 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     472         378 :                 ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
     473             :         }
     474         378 :         ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
     475         378 : }
     476             : 
     477           0 : void zend_shared_alloc_restore_state(void)
     478             : {
     479             :         int i;
     480             : 
     481           0 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     482           0 :                 ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
     483             :         }
     484           0 :         ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
     485           0 :         ZSMMG(memory_exhausted) = 0;
     486           0 :         ZSMMG(wasted_shared_memory) = 0;
     487           0 : }
     488             : 
     489           1 : const char *zend_accel_get_shared_model(void)
     490             : {
     491           1 :         return g_shared_model;
     492             : }
     493             : 
     494           0 : void zend_accel_shared_protect(int mode)
     495             : {
     496             : #ifdef HAVE_MPROTECT
     497             :         int i;
     498             : 
     499           0 :         if (mode) {
     500           0 :                 mode = PROT_READ;
     501             :         } else {
     502           0 :                 mode = PROT_READ|PROT_WRITE;
     503             :         }
     504             : 
     505           0 :         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
     506           0 :                 mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
     507             :         }
     508             : #endif
     509           0 : }

Generated by: LCOV version 1.10

Generated at Fri, 05 Feb 2016 08:39:19 +0000 (15 hours ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.