1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "dsynth.h" 39 40 static runstats_t *RSBase; 41 static runstats_t **RSTailp = &RSBase; 42 static time_t RSStartTime; 43 44 #define RHISTSIZE 600 /* impulse record is 10 minutes */ 45 #define ONEHOUR (60 * 60) 46 47 void 48 RunStatsInit(void) 49 { 50 runstats_t *rs; 51 52 RSStartTime = time(NULL); 53 54 *RSTailp = &NCursesRunStats; 55 RSTailp = &(*RSTailp)->next; 56 57 *RSTailp = &MonitorRunStats; 58 RSTailp = &(*RSTailp)->next; 59 60 *RSTailp = &HtmlRunStats; 61 RSTailp = &(*RSTailp)->next; 62 63 for (rs = RSBase; rs; rs = rs->next) 64 rs->init(); 65 } 66 67 void 68 RunStatsDone(void) 69 { 70 runstats_t *rs; 71 72 for (rs = RSBase; rs; rs = rs->next) 73 rs->done(); 74 } 75 76 void 77 RunStatsReset(void) 78 { 79 runstats_t *rs; 80 81 for (rs = RSBase; rs; rs = rs->next) 82 rs->reset(); 83 } 84 85 void 86 RunStatsUpdate(worker_t *work, const char *portdir) 87 { 88 runstats_t *rs; 89 90 for (rs = RSBase; rs; rs = rs->next) 91 rs->update(work, portdir); 92 } 93 94 void 95 RunStatsUpdateTop(int active) 96 { 97 static int rate_history[RHISTSIZE]; 98 static u_int last_ti; 99 topinfo_t info; 100 runstats_t *rs; 101 u_int ti; 102 time_t t; 103 104 /* 105 * Time 106 */ 107 bzero(&info, sizeof(info)); 108 t = time(NULL) - RSStartTime; 109 info.s = t % 60; 110 info.m = t / 60 % 60; 111 info.h = t / 60 / 60; 112 info.active = active; 113 114 /* 115 * Easy fields 116 */ 117 info.total = BuildTotal; 118 info.successful = BuildSuccessCount; 119 info.ignored = BuildIgnoreCount; 120 info.remaining = BuildTotal - BuildCount; 121 info.failed = BuildFailCount; 122 info.skipped = BuildSkipCount; 123 info.meta = BuildMetaCount; 124 125 /* 126 * Load and swap 127 */ 128 getloadavg(info.dload, 3); 129 info.dswap = getswappct(&info.noswap) * 100.0; 130 131 /* 132 * Rate and 10-minute impulse 133 */ 134 if (t > 20) 135 info.pkgrate = (BuildSuccessCount + BuildFailCount) * 136 ONEHOUR / t; 137 else 138 info.pkgrate = 0; 139 ti = (u_int)((unsigned long)t % RHISTSIZE); 140 rate_history[ti] = BuildSuccessCount + BuildFailCount; 141 #if 0 142 dlog(DLOG_ALL, "ti[%3d] = %d\n", ti, rate_history[ti]); 143 #endif 144 while (last_ti != ti) { 145 rate_history[last_ti] = rate_history[ti]; 146 last_ti = (last_ti + 1) % RHISTSIZE; 147 } 148 149 if (t < 20) { 150 info.pkgimpulse = 0; 151 } else if (t < RHISTSIZE) { 152 info.pkgimpulse = rate_history[ti] - 153 rate_history[(ti - t) % RHISTSIZE]; 154 info.pkgimpulse = info.pkgimpulse * ONEHOUR / t; 155 } else { 156 info.pkgimpulse = rate_history[ti] - 157 rate_history[(ti + 1) % RHISTSIZE]; 158 info.pkgimpulse = info.pkgimpulse * ONEHOUR / RHISTSIZE; 159 #if 0 160 dlog(DLOG_ALL, "pkgimpulse %d - %d -> %d\n", 161 rate_history[ti], 162 rate_history[(ti + 1) % RHISTSIZE], 163 info.pkgimpulse); 164 #endif 165 } 166 167 info.dynmaxworkers = DynamicMaxWorkers; 168 169 /* 170 * Issue update 171 */ 172 for (rs = RSBase; rs; rs = rs->next) 173 rs->updateTop(&info); 174 } 175 176 void 177 RunStatsUpdateLogs(void) 178 { 179 runstats_t *rs; 180 181 for (rs = RSBase; rs; rs = rs->next) 182 rs->updateLogs(); 183 } 184 185 void 186 RunStatsSync(void) 187 { 188 runstats_t *rs; 189 190 for (rs = RSBase; rs; rs = rs->next) 191 rs->sync(); 192 } 193 194 void 195 RunStatsUpdateCompletion(worker_t *work, int logid, pkg_t *pkg, 196 const char *reason, const char *skipbuf) 197 { 198 runstats_t *rs; 199 200 for (rs = RSBase; rs; rs = rs->next) { 201 if (rs->updateCompletion) 202 rs->updateCompletion(work, logid, pkg, reason, skipbuf); 203 } 204 } 205