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