1 /* $OpenBSD: stats.c,v 1.10 2010/07/19 19:46:44 espie Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Marc Espie. 5 * 6 * Code written for the OpenBSD project. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 21 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 31 /* statistics gathering */ 32 33 /* collection across make invocations is done with an mmap shared file, 34 to allow for concurrent adjustment to variables. 35 */ 36 37 #include "config.h" 38 #include "defines.h" 39 #include "stats.h" 40 41 #ifdef HAS_STATS 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <sys/time.h> 46 #include <sys/mman.h> 47 #include <sys/types.h> 48 #include <sys/resource.h> 49 #include "memory.h" 50 51 static void print_stats(void); 52 void Init_Stats(void); 53 static float average_runs(unsigned long val); 54 unsigned long *statarray; 55 56 static bool mmapped = false; 57 58 static float 59 average_runs(unsigned long val) 60 { 61 return (float)val / STAT_INVOCATIONS; 62 } 63 64 static void 65 print_stats(void) 66 { 67 struct rusage ru; 68 69 if (getrusage(RUSAGE_SELF, &ru) != -1) { 70 STAT_USER_SECONDS += ru.ru_utime.tv_sec; 71 STAT_USER_MS += ru.ru_utime.tv_usec; 72 if (STAT_USER_MS > 1000000) { 73 STAT_USER_MS -= 1000000; 74 STAT_USER_SECONDS++; 75 } 76 STAT_SYS_SECONDS += ru.ru_stime.tv_sec; 77 STAT_SYS_MS += ru.ru_stime.tv_usec; 78 if (STAT_SYS_MS > 1000000) { 79 STAT_SYS_MS -= 1000000; 80 STAT_SYS_SECONDS++; 81 } 82 } 83 fprintf(stderr, "Make runs: %lu\n", STAT_INVOCATIONS); 84 fprintf(stderr, "Time user: %lu.%06lu, sys %lu.%06lu\n", 85 STAT_USER_SECONDS, STAT_USER_MS, 86 STAT_SYS_SECONDS, STAT_SYS_MS); 87 #ifdef STATS_VAR_LOOKUP 88 /* to get the average total of MAXSIZE, we need this value */ 89 STAT_VAR_POWER += 90 STAT_VAR_HASH_MAXSIZE * STAT_VAR_HASH_CREATION; 91 fprintf(stderr, "Var finds: %f, lookups: %f, average: %f, max: %lu\n", 92 average_runs(STAT_VAR_FIND), 93 average_runs(STAT_VAR_SEARCHES), 94 (float)STAT_VAR_COUNT/STAT_VAR_SEARCHES, 95 STAT_VAR_MAXCOUNT); 96 fprintf(stderr, "Average hash: %f, creation: %f, from env %f\n", 97 average_runs(STAT_VAR_HASH_CREATION), 98 average_runs(STAT_VAR_CREATION), 99 average_runs(STAT_VAR_FROM_ENV)); 100 fprintf(stderr, "Local hash max: %lu, global hash max: %lu, average local: %f\n", 101 STAT_VAR_HASH_MAXSIZE, 102 STAT_VAR_GHASH_MAXSIZE, 103 (float)STAT_VAR_POWER/STAT_VAR_HASH_CREATION); 104 #endif 105 #ifdef STATS_GN_CREATION 106 fprintf(stderr, "Average GN: %f\n", average_runs(STAT_GN_COUNT)); 107 #endif 108 #ifdef STATS_SUFF 109 fprintf(stderr, "Average Suffix lookup: %f, transforms: %f\n", 110 average_runs(STAT_SUFF_LOOKUP_NAME), 111 average_runs(STAT_TRANSFORM_LOOKUP_NAME)); 112 #endif 113 #ifdef STATS_BUF 114 fprintf(stderr, "Buf tot: %f, def: %f, exp %f, weird %f, bad %f\n", 115 average_runs(STAT_TOTAL_BUFS), 116 average_runs(STAT_DEFAULT_BUFS), 117 average_runs(STAT_BUFS_EXPANSION), 118 average_runs(STAT_WEIRD_BUFS), 119 average_runs(STAT_WEIRD_INEFFICIENT)); 120 #endif 121 #ifdef STATS_HASH 122 fprintf(stderr, "Hashes new: %f, exp: %f, lookup %f, l: %f, +: %f, ent : %f\n", 123 average_runs(STAT_HASH_CREATION), 124 average_runs(STAT_HASH_EXPAND), 125 average_runs(STAT_HASH_LOOKUP), 126 (float)STAT_HASH_LENGTH/STAT_HASH_LOOKUP, 127 (float)STAT_HASH_POSITIVE/STAT_HASH_LOOKUP, 128 (float)STAT_HASH_ENTRIES/STAT_HASH_SIZE); 129 #endif 130 #ifdef STATS_GROW 131 fprintf(stderr, "Grow: %f\n", average_runs(STAT_GROWARRAY)); 132 #endif 133 if (mmapped) 134 munmap(statarray, STAT_NUMBER * sizeof(unsigned long)); 135 } 136 137 void 138 Init_Stats(void) 139 { 140 char *name; 141 int fd; 142 143 /* try to get ahold of a stats collecting file */ 144 name = getenv("MAKESTATS"); 145 if (name) { 146 while ((fd = open(name, O_RDWR)) == -1) { 147 /* if collecting file does not already exist, fill it with 148 * zeros (so all stats starting values should be 0) */ 149 unsigned long n; 150 int i; 151 FILE *f; 152 153 f = fopen(name, "w"); 154 155 n = 0; 156 for (i = 0; i < STAT_NUMBER; i++) 157 fwrite(&n, sizeof(unsigned long), 1, f); 158 fclose(f); 159 } 160 161 /* either we've got the file -> share it across makes */ 162 if (fd) { 163 statarray = mmap(0, STAT_NUMBER * sizeof(unsigned long), 164 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 165 if (statarray == MAP_FAILED) 166 exit(1); 167 mmapped = true; 168 } 169 } else 170 /* or we don't -> simple stats gathering */ 171 statarray = ecalloc(STAT_NUMBER, sizeof(unsigned long)); 172 STAT_INVOCATIONS++; 173 atexit(print_stats); 174 } 175 176 #endif 177 178