1 /* Copyright (c) 2003-2005, 2007 MySQL AB
2    Use is subject to license terms
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
16 
17 //----------------------------------------------------------------
18 // REDOLOGFILEREADER
19 // Reads a redo log file and checks it for errors and/or prints
20 // the file in a human readable format.
21 //
22 // Usage: redoLogFileReader <file> [-noprint] [-nocheck]
23 //        [-mbyte <0-15>] [-mbyteHeaders] [-pageHeaders]
24 //
25 //----------------------------------------------------------------
26 
27 
28 #include <ndb_global.h>
29 
30 #include "records.hpp"
31 
32 #define RETURN_ERROR 1
33 #define RETURN_OK 0
34 
35 #define FROM_BEGINNING 0
36 
37 void usage(const char * prg);
38 Uint32 readFromFile(FILE * f, Uint32 *toPtr, Uint32 sizeInWords);
39 void readArguments(int argc, const char** argv);
40 void doExit();
41 
42 FILE * f= 0;
43 char fileName[256];
44 bool theDumpFlag = false;
45 bool thePrintFlag = true;
46 bool theCheckFlag = true;
47 bool onlyPageHeaders = false;
48 bool onlyMbyteHeaders = false;
49 bool onlyFileDesc = false;
50 bool firstLap = true;
51 Uint32 startAtMbyte = 0;
52 Uint32 startAtPage = 0;
53 Uint32 startAtPageIndex = 0;
54 Uint32 *redoLogPage;
55 
56 NDB_COMMAND(redoLogFileReader,  "redoLogFileReader", "redoLogFileReader", "Read a redo log file", 16384) {
57   int wordIndex = 0;
58   int oldWordIndex = 0;
59   Uint32 recordType = 1234567890;
60 
61   PageHeader *thePageHeader;
62   CompletedGCIRecord *cGCIrecord;
63   PrepareOperationRecord *poRecord;
64   NextLogRecord *nlRecord;
65   FileDescriptor *fdRecord;
66   CommitTransactionRecord *ctRecord;
67   InvalidCommitTransactionRecord *ictRecord;
68   NextMbyteRecord *nmRecord;
69   AbortTransactionRecord *atRecord;
70 
71   readArguments(argc, argv);
72 
73   f = fopen(fileName, "rb");
74   if(!f){
75     perror("Error: open file");
76     exit(RETURN_ERROR);
77   }
78 
79   Uint32 tmpFileOffset = startAtMbyte * PAGESIZE * NO_PAGES_IN_MBYTE * sizeof(Uint32);
80   if (fseek(f, tmpFileOffset, FROM_BEGINNING)) {
81     perror("Error: Move in file");
82     exit(RETURN_ERROR);
83   }
84 
85   redoLogPage = new Uint32[PAGESIZE*NO_PAGES_IN_MBYTE];
86   Uint32 words_from_previous_page = 0;
87 
88   // Loop for every mbyte.
89   bool lastPage = false;
90   for (Uint32 j = startAtMbyte; j < NO_MBYTE_IN_FILE && !lastPage; j++) {
91 
92     readFromFile(f, redoLogPage, PAGESIZE*NO_PAGES_IN_MBYTE);
93 
94     words_from_previous_page = 0;
95 
96     // Loop for every page.
97     for (int i = 0; i < NO_PAGES_IN_MBYTE; i++) {
98       wordIndex = 0;
99       thePageHeader = (PageHeader *) &redoLogPage[i*PAGESIZE];
100       // Print out mbyte number, page number and page index.
101       ndbout << j << ":" << i << ":" << wordIndex << endl
102 	     << " " << j*32 + i << ":" << wordIndex << " ";
103       if (thePrintFlag) ndbout << (*thePageHeader);
104       if (theCheckFlag) {
105 	if(!thePageHeader->check()) {
106 	  ndbout << "Error in thePageHeader->check()" << endl;
107 	  doExit();
108 	}
109 
110 	Uint32 checkSum = 37;
111 	for (int ps = 1; ps < PAGESIZE; ps++)
112 	  checkSum = redoLogPage[i*PAGESIZE+ps] ^ checkSum;
113 
114 	if (checkSum != redoLogPage[i*PAGESIZE]){
115 	  ndbout << "WRONG CHECKSUM: checksum = " << redoLogPage[i*PAGESIZE]
116 		 << " expected = " << checkSum << endl;
117 	  doExit();
118 	}
119 	else
120 	  ndbout << "expected checksum: " << checkSum << endl;
121 
122       }
123 
124       lastPage = i != 0 && thePageHeader->lastPage();
125       Uint32 lastWord = thePageHeader->lastWord();
126 
127       if (onlyMbyteHeaders) {
128 	// Show only the first page header in every mbyte of the file.
129 	break;
130       }
131 
132       if (onlyPageHeaders) {
133 	// Show only page headers. Continue with the next page in this for loop.
134 	continue;
135       }
136 
137 
138       wordIndex = thePageHeader->getLogRecordSize() - words_from_previous_page;
139       Uint32 *redoLogPagePos = redoLogPage + i*PAGESIZE;
140       if (words_from_previous_page)
141       {
142 	memmove(redoLogPagePos + wordIndex ,
143 		redoLogPagePos - words_from_previous_page,
144 		words_from_previous_page*4);
145       }
146 
147       do {
148 	if (words_from_previous_page)
149 	{
150 	  // Print out mbyte number, page number and word index.
151 	  ndbout << j << ":" << i-1 << ":" << PAGESIZE-words_from_previous_page << endl
152 		 << j << ":" << i   << ":" << wordIndex+words_from_previous_page << endl
153 		 << " " << j*32 + i-1 << ":" << PAGESIZE-words_from_previous_page << " ";
154 	  words_from_previous_page = 0;
155 	}
156 	else
157 	{
158 	  // Print out mbyte number, page number and word index.
159 	  ndbout << j << ":" << i << ":" << wordIndex << endl
160 		 << " " << j*32 + i << ":" << wordIndex << " ";
161 	}
162 	redoLogPagePos = redoLogPage + i*PAGESIZE + wordIndex;
163 	oldWordIndex = wordIndex;
164 	recordType = *redoLogPagePos;
165 	switch(recordType) {
166 	case ZFD_TYPE:
167 	  fdRecord = (FileDescriptor *) redoLogPagePos;
168 	  if (thePrintFlag) ndbout << (*fdRecord);
169 	  if (theCheckFlag) {
170 	    if(!fdRecord->check()) {
171 	      ndbout << "Error in fdRecord->check()" << endl;
172 	      doExit();
173 	    }
174 	  }
175 	  if (onlyFileDesc) {
176 	    delete [] redoLogPage;
177 	    exit(RETURN_OK);
178 	  }
179 	  wordIndex += fdRecord->getLogRecordSize();
180 	  break;
181 
182 	case ZNEXT_LOG_RECORD_TYPE:
183 	  nlRecord = (NextLogRecord *) redoLogPagePos;
184 	  wordIndex += nlRecord->getLogRecordSize(wordIndex);
185 	  if (wordIndex <= PAGESIZE) {
186 	    if (thePrintFlag) ndbout << (*nlRecord);
187 	    if (theCheckFlag) {
188 	      if(!nlRecord->check()) {
189 		ndbout << "Error in nlRecord->check()" << endl;
190 		doExit();
191 	      }
192 	    }
193 	  }
194 	  break;
195 
196 	case ZCOMPLETED_GCI_TYPE:
197 	  cGCIrecord = (CompletedGCIRecord *) redoLogPagePos;
198 	  wordIndex += cGCIrecord->getLogRecordSize();
199 	  if (wordIndex <= PAGESIZE) {
200 	    if (thePrintFlag) ndbout << (*cGCIrecord);
201 	    if (theCheckFlag) {
202 	      if(!cGCIrecord->check()) {
203 		ndbout << "Error in cGCIrecord->check()" << endl;
204 		doExit();
205 	      }
206 	    }
207 	  }
208 	  break;
209 
210 	case ZPREP_OP_TYPE:
211 	  poRecord = (PrepareOperationRecord *) redoLogPagePos;
212 	  wordIndex += poRecord->getLogRecordSize(PAGESIZE-wordIndex);
213 	  if (wordIndex <= PAGESIZE) {
214 	    if (thePrintFlag) ndbout << (*poRecord);
215 	    if (theCheckFlag) {
216 	      if(!poRecord->check()) {
217 		ndbout << "Error in poRecord->check()" << endl;
218 		doExit();
219 	      }
220 	    }
221 	  }
222 	  break;
223 
224 	case ZCOMMIT_TYPE:
225 	  ctRecord = (CommitTransactionRecord *) redoLogPagePos;
226 	  wordIndex += ctRecord->getLogRecordSize();
227 	  if (wordIndex <= PAGESIZE) {
228 	    if (thePrintFlag) ndbout << (*ctRecord);
229 	    if (theCheckFlag) {
230 	      if(!ctRecord->check()) {
231 		ndbout << "Error in ctRecord->check()" << endl;
232 		doExit();
233 	      }
234 	    }
235 	  }
236 	  break;
237 
238 	case ZINVALID_COMMIT_TYPE:
239 	  ictRecord = (InvalidCommitTransactionRecord *) redoLogPagePos;
240 	  wordIndex += ictRecord->getLogRecordSize();
241 	  if (wordIndex <= PAGESIZE) {
242 	    if (thePrintFlag) ndbout << (*ictRecord);
243 	    if (theCheckFlag) {
244 	      if(!ictRecord->check()) {
245 		ndbout << "Error in ictRecord->check()" << endl;
246 		doExit();
247 	      }
248 	    }
249 	  }
250 	  break;
251 
252 	case ZNEXT_MBYTE_TYPE:
253 	  nmRecord = (NextMbyteRecord *) redoLogPagePos;
254 	  if (thePrintFlag) ndbout << (*nmRecord);
255 	  i = NO_PAGES_IN_MBYTE;
256 	  break;
257 
258 	case ZABORT_TYPE:
259 	  atRecord = (AbortTransactionRecord *) redoLogPagePos;
260 	  wordIndex += atRecord->getLogRecordSize();
261 	  if (wordIndex <= PAGESIZE) {
262 	    if (thePrintFlag) ndbout << (*atRecord);
263 	    if (theCheckFlag) {
264 	      if(!atRecord->check()) {
265 		ndbout << "Error in atRecord->check()" << endl;
266 		doExit();
267 	      }
268 	    }
269 	  }
270 	  break;
271 
272 	case ZNEW_PREP_OP_TYPE:
273 	case ZFRAG_SPLIT_TYPE:
274 	  ndbout << endl << "Record type = " << recordType << " not implemented." << endl;
275 	  doExit();
276 
277 	default:
278 	  ndbout << " ------ERROR: UNKNOWN RECORD TYPE------" << endl;
279 
280 	  // Print out remaining data in this page
281 	  for (int k = wordIndex; k < PAGESIZE; k++){
282 	    Uint32 unknown = redoLogPage[i*PAGESIZE + k];
283 	    ndbout_c("%-30d%-12u%-12x", k, unknown, unknown);
284 	  }
285 
286 	  doExit();
287 	}
288       } while(wordIndex < lastWord && i < NO_PAGES_IN_MBYTE);
289 
290 
291       if (lastPage)
292       {
293 	if (theDumpFlag)
294 	{
295 	  ndbout << " ------PAGE END: DUMPING REST OF PAGE------" << endl;
296 	  for (int k = wordIndex > PAGESIZE ? oldWordIndex : wordIndex;
297 	       k < PAGESIZE; k++)
298 	  {
299 	    Uint32 word = redoLogPage[i*PAGESIZE + k];
300 	    ndbout_c("%-30d%-12u%-12x", k, word, word);
301 	  }
302 	}
303 	break;
304       }
305       if (wordIndex > PAGESIZE) {
306 	words_from_previous_page = PAGESIZE - oldWordIndex;
307 	ndbout << " ----------- Record continues on next page -----------" << endl;
308       } else {
309 	wordIndex = 0;
310 	words_from_previous_page = 0;
311       }
312       ndbout << endl;
313     }//for
314     ndbout << endl;
315     if (startAtMbyte != 0) {
316       break;
317     }
318   }//for
319   fclose(f);
320   delete [] redoLogPage;
321   exit(RETURN_OK);
322 }
323 
324 //----------------------------------------------------------------
325 //
326 //----------------------------------------------------------------
327 
readFromFile(FILE * f,Uint32 * toPtr,Uint32 sizeInWords)328 Uint32 readFromFile(FILE * f, Uint32 *toPtr, Uint32 sizeInWords) {
329   Uint32 noOfReadWords;
330   if ( !(noOfReadWords = fread(toPtr, sizeof(Uint32), sizeInWords, f)) ) {
331     ndbout << "Error reading file" << endl;
332     doExit();
333   }
334 
335   return noOfReadWords;
336 }
337 
338 
339 //----------------------------------------------------------------
340 //
341 //----------------------------------------------------------------
342 
343 
usage(const char * prg)344 void usage(const char * prg){
345   ndbout << endl << "Usage: " << endl << prg
346 	 << " <Binary log file> [-noprint] [-nocheck] [-mbyte <0-15>] "
347 	 << "[-mbyteheaders] [-pageheaders] [-filedescriptors] [-page <0-31>] "
348 	 << "[-pageindex <12-8191>]"
349 	 << endl << endl;
350 
351 }
readArguments(int argc,const char ** argv)352 void readArguments(int argc, const char** argv)
353 {
354   if(argc < 2 || argc > 9){
355     usage(argv[0]);
356     doExit();
357   }
358 
359   strcpy(fileName, argv[1]);
360   argc--;
361 
362   int i = 2;
363   while (argc > 1)
364     {
365       if (strcmp(argv[i], "-noprint") == 0) {
366 	thePrintFlag = false;
367       } else if (strcmp(argv[i], "-dump") == 0) {
368 	theDumpFlag = true;
369       } else if (strcmp(argv[i], "-nocheck") == 0) {
370 	theCheckFlag = false;
371       } else if (strcmp(argv[i], "-mbyteheaders") == 0) {
372 	onlyMbyteHeaders = true;
373       } else if (strcmp(argv[i], "-pageheaders") == 0) {
374 	onlyPageHeaders = true;
375       } else if (strcmp(argv[i], "-filedescriptors") == 0) {
376 	onlyFileDesc = true;
377       } else if (strcmp(argv[i], "-mbyte") == 0) {
378 	startAtMbyte = atoi(argv[i+1]);
379 	if (startAtMbyte > 15) {
380 	  usage(argv[0]);
381 	  doExit();
382 	}
383 	argc--;
384 	i++;
385       } else if (strcmp(argv[i], "-page") == 0) {
386 	startAtPage = atoi(argv[i+1]);
387 	if (startAtPage > 31) {
388 	  usage(argv[0]);
389 	  doExit();
390 	}
391 	argc--;
392 	i++;
393       } else if (strcmp(argv[i], "-pageindex") == 0) {
394 	startAtPageIndex = atoi(argv[i+1]);
395 	if (startAtPageIndex > 8191 || startAtPageIndex < 12) {
396 	  usage(argv[0]);
397 	  doExit();
398 	}
399 	argc--;
400 	i++;
401       } else {
402 	usage(argv[0]);
403 	doExit();
404       }
405       argc--;
406       i++;
407     }
408 
409 }
410 
doExit()411 void doExit() {
412   ndbout << "Error in redoLogReader(). Exiting!" << endl;
413   if (f) fclose(f);
414   delete [] redoLogPage;
415   exit(RETURN_ERROR);
416 }
417