1 
2 /* Web Polygraph       http://www.web-polygraph.org/
3  * Copyright 2003-2011 The Measurement Factory
4  * Licensed under the Apache License, Version 2.0 */
5 
6 #include "base/polygraph.h"
7 
8 #include "xstd/h/os_std.h"
9 #include "xstd/h/iomanip.h"
10 
11 #include "xstd/ZFStream.h"
12 #include "xstd/gadgets.h"
13 #include "base/ILog.h"
14 #include "base/polyLogCats.h"
15 #include "base/polyLogTags.h"
16 
17 
18 /* LogEntryPx */
19 
20 // XXX: we should log lgEnd/lgcEnd instead of using current constants!
good() const21 bool LogEntryPx::good() const {
22 	return
23 		0 <= theCat && theCat < lgcEnd &&
24 		0 <= theTag && theTag < lgEnd &&
25 		0 < theSize && theSize <= 10*1024*1024;
26 }
27 
28 // assume buffer size is at least TheLoadSize
29 // XXX: we should log lgEnd/lgcEnd instead of using current constants!
load(const void * const buf)30 bool LogEntryPx::load(const void *const buf) {
31 	const int *const ibuf = reinterpret_cast<const int *>(buf);
32 	theSize = ntohl(ibuf[0]);
33 	const int h = ntohl(ibuf[1]);
34 	theTag = (h << 16) >> 16;
35 	theCat = h >> 16; // XXX: assumes sizeof(int) == 32
36 	return *this;
37 }
38 
print(ostream & os) const39 ostream &LogEntryPx::print(ostream &os) const {
40 	return os
41 		<< "tag: " << theCat << '/' << theTag
42 		<< " size: " << theSize;
43 
44 }
45 
46 /* ILog */
47 
ILog()48 ILog::ILog(): theStream(0), theZStream(0), theEntryEnd(-1) {
49 }
50 
~ILog()51 ILog::~ILog() {
52 	delete theZStream;
53 }
54 
stream(const String & aFileName,istream * aStream)55 void ILog::stream(const String &aFileName, istream *aStream) {
56 	theFileName = aFileName;
57 	theStream = aStream;
58 	getHeader();
59 }
60 
stream(const ILog & log)61 void ILog::stream(const ILog &log) {
62 	log.theStream->clear();
63 	log.theStream->seekg(0, ios::beg);
64 	stream(log.theFileName, log.theStream);
65 }
66 
67 // loads prefix of the current entry
68 // transparently loads progress entries
begEntry()69 LogEntryPx ILog::begEntry() {
70 	theEntryEnd = pos();
71 	char buf[LogEntryPx::TheLoadSize];
72 	while (get(buf, LogEntryPx::TheLoadSize)) {
73 		if (theCurPx.load(buf) && !theCurPx.good()) {
74 			const streampos startOff = theEntryEnd;
75 			clog << fileName() << ':' << theEntryEnd
76 				<< ": warning: corrupted log entry: " << theCurPx << endl;
77 
78 			do {
79 				theEntryEnd += 1;
80 				memmove(buf, buf + 1, LogEntryPx::TheLoadSize - 1);
81 				buf[LogEntryPx::TheLoadSize - 1] = getc();
82 			} while (!fail() && !(theCurPx.load(buf) &&
83 				theCurPx.good() &&
84 				theCurPx.theTag == lgProgress &&
85 				theCurPx.theSize < 1024));
86 			if (fail())
87 				break;
88 
89 			clog << fileName() << ':' << theEntryEnd
90 				<< ": maybe recovered after skipping " << (theEntryEnd-startOff)
91 				<< " bytes: " << theCurPx << endl;
92 		}
93 
94 		theEntryEnd += theCurPx.theSize;
95 		if (theCurPx.theTag != lgProgress)
96 			return theCurPx;
97 		theProgress.load(*this);
98 		endEntry();
99 	}
100 	theCurPx = LogEntryPx();
101 	return theCurPx;
102 }
103 
endEntry()104 void ILog::endEntry() {
105 	const int nextEntryOffset = theEntryEnd - pos();
106 	ignore(nextEntryOffset);
107 	theCurPx = LogEntryPx();
108 }
109 
getHeader()110 void ILog::getHeader() {
111 	Assert(theStream);
112 
113 	// check magic
114 	if (geti() != lgMagic1 || geti() != lgMagic2 || geti() != 0) {
115 		if (!*theStream || theStream->bad())
116 			cerr << theFileName << ": read error; " << Error::Last() << endl;
117 		else
118 			cerr << theFileName << ": unknown log file format" << endl;
119 		exit(-2);
120 	}
121 
122 	const int sver = 27;       // supported version
123 	const int cver = geti();   // current log version
124 	const int rver = geti();   // required min version to support
125 	int extraHdrSz = geti();   // size of extra headers
126 
127 	if (sver < rver) {
128 		cerr << theFileName << ": log version " << cver << endl;
129 		cerr << theFileName << ": requires support for log version " << rver << " or higher" << endl;
130 		cerr << theFileName << ": this program supports version " << sver << endl;
131 		exit(-2);
132 	}
133 
134 	if (sver != cver) {
135 		cerr << theFileName << ": log version " << cver << endl;
136 		cerr << theFileName << ": this program supports version " << sver << endl;
137 		cerr << theFileName << ": continuing at your own risk..." << endl;
138 		sleep(3);
139 	}
140 
141 	const int zlibHdrSz = 32;
142 	bool doCompression = false;
143 	if (extraHdrSz >= zlibHdrSz) {
144 		char buf[zlibHdrSz + 1];
145 		get(buf, zlibHdrSz);
146 		buf[zlibHdrSz] = '\0';
147 		extraHdrSz -= zlibHdrSz;
148 
149 		if (!strcmp(buf, "zlib")) {
150 			if (!zlib::Supported) {
151 				cerr << theFileName << ": log file is "
152 					"compressed with zlib" << endl <<
153 					"this program was built without zlib "
154 					"support" << endl << xexit;
155 			}
156 			doCompression = true;
157 		} else {
158 			cerr << theFileName << ": log file is compressed with "
159 				<< buf << endl << "this program does not "
160 				"support it" << endl << xexit;
161 		}
162 	}
163 
164 	ignore(extraHdrSz);
165 	if (doCompression)
166 		theZStream = new zlib::IFStream(*theStream);
167 }
168 
geti(int * & xs,int & count)169 int ILog::geti(int *&xs, int &count) {
170 	Must(geti(count) >= 0);
171 	xs = new int[count];
172 	for (int i = 0; i < count; ++i)
173 		geti(xs[i]);
174 	return count;
175 }
176 
gets(String & s)177 String &ILog::gets(String &s) {
178 	const int sz = geti();
179 	if (Should(sz >= 0)) {
180 		if (sz > 0) {
181 			char *buf = s.alloc(sz);
182 			get(buf, sz);
183 			return s;
184 		}
185 	}
186 	s = String();
187 	return s;
188 }
189 
get(void * const buf,const int len)190 bool ILog::get(void *const buf, const int len) {
191 	char *const cbuf = reinterpret_cast<char *>(buf);
192 	int count;
193 	if (theZStream)
194 		count = theZStream->read(cbuf, len);
195 	else {
196 		theStream->read(cbuf, len);
197 		count = theStream->gcount();
198 	}
199 	return count == len;
200 }
201 
fail() const202 bool ILog::fail() const {
203 	return theZStream ? theZStream->fail() : theStream ? theStream->fail() :
204 		true;
205 }
206 
pos() const207 istream::pos_type ILog::pos() const {
208 	return theZStream ? theZStream->pos() : theStream ? theStream->tellg() :
209 		istream::pos_type(-1);
210 }
211 
ignore(const int n)212 void ILog::ignore(const int n) {
213 	Must(n >= 0);
214 	if (theZStream)
215 		theZStream->ignore(n);
216 	else
217 		theStream->ignore(n);
218 }
219