citrun.c (4817B)
1 /* 2 * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 #include <stdlib.h> /* exit */ 17 #include <stdio.h> /* fprintf, stderr */ 18 #include <string.h> /* strncpy */ 19 20 #include "citrun.h" /* struct citrun_header, struct citrun_node */ 21 #include "os.h" /* citrun_{extend,os_info,open_fd} */ 22 23 24 /* 25 * The purpose of the instrumentation runtime is to create a file in a well 26 * known location and constantly write counter information to it. 27 * 28 * Once the file is initially created it does not change in size. There is one 29 * `struct citrun_node` per instrumented translation unit. The file is 30 * structured like the following: 31 * 32 * /tmp/citrun.out: 33 * 34 * +-----------------------+ 35 * | struct citrun_header | 36 * | - citrun version | 37 * | - process ids | 38 * | - program name | 39 * | - working directory | 40 * | padding | 41 * +-----------------------+ 42 * | struct citrun_node 1 | 43 * | - source file paths | 44 * | - counter buffer | <-- size ~ lines in source file 45 * | padding | 46 * +-----------------------+ 47 * | struct citrun_node .. | 48 * | - source file paths | 49 * | - counter buffer | 50 * | padding | 51 * + ----------------------+------ 52 * | struct citrun_node N | ^ 53 * | - source file paths | |-- sized for efficient memory access 54 * | - counter buffer | | 55 * | padding | v 56 * +-----------------------+------ 57 */ 58 59 /* 60 * Internal function that extends the file descriptor given as the first 61 * argument and fills the extended space with version and runtime information. 62 * 63 * Returns a pointer to the beginning of the extended region on success. 64 * The instrumented program will exit nonzero on failure. 65 */ 66 static struct citrun_header * 67 citrun_add_header(int fd) 68 { 69 struct citrun_header *new_header; 70 71 new_header = citrun_extend(fd, sizeof(struct citrun_header)); 72 73 strncpy(new_header->magic, "ctrn", sizeof(new_header->magic)); 74 new_header->major = citrun_major; 75 new_header->minor = citrun_minor; 76 77 /* Fill in various runtime information fields. */ 78 citrun_os_info(new_header); 79 80 return new_header; 81 } 82 83 /* 84 * Public function called by code inserted into each translation unit. Takes a 85 * version major and minor as the first two arguments and a pointer to an 86 * existing `struct citrun_node` as the third argument. 87 * 88 * If the passed version numbers don't exactly match the version numbers this 89 * library was compiled with a runtime error occurs. This means all instrumented 90 * object files must be generated by the same citrun tools. 91 * 92 * The passed in `struct citrun_node` gets its fields copied to the runtime 93 * file, and extra counter buffer space is allocated. 94 * 95 * Instrumented program will exit nonzero on failure. 96 */ 97 void 98 citrun_node_add(unsigned int major, unsigned int minor, struct citrun_node *n) 99 { 100 size_t sz; 101 struct citrun_node *new; 102 static struct citrun_header *header = NULL; 103 static int fd = 0; 104 105 /* Binary compatibility between versions not guaranteed. */ 106 if (major != citrun_major || minor != citrun_minor) { 107 fprintf(stderr, "libcitrun %i.%i: incompatible version %i.%i.\n" 108 "Try cleaning and rebuilding your project.\n", 109 citrun_major, citrun_minor, major, minor); 110 exit(1); 111 } 112 113 if (header == NULL) { 114 fd = citrun_open_fd(); 115 header = citrun_add_header(fd); 116 citrun_start_viewer(); 117 } 118 119 /* Allocate enough room for node and live execution buffers. */ 120 sz = sizeof(struct citrun_node); 121 sz += n->size * sizeof(unsigned long long); 122 new = citrun_extend(fd, sz); 123 124 /* Increment accumulation fields in header. */ 125 header->units++; 126 header->loc += n->size; 127 128 /* Copy these fields from incoming node verbatim. */ 129 new->size = n->size; 130 strncpy(new->comp_file_path, n->comp_file_path, CITRUN_PATH_MAX); 131 strncpy(new->abs_file_path, n->abs_file_path, CITRUN_PATH_MAX); 132 new->comp_file_path[CITRUN_PATH_MAX - 1] = '\0'; 133 new->abs_file_path[CITRUN_PATH_MAX - 1] = '\0'; 134 135 /* Set incoming nodes data pointer to allocated space after struct. */ 136 n->data = (unsigned long long *)(new + 1); 137 }