1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3 
4 #include "util.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7 
8 #ifdef _WIN32
9 #include <windows.h>
10 #else
11 #include <sys/time.h>
12 #endif
13 
14 /**
15  *   For now error just uses stderr.
16  */
17 class baselifeerrors : public lifeerrors {
18 public:
fatal(const char * s)19    virtual void fatal(const char *s) {
20       fprintf(stderr, "%s\n", s) ;
21       exit(10) ;
22    }
warning(const char * s)23    virtual void warning(const char *s) {
24       fprintf(stderr, "%s\n", s) ;
25    }
status(const char * s)26    virtual void status(const char *s) {
27       fprintf(stderr, "%s\n", s) ;
28    }
beginprogress(const char *)29    virtual void beginprogress(const char *) {
30       aborted = false ;
31    }
abortprogress(double,const char *)32    virtual bool abortprogress(double, const char *) {
33       return false ;
34    }
endprogress()35    virtual void endprogress() {
36       // do nothing
37    }
getuserrules()38    virtual const char *getuserrules() {
39       return "" ;
40    }
getrulesdir()41    virtual const char *getrulesdir() {
42       return "" ;
43    }
44 } ;
45 
46 baselifeerrors baselifeerrors ;
47 lifeerrors *errorhandler = &baselifeerrors ;
48 
seterrorhandler(lifeerrors * o)49 void lifeerrors::seterrorhandler(lifeerrors *o) {
50   if (o == 0)
51     errorhandler = &baselifeerrors ;
52   else
53     errorhandler = o ;
54 }
55 
lifefatal(const char * s)56 void lifefatal(const char *s) {
57    errorhandler->fatal(s) ;
58 }
59 
lifewarning(const char * s)60 void lifewarning(const char *s) {
61    errorhandler->warning(s) ;
62 }
63 
lifestatus(const char * s)64 void lifestatus(const char *s) {
65    errorhandler->status(s) ;
66 }
67 
lifebeginprogress(const char * dlgtitle)68 void lifebeginprogress(const char *dlgtitle) {
69    errorhandler->beginprogress(dlgtitle) ;
70 }
71 
lifeabortprogress(double fracdone,const char * newmsg)72 bool lifeabortprogress(double fracdone, const char *newmsg) {
73    return errorhandler->aborted |=
74       errorhandler->abortprogress(fracdone, newmsg) ;
75 }
76 
isaborted()77 bool isaborted() {
78    return errorhandler->aborted ;
79 }
80 
lifeendprogress()81 void lifeendprogress() {
82    errorhandler->endprogress() ;
83 }
84 
lifegetuserrules()85 const char *lifegetuserrules() {
86    return errorhandler->getuserrules() ;
87 }
88 
lifegetrulesdir()89 const char *lifegetrulesdir() {
90    return errorhandler->getrulesdir() ;
91 }
92 
93 static FILE *f ;
getdebugfile()94 FILE *getdebugfile() {
95   if (f == 0)
96     f = fopen("trace.txt", "w") ;
97   return f ;
98 }
99 /**
100  *   Manage reading lines from a FILE* without worrying about
101  *   line terminates.  Note that the fgets() routine does not
102  *   insert any line termination characters at all.
103  */
linereader(FILE * f)104 linereader::linereader(FILE *f) {
105    setfile(f) ;
106 }
setfile(FILE * f)107 void linereader::setfile(FILE *f) {
108    fp = f ;
109    lastchar = 0 ;
110    closeonfree = 0 ;    // AKT: avoid crash on Linux
111 }
setcloseonfree()112 void linereader::setcloseonfree() {
113    closeonfree = 1 ;
114 }
close()115 int linereader::close() {
116    if (fp) {
117       int r = fclose(fp) ;
118       fp = 0 ;
119       return r ;
120    }
121    return 0 ;
122 }
~linereader()123 linereader::~linereader() {
124    if (closeonfree)
125       close() ;
126 }
127 const int LF = 10 ;
128 const int CR = 13 ;
fgets(char * buf,int maxlen)129 char *linereader::fgets(char *buf, int maxlen) {
130    int i = 0 ;
131    for (;;) {
132       if (i+1 >= maxlen) {
133          buf[i] = 0 ;
134          return buf ;
135       }
136       int c = getc(fp) ;
137       switch (c) {
138       case EOF:
139          if (i == 0)
140             return 0 ;
141          buf[i] = 0 ;
142          return buf ;
143       case LF:
144          if (lastchar != CR) {
145             lastchar = LF ;
146             buf[i] = 0 ;
147             return buf ;
148          }
149          break ;
150       case CR:
151          lastchar = CR ;
152          buf[i] = 0 ;
153          return buf ;
154       default:
155          lastchar = c ;
156          buf[i++] = (char)c ;
157          break ;
158       }
159    }
160 }
161 
162 #ifdef _WIN32
163 static double freq = 0.0;
gollySecondCount()164 double gollySecondCount() {
165    LARGE_INTEGER now;
166    if (freq == 0.0) {
167       LARGE_INTEGER f;
168       QueryPerformanceFrequency(&f);
169       freq = (double)f.QuadPart;
170       if (freq <= 0.0) freq = 1.0;	// play safe and avoid div by 0
171    }
172    QueryPerformanceCounter(&now);
173    return (now.QuadPart) / freq;
174 }
175 #else
gollySecondCount()176 double gollySecondCount() {
177    struct timeval tv ;
178    gettimeofday(&tv, 0) ;
179    return tv.tv_sec + 0.000001 * tv.tv_usec ;
180 }
181 #endif
182 
183 /*
184  *   Reporting.
185  *   The node count listed here wants to be big to reduce the number
186  *   of "get times" we do (which can be fairly expensive on some systems),
187  *   but it wants to be small in case something is going wrong like swapping
188  *   and we want to tell the user that the node rate has dropped.
189  *   A value of 65K is a reasonable compromise.
190  */
191 int hperf::reportMask = ((1<<16)-1) ; // node count between checks
192 /*
193  *   How frequently do we update the status bar?  Every two seconds
194  *   should be reasonable.  If we set this to zero, then that disables
195  *   performance reporting.
196  */
197 double hperf::reportInterval = 2 ; // time between reports
198 /*
199  *   Static buffer for status updates.
200  */
201 char perfstatusline[200] ;
report(hperf & mark,int verbose)202 void hperf::report(hperf &mark, int verbose) {
203    double ts = gollySecondCount() ;
204    double elapsed = ts - mark.timeStamp ;
205    if (reportInterval == 0 || elapsed < reportInterval)
206       return ;
207    timeStamp = ts ;
208    nodesCalculated += fastNodeInc ;
209    fastNodeInc = 0 ;
210    if (verbose) {
211       double nodeCount = nodesCalculated - mark.nodesCalculated ;
212       double halfFrac = 0 ;
213       if (nodeCount > 0)
214          halfFrac = (halfNodes - mark.halfNodes) / nodeCount ;
215       double depthDelta = depthSum - mark.depthSum ;
216       sprintf(perfstatusline, "RATE noderate %g depth %g half %g",
217                        nodeCount/elapsed, 1+depthDelta/nodeCount, halfFrac) ;
218       lifestatus(perfstatusline) ;
219    }
220    mark = *this ;
221 }
reportStep(hperf & mark,hperf & ratemark,double newGen,int verbose)222 void hperf::reportStep(hperf &mark, hperf &ratemark, double newGen, int verbose) {
223    nodesCalculated += fastNodeInc ;
224    fastNodeInc = 0 ;
225    frames++ ;
226    timeStamp = gollySecondCount() ;
227    double elapsed = timeStamp - mark.timeStamp ;
228    if (reportInterval == 0 || elapsed < reportInterval)
229       return ;
230    if (verbose) {
231       double inc = newGen - mark.genval ;
232       if (inc == 0)
233          inc = 1e30 ;
234       double nodeCount = nodesCalculated - mark.nodesCalculated ;
235       double halfFrac = 0 ;
236       if (nodeCount > 0)
237          halfFrac = (halfNodes - mark.halfNodes) / nodeCount ;
238       double depthDelta = depthSum - mark.depthSum ;
239       double genspersec = inc / elapsed ;
240       double nodespergen = nodeCount / inc ;
241       double fps = (frames - mark.frames) / elapsed ;
242       sprintf(perfstatusline,
243           "PERF gps %g nps %g fps %g depth %g half %g npg %g nodes %g",
244           genspersec, nodeCount/elapsed, fps, 1+depthDelta/nodeCount, halfFrac,
245           nodespergen, nodeCount) ;
246       lifestatus(perfstatusline) ;
247    }
248    genval = newGen ;
249    mark = *this ;
250    ratemark = *this ;
251 }
252