source: trunk/coverage.c @ 1

Last change on this file since 1 was 1, checked in by moo, 8 years ago

initial import to online

File size: 9.5 KB
Line 
1#include <stdio.h>
2#include "xcache.h"
3#include "ext/standard/flock_compat.h"
4#ifdef HAVE_SYS_FILE_H
5#   include <sys/file.h>
6#endif
7#include "stack.h"
8#include "xcache_globals.h"
9#include "coverage.h"
10#include "utils.h"
11typedef HashTable *coverage_t;
12#define PCOV_HEADER_MAGIC 0x564f4350
13
14char *xc_coveragedump_dir = NULL;
15static zend_compile_file_t *origin_compile_file;
16
17#undef DEBUG
18/* dumper */
19static void xc_destroy_coverage(void *pDest) /* {{{ */
20{
21    coverage_t cov = *(coverage_t*) pDest;
22#ifdef DEBUG
23    fprintf(stderr, "destroy %p\n", cov);
24#endif
25    zend_hash_destroy(cov);
26    efree(cov);
27}
28/* }}} */
29PHPAPI void xcache_mkdirs_ex(char *root, int rootlen, char *path, int pathlen TSRMLS_DC) /* {{{ */
30{
31    char *fullpath;
32    struct stat st;
33
34#ifdef DEBUG
35    fprintf(stderr, "mkdirs %s %d %s %d\n", root, rootlen, path, pathlen);
36#endif
37    fullpath = do_alloca(rootlen + pathlen + 1);
38    memcpy(fullpath, root, rootlen);
39    memcpy(fullpath + rootlen, path, pathlen);
40    fullpath[rootlen + pathlen] = '\0';
41
42    if (stat(fullpath, &st) != 0) {
43        char *chr;
44
45        chr = strrchr(path, PHP_DIR_SEPARATOR);
46        if (chr && chr != path) {
47            *chr = '\0';
48            xcache_mkdirs_ex(root, rootlen, path, chr - path TSRMLS_CC);
49            *chr = PHP_DIR_SEPARATOR;
50        }
51#ifdef DEBUG
52        fprintf(stderr, "mkdir %s\n", fullpath);
53#endif
54        php_stream_mkdir(fullpath, 0700, REPORT_ERRORS, NULL);
55    }
56    free_alloca(fullpath);
57}
58/* }}} */
59static void xc_coverage_save_cov(char *srcfile, char *outfilename, coverage_t cov TSRMLS_DC) /* {{{ */
60{
61    long *buf = NULL, *p;
62    long covlines, *phits;
63    int fd = -1;
64    int size;
65    int newfile;
66    struct stat srcstat, outstat;
67    HashPosition pos;
68    char *contents = NULL;
69    long len;
70
71    if (stat(srcfile, &srcstat) != 0) {
72        return;
73    }
74
75    newfile = 0;
76    if (stat(outfilename, &outstat) != 0) {
77        newfile = 1;
78    }
79    else {
80        if (srcstat.st_mtime > outstat.st_mtime) {
81            newfile = 1;
82        }
83    }
84
85    fd = open(outfilename, O_RDWR | O_CREAT, 0600);
86    if (fd < 0) {
87        char *chr;
88        chr = strrchr(srcfile, PHP_DIR_SEPARATOR);
89        if (chr) {
90            *chr = '\0';
91            xcache_mkdirs_ex(xc_coveragedump_dir, strlen(xc_coveragedump_dir), srcfile, chr - srcfile TSRMLS_CC);
92            *chr = PHP_DIR_SEPARATOR;
93        }
94        fd = open(outfilename, O_RDWR | O_CREAT, 0600);
95        if (fd < 0) {
96            goto bailout;
97        }
98    }
99    if (flock(fd, LOCK_EX) != SUCCESS) {
100        goto bailout;
101    }
102
103    if (newfile) {
104#ifdef DEBUG
105        fprintf(stderr, "new file\n");
106#endif
107    }
108    else if (outstat.st_size) {
109        len = outstat.st_size;
110        contents = emalloc(len);
111        if (read(fd, (void *) contents, len) != len) {
112            goto bailout;
113        }
114#ifdef DEBUG
115        fprintf(stderr, "oldsize %d\n", (int) len);
116#endif
117        do {
118            p = (long *) contents;
119            len -= sizeof(long);
120            if (len < 0) {
121                break;
122            }
123            if (*p++ != PCOV_HEADER_MAGIC) {
124#ifdef DEBUG
125                fprintf(stderr, "wrong magic in file %s\n", outfilename);
126#endif
127                break;
128            }
129
130            p += 2; /* skip covliens */
131            len -= sizeof(long) * 2;
132            if (len < 0) {
133                break;
134            }
135
136            for (; len >= sizeof(long) * 2; len -= sizeof(long) * 2, p += 2) {
137                if (zend_hash_index_find(cov, p[0], (void**)&phits) == SUCCESS) {
138                    if (p[1] <= 0) {
139                        /* already marked */
140                        continue;
141                    }
142                    if (*phits > 0) {
143                        p[1] += *phits;
144                    }
145                }
146                zend_hash_index_update(cov, p[0], &p[1], sizeof(p[1]), NULL);
147            }
148        } while (0);
149        efree(contents);
150        contents = NULL;
151    }
152
153
154    /* serialize */
155    size = (zend_hash_num_elements(cov) + 1) * sizeof(long) * 2 + sizeof(long);
156    p = buf = emalloc(size);
157    *p++ = PCOV_HEADER_MAGIC;
158    p += 2; /* for covlines */
159    covlines = 0;
160
161    zend_hash_internal_pointer_reset_ex(cov, &pos);
162    while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos) == SUCCESS) {
163        *p++ = pos->h;
164        if (*phits <= 0) {
165            *p++ = 0;
166        }
167        else {
168            *p++ = *phits;
169            covlines ++;
170        }
171        zend_hash_move_forward_ex(cov, &pos);
172    }
173    p = buf + 1;
174    p[0] = 0;
175    p[1] = covlines;
176
177    ftruncate(fd, 0);
178    lseek(fd, 0, SEEK_SET);
179    write(fd, (char *) buf, size);
180
181bailout:
182    if (contents) efree(contents);
183    if (fd >= 0) close(fd);
184    if (buf) efree(buf);
185}
186/* }}} */
187void xc_coverage_request_init(TSRMLS_D) /* {{{ */
188{
189    if (XG(coveragedumper)) {
190        XG(coverages) = emalloc(sizeof(HashTable));
191        zend_hash_init(XG(coverages), 0, NULL, xc_destroy_coverage, 0);
192    }
193}
194/* }}} */
195void xc_coverage_request_shutdown(TSRMLS_D) /* {{{ */
196{
197    coverage_t *pcov;
198    zstr s;
199    char *outfilename;
200    int dumpdir_len, outfilelen, size, alloc_len = 0;
201
202    if (!XG(coverages)) {
203        return;
204    }
205    if (XG(coveragedumper)) {   
206        dumpdir_len = strlen(xc_coveragedump_dir);
207        alloc_len = dumpdir_len + 1 + 128;
208        outfilename = emalloc(alloc_len);
209        strcpy(outfilename, xc_coveragedump_dir);
210
211        zend_hash_internal_pointer_reset(XG(coverages));
212        while (zend_hash_get_current_data(XG(coverages), (void **) &pcov) == SUCCESS) {
213            zend_hash_get_current_key_ex(XG(coverages), &s, &size, NULL, 0, NULL);
214            outfilelen = dumpdir_len + size + 5;
215            if (alloc_len < outfilelen) {
216                alloc_len = outfilelen + 128;
217                outfilename = erealloc(outfilename, alloc_len);
218            }
219            strcpy(outfilename + dumpdir_len, ZSTR_S(s));
220            strcpy(outfilename + dumpdir_len + size - 1, ".pcov");
221
222#ifdef DEBUG
223            fprintf(stderr, "outfilename %s\n", outfilename);
224#endif
225            xc_coverage_save_cov(ZSTR_S(s), outfilename, *pcov TSRMLS_CC);
226            zend_hash_move_forward(XG(coverages));
227        }
228    }
229
230    zend_hash_destroy(XG(coverages));
231    efree(XG(coverages));
232    XG(coverages) = NULL;
233}
234/* }}} */
235
236/* helper func to store hits into coverages */
237static coverage_t xc_coverage_get(char *filename TSRMLS_DC) /* {{{ */
238{
239    int len = strlen(filename) + 1;
240    coverage_t cov, *pcov;
241
242    if (zend_hash_find(XG(coverages), filename, len, (void **) &pcov) == SUCCESS) {
243#ifdef DEBUG
244        fprintf(stderr, "got coverage %s %p\n", filename, *pcov);
245#endif
246        return *pcov;
247    }
248    else {
249        cov = emalloc(sizeof(HashTable));
250        zend_hash_init(cov, 0, NULL, NULL, 0);
251        zend_hash_add(XG(coverages), filename, len, (void **) &cov, sizeof(cov), NULL);
252#ifdef DEBUG
253        fprintf(stderr, "new coverage %s %p\n", filename, cov);
254#endif
255        return cov;
256    }
257}
258/* }}} */
259static void xc_coverage_add_hits(HashTable *cov, long line, long hits TSRMLS_DC) /* {{{ */
260{
261    long *poldhits;
262
263    if (line == 0) {
264        return;
265    }
266    if (zend_hash_index_find(cov, line, (void**)&poldhits) == SUCCESS) {
267        if (hits == -1) {
268            /* already marked */
269            return;
270        }
271        if (*poldhits != -1) {
272            hits += *poldhits;
273        }
274    }
275    zend_hash_index_update(cov, line, &hits, sizeof(hits), NULL);
276}
277/* }}} */
278
279static int xc_coverage_get_op_array_size_no_tail(zend_op_array *op_array) /* {{{ */
280{
281    zend_uint size;
282
283    size = op_array->size;
284#ifdef ZEND_ENGINE_2
285    if (op_array->opcodes[size - 1].opcode == ZEND_HANDLE_EXCEPTION) {
286        size --;
287#endif
288        if (op_array->opcodes[size - 1].opcode == ZEND_RETURN) {
289            size --;
290            /* it's not real php statement */
291            if (op_array->opcodes[size - 1].opcode == ZEND_EXT_STMT) {
292                size --;
293            }
294        }   
295#ifdef ZEND_ENGINE_2
296    }
297#endif
298    return size;
299}
300/* }}} */
301
302/* prefill */
303static int xc_coverage_init_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
304{
305    zend_uint size;
306    coverage_t cov;
307    int i;
308
309    if (op_array->type != ZEND_USER_FUNCTION) {
310        return 0;
311    }
312
313    size = xc_coverage_get_op_array_size_no_tail(op_array);
314    cov = xc_coverage_get(op_array->filename);
315    for (i = 0; i < size; i++) {
316        switch (op_array->opcodes[i].opcode) {
317            case ZEND_EXT_STMT:
318#if 0
319            case ZEND_EXT_FCALL_BEGIN:
320            case ZEND_EXT_FCALL_END:
321#endif
322                xc_coverage_add_hits(cov, op_array->opcodes[i].lineno, -1 TSRMLS_CC);
323                break;
324        }
325    }
326    return 0;
327}
328/* }}} */
329static void xc_coverage_init_compile_result(zend_op_array *op_array TSRMLS_DC) /* {{{ */
330{
331    xc_compile_result_t cr;
332
333    xc_compile_result_init_cur(&cr, op_array TSRMLS_CC);
334    xc_apply_op_array(&cr, (apply_func_t) xc_coverage_init_op_array TSRMLS_CC);
335    xc_compile_result_free(&cr);
336}
337/* }}} */
338static zend_op_array *xc_compile_file_for_coverage(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
339{
340    zend_op_array *op_array;
341
342    op_array = origin_compile_file(h, type TSRMLS_CC);
343    if (XG(coveragedumper) && XG(coverages)) {
344        xc_coverage_init_compile_result(op_array TSRMLS_CC);
345    }
346    return op_array;
347}
348/* }}} */
349
350/* hits */
351void xc_coverage_handle_ext_stmt(zend_op_array *op_array, zend_uchar op) /* {{{ */
352{
353    if (XG(coveragedumper) && XG(coverages)) {
354        TSRMLS_FETCH();
355        int size = xc_coverage_get_op_array_size_no_tail(op_array);
356        int oplineno = (*EG(opline_ptr)) - op_array->opcodes;
357        if (oplineno < size) {
358            xc_coverage_add_hits(xc_coverage_get(op_array->filename), (*EG(opline_ptr))->lineno, 1 TSRMLS_CC);
359        }
360    }
361}
362/* }}} */
363
364/* init/destroy */
365int xc_coverage_init(int module_number TSRMLS_DC) /* {{{ */
366{
367    if (xc_coveragedump_dir) {
368        int len = strlen(xc_coveragedump_dir);
369        if (len) {
370            if (xc_coveragedump_dir[len - 1] == '/') {
371                xc_coveragedump_dir[len - 1] = '\0';
372            }
373        }
374    }
375    if (xc_coveragedump_dir && xc_coveragedump_dir[0]) {
376        origin_compile_file = zend_compile_file;
377        zend_compile_file = xc_compile_file_for_coverage;
378        CG(extended_info) = 1;
379    }
380    return SUCCESS;
381}
382/* }}} */
383void xc_coverage_destroy() /* {{{ */
384{
385    if (origin_compile_file == xc_compile_file_for_coverage) {
386        zend_compile_file = origin_compile_file;
387    }
388    if (xc_coveragedump_dir) {
389        pefree(xc_coveragedump_dir, 1);
390        xc_coveragedump_dir = NULL;
391    }
392}
393/* }}} */
394
395/* user api */
396PHP_FUNCTION(xcache_coverage_decode) /* {{{ */
397{
398    char *str;
399    int len;
400    long *p;
401
402    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {
403        return;
404    }
405
406    array_init(return_value);
407
408    p = (long*) str;
409    len -= sizeof(long);
410    if (len < 0) {
411        return;
412    }
413    if (*p++ != PCOV_HEADER_MAGIC) {
414#ifdef DEBUG
415        fprintf(stderr, "wrong magic in xcache_coverage_decode");
416#endif
417        return;
418    }
419
420    for (; len >= sizeof(long) * 2; len -= sizeof(long) * 2, p += 2) {
421        add_index_long(return_value, p[0], p[1]);
422    }
423}
424/* }}} */
425
Note: See TracBrowser for help on using the repository browser.