1 #include <inttypes.h>
2 #include "internal.h"
3
4 // update timings for writeout. only call on success. call only under statlock.
update_write_stats(const struct timespec * time1,const struct timespec * time0,ncstats * stats,int bytes)5 void update_write_stats(const struct timespec* time1, const struct timespec* time0,
6 ncstats* stats, int bytes){
7 if(bytes >= 0){
8 const int64_t elapsed = timespec_to_ns(time1) - timespec_to_ns(time0);
9 if(elapsed > 0){ // don't count clearly incorrect information, egads
10 ++stats->writeouts;
11 stats->writeout_ns += elapsed;
12 if(elapsed > stats->writeout_max_ns){
13 stats->writeout_max_ns = elapsed;
14 }
15 if(elapsed < stats->writeout_min_ns){
16 stats->writeout_min_ns = elapsed;
17 }
18 }
19 }else{
20 ++stats->failed_writeouts;
21 }
22 }
23
24 // negative 'bytes' are ignored as failures. call only while holding statlock.
25 // we don't increment failed_rasters here because 'bytes' < 0 actually indicates
26 // a rasterization failure -- we can't fail in rastering anymore.
update_raster_bytes(ncstats * stats,int bytes)27 void update_raster_bytes(ncstats* stats, int bytes){
28 if(bytes >= 0){
29 stats->raster_bytes += bytes;
30 if(bytes > stats->raster_max_bytes){
31 stats->raster_max_bytes = bytes;
32 }
33 if(bytes < stats->raster_min_bytes){
34 stats->raster_min_bytes = bytes;
35 }
36 }
37 }
38
39 // call only while holding statlock.
update_render_stats(const struct timespec * time1,const struct timespec * time0,ncstats * stats)40 void update_render_stats(const struct timespec* time1, const struct timespec* time0,
41 ncstats* stats){
42 const int64_t elapsed = timespec_to_ns(time1) - timespec_to_ns(time0);
43 //fprintf(stderr, "Rendering took %ld.%03lds\n", elapsed / NANOSECS_IN_SEC,
44 // (elapsed % NANOSECS_IN_SEC) / 1000000);
45 if(elapsed > 0){ // don't count clearly incorrect information, egads
46 ++stats->renders;
47 stats->render_ns += elapsed;
48 if(elapsed > stats->render_max_ns){
49 stats->render_max_ns = elapsed;
50 }
51 if(elapsed < stats->render_min_ns){
52 stats->render_min_ns = elapsed;
53 }
54 }
55 }
56
57 // call only while holding statlock.
update_raster_stats(const struct timespec * time1,const struct timespec * time0,ncstats * stats)58 void update_raster_stats(const struct timespec* time1, const struct timespec* time0,
59 ncstats* stats){
60 const int64_t elapsed = timespec_to_ns(time1) - timespec_to_ns(time0);
61 //fprintf(stderr, "Rasterizing took %ld.%03lds\n", elapsed / NANOSECS_IN_SEC,
62 // (elapsed % NANOSECS_IN_SEC) / 1000000);
63 if(elapsed > 0){ // don't count clearly incorrect information, egads
64 stats->raster_ns += elapsed;
65 if(elapsed > stats->raster_max_ns){
66 stats->raster_max_ns = elapsed;
67 }
68 if(elapsed < stats->raster_min_ns){
69 stats->raster_min_ns = elapsed;
70 }
71 }
72 }
73
reset_stats(ncstats * stats)74 void reset_stats(ncstats* stats){
75 uint64_t fbbytes = stats->fbbytes;
76 unsigned planes = stats->planes;
77 memset(stats, 0, sizeof(*stats));
78 stats->render_min_ns = 1ull << 62u;
79 stats->raster_min_bytes = 1ull << 62u;
80 stats->raster_min_ns = 1ull << 62u;
81 stats->writeout_min_ns = 1ull << 62u;
82 stats->fbbytes = fbbytes;
83 stats->planes = planes;
84 }
85
notcurses_stats(notcurses * nc,ncstats * stats)86 void notcurses_stats(notcurses* nc, ncstats* stats){
87 pthread_mutex_lock(&nc->stats.lock);
88 memcpy(stats, &nc->stats.s, sizeof(*stats));
89 pthread_mutex_unlock(&nc->stats.lock);
90 }
91
notcurses_stats_alloc(const notcurses * nc)92 ncstats* notcurses_stats_alloc(const notcurses* nc __attribute__ ((unused))){
93 ncstats* ret = malloc(sizeof(ncstats));
94 if(ret == NULL){
95 return NULL;
96 }
97 return ret;
98 }
99
notcurses_stats_reset(notcurses * nc,ncstats * stats)100 void notcurses_stats_reset(notcurses* nc, ncstats* stats){
101 pthread_mutex_lock(&nc->stats.lock);
102 if(stats){
103 memcpy(stats, &nc->stats.s, sizeof(*stats));
104 }
105 // add the stats to the stashed stats, so that we can show true totals on
106 // shutdown in the closing banner
107 ncstats* stash = &nc->stashed_stats;
108 if(nc->stats.s.render_min_ns < stash->render_min_ns){
109 stash->render_min_ns = nc->stats.s.render_min_ns;
110 }
111 if(nc->stats.s.raster_min_bytes < stash->raster_min_bytes){
112 stash->raster_min_bytes = nc->stats.s.raster_min_bytes;
113 }
114 if(nc->stats.s.raster_min_ns < stash->raster_min_ns){
115 stash->raster_min_ns = nc->stats.s.raster_min_ns;
116 }
117 if(nc->stats.s.writeout_min_ns < stash->writeout_min_ns){
118 stash->writeout_min_ns = nc->stats.s.writeout_min_ns;
119 }
120 if(nc->stats.s.render_max_ns > stash->render_max_ns){
121 stash->render_max_ns = nc->stats.s.render_max_ns;
122 }
123 if(nc->stats.s.raster_max_bytes > stash->raster_max_bytes){
124 stash->raster_max_bytes = nc->stats.s.raster_max_bytes;
125 }
126 if(nc->stats.s.raster_max_ns > stash->raster_max_ns){
127 stash->raster_max_ns = nc->stats.s.raster_max_ns;
128 }
129 if(nc->stats.s.writeout_max_ns > stash->writeout_max_ns){
130 stash->writeout_max_ns = nc->stats.s.writeout_max_ns;
131 }
132 stash->writeout_ns += nc->stats.s.writeout_ns;
133 stash->raster_ns += nc->stats.s.raster_ns;
134 stash->render_ns += nc->stats.s.render_ns;
135 stash->raster_bytes += nc->stats.s.raster_bytes;
136 stash->failed_renders += nc->stats.s.failed_renders;
137 stash->failed_writeouts += nc->stats.s.failed_writeouts;
138 stash->renders += nc->stats.s.renders;
139 stash->writeouts += nc->stats.s.writeouts;
140 stash->cellelisions += nc->stats.s.cellelisions;
141 stash->cellemissions += nc->stats.s.cellemissions;
142 stash->fgelisions += nc->stats.s.fgelisions;
143 stash->fgemissions += nc->stats.s.fgemissions;
144 stash->bgelisions += nc->stats.s.bgelisions;
145 stash->bgemissions += nc->stats.s.bgemissions;
146 stash->defaultelisions += nc->stats.s.defaultelisions;
147 stash->defaultemissions += nc->stats.s.defaultemissions;
148 stash->refreshes += nc->stats.s.refreshes;
149 stash->sprixelemissions += nc->stats.s.sprixelemissions;
150 stash->sprixelelisions += nc->stats.s.sprixelelisions;
151 stash->sprixelbytes += nc->stats.s.sprixelbytes;
152 stash->appsync_updates += nc->stats.s.appsync_updates;
153 stash->input_errors += nc->stats.s.input_errors;
154 stash->input_events += nc->stats.s.input_events;
155 stash->hpa_gratuitous += nc->stats.s.hpa_gratuitous;
156 stash->cell_geo_changes += nc->stats.s.cell_geo_changes;
157 stash->pixel_geo_changes += nc->stats.s.pixel_geo_changes;
158
159 stash->fbbytes = nc->stats.s.fbbytes;
160 stash->planes = nc->stats.s.planes;
161 reset_stats(&nc->stats.s);
162 pthread_mutex_unlock(&nc->stats.lock);
163 }
164
summarize_stats(notcurses * nc)165 void summarize_stats(notcurses* nc){
166 const char* clreol = get_escape(&nc->tcache, ESCAPE_EL);
167 if(clreol == NULL){
168 clreol = "";
169 }
170 const ncstats *stats = &nc->stashed_stats;
171 char totalbuf[NCBPREFIXSTRLEN + 1];
172 char minbuf[NCBPREFIXSTRLEN + 1];
173 char maxbuf[NCBPREFIXSTRLEN + 1];
174 char avgbuf[NCBPREFIXSTRLEN + 1];
175 if(stats->renders){
176 ncqprefix(stats->render_ns, NANOSECS_IN_SEC, totalbuf, 0);
177 ncqprefix(stats->render_min_ns, NANOSECS_IN_SEC, minbuf, 0);
178 ncqprefix(stats->render_max_ns, NANOSECS_IN_SEC, maxbuf, 0);
179 ncqprefix(stats->render_ns / stats->renders, NANOSECS_IN_SEC, avgbuf, 0);
180 fprintf(stderr, "%s%"PRIu64" render%s, %ss (%ss min, %ss avg, %ss max)" NL,
181 clreol, stats->renders, stats->renders == 1 ? "" : "s",
182 totalbuf, minbuf, avgbuf, maxbuf);
183 }
184 if(stats->writeouts || stats->failed_writeouts){
185 ncqprefix(stats->raster_ns, NANOSECS_IN_SEC, totalbuf, 0);
186 ncqprefix(stats->raster_min_ns, NANOSECS_IN_SEC, minbuf, 0);
187 ncqprefix(stats->raster_max_ns, NANOSECS_IN_SEC, maxbuf, 0);
188 ncqprefix(stats->raster_ns / (stats->writeouts + stats->failed_writeouts),
189 NANOSECS_IN_SEC, avgbuf, 0);
190 fprintf(stderr, "%s%"PRIu64" raster%s, %ss (%ss min, %ss avg, %ss max)" NL,
191 clreol, stats->writeouts, stats->writeouts == 1 ? "" : "s",
192 totalbuf, minbuf, avgbuf, maxbuf);
193 ncqprefix(stats->writeout_ns, NANOSECS_IN_SEC, totalbuf, 0);
194 ncqprefix(stats->writeout_ns ? stats->writeout_min_ns : 0,
195 NANOSECS_IN_SEC, minbuf, 0);
196 ncqprefix(stats->writeout_max_ns, NANOSECS_IN_SEC, maxbuf, 0);
197 ncqprefix(stats->writeouts ? stats->writeout_ns / stats->writeouts : 0,
198 NANOSECS_IN_SEC, avgbuf, 0);
199 fprintf(stderr, "%s%"PRIu64" write%s, %ss (%ss min, %ss avg, %ss max)" NL,
200 clreol, stats->writeouts, stats->writeouts == 1 ? "" : "s",
201 totalbuf, minbuf, avgbuf, maxbuf);
202 }
203 if(stats->renders || stats->input_events){
204 ncbprefix(stats->raster_bytes, 1, totalbuf, 1),
205 ncbprefix(stats->raster_bytes ? stats->raster_min_bytes : 0,
206 1, minbuf, 1),
207 ncbprefix(stats->renders ? stats->raster_bytes / stats->renders : 0, 1, avgbuf, 1);
208 ncbprefix(stats->raster_max_bytes, 1, maxbuf, 1),
209 fprintf(stderr, "%s%sB (%sB min, %sB avg, %sB max) %"PRIu64" input%s Ghpa: %"PRIu64 NL,
210 clreol, totalbuf, minbuf, avgbuf, maxbuf,
211 stats->input_events,
212 stats->input_events == 1 ? "" : "s",
213 stats->hpa_gratuitous);
214 }
215 fprintf(stderr, "%s%"PRIu64" failed render%s, %"PRIu64" failed raster%s, %"
216 PRIu64" refresh%s, %"PRIu64" input error%s" NL,
217 clreol, stats->failed_renders, stats->failed_renders == 1 ? "" : "s",
218 stats->failed_writeouts, stats->failed_writeouts == 1 ? "" : "s",
219 stats->refreshes, stats->refreshes == 1 ? "" : "es",
220 stats->input_errors, stats->input_errors == 1 ? "" : "s");
221 fprintf(stderr, "%sRGB emits:elides: def %"PRIu64":%"PRIu64" fg %"PRIu64":%"
222 PRIu64" bg %"PRIu64":%"PRIu64 NL,
223 clreol, stats->defaultemissions,
224 stats->defaultelisions,
225 stats->fgemissions,
226 stats->fgelisions,
227 stats->bgemissions,
228 stats->bgelisions);
229 fprintf(stderr, "%sCell emits:elides: %"PRIu64":%"PRIu64" (%.2f%%) %.2f%% %.2f%% %.2f%%" NL,
230 clreol, stats->cellemissions, stats->cellelisions,
231 (stats->cellemissions + stats->cellelisions) == 0 ? 0 :
232 (stats->cellelisions * 100.0) / (stats->cellemissions + stats->cellelisions),
233 (stats->defaultemissions + stats->defaultelisions) == 0 ? 0 :
234 (stats->defaultelisions * 100.0) / (stats->defaultemissions + stats->defaultelisions),
235 (stats->fgemissions + stats->fgelisions) == 0 ? 0 :
236 (stats->fgelisions * 100.0) / (stats->fgemissions + stats->fgelisions),
237 (stats->bgemissions + stats->bgelisions) == 0 ? 0 :
238 (stats->bgelisions * 100.0) / (stats->bgemissions + stats->bgelisions));
239 ncbprefix(stats->sprixelbytes, 1, totalbuf, 1);
240 fprintf(stderr, "%sBitmap emits:elides: %"PRIu64":%"PRIu64" (%.2f%%) %sB (%.2f%%) SuM: %"PRIu64" (%.2f%%)" NL,
241 clreol, stats->sprixelemissions, stats->sprixelelisions,
242 (stats->sprixelemissions + stats->sprixelelisions) == 0 ? 0 :
243 (stats->sprixelelisions * 100.0) / (stats->sprixelemissions + stats->sprixelelisions),
244 totalbuf,
245 stats->raster_bytes ? (stats->sprixelbytes * 100.0) / stats->raster_bytes : 0,
246 stats->appsync_updates,
247 stats->writeouts ? stats->appsync_updates * 100.0 / stats->writeouts : 0);
248 if(stats->cell_geo_changes || stats->pixel_geo_changes){
249 fprintf(stderr,"%sScreen/cell geometry changes: %"PRIu64"/%"PRIu64 NL,
250 clreol, stats->cell_geo_changes, stats->pixel_geo_changes);
251 }
252 }
253