├── .gitignore ├── Classes ├── CMakeLists.txt ├── GCDAProfiling.c ├── GCDAProfiling.h ├── InstrProfData.inc ├── InstrProfiling.c ├── InstrProfiling.h ├── InstrProfilingBuffer.c ├── InstrProfilingFile.c ├── InstrProfilingInternal.h ├── InstrProfilingMerge.c ├── InstrProfilingMergeFile.c ├── InstrProfilingNameVar.c ├── InstrProfilingPlatformDarwin.c ├── InstrProfilingPlatformFuchsia.c ├── InstrProfilingPlatformLinux.c ├── InstrProfilingPlatformOther.c ├── InstrProfilingPlatformWindows.c ├── InstrProfilingPort.h ├── InstrProfilingRuntime.cc ├── InstrProfilingUtil.c ├── InstrProfilingUtil.h ├── InstrProfilingValue.c ├── InstrProfilingWriter.c ├── WindowsMMap.c └── WindowsMMap.h ├── Coverage.podspec ├── GenerateEnv.py ├── GitAnalyze.py ├── InfoAnalyze.py ├── README.md ├── coverage.py ├── deletePrePush.py ├── exportenv.sh ├── genPrePushFile.py ├── gitdiffmodel.py ├── lcov ├── etc │ └── lcovrc └── usr │ ├── bin │ ├── gendesc │ ├── genhtml │ ├── geninfo │ ├── genpng │ └── lcov │ └── share │ └── man │ ├── man1 │ ├── gendesc.1.gz │ ├── genhtml.1.gz │ ├── geninfo.1.gz │ ├── genpng.1.gz │ └── lcov.1.gz │ └── man5 │ └── lcovrc.5.gz └── lcovinfomodel.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | Coverage/ 3 | Coverage.info 4 | env.sh 5 | diff 6 | *.pyc 7 | -------------------------------------------------------------------------------- /Classes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | CHECK_CXX_SOURCE_COMPILES(" 3 | #ifdef _MSC_VER 4 | #include /* Workaround for PR19898. */ 5 | #include 6 | #endif 7 | int main() { 8 | #ifdef _MSC_VER 9 | volatile LONG val = 1; 10 | MemoryBarrier(); 11 | InterlockedCompareExchange(&val, 0, 1); 12 | InterlockedIncrement(&val); 13 | InterlockedDecrement(&val); 14 | #else 15 | volatile unsigned long val = 1; 16 | __sync_synchronize(); 17 | __sync_val_compare_and_swap(&val, 1, 0); 18 | __sync_add_and_fetch(&val, 1); 19 | __sync_sub_and_fetch(&val, 1); 20 | #endif 21 | return 0; 22 | } 23 | " COMPILER_RT_TARGET_HAS_ATOMICS) 24 | 25 | CHECK_CXX_SOURCE_COMPILES(" 26 | #if defined(__linux__) 27 | #include 28 | #endif 29 | #include 30 | int fd; 31 | int main() { 32 | struct flock s_flock; 33 | 34 | s_flock.l_type = F_WRLCK; 35 | fcntl(fd, F_SETLKW, &s_flock); 36 | return 0; 37 | } 38 | 39 | " COMPILER_RT_TARGET_HAS_FCNTL_LCK) 40 | 41 | CHECK_CXX_SOURCE_COMPILES(" 42 | #include 43 | int main() { 44 | return 0; 45 | } 46 | 47 | " COMPILER_RT_TARGET_HAS_UNAME) 48 | 49 | add_compiler_rt_component(profile) 50 | 51 | set(PROFILE_SOURCES 52 | GCDAProfiling.c 53 | InstrProfiling.c 54 | InstrProfilingValue.c 55 | InstrProfilingBuffer.c 56 | InstrProfilingFile.c 57 | InstrProfilingMerge.c 58 | InstrProfilingMergeFile.c 59 | InstrProfilingNameVar.c 60 | InstrProfilingWriter.c 61 | InstrProfilingPlatformDarwin.c 62 | InstrProfilingPlatformFuchsia.c 63 | InstrProfilingPlatformLinux.c 64 | InstrProfilingPlatformOther.c 65 | InstrProfilingPlatformWindows.c 66 | InstrProfilingRuntime.cc 67 | InstrProfilingUtil.c) 68 | 69 | set(PROFILE_HEADERS 70 | InstrProfData.inc 71 | InstrProfiling.h 72 | InstrProfilingInternal.h 73 | InstrProfilingPort.h 74 | InstrProfilingUtil.h 75 | WindowsMMap.h) 76 | 77 | if(WIN32) 78 | list(APPEND PROFILE_SOURCES WindowsMMap.c) 79 | endif() 80 | 81 | if(FUCHSIA OR UNIX) 82 | set(EXTRA_FLAGS 83 | -fPIC 84 | -Wno-pedantic) 85 | endif() 86 | 87 | if(COMPILER_RT_TARGET_HAS_ATOMICS) 88 | set(EXTRA_FLAGS 89 | ${EXTRA_FLAGS} 90 | -DCOMPILER_RT_HAS_ATOMICS=1) 91 | endif() 92 | 93 | if(COMPILER_RT_TARGET_HAS_FCNTL_LCK) 94 | set(EXTRA_FLAGS 95 | ${EXTRA_FLAGS} 96 | -DCOMPILER_RT_HAS_FCNTL_LCK=1) 97 | endif() 98 | 99 | if(COMPILER_RT_TARGET_HAS_UNAME) 100 | set(EXTRA_FLAGS 101 | ${EXTRA_FLAGS} 102 | -DCOMPILER_RT_HAS_UNAME=1) 103 | endif() 104 | 105 | # This appears to be a C-only warning banning the use of locals in aggregate 106 | # initializers. All other compilers accept this, though. 107 | # nonstandard extension used : 'identifier' : cannot be initialized using address of automatic variable 108 | append_list_if(COMPILER_RT_HAS_WD4221_FLAG /wd4221 EXTRA_FLAGS) 109 | 110 | if(APPLE) 111 | add_compiler_rt_runtime(clang_rt.profile 112 | STATIC 113 | OS ${PROFILE_SUPPORTED_OS} 114 | ARCHS ${PROFILE_SUPPORTED_ARCH} 115 | CFLAGS ${EXTRA_FLAGS} 116 | SOURCES ${PROFILE_SOURCES} 117 | ADDITIONAL_HEADERS ${PROFILE_HEADERS} 118 | PARENT_TARGET profile) 119 | else() 120 | add_compiler_rt_runtime(clang_rt.profile 121 | STATIC 122 | ARCHS ${PROFILE_SUPPORTED_ARCH} 123 | CFLAGS ${EXTRA_FLAGS} 124 | SOURCES ${PROFILE_SOURCES} 125 | ADDITIONAL_HEADERS ${PROFILE_HEADERS} 126 | PARENT_TARGET profile) 127 | endif() 128 | -------------------------------------------------------------------------------- /Classes/GCDAProfiling.c: -------------------------------------------------------------------------------- 1 | /*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | |*===----------------------------------------------------------------------===*| 8 | |* 9 | |* This file implements the call back routines for the gcov profiling 10 | |* instrumentation pass. Link against this library when running code through 11 | |* the -insert-gcov-profiling LLVM pass. 12 | |* 13 | |* We emit files in a corrupt version of GCOV's "gcda" file format. These files 14 | |* are only close enough that LCOV will happily parse them. Anything that lcov 15 | |* ignores is missing. 16 | |* 17 | |* TODO: gcov is multi-process safe by having each exit open the existing file 18 | |* and append to it. We'd like to achieve that and be thread-safe too. 19 | |* 20 | \*===----------------------------------------------------------------------===*/ 21 | 22 | #if !defined(__Fuchsia__) 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if defined(_WIN32) 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | #include "WindowsMMap.h" 34 | #else 35 | #include 36 | #include 37 | #endif 38 | 39 | #if defined(__FreeBSD__) && defined(__i386__) 40 | #define I386_FREEBSD 1 41 | #else 42 | #define I386_FREEBSD 0 43 | #endif 44 | 45 | #if !defined(_MSC_VER) && !I386_FREEBSD 46 | #include 47 | #endif 48 | 49 | #if defined(_MSC_VER) 50 | typedef unsigned char uint8_t; 51 | typedef unsigned int uint32_t; 52 | typedef unsigned long long uint64_t; 53 | #elif I386_FREEBSD 54 | /* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to 55 | * FreeBSD 10, r232261) when compiled in 32-bit mode. 56 | */ 57 | typedef unsigned char uint8_t; 58 | typedef unsigned int uint32_t; 59 | typedef unsigned long long uint64_t; 60 | #endif 61 | 62 | #include "InstrProfiling.h" 63 | #include "InstrProfilingUtil.h" 64 | 65 | /* #define DEBUG_GCDAPROFILING */ 66 | 67 | /* 68 | * --- GCOV file format I/O primitives --- 69 | */ 70 | 71 | /* 72 | * The current file name we're outputting. Used primarily for error logging. 73 | */ 74 | static char *filename = NULL; 75 | 76 | /* 77 | * The current file we're outputting. 78 | */ 79 | static FILE *output_file = NULL; 80 | 81 | /* 82 | * Buffer that we write things into. 83 | */ 84 | #define WRITE_BUFFER_SIZE (128 * 1024) 85 | static unsigned char *write_buffer = NULL; 86 | static uint64_t cur_buffer_size = 0; 87 | static uint64_t cur_pos = 0; 88 | static uint64_t file_size = 0; 89 | static int new_file = 0; 90 | #if defined(_WIN32) 91 | static HANDLE mmap_handle = NULL; 92 | #endif 93 | static int fd = -1; 94 | 95 | typedef void (*fn_ptr)(); 96 | 97 | typedef void* dynamic_object_id; 98 | // The address of this variable identifies a given dynamic object. 99 | static dynamic_object_id current_id; 100 | #define CURRENT_ID (¤t_id) 101 | 102 | struct fn_node { 103 | dynamic_object_id id; 104 | fn_ptr fn; 105 | struct fn_node* next; 106 | }; 107 | 108 | struct fn_list { 109 | struct fn_node *head, *tail; 110 | }; 111 | 112 | /* 113 | * A list of functions to write out the data, shared between all dynamic objects. 114 | */ 115 | struct fn_list writeout_fn_list; 116 | 117 | /* 118 | * A list of flush functions that our __gcov_flush() function should call, shared between all dynamic objects. 119 | */ 120 | struct fn_list flush_fn_list; 121 | 122 | static void fn_list_insert(struct fn_list* list, fn_ptr fn) { 123 | struct fn_node* new_node = malloc(sizeof(struct fn_node)); 124 | new_node->fn = fn; 125 | new_node->next = NULL; 126 | new_node->id = CURRENT_ID; 127 | 128 | if (!list->head) { 129 | list->head = list->tail = new_node; 130 | } else { 131 | list->tail->next = new_node; 132 | list->tail = new_node; 133 | } 134 | } 135 | 136 | static void fn_list_remove(struct fn_list* list) { 137 | struct fn_node* curr = list->head; 138 | struct fn_node* prev = NULL; 139 | struct fn_node* next = NULL; 140 | 141 | while (curr) { 142 | next = curr->next; 143 | 144 | if (curr->id == CURRENT_ID) { 145 | if (curr == list->head) { 146 | list->head = next; 147 | } 148 | 149 | if (curr == list->tail) { 150 | list->tail = prev; 151 | } 152 | 153 | if (prev) { 154 | prev->next = next; 155 | } 156 | 157 | free(curr); 158 | } else { 159 | prev = curr; 160 | } 161 | 162 | curr = next; 163 | } 164 | } 165 | 166 | static void resize_write_buffer(uint64_t size) { 167 | if (!new_file) return; 168 | size += cur_pos; 169 | if (size <= cur_buffer_size) return; 170 | size = (size - 1) / WRITE_BUFFER_SIZE + 1; 171 | size *= WRITE_BUFFER_SIZE; 172 | write_buffer = realloc(write_buffer, size); 173 | cur_buffer_size = size; 174 | } 175 | 176 | static void write_bytes(const char *s, size_t len) { 177 | resize_write_buffer(len); 178 | memcpy(&write_buffer[cur_pos], s, len); 179 | cur_pos += len; 180 | } 181 | 182 | static void write_32bit_value(uint32_t i) { 183 | write_bytes((char*)&i, 4); 184 | } 185 | 186 | static void write_64bit_value(uint64_t i) { 187 | // GCOV uses a lo-/hi-word format even on big-endian systems. 188 | // See also GCOVBuffer::readInt64 in LLVM. 189 | uint32_t lo = (uint32_t) i; 190 | uint32_t hi = (uint32_t) (i >> 32); 191 | write_32bit_value(lo); 192 | write_32bit_value(hi); 193 | } 194 | 195 | static uint32_t length_of_string(const char *s) { 196 | return (strlen(s) / 4) + 1; 197 | } 198 | 199 | static void write_string(const char *s) { 200 | uint32_t len = length_of_string(s); 201 | write_32bit_value(len); 202 | write_bytes(s, strlen(s)); 203 | write_bytes("\0\0\0\0", 4 - (strlen(s) % 4)); 204 | } 205 | 206 | static uint32_t read_32bit_value() { 207 | uint32_t val; 208 | 209 | if (new_file) 210 | return (uint32_t)-1; 211 | 212 | val = *(uint32_t*)&write_buffer[cur_pos]; 213 | cur_pos += 4; 214 | return val; 215 | } 216 | 217 | static uint32_t read_le_32bit_value() { 218 | uint32_t val = 0; 219 | int i; 220 | 221 | if (new_file) 222 | return (uint32_t)-1; 223 | 224 | for (i = 0; i < 4; i++) 225 | val |= write_buffer[cur_pos++] << (8*i); 226 | return val; 227 | } 228 | 229 | static uint64_t read_64bit_value() { 230 | // GCOV uses a lo-/hi-word format even on big-endian systems. 231 | // See also GCOVBuffer::readInt64 in LLVM. 232 | uint32_t lo = read_32bit_value(); 233 | uint32_t hi = read_32bit_value(); 234 | return ((uint64_t)hi << 32) | ((uint64_t)lo); 235 | } 236 | 237 | static char *mangle_filename(const char *orig_filename) { 238 | char *new_filename; 239 | size_t prefix_len; 240 | int prefix_strip; 241 | const char *prefix = lprofGetPathPrefix(&prefix_strip, &prefix_len); 242 | 243 | if (prefix == NULL) 244 | return strdup(orig_filename); 245 | 246 | new_filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); 247 | lprofApplyPathPrefix(new_filename, orig_filename, prefix, prefix_len, 248 | prefix_strip); 249 | 250 | return new_filename; 251 | } 252 | 253 | static int map_file() { 254 | fseek(output_file, 0L, SEEK_END); 255 | file_size = ftell(output_file); 256 | 257 | /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an 258 | * error message because it should "just work" for the user. */ 259 | if (file_size == 0) 260 | return -1; 261 | 262 | #if defined(_WIN32) 263 | HANDLE mmap_fd; 264 | if (fd == -1) 265 | mmap_fd = INVALID_HANDLE_VALUE; 266 | else 267 | mmap_fd = (HANDLE)_get_osfhandle(fd); 268 | 269 | mmap_handle = CreateFileMapping(mmap_fd, NULL, PAGE_READWRITE, DWORD_HI(file_size), DWORD_LO(file_size), NULL); 270 | if (mmap_handle == NULL) { 271 | fprintf(stderr, "profiling: %s: cannot create file mapping: %lu\n", 272 | filename, GetLastError()); 273 | return -1; 274 | } 275 | 276 | write_buffer = MapViewOfFile(mmap_handle, FILE_MAP_WRITE, 0, 0, file_size); 277 | if (write_buffer == NULL) { 278 | fprintf(stderr, "profiling: %s: cannot map: %lu\n", filename, 279 | GetLastError()); 280 | CloseHandle(mmap_handle); 281 | return -1; 282 | } 283 | #else 284 | write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, 285 | MAP_FILE | MAP_SHARED, fd, 0); 286 | if (write_buffer == (void *)-1) { 287 | int errnum = errno; 288 | fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, 289 | strerror(errnum)); 290 | return -1; 291 | } 292 | #endif 293 | 294 | return 0; 295 | } 296 | 297 | static void unmap_file() { 298 | #if defined(_WIN32) 299 | if (!FlushViewOfFile(write_buffer, file_size)) { 300 | fprintf(stderr, "profiling: %s: cannot flush mapped view: %lu\n", filename, 301 | GetLastError()); 302 | } 303 | 304 | if (!UnmapViewOfFile(write_buffer)) { 305 | fprintf(stderr, "profiling: %s: cannot unmap mapped view: %lu\n", filename, 306 | GetLastError()); 307 | } 308 | 309 | if (!CloseHandle(mmap_handle)) { 310 | fprintf(stderr, "profiling: %s: cannot close file mapping handle: %lu\n", 311 | filename, GetLastError()); 312 | } 313 | 314 | mmap_handle = NULL; 315 | #else 316 | if (msync(write_buffer, file_size, MS_SYNC) == -1) { 317 | int errnum = errno; 318 | fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename, 319 | strerror(errnum)); 320 | } 321 | 322 | /* We explicitly ignore errors from unmapping because at this point the data 323 | * is written and we don't care. 324 | */ 325 | (void)munmap(write_buffer, file_size); 326 | #endif 327 | 328 | write_buffer = NULL; 329 | file_size = 0; 330 | } 331 | 332 | /* 333 | * --- LLVM line counter API --- 334 | */ 335 | 336 | /* A file in this case is a translation unit. Each .o file built with line 337 | * profiling enabled will emit to a different file. Only one file may be 338 | * started at a time. 339 | */ 340 | COMPILER_RT_VISIBILITY 341 | void llvm_gcda_start_file(const char *orig_filename, const char version[4], 342 | uint32_t checksum) { 343 | const char *mode = "r+b"; 344 | filename = mangle_filename(orig_filename); 345 | 346 | /* Try just opening the file. */ 347 | new_file = 0; 348 | fd = open(filename, O_RDWR | O_BINARY); 349 | 350 | if (fd == -1) { 351 | /* Try opening the file, creating it if necessary. */ 352 | new_file = 1; 353 | mode = "w+b"; 354 | fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); 355 | if (fd == -1) { 356 | /* Try creating the directories first then opening the file. */ 357 | __llvm_profile_recursive_mkdir(filename); 358 | fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); 359 | if (fd == -1) { 360 | /* Bah! It's hopeless. */ 361 | int errnum = errno; 362 | fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, 363 | strerror(errnum)); 364 | return; 365 | } 366 | } 367 | } 368 | 369 | /* Try to flock the file to serialize concurrent processes writing out to the 370 | * same GCDA. This can fail if the filesystem doesn't support it, but in that 371 | * case we'll just carry on with the old racy behaviour and hope for the best. 372 | */ 373 | lprofLockFd(fd); 374 | output_file = fdopen(fd, mode); 375 | 376 | /* Initialize the write buffer. */ 377 | write_buffer = NULL; 378 | cur_buffer_size = 0; 379 | cur_pos = 0; 380 | 381 | if (new_file) { 382 | resize_write_buffer(WRITE_BUFFER_SIZE); 383 | memset(write_buffer, 0, WRITE_BUFFER_SIZE); 384 | } else { 385 | if (map_file() == -1) { 386 | /* mmap failed, try to recover by clobbering */ 387 | new_file = 1; 388 | write_buffer = NULL; 389 | cur_buffer_size = 0; 390 | resize_write_buffer(WRITE_BUFFER_SIZE); 391 | memset(write_buffer, 0, WRITE_BUFFER_SIZE); 392 | } 393 | } 394 | 395 | /* gcda file, version, stamp checksum. */ 396 | write_bytes("adcg", 4); 397 | write_bytes(version, 4); 398 | write_32bit_value(checksum); 399 | 400 | #ifdef DEBUG_GCDAPROFILING 401 | fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); 402 | #endif 403 | } 404 | 405 | /* Given an array of pointers to counters (counters), increment the n-th one, 406 | * where we're also given a pointer to n (predecessor). 407 | */ 408 | COMPILER_RT_VISIBILITY 409 | void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, 410 | uint64_t **counters) { 411 | uint64_t *counter; 412 | uint32_t pred; 413 | 414 | pred = *predecessor; 415 | if (pred == 0xffffffff) 416 | return; 417 | counter = counters[pred]; 418 | 419 | /* Don't crash if the pred# is out of sync. This can happen due to threads, 420 | or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ 421 | if (counter) 422 | ++*counter; 423 | #ifdef DEBUG_GCDAPROFILING 424 | else 425 | fprintf(stderr, 426 | "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", 427 | *counter, *predecessor); 428 | #endif 429 | } 430 | 431 | COMPILER_RT_VISIBILITY 432 | void llvm_gcda_emit_function(uint32_t ident, const char *function_name, 433 | uint32_t func_checksum, uint8_t use_extra_checksum, 434 | uint32_t cfg_checksum) { 435 | uint32_t len = 2; 436 | 437 | if (use_extra_checksum) 438 | len++; 439 | #ifdef DEBUG_GCDAPROFILING 440 | fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, 441 | function_name ? function_name : "NULL"); 442 | #endif 443 | if (!output_file) return; 444 | 445 | /* function tag */ 446 | write_bytes("\0\0\0\1", 4); 447 | if (function_name) 448 | len += 1 + length_of_string(function_name); 449 | write_32bit_value(len); 450 | write_32bit_value(ident); 451 | write_32bit_value(func_checksum); 452 | if (use_extra_checksum) 453 | write_32bit_value(cfg_checksum); 454 | if (function_name) 455 | write_string(function_name); 456 | } 457 | 458 | COMPILER_RT_VISIBILITY 459 | void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { 460 | uint32_t i; 461 | uint64_t *old_ctrs = NULL; 462 | uint32_t val = 0; 463 | uint64_t save_cur_pos = cur_pos; 464 | 465 | if (!output_file) return; 466 | 467 | val = read_le_32bit_value(); 468 | 469 | if (val != (uint32_t)-1) { 470 | /* There are counters present in the file. Merge them. */ 471 | if (val != 0x01a10000) { 472 | fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 473 | "corrupt arc tag (0x%08x)\n", 474 | filename, val); 475 | remove(filename); 476 | return; 477 | } 478 | 479 | val = read_32bit_value(); 480 | if (val == (uint32_t)-1 || val / 2 != num_counters) { 481 | fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " 482 | "mismatched number of counters (%d)\n", 483 | filename, val); 484 | remove(filename); 485 | return; 486 | } 487 | 488 | old_ctrs = malloc(sizeof(uint64_t) * num_counters); 489 | for (i = 0; i < num_counters; ++i) 490 | old_ctrs[i] = read_64bit_value(); 491 | } 492 | 493 | cur_pos = save_cur_pos; 494 | 495 | /* Counter #1 (arcs) tag */ 496 | write_bytes("\0\0\xa1\1", 4); 497 | write_32bit_value(num_counters * 2); 498 | for (i = 0; i < num_counters; ++i) { 499 | counters[i] += (old_ctrs ? old_ctrs[i] : 0); 500 | write_64bit_value(counters[i]); 501 | } 502 | 503 | free(old_ctrs); 504 | 505 | #ifdef DEBUG_GCDAPROFILING 506 | fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); 507 | for (i = 0; i < num_counters; ++i) 508 | fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); 509 | #endif 510 | } 511 | 512 | COMPILER_RT_VISIBILITY 513 | void llvm_gcda_summary_info() { 514 | const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */ 515 | uint32_t i; 516 | uint32_t runs = 1; 517 | static uint32_t run_counted = 0; // We only want to increase the run count once. 518 | uint32_t val = 0; 519 | uint64_t save_cur_pos = cur_pos; 520 | 521 | if (!output_file) return; 522 | 523 | val = read_le_32bit_value(); 524 | 525 | if (val != (uint32_t)-1) { 526 | /* There are counters present in the file. Merge them. */ 527 | if (val != 0xa1000000) { 528 | fprintf(stderr, "profiling: %s: cannot merge previous run count: " 529 | "corrupt object tag (0x%08x)\n", 530 | filename, val); 531 | remove(filename); 532 | return; 533 | } 534 | 535 | val = read_32bit_value(); /* length */ 536 | if (val != obj_summary_len) { 537 | fprintf(stderr, "profiling: %s: cannot merge previous run count: " 538 | "mismatched object length (%d)\n", 539 | filename, val); 540 | remove(filename); 541 | return; 542 | } 543 | 544 | read_32bit_value(); /* checksum, unused */ 545 | read_32bit_value(); /* num, unused */ 546 | uint32_t prev_runs = read_32bit_value(); 547 | /* Add previous run count to new counter, if not already counted before. */ 548 | runs = run_counted ? prev_runs : prev_runs + 1; 549 | } 550 | 551 | cur_pos = save_cur_pos; 552 | 553 | /* Object summary tag */ 554 | write_bytes("\0\0\0\xa1", 4); 555 | write_32bit_value(obj_summary_len); 556 | write_32bit_value(0); /* checksum, unused */ 557 | write_32bit_value(0); /* num, unused */ 558 | write_32bit_value(runs); 559 | for (i = 3; i < obj_summary_len; ++i) 560 | write_32bit_value(0); 561 | 562 | /* Program summary tag */ 563 | write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */ 564 | write_32bit_value(0); /* 0 length */ 565 | 566 | run_counted = 1; 567 | 568 | #ifdef DEBUG_GCDAPROFILING 569 | fprintf(stderr, "llvmgcda: %u runs\n", runs); 570 | #endif 571 | } 572 | 573 | COMPILER_RT_VISIBILITY 574 | void llvm_gcda_end_file() { 575 | /* Write out EOF record. */ 576 | if (output_file) { 577 | write_bytes("\0\0\0\0\0\0\0\0", 8); 578 | 579 | if (new_file) { 580 | fwrite(write_buffer, cur_pos, 1, output_file); 581 | free(write_buffer); 582 | } else { 583 | unmap_file(); 584 | } 585 | 586 | fflush(output_file); 587 | lprofUnlockFd(fd); 588 | fclose(output_file); 589 | output_file = NULL; 590 | write_buffer = NULL; 591 | } 592 | free(filename); 593 | 594 | #ifdef DEBUG_GCDAPROFILING 595 | fprintf(stderr, "llvmgcda: -----\n"); 596 | #endif 597 | } 598 | 599 | COMPILER_RT_VISIBILITY 600 | void llvm_register_writeout_function(fn_ptr fn) { 601 | fn_list_insert(&writeout_fn_list, fn); 602 | } 603 | 604 | COMPILER_RT_VISIBILITY 605 | void llvm_writeout_files(void) { 606 | struct fn_node *curr = writeout_fn_list.head; 607 | 608 | while (curr) { 609 | if (curr->id == CURRENT_ID) { 610 | curr->fn(); 611 | } 612 | curr = curr->next; 613 | } 614 | } 615 | 616 | COMPILER_RT_VISIBILITY 617 | void llvm_delete_writeout_function_list(void) { 618 | fn_list_remove(&writeout_fn_list); 619 | } 620 | 621 | COMPILER_RT_VISIBILITY 622 | void llvm_register_flush_function(fn_ptr fn) { 623 | fn_list_insert(&flush_fn_list, fn); 624 | } 625 | 626 | void __gcov_flush_r() { 627 | struct fn_node* curr = flush_fn_list.head; 628 | 629 | while (curr) { 630 | curr->fn(); 631 | curr = curr->next; 632 | } 633 | } 634 | 635 | COMPILER_RT_VISIBILITY 636 | void llvm_delete_flush_function_list(void) { 637 | fn_list_remove(&flush_fn_list); 638 | } 639 | 640 | COMPILER_RT_VISIBILITY 641 | void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn) { 642 | static int atexit_ran = 0; 643 | 644 | if (wfn) 645 | llvm_register_writeout_function(wfn); 646 | 647 | if (ffn) 648 | llvm_register_flush_function(ffn); 649 | 650 | if (atexit_ran == 0) { 651 | atexit_ran = 1; 652 | 653 | /* Make sure we write out the data and delete the data structures. */ 654 | atexit(llvm_delete_flush_function_list); 655 | atexit(llvm_delete_writeout_function_list); 656 | atexit(llvm_writeout_files); 657 | } 658 | } 659 | 660 | #endif 661 | -------------------------------------------------------------------------------- /Classes/GCDAProfiling.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define CODE_CCOVER_START NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);\ 4 | NSString *documentsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"gcda_files"];\ 5 | setenv("GCOV_PREFIX", [documentsDirectory cStringUsingEncoding:NSUTF8StringEncoding], 1);\ 6 | setenv("GCOV_PREFIX_STRIP", "14", 1);\ 7 | dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));\ 8 | dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);\ 9 | dispatch_source_set_event_handler(timer, ^{\ 10 | __gcov_flush_r();\ 11 | });\ 12 | dispatch_resume(timer);\ 13 | 14 | 15 | void __gcov_flush_r(); 16 | -------------------------------------------------------------------------------- /Classes/InstrProfiling.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfiling.c - Support library for PGO instrumentation ---------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "InstrProfiling.h" 15 | #include "InstrProfilingInternal.h" 16 | 17 | #define INSTR_PROF_VALUE_PROF_DATA 18 | #include "InstrProfData.inc" 19 | 20 | 21 | COMPILER_RT_WEAK uint64_t INSTR_PROF_RAW_VERSION_VAR = INSTR_PROF_RAW_VERSION; 22 | 23 | COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) { 24 | return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64) 25 | : (INSTR_PROF_RAW_MAGIC_32); 26 | } 27 | 28 | static unsigned ProfileDumped = 0; 29 | 30 | COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { 31 | return ProfileDumped; 32 | } 33 | 34 | COMPILER_RT_VISIBILITY void lprofSetProfileDumped() { 35 | ProfileDumped = 1; 36 | } 37 | 38 | /* Return the number of bytes needed to add to SizeInBytes to make it 39 | * the result a multiple of 8. 40 | */ 41 | COMPILER_RT_VISIBILITY uint8_t 42 | __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) { 43 | return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t)); 44 | } 45 | 46 | COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_version(void) { 47 | return __llvm_profile_raw_version; 48 | } 49 | 50 | COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) { 51 | uint64_t *I = __llvm_profile_begin_counters(); 52 | uint64_t *E = __llvm_profile_end_counters(); 53 | 54 | memset(I, 0, sizeof(uint64_t) * (E - I)); 55 | 56 | const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 57 | const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 58 | const __llvm_profile_data *DI; 59 | for (DI = DataBegin; DI < DataEnd; ++DI) { 60 | uint64_t CurrentVSiteCount = 0; 61 | uint32_t VKI, i; 62 | if (!DI->Values) 63 | continue; 64 | 65 | ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values; 66 | 67 | for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) 68 | CurrentVSiteCount += DI->NumValueSites[VKI]; 69 | 70 | for (i = 0; i < CurrentVSiteCount; ++i) { 71 | ValueProfNode *CurrentVNode = ValueCounters[i]; 72 | 73 | while (CurrentVNode) { 74 | CurrentVNode->Count = 0; 75 | CurrentVNode = CurrentVNode->Next; 76 | } 77 | } 78 | } 79 | ProfileDumped = 0; 80 | } 81 | -------------------------------------------------------------------------------- /Classes/InstrProfiling.h: -------------------------------------------------------------------------------- 1 | /*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifndef PROFILE_INSTRPROFILING_H_ 10 | #define PROFILE_INSTRPROFILING_H_ 11 | 12 | #include "InstrProfilingPort.h" 13 | 14 | #define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY 15 | #include "InstrProfData.inc" 16 | 17 | enum ValueKind { 18 | #define VALUE_PROF_KIND(Enumerator, Value) Enumerator = Value, 19 | #include "InstrProfData.inc" 20 | }; 21 | 22 | typedef void *IntPtrT; 23 | typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT) 24 | __llvm_profile_data { 25 | #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) Type Name; 26 | #include "InstrProfData.inc" 27 | } __llvm_profile_data; 28 | 29 | typedef struct __llvm_profile_header { 30 | #define INSTR_PROF_RAW_HEADER(Type, Name, Initializer) Type Name; 31 | #include "InstrProfData.inc" 32 | } __llvm_profile_header; 33 | 34 | typedef struct ValueProfNode * PtrToNodeT; 35 | typedef struct ValueProfNode { 36 | #define INSTR_PROF_VALUE_NODE(Type, LLVMType, Name, Initializer) Type Name; 37 | #include "InstrProfData.inc" 38 | } ValueProfNode; 39 | 40 | /*! 41 | * \brief Get number of bytes necessary to pad the argument to eight 42 | * byte boundary. 43 | */ 44 | uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes); 45 | 46 | /*! 47 | * \brief Get required size for profile buffer. 48 | */ 49 | uint64_t __llvm_profile_get_size_for_buffer(void); 50 | 51 | /*! 52 | * \brief Write instrumentation data to the given buffer. 53 | * 54 | * \pre \c Buffer is the start of a buffer at least as big as \a 55 | * __llvm_profile_get_size_for_buffer(). 56 | */ 57 | int __llvm_profile_write_buffer(char *Buffer); 58 | 59 | const __llvm_profile_data *__llvm_profile_begin_data(void); 60 | const __llvm_profile_data *__llvm_profile_end_data(void); 61 | const char *__llvm_profile_begin_names(void); 62 | const char *__llvm_profile_end_names(void); 63 | uint64_t *__llvm_profile_begin_counters(void); 64 | uint64_t *__llvm_profile_end_counters(void); 65 | ValueProfNode *__llvm_profile_begin_vnodes(); 66 | ValueProfNode *__llvm_profile_end_vnodes(); 67 | 68 | /*! 69 | * \brief Clear profile counters to zero. 70 | * 71 | */ 72 | void __llvm_profile_reset_counters(void); 73 | 74 | /*! 75 | * \brief Merge profile data from buffer. 76 | * 77 | * Read profile data form buffer \p Profile and merge with 78 | * in-process profile counters. The client is expected to 79 | * have checked or already knows the profile data in the 80 | * buffer matches the in-process counter structure before 81 | * calling it. 82 | */ 83 | void __llvm_profile_merge_from_buffer(const char *Profile, uint64_t Size); 84 | 85 | /*! \brief Check if profile in buffer matches the current binary. 86 | * 87 | * Returns 0 (success) if the profile data in buffer \p Profile with size 88 | * \p Size was generated by the same binary and therefore matches 89 | * structurally the in-process counters. If the profile data in buffer is 90 | * not compatible, the interface returns 1 (failure). 91 | */ 92 | int __llvm_profile_check_compatibility(const char *Profile, 93 | uint64_t Size); 94 | 95 | /*! 96 | * \brief Counts the number of times a target value is seen. 97 | * 98 | * Records the target value for the CounterIndex if not seen before. Otherwise, 99 | * increments the counter associated w/ the target value. 100 | * void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data, 101 | * uint32_t CounterIndex); 102 | */ 103 | void INSTR_PROF_VALUE_PROF_FUNC( 104 | #define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType) ArgType ArgName 105 | #include "InstrProfData.inc" 106 | ); 107 | 108 | void __llvm_profile_instrument_target_value(uint64_t TargetValue, void *Data, 109 | uint32_t CounterIndex, 110 | uint64_t CounterValue); 111 | 112 | /*! 113 | * \brief Write instrumentation data to the current file. 114 | * 115 | * Writes to the file with the last name given to \a * 116 | * __llvm_profile_set_filename(), 117 | * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable, 118 | * or if that's not set, the last name set to INSTR_PROF_PROFILE_NAME_VAR, 119 | * or if that's not set, \c "default.profraw". 120 | */ 121 | int __llvm_profile_write_file(void); 122 | 123 | /*! 124 | * \brief this is a wrapper interface to \c __llvm_profile_write_file. 125 | * After this interface is invoked, a arleady dumped flag will be set 126 | * so that profile won't be dumped again during program exit. 127 | * Invocation of interface __llvm_profile_reset_counters will clear 128 | * the flag. This interface is designed to be used to collect profile 129 | * data from user selected hot regions. The use model is 130 | * __llvm_profile_reset_counters(); 131 | * ... hot region 1 132 | * __llvm_profile_dump(); 133 | * .. some other code 134 | * __llvm_profile_reset_counters(); 135 | * ... hot region 2 136 | * __llvm_profile_dump(); 137 | * 138 | * It is expected that on-line profile merging is on with \c %m specifier 139 | * used in profile filename . If merging is not turned on, user is expected 140 | * to invoke __llvm_profile_set_filename to specify different profile names 141 | * for different regions before dumping to avoid profile write clobbering. 142 | */ 143 | int __llvm_profile_dump(void); 144 | 145 | /*! 146 | * \brief Set the filename for writing instrumentation data. 147 | * 148 | * Sets the filename to be used for subsequent calls to 149 | * \a __llvm_profile_write_file(). 150 | * 151 | * \c Name is not copied, so it must remain valid. Passing NULL resets the 152 | * filename logic to the default behaviour. 153 | */ 154 | void __llvm_profile_set_filename(const char *Name); 155 | 156 | /*! \brief Register to write instrumentation data to file at exit. */ 157 | int __llvm_profile_register_write_file_atexit(void); 158 | 159 | /*! \brief Initialize file handling. */ 160 | void __llvm_profile_initialize_file(void); 161 | 162 | /*! 163 | * \brief Return path prefix (excluding the base filename) of the profile data. 164 | * This is useful for users using \c -fprofile-generate=./path_prefix who do 165 | * not care about the default raw profile name. It is also useful to collect 166 | * more than more profile data files dumped in the same directory (Online 167 | * merge mode is turned on for instrumented programs with shared libs). 168 | * Side-effect: this API call will invoke malloc with dynamic memory allocation. 169 | */ 170 | const char *__llvm_profile_get_path_prefix(); 171 | 172 | /*! 173 | * \brief Return filename (including path) of the profile data. Note that if the 174 | * user calls __llvm_profile_set_filename later after invoking this interface, 175 | * the actual file name may differ from what is returned here. 176 | * Side-effect: this API call will invoke malloc with dynamic memory allocation. 177 | */ 178 | const char *__llvm_profile_get_filename(); 179 | 180 | /*! \brief Get the magic token for the file format. */ 181 | uint64_t __llvm_profile_get_magic(void); 182 | 183 | /*! \brief Get the version of the file format. */ 184 | uint64_t __llvm_profile_get_version(void); 185 | 186 | /*! \brief Get the number of entries in the profile data section. */ 187 | uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin, 188 | const __llvm_profile_data *End); 189 | 190 | /*! 191 | * This variable is defined in InstrProfilingRuntime.cc as a hidden 192 | * symbol. Its main purpose is to enable profile runtime user to 193 | * bypass runtime initialization code -- if the client code explicitly 194 | * define this variable, then InstProfileRuntime.o won't be linked in. 195 | * Note that this variable's visibility needs to be hidden so that the 196 | * definition of this variable in an instrumented shared library won't 197 | * affect runtime initialization decision of the main program. 198 | * __llvm_profile_profile_runtime. */ 199 | COMPILER_RT_VISIBILITY extern int INSTR_PROF_PROFILE_RUNTIME_VAR; 200 | 201 | /*! 202 | * This variable is defined in InstrProfiling.c. Its main purpose is to 203 | * encode the raw profile version value and other format related information 204 | * such as whether the profile is from IR based instrumentation. The variable 205 | * is defined as weak so that compiler can emit an overriding definition 206 | * depending on user option. Since we don't support mixing FE and IR based 207 | * data in the same raw profile data file (in other words, shared libs and 208 | * main program are expected to be instrumented in the same way), there is 209 | * no need for this variable to be hidden. 210 | */ 211 | extern uint64_t INSTR_PROF_RAW_VERSION_VAR; /* __llvm_profile_raw_version */ 212 | 213 | /*! 214 | * This variable is a weak symbol defined in InstrProfiling.c. It allows 215 | * compiler instrumentation to provide overriding definition with value 216 | * from compiler command line. This variable has default visibility. 217 | */ 218 | extern char INSTR_PROF_PROFILE_NAME_VAR[1]; /* __llvm_profile_filename. */ 219 | 220 | #endif /* PROFILE_INSTRPROFILING_H_ */ 221 | -------------------------------------------------------------------------------- /Classes/InstrProfilingBuffer.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingBuffer.c - Write instrumentation to a memory buffer --===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include "InstrProfiling.h" 10 | #include "InstrProfilingInternal.h" 11 | 12 | COMPILER_RT_VISIBILITY 13 | uint64_t __llvm_profile_get_size_for_buffer(void) { 14 | const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 15 | const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 16 | const uint64_t *CountersBegin = __llvm_profile_begin_counters(); 17 | const uint64_t *CountersEnd = __llvm_profile_end_counters(); 18 | const char *NamesBegin = __llvm_profile_begin_names(); 19 | const char *NamesEnd = __llvm_profile_end_names(); 20 | 21 | return __llvm_profile_get_size_for_buffer_internal( 22 | DataBegin, DataEnd, CountersBegin, CountersEnd, NamesBegin, NamesEnd); 23 | } 24 | 25 | COMPILER_RT_VISIBILITY 26 | uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin, 27 | const __llvm_profile_data *End) { 28 | intptr_t BeginI = (intptr_t)Begin, EndI = (intptr_t)End; 29 | return ((EndI + sizeof(__llvm_profile_data) - 1) - BeginI) / 30 | sizeof(__llvm_profile_data); 31 | } 32 | 33 | COMPILER_RT_VISIBILITY 34 | uint64_t __llvm_profile_get_size_for_buffer_internal( 35 | const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, 36 | const uint64_t *CountersBegin, const uint64_t *CountersEnd, 37 | const char *NamesBegin, const char *NamesEnd) { 38 | /* Match logic in __llvm_profile_write_buffer(). */ 39 | const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char); 40 | const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); 41 | return sizeof(__llvm_profile_header) + 42 | (__llvm_profile_get_data_size(DataBegin, DataEnd) * 43 | sizeof(__llvm_profile_data)) + 44 | (CountersEnd - CountersBegin) * sizeof(uint64_t) + NamesSize + Padding; 45 | } 46 | 47 | COMPILER_RT_VISIBILITY 48 | void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer) { 49 | BufferWriter->Write = lprofBufferWriter; 50 | BufferWriter->WriterCtx = Buffer; 51 | } 52 | 53 | COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer(char *Buffer) { 54 | ProfDataWriter BufferWriter; 55 | initBufferWriter(&BufferWriter, Buffer); 56 | return lprofWriteData(&BufferWriter, 0, 0); 57 | } 58 | 59 | COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer_internal( 60 | char *Buffer, const __llvm_profile_data *DataBegin, 61 | const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, 62 | const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) { 63 | ProfDataWriter BufferWriter; 64 | initBufferWriter(&BufferWriter, Buffer); 65 | return lprofWriteDataImpl(&BufferWriter, DataBegin, DataEnd, CountersBegin, 66 | CountersEnd, 0, NamesBegin, NamesEnd, 0); 67 | } 68 | -------------------------------------------------------------------------------- /Classes/InstrProfilingFile.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #if !defined(__Fuchsia__) 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #ifdef _MSC_VER 16 | /* For _alloca. */ 17 | #include 18 | #endif 19 | #if defined(_WIN32) 20 | #include "WindowsMMap.h" 21 | /* For _chsize_s */ 22 | #include 23 | #include 24 | #else 25 | #include 26 | #include 27 | #include 28 | #if defined(__linux__) 29 | #include 30 | #endif 31 | #endif 32 | 33 | #include "InstrProfiling.h" 34 | #include "InstrProfilingInternal.h" 35 | #include "InstrProfilingUtil.h" 36 | 37 | /* From where is profile name specified. 38 | * The order the enumerators define their 39 | * precedence. Re-order them may lead to 40 | * runtime behavior change. */ 41 | typedef enum ProfileNameSpecifier { 42 | PNS_unknown = 0, 43 | PNS_default, 44 | PNS_command_line, 45 | PNS_environment, 46 | PNS_runtime_api 47 | } ProfileNameSpecifier; 48 | 49 | static const char *getPNSStr(ProfileNameSpecifier PNS) { 50 | switch (PNS) { 51 | case PNS_default: 52 | return "default setting"; 53 | case PNS_command_line: 54 | return "command line"; 55 | case PNS_environment: 56 | return "environment variable"; 57 | case PNS_runtime_api: 58 | return "runtime API"; 59 | default: 60 | return "Unknown"; 61 | } 62 | } 63 | 64 | #define MAX_PID_SIZE 16 65 | /* Data structure holding the result of parsed filename pattern. */ 66 | typedef struct lprofFilename { 67 | /* File name string possibly with %p or %h specifiers. */ 68 | const char *FilenamePat; 69 | /* A flag indicating if FilenamePat's memory is allocated 70 | * by runtime. */ 71 | unsigned OwnsFilenamePat; 72 | const char *ProfilePathPrefix; 73 | const char *Filename; 74 | char PidChars[MAX_PID_SIZE]; 75 | char Hostname[COMPILER_RT_MAX_HOSTLEN]; 76 | unsigned NumPids; 77 | unsigned NumHosts; 78 | /* When in-process merging is enabled, this parameter specifies 79 | * the total number of profile data files shared by all the processes 80 | * spawned from the same binary. By default the value is 1. If merging 81 | * is not enabled, its value should be 0. This parameter is specified 82 | * by the %[0-9]m specifier. For instance %2m enables merging using 83 | * 2 profile data files. %1m is equivalent to %m. Also %m specifier 84 | * can only appear once at the end of the name pattern. */ 85 | unsigned MergePoolSize; 86 | ProfileNameSpecifier PNS; 87 | } lprofFilename; 88 | 89 | COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0}, 90 | {0}, 0, 0, 0, PNS_unknown}; 91 | 92 | static int getCurFilenameLength(); 93 | static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf); 94 | static unsigned doMerging() { return lprofCurFilename.MergePoolSize; } 95 | 96 | /* Return 1 if there is an error, otherwise return 0. */ 97 | static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 98 | uint32_t NumIOVecs) { 99 | uint32_t I; 100 | FILE *File = (FILE *)This->WriterCtx; 101 | for (I = 0; I < NumIOVecs; I++) { 102 | if (IOVecs[I].Data) { 103 | if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != 104 | IOVecs[I].NumElm) 105 | return 1; 106 | } else { 107 | if (fseek(File, IOVecs[I].ElmSize * IOVecs[I].NumElm, SEEK_CUR) == -1) 108 | return 1; 109 | } 110 | } 111 | return 0; 112 | } 113 | 114 | static void initFileWriter(ProfDataWriter *This, FILE *File) { 115 | This->Write = fileWriter; 116 | This->WriterCtx = File; 117 | } 118 | 119 | COMPILER_RT_VISIBILITY ProfBufferIO * 120 | lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { 121 | FreeHook = &free; 122 | DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); 123 | VPBufferSize = BufferSz; 124 | ProfDataWriter *fileWriter = 125 | (ProfDataWriter *)calloc(sizeof(ProfDataWriter), 1); 126 | initFileWriter(fileWriter, File); 127 | ProfBufferIO *IO = lprofCreateBufferIO(fileWriter); 128 | IO->OwnFileWriter = 1; 129 | return IO; 130 | } 131 | 132 | static void setupIOBuffer() { 133 | const char *BufferSzStr = 0; 134 | BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); 135 | if (BufferSzStr && BufferSzStr[0]) { 136 | VPBufferSize = atoi(BufferSzStr); 137 | DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); 138 | } 139 | } 140 | 141 | /* Read profile data in \c ProfileFile and merge with in-memory 142 | profile counters. Returns -1 if there is fatal error, otheriwse 143 | 0 is returned. Returning 0 does not mean merge is actually 144 | performed. If merge is actually done, *MergeDone is set to 1. 145 | */ 146 | static int doProfileMerging(FILE *ProfileFile, int *MergeDone) { 147 | uint64_t ProfileFileSize; 148 | char *ProfileBuffer; 149 | 150 | if (fseek(ProfileFile, 0L, SEEK_END) == -1) { 151 | PROF_ERR("Unable to merge profile data, unable to get size: %s\n", 152 | strerror(errno)); 153 | return -1; 154 | } 155 | ProfileFileSize = ftell(ProfileFile); 156 | 157 | /* Restore file offset. */ 158 | if (fseek(ProfileFile, 0L, SEEK_SET) == -1) { 159 | PROF_ERR("Unable to merge profile data, unable to rewind: %s\n", 160 | strerror(errno)); 161 | return -1; 162 | } 163 | 164 | /* Nothing to merge. */ 165 | if (ProfileFileSize < sizeof(__llvm_profile_header)) { 166 | if (ProfileFileSize) 167 | PROF_WARN("Unable to merge profile data: %s\n", 168 | "source profile file is too small."); 169 | return 0; 170 | } 171 | 172 | ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE, 173 | fileno(ProfileFile), 0); 174 | if (ProfileBuffer == MAP_FAILED) { 175 | PROF_ERR("Unable to merge profile data, mmap failed: %s\n", 176 | strerror(errno)); 177 | return -1; 178 | } 179 | 180 | if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) { 181 | (void)munmap(ProfileBuffer, ProfileFileSize); 182 | PROF_WARN("Unable to merge profile data: %s\n", 183 | "source profile file is not compatible."); 184 | return 0; 185 | } 186 | 187 | /* Now start merging */ 188 | __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize); 189 | 190 | // Truncate the file in case merging of value profile did not happend to 191 | // prevent from leaving garbage data at the end of the profile file. 192 | COMPILER_RT_FTRUNCATE(ProfileFile, __llvm_profile_get_size_for_buffer()); 193 | 194 | (void)munmap(ProfileBuffer, ProfileFileSize); 195 | *MergeDone = 1; 196 | 197 | return 0; 198 | } 199 | 200 | /* Create the directory holding the file, if needed. */ 201 | static void createProfileDir(const char *Filename) { 202 | size_t Length = strlen(Filename); 203 | if (lprofFindFirstDirSeparator(Filename)) { 204 | char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1); 205 | strncpy(Copy, Filename, Length + 1); 206 | __llvm_profile_recursive_mkdir(Copy); 207 | } 208 | } 209 | 210 | /* Open the profile data for merging. It opens the file in r+b mode with 211 | * file locking. If the file has content which is compatible with the 212 | * current process, it also reads in the profile data in the file and merge 213 | * it with in-memory counters. After the profile data is merged in memory, 214 | * the original profile data is truncated and gets ready for the profile 215 | * dumper. With profile merging enabled, each executable as well as any of 216 | * its instrumented shared libraries dump profile data into their own data file. 217 | */ 218 | static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { 219 | FILE *ProfileFile; 220 | int rc; 221 | 222 | createProfileDir(ProfileFileName); 223 | ProfileFile = lprofOpenFileEx(ProfileFileName); 224 | if (!ProfileFile) 225 | return NULL; 226 | 227 | rc = doProfileMerging(ProfileFile, MergeDone); 228 | if (rc || (!*MergeDone && COMPILER_RT_FTRUNCATE(ProfileFile, 0L)) || 229 | fseek(ProfileFile, 0L, SEEK_SET) == -1) { 230 | PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName, 231 | strerror(errno)); 232 | fclose(ProfileFile); 233 | return NULL; 234 | } 235 | return ProfileFile; 236 | } 237 | 238 | /* Write profile data to file \c OutputName. */ 239 | static int writeFile(const char *OutputName) { 240 | int RetVal; 241 | FILE *OutputFile; 242 | 243 | int MergeDone = 0; 244 | VPMergeHook = &lprofMergeValueProfData; 245 | if (!doMerging()) 246 | OutputFile = fopen(OutputName, "ab"); 247 | else 248 | OutputFile = openFileForMerging(OutputName, &MergeDone); 249 | 250 | if (!OutputFile) 251 | return -1; 252 | 253 | FreeHook = &free; 254 | setupIOBuffer(); 255 | ProfDataWriter fileWriter; 256 | initFileWriter(&fileWriter, OutputFile); 257 | RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone); 258 | 259 | fclose(OutputFile); 260 | return RetVal; 261 | } 262 | 263 | static void truncateCurrentFile(void) { 264 | const char *Filename; 265 | char *FilenameBuf; 266 | FILE *File; 267 | int Length; 268 | 269 | Length = getCurFilenameLength(); 270 | FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 271 | Filename = getCurFilename(FilenameBuf, 0); 272 | if (!Filename) 273 | return; 274 | 275 | /* By pass file truncation to allow online raw profile 276 | * merging. */ 277 | if (lprofCurFilename.MergePoolSize) 278 | return; 279 | 280 | createProfileDir(Filename); 281 | 282 | /* Truncate the file. Later we'll reopen and append. */ 283 | File = fopen(Filename, "w"); 284 | if (!File) 285 | return; 286 | fclose(File); 287 | } 288 | 289 | static const char *DefaultProfileName = "default.profraw"; 290 | static void resetFilenameToDefault(void) { 291 | if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { 292 | free((void *)lprofCurFilename.FilenamePat); 293 | } 294 | memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); 295 | lprofCurFilename.FilenamePat = DefaultProfileName; 296 | lprofCurFilename.PNS = PNS_default; 297 | } 298 | 299 | static int containsMergeSpecifier(const char *FilenamePat, int I) { 300 | return (FilenamePat[I] == 'm' || 301 | (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && 302 | /* If FilenamePat[I] is not '\0', the next byte is guaranteed 303 | * to be in-bound as the string is null terminated. */ 304 | FilenamePat[I + 1] == 'm')); 305 | } 306 | 307 | /* Parses the pattern string \p FilenamePat and stores the result to 308 | * lprofcurFilename structure. */ 309 | static int parseFilenamePattern(const char *FilenamePat, 310 | unsigned CopyFilenamePat) { 311 | int NumPids = 0, NumHosts = 0, I; 312 | char *PidChars = &lprofCurFilename.PidChars[0]; 313 | char *Hostname = &lprofCurFilename.Hostname[0]; 314 | int MergingEnabled = 0; 315 | 316 | /* Clean up cached prefix and filename. */ 317 | if (lprofCurFilename.ProfilePathPrefix) 318 | free((void *)lprofCurFilename.ProfilePathPrefix); 319 | if (lprofCurFilename.Filename) 320 | free((void *)lprofCurFilename.Filename); 321 | 322 | if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { 323 | free((void *)lprofCurFilename.FilenamePat); 324 | } 325 | 326 | memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); 327 | 328 | if (!CopyFilenamePat) 329 | lprofCurFilename.FilenamePat = FilenamePat; 330 | else { 331 | lprofCurFilename.FilenamePat = strdup(FilenamePat); 332 | lprofCurFilename.OwnsFilenamePat = 1; 333 | } 334 | /* Check the filename for "%p", which indicates a pid-substitution. */ 335 | for (I = 0; FilenamePat[I]; ++I) 336 | if (FilenamePat[I] == '%') { 337 | if (FilenamePat[++I] == 'p') { 338 | if (!NumPids++) { 339 | if (snprintf(PidChars, MAX_PID_SIZE, "%ld", (long)getpid()) <= 0) { 340 | PROF_WARN("Unable to get pid for filename pattern %s. Using the " 341 | "default name.", 342 | FilenamePat); 343 | return -1; 344 | } 345 | } 346 | } else if (FilenamePat[I] == 'h') { 347 | if (!NumHosts++) 348 | if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) { 349 | PROF_WARN("Unable to get hostname for filename pattern %s. Using " 350 | "the default name.", 351 | FilenamePat); 352 | return -1; 353 | } 354 | } else if (containsMergeSpecifier(FilenamePat, I)) { 355 | if (MergingEnabled) { 356 | PROF_WARN("%%m specifier can only be specified once in %s.\n", 357 | FilenamePat); 358 | return -1; 359 | } 360 | MergingEnabled = 1; 361 | if (FilenamePat[I] == 'm') 362 | lprofCurFilename.MergePoolSize = 1; 363 | else { 364 | lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; 365 | I++; /* advance to 'm' */ 366 | } 367 | } 368 | } 369 | 370 | lprofCurFilename.NumPids = NumPids; 371 | lprofCurFilename.NumHosts = NumHosts; 372 | return 0; 373 | } 374 | 375 | static void parseAndSetFilename(const char *FilenamePat, 376 | ProfileNameSpecifier PNS, 377 | unsigned CopyFilenamePat) { 378 | 379 | const char *OldFilenamePat = lprofCurFilename.FilenamePat; 380 | ProfileNameSpecifier OldPNS = lprofCurFilename.PNS; 381 | 382 | if (PNS < OldPNS) 383 | return; 384 | 385 | if (!FilenamePat) 386 | FilenamePat = DefaultProfileName; 387 | 388 | if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) { 389 | lprofCurFilename.PNS = PNS; 390 | return; 391 | } 392 | 393 | /* When PNS >= OldPNS, the last one wins. */ 394 | if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat)) 395 | resetFilenameToDefault(); 396 | lprofCurFilename.PNS = PNS; 397 | 398 | if (!OldFilenamePat) { 399 | if (getenv("LLVM_PROFILE_VERBOSE")) 400 | PROF_NOTE("Set profile file path to \"%s\" via %s.\n", 401 | lprofCurFilename.FilenamePat, getPNSStr(PNS)); 402 | } else { 403 | if (getenv("LLVM_PROFILE_VERBOSE")) 404 | PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n", 405 | OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat, 406 | getPNSStr(PNS)); 407 | } 408 | 409 | truncateCurrentFile(); 410 | } 411 | 412 | /* Return buffer length that is required to store the current profile 413 | * filename with PID and hostname substitutions. */ 414 | /* The length to hold uint64_t followed by 2 digit pool id including '_' */ 415 | #define SIGLEN 24 416 | static int getCurFilenameLength() { 417 | int Len; 418 | if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 419 | return 0; 420 | 421 | if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || 422 | lprofCurFilename.MergePoolSize)) 423 | return strlen(lprofCurFilename.FilenamePat); 424 | 425 | Len = strlen(lprofCurFilename.FilenamePat) + 426 | lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + 427 | lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); 428 | if (lprofCurFilename.MergePoolSize) 429 | Len += SIGLEN; 430 | return Len; 431 | } 432 | 433 | /* Return the pointer to the current profile file name (after substituting 434 | * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer 435 | * to store the resulting filename. If no substitution is needed, the 436 | * current filename pattern string is directly returned, unless ForceUseBuf 437 | * is enabled. */ 438 | static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { 439 | int I, J, PidLength, HostNameLength, FilenamePatLength; 440 | const char *FilenamePat = lprofCurFilename.FilenamePat; 441 | 442 | if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 443 | return 0; 444 | 445 | if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || 446 | lprofCurFilename.MergePoolSize)) { 447 | if (!ForceUseBuf) 448 | return lprofCurFilename.FilenamePat; 449 | 450 | FilenamePatLength = strlen(lprofCurFilename.FilenamePat); 451 | memcpy(FilenameBuf, lprofCurFilename.FilenamePat, FilenamePatLength); 452 | FilenameBuf[FilenamePatLength] = '\0'; 453 | return FilenameBuf; 454 | } 455 | 456 | PidLength = strlen(lprofCurFilename.PidChars); 457 | HostNameLength = strlen(lprofCurFilename.Hostname); 458 | /* Construct the new filename. */ 459 | for (I = 0, J = 0; FilenamePat[I]; ++I) 460 | if (FilenamePat[I] == '%') { 461 | if (FilenamePat[++I] == 'p') { 462 | memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength); 463 | J += PidLength; 464 | } else if (FilenamePat[I] == 'h') { 465 | memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength); 466 | J += HostNameLength; 467 | } else if (containsMergeSpecifier(FilenamePat, I)) { 468 | char LoadModuleSignature[SIGLEN]; 469 | int S; 470 | int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize; 471 | S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d", 472 | lprofGetLoadModuleSignature(), ProfilePoolId); 473 | if (S == -1 || S > SIGLEN) 474 | S = SIGLEN; 475 | memcpy(FilenameBuf + J, LoadModuleSignature, S); 476 | J += S; 477 | if (FilenamePat[I] != 'm') 478 | I++; 479 | } 480 | /* Drop any unknown substitutions. */ 481 | } else 482 | FilenameBuf[J++] = FilenamePat[I]; 483 | FilenameBuf[J] = 0; 484 | 485 | return FilenameBuf; 486 | } 487 | 488 | /* Returns the pointer to the environment variable 489 | * string. Returns null if the env var is not set. */ 490 | static const char *getFilenamePatFromEnv(void) { 491 | const char *Filename = getenv("LLVM_PROFILE_FILE"); 492 | if (!Filename || !Filename[0]) 493 | return 0; 494 | return Filename; 495 | } 496 | 497 | COMPILER_RT_VISIBILITY 498 | const char *__llvm_profile_get_path_prefix(void) { 499 | int Length; 500 | char *FilenameBuf, *Prefix; 501 | const char *Filename, *PrefixEnd; 502 | 503 | if (lprofCurFilename.ProfilePathPrefix) 504 | return lprofCurFilename.ProfilePathPrefix; 505 | 506 | Length = getCurFilenameLength(); 507 | FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 508 | Filename = getCurFilename(FilenameBuf, 0); 509 | if (!Filename) 510 | return "\0"; 511 | 512 | PrefixEnd = lprofFindLastDirSeparator(Filename); 513 | if (!PrefixEnd) 514 | return "\0"; 515 | 516 | Length = PrefixEnd - Filename + 1; 517 | Prefix = (char *)malloc(Length + 1); 518 | if (!Prefix) { 519 | PROF_ERR("Failed to %s\n", "allocate memory."); 520 | return "\0"; 521 | } 522 | memcpy(Prefix, Filename, Length); 523 | Prefix[Length] = '\0'; 524 | lprofCurFilename.ProfilePathPrefix = Prefix; 525 | return Prefix; 526 | } 527 | 528 | COMPILER_RT_VISIBILITY 529 | const char *__llvm_profile_get_filename(void) { 530 | int Length; 531 | char *FilenameBuf; 532 | const char *Filename; 533 | 534 | if (lprofCurFilename.Filename) 535 | return lprofCurFilename.Filename; 536 | 537 | Length = getCurFilenameLength(); 538 | FilenameBuf = (char *)malloc(Length + 1); 539 | if (!FilenameBuf) { 540 | PROF_ERR("Failed to %s\n", "allocate memory."); 541 | return "\0"; 542 | } 543 | Filename = getCurFilename(FilenameBuf, 1); 544 | if (!Filename) 545 | return "\0"; 546 | 547 | lprofCurFilename.Filename = FilenameBuf; 548 | return FilenameBuf; 549 | } 550 | 551 | /* This method is invoked by the runtime initialization hook 552 | * InstrProfilingRuntime.o if it is linked in. Both user specified 553 | * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE 554 | * environment variable can override this default value. */ 555 | COMPILER_RT_VISIBILITY 556 | void __llvm_profile_initialize_file(void) { 557 | const char *EnvFilenamePat; 558 | const char *SelectedPat = NULL; 559 | ProfileNameSpecifier PNS = PNS_unknown; 560 | int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0); 561 | 562 | EnvFilenamePat = getFilenamePatFromEnv(); 563 | if (EnvFilenamePat) { 564 | /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid 565 | at the moment when __llvm_profile_write_file() gets executed. */ 566 | parseAndSetFilename(EnvFilenamePat, PNS_environment, 1); 567 | return; 568 | } else if (hasCommandLineOverrider) { 569 | SelectedPat = INSTR_PROF_PROFILE_NAME_VAR; 570 | PNS = PNS_command_line; 571 | } else { 572 | SelectedPat = NULL; 573 | PNS = PNS_default; 574 | } 575 | 576 | parseAndSetFilename(SelectedPat, PNS, 0); 577 | } 578 | 579 | /* This API is directly called by the user application code. It has the 580 | * highest precedence compared with LLVM_PROFILE_FILE environment variable 581 | * and command line option -fprofile-instr-generate=. 582 | */ 583 | COMPILER_RT_VISIBILITY 584 | void __llvm_profile_set_filename(const char *FilenamePat) { 585 | parseAndSetFilename(FilenamePat, PNS_runtime_api, 1); 586 | } 587 | 588 | /* The public API for writing profile data into the file with name 589 | * set by previous calls to __llvm_profile_set_filename or 590 | * __llvm_profile_override_default_filename or 591 | * __llvm_profile_initialize_file. */ 592 | COMPILER_RT_VISIBILITY 593 | int __llvm_profile_write_file(void) { 594 | int rc, Length; 595 | const char *Filename; 596 | char *FilenameBuf; 597 | int PDeathSig = 0; 598 | 599 | if (lprofProfileDumped()) { 600 | PROF_NOTE("Profile data not written to file: %s.\n", 601 | "already written"); 602 | return 0; 603 | } 604 | 605 | Length = getCurFilenameLength(); 606 | FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 607 | Filename = getCurFilename(FilenameBuf, 0); 608 | 609 | /* Check the filename. */ 610 | if (!Filename) { 611 | PROF_ERR("Failed to write file : %s\n", "Filename not set"); 612 | return -1; 613 | } 614 | 615 | /* Check if there is llvm/runtime version mismatch. */ 616 | if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 617 | PROF_ERR("Runtime and instrumentation version mismatch : " 618 | "expected %d, but get %d\n", 619 | INSTR_PROF_RAW_VERSION, 620 | (int)GET_VERSION(__llvm_profile_get_version())); 621 | return -1; 622 | } 623 | 624 | // Temporarily suspend getting SIGKILL when the parent exits. 625 | PDeathSig = lprofSuspendSigKill(); 626 | 627 | /* Write profile data to the file. */ 628 | rc = writeFile(Filename); 629 | if (rc) 630 | PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); 631 | 632 | // Restore SIGKILL. 633 | if (PDeathSig == 1) 634 | lprofRestoreSigKill(); 635 | 636 | return rc; 637 | } 638 | 639 | COMPILER_RT_VISIBILITY 640 | int __llvm_profile_dump(void) { 641 | if (!doMerging()) 642 | PROF_WARN("Later invocation of __llvm_profile_dump can lead to clobbering " 643 | " of previously dumped profile data : %s. Either use %%m " 644 | "in profile name or change profile name before dumping.\n", 645 | "online profile merging is not on"); 646 | int rc = __llvm_profile_write_file(); 647 | lprofSetProfileDumped(); 648 | return rc; 649 | } 650 | 651 | static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } 652 | 653 | COMPILER_RT_VISIBILITY 654 | int __llvm_profile_register_write_file_atexit(void) { 655 | static int HasBeenRegistered = 0; 656 | 657 | if (HasBeenRegistered) 658 | return 0; 659 | 660 | lprofSetupValueProfiler(); 661 | 662 | HasBeenRegistered = 1; 663 | return atexit(writeFileWithoutReturn); 664 | } 665 | 666 | #endif 667 | -------------------------------------------------------------------------------- /Classes/InstrProfilingInternal.h: -------------------------------------------------------------------------------- 1 | /*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifndef PROFILE_INSTRPROFILING_INTERNALH_ 10 | #define PROFILE_INSTRPROFILING_INTERNALH_ 11 | 12 | #include 13 | 14 | #include "InstrProfiling.h" 15 | 16 | /*! 17 | * \brief Write instrumentation data to the given buffer, given explicit 18 | * pointers to the live data in memory. This function is probably not what you 19 | * want. Use __llvm_profile_get_size_for_buffer instead. Use this function if 20 | * your program has a custom memory layout. 21 | */ 22 | uint64_t __llvm_profile_get_size_for_buffer_internal( 23 | const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, 24 | const uint64_t *CountersBegin, const uint64_t *CountersEnd, 25 | const char *NamesBegin, const char *NamesEnd); 26 | 27 | /*! 28 | * \brief Write instrumentation data to the given buffer, given explicit 29 | * pointers to the live data in memory. This function is probably not what you 30 | * want. Use __llvm_profile_write_buffer instead. Use this function if your 31 | * program has a custom memory layout. 32 | * 33 | * \pre \c Buffer is the start of a buffer at least as big as \a 34 | * __llvm_profile_get_size_for_buffer_internal(). 35 | */ 36 | int __llvm_profile_write_buffer_internal( 37 | char *Buffer, const __llvm_profile_data *DataBegin, 38 | const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, 39 | const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd); 40 | 41 | /*! 42 | * The data structure describing the data to be written by the 43 | * low level writer callback function. 44 | */ 45 | typedef struct ProfDataIOVec { 46 | const void *Data; 47 | size_t ElmSize; 48 | size_t NumElm; 49 | } ProfDataIOVec; 50 | 51 | struct ProfDataWriter; 52 | typedef uint32_t (*WriterCallback)(struct ProfDataWriter *This, ProfDataIOVec *, 53 | uint32_t NumIOVecs); 54 | 55 | typedef struct ProfDataWriter { 56 | WriterCallback Write; 57 | void *WriterCtx; 58 | } ProfDataWriter; 59 | 60 | /*! 61 | * The data structure for buffered IO of profile data. 62 | */ 63 | typedef struct ProfBufferIO { 64 | ProfDataWriter *FileWriter; 65 | uint32_t OwnFileWriter; 66 | /* The start of the buffer. */ 67 | uint8_t *BufferStart; 68 | /* Total size of the buffer. */ 69 | uint32_t BufferSz; 70 | /* Current byte offset from the start of the buffer. */ 71 | uint32_t CurOffset; 72 | } ProfBufferIO; 73 | 74 | /* The creator interface used by testing. */ 75 | ProfBufferIO *lprofCreateBufferIOInternal(void *File, uint32_t BufferSz); 76 | 77 | /*! 78 | * This is the interface to create a handle for buffered IO. 79 | */ 80 | ProfBufferIO *lprofCreateBufferIO(ProfDataWriter *FileWriter); 81 | 82 | /*! 83 | * The interface to destroy the bufferIO handle and reclaim 84 | * the memory. 85 | */ 86 | void lprofDeleteBufferIO(ProfBufferIO *BufferIO); 87 | 88 | /*! 89 | * This is the interface to write \c Data of \c Size bytes through 90 | * \c BufferIO. Returns 0 if successful, otherwise return -1. 91 | */ 92 | int lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, 93 | uint32_t Size); 94 | /*! 95 | * The interface to flush the remaining data in the buffer. 96 | * through the low level writer callback. 97 | */ 98 | int lprofBufferIOFlush(ProfBufferIO *BufferIO); 99 | 100 | /* The low level interface to write data into a buffer. It is used as the 101 | * callback by other high level writer methods such as buffered IO writer 102 | * and profile data writer. */ 103 | uint32_t lprofBufferWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 104 | uint32_t NumIOVecs); 105 | void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer); 106 | 107 | struct ValueProfData; 108 | struct ValueProfRecord; 109 | struct InstrProfValueData; 110 | struct ValueProfNode; 111 | 112 | /*! 113 | * The class that defines a set of methods to read value profile 114 | * data for streaming/serialization from the instrumentation runtime. 115 | */ 116 | typedef struct VPDataReaderType { 117 | uint32_t (*InitRTRecord)(const __llvm_profile_data *Data, 118 | uint8_t *SiteCountArray[]); 119 | /* Function pointer to getValueProfRecordHeader method. */ 120 | uint32_t (*GetValueProfRecordHeaderSize)(uint32_t NumSites); 121 | /* Function pointer to getFristValueProfRecord method. */ 122 | struct ValueProfRecord *(*GetFirstValueProfRecord)(struct ValueProfData *); 123 | /* Return the number of value data for site \p Site. */ 124 | uint32_t (*GetNumValueDataForSite)(uint32_t VK, uint32_t Site); 125 | /* Return the total size of the value profile data of the 126 | * current function. */ 127 | uint32_t (*GetValueProfDataSize)(void); 128 | /*! 129 | * Read the next \p N value data for site \p Site and store the data 130 | * in \p Dst. \p StartNode is the first value node to start with if 131 | * it is not null. The function returns the pointer to the value 132 | * node pointer to be used as the \p StartNode of the next batch reading. 133 | * If there is nothing left, it returns NULL. 134 | */ 135 | struct ValueProfNode *(*GetValueData)(uint32_t ValueKind, uint32_t Site, 136 | struct InstrProfValueData *Dst, 137 | struct ValueProfNode *StartNode, 138 | uint32_t N); 139 | } VPDataReaderType; 140 | 141 | /* Write profile data to destinitation. If SkipNameDataWrite is set to 1, 142 | the name data is already in destintation, we just skip over it. */ 143 | int lprofWriteData(ProfDataWriter *Writer, VPDataReaderType *VPDataReader, 144 | int SkipNameDataWrite); 145 | int lprofWriteDataImpl(ProfDataWriter *Writer, 146 | const __llvm_profile_data *DataBegin, 147 | const __llvm_profile_data *DataEnd, 148 | const uint64_t *CountersBegin, 149 | const uint64_t *CountersEnd, 150 | VPDataReaderType *VPDataReader, const char *NamesBegin, 151 | const char *NamesEnd, int SkipNameDataWrite); 152 | 153 | /* Merge value profile data pointed to by SrcValueProfData into 154 | * in-memory profile counters pointed by to DstData. */ 155 | void lprofMergeValueProfData(struct ValueProfData *SrcValueProfData, 156 | __llvm_profile_data *DstData); 157 | 158 | VPDataReaderType *lprofGetVPDataReader(); 159 | 160 | /* Internal interface used by test to reset the max number of 161 | * tracked values per value site to be \p MaxVals. 162 | */ 163 | void lprofSetMaxValsPerSite(uint32_t MaxVals); 164 | void lprofSetupValueProfiler(); 165 | 166 | /* Return the profile header 'signature' value associated with the current 167 | * executable or shared library. The signature value can be used to for 168 | * a profile name that is unique to this load module so that it does not 169 | * collide with profiles from other binaries. It also allows shared libraries 170 | * to dump merged profile data into its own profile file. */ 171 | uint64_t lprofGetLoadModuleSignature(); 172 | 173 | /* 174 | * Return non zero value if the profile data has already been 175 | * dumped to the file. 176 | */ 177 | unsigned lprofProfileDumped(); 178 | void lprofSetProfileDumped(); 179 | 180 | COMPILER_RT_VISIBILITY extern void (*FreeHook)(void *); 181 | COMPILER_RT_VISIBILITY extern uint8_t *DynamicBufferIOBuffer; 182 | COMPILER_RT_VISIBILITY extern uint32_t VPBufferSize; 183 | COMPILER_RT_VISIBILITY extern uint32_t VPMaxNumValsPerSite; 184 | /* Pointer to the start of static value counters to be allocted. */ 185 | COMPILER_RT_VISIBILITY extern ValueProfNode *CurrentVNode; 186 | COMPILER_RT_VISIBILITY extern ValueProfNode *EndVNode; 187 | extern void (*VPMergeHook)(struct ValueProfData *, __llvm_profile_data *); 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /Classes/InstrProfilingMerge.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingMerge.c - Profile in-process Merging ---------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | |*===----------------------------------------------------------------------===* 8 | |* This file defines the API needed for in-process merging of profile data 9 | |* stored in memory buffer. 10 | \*===---------------------------------------------------------------------===*/ 11 | 12 | #include "InstrProfiling.h" 13 | #include "InstrProfilingInternal.h" 14 | #include "InstrProfilingUtil.h" 15 | 16 | #define INSTR_PROF_VALUE_PROF_DATA 17 | #include "InstrProfData.inc" 18 | 19 | COMPILER_RT_VISIBILITY 20 | void (*VPMergeHook)(ValueProfData *, __llvm_profile_data *); 21 | 22 | COMPILER_RT_VISIBILITY 23 | uint64_t lprofGetLoadModuleSignature() { 24 | /* A very fast way to compute a module signature. */ 25 | uint64_t CounterSize = (uint64_t)(__llvm_profile_end_counters() - 26 | __llvm_profile_begin_counters()); 27 | uint64_t DataSize = __llvm_profile_get_data_size(__llvm_profile_begin_data(), 28 | __llvm_profile_end_data()); 29 | uint64_t NamesSize = 30 | (uint64_t)(__llvm_profile_end_names() - __llvm_profile_begin_names()); 31 | uint64_t NumVnodes = 32 | (uint64_t)(__llvm_profile_end_vnodes() - __llvm_profile_begin_vnodes()); 33 | const __llvm_profile_data *FirstD = __llvm_profile_begin_data(); 34 | 35 | return (NamesSize << 40) + (CounterSize << 30) + (DataSize << 20) + 36 | (NumVnodes << 10) + (DataSize > 0 ? FirstD->NameRef : 0); 37 | } 38 | 39 | /* Returns 1 if profile is not structurally compatible. */ 40 | COMPILER_RT_VISIBILITY 41 | int __llvm_profile_check_compatibility(const char *ProfileData, 42 | uint64_t ProfileSize) { 43 | /* Check profile header only for now */ 44 | __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData; 45 | __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData; 46 | SrcDataStart = 47 | (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header)); 48 | SrcDataEnd = SrcDataStart + Header->DataSize; 49 | 50 | if (ProfileSize < sizeof(__llvm_profile_header)) 51 | return 1; 52 | 53 | /* Check the header first. */ 54 | if (Header->Magic != __llvm_profile_get_magic() || 55 | Header->Version != __llvm_profile_get_version() || 56 | Header->DataSize != 57 | __llvm_profile_get_data_size(__llvm_profile_begin_data(), 58 | __llvm_profile_end_data()) || 59 | Header->CountersSize != (uint64_t)(__llvm_profile_end_counters() - 60 | __llvm_profile_begin_counters()) || 61 | Header->NamesSize != (uint64_t)(__llvm_profile_end_names() - 62 | __llvm_profile_begin_names()) || 63 | Header->ValueKindLast != IPVK_Last) 64 | return 1; 65 | 66 | if (ProfileSize < sizeof(__llvm_profile_header) + 67 | Header->DataSize * sizeof(__llvm_profile_data) + 68 | Header->NamesSize + Header->CountersSize) 69 | return 1; 70 | 71 | for (SrcData = SrcDataStart, 72 | DstData = (__llvm_profile_data *)__llvm_profile_begin_data(); 73 | SrcData < SrcDataEnd; ++SrcData, ++DstData) { 74 | if (SrcData->NameRef != DstData->NameRef || 75 | SrcData->FuncHash != DstData->FuncHash || 76 | SrcData->NumCounters != DstData->NumCounters) 77 | return 1; 78 | } 79 | 80 | /* Matched! */ 81 | return 0; 82 | } 83 | 84 | COMPILER_RT_VISIBILITY 85 | void __llvm_profile_merge_from_buffer(const char *ProfileData, 86 | uint64_t ProfileSize) { 87 | __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData; 88 | __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData; 89 | uint64_t *SrcCountersStart; 90 | const char *SrcNameStart; 91 | ValueProfData *SrcValueProfDataStart, *SrcValueProfData; 92 | 93 | SrcDataStart = 94 | (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header)); 95 | SrcDataEnd = SrcDataStart + Header->DataSize; 96 | SrcCountersStart = (uint64_t *)SrcDataEnd; 97 | SrcNameStart = (const char *)(SrcCountersStart + Header->CountersSize); 98 | SrcValueProfDataStart = 99 | (ValueProfData *)(SrcNameStart + Header->NamesSize + 100 | __llvm_profile_get_num_padding_bytes( 101 | Header->NamesSize)); 102 | 103 | for (SrcData = SrcDataStart, 104 | DstData = (__llvm_profile_data *)__llvm_profile_begin_data(), 105 | SrcValueProfData = SrcValueProfDataStart; 106 | SrcData < SrcDataEnd; ++SrcData, ++DstData) { 107 | uint64_t *SrcCounters; 108 | uint64_t *DstCounters = (uint64_t *)DstData->CounterPtr; 109 | unsigned I, NC, NVK = 0; 110 | 111 | NC = SrcData->NumCounters; 112 | SrcCounters = SrcCountersStart + 113 | ((size_t)SrcData->CounterPtr - Header->CountersDelta) / 114 | sizeof(uint64_t); 115 | for (I = 0; I < NC; I++) 116 | DstCounters[I] += SrcCounters[I]; 117 | 118 | /* Now merge value profile data. */ 119 | if (!VPMergeHook) 120 | continue; 121 | 122 | for (I = 0; I <= IPVK_Last; I++) 123 | NVK += (SrcData->NumValueSites[I] != 0); 124 | 125 | if (!NVK) 126 | continue; 127 | 128 | VPMergeHook(SrcValueProfData, DstData); 129 | SrcValueProfData = (ValueProfData *)((char *)SrcValueProfData + 130 | SrcValueProfData->TotalSize); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Classes/InstrProfilingMergeFile.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingMergeFile.c - Profile in-process Merging ------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | |*===----------------------------------------------------------------------=== 8 | |* This file defines APIs needed to support in-process merging for profile data 9 | |* stored in files. 10 | \*===----------------------------------------------------------------------===*/ 11 | 12 | #if !defined(__Fuchsia__) 13 | 14 | #include "InstrProfiling.h" 15 | #include "InstrProfilingInternal.h" 16 | #include "InstrProfilingUtil.h" 17 | 18 | #define INSTR_PROF_VALUE_PROF_DATA 19 | #include "InstrProfData.inc" 20 | 21 | /* Merge value profile data pointed to by SrcValueProfData into 22 | * in-memory profile counters pointed by to DstData. */ 23 | COMPILER_RT_VISIBILITY 24 | void lprofMergeValueProfData(ValueProfData *SrcValueProfData, 25 | __llvm_profile_data *DstData) { 26 | unsigned I, S, V, DstIndex = 0; 27 | InstrProfValueData *VData; 28 | ValueProfRecord *VR = getFirstValueProfRecord(SrcValueProfData); 29 | for (I = 0; I < SrcValueProfData->NumValueKinds; I++) { 30 | VData = getValueProfRecordValueData(VR); 31 | unsigned SrcIndex = 0; 32 | for (S = 0; S < VR->NumValueSites; S++) { 33 | uint8_t NV = VR->SiteCountArray[S]; 34 | for (V = 0; V < NV; V++) { 35 | __llvm_profile_instrument_target_value(VData[SrcIndex].Value, DstData, 36 | DstIndex, VData[SrcIndex].Count); 37 | ++SrcIndex; 38 | } 39 | ++DstIndex; 40 | } 41 | VR = getValueProfRecordNext(VR); 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Classes/InstrProfilingNameVar.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingNameVar.c - profile name variable setup -------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include "InstrProfiling.h" 10 | 11 | /* char __llvm_profile_filename[1] 12 | * 13 | * The runtime should only provide its own definition of this symbol when the 14 | * user has not specified one. Set this up by moving the runtime's copy of this 15 | * symbol to an object file within the archive. 16 | */ 17 | COMPILER_RT_WEAK char INSTR_PROF_PROFILE_NAME_VAR[1] = {0}; 18 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPlatformDarwin.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPlatformDarwin.c - Profile data on Darwin ------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include "InstrProfiling.h" 10 | 11 | #if defined(__APPLE__) 12 | /* Use linker magic to find the bounds of the Data section. */ 13 | COMPILER_RT_VISIBILITY 14 | extern __llvm_profile_data 15 | DataStart __asm("section$start$__DATA$" INSTR_PROF_DATA_SECT_NAME); 16 | COMPILER_RT_VISIBILITY 17 | extern __llvm_profile_data 18 | DataEnd __asm("section$end$__DATA$" INSTR_PROF_DATA_SECT_NAME); 19 | COMPILER_RT_VISIBILITY 20 | extern char 21 | NamesStart __asm("section$start$__DATA$" INSTR_PROF_NAME_SECT_NAME); 22 | COMPILER_RT_VISIBILITY 23 | extern char NamesEnd __asm("section$end$__DATA$" INSTR_PROF_NAME_SECT_NAME); 24 | COMPILER_RT_VISIBILITY 25 | extern uint64_t 26 | CountersStart __asm("section$start$__DATA$" INSTR_PROF_CNTS_SECT_NAME); 27 | COMPILER_RT_VISIBILITY 28 | extern uint64_t 29 | CountersEnd __asm("section$end$__DATA$" INSTR_PROF_CNTS_SECT_NAME); 30 | 31 | COMPILER_RT_VISIBILITY 32 | extern ValueProfNode 33 | VNodesStart __asm("section$start$__DATA$" INSTR_PROF_VNODES_SECT_NAME); 34 | COMPILER_RT_VISIBILITY 35 | extern ValueProfNode 36 | VNodesEnd __asm("section$end$__DATA$" INSTR_PROF_VNODES_SECT_NAME); 37 | 38 | COMPILER_RT_VISIBILITY 39 | const __llvm_profile_data *__llvm_profile_begin_data(void) { 40 | return &DataStart; 41 | } 42 | COMPILER_RT_VISIBILITY 43 | const __llvm_profile_data *__llvm_profile_end_data(void) { return &DataEnd; } 44 | COMPILER_RT_VISIBILITY 45 | const char *__llvm_profile_begin_names(void) { return &NamesStart; } 46 | COMPILER_RT_VISIBILITY 47 | const char *__llvm_profile_end_names(void) { return &NamesEnd; } 48 | COMPILER_RT_VISIBILITY 49 | uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; } 50 | COMPILER_RT_VISIBILITY 51 | uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; } 52 | 53 | COMPILER_RT_VISIBILITY 54 | ValueProfNode *__llvm_profile_begin_vnodes(void) { 55 | return &VNodesStart; 56 | } 57 | COMPILER_RT_VISIBILITY 58 | ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; } 59 | 60 | COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &VNodesStart; 61 | COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &VNodesEnd; 62 | #endif 63 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPlatformFuchsia.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPlatformFuchsia.c - Profile data Fuchsia platform ----===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | /* 9 | * This file implements the profiling runtime for Fuchsia and defines the 10 | * shared profile runtime interface. Each module (executable or DSO) statically 11 | * links in the whole profile runtime to satisfy the calls from its 12 | * instrumented code. Several modules in the same program might be separately 13 | * compiled and even use different versions of the instrumentation ABI and data 14 | * format. All they share in common is the VMO and the offset, which live in 15 | * exported globals so that exactly one definition will be shared across all 16 | * modules. Each module has its own independent runtime that registers its own 17 | * atexit hook to append its own data into the shared VMO which is published 18 | * via the data sink hook provided by Fuchsia's dynamic linker. 19 | */ 20 | 21 | #if defined(__Fuchsia__) 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "InstrProfiling.h" 33 | #include "InstrProfilingInternal.h" 34 | #include "InstrProfilingUtil.h" 35 | 36 | /* VMO that contains the coverage data shared across all modules. This symbol 37 | * has default visibility and is exported in each module (executable or DSO) 38 | * that statically links in the profiling runtime. 39 | */ 40 | zx_handle_t __llvm_profile_vmo; 41 | /* Current offset within the VMO where data should be written next. This symbol 42 | * has default visibility and is exported in each module (executable or DSO) 43 | * that statically links in the profiling runtime. 44 | */ 45 | uint64_t __llvm_profile_offset; 46 | 47 | static const char ProfileSinkName[] = "llvm-profile"; 48 | 49 | static inline void lprofWrite(const char *fmt, ...) { 50 | char s[256]; 51 | 52 | va_list ap; 53 | va_start(ap, fmt); 54 | int ret = vsnprintf(s, sizeof(s), fmt, ap); 55 | va_end(ap); 56 | 57 | __sanitizer_log_write(s, ret + 1); 58 | } 59 | 60 | static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 61 | uint32_t NumIOVecs) { 62 | /* Allocate VMO if it hasn't been created yet. */ 63 | if (__llvm_profile_vmo == ZX_HANDLE_INVALID) { 64 | /* Get information about the current process. */ 65 | zx_info_handle_basic_t Info; 66 | zx_status_t Status = 67 | _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, 68 | sizeof(Info), NULL, NULL); 69 | if (Status != ZX_OK) 70 | return -1; 71 | 72 | /* Create VMO to hold the profile data. */ 73 | Status = _zx_vmo_create(0, 0, &__llvm_profile_vmo); 74 | if (Status != ZX_OK) 75 | return -1; 76 | 77 | /* Give the VMO a name including our process KOID so it's easy to spot. */ 78 | char VmoName[ZX_MAX_NAME_LEN]; 79 | snprintf(VmoName, sizeof(VmoName), "%s.%" PRIu64, ProfileSinkName, 80 | Info.koid); 81 | _zx_object_set_property(__llvm_profile_vmo, ZX_PROP_NAME, VmoName, 82 | strlen(VmoName)); 83 | 84 | /* Duplicate the handle since __sanitizer_publish_data consumes it. */ 85 | zx_handle_t Handle; 86 | Status = 87 | _zx_handle_duplicate(__llvm_profile_vmo, ZX_RIGHT_SAME_RIGHTS, &Handle); 88 | if (Status != ZX_OK) 89 | return -1; 90 | 91 | /* Publish the VMO which contains profile data to the system. */ 92 | __sanitizer_publish_data(ProfileSinkName, Handle); 93 | 94 | /* Use the dumpfile symbolizer markup element to write the name of VMO. */ 95 | lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", 96 | ProfileSinkName, VmoName); 97 | } 98 | 99 | /* Compute the total length of data to be written. */ 100 | size_t Length = 0; 101 | for (uint32_t I = 0; I < NumIOVecs; I++) 102 | Length += IOVecs[I].ElmSize * IOVecs[I].NumElm; 103 | 104 | /* Resize the VMO to ensure there's sufficient space for the data. */ 105 | zx_status_t Status = 106 | _zx_vmo_set_size(__llvm_profile_vmo, __llvm_profile_offset + Length); 107 | if (Status != ZX_OK) 108 | return -1; 109 | 110 | /* Copy the data into VMO. */ 111 | for (uint32_t I = 0; I < NumIOVecs; I++) { 112 | size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; 113 | if (IOVecs[I].Data) { 114 | Status = _zx_vmo_write(__llvm_profile_vmo, IOVecs[I].Data, 115 | __llvm_profile_offset, Length); 116 | if (Status != ZX_OK) 117 | return -1; 118 | } 119 | __llvm_profile_offset += Length; 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | static void initVMOWriter(ProfDataWriter *This) { 126 | This->Write = lprofVMOWriter; 127 | This->WriterCtx = NULL; 128 | } 129 | 130 | static int dump(void) { 131 | if (lprofProfileDumped()) { 132 | lprofWrite("Profile data not published: already written.\n"); 133 | return 0; 134 | } 135 | 136 | /* Check if there is llvm/runtime version mismatch. */ 137 | if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 138 | lprofWrite("Runtime and instrumentation version mismatch : " 139 | "expected %d, but got %d\n", 140 | INSTR_PROF_RAW_VERSION, 141 | (int)GET_VERSION(__llvm_profile_get_version())); 142 | return -1; 143 | } 144 | 145 | /* Write the profile data into the mapped region. */ 146 | ProfDataWriter VMOWriter; 147 | initVMOWriter(&VMOWriter); 148 | if (lprofWriteData(&VMOWriter, lprofGetVPDataReader(), 0) != 0) 149 | return -1; 150 | 151 | return 0; 152 | } 153 | 154 | COMPILER_RT_VISIBILITY 155 | int __llvm_profile_dump(void) { 156 | int rc = dump(); 157 | lprofSetProfileDumped(); 158 | return rc; 159 | } 160 | 161 | static void dumpWithoutReturn(void) { dump(); } 162 | 163 | /* This method is invoked by the runtime initialization hook 164 | * InstrProfilingRuntime.o if it is linked in. 165 | */ 166 | COMPILER_RT_VISIBILITY 167 | void __llvm_profile_initialize_file(void) {} 168 | 169 | COMPILER_RT_VISIBILITY 170 | int __llvm_profile_register_write_file_atexit(void) { 171 | static bool HasBeenRegistered = false; 172 | 173 | if (HasBeenRegistered) 174 | return 0; 175 | 176 | lprofSetupValueProfiler(); 177 | 178 | HasBeenRegistered = true; 179 | return atexit(dumpWithoutReturn); 180 | } 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPlatformLinux.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPlatformLinux.c - Profile data Linux platform ------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ 10 | (defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) 11 | 12 | #include 13 | 14 | #include "InstrProfiling.h" 15 | 16 | #define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_COMMON) 17 | #define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_COMMON) 18 | #define PROF_NAME_START INSTR_PROF_SECT_START(INSTR_PROF_NAME_COMMON) 19 | #define PROF_NAME_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_NAME_COMMON) 20 | #define PROF_CNTS_START INSTR_PROF_SECT_START(INSTR_PROF_CNTS_COMMON) 21 | #define PROF_CNTS_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_CNTS_COMMON) 22 | #define PROF_VNODES_START INSTR_PROF_SECT_START(INSTR_PROF_VNODES_COMMON) 23 | #define PROF_VNODES_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_VNODES_COMMON) 24 | 25 | /* Declare section start and stop symbols for various sections 26 | * generated by compiler instrumentation. 27 | */ 28 | extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY; 29 | extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY; 30 | extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY; 31 | extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY; 32 | extern char PROF_NAME_START COMPILER_RT_VISIBILITY; 33 | extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY; 34 | extern ValueProfNode PROF_VNODES_START COMPILER_RT_VISIBILITY; 35 | extern ValueProfNode PROF_VNODES_STOP COMPILER_RT_VISIBILITY; 36 | 37 | /* Add dummy data to ensure the section is always created. */ 38 | __llvm_profile_data 39 | __prof_data_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_DATA_SECT_NAME); 40 | uint64_t 41 | __prof_cnts_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_CNTS_SECT_NAME); 42 | char __prof_nms_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_NAME_SECT_NAME); 43 | ValueProfNode __prof_vnodes_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_VNODES_SECT_NAME); 44 | 45 | COMPILER_RT_VISIBILITY const __llvm_profile_data * 46 | __llvm_profile_begin_data(void) { 47 | return &PROF_DATA_START; 48 | } 49 | COMPILER_RT_VISIBILITY const __llvm_profile_data * 50 | __llvm_profile_end_data(void) { 51 | return &PROF_DATA_STOP; 52 | } 53 | COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) { 54 | return &PROF_NAME_START; 55 | } 56 | COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) { 57 | return &PROF_NAME_STOP; 58 | } 59 | COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) { 60 | return &PROF_CNTS_START; 61 | } 62 | COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) { 63 | return &PROF_CNTS_STOP; 64 | } 65 | 66 | COMPILER_RT_VISIBILITY ValueProfNode * 67 | __llvm_profile_begin_vnodes(void) { 68 | return &PROF_VNODES_START; 69 | } 70 | COMPILER_RT_VISIBILITY ValueProfNode *__llvm_profile_end_vnodes(void) { 71 | return &PROF_VNODES_STOP; 72 | } 73 | COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &PROF_VNODES_START; 74 | COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &PROF_VNODES_STOP; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPlatformOther.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPlatformOther.c - Profile data default platform ------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \ 10 | !(defined(__sun__) && defined(__svr4__)) && !defined(__NetBSD__) && \ 11 | !defined(_WIN32) 12 | 13 | #include 14 | #include 15 | 16 | #include "InstrProfiling.h" 17 | 18 | static const __llvm_profile_data *DataFirst = NULL; 19 | static const __llvm_profile_data *DataLast = NULL; 20 | static const char *NamesFirst = NULL; 21 | static const char *NamesLast = NULL; 22 | static uint64_t *CountersFirst = NULL; 23 | static uint64_t *CountersLast = NULL; 24 | 25 | static const void *getMinAddr(const void *A1, const void *A2) { 26 | return A1 < A2 ? A1 : A2; 27 | } 28 | 29 | static const void *getMaxAddr(const void *A1, const void *A2) { 30 | return A1 > A2 ? A1 : A2; 31 | } 32 | 33 | /*! 34 | * \brief Register an instrumented function. 35 | * 36 | * Calls to this are emitted by clang with -fprofile-instr-generate. Such 37 | * calls are only required (and only emitted) on targets where we haven't 38 | * implemented linker magic to find the bounds of the sections. 39 | */ 40 | COMPILER_RT_VISIBILITY 41 | void __llvm_profile_register_function(void *Data_) { 42 | /* TODO: Only emit this function if we can't use linker magic. */ 43 | const __llvm_profile_data *Data = (__llvm_profile_data *)Data_; 44 | if (!DataFirst) { 45 | DataFirst = Data; 46 | DataLast = Data + 1; 47 | CountersFirst = Data->CounterPtr; 48 | CountersLast = (uint64_t *)Data->CounterPtr + Data->NumCounters; 49 | return; 50 | } 51 | 52 | DataFirst = (const __llvm_profile_data *)getMinAddr(DataFirst, Data); 53 | CountersFirst = (uint64_t *)getMinAddr(CountersFirst, Data->CounterPtr); 54 | 55 | DataLast = (const __llvm_profile_data *)getMaxAddr(DataLast, Data + 1); 56 | CountersLast = (uint64_t *)getMaxAddr( 57 | CountersLast, (uint64_t *)Data->CounterPtr + Data->NumCounters); 58 | } 59 | 60 | COMPILER_RT_VISIBILITY 61 | void __llvm_profile_register_names_function(void *NamesStart, 62 | uint64_t NamesSize) { 63 | if (!NamesFirst) { 64 | NamesFirst = (const char *)NamesStart; 65 | NamesLast = (const char *)NamesStart + NamesSize; 66 | return; 67 | } 68 | NamesFirst = (const char *)getMinAddr(NamesFirst, NamesStart); 69 | NamesLast = 70 | (const char *)getMaxAddr(NamesLast, (const char *)NamesStart + NamesSize); 71 | } 72 | 73 | COMPILER_RT_VISIBILITY 74 | const __llvm_profile_data *__llvm_profile_begin_data(void) { return DataFirst; } 75 | COMPILER_RT_VISIBILITY 76 | const __llvm_profile_data *__llvm_profile_end_data(void) { return DataLast; } 77 | COMPILER_RT_VISIBILITY 78 | const char *__llvm_profile_begin_names(void) { return NamesFirst; } 79 | COMPILER_RT_VISIBILITY 80 | const char *__llvm_profile_end_names(void) { return NamesLast; } 81 | COMPILER_RT_VISIBILITY 82 | uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; } 83 | COMPILER_RT_VISIBILITY 84 | uint64_t *__llvm_profile_end_counters(void) { return CountersLast; } 85 | 86 | COMPILER_RT_VISIBILITY 87 | ValueProfNode *__llvm_profile_begin_vnodes(void) { 88 | return 0; 89 | } 90 | COMPILER_RT_VISIBILITY 91 | ValueProfNode *__llvm_profile_end_vnodes(void) { return 0; } 92 | 93 | COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = 0; 94 | COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = 0; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPlatformWindows.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPlatformWindows.c - Profile data on Windows ----------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include "InstrProfiling.h" 10 | 11 | #if defined(_WIN32) 12 | 13 | #if defined(_MSC_VER) 14 | /* Merge read-write sections into .data. */ 15 | #pragma comment(linker, "/MERGE:.lprfc=.data") 16 | #pragma comment(linker, "/MERGE:.lprfd=.data") 17 | #pragma comment(linker, "/MERGE:.lprfv=.data") 18 | #pragma comment(linker, "/MERGE:.lprfnd=.data") 19 | /* Merge read-only sections into .rdata. */ 20 | #pragma comment(linker, "/MERGE:.lprfn=.rdata") 21 | #pragma comment(linker, "/MERGE:.lcovmap=.rdata") 22 | 23 | /* Allocate read-only section bounds. */ 24 | #pragma section(".lprfn$A", read) 25 | #pragma section(".lprfn$Z", read) 26 | 27 | /* Allocate read-write section bounds. */ 28 | #pragma section(".lprfd$A", read, write) 29 | #pragma section(".lprfd$Z", read, write) 30 | #pragma section(".lprfc$A", read, write) 31 | #pragma section(".lprfc$Z", read, write) 32 | #pragma section(".lprfnd$A", read, write) 33 | #pragma section(".lprfnd$Z", read, write) 34 | #endif 35 | 36 | __llvm_profile_data COMPILER_RT_SECTION(".lprfd$A") DataStart = {0}; 37 | __llvm_profile_data COMPILER_RT_SECTION(".lprfd$Z") DataEnd = {0}; 38 | 39 | const char COMPILER_RT_SECTION(".lprfn$A") NamesStart = '\0'; 40 | const char COMPILER_RT_SECTION(".lprfn$Z") NamesEnd = '\0'; 41 | 42 | uint64_t COMPILER_RT_SECTION(".lprfc$A") CountersStart; 43 | uint64_t COMPILER_RT_SECTION(".lprfc$Z") CountersEnd; 44 | 45 | ValueProfNode COMPILER_RT_SECTION(".lprfnd$A") VNodesStart; 46 | ValueProfNode COMPILER_RT_SECTION(".lprfnd$Z") VNodesEnd; 47 | 48 | const __llvm_profile_data *__llvm_profile_begin_data(void) { 49 | return &DataStart + 1; 50 | } 51 | const __llvm_profile_data *__llvm_profile_end_data(void) { return &DataEnd; } 52 | 53 | const char *__llvm_profile_begin_names(void) { return &NamesStart + 1; } 54 | const char *__llvm_profile_end_names(void) { return &NamesEnd; } 55 | 56 | uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart + 1; } 57 | uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; } 58 | 59 | ValueProfNode *__llvm_profile_begin_vnodes(void) { return &VNodesStart + 1; } 60 | ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; } 61 | 62 | ValueProfNode *CurrentVNode = &VNodesStart + 1; 63 | ValueProfNode *EndVNode = &VNodesEnd; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /Classes/InstrProfilingPort.h: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingPort.h- Support library for PGO instrumentation ------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | /* This header must be included after all others so it can provide fallback 10 | definitions for stuff missing in system headers. */ 11 | 12 | #ifndef PROFILE_INSTRPROFILING_PORT_H_ 13 | #define PROFILE_INSTRPROFILING_PORT_H_ 14 | 15 | #ifdef _MSC_VER 16 | #define COMPILER_RT_ALIGNAS(x) __declspec(align(x)) 17 | #define COMPILER_RT_VISIBILITY 18 | /* FIXME: selectany does not have the same semantics as weak. */ 19 | #define COMPILER_RT_WEAK __declspec(selectany) 20 | /* Need to include */ 21 | #define COMPILER_RT_ALLOCA _alloca 22 | /* Need to include and */ 23 | #define COMPILER_RT_FTRUNCATE(f,l) _chsize(_fileno(f),l) 24 | #define COMPILER_RT_ALWAYS_INLINE __forceinline 25 | #elif __GNUC__ 26 | #define COMPILER_RT_ALIGNAS(x) __attribute__((aligned(x))) 27 | #define COMPILER_RT_VISIBILITY __attribute__((visibility("hidden"))) 28 | #define COMPILER_RT_WEAK __attribute__((weak)) 29 | #define COMPILER_RT_ALLOCA __builtin_alloca 30 | #define COMPILER_RT_FTRUNCATE(f,l) ftruncate(fileno(f),l) 31 | #define COMPILER_RT_ALWAYS_INLINE inline __attribute((always_inline)) 32 | #endif 33 | 34 | #if defined(__APPLE__) 35 | #define COMPILER_RT_SEG "__DATA," 36 | #else 37 | #define COMPILER_RT_SEG "" 38 | #endif 39 | 40 | #ifdef _MSC_VER 41 | #define COMPILER_RT_SECTION(Sect) __declspec(allocate(Sect)) 42 | #else 43 | #define COMPILER_RT_SECTION(Sect) __attribute__((section(Sect))) 44 | #endif 45 | 46 | #define COMPILER_RT_MAX_HOSTLEN 128 47 | #ifdef __ORBIS__ 48 | #define COMPILER_RT_GETHOSTNAME(Name, Len) ((void)(Name), (void)(Len), (-1)) 49 | #else 50 | #define COMPILER_RT_GETHOSTNAME(Name, Len) lprofGetHostName(Name, Len) 51 | #endif 52 | 53 | #if COMPILER_RT_HAS_ATOMICS == 1 54 | #ifdef _MSC_VER 55 | #include 56 | #if _MSC_VER < 1900 57 | #define snprintf _snprintf 58 | #endif 59 | #if defined(_WIN64) 60 | #define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ 61 | (InterlockedCompareExchange64((LONGLONG volatile *)Ptr, (LONGLONG)NewV, \ 62 | (LONGLONG)OldV) == (LONGLONG)OldV) 63 | #define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \ 64 | (DomType *)InterlockedExchangeAdd64((LONGLONG volatile *)&PtrVar, \ 65 | (LONGLONG)sizeof(DomType) * PtrIncr) 66 | #else /* !defined(_WIN64) */ 67 | #define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ 68 | (InterlockedCompareExchange((LONG volatile *)Ptr, (LONG)NewV, (LONG)OldV) == \ 69 | (LONG)OldV) 70 | #define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \ 71 | (DomType *)InterlockedExchangeAdd((LONG volatile *)&PtrVar, \ 72 | (LONG)sizeof(DomType) * PtrIncr) 73 | #endif 74 | #else /* !defined(_MSC_VER) */ 75 | #define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ 76 | __sync_bool_compare_and_swap(Ptr, OldV, NewV) 77 | #define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \ 78 | (DomType *)__sync_fetch_and_add((long *)&PtrVar, sizeof(DomType) * PtrIncr) 79 | #endif 80 | #else /* COMPILER_RT_HAS_ATOMICS != 1 */ 81 | #include "InstrProfilingUtil.h" 82 | #define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ 83 | lprofBoolCmpXchg((void **)Ptr, OldV, NewV) 84 | #define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \ 85 | (DomType *)lprofPtrFetchAdd((void **)&PtrVar, sizeof(DomType) * PtrIncr) 86 | #endif 87 | 88 | #if defined(_WIN32) 89 | #define DIR_SEPARATOR '\\' 90 | #define DIR_SEPARATOR_2 '/' 91 | #else 92 | #define DIR_SEPARATOR '/' 93 | #endif 94 | 95 | #ifndef DIR_SEPARATOR_2 96 | #define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) 97 | #else /* DIR_SEPARATOR_2 */ 98 | #define IS_DIR_SEPARATOR(ch) \ 99 | (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) 100 | #endif /* DIR_SEPARATOR_2 */ 101 | 102 | #define PROF_ERR(Format, ...) \ 103 | fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__); 104 | 105 | #define PROF_WARN(Format, ...) \ 106 | fprintf(stderr, "LLVM Profile Warning: " Format, __VA_ARGS__); 107 | 108 | #define PROF_NOTE(Format, ...) \ 109 | fprintf(stderr, "LLVM Profile Note: " Format, __VA_ARGS__); 110 | 111 | #ifndef MAP_FILE 112 | #define MAP_FILE 0 113 | #endif 114 | 115 | #ifndef O_BINARY 116 | #define O_BINARY 0 117 | #endif 118 | 119 | #if defined(__FreeBSD__) 120 | 121 | #include 122 | #include 123 | 124 | #else /* defined(__FreeBSD__) */ 125 | 126 | #include 127 | #include 128 | 129 | #endif /* defined(__FreeBSD__) && defined(__i386__) */ 130 | 131 | #endif /* PROFILE_INSTRPROFILING_PORT_H_ */ 132 | -------------------------------------------------------------------------------- /Classes/InstrProfilingRuntime.cc: -------------------------------------------------------------------------------- 1 | //===- InstrProfilingRuntime.cpp - PGO runtime initialization -------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | extern "C" { 10 | 11 | #include "InstrProfiling.h" 12 | 13 | /* int __llvm_profile_runtime */ 14 | COMPILER_RT_VISIBILITY int INSTR_PROF_PROFILE_RUNTIME_VAR; 15 | } 16 | 17 | namespace { 18 | 19 | class RegisterRuntime { 20 | public: 21 | RegisterRuntime() { 22 | __llvm_profile_register_write_file_atexit(); 23 | __llvm_profile_initialize_file(); 24 | } 25 | }; 26 | 27 | RegisterRuntime Registration; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Classes/InstrProfilingUtil.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifdef _WIN32 10 | #include 11 | #include 12 | #include 13 | #include "WindowsMMap.h" 14 | #else 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #endif 21 | 22 | #ifdef COMPILER_RT_HAS_UNAME 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | 29 | #if defined(__linux__) 30 | #include 31 | #include 32 | #endif 33 | 34 | #include "InstrProfiling.h" 35 | #include "InstrProfilingUtil.h" 36 | 37 | COMPILER_RT_WEAK unsigned lprofDirMode = 0755; 38 | 39 | COMPILER_RT_VISIBILITY 40 | void __llvm_profile_recursive_mkdir(char *path) { 41 | int i; 42 | 43 | for (i = 1; path[i] != '\0'; ++i) { 44 | char save = path[i]; 45 | if (!IS_DIR_SEPARATOR(path[i])) 46 | continue; 47 | path[i] = '\0'; 48 | #ifdef _WIN32 49 | _mkdir(path); 50 | #else 51 | /* Some of these will fail, ignore it. */ 52 | mkdir(path, __llvm_profile_get_dir_mode()); 53 | #endif 54 | path[i] = save; 55 | } 56 | } 57 | 58 | COMPILER_RT_VISIBILITY 59 | void __llvm_profile_set_dir_mode(unsigned Mode) { lprofDirMode = Mode; } 60 | 61 | COMPILER_RT_VISIBILITY 62 | unsigned __llvm_profile_get_dir_mode(void) { return lprofDirMode; } 63 | 64 | #if COMPILER_RT_HAS_ATOMICS != 1 65 | COMPILER_RT_VISIBILITY 66 | uint32_t lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV) { 67 | void *R = *Ptr; 68 | if (R == OldV) { 69 | *Ptr = NewV; 70 | return 1; 71 | } 72 | return 0; 73 | } 74 | COMPILER_RT_VISIBILITY 75 | void *lprofPtrFetchAdd(void **Mem, long ByteIncr) { 76 | void *Old = *Mem; 77 | *((char **)Mem) += ByteIncr; 78 | return Old; 79 | } 80 | 81 | #endif 82 | 83 | #ifdef _WIN32 84 | COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) { 85 | WCHAR Buffer[COMPILER_RT_MAX_HOSTLEN]; 86 | DWORD BufferSize = sizeof(Buffer); 87 | BOOL Result = 88 | GetComputerNameExW(ComputerNameDnsFullyQualified, Buffer, &BufferSize); 89 | if (!Result) 90 | return -1; 91 | if (WideCharToMultiByte(CP_UTF8, 0, Buffer, -1, Name, Len, NULL, NULL) == 0) 92 | return -1; 93 | return 0; 94 | } 95 | #elif defined(COMPILER_RT_HAS_UNAME) 96 | COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) { 97 | struct utsname N; 98 | int R = uname(&N); 99 | if (R >= 0) { 100 | strncpy(Name, N.nodename, Len); 101 | return 0; 102 | } 103 | return R; 104 | } 105 | #endif 106 | 107 | COMPILER_RT_VISIBILITY int lprofLockFd(int fd) { 108 | #ifdef COMPILER_RT_HAS_FCNTL_LCK 109 | struct flock s_flock; 110 | 111 | s_flock.l_whence = SEEK_SET; 112 | s_flock.l_start = 0; 113 | s_flock.l_len = 0; /* Until EOF. */ 114 | s_flock.l_pid = getpid(); 115 | s_flock.l_type = F_WRLCK; 116 | 117 | while (fcntl(fd, F_SETLKW, &s_flock) == -1) { 118 | if (errno != EINTR) { 119 | if (errno == ENOLCK) { 120 | return -1; 121 | } 122 | break; 123 | } 124 | } 125 | return 0; 126 | #else 127 | flock(fd, LOCK_EX); 128 | return 0; 129 | #endif 130 | } 131 | 132 | COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) { 133 | #ifdef COMPILER_RT_HAS_FCNTL_LCK 134 | struct flock s_flock; 135 | 136 | s_flock.l_whence = SEEK_SET; 137 | s_flock.l_start = 0; 138 | s_flock.l_len = 0; /* Until EOF. */ 139 | s_flock.l_pid = getpid(); 140 | s_flock.l_type = F_UNLCK; 141 | 142 | while (fcntl(fd, F_SETLKW, &s_flock) == -1) { 143 | if (errno != EINTR) { 144 | if (errno == ENOLCK) { 145 | return -1; 146 | } 147 | break; 148 | } 149 | } 150 | return 0; 151 | #else 152 | flock(fd, LOCK_UN); 153 | return 0; 154 | #endif 155 | } 156 | 157 | COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) { 158 | FILE *f; 159 | int fd; 160 | #ifdef COMPILER_RT_HAS_FCNTL_LCK 161 | fd = open(ProfileName, O_RDWR | O_CREAT, 0666); 162 | if (fd < 0) 163 | return NULL; 164 | 165 | if (lprofLockFd(fd) != 0) 166 | PROF_WARN("Data may be corrupted during profile merging : %s\n", 167 | "Fail to obtain file lock due to system limit."); 168 | 169 | f = fdopen(fd, "r+b"); 170 | #elif defined(_WIN32) 171 | // FIXME: Use the wide variants to handle Unicode filenames. 172 | HANDLE h = CreateFileA(ProfileName, GENERIC_READ | GENERIC_WRITE, 0, 0, 173 | OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 174 | if (h == INVALID_HANDLE_VALUE) 175 | return NULL; 176 | 177 | fd = _open_osfhandle((intptr_t)h, 0); 178 | if (fd == -1) { 179 | CloseHandle(h); 180 | return NULL; 181 | } 182 | 183 | f = _fdopen(fd, "r+b"); 184 | if (f == 0) { 185 | CloseHandle(h); 186 | return NULL; 187 | } 188 | #else 189 | /* Worst case no locking applied. */ 190 | PROF_WARN("Concurrent file access is not supported : %s\n", 191 | "lack file locking"); 192 | fd = open(ProfileName, O_RDWR | O_CREAT, 0666); 193 | if (fd < 0) 194 | return NULL; 195 | f = fdopen(fd, "r+b"); 196 | #endif 197 | 198 | return f; 199 | } 200 | 201 | COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip, 202 | size_t *PrefixLen) { 203 | const char *Prefix = getenv("GCOV_PREFIX"); 204 | const char *PrefixStripStr = getenv("GCOV_PREFIX_STRIP"); 205 | 206 | *PrefixLen = 0; 207 | *PrefixStrip = 0; 208 | if (Prefix == NULL || Prefix[0] == '\0') 209 | return NULL; 210 | 211 | if (PrefixStripStr) { 212 | *PrefixStrip = atoi(PrefixStripStr); 213 | 214 | /* Negative GCOV_PREFIX_STRIP values are ignored */ 215 | if (*PrefixStrip < 0) 216 | *PrefixStrip = 0; 217 | } else { 218 | *PrefixStrip = 0; 219 | } 220 | *PrefixLen = strlen(Prefix); 221 | 222 | return Prefix; 223 | } 224 | 225 | COMPILER_RT_VISIBILITY void 226 | lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix, 227 | size_t PrefixLen, int PrefixStrip) { 228 | 229 | const char *Ptr; 230 | int Level; 231 | const char *StrippedPathStr = PathStr; 232 | 233 | for (Level = 0, Ptr = PathStr + 1; Level < PrefixStrip; ++Ptr) { 234 | if (*Ptr == '\0') 235 | break; 236 | 237 | if (!IS_DIR_SEPARATOR(*Ptr)) 238 | continue; 239 | 240 | StrippedPathStr = Ptr; 241 | ++Level; 242 | } 243 | 244 | memcpy(Dest, Prefix, PrefixLen); 245 | 246 | if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1])) 247 | Dest[PrefixLen++] = DIR_SEPARATOR; 248 | 249 | memcpy(Dest + PrefixLen, StrippedPathStr, strlen(StrippedPathStr) + 1); 250 | } 251 | 252 | COMPILER_RT_VISIBILITY const char * 253 | lprofFindFirstDirSeparator(const char *Path) { 254 | const char *Sep = strchr(Path, DIR_SEPARATOR); 255 | #if defined(DIR_SEPARATOR_2) 256 | const char *Sep2 = strchr(Path, DIR_SEPARATOR_2); 257 | if (Sep2 && (!Sep || Sep2 < Sep)) 258 | Sep = Sep2; 259 | #endif 260 | return Sep; 261 | } 262 | 263 | COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { 264 | const char *Sep = strrchr(Path, DIR_SEPARATOR); 265 | #if defined(DIR_SEPARATOR_2) 266 | const char *Sep2 = strrchr(Path, DIR_SEPARATOR_2); 267 | if (Sep2 && (!Sep || Sep2 > Sep)) 268 | Sep = Sep2; 269 | #endif 270 | return Sep; 271 | } 272 | 273 | COMPILER_RT_VISIBILITY int lprofSuspendSigKill() { 274 | #if defined(__linux__) 275 | int PDeachSig = 0; 276 | /* Temporarily suspend getting SIGKILL upon exit of the parent process. */ 277 | if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL) 278 | prctl(PR_SET_PDEATHSIG, 0); 279 | return (PDeachSig == SIGKILL); 280 | #else 281 | return 0; 282 | #endif 283 | } 284 | 285 | COMPILER_RT_VISIBILITY void lprofRestoreSigKill() { 286 | #if defined(__linux__) 287 | prctl(PR_SET_PDEATHSIG, SIGKILL); 288 | #endif 289 | } 290 | -------------------------------------------------------------------------------- /Classes/InstrProfilingUtil.h: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifndef PROFILE_INSTRPROFILINGUTIL_H 10 | #define PROFILE_INSTRPROFILINGUTIL_H 11 | 12 | #include 13 | #include 14 | 15 | /*! \brief Create a directory tree. */ 16 | void __llvm_profile_recursive_mkdir(char *Pathname); 17 | 18 | /*! Set the mode used when creating profile directories. */ 19 | void __llvm_profile_set_dir_mode(unsigned Mode); 20 | 21 | /*! Return the directory creation mode. */ 22 | unsigned __llvm_profile_get_dir_mode(void); 23 | 24 | int lprofLockFd(int fd); 25 | int lprofUnlockFd(int fd); 26 | 27 | /*! Open file \c Filename for read+write with write 28 | * lock for exclusive access. The caller will block 29 | * if the lock is already held by another process. */ 30 | FILE *lprofOpenFileEx(const char *Filename); 31 | /* PS4 doesn't have getenv. Define a shim. */ 32 | #if __ORBIS__ 33 | static inline char *getenv(const char *name) { return NULL; } 34 | #endif /* #if __ORBIS__ */ 35 | 36 | /* GCOV_PREFIX and GCOV_PREFIX_STRIP support */ 37 | /* Return the path prefix specified by GCOV_PREFIX environment variable. 38 | * If GCOV_PREFIX_STRIP is also specified, the strip level (integer value) 39 | * is returned via \c *PrefixStrip. The prefix length is stored in *PrefixLen. 40 | */ 41 | const char *lprofGetPathPrefix(int *PrefixStrip, size_t *PrefixLen); 42 | /* Apply the path prefix specified in \c Prefix to path string in \c PathStr, 43 | * and store the result to buffer pointed to by \c Buffer. If \c PrefixStrip 44 | * is not zero, path prefixes are stripped from \c PathStr (the level of 45 | * stripping is specified by \c PrefixStrip) before \c Prefix is added. 46 | */ 47 | void lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix, 48 | size_t PrefixLen, int PrefixStrip); 49 | 50 | /* Returns a pointer to the first occurrence of \c DIR_SEPARATOR char in 51 | * the string \c Path, or NULL if the char is not found. */ 52 | const char *lprofFindFirstDirSeparator(const char *Path); 53 | /* Returns a pointer to the last occurrence of \c DIR_SEPARATOR char in 54 | * the string \c Path, or NULL if the char is not found. */ 55 | const char *lprofFindLastDirSeparator(const char *Path); 56 | 57 | int lprofGetHostName(char *Name, int Len); 58 | 59 | unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV); 60 | void *lprofPtrFetchAdd(void **Mem, long ByteIncr); 61 | 62 | /* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed. 63 | * Other return values mean no restore is needed. 64 | */ 65 | int lprofSuspendSigKill(); 66 | 67 | /* Restore previously suspended SIGKILL. */ 68 | void lprofRestoreSigKill(); 69 | 70 | #endif /* PROFILE_INSTRPROFILINGUTIL_H */ 71 | -------------------------------------------------------------------------------- /Classes/InstrProfilingValue.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingValue.c - Support library for PGO instrumentation ----===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "InstrProfiling.h" 15 | #include "InstrProfilingInternal.h" 16 | #include "InstrProfilingUtil.h" 17 | 18 | #define INSTR_PROF_VALUE_PROF_DATA 19 | #define INSTR_PROF_COMMON_API_IMPL 20 | #include "InstrProfData.inc" 21 | 22 | static int hasStaticCounters = 1; 23 | static int OutOfNodesWarnings = 0; 24 | static int hasNonDefaultValsPerSite = 0; 25 | #define INSTR_PROF_MAX_VP_WARNS 10 26 | #define INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE 16 27 | #define INSTR_PROF_VNODE_POOL_SIZE 1024 28 | 29 | #ifndef _MSC_VER 30 | /* A shared static pool in addition to the vnodes statically 31 | * allocated by the compiler. */ 32 | COMPILER_RT_VISIBILITY ValueProfNode 33 | lprofValueProfNodes[INSTR_PROF_VNODE_POOL_SIZE] COMPILER_RT_SECTION( 34 | COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME); 35 | #endif 36 | 37 | COMPILER_RT_VISIBILITY uint32_t VPMaxNumValsPerSite = 38 | INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE; 39 | 40 | COMPILER_RT_VISIBILITY void lprofSetupValueProfiler() { 41 | const char *Str = 0; 42 | Str = getenv("LLVM_VP_MAX_NUM_VALS_PER_SITE"); 43 | if (Str && Str[0]) { 44 | VPMaxNumValsPerSite = atoi(Str); 45 | hasNonDefaultValsPerSite = 1; 46 | } 47 | if (VPMaxNumValsPerSite > INSTR_PROF_MAX_NUM_VAL_PER_SITE) 48 | VPMaxNumValsPerSite = INSTR_PROF_MAX_NUM_VAL_PER_SITE; 49 | } 50 | 51 | COMPILER_RT_VISIBILITY void lprofSetMaxValsPerSite(uint32_t MaxVals) { 52 | VPMaxNumValsPerSite = MaxVals; 53 | hasNonDefaultValsPerSite = 1; 54 | } 55 | 56 | /* This method is only used in value profiler mock testing. */ 57 | COMPILER_RT_VISIBILITY void 58 | __llvm_profile_set_num_value_sites(__llvm_profile_data *Data, 59 | uint32_t ValueKind, uint16_t NumValueSites) { 60 | *((uint16_t *)&Data->NumValueSites[ValueKind]) = NumValueSites; 61 | } 62 | 63 | /* This method is only used in value profiler mock testing. */ 64 | COMPILER_RT_VISIBILITY const __llvm_profile_data * 65 | __llvm_profile_iterate_data(const __llvm_profile_data *Data) { 66 | return Data + 1; 67 | } 68 | 69 | /* This method is only used in value profiler mock testing. */ 70 | COMPILER_RT_VISIBILITY void * 71 | __llvm_get_function_addr(const __llvm_profile_data *Data) { 72 | return Data->FunctionPointer; 73 | } 74 | 75 | /* Allocate an array that holds the pointers to the linked lists of 76 | * value profile counter nodes. The number of element of the array 77 | * is the total number of value profile sites instrumented. Returns 78 | * 0 if allocation fails. 79 | */ 80 | 81 | static int allocateValueProfileCounters(__llvm_profile_data *Data) { 82 | uint64_t NumVSites = 0; 83 | uint32_t VKI; 84 | 85 | /* This function will never be called when value site array is allocated 86 | statically at compile time. */ 87 | hasStaticCounters = 0; 88 | /* When dynamic allocation is enabled, allow tracking the max number of 89 | * values allowd. */ 90 | if (!hasNonDefaultValsPerSite) 91 | VPMaxNumValsPerSite = INSTR_PROF_MAX_NUM_VAL_PER_SITE; 92 | 93 | for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) 94 | NumVSites += Data->NumValueSites[VKI]; 95 | 96 | ValueProfNode **Mem = 97 | (ValueProfNode **)calloc(NumVSites, sizeof(ValueProfNode *)); 98 | if (!Mem) 99 | return 0; 100 | if (!COMPILER_RT_BOOL_CMPXCHG(&Data->Values, 0, Mem)) { 101 | free(Mem); 102 | return 0; 103 | } 104 | return 1; 105 | } 106 | 107 | static ValueProfNode *allocateOneNode(void) { 108 | ValueProfNode *Node; 109 | 110 | if (!hasStaticCounters) 111 | return (ValueProfNode *)calloc(1, sizeof(ValueProfNode)); 112 | 113 | /* Early check to avoid value wrapping around. */ 114 | if (CurrentVNode + 1 > EndVNode) { 115 | if (OutOfNodesWarnings++ < INSTR_PROF_MAX_VP_WARNS) { 116 | PROF_WARN("Unable to track new values: %s. " 117 | " Consider using option -mllvm -vp-counters-per-site= to " 118 | "allocate more" 119 | " value profile counters at compile time. \n", 120 | "Running out of static counters"); 121 | } 122 | return 0; 123 | } 124 | Node = COMPILER_RT_PTR_FETCH_ADD(ValueProfNode, CurrentVNode, 1); 125 | /* Due to section padding, EndVNode point to a byte which is one pass 126 | * an incomplete VNode, so we need to skip the last incomplete node. */ 127 | if (Node + 1 > EndVNode) 128 | return 0; 129 | 130 | return Node; 131 | } 132 | 133 | static COMPILER_RT_ALWAYS_INLINE void 134 | instrumentTargetValueImpl(uint64_t TargetValue, void *Data, 135 | uint32_t CounterIndex, uint64_t CountValue) { 136 | __llvm_profile_data *PData = (__llvm_profile_data *)Data; 137 | if (!PData) 138 | return; 139 | if (!CountValue) 140 | return; 141 | if (!PData->Values) { 142 | if (!allocateValueProfileCounters(PData)) 143 | return; 144 | } 145 | 146 | ValueProfNode **ValueCounters = (ValueProfNode **)PData->Values; 147 | ValueProfNode *PrevVNode = NULL; 148 | ValueProfNode *MinCountVNode = NULL; 149 | ValueProfNode *CurVNode = ValueCounters[CounterIndex]; 150 | uint64_t MinCount = UINT64_MAX; 151 | 152 | uint8_t VDataCount = 0; 153 | while (CurVNode) { 154 | if (TargetValue == CurVNode->Value) { 155 | CurVNode->Count += CountValue; 156 | return; 157 | } 158 | if (CurVNode->Count < MinCount) { 159 | MinCount = CurVNode->Count; 160 | MinCountVNode = CurVNode; 161 | } 162 | PrevVNode = CurVNode; 163 | CurVNode = CurVNode->Next; 164 | ++VDataCount; 165 | } 166 | 167 | if (VDataCount >= VPMaxNumValsPerSite) { 168 | /* Bump down the min count node's count. If it reaches 0, 169 | * evict it. This eviction/replacement policy makes hot 170 | * targets more sticky while cold targets less so. In other 171 | * words, it makes it less likely for the hot targets to be 172 | * prematurally evicted during warmup/establishment period, 173 | * when their counts are still low. In a special case when 174 | * the number of values tracked is reduced to only one, this 175 | * policy will guarantee that the dominating target with >50% 176 | * total count will survive in the end. Note that this scheme 177 | * allows the runtime to track the min count node in an adaptive 178 | * manner. It can correct previous mistakes and eventually 179 | * lock on a cold target that is alread in stable state. 180 | * 181 | * In very rare cases, this replacement scheme may still lead 182 | * to target loss. For instance, out of \c N value slots, \c N-1 183 | * slots are occupied by luke warm targets during the warmup 184 | * period and the remaining one slot is competed by two or more 185 | * very hot targets. If those hot targets occur in an interleaved 186 | * way, none of them will survive (gain enough weight to throw out 187 | * other established entries) due to the ping-pong effect. 188 | * To handle this situation, user can choose to increase the max 189 | * number of tracked values per value site. Alternatively, a more 190 | * expensive eviction mechanism can be implemented. It requires 191 | * the runtime to track the total number of evictions per-site. 192 | * When the total number of evictions reaches certain threshold, 193 | * the runtime can wipe out more than one lowest count entries 194 | * to give space for hot targets. 195 | */ 196 | if (MinCountVNode->Count <= CountValue) { 197 | CurVNode = MinCountVNode; 198 | CurVNode->Value = TargetValue; 199 | CurVNode->Count = CountValue; 200 | } else 201 | MinCountVNode->Count -= CountValue; 202 | 203 | return; 204 | } 205 | 206 | CurVNode = allocateOneNode(); 207 | if (!CurVNode) 208 | return; 209 | CurVNode->Value = TargetValue; 210 | CurVNode->Count += CountValue; 211 | 212 | uint32_t Success = 0; 213 | if (!ValueCounters[CounterIndex]) 214 | Success = 215 | COMPILER_RT_BOOL_CMPXCHG(&ValueCounters[CounterIndex], 0, CurVNode); 216 | else if (PrevVNode && !PrevVNode->Next) 217 | Success = COMPILER_RT_BOOL_CMPXCHG(&(PrevVNode->Next), 0, CurVNode); 218 | 219 | if (!Success && !hasStaticCounters) { 220 | free(CurVNode); 221 | return; 222 | } 223 | } 224 | 225 | COMPILER_RT_VISIBILITY void 226 | __llvm_profile_instrument_target(uint64_t TargetValue, void *Data, 227 | uint32_t CounterIndex) { 228 | instrumentTargetValueImpl(TargetValue, Data, CounterIndex, 1); 229 | } 230 | COMPILER_RT_VISIBILITY void 231 | __llvm_profile_instrument_target_value(uint64_t TargetValue, void *Data, 232 | uint32_t CounterIndex, 233 | uint64_t CountValue) { 234 | instrumentTargetValueImpl(TargetValue, Data, CounterIndex, CountValue); 235 | } 236 | 237 | /* 238 | * The target values are partitioned into multiple regions/ranges. There is one 239 | * contiguous region which is precise -- every value in the range is tracked 240 | * individually. A value outside the precise region will be collapsed into one 241 | * value depending on the region it falls in. 242 | * 243 | * There are three regions: 244 | * 1. (-inf, PreciseRangeStart) and (PreciseRangeLast, LargeRangeValue) belong 245 | * to one region -- all values here should be mapped to one value of 246 | * "PreciseRangeLast + 1". 247 | * 2. [PreciseRangeStart, PreciseRangeLast] 248 | * 3. Large values: [LargeValue, +inf) maps to one value of LargeValue. 249 | * 250 | * The range for large values is optional. The default value of INT64_MIN 251 | * indicates it is not specified. 252 | */ 253 | COMPILER_RT_VISIBILITY void __llvm_profile_instrument_range( 254 | uint64_t TargetValue, void *Data, uint32_t CounterIndex, 255 | int64_t PreciseRangeStart, int64_t PreciseRangeLast, int64_t LargeValue) { 256 | 257 | if (LargeValue != INT64_MIN && (int64_t)TargetValue >= LargeValue) 258 | TargetValue = LargeValue; 259 | else if ((int64_t)TargetValue < PreciseRangeStart || 260 | (int64_t)TargetValue > PreciseRangeLast) 261 | TargetValue = PreciseRangeLast + 1; 262 | 263 | __llvm_profile_instrument_target(TargetValue, Data, CounterIndex); 264 | } 265 | 266 | /* 267 | * A wrapper struct that represents value profile runtime data. 268 | * Like InstrProfRecord class which is used by profiling host tools, 269 | * ValueProfRuntimeRecord also implements the abstract intefaces defined in 270 | * ValueProfRecordClosure so that the runtime data can be serialized using 271 | * shared C implementation. 272 | */ 273 | typedef struct ValueProfRuntimeRecord { 274 | const __llvm_profile_data *Data; 275 | ValueProfNode **NodesKind[IPVK_Last + 1]; 276 | uint8_t **SiteCountArray; 277 | } ValueProfRuntimeRecord; 278 | 279 | /* ValueProfRecordClosure Interface implementation. */ 280 | 281 | static uint32_t getNumValueSitesRT(const void *R, uint32_t VK) { 282 | return ((const ValueProfRuntimeRecord *)R)->Data->NumValueSites[VK]; 283 | } 284 | 285 | static uint32_t getNumValueDataRT(const void *R, uint32_t VK) { 286 | uint32_t S = 0, I; 287 | const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R; 288 | if (Record->SiteCountArray[VK] == INSTR_PROF_NULLPTR) 289 | return 0; 290 | for (I = 0; I < Record->Data->NumValueSites[VK]; I++) 291 | S += Record->SiteCountArray[VK][I]; 292 | return S; 293 | } 294 | 295 | static uint32_t getNumValueDataForSiteRT(const void *R, uint32_t VK, 296 | uint32_t S) { 297 | const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R; 298 | return Record->SiteCountArray[VK][S]; 299 | } 300 | 301 | static ValueProfRuntimeRecord RTRecord; 302 | static ValueProfRecordClosure RTRecordClosure = { 303 | &RTRecord, INSTR_PROF_NULLPTR, /* GetNumValueKinds */ 304 | getNumValueSitesRT, getNumValueDataRT, getNumValueDataForSiteRT, 305 | INSTR_PROF_NULLPTR, /* RemapValueData */ 306 | INSTR_PROF_NULLPTR, /* GetValueForSite, */ 307 | INSTR_PROF_NULLPTR /* AllocValueProfData */ 308 | }; 309 | 310 | static uint32_t 311 | initializeValueProfRuntimeRecord(const __llvm_profile_data *Data, 312 | uint8_t *SiteCountArray[]) { 313 | unsigned I, J, S = 0, NumValueKinds = 0; 314 | ValueProfNode **Nodes = (ValueProfNode **)Data->Values; 315 | RTRecord.Data = Data; 316 | RTRecord.SiteCountArray = SiteCountArray; 317 | for (I = 0; I <= IPVK_Last; I++) { 318 | uint16_t N = Data->NumValueSites[I]; 319 | if (!N) 320 | continue; 321 | 322 | NumValueKinds++; 323 | 324 | RTRecord.NodesKind[I] = Nodes ? &Nodes[S] : INSTR_PROF_NULLPTR; 325 | for (J = 0; J < N; J++) { 326 | /* Compute value count for each site. */ 327 | uint32_t C = 0; 328 | ValueProfNode *Site = 329 | Nodes ? RTRecord.NodesKind[I][J] : INSTR_PROF_NULLPTR; 330 | while (Site) { 331 | C++; 332 | Site = Site->Next; 333 | } 334 | if (C > UCHAR_MAX) 335 | C = UCHAR_MAX; 336 | RTRecord.SiteCountArray[I][J] = C; 337 | } 338 | S += N; 339 | } 340 | return NumValueKinds; 341 | } 342 | 343 | static ValueProfNode *getNextNValueData(uint32_t VK, uint32_t Site, 344 | InstrProfValueData *Dst, 345 | ValueProfNode *StartNode, uint32_t N) { 346 | unsigned I; 347 | ValueProfNode *VNode = StartNode ? StartNode : RTRecord.NodesKind[VK][Site]; 348 | for (I = 0; I < N; I++) { 349 | Dst[I].Value = VNode->Value; 350 | Dst[I].Count = VNode->Count; 351 | VNode = VNode->Next; 352 | } 353 | return VNode; 354 | } 355 | 356 | static uint32_t getValueProfDataSizeWrapper(void) { 357 | return getValueProfDataSize(&RTRecordClosure); 358 | } 359 | 360 | static uint32_t getNumValueDataForSiteWrapper(uint32_t VK, uint32_t S) { 361 | return getNumValueDataForSiteRT(&RTRecord, VK, S); 362 | } 363 | 364 | static VPDataReaderType TheVPDataReader = { 365 | initializeValueProfRuntimeRecord, getValueProfRecordHeaderSize, 366 | getFirstValueProfRecord, getNumValueDataForSiteWrapper, 367 | getValueProfDataSizeWrapper, getNextNValueData}; 368 | 369 | COMPILER_RT_VISIBILITY VPDataReaderType *lprofGetVPDataReader() { 370 | return &TheVPDataReader; 371 | } 372 | -------------------------------------------------------------------------------- /Classes/InstrProfilingWriter.c: -------------------------------------------------------------------------------- 1 | /*===- InstrProfilingWriter.c - Write instrumentation to a file or buffer -===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifdef _MSC_VER 10 | /* For _alloca */ 11 | #include 12 | #endif 13 | #include 14 | 15 | #include "InstrProfiling.h" 16 | #include "InstrProfilingInternal.h" 17 | 18 | #define INSTR_PROF_VALUE_PROF_DATA 19 | #include "InstrProfData.inc" 20 | 21 | COMPILER_RT_VISIBILITY void (*FreeHook)(void *) = NULL; 22 | static ProfBufferIO TheBufferIO; 23 | #define VP_BUFFER_SIZE 8 * 1024 24 | static uint8_t BufferIOBuffer[VP_BUFFER_SIZE]; 25 | static InstrProfValueData VPDataArray[16]; 26 | static uint32_t VPDataArraySize = sizeof(VPDataArray) / sizeof(*VPDataArray); 27 | 28 | COMPILER_RT_VISIBILITY uint8_t *DynamicBufferIOBuffer = 0; 29 | COMPILER_RT_VISIBILITY uint32_t VPBufferSize = 0; 30 | 31 | /* The buffer writer is reponsponsible in keeping writer state 32 | * across the call. 33 | */ 34 | COMPILER_RT_VISIBILITY uint32_t lprofBufferWriter(ProfDataWriter *This, 35 | ProfDataIOVec *IOVecs, 36 | uint32_t NumIOVecs) { 37 | uint32_t I; 38 | char **Buffer = (char **)&This->WriterCtx; 39 | for (I = 0; I < NumIOVecs; I++) { 40 | size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; 41 | if (IOVecs[I].Data) 42 | memcpy(*Buffer, IOVecs[I].Data, Length); 43 | *Buffer += Length; 44 | } 45 | return 0; 46 | } 47 | 48 | static void llvmInitBufferIO(ProfBufferIO *BufferIO, ProfDataWriter *FileWriter, 49 | uint8_t *Buffer, uint32_t BufferSz) { 50 | BufferIO->FileWriter = FileWriter; 51 | BufferIO->OwnFileWriter = 0; 52 | BufferIO->BufferStart = Buffer; 53 | BufferIO->BufferSz = BufferSz; 54 | BufferIO->CurOffset = 0; 55 | } 56 | 57 | COMPILER_RT_VISIBILITY ProfBufferIO * 58 | lprofCreateBufferIO(ProfDataWriter *FileWriter) { 59 | uint8_t *Buffer = DynamicBufferIOBuffer; 60 | uint32_t BufferSize = VPBufferSize; 61 | if (!Buffer) { 62 | Buffer = &BufferIOBuffer[0]; 63 | BufferSize = sizeof(BufferIOBuffer); 64 | } 65 | llvmInitBufferIO(&TheBufferIO, FileWriter, Buffer, BufferSize); 66 | return &TheBufferIO; 67 | } 68 | 69 | COMPILER_RT_VISIBILITY void lprofDeleteBufferIO(ProfBufferIO *BufferIO) { 70 | if (BufferIO->OwnFileWriter) 71 | FreeHook(BufferIO->FileWriter); 72 | if (DynamicBufferIOBuffer) { 73 | FreeHook(DynamicBufferIOBuffer); 74 | DynamicBufferIOBuffer = 0; 75 | VPBufferSize = 0; 76 | } 77 | } 78 | 79 | COMPILER_RT_VISIBILITY int 80 | lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size) { 81 | /* Buffer is not large enough, it is time to flush. */ 82 | if (Size + BufferIO->CurOffset > BufferIO->BufferSz) { 83 | if (lprofBufferIOFlush(BufferIO) != 0) 84 | return -1; 85 | } 86 | /* Special case, bypass the buffer completely. */ 87 | ProfDataIOVec IO[] = {{Data, sizeof(uint8_t), Size}}; 88 | if (Size > BufferIO->BufferSz) { 89 | if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1)) 90 | return -1; 91 | } else { 92 | /* Write the data to buffer */ 93 | uint8_t *Buffer = BufferIO->BufferStart + BufferIO->CurOffset; 94 | ProfDataWriter BufferWriter; 95 | initBufferWriter(&BufferWriter, (char *)Buffer); 96 | lprofBufferWriter(&BufferWriter, IO, 1); 97 | BufferIO->CurOffset = 98 | (uint8_t *)BufferWriter.WriterCtx - BufferIO->BufferStart; 99 | } 100 | return 0; 101 | } 102 | 103 | COMPILER_RT_VISIBILITY int lprofBufferIOFlush(ProfBufferIO *BufferIO) { 104 | if (BufferIO->CurOffset) { 105 | ProfDataIOVec IO[] = { 106 | {BufferIO->BufferStart, sizeof(uint8_t), BufferIO->CurOffset}}; 107 | if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1)) 108 | return -1; 109 | BufferIO->CurOffset = 0; 110 | } 111 | return 0; 112 | } 113 | 114 | /* Write out value profile data for function specified with \c Data. 115 | * The implementation does not use the method \c serializeValueProfData 116 | * which depends on dynamic memory allocation. In this implementation, 117 | * value profile data is written out to \c BufferIO piecemeal. 118 | */ 119 | static int writeOneValueProfData(ProfBufferIO *BufferIO, 120 | VPDataReaderType *VPDataReader, 121 | const __llvm_profile_data *Data) { 122 | unsigned I, NumValueKinds = 0; 123 | ValueProfData VPHeader; 124 | uint8_t *SiteCountArray[IPVK_Last + 1]; 125 | 126 | for (I = 0; I <= IPVK_Last; I++) { 127 | if (!Data->NumValueSites[I]) 128 | SiteCountArray[I] = 0; 129 | else { 130 | uint32_t Sz = 131 | VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) - 132 | offsetof(ValueProfRecord, SiteCountArray); 133 | /* Only use alloca for this small byte array to avoid excessive 134 | * stack growth. */ 135 | SiteCountArray[I] = (uint8_t *)COMPILER_RT_ALLOCA(Sz); 136 | memset(SiteCountArray[I], 0, Sz); 137 | } 138 | } 139 | 140 | /* If NumValueKinds returned is 0, there is nothing to write, report 141 | success and return. This should match the raw profile reader's behavior. */ 142 | if (!(NumValueKinds = VPDataReader->InitRTRecord(Data, SiteCountArray))) 143 | return 0; 144 | 145 | /* First write the header structure. */ 146 | VPHeader.TotalSize = VPDataReader->GetValueProfDataSize(); 147 | VPHeader.NumValueKinds = NumValueKinds; 148 | if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPHeader, 149 | sizeof(ValueProfData))) 150 | return -1; 151 | 152 | /* Make sure nothing else needs to be written before value profile 153 | * records. */ 154 | if ((void *)VPDataReader->GetFirstValueProfRecord(&VPHeader) != 155 | (void *)(&VPHeader + 1)) 156 | return -1; 157 | 158 | /* Write out the value profile record for each value kind 159 | * one by one. */ 160 | for (I = 0; I <= IPVK_Last; I++) { 161 | uint32_t J; 162 | ValueProfRecord RecordHeader; 163 | /* The size of the value prof record header without counting the 164 | * site count array .*/ 165 | uint32_t RecordHeaderSize = offsetof(ValueProfRecord, SiteCountArray); 166 | uint32_t SiteCountArraySize; 167 | 168 | if (!Data->NumValueSites[I]) 169 | continue; 170 | 171 | /* Write out the record header. */ 172 | RecordHeader.Kind = I; 173 | RecordHeader.NumValueSites = Data->NumValueSites[I]; 174 | if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&RecordHeader, 175 | RecordHeaderSize)) 176 | return -1; 177 | 178 | /* Write out the site value count array including padding space. */ 179 | SiteCountArraySize = 180 | VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) - 181 | RecordHeaderSize; 182 | if (lprofBufferIOWrite(BufferIO, SiteCountArray[I], SiteCountArraySize)) 183 | return -1; 184 | 185 | /* Write out the value profile data for each value site. */ 186 | for (J = 0; J < Data->NumValueSites[I]; J++) { 187 | uint32_t NRead, NRemain; 188 | ValueProfNode *NextStartNode = 0; 189 | NRemain = VPDataReader->GetNumValueDataForSite(I, J); 190 | if (!NRemain) 191 | continue; 192 | /* Read and write out value data in small chunks till it is done. */ 193 | do { 194 | NRead = (NRemain > VPDataArraySize ? VPDataArraySize : NRemain); 195 | NextStartNode = 196 | VPDataReader->GetValueData(I, /* ValueKind */ 197 | J, /* Site */ 198 | &VPDataArray[0], NextStartNode, NRead); 199 | if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPDataArray[0], 200 | NRead * sizeof(InstrProfValueData))) 201 | return -1; 202 | NRemain -= NRead; 203 | } while (NRemain != 0); 204 | } 205 | } 206 | /* All done report success. */ 207 | return 0; 208 | } 209 | 210 | static int writeValueProfData(ProfDataWriter *Writer, 211 | VPDataReaderType *VPDataReader, 212 | const __llvm_profile_data *DataBegin, 213 | const __llvm_profile_data *DataEnd) { 214 | ProfBufferIO *BufferIO; 215 | const __llvm_profile_data *DI = 0; 216 | 217 | if (!VPDataReader) 218 | return 0; 219 | 220 | BufferIO = lprofCreateBufferIO(Writer); 221 | 222 | for (DI = DataBegin; DI < DataEnd; DI++) { 223 | if (writeOneValueProfData(BufferIO, VPDataReader, DI)) 224 | return -1; 225 | } 226 | 227 | if (lprofBufferIOFlush(BufferIO) != 0) 228 | return -1; 229 | lprofDeleteBufferIO(BufferIO); 230 | 231 | return 0; 232 | } 233 | 234 | COMPILER_RT_VISIBILITY int lprofWriteData(ProfDataWriter *Writer, 235 | VPDataReaderType *VPDataReader, 236 | int SkipNameDataWrite) { 237 | /* Match logic in __llvm_profile_write_buffer(). */ 238 | const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 239 | const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 240 | const uint64_t *CountersBegin = __llvm_profile_begin_counters(); 241 | const uint64_t *CountersEnd = __llvm_profile_end_counters(); 242 | const char *NamesBegin = __llvm_profile_begin_names(); 243 | const char *NamesEnd = __llvm_profile_end_names(); 244 | return lprofWriteDataImpl(Writer, DataBegin, DataEnd, CountersBegin, 245 | CountersEnd, VPDataReader, NamesBegin, NamesEnd, 246 | SkipNameDataWrite); 247 | } 248 | 249 | COMPILER_RT_VISIBILITY int 250 | lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, 251 | const __llvm_profile_data *DataEnd, 252 | const uint64_t *CountersBegin, const uint64_t *CountersEnd, 253 | VPDataReaderType *VPDataReader, const char *NamesBegin, 254 | const char *NamesEnd, int SkipNameDataWrite) { 255 | 256 | /* Calculate size of sections. */ 257 | const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); 258 | const uint64_t CountersSize = CountersEnd - CountersBegin; 259 | const uint64_t NamesSize = NamesEnd - NamesBegin; 260 | const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); 261 | 262 | /* Enough zeroes for padding. */ 263 | const char Zeroes[sizeof(uint64_t)] = {0}; 264 | 265 | /* Create the header. */ 266 | __llvm_profile_header Header; 267 | 268 | if (!DataSize) 269 | return 0; 270 | 271 | /* Initialize header structure. */ 272 | #define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init; 273 | #include "InstrProfData.inc" 274 | 275 | /* Write the data. */ 276 | ProfDataIOVec IOVec[] = { 277 | {&Header, sizeof(__llvm_profile_header), 1}, 278 | {DataBegin, sizeof(__llvm_profile_data), DataSize}, 279 | {CountersBegin, sizeof(uint64_t), CountersSize}, 280 | {SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize}, 281 | {Zeroes, sizeof(uint8_t), Padding}}; 282 | if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec))) 283 | return -1; 284 | 285 | return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd); 286 | } 287 | -------------------------------------------------------------------------------- /Classes/WindowsMMap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is derived from uClibc (original license follows). 3 | * https://git.uclibc.org/uClibc/tree/utils/mmap-windows.c 4 | */ 5 | /* mmap() replacement for Windows 6 | * 7 | * Author: Mike Frysinger 8 | * Placed into the public domain 9 | */ 10 | 11 | /* References: 12 | * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx 13 | * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx 14 | * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx 15 | * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx 16 | */ 17 | 18 | #if defined(_WIN32) 19 | 20 | #include "WindowsMMap.h" 21 | 22 | #define WIN32_LEAN_AND_MEAN 23 | #include 24 | 25 | #include "InstrProfiling.h" 26 | 27 | COMPILER_RT_VISIBILITY 28 | void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) 29 | { 30 | if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) 31 | return MAP_FAILED; 32 | if (fd == -1) { 33 | if (!(flags & MAP_ANON) || offset) 34 | return MAP_FAILED; 35 | } else if (flags & MAP_ANON) 36 | return MAP_FAILED; 37 | 38 | DWORD flProtect; 39 | if (prot & PROT_WRITE) { 40 | if (prot & PROT_EXEC) 41 | flProtect = PAGE_EXECUTE_READWRITE; 42 | else 43 | flProtect = PAGE_READWRITE; 44 | } else if (prot & PROT_EXEC) { 45 | if (prot & PROT_READ) 46 | flProtect = PAGE_EXECUTE_READ; 47 | else if (prot & PROT_EXEC) 48 | flProtect = PAGE_EXECUTE; 49 | } else 50 | flProtect = PAGE_READONLY; 51 | 52 | off_t end = length + offset; 53 | HANDLE mmap_fd, h; 54 | if (fd == -1) 55 | mmap_fd = INVALID_HANDLE_VALUE; 56 | else 57 | mmap_fd = (HANDLE)_get_osfhandle(fd); 58 | h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); 59 | if (h == NULL) 60 | return MAP_FAILED; 61 | 62 | DWORD dwDesiredAccess; 63 | if (prot & PROT_WRITE) 64 | dwDesiredAccess = FILE_MAP_WRITE; 65 | else 66 | dwDesiredAccess = FILE_MAP_READ; 67 | if (prot & PROT_EXEC) 68 | dwDesiredAccess |= FILE_MAP_EXECUTE; 69 | if (flags & MAP_PRIVATE) 70 | dwDesiredAccess |= FILE_MAP_COPY; 71 | void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); 72 | if (ret == NULL) { 73 | CloseHandle(h); 74 | ret = MAP_FAILED; 75 | } 76 | return ret; 77 | } 78 | 79 | COMPILER_RT_VISIBILITY 80 | void munmap(void *addr, size_t length) 81 | { 82 | UnmapViewOfFile(addr); 83 | /* ruh-ro, we leaked handle from CreateFileMapping() ... */ 84 | } 85 | 86 | COMPILER_RT_VISIBILITY 87 | int msync(void *addr, size_t length, int flags) 88 | { 89 | if (flags & MS_INVALIDATE) 90 | return -1; /* Not supported. */ 91 | 92 | /* Exactly one of MS_ASYNC or MS_SYNC must be specified. */ 93 | switch (flags & (MS_ASYNC | MS_SYNC)) { 94 | case MS_SYNC: 95 | case MS_ASYNC: 96 | break; 97 | default: 98 | return -1; 99 | } 100 | 101 | if (!FlushViewOfFile(addr, length)) 102 | return -1; 103 | 104 | if (flags & MS_SYNC) { 105 | /* FIXME: No longer have access to handle from CreateFileMapping(). */ 106 | /* 107 | * if (!FlushFileBuffers(h)) 108 | * return -1; 109 | */ 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | COMPILER_RT_VISIBILITY 116 | int lock(HANDLE handle, DWORD lockType, BOOL blocking) { 117 | DWORD flags = lockType; 118 | if (!blocking) 119 | flags |= LOCKFILE_FAIL_IMMEDIATELY; 120 | 121 | OVERLAPPED overlapped; 122 | ZeroMemory(&overlapped, sizeof(OVERLAPPED)); 123 | overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 124 | BOOL result = LockFileEx(handle, flags, 0, MAXDWORD, MAXDWORD, &overlapped); 125 | if (!result) { 126 | DWORD dw = GetLastError(); 127 | 128 | // In non-blocking mode, return an error if the file is locked. 129 | if (!blocking && dw == ERROR_LOCK_VIOLATION) 130 | return -1; // EWOULDBLOCK 131 | 132 | // If the error is ERROR_IO_PENDING, we need to wait until the operation 133 | // finishes. Otherwise, we return an error. 134 | if (dw != ERROR_IO_PENDING) 135 | return -1; 136 | 137 | DWORD dwNumBytes; 138 | if (!GetOverlappedResult(handle, &overlapped, &dwNumBytes, TRUE)) 139 | return -1; 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | COMPILER_RT_VISIBILITY 146 | int flock(int fd, int operation) { 147 | HANDLE handle = (HANDLE)_get_osfhandle(fd); 148 | if (handle == INVALID_HANDLE_VALUE) 149 | return -1; 150 | 151 | BOOL blocking = (operation & LOCK_NB) == 0; 152 | int op = operation & ~LOCK_NB; 153 | 154 | switch (op) { 155 | case LOCK_EX: 156 | return lock(handle, LOCKFILE_EXCLUSIVE_LOCK, blocking); 157 | 158 | case LOCK_SH: 159 | return lock(handle, 0, blocking); 160 | 161 | case LOCK_UN: 162 | if (!UnlockFile(handle, 0, 0, MAXDWORD, MAXDWORD)) 163 | return -1; 164 | break; 165 | 166 | default: 167 | return -1; 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | #undef DWORD_HI 174 | #undef DWORD_LO 175 | 176 | #endif /* _WIN32 */ 177 | -------------------------------------------------------------------------------- /Classes/WindowsMMap.h: -------------------------------------------------------------------------------- 1 | /*===- WindowsMMap.h - Support library for PGO instrumentation ------------===*\ 2 | |* 3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | |* See https://llvm.org/LICENSE.txt for license information. 5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | |* 7 | \*===----------------------------------------------------------------------===*/ 8 | 9 | #ifndef PROFILE_INSTRPROFILING_WINDOWS_MMAP_H 10 | #define PROFILE_INSTRPROFILING_WINDOWS_MMAP_H 11 | 12 | #if defined(_WIN32) 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /* 19 | * mmap() flags 20 | */ 21 | #define PROT_READ 0x1 22 | #define PROT_WRITE 0x2 23 | #define PROT_EXEC 0x0 24 | 25 | #define MAP_FILE 0x00 26 | #define MAP_SHARED 0x01 27 | #define MAP_PRIVATE 0x02 28 | #define MAP_ANONYMOUS 0x20 29 | #define MAP_ANON MAP_ANONYMOUS 30 | #define MAP_FAILED ((void *) -1) 31 | 32 | /* 33 | * msync() flags 34 | */ 35 | #define MS_ASYNC 0x0001 /* return immediately */ 36 | #define MS_INVALIDATE 0x0002 /* invalidate all cached data */ 37 | #define MS_SYNC 0x0010 /* msync synchronously */ 38 | 39 | /* 40 | * flock() operations 41 | */ 42 | #define LOCK_SH 1 /* shared lock */ 43 | #define LOCK_EX 2 /* exclusive lock */ 44 | #define LOCK_NB 4 /* don't block when locking */ 45 | #define LOCK_UN 8 /* unlock */ 46 | 47 | #ifdef __USE_FILE_OFFSET64 48 | # define DWORD_HI(x) (x >> 32) 49 | # define DWORD_LO(x) ((x) & 0xffffffff) 50 | #else 51 | # define DWORD_HI(x) (0) 52 | # define DWORD_LO(x) (x) 53 | #endif 54 | 55 | void *mmap(void *start, size_t length, int prot, int flags, int fd, 56 | off_t offset); 57 | 58 | void munmap(void *addr, size_t length); 59 | 60 | int msync(void *addr, size_t length, int flags); 61 | 62 | int flock(int fd, int operation); 63 | 64 | #endif /* _WIN32 */ 65 | 66 | #endif /* PROFILE_INSTRPROFILING_WINDOWS_MMAP_H */ 67 | -------------------------------------------------------------------------------- /Coverage.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint RCodeCoverage.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "Coverage" 19 | s.version = "1.0.0" 20 | s.summary = "objective-c increment code coverage based on git." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = "objective-c increment code coverage based on git." 28 | 29 | s.homepage = "git@github.com:xuezhulian/Coverage.git" 30 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 31 | 32 | 33 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 34 | # 35 | # Licensing your code is important. See http://choosealicense.com for more info. 36 | # CocoaPods will detect a license file if there is a named LICENSE* 37 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 38 | # 39 | 40 | s.license = "GPL" 41 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 42 | 43 | 44 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 45 | # 46 | # Specify the authors of the library, with email addresses. Email addresses 47 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 48 | # accepts just a name if you'd rather not provide an email address. 49 | # 50 | # Specify a social_media_url where others can refer to, for example a twitter 51 | # profile URL. 52 | # 53 | 54 | s.author = { "xuezhulian" => "xuezhulian@gmail.com" } 55 | 56 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 57 | # 58 | # If this Pod runs only on iOS or OS X, then specify the platform and 59 | # the deployment target. You can optionally include the target after the platform. 60 | # 61 | 62 | # s.platform = :ios 63 | # s.platform = :ios, "5.0" 64 | 65 | # When using multiple platforms 66 | s.ios.deployment_target = "8.0" 67 | # s.osx.deployment_target = "10.7" 68 | # s.watchos.deployment_target = "2.0" 69 | # s.tvos.deployment_target = "9.0" 70 | 71 | 72 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 73 | # 74 | # Specify the location from where the source should be retrieved. 75 | # Supports git, hg, bzr, svn and HTTP. 76 | # 77 | 78 | s.source = { :git => "git@github.com:xuezhulian/Coverage.git", :tag => "#{s.version}" } 79 | 80 | 81 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 82 | # 83 | # CocoaPods is smart about how it includes source code. For source files 84 | # giving a folder will include any swift, h, m, mm, c & cpp files. 85 | # For header files it will include any header in the folder. 86 | # Not including the public_header_files will make all headers public. 87 | # 88 | 89 | s.source_files = "Classes" 90 | s.exclude_files = "Classes/Exclude" 91 | 92 | s.public_header_files = "Classes/GCDAProfiling.h" 93 | 94 | 95 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 96 | # 97 | # A list of resources included with the Pod. These are copied into the 98 | # target bundle with a build phase script. Anything else will be cleaned. 99 | # You can preserve files from being cleaned, please don't preserve 100 | # non-essential files like tests, examples and documentation. 101 | # 102 | 103 | # s.resource = "icon.png" 104 | # s.resources = "Resources/*.png" 105 | 106 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 107 | 108 | 109 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 110 | # 111 | # Link your library with frameworks, or libraries. Libraries do not include 112 | # the lib prefix of their name. 113 | # 114 | 115 | # s.framework = "SomeFramework" 116 | # s.frameworks = "SomeFramework", "AnotherFramework" 117 | 118 | # s.library = "iconv" 119 | # s.libraries = "iconv", "xml2" 120 | 121 | 122 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 123 | # 124 | # If your library depends on compiler flags you can set them in the xcconfig hash 125 | # where they will only apply to your library. If you depend on other Podspecs 126 | # you can include multiple dependencies to ensure it works. 127 | 128 | # s.requires_arc = true 129 | 130 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 131 | # s.dependency "JSONKit", "~> 1.4" 132 | 133 | end 134 | -------------------------------------------------------------------------------- /GenerateEnv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __author__ = "yuencong" 4 | __date__ = "2019-03-20" 5 | __license__ = "GPL" 6 | 7 | import os 8 | import sys 9 | import re 10 | import string 11 | 12 | class GenerateEnv(): 13 | def __init__(self): 14 | scriptdir() 15 | xcodeconfigdir() 16 | lcovtooldir() 17 | handlepoddir() 18 | gcnodir() 19 | gcdadir() 20 | 21 | def scriptdir(): 22 | global SCRIPT_DIR 23 | SCRIPT_DIR = sys.path[0] 24 | os.environ['SCRIPT_DIR'] = SCRIPT_DIR 25 | 26 | def xcodeconfigdir(): 27 | global OBJROOT 28 | global SRCROOT 29 | global OBJECT_FILE_DIR_normal 30 | global PRODUCT_BUNDLE_ID 31 | global TARGET_DEVICE_ID 32 | global BUILT_PRODUCTS_DIR 33 | 34 | envFilePath = SCRIPT_DIR+'/env.sh' 35 | if not os.path.exists(envFilePath): 36 | print 'Error:No env.sh,checkout whether exportenv.sh is config' 37 | exit(1) 38 | envFile = open(envFilePath,'r') 39 | envRe = re.compile('export (.*)=\"(.*)\"') 40 | for line in envFile.xreadlines(): 41 | result = envRe.findall(line) 42 | if result: 43 | (envKey,envValue) = result[0] 44 | os.environ[envKey] = envValue 45 | envFile.close() 46 | 47 | OBJROOT = os.environ['OBJROOT'].strip('\r') 48 | SRCROOT = os.environ['SRCROOT'].strip('\r') 49 | OBJECT_FILE_DIR_normal = os.environ['OBJECT_FILE_DIR_normal'].strip('\r') 50 | PRODUCT_BUNDLE_ID = os.environ['PRODUCT_BUNDLE_IDENTIFIER'].strip('\r') 51 | TARGET_DEVICE_ID = os.environ['TARGET_DEVICE_IDENTIFIER'].strip('\r') 52 | BUILT_PRODUCTS_DIR = os.environ['BUILT_PRODUCTS_DIR'].strip('\r').split('/')[-1] 53 | #main repo object file,resolve the case when main and pod in same dir 54 | os.environ['OBJECT_FILE_DIR_main'] = OBJECT_FILE_DIR_normal 55 | 56 | def lcovtooldir(): 57 | lcov = SCRIPT_DIR + '/lcov/usr/bin/lcov ' 58 | genhtml = SCRIPT_DIR + '/lcov/usr/bin/genhtml ' 59 | os.environ['lcov'] = lcov 60 | os.environ['genhtml'] = genhtml 61 | 62 | def handlepoddir(): 63 | global OBJECT_FILE_DIR_normal 64 | global SRCROOT 65 | 66 | #default main repo 67 | if len(sys.argv) != 2: 68 | return 69 | #filter coverage dir 70 | if sys.argv[1] == SCRIPT_DIR.split('/')[-1]: 71 | return 72 | repodir = sys.argv[1] 73 | SRCROOT = SCRIPT_DIR.replace(SCRIPT_DIR.split('/')[-1],repodir.strip()) 74 | os.environ['SRCROOT'] = SRCROOT 75 | podspec = None 76 | for podspecPath in os.popen('find %s -name \"*.podspec\" -maxdepth 1' %SRCROOT).xreadlines(): 77 | podspec = podspecPath.strip() 78 | break 79 | 80 | if podspec and os.path.exists(podspec): 81 | podspecFile = open(podspec,'r') 82 | snameRe = re.compile('s.name\s*=\s*[\"|\']([\w-]*)[\"|\']') 83 | for line in podspecFile.xreadlines(): 84 | snameResult = snameRe.findall(line) 85 | if snameResult: 86 | break 87 | 88 | sname = snameResult[0].strip() 89 | OBJECT_FILE_DIR_normal = OBJROOT + '/Pods.build/%s/%s.build/Objects-normal'%(BUILT_PRODUCTS_DIR,sname) 90 | if not os.path.exists(OBJECT_FILE_DIR_normal): 91 | print 'Error:\nOBJECT_FILE_DIR_normal:%s invalid path'%OBJECT_FILE_DIR_normal 92 | exit(1) 93 | os.environ['OBJECT_FILE_DIR_normal'] = OBJECT_FILE_DIR_normal 94 | 95 | def gcnodir(): 96 | GCNO_DIR = OBJECT_FILE_DIR_normal + '/x86_64' 97 | os.environ['GCNO_DIR'] = GCNO_DIR 98 | print("GCNO_DIR :"+GCNO_DIR) 99 | 100 | def gcdadir(): 101 | GCDA_DIR = None 102 | USER_ROOT = os.environ['HOME'].strip() 103 | APPLICATIONS_DIR = '%s/Library/Developer/CoreSimulator/Devices/%s/data/Containers/Data/Application/' %(USER_ROOT,TARGET_DEVICE_ID) 104 | if not os.path.exists(APPLICATIONS_DIR): 105 | print 'Error:\nAPPLICATIONS_DIR:%s invaild file path'%APPLICATIONS_DIR 106 | exit(1) 107 | APPLICATION_ID_RE = re.compile('\w{8}-\w{4}-\w{4}-\w{4}-\w{12}') 108 | for file in os.listdir(APPLICATIONS_DIR): 109 | if not APPLICATION_ID_RE.findall(file): 110 | continue 111 | plistPath = APPLICATIONS_DIR + file.strip() + '/.com.apple.mobile_container_manager.metadata.plist' 112 | if not os.path.exists(plistPath): 113 | continue 114 | plistFile = open(plistPath,'r') 115 | plistContent = plistFile.read() 116 | plistFile.close() 117 | if string.find(plistContent,PRODUCT_BUNDLE_ID) != -1: 118 | GCDA_DIR = APPLICATIONS_DIR + file + '/Documents/gcda_files' 119 | break 120 | if not GCDA_DIR: 121 | print 'GCDA DIR invalid,please check xcode config' 122 | exit(1) 123 | if not os.path.exists(GCDA_DIR): 124 | print 'GCDA_DIR:%s path invalid'%GCDA_DIR 125 | exit(1) 126 | os.environ['GCDA_DIR'] = GCDA_DIR 127 | print("GCDA_DIR :"+GCDA_DIR) 128 | -------------------------------------------------------------------------------- /GitAnalyze.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __author__ = "yuencong" 4 | __date__ = "2019-03-21" 5 | __license__ = "GPL" 6 | 7 | import re 8 | import os 9 | import string 10 | from gitdiffmodel import PushDiff,CommitDiff,ClassDiff 11 | 12 | def generatePushdiff(): 13 | SRCROOT = os.environ['SRCROOT'].strip() 14 | SCRIPT_DIR = os.environ['SCRIPT_DIR'].strip('\r') 15 | os.chdir(SRCROOT) 16 | pushdiff = PushDiff() 17 | 18 | 19 | #TODO: MAYBE BETTER METHOD 20 | aheadCommitRe = re.compile('Your branch is ahead of \'.*\' by ([0-9]*) commit') 21 | aheadCommitNum = None 22 | for line in os.popen('git status').xreadlines(): 23 | result = aheadCommitRe.findall(line) 24 | if result: 25 | aheadCommitNum = result[0] 26 | break 27 | 28 | if aheadCommitNum: 29 | for i in range(0,int(aheadCommitNum)): 30 | commitid = os.popen('git rev-parse HEAD~%s'%i).read().strip() 31 | pushdiff.commitdiffs.append(CommitDiff(commitid)) 32 | stashName = 'git-diff-stash' 33 | os.system('git stash save \'%s\'; git log -%s -v -U0> "%s/diff"'%(stashName,aheadCommitNum,SCRIPT_DIR)) 34 | if string.find(os.popen('git stash list').readline(),stashName) != -1: 35 | os.system('git stash pop') 36 | else: 37 | #prevent change last commit msg without new commit 38 | print 'No new commit' 39 | exit(1) 40 | 41 | if not os.path.exists(SCRIPT_DIR + '/diff'): 42 | print 'No diff file' 43 | exit(1) 44 | 45 | diffFile = open(SCRIPT_DIR+'/diff') 46 | 47 | commitidRe = re.compile('commit (\w{40})') 48 | classRe = re.compile('\+\+\+ b(.*)') 49 | changedLineRe = re.compile('\+(\d+),*(\d*) \@\@') 50 | 51 | commitdiff = None 52 | classdiff = None 53 | 54 | for line in diffFile.xreadlines(): 55 | #match commit id 56 | commmidResult = commitidRe.findall(line) 57 | if commmidResult: 58 | commitid = commmidResult[0].strip() 59 | if pushdiff.contains_commitdiff(commitid): 60 | commitdiff = pushdiff.commitdiff(commitid) 61 | else: 62 | #TODO filter merge 63 | commitdiff = None 64 | 65 | if not commitdiff: 66 | continue 67 | 68 | #match class name 69 | classResult = classRe.findall(line) 70 | if classResult: 71 | classname = classResult[0].strip().split('/')[-1] 72 | classdiff = commitdiff.classdiff(classname) 73 | 74 | if not classdiff: 75 | continue 76 | 77 | #match lines 78 | lineResult = changedLineRe.findall(line) 79 | if lineResult: 80 | (startIndex,lines) = lineResult[0] 81 | # add nothing 82 | if cmp(lines,'0') == 0: 83 | pass 84 | #add startIndex line 85 | elif cmp(lines,'') == 0: 86 | classdiff.changedlines.add(int(startIndex)) 87 | #add lines from startindex 88 | else: 89 | for num in range(0,int(lines)): 90 | classdiff.changedlines.add(int(startIndex) + num) 91 | 92 | diffFile.close() 93 | os.remove(SCRIPT_DIR+'/diff') 94 | 95 | return pushdiff 96 | -------------------------------------------------------------------------------- /InfoAnalyze.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __author__ = "yuencong" 4 | __date__ = "2019-03-21" 5 | __license__ = "GPL" 6 | 7 | import os 8 | import sys 9 | import re 10 | import string 11 | from lcovinfomodel import LcovInfo,LcovClassInfo 12 | from gitdiffmodel import PushDiff,CommitDiff,ClassDiff 13 | 14 | def getLcovInfo(pushdiff): 15 | SCRIPT_DIR = os.environ['SCRIPT_DIR'] 16 | lcov = os.environ['lcov'].strip('\r') 17 | genhtml = os.environ['genhtml'].strip('\r') 18 | os.chdir(SCRIPT_DIR) 19 | lcovInfo = LcovInfo() 20 | 21 | if not os.path.exists('Coverage.info'): 22 | print 'Error:No Coverage.info' 23 | exit(1) 24 | 25 | if os.path.getsize('Coverage.info') == 0: 26 | print 'Error:Coveragte.info size is 0' 27 | os.remove('Coverage.info') 28 | exit(1) 29 | 30 | #filter the file not recorded in git 31 | headerFileRe = re.compile("([0-9a-zA-Z\+]*\.[h|m|mm|c]+)") 32 | changedClasses = pushdiff.changedClasses() 33 | filterClasses = set() 34 | for line in os.popen(lcov + ' -l Coverage.info').xreadlines(): 35 | result = headerFileRe.findall(line) 36 | if result and not result[0].strip() in changedClasses: 37 | filterClasses.add(result[0].strip()) 38 | if len(filterClasses) != 0: 39 | os.system(lcov + '--remove Coverage.info *%s* -o Coverage.info' %'* *'.join(filterClasses)) 40 | 41 | if os.path.getsize('Coverage.info') == 0: 42 | os.remove('Coverage.info') 43 | return 44 | #filter unused lines 45 | infoFiler = open('Coverage.info','r') 46 | lines = infoFiler.readlines() 47 | infoFiler.close() 48 | 49 | infoFilew = open('Coverage.info','w') 50 | # DA:,[,] 51 | DARe = re.compile('^DA:([0-9]*),([0-9]*)') 52 | 53 | changedlines = None 54 | lcovclassinfo = None 55 | #generate lcov data model 56 | for line in lines: 57 | #match file name 58 | if line.startswith('SF:'): 59 | infoFilew.write('end_of_record\n') 60 | classname = line.strip().split('/')[-1].strip() 61 | changedlines = pushdiff.changedLinesForClass(classname) 62 | if len(changedlines) == 0: 63 | lcovclassinfo = None 64 | else: 65 | lcovclassinfo = lcovInfo.lcovclassinfo(classname) 66 | infoFilew.write(line) 67 | 68 | if not lcovclassinfo: 69 | continue 70 | #match lines 71 | DAResult = DARe.findall(line) 72 | if DAResult: 73 | (startIndex,count) = DAResult[0] 74 | if not int(startIndex) in changedlines: 75 | continue 76 | infoFilew.write(line) 77 | if int(count) == 0: 78 | lcovclassinfo.nohitlines.add(int(startIndex)) 79 | else: 80 | lcovclassinfo.hitlines.add(int(startIndex)) 81 | continue 82 | 83 | infoFilew.write('end_of_record\n') 84 | infoFilew.close() 85 | 86 | #genhtml 87 | if not os.path.getsize('Coverage.info') == 0: 88 | os.system(genhtml + 'Coverage.info -o Coverage') 89 | os.remove('Coverage.info') 90 | 91 | return lcovInfo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coverage 2 | iOS增量代码覆盖率工具 3 | 4 | 5 | ## config 6 | 7 | 下载到本地通过pod导入到工程中 8 | 9 | xcode->build phases->new run script phase 添加exportenv.sh脚本 例如: ../../Coverage/exportenv.sh 10 | 11 | 建议新添加一个基于DEBUG的scheme 12 | xcode->build setttings:GCC_GENERATE_TEST_COVERAGE_FILES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS设置为YES 13 | (新添加scheme需要设置 GCC_OPTIMIZATION_LEVEL GCC_PREPROCESSOR_DEFINITIONS) 14 | 15 | 修改main函数 16 | CODECOVERAGE在GCC_PREPROCESSOR_DEFINITIONS里面定义 17 | ``` 18 | #if CODECOVERAGE 19 | #import 20 | #endif 21 | int main(int argc, char * argv[]) { 22 | @autoreleasepool { 23 | #if CODECOVERAGE 24 | CODE_CCOVER_START 25 | #endif 26 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 27 | } 28 | } 29 | ``` 30 | 31 | git commit之后 执行 python coverage.py (你的工程目录) 会添加覆盖率信息到commit-msg 这一步可以自定义push脚本 32 | 33 | 34 | -------------------------------------------------------------------------------- /coverage.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | import shutil 5 | import re 6 | import string 7 | from gitdiffmodel import PushDiff,CommitDiff,ClassDiff 8 | from lcovinfomodel import LcovInfo,LcovClassInfo 9 | import GitAnalyze 10 | from GenerateEnv import GenerateEnv 11 | import InfoAnalyze 12 | 13 | pushdiff = PushDiff() 14 | lcovinfo = LcovInfo() 15 | 16 | def generateEnv(): 17 | GenerateEnv() 18 | 19 | global OBJECT_FILE_DIR_normal 20 | global OBJECT_FILE_DIR_main 21 | global SRCROOT 22 | global SCRIPT_DIR 23 | global lcov 24 | global GCNO_DIR 25 | global GCDA_DIR 26 | 27 | OBJECT_FILE_DIR_normal = os.environ['OBJECT_FILE_DIR_normal'].strip('\r') 28 | OBJECT_FILE_DIR_main = os.environ['OBJECT_FILE_DIR_main'].strip('\r') 29 | SRCROOT = os.environ['SRCROOT'].strip('\r') 30 | SCRIPT_DIR = os.environ['SCRIPT_DIR'] 31 | lcov = os.environ['lcov'].strip('\r') 32 | GCNO_DIR = os.environ['GCNO_DIR'].strip('\r') 33 | GCDA_DIR = os.environ['GCDA_DIR'].strip('\r') 34 | 35 | def generateInfo(): 36 | os.chdir(SCRIPT_DIR) 37 | 38 | changedfiles = map(lambda x:x.split('.')[0],pushdiff.changedClasses()) 39 | if len(changedfiles) == 0: 40 | exit(0) 41 | 42 | sourcespath = SCRIPT_DIR + '/sources' 43 | if os.path.isdir(sourcespath): 44 | shutil.rmtree(sourcespath) 45 | os.makedirs(sourcespath) 46 | 47 | for filename in changedfiles: 48 | gcdafile = GCDA_DIR+'/'+filename+'.gcda' 49 | if os.path.exists(gcdafile): 50 | shutil.copy(gcdafile,sourcespath) 51 | else: 52 | print 'Error:GCDA file not found for %s' %gcdafile 53 | exit(1) 54 | gcnofile = GCNO_DIR + '/'+filename + '.gcno' 55 | if not os.path.exists(gcnofile): 56 | gcnofile = gcnofile.replace(OBJECT_FILE_DIR_normal,OBJECT_FILE_DIR_main) 57 | if not os.path.exists(gcnofile): 58 | print 'Error:GCNO file not found for %s' %gcnofile 59 | exit(1) 60 | shutil.copy(gcnofile,sourcespath) 61 | os.system(lcov + '-c -b %s -d %s -o \"Coverage.info\"' %(SCRIPT_DIR,sourcespath)) 62 | if not os.path.exists(SCRIPT_DIR+'/Coverage.info'): 63 | print 'Error:failed to generate Coverage.info' 64 | exit(1) 65 | 66 | if os.path.getsize(SCRIPT_DIR+'/Coverage.info') == 0: 67 | print 'Error:Coveragte.info size is 0' 68 | os.remove(SCRIPT_DIR+'/Coverage.info') 69 | exit(1) 70 | 71 | shutil.rmtree(sourcespath) 72 | 73 | 74 | def rewriteCommitMsg(pushdiff,lcovinfo): 75 | os.chdir(SRCROOT) 76 | 77 | if pushdiff == None or lcovinfo == None: 78 | return 79 | 80 | for classinfo in lcovinfo.classinfos: 81 | pushdiff.addhitlinesForClass(classinfo) 82 | 83 | print 'RCoverageRate:%.2f'%pushdiff.coveragerate() 84 | os.system('GIT_SEQUENCE_EDITOR=\"sed -i -re \'s/^pick /e /\'\" git rebase -i --autostash HEAD~%s'%len(pushdiff.commitdiffs)) 85 | for i in reversed(range(0,len(pushdiff.commitdiffs))): 86 | commitdiff = pushdiff.commitdiffs[i] 87 | if not commitdiff: 88 | os.system('git rebase --abort') 89 | continue 90 | 91 | coveragerate = commitdiff.coveragerate() 92 | lines = os.popen('git log -1 --pretty=%B').readlines() 93 | 94 | commitMsg = lines[0].strip() 95 | commitMsgRe = re.compile('coverage: ([0-9\.-]*)') 96 | result = commitMsgRe.findall(commitMsg) 97 | if result: 98 | if result[0].strip() == '%.2f'%coveragerate: 99 | os.system('git rebase --continue') 100 | continue 101 | commitMsg = commitMsg.replace('coverage: %s'%result[0],'coverage: %.2f'%coveragerate) 102 | else: 103 | commitMsg = commitMsg + ' coverage: %.2f%%'%coveragerate 104 | lines[0] = commitMsg+'\n' 105 | 106 | stashName = 'commit-amend-stash' 107 | os.system('git stash save \'%s\';git commit --amend -m \'%s \' --no-edit;' %(stashName,''.join(lines))) 108 | if string.find(os.popen('cd %s;git stash list'%SRCROOT).readline(),stashName) != -1: 109 | os.system('git stash pop') 110 | 111 | os.system('git rebase --continue;') 112 | 113 | 114 | if __name__ == "__main__": 115 | generateEnv() 116 | 117 | pushdiff = GitAnalyze.generatePushdiff() 118 | 119 | generateInfo() 120 | 121 | lcovinfo = InfoAnalyze.getLcovInfo(pushdiff) 122 | 123 | rewriteCommitMsg(pushdiff,lcovinfo) 124 | -------------------------------------------------------------------------------- /deletePrePush.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding=utf-8 3 | 4 | import os 5 | import sys 6 | 7 | if __name__ == '__main__': 8 | dirpath = sys.path[0] 9 | dirpath = '/'.join(dirpath.split('/')[0:-1]) 10 | if not os.path.exists(dirpath): 11 | print 'pre push hook failed with invalid dirpath' 12 | exit() 13 | 14 | dirlists = set() 15 | for file in os.listdir(dirpath): 16 | if file == 'RCodeCoverage': 17 | continue 18 | if os.path.exists(dirpath + '/' + file + '/.git/hooks/pre-push'): 19 | dirlists.add(dirpath + '/' + file + '/.git/hooks/pre-push') 20 | 21 | for name in dirlists: 22 | os.remove(name) 23 | print u'已移除代码覆盖率检查' 24 | -------------------------------------------------------------------------------- /exportenv.sh: -------------------------------------------------------------------------------- 1 | scripts="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 2 | export | egrep '( BUILT_PRODUCTS_DIR)|(CURRENT_ARCH)|(OBJECT_FILE_DIR_normal)|(SRCROOT)|(OBJROOT)|(TARGET_DEVICE_IDENTIFIER)|(TARGET_DEVICE_MODEL)|(PRODUCT_BUNDLE_IDENTIFIER)' > "${scripts}/env.sh" 3 | -------------------------------------------------------------------------------- /genPrePushFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding=utf-8 3 | 4 | import os 5 | import stat 6 | import sys 7 | import string 8 | 9 | defaultencoding = 'utf-8' 10 | if sys.getdefaultencoding() != defaultencoding: 11 | reload(sys) 12 | sys.setdefaultencoding(defaultencoding) 13 | 14 | 15 | def gen_by_componet_name(name): 16 | content = """ 17 | cd ../RCodeCoverage 18 | echo '----------------' 19 | rate=$(python coverage.py %s | grep "RCoverageRate:" | sed 's/RCoverageRate:\([0-9-]*\).*/\\1/g') 20 | if [ $rate -eq -1 ]; then 21 | echo '没有覆盖率信息,跳过...' 22 | exit 0 23 | fi 24 | 25 | if [ $(echo "$rate < 80.0" | bc) = 1 ];then 26 | echo '代码覆盖率为'$rate',不满足需求' 27 | echo '----------------' 28 | exit 1 29 | else 30 | echo '代码覆盖率为'$rate',即将上传代码' 31 | fi 32 | echo '----------------' 33 | exit 0 34 | """ % (name,) 35 | return content 36 | 37 | 38 | if __name__ == '__main__': 39 | dirpath = sys.path[0] 40 | dirpath = '/'.join(dirpath.split('/')[0:-1]) 41 | if not os.path.exists(dirpath): 42 | print 'pre push hook failed with invalid dirpath' 43 | exit() 44 | 45 | dirlists = set() 46 | for file in os.listdir(dirpath): 47 | if file == 'RCodeCoverage': 48 | continue 49 | if os.path.isdir(dirpath + '/' + file): 50 | dirlists.add(file) 51 | 52 | for name in dirlists: 53 | dirpath = sys.path[0] 54 | dirpath = dirpath.replace(dirpath.split('/')[-1],name) 55 | filepath = os.path.join(dirpath, '.git', 'hooks', 'pre-push.sample') 56 | if os.path.exists(filepath): 57 | os.rename(filepath,filepath[0:-7]) 58 | filepath = filepath[0:-7] 59 | 60 | if not os.path.exists(filepath): 61 | continue 62 | with open(filepath, 'wb') as f: 63 | if name == 'RBigApp': 64 | name = '' 65 | f.write(gen_by_componet_name(name).encode(encoding='UTF-8')) 66 | os.chmod(filepath, stat.S_IRWXU|stat.S_IRGRP|stat.S_IROTH) 67 | -------------------------------------------------------------------------------- /gitdiffmodel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __author__ = "yuencong" 4 | __date__ = "2019-03-20" 5 | __license__ = "GPL" 6 | 7 | from lcovinfomodel import LcovClassInfo 8 | 9 | class PushDiff: 10 | def __init__(self): 11 | self.commitdiffs = [] 12 | 13 | def contains_commitdiff(self,commitid): 14 | for commitdiff in self.commitdiffs: 15 | if commitdiff.commitid == commitid: 16 | return True 17 | return False 18 | 19 | def commitdiff(self,commitid): 20 | for commitdiff in self.commitdiffs: 21 | if commitdiff.commitid == commitid: 22 | return commitdiff 23 | return None 24 | 25 | def changedClasses(self): 26 | classes = set() 27 | for commitid in self.commitdiffs: 28 | for classdiff in commitid.classdiffs: 29 | if len(classdiff.changedlines) == 0: 30 | continue 31 | classname = classdiff.classname 32 | if classname.endswith('.m') or classname.endswith('.mm') or classname.endswith('.c'): 33 | classes.add(classname) 34 | 35 | return classes 36 | 37 | def changedLinesForClass(self,classname): 38 | changedlines = set() 39 | for commitdiff in self.commitdiffs: 40 | if commitdiff.contains_classdiff(classname): 41 | classdiff = commitdiff.classdiff(classname) 42 | changedlines = changedlines | classdiff.changedlines 43 | return changedlines 44 | 45 | def addhitlinesForClass(self,classinfo): 46 | for commitdiff in self.commitdiffs: 47 | if commitdiff.contains_classdiff(classinfo.classname): 48 | classdiff = commitdiff.classdiff(classinfo.classname) 49 | classdiff.hitlines = classinfo.hitlines & classdiff.changedlines 50 | classdiff.nohitlines = classinfo.nohitlines & classdiff.changedlines 51 | 52 | def coveragerate(self): 53 | hitlines = 0 54 | nohitlines = 0 55 | for commitdiff in self.commitdiffs: 56 | for classdiff in commitdiff.classdiffs: 57 | hitlines = hitlines + len(classdiff.hitlines) 58 | nohitlines = nohitlines + len(classdiff.nohitlines) 59 | if hitlines + nohitlines == 0: 60 | return -1 61 | 62 | return (hitlines / float(hitlines + nohitlines) * 100) 63 | 64 | 65 | def description(self): 66 | print '---------- PUSH DIFF DESCRIPTION' 67 | for commitdiff in self.commitdiffs: 68 | commitdiff.description() 69 | 70 | 71 | 72 | class CommitDiff: 73 | def __init__(self,commitid): 74 | self.commitid = commitid 75 | self.classdiffs = set() 76 | 77 | def contains_classdiff(self,classname): 78 | for classdiff in self.classdiffs: 79 | if classdiff.classname == classname: 80 | return True 81 | return False 82 | 83 | def classdiff(self,classname): 84 | for classdiff in self.classdiffs: 85 | if classdiff.classname == classname: 86 | return classdiff 87 | 88 | classdiff = ClassDiff(classname) 89 | self.classdiffs.add(classdiff) 90 | return classdiff 91 | 92 | def coveragerate(self): 93 | hitlines = 0 94 | nohitlines = 0 95 | for classdiff in self.classdiffs: 96 | hitlines = hitlines + len(classdiff.hitlines) 97 | nohitlines = nohitlines + len(classdiff.nohitlines) 98 | 99 | if hitlines + nohitlines == 0: 100 | return -1 101 | return (hitlines/float(hitlines + nohitlines) * 100) 102 | 103 | def description(self): 104 | print '---------- COMMIT ID: %s' %self.commitid 105 | for classdiff in self.classdiffs: 106 | classdiff.description() 107 | 108 | 109 | 110 | class ClassDiff: 111 | def __init__(self,classname): 112 | self.classname = classname 113 | self.changedlines = set() 114 | self.hitlines = set() 115 | self.nohitlines = set() 116 | 117 | def description(self): 118 | print '---------- CLASS NAME: %s' %self.classname 119 | print '---------- LINES: %s' %self.changedlines 120 | print '---------- HIT LINES: %s' %self.hitlines 121 | print '---------- NO HIT LINES: %s' %self.nohitlines -------------------------------------------------------------------------------- /lcov/etc/lcovrc: -------------------------------------------------------------------------------- 1 | # 2 | # /etc/lcovrc - system-wide defaults for LCOV 3 | # 4 | # To change settings for a single user, place a customized copy of this file 5 | # at location ~/.lcovrc 6 | # 7 | 8 | # Specify an external style sheet file (same as --css-file option of genhtml) 9 | #genhtml_css_file = gcov.css 10 | 11 | # Specify coverage rate limits (in %) for classifying file entries 12 | # HI: hi_limit <= rate <= 100 graph color: green 13 | # MED: med_limit <= rate < hi_limit graph color: orange 14 | # LO: 0 <= rate < med_limit graph color: red 15 | genhtml_hi_limit = 90 16 | genhtml_med_limit = 75 17 | 18 | # Width of line coverage field in source code view 19 | genhtml_line_field_width = 12 20 | 21 | # Width of branch coverage field in source code view 22 | genhtml_branch_field_width = 16 23 | 24 | # Width of overview image (used by --frames option of genhtml) 25 | genhtml_overview_width = 80 26 | 27 | # Resolution of overview navigation: this number specifies the maximum 28 | # difference in lines between the position a user selected from the overview 29 | # and the position the source code window is scrolled to (used by --frames 30 | # option of genhtml) 31 | genhtml_nav_resolution = 4 32 | 33 | # Clicking a line in the overview image should show the source code view at 34 | # a position a bit further up so that the requested line is not the first 35 | # line in the window. This number specifies that offset in lines (used by 36 | # --frames option of genhtml) 37 | genhtml_nav_offset = 10 38 | 39 | # Do not remove unused test descriptions if non-zero (same as 40 | # --keep-descriptions option of genhtml) 41 | genhtml_keep_descriptions = 0 42 | 43 | # Do not remove prefix from directory names if non-zero (same as --no-prefix 44 | # option of genhtml) 45 | genhtml_no_prefix = 0 46 | 47 | # Do not create source code view if non-zero (same as --no-source option of 48 | # genhtml) 49 | genhtml_no_source = 0 50 | 51 | # Replace tabs with number of spaces in source view (same as --num-spaces 52 | # option of genhtml) 53 | genhtml_num_spaces = 8 54 | 55 | # Highlight lines with converted-only data if non-zero (same as --highlight 56 | # option of genhtml) 57 | genhtml_highlight = 0 58 | 59 | # Include color legend in HTML output if non-zero (same as --legend option of 60 | # genhtml) 61 | genhtml_legend = 0 62 | 63 | # Use FILE as HTML prolog for generated pages (same as --html-prolog option of 64 | # genhtml) 65 | #genhtml_html_prolog = FILE 66 | 67 | # Use FILE as HTML epilog for generated pages (same as --html-epilog option of 68 | # genhtml) 69 | #genhtml_html_epilog = FILE 70 | 71 | # Use custom filename extension for pages (same as --html-extension option of 72 | # genhtml) 73 | #genhtml_html_extension = html 74 | 75 | # Compress all generated html files with gzip. 76 | #genhtml_html_gzip = 1 77 | 78 | # Include sorted overview pages (can be disabled by the --no-sort option of 79 | # genhtml) 80 | genhtml_sort = 1 81 | 82 | # Include function coverage data display (can be disabled by the 83 | # --no-func-coverage option of genhtml) 84 | #genhtml_function_coverage = 1 85 | 86 | # Include branch coverage data display (can be disabled by the 87 | # --no-branch-coverage option of genhtml) 88 | #genhtml_branch_coverage = 1 89 | 90 | # Specify the character set of all generated HTML pages 91 | genhtml_charset=UTF-8 92 | 93 | # Allow HTML markup in test case description text if non-zero 94 | genhtml_desc_html=0 95 | 96 | # Specify the precision for coverage rates 97 | #genhtml_precision=1 98 | 99 | # Location of the gcov tool (same as --gcov-info option of geninfo) 100 | #geninfo_gcov_tool = gcov 101 | 102 | # Adjust test names to include operating system information if non-zero 103 | #geninfo_adjust_testname = 0 104 | 105 | # Calculate checksum for each source code line if non-zero (same as --checksum 106 | # option of geninfo if non-zero, same as --no-checksum if zero) 107 | #geninfo_checksum = 1 108 | 109 | # Specify whether to capture coverage data for external source files (can 110 | # be overridden by the --external and --no-external options of geninfo/lcov) 111 | #geninfo_external = 1 112 | 113 | # Enable libtool compatibility mode if non-zero (same as --compat-libtool option 114 | # of geninfo if non-zero, same as --no-compat-libtool if zero) 115 | #geninfo_compat_libtool = 0 116 | 117 | # Use gcov's --all-blocks option if non-zero 118 | #geninfo_gcov_all_blocks = 1 119 | 120 | # Specify compatiblity modes (same as --compat option of geninfo). 121 | #geninfo_compat = libtool=on, hammer=auto, split_crc=auto 122 | 123 | # Adjust path to source files by removing or changing path components that 124 | # match the specified pattern (Perl regular expression format) 125 | #geninfo_adjust_src_path = /tmp/build => /usr/src 126 | 127 | # Specify if geninfo should try to automatically determine the base-directory 128 | # when collecting coverage data. 129 | geninfo_auto_base = 1 130 | 131 | # Directory containing gcov kernel files 132 | # lcov_gcov_dir = /proc/gcov 133 | 134 | # Location of the insmod tool 135 | lcov_insmod_tool = /sbin/insmod 136 | 137 | # Location of the modprobe tool 138 | lcov_modprobe_tool = /sbin/modprobe 139 | 140 | # Location of the rmmod tool 141 | lcov_rmmod_tool = /sbin/rmmod 142 | 143 | # Location for temporary directories 144 | lcov_tmp_dir = /tmp 145 | 146 | # Show full paths during list operation if non-zero (same as --list-full-path 147 | # option of lcov) 148 | lcov_list_full_path = 0 149 | 150 | # Specify the maximum width for list output. This value is ignored when 151 | # lcov_list_full_path is non-zero. 152 | lcov_list_width = 80 153 | 154 | # Specify the maximum percentage of file names which may be truncated when 155 | # choosing a directory prefix in list output. This value is ignored when 156 | # lcov_list_full_path is non-zero. 157 | lcov_list_truncate_max = 20 158 | 159 | # Specify if function coverage data should be collected and processed. 160 | lcov_function_coverage = 1 161 | 162 | # Specify if branch coverage data should be collected and processed. 163 | lcov_branch_coverage = 0 164 | -------------------------------------------------------------------------------- /lcov/usr/bin/gendesc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Copyright (c) International Business Machines Corp., 2002 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or (at 8 | # your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | # 19 | # 20 | # gendesc 21 | # 22 | # This script creates a description file as understood by genhtml. 23 | # Input file format: 24 | # 25 | # For each test case: 26 | # 27 | # 28 | # 29 | # Actual description may consist of several lines. By default, output is 30 | # written to stdout. Test names consist of alphanumeric characters 31 | # including _ and -. 32 | # 33 | # 34 | # History: 35 | # 2002-09-02: created by Peter Oberparleiter 36 | # 37 | 38 | use strict; 39 | use File::Basename; 40 | use Getopt::Long; 41 | use Cwd qw/abs_path/; 42 | 43 | 44 | # Constants 45 | our $tool_dir = abs_path(dirname($0)); 46 | our $lcov_version = "LCOV version 1.13"; 47 | our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; 48 | our $tool_name = basename($0); 49 | 50 | 51 | # Prototypes 52 | sub print_usage(*); 53 | sub gen_desc(); 54 | sub warn_handler($); 55 | sub die_handler($); 56 | 57 | 58 | # Global variables 59 | our $help; 60 | our $version; 61 | our $output_filename; 62 | our $input_filename; 63 | 64 | 65 | # 66 | # Code entry point 67 | # 68 | 69 | $SIG{__WARN__} = \&warn_handler; 70 | $SIG{__DIE__} = \&die_handler; 71 | 72 | # Parse command line options 73 | if (!GetOptions("output-filename=s" => \$output_filename, 74 | "version" =>\$version, 75 | "help|?" => \$help 76 | )) 77 | { 78 | print(STDERR "Use $tool_name --help to get usage information\n"); 79 | exit(1); 80 | } 81 | 82 | $input_filename = $ARGV[0]; 83 | 84 | # Check for help option 85 | if ($help) 86 | { 87 | print_usage(*STDOUT); 88 | exit(0); 89 | } 90 | 91 | # Check for version option 92 | if ($version) 93 | { 94 | print("$tool_name: $lcov_version\n"); 95 | exit(0); 96 | } 97 | 98 | 99 | # Check for input filename 100 | if (!$input_filename) 101 | { 102 | die("No input filename specified\n". 103 | "Use $tool_name --help to get usage information\n"); 104 | } 105 | 106 | # Do something 107 | gen_desc(); 108 | 109 | 110 | # 111 | # print_usage(handle) 112 | # 113 | # Write out command line usage information to given filehandle. 114 | # 115 | 116 | sub print_usage(*) 117 | { 118 | local *HANDLE = $_[0]; 119 | 120 | print(HANDLE < 142 | # TD: 143 | # 144 | # If defined, write output to OUTPUT_FILENAME, otherwise to stdout. 145 | # 146 | # Die on error. 147 | # 148 | 149 | sub gen_desc() 150 | { 151 | local *INPUT_HANDLE; 152 | local *OUTPUT_HANDLE; 153 | my $empty_line = "ignore"; 154 | 155 | open(INPUT_HANDLE, "<", $input_filename) 156 | or die("ERROR: cannot open $input_filename!\n"); 157 | 158 | # Open output file for writing 159 | if ($output_filename) 160 | { 161 | open(OUTPUT_HANDLE, ">", $output_filename) 162 | or die("ERROR: cannot create $output_filename!\n"); 163 | } 164 | else 165 | { 166 | *OUTPUT_HANDLE = *STDOUT; 167 | } 168 | 169 | # Process all lines in input file 170 | while () 171 | { 172 | chomp($_); 173 | 174 | if (/^(\w[\w-]*)(\s*)$/) 175 | { 176 | # Matched test name 177 | # Name starts with alphanum or _, continues with 178 | # alphanum, _ or - 179 | print(OUTPUT_HANDLE "TN: $1\n"); 180 | $empty_line = "ignore"; 181 | } 182 | elsif (/^(\s+)(\S.*?)\s*$/) 183 | { 184 | # Matched test description 185 | if ($empty_line eq "insert") 186 | { 187 | # Write preserved empty line 188 | print(OUTPUT_HANDLE "TD: \n"); 189 | } 190 | print(OUTPUT_HANDLE "TD: $2\n"); 191 | $empty_line = "observe"; 192 | } 193 | elsif (/^\s*$/) 194 | { 195 | # Matched empty line to preserve paragraph separation 196 | # inside description text 197 | if ($empty_line eq "observe") 198 | { 199 | $empty_line = "insert"; 200 | } 201 | } 202 | } 203 | 204 | # Close output file if defined 205 | if ($output_filename) 206 | { 207 | close(OUTPUT_HANDLE); 208 | } 209 | 210 | close(INPUT_HANDLE); 211 | } 212 | 213 | sub warn_handler($) 214 | { 215 | my ($msg) = @_; 216 | 217 | warn("$tool_name: $msg"); 218 | } 219 | 220 | sub die_handler($) 221 | { 222 | my ($msg) = @_; 223 | 224 | die("$tool_name: $msg"); 225 | } 226 | -------------------------------------------------------------------------------- /lcov/usr/bin/genpng: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Copyright (c) International Business Machines Corp., 2002 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or (at 8 | # your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | # 19 | # 20 | # genpng 21 | # 22 | # This script creates an overview PNG image of a source code file by 23 | # representing each source code character by a single pixel. 24 | # 25 | # Note that the Perl module GD.pm is required for this script to work. 26 | # It may be obtained from http://www.cpan.org 27 | # 28 | # History: 29 | # 2002-08-26: created by Peter Oberparleiter 30 | # 31 | 32 | use strict; 33 | use File::Basename; 34 | use Getopt::Long; 35 | use Cwd qw/abs_path/; 36 | 37 | 38 | # Constants 39 | our $tool_dir = abs_path(dirname($0)); 40 | our $lcov_version = "LCOV version 1.13"; 41 | our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; 42 | our $tool_name = basename($0); 43 | 44 | 45 | # Prototypes 46 | sub gen_png($$$@); 47 | sub check_and_load_module($); 48 | sub genpng_print_usage(*); 49 | sub genpng_process_file($$$$); 50 | sub genpng_warn_handler($); 51 | sub genpng_die_handler($); 52 | 53 | 54 | # 55 | # Code entry point 56 | # 57 | 58 | # Check whether required module GD.pm is installed 59 | if (check_and_load_module("GD")) 60 | { 61 | # Note: cannot use die() to print this message because inserting this 62 | # code into another script via do() would not fail as required! 63 | print(STDERR < \$tab_size, 85 | "width=i" => \$width, 86 | "output-filename=s" => \$out_filename, 87 | "help" => \$help, 88 | "version" => \$version)) 89 | { 90 | print(STDERR "Use $tool_name --help to get usage ". 91 | "information\n"); 92 | exit(1); 93 | } 94 | 95 | $filename = $ARGV[0]; 96 | 97 | # Check for help flag 98 | if ($help) 99 | { 100 | genpng_print_usage(*STDOUT); 101 | exit(0); 102 | } 103 | 104 | # Check for version flag 105 | if ($version) 106 | { 107 | print("$tool_name: $lcov_version\n"); 108 | exit(0); 109 | } 110 | 111 | # Check options 112 | if (!$filename) 113 | { 114 | die("No filename specified\n"); 115 | } 116 | 117 | # Check for output filename 118 | if (!$out_filename) 119 | { 120 | $out_filename = "$filename.png"; 121 | } 122 | 123 | genpng_process_file($filename, $out_filename, $width, $tab_size); 124 | exit(0); 125 | } 126 | 127 | 128 | # 129 | # genpng_print_usage(handle) 130 | # 131 | # Write out command line usage information to given filehandle. 132 | # 133 | 134 | sub genpng_print_usage(*) 135 | { 136 | local *HANDLE = $_[0]; 137 | 138 | print(HANDLE <) 192 | { 193 | if (/^\t\t(.*)$/) 194 | { 195 | # Uninstrumented line 196 | push(@source, ":$1"); 197 | } 198 | elsif (/^ ###### (.*)$/) 199 | { 200 | # Line with zero execution count 201 | push(@source, "0:$1"); 202 | } 203 | elsif (/^( *)(\d*) (.*)$/) 204 | { 205 | # Line with positive execution count 206 | push(@source, "$2:$3"); 207 | } 208 | } 209 | } 210 | else 211 | { 212 | # Plain text file 213 | while () { push(@source, ":$_"); } 214 | } 215 | close(HANDLE); 216 | 217 | gen_png($out_filename, $width, $tab_size, @source); 218 | } 219 | 220 | 221 | # 222 | # gen_png(filename, width, tab_size, source) 223 | # 224 | # Write an overview PNG file to FILENAME. Source code is defined by SOURCE 225 | # which is a list of lines : per source code line. 226 | # The output image will be made up of one pixel per character of source, 227 | # coloring will be done according to execution counts. WIDTH defines the 228 | # image width. TAB_SIZE specifies the number of spaces to use as replacement 229 | # string for tabulator signs in source code text. 230 | # 231 | # Die on error. 232 | # 233 | 234 | sub gen_png($$$@) 235 | { 236 | my $filename = shift(@_); # Filename for PNG file 237 | my $overview_width = shift(@_); # Imagewidth for image 238 | my $tab_size = shift(@_); # Replacement string for tab signs 239 | my @source = @_; # Source code as passed via argument 2 240 | my $height; # Height as define by source size 241 | my $overview; # Source code overview image data 242 | my $col_plain_back; # Color for overview background 243 | my $col_plain_text; # Color for uninstrumented text 244 | my $col_cov_back; # Color for background of covered lines 245 | my $col_cov_text; # Color for text of covered lines 246 | my $col_nocov_back; # Color for background of lines which 247 | # were not covered (count == 0) 248 | my $col_nocov_text; # Color for test of lines which were not 249 | # covered (count == 0) 250 | my $col_hi_back; # Color for background of highlighted lines 251 | my $col_hi_text; # Color for text of highlighted lines 252 | my $line; # Current line during iteration 253 | my $row = 0; # Current row number during iteration 254 | my $column; # Current column number during iteration 255 | my $color_text; # Current text color during iteration 256 | my $color_back; # Current background color during iteration 257 | my $last_count; # Count of last processed line 258 | my $count; # Count of current line 259 | my $source; # Source code of current line 260 | my $replacement; # Replacement string for tabulator chars 261 | local *PNG_HANDLE; # Handle for output PNG file 262 | 263 | # Handle empty source files 264 | if (!@source) { 265 | @source = ( "" ); 266 | } 267 | $height = scalar(@source); 268 | # Create image 269 | $overview = new GD::Image($overview_width, $height) 270 | or die("ERROR: cannot allocate overview image!\n"); 271 | 272 | # Define colors 273 | $col_plain_back = $overview->colorAllocate(0xff, 0xff, 0xff); 274 | $col_plain_text = $overview->colorAllocate(0xaa, 0xaa, 0xaa); 275 | $col_cov_back = $overview->colorAllocate(0xaa, 0xa7, 0xef); 276 | $col_cov_text = $overview->colorAllocate(0x5d, 0x5d, 0xea); 277 | $col_nocov_back = $overview->colorAllocate(0xff, 0x00, 0x00); 278 | $col_nocov_text = $overview->colorAllocate(0xaa, 0x00, 0x00); 279 | $col_hi_back = $overview->colorAllocate(0x00, 0xff, 0x00); 280 | $col_hi_text = $overview->colorAllocate(0x00, 0xaa, 0x00); 281 | 282 | # Visualize each line 283 | foreach $line (@source) 284 | { 285 | # Replace tabs with spaces to keep consistent with source 286 | # code view 287 | while ($line =~ /^([^\t]*)(\t)/) 288 | { 289 | $replacement = " "x($tab_size - ((length($1) - 1) % 290 | $tab_size)); 291 | $line =~ s/^([^\t]*)(\t)/$1$replacement/; 292 | } 293 | 294 | # Skip lines which do not follow the : 295 | # specification, otherwise $1 = count, $2 = source code 296 | if (!($line =~ /(\*?)(\d*):(.*)$/)) { next; } 297 | $count = $2; 298 | $source = $3; 299 | 300 | # Decide which color pair to use 301 | 302 | # If this line was not instrumented but the one before was, 303 | # take the color of that line to widen color areas in 304 | # resulting image 305 | if (($count eq "") && defined($last_count) && 306 | ($last_count ne "")) 307 | { 308 | $count = $last_count; 309 | } 310 | 311 | if ($count eq "") 312 | { 313 | # Line was not instrumented 314 | $color_text = $col_plain_text; 315 | $color_back = $col_plain_back; 316 | } 317 | elsif ($count == 0) 318 | { 319 | # Line was instrumented but not executed 320 | $color_text = $col_nocov_text; 321 | $color_back = $col_nocov_back; 322 | } 323 | elsif ($1 eq "*") 324 | { 325 | # Line was highlighted 326 | $color_text = $col_hi_text; 327 | $color_back = $col_hi_back; 328 | } 329 | else 330 | { 331 | # Line was instrumented and executed 332 | $color_text = $col_cov_text; 333 | $color_back = $col_cov_back; 334 | } 335 | 336 | # Write one pixel for each source character 337 | $column = 0; 338 | foreach (split("", $source)) 339 | { 340 | # Check for width 341 | if ($column >= $overview_width) { last; } 342 | 343 | if ($_ eq " ") 344 | { 345 | # Space 346 | $overview->setPixel($column++, $row, 347 | $color_back); 348 | } 349 | else 350 | { 351 | # Text 352 | $overview->setPixel($column++, $row, 353 | $color_text); 354 | } 355 | } 356 | 357 | # Fill rest of line 358 | while ($column < $overview_width) 359 | { 360 | $overview->setPixel($column++, $row, $color_back); 361 | } 362 | 363 | $last_count = $2; 364 | 365 | $row++; 366 | } 367 | 368 | # Write PNG file 369 | open (PNG_HANDLE, ">", $filename) 370 | or die("ERROR: cannot write png file $filename!\n"); 371 | binmode(*PNG_HANDLE); 372 | print(PNG_HANDLE $overview->png()); 373 | close(PNG_HANDLE); 374 | } 375 | 376 | sub genpng_warn_handler($) 377 | { 378 | my ($msg) = @_; 379 | 380 | warn("$tool_name: $msg"); 381 | } 382 | 383 | sub genpng_die_handler($) 384 | { 385 | my ($msg) = @_; 386 | 387 | die("$tool_name: $msg"); 388 | } 389 | -------------------------------------------------------------------------------- /lcov/usr/share/man/man1/gendesc.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man1/gendesc.1.gz -------------------------------------------------------------------------------- /lcov/usr/share/man/man1/genhtml.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man1/genhtml.1.gz -------------------------------------------------------------------------------- /lcov/usr/share/man/man1/geninfo.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man1/geninfo.1.gz -------------------------------------------------------------------------------- /lcov/usr/share/man/man1/genpng.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man1/genpng.1.gz -------------------------------------------------------------------------------- /lcov/usr/share/man/man1/lcov.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man1/lcov.1.gz -------------------------------------------------------------------------------- /lcov/usr/share/man/man5/lcovrc.5.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuezhulian/Coverage/040bc5328553d01dfbf37e28ec14f8d4d215b228/lcov/usr/share/man/man5/lcovrc.5.gz -------------------------------------------------------------------------------- /lcovinfomodel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __author__ = "yuencong" 4 | __date__ = "2019-03-20" 5 | __license__ = "GPL" 6 | 7 | class LcovInfo: 8 | def __init__(self): 9 | self.classinfos = set() 10 | 11 | def lcovclassinfo(self,classname): 12 | for classinfo in self.classinfos: 13 | if classinfo.classname == classname: 14 | return classinfo 15 | classinfo = LcovClassInfo(classname) 16 | self.classinfos.add(classinfo) 17 | return classinfo 18 | 19 | def description(self): 20 | print '---------- LCOV INFO' 21 | for classinfo in self.classinfos: 22 | classinfo.description() 23 | 24 | class LcovClassInfo: 25 | def __init__(self,classname): 26 | self.classname = classname 27 | self.hitlines = set() 28 | self.nohitlines = set() 29 | def description(self): 30 | print '---------- LCOV CLASS NAME:%s'%self.classname 31 | print '---------- LCOV CLASS HIT LINES:%s'%self.hitlines 32 | print '---------- LCOV CLASS NO HIT LINES:%s'%self.nohitlines 33 | 34 | --------------------------------------------------------------------------------