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
RunStatsInit(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
RunStatsDone(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
RunStatsReset(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
RunStatsUpdate(worker_t * work,const char * portdir)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
RunStatsUpdateTop(int active)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
RunStatsUpdateLogs(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
RunStatsSync(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
RunStatsUpdateCompletion(worker_t * work,int logid,pkg_t * pkg,const char * reason,const char * skipbuf)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