1/* $OpenBSD: stats.c,v 1.12 2023/09/04 11:35:11 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 "defines.h" 38#include "stats.h" 39 40#ifdef HAS_STATS 41#include <fcntl.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <sys/time.h> 45#include <sys/mman.h> 46#include <sys/types.h> 47#include <sys/resource.h> 48#include "memory.h" 49 50static void print_stats(void); 51void Init_Stats(void); 52static float average_runs(unsigned long val); 53unsigned long *statarray; 54 55static bool mmapped = false; 56 57static float 58average_runs(unsigned long val) 59{ 60 return (float)val / STAT_INVOCATIONS; 61} 62 63static void 64print_stats(void) 65{ 66 struct rusage ru; 67 68 if (getrusage(RUSAGE_SELF, &ru) != -1) { 69 STAT_USER_SECONDS += ru.ru_utime.tv_sec; 70 STAT_USER_MS += ru.ru_utime.tv_usec; 71 if (STAT_USER_MS > 1000000) { 72 STAT_USER_MS -= 1000000; 73 STAT_USER_SECONDS++; 74 } 75 STAT_SYS_SECONDS += ru.ru_stime.tv_sec; 76 STAT_SYS_MS += ru.ru_stime.tv_usec; 77 if (STAT_SYS_MS > 1000000) { 78 STAT_SYS_MS -= 1000000; 79 STAT_SYS_SECONDS++; 80 } 81 } 82 fprintf(stderr, "Make runs: %lu\n", STAT_INVOCATIONS); 83 fprintf(stderr, "Time user: %lu.%06lu, sys %lu.%06lu\n", 84 STAT_USER_SECONDS, STAT_USER_MS, 85 STAT_SYS_SECONDS, STAT_SYS_MS); 86#ifdef STATS_VAR_LOOKUP 87 /* to get the average total of MAXSIZE, we need this value */ 88 STAT_VAR_POWER += 89 STAT_VAR_HASH_MAXSIZE * STAT_VAR_HASH_CREATION; 90 fprintf(stderr, "Var finds: %f, lookups: %f, average: %f, max: %lu\n", 91 average_runs(STAT_VAR_FIND), 92 average_runs(STAT_VAR_SEARCHES), 93 (float)STAT_VAR_COUNT/STAT_VAR_SEARCHES, 94 STAT_VAR_MAXCOUNT); 95 fprintf(stderr, "Average hash: %f, creation: %f, from env %f\n", 96 average_runs(STAT_VAR_HASH_CREATION), 97 average_runs(STAT_VAR_CREATION), 98 average_runs(STAT_VAR_FROM_ENV)); 99 fprintf(stderr, "Local hash max: %lu, global hash max: %lu, average local: %f\n", 100 STAT_VAR_HASH_MAXSIZE, 101 STAT_VAR_GHASH_MAXSIZE, 102 (float)STAT_VAR_POWER/STAT_VAR_HASH_CREATION); 103#endif 104#ifdef STATS_GN_CREATION 105 fprintf(stderr, "Average GN: %f\n", average_runs(STAT_GN_COUNT)); 106#endif 107#ifdef STATS_SUFF 108 fprintf(stderr, "Average Suffix lookup: %f, transforms: %f\n", 109 average_runs(STAT_SUFF_LOOKUP_NAME), 110 average_runs(STAT_TRANSFORM_LOOKUP_NAME)); 111#endif 112#ifdef STATS_BUF 113 fprintf(stderr, "Buf tot: %f, def: %f, exp %f, weird %f, bad %f\n", 114 average_runs(STAT_TOTAL_BUFS), 115 average_runs(STAT_DEFAULT_BUFS), 116 average_runs(STAT_BUFS_EXPANSION), 117 average_runs(STAT_WEIRD_BUFS), 118 average_runs(STAT_WEIRD_INEFFICIENT)); 119#endif 120#ifdef STATS_HASH 121 fprintf(stderr, "Hashes new: %f, exp: %f, lookup %f, l: %f, +: %f, ent : %f\n", 122 average_runs(STAT_HASH_CREATION), 123 average_runs(STAT_HASH_EXPAND), 124 average_runs(STAT_HASH_LOOKUP), 125 (float)STAT_HASH_LENGTH/STAT_HASH_LOOKUP, 126 (float)STAT_HASH_POSITIVE/STAT_HASH_LOOKUP, 127 (float)STAT_HASH_ENTRIES/STAT_HASH_SIZE); 128#endif 129#ifdef STATS_GROW 130 fprintf(stderr, "Grow: %f\n", average_runs(STAT_GROWARRAY)); 131#endif 132 if (mmapped) 133 munmap(statarray, STAT_NUMBER * sizeof(unsigned long)); 134} 135 136void 137Init_Stats(void) 138{ 139 char *name; 140 int fd; 141 142 /* try to get ahold of a stats collecting file */ 143 name = getenv("MAKESTATS"); 144 if (name) { 145 while ((fd = open(name, O_RDWR)) == -1) { 146 /* if collecting file does not already exist, fill it with 147 * zeros (so all stats starting values should be 0) */ 148 unsigned long n; 149 int i; 150 FILE *f; 151 152 f = fopen(name, "w"); 153 154 n = 0; 155 for (i = 0; i < STAT_NUMBER; i++) 156 fwrite(&n, sizeof(unsigned long), 1, f); 157 fclose(f); 158 } 159 160 /* either we've got the file -> share it across makes */ 161 if (fd) { 162 statarray = mmap(0, STAT_NUMBER * sizeof(unsigned long), 163 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 164 if (statarray == MAP_FAILED) 165 exit(1); 166 mmapped = true; 167 } 168 } else 169 /* or we don't -> simple stats gathering */ 170 statarray = ecalloc(STAT_NUMBER, sizeof(unsigned long)); 171 STAT_INVOCATIONS++; 172 atexit(print_stats); 173} 174 175#endif 176