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