/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/coverage.c
299 строк
9 KB
Jameson Nash
clangsa: add thread-safety capability analysis of safepoint annotations (#62288)
15 июл 2026, 21:56
Не верифицирован
15 июл 2026, 21:56
9821f8a
Код
Авторство
О чём код?
// This file is a part of Julia. License is MIT: https://julialang.org/license #include <stdint.h> #include <string.h> #include <stdio.h> #include <inttypes.h> #include "julia.h" #include "julia_internal.h" #include "support/strhash.h" static int codegen_imaging_mode(void) JL_NOTSAFEPOINT { return jl_options.image_codegen || (jl_generating_output() && jl_options.use_pkgimages); } // Logging for code coverage and memory allocation #define logdata_blocksize 32 // target getting nearby lines in the same general cache area and reducing calls to malloc by chunking typedef uint64_t logdata_block[logdata_blocksize]; // Per-file line data: a growable array of logdata_block pointers, indexed by block number. typedef struct { logdata_block **blocks; size_t len; size_t cap; } logdata_vec_t; // A logdata_t is a string-keyed hash table mapping filenames to logdata_vec_t. // We store the logdata_vec_t pointer as the htable value. typedef htable_t logdata_t; static void logdata_vec_resize(logdata_vec_t *v, size_t newlen) JL_NOTSAFEPOINT { if (newlen > v->cap) { size_t newcap = v->cap ? v->cap * 2 : 8; if (newcap < newlen) newcap = newlen; v->blocks = (logdata_block **)realloc_s(v->blocks, newcap * sizeof(logdata_block *)); memset(v->blocks + v->cap, 0, (newcap - v->cap) * sizeof(logdata_block *)); v->cap = newcap; } v->len = newlen; } static logdata_vec_t *logdata_get_or_create(logdata_t *ld, const char *filename) JL_NOTSAFEPOINT { void **bp = strhash_bp(ld, (void *)filename); if (*bp == HT_NOTFOUND) { logdata_vec_t *v = (logdata_vec_t *)calloc_s(sizeof(logdata_vec_t)); *bp = v; } return (logdata_vec_t *)*bp; } static uv_mutex_t coverage_lock; static uint64_t *allocLine(logdata_vec_t *vec, int line) JL_NOTSAFEPOINT { unsigned block = line / logdata_blocksize; line = line % logdata_blocksize; if (vec->len <= block) logdata_vec_resize(vec, block + 1); jl_assume(vec->blocks != NULL); if (vec->blocks[block] == NULL) { vec->blocks[block] = (logdata_block *)calloc(1, sizeof(logdata_block)); } logdata_block *data = vec->blocks[block]; if ((*data)[line] == 0) (*data)[line] = 1; return &(*data)[line]; } // Code coverage static logdata_t coverageData; static int is_skip_filename(const char *filename) JL_NOTSAFEPOINT { if (!filename || filename[0] == '\0') return 1; if (strcmp(filename, "none") == 0) return 1; if (strcmp(filename, "no file") == 0) return 1; if (strcmp(filename, "<missing>") == 0) return 1; return 0; } JL_DLLEXPORT void jl_coverage_alloc_line(const char *filename, int line) { assert(!codegen_imaging_mode()); if (is_skip_filename(filename) || line < 0) return; uv_mutex_lock(&coverage_lock); allocLine(logdata_get_or_create(&coverageData, filename), line); uv_mutex_unlock(&coverage_lock); } JL_DLLEXPORT uint64_t *jl_coverage_data_pointer(const char *filename, int line) { uv_mutex_lock(&coverage_lock); uint64_t *ret = allocLine(logdata_get_or_create(&coverageData, filename), line); uv_mutex_unlock(&coverage_lock); return ret; } JL_DLLEXPORT void jl_coverage_visit_line(const char *filename, size_t len, int line) JL_CANSAFEPOINT { // TODO: remove `len` and use C-style strings exclusively // (kept for backwards-compatibility with JuliaInterpreter) assert(filename[len] == '\0'); if (codegen_imaging_mode() || is_skip_filename(filename) || line < 0) return; uv_mutex_lock(&coverage_lock); logdata_vec_t *vec = logdata_get_or_create(&coverageData, filename); uint64_t *ptr = allocLine(vec, line); (*ptr)++; uv_mutex_unlock(&coverage_lock); } // Memory allocation log (malloc_log) static logdata_t mallocData; JL_DLLEXPORT uint64_t *jl_malloc_data_pointer(const char *filename, int line) JL_NOTSAFEPOINT { uv_mutex_lock(&coverage_lock); uint64_t *ret = allocLine(logdata_get_or_create(&mallocData, filename), line); uv_mutex_unlock(&coverage_lock); return ret; } static void clear_log_data(logdata_t *logData) JL_NOTSAFEPOINT { size_t sz = logData->size; void **tab = logData->table; for (size_t i = 0; i < sz; i += 2) { if (tab[i] == HT_NOTFOUND || tab[i+1] == HT_NOTFOUND) continue; logdata_vec_t *vec = (logdata_vec_t *)tab[i+1]; for (size_t j = 0; j < vec->len; j++) { if (vec->blocks[j]) { logdata_block *data = vec->blocks[j]; for (int k = 0; k < logdata_blocksize; k++) { if ((*data)[k] > 0) (*data)[k] = 1; } } } } jl_gc_sync_total_bytes(0); } // Resets the malloc counts. JL_DLLEXPORT void jl_clear_malloc_data(void) JL_NOTSAFEPOINT { uv_mutex_lock(&coverage_lock); clear_log_data(&mallocData); uv_mutex_unlock(&coverage_lock); } // Resets the code coverage JL_DLLEXPORT void jl_clear_coverage_data(void) JL_NOTSAFEPOINT { uv_mutex_lock(&coverage_lock); clear_log_data(&coverageData); uv_mutex_unlock(&coverage_lock); } static void write_log_data(logdata_t *logData, const char *extension) JL_NOTSAFEPOINT { char base[4096]; snprintf(base, sizeof(base), "%s/../share/julia/base/", jl_options.julia_bindir); size_t sz = logData->size; void **tab = logData->table; for (size_t i = 0; i < sz; i += 2) { if (tab[i] == HT_NOTFOUND || tab[i+1] == HT_NOTFOUND) continue; const char *filename = (const char *)tab[i]; logdata_vec_t *values = (logdata_vec_t *)tab[i+1]; if (values->len == 0) continue; char fullpath[4096]; if (!jl_isabspath(filename)) snprintf(fullpath, sizeof(fullpath), "%s%s", base, filename); else snprintf(fullpath, sizeof(fullpath), "%s", filename); FILE *inf = fopen(fullpath, "r"); if (!inf) continue; char outpath[4096]; snprintf(outpath, sizeof(outpath), "%s%s", fullpath, extension); FILE *outf = fopen(outpath, "wb"); if (outf) { char line[1024]; int l = 1; unsigned block = 0; int ret = 0; while (ret != EOF && (ret = fscanf(inf, "%1023[^\n]", line)) != EOF) { // Skip n non-newline chars and a single trailing newline if ((ret = fscanf(inf, "%*[^\n]")) != EOF) ret = fscanf(inf, "%*1[\n]"); logdata_block *data = NULL; if (block < values->len) { data = values->blocks[block]; } uint64_t value = data ? (*data)[l] : 0; if (++l >= logdata_blocksize) { l = 0; block++; } if (value == 0) fprintf(outf, " -"); else fprintf(outf, "%9" PRIu64, value - 1); fprintf(outf, " %s\n", line); line[0] = 0; } fclose(outf); } fclose(inf); } } static void write_lcov_data(logdata_t *logData, const char *outfile) JL_NOTSAFEPOINT { FILE *outf = fopen(outfile, "ab"); if (!outf) return; size_t sz = logData->size; void **tab = logData->table; for (size_t i = 0; i < sz; i += 2) { if (tab[i] == HT_NOTFOUND || tab[i+1] == HT_NOTFOUND) continue; const char *filename = (const char *)tab[i]; logdata_vec_t *values = (logdata_vec_t *)tab[i+1]; if (values->len == 0) continue; fprintf(outf, "SF:%s\n", filename); size_t n_covered = 0; size_t n_instrumented = 0; size_t lno = 0; for (size_t j = 0; j < values->len; j++) { if (values->blocks[j]) { logdata_block *data = values->blocks[j]; for (int k = 0; k < logdata_blocksize; k++) { uint64_t cov = (*data)[k]; if (cov > 0) { n_instrumented++; if (cov > 1) n_covered++; fprintf(outf, "DA:%zu,%" PRIu64 "\n", lno, cov - 1); } lno++; } } else { lno += logdata_blocksize; } } fprintf(outf, "LH:%zu\n", n_covered); fprintf(outf, "LF:%zu\n", n_instrumented); fprintf(outf, "end_of_record\n"); } fclose(outf); } JL_DLLEXPORT void jl_write_coverage_data(const char *output) { uv_mutex_lock(&coverage_lock); if (output) { size_t len = strlen(output); if (len >= 5 && strcmp(output + len - 5, ".info") == 0) { char *formatted = jl_format_filename(output); write_lcov_data(&coverageData, formatted); free(formatted); } } else { char stm[32]; snprintf(stm, sizeof(stm), ".%d.cov", uv_os_getpid()); write_log_data(&coverageData, stm); } uv_mutex_unlock(&coverage_lock); } void jl_write_malloc_log(void) { uv_mutex_lock(&coverage_lock); char stm[32]; snprintf(stm, sizeof(stm), ".%d.mem", uv_os_getpid()); write_log_data(&mallocData, stm); uv_mutex_unlock(&coverage_lock); } void jl_init_coverage(void) { uv_mutex_init(&coverage_lock); strhash_new(&coverageData, 0); strhash_new(&mallocData, 0); }