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