xref: /dragonfly/usr.bin/dsynth/runstats.c (revision c9c5aa9e)
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